From 73615f7e3ce23f2ea656d04ea9f96184f5ebdc0a Mon Sep 17 00:00:00 2001 From: Laurenz Date: Wed, 24 Mar 2021 17:12:34 +0100 Subject: =?UTF-8?q?Text=20shaping=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Shapes text with rustybuzz - Font fallback with family list - Tofus are shown in the first font Co-Authored-By: Martin --- tests/ref/control/for.png | Bin 2723 -> 2715 bytes tests/ref/control/if.png | Bin 1637 -> 1644 bytes tests/ref/control/let.png | Bin 1961 -> 1974 bytes tests/ref/expr/call.png | Bin 5991 -> 6000 bytes tests/ref/expr/ops.png | Bin 776 -> 770 bytes tests/ref/full/coma.png | Bin 59286 -> 59402 bytes tests/ref/library/circle.png | Bin 13617 -> 13642 bytes tests/ref/library/ellipse.png | Bin 7638 -> 7588 bytes tests/ref/library/font.png | Bin 21246 -> 21305 bytes tests/ref/library/pad.png | Bin 1226 -> 1224 bytes tests/ref/library/page.png | Bin 8290 -> 8180 bytes tests/ref/library/pagebreak.png | Bin 1357 -> 1360 bytes tests/ref/library/paragraph.png | Bin 4170 -> 4162 bytes tests/ref/library/rect.png | Bin 2769 -> 2760 bytes tests/ref/library/spacing.png | Bin 3833 -> 3845 bytes tests/ref/library/square.png | Bin 7166 -> 7184 bytes tests/ref/markup/emph.png | Bin 3387 -> 3372 bytes tests/ref/markup/escape.png | Bin 6530 -> 6517 bytes tests/ref/markup/heading.png | Bin 5205 -> 5177 bytes tests/ref/markup/linebreak.png | Bin 4308 -> 4292 bytes tests/ref/markup/nbsp.png | Bin 1763 -> 1769 bytes tests/ref/markup/parbreak.png | Bin 1654 -> 1682 bytes tests/ref/markup/raw.png | Bin 8299 -> 8261 bytes tests/ref/markup/strong.png | Bin 3374 -> 3385 bytes tests/ref/repr.png | Bin 10828 -> 10833 bytes tests/ref/text.png | Bin 36910 -> 0 bytes tests/ref/text/basic.png | Bin 0 -> 37041 bytes tests/ref/text/complex.png | Bin 0 -> 12273 bytes tests/typ/text.typ | 19 -------------- tests/typ/text/basic.typ | 19 ++++++++++++++ tests/typ/text/complex.typ | 38 +++++++++++++++++++++++++++ tests/typeset.rs | 55 ++++++++++++++++------------------------ 32 files changed, 79 insertions(+), 52 deletions(-) delete mode 100644 tests/ref/text.png create mode 100644 tests/ref/text/basic.png create mode 100644 tests/ref/text/complex.png delete mode 100644 tests/typ/text.typ create mode 100644 tests/typ/text/basic.typ create mode 100644 tests/typ/text/complex.typ (limited to 'tests') diff --git a/tests/ref/control/for.png b/tests/ref/control/for.png index b3757692..1c7dfd42 100644 Binary files a/tests/ref/control/for.png and b/tests/ref/control/for.png differ diff --git a/tests/ref/control/if.png b/tests/ref/control/if.png index e38cf7c4..b30e63f8 100644 Binary files a/tests/ref/control/if.png and b/tests/ref/control/if.png differ diff --git a/tests/ref/control/let.png b/tests/ref/control/let.png index 20aad953..8a5c5fc6 100644 Binary files a/tests/ref/control/let.png and b/tests/ref/control/let.png differ diff --git a/tests/ref/expr/call.png b/tests/ref/expr/call.png index 26f1cb03..f05fb835 100644 Binary files a/tests/ref/expr/call.png and b/tests/ref/expr/call.png differ diff --git a/tests/ref/expr/ops.png b/tests/ref/expr/ops.png index 2f3a28ea..e6f2edab 100644 Binary files a/tests/ref/expr/ops.png and b/tests/ref/expr/ops.png differ diff --git a/tests/ref/full/coma.png b/tests/ref/full/coma.png index 5fd6d801..d869d57e 100644 Binary files a/tests/ref/full/coma.png and b/tests/ref/full/coma.png differ diff --git a/tests/ref/library/circle.png b/tests/ref/library/circle.png index 9e89d98b..f1128642 100644 Binary files a/tests/ref/library/circle.png and b/tests/ref/library/circle.png differ diff --git a/tests/ref/library/ellipse.png b/tests/ref/library/ellipse.png index de178d60..c78c9272 100644 Binary files a/tests/ref/library/ellipse.png and b/tests/ref/library/ellipse.png differ diff --git a/tests/ref/library/font.png b/tests/ref/library/font.png index 57a45006..07c65726 100644 Binary files a/tests/ref/library/font.png and b/tests/ref/library/font.png differ diff --git a/tests/ref/library/pad.png b/tests/ref/library/pad.png index 3536db5d..3bc0ddd0 100644 Binary files a/tests/ref/library/pad.png and b/tests/ref/library/pad.png differ diff --git a/tests/ref/library/page.png b/tests/ref/library/page.png index 7d1ff96f..8e5a83ff 100644 Binary files a/tests/ref/library/page.png and b/tests/ref/library/page.png differ diff --git a/tests/ref/library/pagebreak.png b/tests/ref/library/pagebreak.png index d513c963..ab990c69 100644 Binary files a/tests/ref/library/pagebreak.png and b/tests/ref/library/pagebreak.png differ diff --git a/tests/ref/library/paragraph.png b/tests/ref/library/paragraph.png index bf38bdf8..37898017 100644 Binary files a/tests/ref/library/paragraph.png and b/tests/ref/library/paragraph.png differ diff --git a/tests/ref/library/rect.png b/tests/ref/library/rect.png index 81ee91d7..56f1003f 100644 Binary files a/tests/ref/library/rect.png and b/tests/ref/library/rect.png differ diff --git a/tests/ref/library/spacing.png b/tests/ref/library/spacing.png index 6a234a8a..c266b9fa 100644 Binary files a/tests/ref/library/spacing.png and b/tests/ref/library/spacing.png differ diff --git a/tests/ref/library/square.png b/tests/ref/library/square.png index 401b1ab2..26469d20 100644 Binary files a/tests/ref/library/square.png and b/tests/ref/library/square.png differ diff --git a/tests/ref/markup/emph.png b/tests/ref/markup/emph.png index f43eeecb..9b6bda5c 100644 Binary files a/tests/ref/markup/emph.png and b/tests/ref/markup/emph.png differ diff --git a/tests/ref/markup/escape.png b/tests/ref/markup/escape.png index 561b5f1e..9c6f1f59 100644 Binary files a/tests/ref/markup/escape.png and b/tests/ref/markup/escape.png differ diff --git a/tests/ref/markup/heading.png b/tests/ref/markup/heading.png index f95b8c2c..a32229e7 100644 Binary files a/tests/ref/markup/heading.png and b/tests/ref/markup/heading.png differ diff --git a/tests/ref/markup/linebreak.png b/tests/ref/markup/linebreak.png index 4dfca22f..512fa0f5 100644 Binary files a/tests/ref/markup/linebreak.png and b/tests/ref/markup/linebreak.png differ diff --git a/tests/ref/markup/nbsp.png b/tests/ref/markup/nbsp.png index 89f75f14..cc920776 100644 Binary files a/tests/ref/markup/nbsp.png and b/tests/ref/markup/nbsp.png differ diff --git a/tests/ref/markup/parbreak.png b/tests/ref/markup/parbreak.png index 008afca2..f100b9d7 100644 Binary files a/tests/ref/markup/parbreak.png and b/tests/ref/markup/parbreak.png differ diff --git a/tests/ref/markup/raw.png b/tests/ref/markup/raw.png index 1aebc02d..a20ca999 100644 Binary files a/tests/ref/markup/raw.png and b/tests/ref/markup/raw.png differ diff --git a/tests/ref/markup/strong.png b/tests/ref/markup/strong.png index 20e29b1f..4bbf6ac0 100644 Binary files a/tests/ref/markup/strong.png and b/tests/ref/markup/strong.png differ diff --git a/tests/ref/repr.png b/tests/ref/repr.png index 390c2cab..71f415ef 100644 Binary files a/tests/ref/repr.png and b/tests/ref/repr.png differ diff --git a/tests/ref/text.png b/tests/ref/text.png deleted file mode 100644 index 1dd70c1c..00000000 Binary files a/tests/ref/text.png and /dev/null differ diff --git a/tests/ref/text/basic.png b/tests/ref/text/basic.png new file mode 100644 index 00000000..a06c8763 Binary files /dev/null and b/tests/ref/text/basic.png differ diff --git a/tests/ref/text/complex.png b/tests/ref/text/complex.png new file mode 100644 index 00000000..9af49f16 Binary files /dev/null and b/tests/ref/text/complex.png differ diff --git a/tests/typ/text.typ b/tests/typ/text.typ deleted file mode 100644 index b424cee8..00000000 --- a/tests/typ/text.typ +++ /dev/null @@ -1,19 +0,0 @@ -// Test simple text. - -#page(width: 250pt) - -But, soft! what light through yonder window breaks? It is the east, and Juliet -is the sun. Arise, fair sun, and kill the envious moon, Who is already sick and -pale with grief, That thou her maid art far more fair than she: Be not her maid, -since she is envious; Her vestal livery is but sick and green And none but fools -do wear it; cast it off. It is my lady, O, it is my love! O, that she knew she -were! She speaks yet she says nothing: what of that? Her eye discourses; I will -answer it. - -I am too bold, 'tis not to me she speaks: Two of the fairest stars in all the -heaven, Having some business, do entreat her eyes To twinkle in their spheres -till they return. What if her eyes were there, they in her head? The brightness -of her cheek would shame those stars, As daylight doth a lamp; her eyes in -heaven Would through the airy region stream so bright That birds would sing and -think it were not night. See, how she leans her cheek upon her hand! O, that I -were a glove upon that hand, That I might touch that cheek! diff --git a/tests/typ/text/basic.typ b/tests/typ/text/basic.typ new file mode 100644 index 00000000..b424cee8 --- /dev/null +++ b/tests/typ/text/basic.typ @@ -0,0 +1,19 @@ +// Test simple text. + +#page(width: 250pt) + +But, soft! what light through yonder window breaks? It is the east, and Juliet +is the sun. Arise, fair sun, and kill the envious moon, Who is already sick and +pale with grief, That thou her maid art far more fair than she: Be not her maid, +since she is envious; Her vestal livery is but sick and green And none but fools +do wear it; cast it off. It is my lady, O, it is my love! O, that she knew she +were! She speaks yet she says nothing: what of that? Her eye discourses; I will +answer it. + +I am too bold, 'tis not to me she speaks: Two of the fairest stars in all the +heaven, Having some business, do entreat her eyes To twinkle in their spheres +till they return. What if her eyes were there, they in her head? The brightness +of her cheek would shame those stars, As daylight doth a lamp; her eyes in +heaven Would through the airy region stream so bright That birds would sing and +think it were not night. See, how she leans her cheek upon her hand! O, that I +were a glove upon that hand, That I might touch that cheek! diff --git a/tests/typ/text/complex.typ b/tests/typ/text/complex.typ new file mode 100644 index 00000000..567a208d --- /dev/null +++ b/tests/typ/text/complex.typ @@ -0,0 +1,38 @@ +// Test complex text shaping. + +--- +// Test ligatures. + +// This should create an "fi" ligature. +Le fira + +// This should just shape nicely. +#font("Noto Sans Arabic") +منش إلا بسم الله + +// This should form a three-member family. +#font("Twitter Color Emoji") +👩‍👩‍👦 🤚🏿 + +// These two shouldn't be affected by a zero-width joiner. +🏞‍🌋 + +--- +// Test font fallback. + +#font("EB Garamond", "Noto Sans Arabic", "Twitter Color Emoji") + +// Font fallback for emoji. +A😀B + +// Font fallback for entire text. +منش إلا بسم الله + +// Font fallback in right-to-left text. +ب🐈😀سم + +// Multi-layer font fallback. +Aب😀🏞سمB + +// Tofus are rendered with the first font. +A🐈中文B diff --git a/tests/typeset.rs b/tests/typeset.rs index c5ec01d2..b38311aa 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -21,7 +21,7 @@ use typst::eval::{EvalContext, FuncArgs, FuncValue, Scope, Value}; use typst::exec::State; use typst::export::pdf; use typst::geom::{self, Length, Point, Sides, Size}; -use typst::layout::{Element, Fill, Frame, Geometry, Image, Shape, Shaped}; +use typst::layout::{Element, Fill, Frame, Geometry, Image, Shape, ShapedText}; use typst::library; use typst::parse::{LineMap, Scanner}; use typst::pretty::pretty; @@ -391,15 +391,18 @@ fn draw(env: &Env, frames: &[Frame], pixel_per_pt: f32) -> Pixmap { for &(pos, ref element) in &frame.elements { let pos = origin + pos; + let x = pos.x.to_pt() as f32; + let y = pos.y.to_pt() as f32; + let ts = ts.pre_translate(x, y); match element { Element::Text(shaped) => { - draw_text(&mut canvas, env, ts, pos, shaped); + draw_text(&mut canvas, env, ts, shaped); } Element::Image(image) => { - draw_image(&mut canvas, env, ts, pos, image); + draw_image(&mut canvas, env, ts, image); } Element::Geometry(geom) => { - draw_geometry(&mut canvas, ts, pos, geom); + draw_geometry(&mut canvas, ts, geom); } } } @@ -410,18 +413,18 @@ fn draw(env: &Env, frames: &[Frame], pixel_per_pt: f32) -> Pixmap { canvas } -fn draw_text(canvas: &mut Pixmap, env: &Env, ts: Transform, pos: Point, shaped: &Shaped) { - let face = env.fonts.face(shaped.face).get(); +fn draw_text(canvas: &mut Pixmap, env: &Env, ts: Transform, shaped: &ShapedText) { + let ttf = env.fonts.face(shaped.face).ttf(); for (&glyph, &offset) in shaped.glyphs.iter().zip(&shaped.offsets) { - let units_per_em = face.units_per_em().unwrap_or(1000); + let units_per_em = ttf.units_per_em().unwrap_or(1000); - let x = (pos.x + offset).to_pt() as f32; - let y = pos.y.to_pt() as f32; - let scale = (shaped.font_size / units_per_em as f64).to_pt() as f32; + let x = offset.to_pt() as f32; + let s = (shaped.size / units_per_em as f64).to_pt() as f32; + let ts = ts.pre_translate(x, 0.0); // Try drawing SVG if present. - if let Some(tree) = face + if let Some(tree) = ttf .glyph_svg_image(glyph) .and_then(|data| std::str::from_utf8(data).ok()) .map(|svg| { @@ -433,11 +436,9 @@ fn draw_text(canvas: &mut Pixmap, env: &Env, ts: Transform, pos: Point, shaped: for child in tree.root().children() { if let usvg::NodeKind::Path(node) = &*child.borrow() { let path = convert_usvg_path(&node.data); - let transform = convert_usvg_transform(node.transform); - let ts = transform - .post_concat(Transform::from_row(scale, 0.0, 0.0, scale, x, y)) + let ts = convert_usvg_transform(node.transform) + .post_scale(s, s) .post_concat(ts); - if let Some(fill) = &node.fill { let (paint, fill_rule) = convert_usvg_fill(fill); canvas.fill_path(&path, &paint, fill_rule, ts, None); @@ -450,9 +451,9 @@ fn draw_text(canvas: &mut Pixmap, env: &Env, ts: Transform, pos: Point, shaped: // Otherwise, draw normal outline. let mut builder = WrappedPathBuilder(tiny_skia::PathBuilder::new()); - if face.outline_glyph(glyph, &mut builder).is_some() { + if ttf.outline_glyph(glyph, &mut builder).is_some() { let path = builder.0.finish().unwrap(); - let ts = Transform::from_row(scale, 0.0, 0.0, -scale, x, y).post_concat(ts); + let ts = ts.pre_scale(s, -s); let mut paint = convert_typst_fill(shaped.color); paint.anti_alias = true; canvas.fill_path(&path, &paint, FillRule::default(), ts, None); @@ -460,11 +461,7 @@ fn draw_text(canvas: &mut Pixmap, env: &Env, ts: Transform, pos: Point, shaped: } } -fn draw_geometry(canvas: &mut Pixmap, ts: Transform, pos: Point, element: &Geometry) { - let x = pos.x.to_pt() as f32; - let y = pos.y.to_pt() as f32; - let ts = Transform::from_translate(x, y).post_concat(ts); - +fn draw_geometry(canvas: &mut Pixmap, ts: Transform, element: &Geometry) { let paint = convert_typst_fill(element.fill); let rule = FillRule::default(); @@ -486,13 +483,7 @@ fn draw_geometry(canvas: &mut Pixmap, ts: Transform, pos: Point, element: &Geome }; } -fn draw_image( - canvas: &mut Pixmap, - env: &Env, - ts: Transform, - pos: Point, - element: &Image, -) { +fn draw_image(canvas: &mut Pixmap, env: &Env, ts: Transform, element: &Image) { let img = &env.resources.loaded::(element.res); let mut pixmap = Pixmap::new(img.buf.width(), img.buf.height()).unwrap(); @@ -501,8 +492,6 @@ fn draw_image( *dest = ColorU8::from_rgba(r, g, b, a).premultiply(); } - let x = pos.x.to_pt() as f32; - let y = pos.y.to_pt() as f32; let view_width = element.size.width.to_pt() as f32; let view_height = element.size.height.to_pt() as f32; let scale_x = view_width as f32 / pixmap.width() as f32; @@ -514,10 +503,10 @@ fn draw_image( SpreadMode::Pad, FilterQuality::Bilinear, 1.0, - Transform::from_row(scale_x, 0.0, 0.0, scale_y, x, y), + Transform::from_row(scale_x, 0.0, 0.0, scale_y, 0.0, 0.0), ); - let rect = Rect::from_xywh(x, y, view_width, view_height).unwrap(); + let rect = Rect::from_xywh(0.0, 0.0, view_width, view_height).unwrap(); canvas.fill_rect(rect, &paint, ts, None); } -- cgit v1.2.3