@@ -10,15 +10,15 @@ use pdf_writer::types::{
1010} ;
1111use pdf_writer:: writers:: PositionedItems ;
1212use pdf_writer:: { Content , Finish , Name , Rect , Str } ;
13- use typst:: diag:: { bail, SourceResult } ;
13+ use typst:: diag:: { bail, error , SourceDiagnostic , SourceResult } ;
1414use typst:: foundations:: Repr ;
1515use typst:: layout:: {
1616 Abs , Em , Frame , FrameItem , GroupItem , Point , Ratio , Size , Transform ,
1717} ;
1818use typst:: model:: Destination ;
1919use typst:: syntax:: Span ;
20- use typst:: text:: color:: is_color_glyph ;
21- use typst:: text:: { Font , TextItem , TextItemView } ;
20+ use typst:: text:: color:: should_outline ;
21+ use typst:: text:: { Font , Glyph , TextItem , TextItemView } ;
2222use typst:: utils:: { Deferred , Numeric , SliceExt } ;
2323use typst:: visualize:: {
2424 FillRule , FixedStroke , Geometry , Image , LineCap , LineJoin , Paint , Path , PathItem ,
@@ -418,46 +418,27 @@ fn write_group(ctx: &mut Builder, pos: Point, group: &GroupItem) -> SourceResult
418418
419419/// Encode a text run into the content stream.
420420fn write_text ( ctx : & mut Builder , pos : Point , text : & TextItem ) -> SourceResult < ( ) > {
421- if ctx. options . standards . pdfa {
422- let last_resort = text. font . info ( ) . is_last_resort ( ) ;
423- for g in & text. glyphs {
424- if last_resort || g. id == 0 {
425- bail ! (
426- g. span. 0 ,
427- "the text {} could not be displayed with any font" ,
428- TextItemView :: full( text) . glyph_text( g) . repr( ) ,
429- ) ;
430- }
431- }
432- }
433-
434- let ttf = text. font . ttf ( ) ;
435- let tables = ttf. tables ( ) ;
436-
437- // If the text run contains either only color glyphs (used for emojis for
438- // example) or normal text we can render it directly
439- let has_color_glyphs = tables. sbix . is_some ( )
440- || tables. cbdt . is_some ( )
441- || tables. svg . is_some ( )
442- || tables. colr . is_some ( ) ;
443- if !has_color_glyphs {
444- write_normal_text ( ctx, pos, TextItemView :: full ( text) ) ?;
445- return Ok ( ( ) ) ;
421+ if ctx. options . standards . pdfa && text. font . info ( ) . is_last_resort ( ) {
422+ bail ! (
423+ Span :: find( text. glyphs. iter( ) . map( |g| g. span. 0 ) ) ,
424+ "the text {} could not be displayed with any font" ,
425+ & text. text,
426+ ) ;
446427 }
447428
448- let color_glyph_count =
449- text. glyphs . iter ( ) . filter ( |g| is_color_glyph ( & text. font , g) ) . count ( ) ;
429+ let outline_glyphs =
430+ text. glyphs . iter ( ) . filter ( |g| should_outline ( & text. font , g) ) . count ( ) ;
450431
451- if color_glyph_count == text. glyphs . len ( ) {
452- write_color_glyphs ( ctx, pos, TextItemView :: full ( text) ) ?;
453- } else if color_glyph_count == 0 {
432+ if outline_glyphs == text. glyphs . len ( ) {
454433 write_normal_text ( ctx, pos, TextItemView :: full ( text) ) ?;
434+ } else if outline_glyphs == 0 {
435+ write_complex_glyphs ( ctx, pos, TextItemView :: full ( text) ) ?;
455436 } else {
456- // Otherwise we need to split it in smaller text runs
437+ // Otherwise we need to split it into smaller text runs.
457438 let mut offset = 0 ;
458439 let mut position_in_run = Abs :: zero ( ) ;
459- for ( color , sub_run) in
460- text. glyphs . group_by_key ( |g| is_color_glyph ( & text. font , g) )
440+ for ( should_outline , sub_run) in
441+ text. glyphs . group_by_key ( |g| should_outline ( & text. font , g) )
461442 {
462443 let end = offset + sub_run. len ( ) ;
463444
@@ -468,11 +449,12 @@ fn write_text(ctx: &mut Builder, pos: Point, text: &TextItem) -> SourceResult<()
468449 let pos = pos + Point :: new ( position_in_run, Abs :: zero ( ) ) ;
469450 position_in_run += text_item_view. width ( ) ;
470451 offset = end;
471- // Actually write the sub text-run
472- if color {
473- write_color_glyphs ( ctx, pos, text_item_view) ?;
474- } else {
452+
453+ // Actually write the sub text-run.
454+ if should_outline {
475455 write_normal_text ( ctx, pos, text_item_view) ?;
456+ } else {
457+ write_complex_glyphs ( ctx, pos, text_item_view) ?;
476458 }
477459 }
478460 }
@@ -534,6 +516,10 @@ fn write_normal_text(
534516
535517 // Write the glyphs with kerning adjustments.
536518 for glyph in text. glyphs ( ) {
519+ if ctx. options . standards . pdfa && glyph. id == 0 {
520+ bail ! ( tofu( & text, glyph) ) ;
521+ }
522+
537523 adjustment += glyph. x_offset ;
538524
539525 if !adjustment. is_zero ( ) {
@@ -596,7 +582,7 @@ fn show_text(items: &mut PositionedItems, encoded: &[u8]) {
596582}
597583
598584/// Encodes a text run made only of color glyphs into the content stream
599- fn write_color_glyphs (
585+ fn write_complex_glyphs (
600586 ctx : & mut Builder ,
601587 pos : Point ,
602588 text : TextItemView ,
@@ -621,12 +607,17 @@ fn write_color_glyphs(
621607 . or_default ( ) ;
622608
623609 for glyph in text. glyphs ( ) {
610+ if ctx. options . standards . pdfa && glyph. id == 0 {
611+ bail ! ( tofu( & text, glyph) ) ;
612+ }
613+
624614 // Retrieve the Type3 font reference and the glyph index in the font.
625615 let color_fonts = ctx
626616 . resources
627617 . color_fonts
628618 . get_or_insert_with ( || Box :: new ( ColorFontMap :: new ( ) ) ) ;
629- let ( font, index) = color_fonts. get ( ctx. options , & text. item . font , glyph. id ) ?;
619+
620+ let ( font, index) = color_fonts. get ( ctx. options , & text, glyph) ?;
630621
631622 if last_font != Some ( font) {
632623 ctx. content . set_font (
@@ -824,3 +815,13 @@ fn to_pdf_line_join(join: LineJoin) -> LineJoinStyle {
824815 LineJoin :: Bevel => LineJoinStyle :: BevelJoin ,
825816 }
826817}
818+
819+ /// The error when there is a tofu glyph.
820+ #[ cold]
821+ fn tofu ( text : & TextItemView , glyph : & Glyph ) -> SourceDiagnostic {
822+ error ! (
823+ glyph. span. 0 ,
824+ "the text {} could not be displayed with any font" ,
825+ text. glyph_text( glyph) . repr( ) ,
826+ )
827+ }
0 commit comments