diff options
| author | Laurenz <laurmaedje@gmail.com> | 2024-10-10 13:59:00 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-10 11:59:00 +0000 |
| commit | 6257e4d6cf060f367c3f2eb2d005a091b6432e88 (patch) | |
| tree | 9ae46a48c3b3d4c90d3cfc9da77df5951e942fa4 /crates/typst-pdf/src/content.rs | |
| parent | 9ee80762a55c0d173664d9ac33cb762b79341a96 (diff) | |
More robust glyph drawing (#5159)
Diffstat (limited to 'crates/typst-pdf/src/content.rs')
| -rw-r--r-- | crates/typst-pdf/src/content.rs | 85 |
1 files changed, 43 insertions, 42 deletions
diff --git a/crates/typst-pdf/src/content.rs b/crates/typst-pdf/src/content.rs index 79323b6a..babb3e57 100644 --- a/crates/typst-pdf/src/content.rs +++ b/crates/typst-pdf/src/content.rs @@ -10,15 +10,15 @@ use pdf_writer::types::{ }; use pdf_writer::writers::PositionedItems; use pdf_writer::{Content, Finish, Name, Rect, Str}; -use typst::diag::{bail, SourceResult}; +use typst::diag::{bail, error, SourceDiagnostic, SourceResult}; use typst::foundations::Repr; use typst::layout::{ Abs, Em, Frame, FrameItem, GroupItem, Point, Ratio, Size, Transform, }; use typst::model::Destination; use typst::syntax::Span; -use typst::text::color::is_color_glyph; -use typst::text::{Font, TextItem, TextItemView}; +use typst::text::color::should_outline; +use typst::text::{Font, Glyph, TextItem, TextItemView}; use typst::utils::{Deferred, Numeric, SliceExt}; use typst::visualize::{ FillRule, FixedStroke, Geometry, Image, LineCap, LineJoin, Paint, Path, PathItem, @@ -418,46 +418,27 @@ fn write_group(ctx: &mut Builder, pos: Point, group: &GroupItem) -> SourceResult /// Encode a text run into the content stream. fn write_text(ctx: &mut Builder, pos: Point, text: &TextItem) -> SourceResult<()> { - if ctx.options.standards.pdfa { - let last_resort = text.font.info().is_last_resort(); - for g in &text.glyphs { - if last_resort || g.id == 0 { - bail!( - g.span.0, - "the text {} could not be displayed with any font", - TextItemView::full(text).glyph_text(g).repr(), - ); - } - } - } - - let ttf = text.font.ttf(); - let tables = ttf.tables(); - - // If the text run contains either only color glyphs (used for emojis for - // example) or normal text we can render it directly - let has_color_glyphs = tables.sbix.is_some() - || tables.cbdt.is_some() - || tables.svg.is_some() - || tables.colr.is_some(); - if !has_color_glyphs { - write_normal_text(ctx, pos, TextItemView::full(text))?; - return Ok(()); + if ctx.options.standards.pdfa && text.font.info().is_last_resort() { + bail!( + Span::find(text.glyphs.iter().map(|g| g.span.0)), + "the text {} could not be displayed with any font", + &text.text, + ); } - let color_glyph_count = - text.glyphs.iter().filter(|g| is_color_glyph(&text.font, g)).count(); + let outline_glyphs = + text.glyphs.iter().filter(|g| should_outline(&text.font, g)).count(); - if color_glyph_count == text.glyphs.len() { - write_color_glyphs(ctx, pos, TextItemView::full(text))?; - } else if color_glyph_count == 0 { + if outline_glyphs == text.glyphs.len() { write_normal_text(ctx, pos, TextItemView::full(text))?; + } else if outline_glyphs == 0 { + write_complex_glyphs(ctx, pos, TextItemView::full(text))?; } else { - // Otherwise we need to split it in smaller text runs + // Otherwise we need to split it into smaller text runs. let mut offset = 0; let mut position_in_run = Abs::zero(); - for (color, sub_run) in - text.glyphs.group_by_key(|g| is_color_glyph(&text.font, g)) + for (should_outline, sub_run) in + text.glyphs.group_by_key(|g| should_outline(&text.font, g)) { let end = offset + sub_run.len(); @@ -468,11 +449,12 @@ fn write_text(ctx: &mut Builder, pos: Point, text: &TextItem) -> SourceResult<() let pos = pos + Point::new(position_in_run, Abs::zero()); position_in_run += text_item_view.width(); offset = end; - // Actually write the sub text-run - if color { - write_color_glyphs(ctx, pos, text_item_view)?; - } else { + + // Actually write the sub text-run. + if should_outline { write_normal_text(ctx, pos, text_item_view)?; + } else { + write_complex_glyphs(ctx, pos, text_item_view)?; } } } @@ -534,6 +516,10 @@ fn write_normal_text( // Write the glyphs with kerning adjustments. for glyph in text.glyphs() { + if ctx.options.standards.pdfa && glyph.id == 0 { + bail!(tofu(&text, glyph)); + } + adjustment += glyph.x_offset; if !adjustment.is_zero() { @@ -596,7 +582,7 @@ fn show_text(items: &mut PositionedItems, encoded: &[u8]) { } /// Encodes a text run made only of color glyphs into the content stream -fn write_color_glyphs( +fn write_complex_glyphs( ctx: &mut Builder, pos: Point, text: TextItemView, @@ -621,12 +607,17 @@ fn write_color_glyphs( .or_default(); for glyph in text.glyphs() { + if ctx.options.standards.pdfa && glyph.id == 0 { + bail!(tofu(&text, glyph)); + } + // Retrieve the Type3 font reference and the glyph index in the font. let color_fonts = ctx .resources .color_fonts .get_or_insert_with(|| Box::new(ColorFontMap::new())); - let (font, index) = color_fonts.get(ctx.options, &text.item.font, glyph.id)?; + + let (font, index) = color_fonts.get(ctx.options, &text, glyph)?; if last_font != Some(font) { ctx.content.set_font( @@ -824,3 +815,13 @@ fn to_pdf_line_join(join: LineJoin) -> LineJoinStyle { LineJoin::Bevel => LineJoinStyle::BevelJoin, } } + +/// The error when there is a tofu glyph. +#[cold] +fn tofu(text: &TextItemView, glyph: &Glyph) -> SourceDiagnostic { + error!( + glyph.span.0, + "the text {} could not be displayed with any font", + text.glyph_text(glyph).repr(), + ) +} |
