Compare commits
5 Commits
f7f163a8fa
...
aee99408e1
Author | SHA1 | Date | |
---|---|---|---|
|
aee99408e1 | ||
|
1de2095f67 | ||
|
5f776c7372 | ||
|
128c40d839 | ||
|
4a8367e90a |
3
Cargo.lock
generated
@ -1215,6 +1215,7 @@ dependencies = [
|
||||
"byteorder-lite",
|
||||
"color_quant",
|
||||
"gif",
|
||||
"image-webp",
|
||||
"num-traits",
|
||||
"png",
|
||||
"zune-core",
|
||||
@ -2863,7 +2864,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "typst-assets"
|
||||
version = "0.13.1"
|
||||
source = "git+https://github.com/typst/typst-assets?rev=ab1295f#ab1295ff896444e51902e03c2669955e1d73604a"
|
||||
source = "git+https://github.com/typst/typst-assets?rev=c74e539#c74e539b090070a0c66fd007c550f5b6d3b724bd"
|
||||
|
||||
[[package]]
|
||||
name = "typst-cli"
|
||||
|
@ -32,7 +32,7 @@ typst-svg = { path = "crates/typst-svg", version = "0.13.1" }
|
||||
typst-syntax = { path = "crates/typst-syntax", version = "0.13.1" }
|
||||
typst-timing = { path = "crates/typst-timing", version = "0.13.1" }
|
||||
typst-utils = { path = "crates/typst-utils", version = "0.13.1" }
|
||||
typst-assets = { git = "https://github.com/typst/typst-assets", rev = "ab1295f" }
|
||||
typst-assets = { git = "https://github.com/typst/typst-assets", rev = "c74e539" }
|
||||
typst-dev-assets = { git = "https://github.com/typst/typst-dev-assets", rev = "fddbf8b" }
|
||||
arrayvec = "0.7.4"
|
||||
az = "1.2"
|
||||
@ -69,7 +69,7 @@ icu_provider_adapters = "1.4"
|
||||
icu_provider_blob = "1.4"
|
||||
icu_segmenter = { version = "1.4", features = ["serde"] }
|
||||
if_chain = "1"
|
||||
image = { version = "0.25.5", default-features = false, features = ["png", "jpeg", "gif"] }
|
||||
image = { version = "0.25.5", default-features = false, features = ["png", "jpeg", "gif", "webp"] }
|
||||
indexmap = { version = "2", features = ["serde"] }
|
||||
infer = { version = "0.19.0", default-features = false }
|
||||
kamadak-exif = "0.6"
|
||||
|
@ -841,7 +841,9 @@ fn param_value_completions<'a>(
|
||||
/// Returns which file extensions to complete for the given parameter if any.
|
||||
fn path_completion(func: &Func, param: &ParamInfo) -> Option<&'static [&'static str]> {
|
||||
Some(match (func.name(), param.name) {
|
||||
(Some("image"), "source") => &["png", "jpg", "jpeg", "gif", "svg", "svgz"],
|
||||
(Some("image"), "source") => {
|
||||
&["png", "jpg", "jpeg", "gif", "svg", "svgz", "webp"]
|
||||
}
|
||||
(Some("csv"), "source") => &["csv"],
|
||||
(Some("plugin"), "source") => &["wasm"],
|
||||
(Some("cbor"), "source") => &["cbor"],
|
||||
|
@ -147,6 +147,7 @@ fn determine_format(source: &DataSource, data: &Bytes) -> StrResult<ImageFormat>
|
||||
"jpg" | "jpeg" => return Ok(ExchangeFormat::Jpg.into()),
|
||||
"gif" => return Ok(ExchangeFormat::Gif.into()),
|
||||
"svg" | "svgz" => return Ok(VectorFormat::Svg.into()),
|
||||
"webp" => return Ok(ExchangeFormat::Webp.into()),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ pub fn layout_accent(
|
||||
// wide in many case.
|
||||
let width = elem.size(styles).relative_to(base.width());
|
||||
let short_fall = ACCENT_SHORT_FALL.at(glyph.font_size);
|
||||
let variant = glyph.stretch_horizontal(ctx, width, short_fall);
|
||||
let variant = glyph.stretch_horizontal(ctx, width - short_fall);
|
||||
let accent = variant.frame;
|
||||
let accent_attach = variant.accent_attach.0;
|
||||
|
||||
|
@ -110,12 +110,12 @@ fn layout_frac_like(
|
||||
|
||||
if binom {
|
||||
let mut left = GlyphFragment::new(ctx, styles, '(', span)
|
||||
.stretch_vertical(ctx, height, short_fall);
|
||||
.stretch_vertical(ctx, height - short_fall);
|
||||
left.center_on_axis(ctx);
|
||||
ctx.push(left);
|
||||
ctx.push(FrameFragment::new(styles, frame));
|
||||
let mut right = GlyphFragment::new(ctx, styles, ')', span)
|
||||
.stretch_vertical(ctx, height, short_fall);
|
||||
.stretch_vertical(ctx, height - short_fall);
|
||||
right.center_on_axis(ctx);
|
||||
ctx.push(right);
|
||||
} else {
|
||||
|
@ -435,13 +435,8 @@ impl GlyphFragment {
|
||||
}
|
||||
|
||||
/// Try to stretch a glyph to a desired height.
|
||||
pub fn stretch_vertical(
|
||||
self,
|
||||
ctx: &mut MathContext,
|
||||
height: Abs,
|
||||
short_fall: Abs,
|
||||
) -> VariantFragment {
|
||||
stretch_glyph(ctx, self, height, short_fall, Axis::Y)
|
||||
pub fn stretch_vertical(self, ctx: &mut MathContext, height: Abs) -> VariantFragment {
|
||||
stretch_glyph(ctx, self, height, Axis::Y)
|
||||
}
|
||||
|
||||
/// Try to stretch a glyph to a desired width.
|
||||
@ -449,9 +444,8 @@ impl GlyphFragment {
|
||||
self,
|
||||
ctx: &mut MathContext,
|
||||
width: Abs,
|
||||
short_fall: Abs,
|
||||
) -> VariantFragment {
|
||||
stretch_glyph(ctx, self, width, short_fall, Axis::X)
|
||||
stretch_glyph(ctx, self, width, Axis::X)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -314,7 +314,7 @@ fn layout_delimiters(
|
||||
|
||||
if let Some(left) = left {
|
||||
let mut left = GlyphFragment::new(ctx, styles, left, span)
|
||||
.stretch_vertical(ctx, target, short_fall);
|
||||
.stretch_vertical(ctx, target - short_fall);
|
||||
left.align_on_axis(ctx, delimiter_alignment(left.c));
|
||||
ctx.push(left);
|
||||
}
|
||||
@ -323,7 +323,7 @@ fn layout_delimiters(
|
||||
|
||||
if let Some(right) = right {
|
||||
let mut right = GlyphFragment::new(ctx, styles, right, span)
|
||||
.stretch_vertical(ctx, target, short_fall);
|
||||
.stretch_vertical(ctx, target - short_fall);
|
||||
right.align_on_axis(ctx, delimiter_alignment(right.c));
|
||||
ctx.push(right);
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ pub fn layout_root(
|
||||
// Layout root symbol.
|
||||
let target = radicand.height() + thickness + gap;
|
||||
let sqrt = GlyphFragment::new(ctx, styles, '√', span)
|
||||
.stretch_vertical(ctx, target, Abs::zero())
|
||||
.stretch_vertical(ctx, target)
|
||||
.frame;
|
||||
|
||||
// Layout the index.
|
||||
|
@ -67,8 +67,7 @@ pub fn stretch_fragment(
|
||||
let mut variant = stretch_glyph(
|
||||
ctx,
|
||||
glyph,
|
||||
stretch.relative_to(relative_to_size),
|
||||
short_fall,
|
||||
stretch.relative_to(relative_to_size) - short_fall,
|
||||
axis,
|
||||
);
|
||||
|
||||
@ -120,7 +119,6 @@ pub fn stretch_glyph(
|
||||
ctx: &mut MathContext,
|
||||
mut base: GlyphFragment,
|
||||
target: Abs,
|
||||
short_fall: Abs,
|
||||
axis: Axis,
|
||||
) -> VariantFragment {
|
||||
// If the base glyph is good enough, use it.
|
||||
@ -128,8 +126,7 @@ pub fn stretch_glyph(
|
||||
Axis::X => base.width,
|
||||
Axis::Y => base.height(),
|
||||
};
|
||||
let short_target = target - short_fall;
|
||||
if short_target <= advance {
|
||||
if target <= advance {
|
||||
return base.into_variant();
|
||||
}
|
||||
|
||||
@ -153,13 +150,13 @@ pub fn stretch_glyph(
|
||||
for variant in construction.variants {
|
||||
best_id = variant.variant_glyph;
|
||||
best_advance = base.font.to_em(variant.advance_measurement).at(base.font_size);
|
||||
if short_target <= best_advance {
|
||||
if target <= best_advance {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// This is either good or the best we've got.
|
||||
if short_target <= best_advance || construction.assembly.is_none() {
|
||||
if target <= best_advance || construction.assembly.is_none() {
|
||||
base.set_id(ctx, best_id);
|
||||
return base.into_variant();
|
||||
}
|
||||
|
@ -65,19 +65,14 @@ fn layout_inline_text(
|
||||
// Small optimization for numbers. Note that this lays out slightly
|
||||
// differently to normal text and is worth re-evaluating in the future.
|
||||
let mut fragments = vec![];
|
||||
let is_single = text.chars().count() == 1;
|
||||
for unstyled_c in text.chars() {
|
||||
let c = styled_char(styles, unstyled_c, false);
|
||||
let mut glyph = GlyphFragment::new(ctx, styles, c, span);
|
||||
if is_single {
|
||||
// Duplicate what `layout_glyph` does exactly even if it's
|
||||
// probably incorrect here.
|
||||
match EquationElem::size_in(styles) {
|
||||
MathSize::Script => glyph.make_script_size(ctx),
|
||||
MathSize::ScriptScript => glyph.make_script_script_size(ctx),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
fragments.push(glyph.into());
|
||||
}
|
||||
let frame = MathRun::new(fragments).into_frame(styles);
|
||||
@ -164,7 +159,7 @@ fn layout_glyph(
|
||||
let mut variant = if math_size == MathSize::Display {
|
||||
let height = scaled!(ctx, styles, display_operator_min_height)
|
||||
.max(SQRT_2 * glyph.height());
|
||||
glyph.stretch_vertical(ctx, height, Abs::zero())
|
||||
glyph.stretch_vertical(ctx, height)
|
||||
} else {
|
||||
glyph.into_variant()
|
||||
};
|
||||
|
@ -286,7 +286,7 @@ fn layout_underoverspreader(
|
||||
let body_class = body.class();
|
||||
let body = body.into_fragment(styles);
|
||||
let glyph = GlyphFragment::new(ctx, styles, c, span);
|
||||
let stretched = glyph.stretch_horizontal(ctx, body.width(), Abs::zero());
|
||||
let stretched = glyph.stretch_horizontal(ctx, body.width());
|
||||
|
||||
let mut rows = vec![];
|
||||
let baseline = match position {
|
||||
|
@ -247,8 +247,9 @@ impl<'s> SmartQuotes<'s> {
|
||||
"es" if matches!(region, Some("ES") | None) => ("“", "”", "«", "»"),
|
||||
"hu" | "pl" | "ro" => ("’", "’", "„", "”"),
|
||||
"no" | "nb" | "nn" if alternative => low_high,
|
||||
"no" | "nb" | "nn" | "uk" => ("’", "’", "«", "»"),
|
||||
"no" | "nb" | "nn" => ("’", "’", "«", "»"),
|
||||
"ru" => ("„", "“", "«", "»"),
|
||||
"uk" => ("“", "”", "«", "»"),
|
||||
"el" => ("‘", "’", "«", "»"),
|
||||
"he" => ("’", "’", "”", "”"),
|
||||
"hr" => ("‘", "’", "„", "”"),
|
||||
|
@ -77,8 +77,8 @@ pub struct ImageElem {
|
||||
/// [`source`]($image.source) (even then, Typst will try to figure out the
|
||||
/// format automatically, but that's not always possible).
|
||||
///
|
||||
/// Supported formats are `{"png"}`, `{"jpg"}`, `{"gif"}`, `{"svg"}` as well
|
||||
/// as raw pixel data. Embedding PDFs as images is
|
||||
/// Supported formats are `{"png"}`, `{"jpg"}`, `{"gif"}`, `{"svg"}`,
|
||||
/// `{"webp"}` as well as raw pixel data. Embedding PDFs as images is
|
||||
/// [not currently supported](https://github.com/typst/typst/issues/145).
|
||||
///
|
||||
/// When providing raw pixel data as the `source`, you must specify a
|
||||
|
@ -9,6 +9,7 @@ use ecow::{eco_format, EcoString};
|
||||
use image::codecs::gif::GifDecoder;
|
||||
use image::codecs::jpeg::JpegDecoder;
|
||||
use image::codecs::png::PngDecoder;
|
||||
use image::codecs::webp::WebPDecoder;
|
||||
use image::{
|
||||
guess_format, DynamicImage, ImageBuffer, ImageDecoder, ImageResult, Limits, Pixel,
|
||||
};
|
||||
@ -77,6 +78,7 @@ impl RasterImage {
|
||||
ExchangeFormat::Jpg => decode(JpegDecoder::new(cursor), icc),
|
||||
ExchangeFormat::Png => decode(PngDecoder::new(cursor), icc),
|
||||
ExchangeFormat::Gif => decode(GifDecoder::new(cursor), icc),
|
||||
ExchangeFormat::Webp => decode(WebPDecoder::new(cursor), icc),
|
||||
}
|
||||
.map_err(format_image_error)?;
|
||||
|
||||
@ -242,6 +244,8 @@ pub enum ExchangeFormat {
|
||||
/// Raster format that is typically used for short animated clips. Typst can
|
||||
/// load GIFs, but they will become static.
|
||||
Gif,
|
||||
/// Raster format that supports both lossy and lossless compression.
|
||||
Webp,
|
||||
}
|
||||
|
||||
impl ExchangeFormat {
|
||||
@ -257,6 +261,7 @@ impl From<ExchangeFormat> for image::ImageFormat {
|
||||
ExchangeFormat::Png => image::ImageFormat::Png,
|
||||
ExchangeFormat::Jpg => image::ImageFormat::Jpeg,
|
||||
ExchangeFormat::Gif => image::ImageFormat::Gif,
|
||||
ExchangeFormat::Webp => image::ImageFormat::WebP,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -269,6 +274,7 @@ impl TryFrom<image::ImageFormat> for ExchangeFormat {
|
||||
image::ImageFormat::Png => ExchangeFormat::Png,
|
||||
image::ImageFormat::Jpeg => ExchangeFormat::Jpg,
|
||||
image::ImageFormat::Gif => ExchangeFormat::Gif,
|
||||
image::ImageFormat::WebP => ExchangeFormat::Webp,
|
||||
_ => bail!("format not yet supported"),
|
||||
})
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ pub fn convert_image_to_base64_url(image: &Image) -> EcoString {
|
||||
ExchangeFormat::Png => "png",
|
||||
ExchangeFormat::Jpg => "jpeg",
|
||||
ExchangeFormat::Gif => "gif",
|
||||
ExchangeFormat::Webp => "webp",
|
||||
},
|
||||
raster.data(),
|
||||
),
|
||||
|
@ -69,7 +69,7 @@ the first item of the list above by indenting it.
|
||||
|
||||
## Adding a figure { #figure }
|
||||
You think that your report would benefit from a figure. Let's add one. Typst
|
||||
supports images in the formats PNG, JPEG, GIF, and SVG. To add an image file to
|
||||
supports images in the formats PNG, JPEG, GIF, SVG, and WebP. To add an image file to
|
||||
your project, first open the _file panel_ by clicking the box icon in the left
|
||||
sidebar. Here, you can see a list of all files in your project. Currently, there
|
||||
is only one: The main Typst file you are writing in. To upload another file,
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 465 B After Width: | Height: | Size: 461 B |
Before Width: | Height: | Size: 644 B After Width: | Height: | Size: 716 B |
Before Width: | Height: | Size: 572 B After Width: | Height: | Size: 567 B |
Before Width: | Height: | Size: 359 B After Width: | Height: | Size: 351 B |
Before Width: | Height: | Size: 510 B After Width: | Height: | Size: 506 B |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 670 B After Width: | Height: | Size: 687 B |
Before Width: | Height: | Size: 340 B After Width: | Height: | Size: 354 B |
Before Width: | Height: | Size: 506 B After Width: | Height: | Size: 492 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 927 B |
Before Width: | Height: | Size: 989 B After Width: | Height: | Size: 903 B |
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 976 B After Width: | Height: | Size: 875 B |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 954 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 818 B After Width: | Height: | Size: 816 B |
Before Width: | Height: | Size: 496 B After Width: | Height: | Size: 526 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 651 B After Width: | Height: | Size: 648 B |
Before Width: | Height: | Size: 882 B After Width: | Height: | Size: 956 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 902 B After Width: | Height: | Size: 897 B |
Before Width: | Height: | Size: 648 B After Width: | Height: | Size: 640 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 927 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 420 B After Width: | Height: | Size: 436 B |
Before Width: | Height: | Size: 651 B After Width: | Height: | Size: 648 B |
Before Width: | Height: | Size: 620 B After Width: | Height: | Size: 630 B |
BIN
tests/ref/smartquote-uk.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
@ -46,6 +46,10 @@
|
||||
#set text(lang: "ru")
|
||||
"Лошадь не ест салат из огурцов" - это была первая фраза, сказанная по 'телефону'.
|
||||
|
||||
--- smartquote-uk ---
|
||||
#set text(lang: "uk")
|
||||
"Кінь не їсть огірковий салат" — перше речення, коли-небудь вимовлене по 'телефону'.
|
||||
|
||||
--- smartquote-it ---
|
||||
#set text(lang: "it")
|
||||
"Il cavallo non mangia insalata di cetrioli" è stata la prima frase pronunciata al 'telefono'.
|
||||
|
@ -243,7 +243,7 @@ A #box(image("/assets/images/tiger.jpg", height: 1cm, width: 80%)) B
|
||||
--- image-png-but-pixmap-format ---
|
||||
#image(
|
||||
read("/assets/images/tiger.jpg", encoding: none),
|
||||
// Error: 11-18 expected "png", "jpg", "gif", dictionary, "svg", or auto
|
||||
// Error: 11-18 expected "png", "jpg", "gif", "webp", dictionary, "svg", or auto
|
||||
format: "rgba8",
|
||||
)
|
||||
|
||||
|