diff --git a/crates/krilla/src/font/bitmap.rs b/crates/krilla/src/font/bitmap.rs index a295f70d..d411a377 100644 --- a/crates/krilla/src/font/bitmap.rs +++ b/crates/krilla/src/font/bitmap.rs @@ -451,7 +451,7 @@ mod tests { #[visreg(document, pdfium, mupdf, pdfbox, ghostscript, poppler, quartz)] fn noto_color_emoji_cbdt(document: &mut Document) { let font_data = NOTO_COLOR_EMOJI_CBDT.clone(); - all_glyphs_to_pdf(font_data, None, false, document); + all_glyphs_to_pdf(font_data, None, false, true, document); } #[cfg(target_os = "macos")] @@ -461,6 +461,6 @@ mod tests { let font_data = Arc::new(std::fs::read("/System/Library/Fonts/Apple Color Emoji.ttc").unwrap()); - all_glyphs_to_pdf(font_data, None, false, document); + all_glyphs_to_pdf(font_data, None, false, true, document); } } diff --git a/crates/krilla/src/font/colr.rs b/crates/krilla/src/font/colr.rs index 0bda36a6..a5ba3e5f 100644 --- a/crates/krilla/src/font/colr.rs +++ b/crates/krilla/src/font/colr.rs @@ -480,13 +480,13 @@ mod tests { .map(|n| (GlyphId::new(n), "".to_string())) .collect::>(); - all_glyphs_to_pdf(font_data, Some(glyphs), false, document); + all_glyphs_to_pdf(font_data, Some(glyphs), false, true, document); } #[visreg] fn colr_context_color(surface: &mut Surface) { let font_data = COLR_TEST_GLYPHS.clone(); - let font = Font::new(font_data, 0).unwrap(); + let font = Font::new(font_data, 0, true).unwrap(); let text = [ 0xf0b00, 0xf0b01, 0xf0b02, 0xf0b03, 0xf0b04, 0xf0b05, 0xf0b06, 0xf0b07, @@ -570,6 +570,6 @@ mod tests { #[visreg(document, pdfium, mupdf, pdfbox, ghostscript, poppler, quartz)] fn noto_color_emoji_colr(document: &mut Document) { let font_data = NOTO_COLOR_EMOJI_COLR.clone(); - all_glyphs_to_pdf(font_data, None, false, document); + all_glyphs_to_pdf(font_data, None, false, true, document); } } diff --git a/crates/krilla/src/font/mod.rs b/crates/krilla/src/font/mod.rs index 549e55a6..8169da61 100644 --- a/crates/krilla/src/font/mod.rs +++ b/crates/krilla/src/font/mod.rs @@ -51,14 +51,24 @@ pub use skrifa::GlyphId; pub struct Font(Arc>); impl Font { - /// Create a new font from some data. The `index` indicates the index that should be + /// Create a new font from some data. + /// + /// The `index` indicates the index that should be /// associated with this font for TrueType collections, otherwise this value should be /// set to 0. The location indicates the variation axes that should be associated with /// the font. /// + /// The `allow_color` property allows you to specify whether krilla should render the font + /// as a color font. When setting this property to false, krilla will always only use the + /// `glyf`/`CFF` tables of the font. If you don't know what this means, just set it to `true`. + /// /// Returns `None` if the index is invalid or the font couldn't be read. - pub fn new(data: Arc + Send + Sync>, index: u32) -> Option { - let font_info = FontInfo::new(data.as_ref().as_ref(), index)?; + pub fn new( + data: Arc + Send + Sync>, + index: u32, + allow_color: bool, + ) -> Option { + let font_info = FontInfo::new(data.as_ref().as_ref(), index, allow_color)?; Font::new_with_info(data, Arc::new(font_info)) } @@ -108,6 +118,10 @@ impl Font { self.0.font_info.ascent.get() } + pub(crate) fn allow_color(&self) -> bool { + self.0.font_info.allow_color + } + pub(crate) fn weight(&self) -> f32 { self.0.font_info.weight.get() } @@ -182,6 +196,7 @@ pub(crate) struct FontInfo { global_bbox: RectWrapper, postscript_name: Option, ascent: FiniteF32, + allow_color: bool, descent: FiniteF32, cap_height: Option, is_monospaced: bool, @@ -208,7 +223,7 @@ impl Hash for Repr { } impl FontInfo { - pub(crate) fn new(data: &[u8], index: u32) -> Option { + pub(crate) fn new(data: &[u8], index: u32, allow_color: bool) -> Option { let font_ref = FontRef::from_index(data, index).ok()?; let checksum = font_ref.head().ok()?.checksum_adjustment(); @@ -252,6 +267,7 @@ impl FontInfo { index, checksum, location, + allow_color, units_per_em, postscript_name, ascent, diff --git a/crates/krilla/src/font/svg.rs b/crates/krilla/src/font/svg.rs index 8b615652..5fa097cd 100644 --- a/crates/krilla/src/font/svg.rs +++ b/crates/krilla/src/font/svg.rs @@ -80,13 +80,19 @@ mod tests { #[visreg(document, all)] fn twitter_color_emoji(document: &mut Document) { let font_data = TWITTER_COLOR_EMOJI.clone(); - all_glyphs_to_pdf(font_data, None, false, document); + all_glyphs_to_pdf(font_data, None, false, true, document); + } + + #[visreg(document)] + fn twitter_color_emoji_no_color(document: &mut Document) { + let font_data = TWITTER_COLOR_EMOJI.clone(); + all_glyphs_to_pdf(font_data, None, false, false, document); } #[visreg] fn svg_extra(surface: &mut Surface) { let font_data = SVG_EXTRA.clone(); - let font = Font::new(font_data, 0).unwrap(); + let font = Font::new(font_data, 0, true).unwrap(); surface.fill_text( Point::from_xy(0., 30.0), diff --git a/crates/krilla/src/object/font/cid_font.rs b/crates/krilla/src/object/font/cid_font.rs index 6b125d10..87c2a122 100644 --- a/crates/krilla/src/object/font/cid_font.rs +++ b/crates/krilla/src/object/font/cid_font.rs @@ -357,7 +357,7 @@ mod tests { #[snapshot] fn cid_font_noto_sans_two_glyphs(sc: &mut SerializerContext) { - let font = Font::new(NOTO_SANS.clone(), 0).unwrap(); + let font = Font::new(NOTO_SANS.clone(), 0, true).unwrap(); let container = sc.create_or_get_font_container(font.clone()); let mut font_container = container.borrow_mut(); @@ -374,7 +374,7 @@ mod tests { #[visreg(all)] fn cid_font_noto_sans_simple_text(surface: &mut Surface) { - let font = Font::new(NOTO_SANS.clone(), 0).unwrap(); + let font = Font::new(NOTO_SANS.clone(), 0, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 100.0), Fill::default(), @@ -389,7 +389,7 @@ mod tests { #[visreg(all)] fn cid_font_latin_modern_simple_text(surface: &mut Surface) { - let font = Font::new(LATIN_MODERN_ROMAN.clone(), 0).unwrap(); + let font = Font::new(LATIN_MODERN_ROMAN.clone(), 0, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 100.0), Fill::default(), @@ -404,7 +404,7 @@ mod tests { #[visreg(all)] fn cid_font_noto_arabic_simple_text(surface: &mut Surface) { - let font = Font::new(NOTO_SANS_ARABIC.clone(), 0).unwrap(); + let font = Font::new(NOTO_SANS_ARABIC.clone(), 0, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 100.0), Fill::default(), @@ -419,7 +419,7 @@ mod tests { #[snapshot] fn cid_font_latin_modern_four_glyphs(sc: &mut SerializerContext) { - let font = Font::new(LATIN_MODERN_ROMAN.clone(), 0).unwrap(); + let font = Font::new(LATIN_MODERN_ROMAN.clone(), 0, true).unwrap(); let container = sc.create_or_get_font_container(font.clone()); let mut font_container = container.borrow_mut(); @@ -445,9 +445,9 @@ mod tests { let font_data = Arc::new(std::fs::read("/System/Library/Fonts/Supplemental/Songti.ttc").unwrap()); - let font_1 = Font::new(font_data.clone(), 0).unwrap(); - let font_2 = Font::new(font_data.clone(), 3).unwrap(); - let font_3 = Font::new(font_data, 6).unwrap(); + let font_1 = Font::new(font_data.clone(), 0, true).unwrap(); + let font_2 = Font::new(font_data.clone(), 3, true).unwrap(); + let font_3 = Font::new(font_data, 6, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 75.0), diff --git a/crates/krilla/src/object/font/type3_font.rs b/crates/krilla/src/object/font/type3_font.rs index e1a6fa76..b4e35b59 100644 --- a/crates/krilla/src/object/font/type3_font.rs +++ b/crates/krilla/src/object/font/type3_font.rs @@ -552,7 +552,7 @@ mod tests { #[test] fn type3_more_than_256_glyphs() { let mut sc = SerializerContext::new(SerializeSettings::settings_1()); - let font = Font::new(TWITTER_COLOR_EMOJI.clone(), 0).unwrap(); + let font = Font::new(TWITTER_COLOR_EMOJI.clone(), 0, true).unwrap(); let container = sc.create_or_get_font_container(font.clone()); let mut font_container = container.borrow_mut(); @@ -586,7 +586,7 @@ mod tests { #[snapshot(single_page, settings_1)] fn type3_color_glyphs(page: &mut Page) { - let font = Font::new(TWITTER_COLOR_EMOJI.clone(), 0).unwrap(); + let font = Font::new(TWITTER_COLOR_EMOJI.clone(), 0, true).unwrap(); let mut surface = page.surface(); surface.fill_text( @@ -603,7 +603,7 @@ mod tests { #[snapshot(single_page, settings_17)] fn type3_pdf_14(page: &mut Page) { - let font = Font::new(TWITTER_COLOR_EMOJI.clone(), 0).unwrap(); + let font = Font::new(TWITTER_COLOR_EMOJI.clone(), 0, true).unwrap(); let mut surface = page.surface(); surface.fill_text( diff --git a/crates/krilla/src/serialize.rs b/crates/krilla/src/serialize.rs index 2b79e4b3..edfe07d8 100644 --- a/crates/krilla/src/serialize.rs +++ b/crates/krilla/src/serialize.rs @@ -425,11 +425,15 @@ impl SerializerContext { // For now, we make the simplifying assumption that a font is either mapped // to a series of Type3 fonts or to a single CID font, but not a mix of both. let font_ref = font.font_ref(); - let use_type3 = font_ref.svg().is_ok() - || font_ref.colr().is_ok() - || font_ref.sbix().is_ok() - || font_ref.cbdt().is_ok() - || font_ref.ebdt().is_ok(); + let use_type3 = if !font.allow_color() { + false + } else { + font_ref.svg().is_ok() + || font_ref.colr().is_ok() + || font_ref.sbix().is_ok() + || font_ref.cbdt().is_ok() + || font_ref.ebdt().is_ok() + }; if use_type3 { Rc::new(RefCell::new(FontContainer::Type3(Type3FontMapper::new( @@ -487,7 +491,7 @@ impl SerializerContext { // cheaper, and then check whether we already have a corresponding font object in the cache. // If not, we still need to construct it. if let Some((font_data, index)) = unsafe { db.make_shared_face_data(id) } { - if let Some(font_info) = FontInfo::new(font_data.as_ref().as_ref(), index) { + if let Some(font_info) = FontInfo::new(font_data.as_ref().as_ref(), index, true) { let font_info = Arc::new(font_info); let font = self .font_cache diff --git a/crates/krilla/src/surface.rs b/crates/krilla/src/surface.rs index d78f85ae..625c0d21 100644 --- a/crates/krilla/src/surface.rs +++ b/crates/krilla/src/surface.rs @@ -658,7 +658,7 @@ mod tests { #[visreg] fn text_direction_ltr(surface: &mut Surface) { - let font = Font::new(NOTO_SANS_CJK.clone(), 0).unwrap(); + let font = Font::new(NOTO_SANS_CJK.clone(), 0, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 100.0), Fill::default(), @@ -673,7 +673,7 @@ mod tests { #[visreg] fn text_direction_rtl(surface: &mut Surface) { - let font = Font::new(NOTO_SANS_CJK.clone(), 0).unwrap(); + let font = Font::new(NOTO_SANS_CJK.clone(), 0, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 100.0), Fill::default(), @@ -688,7 +688,7 @@ mod tests { #[visreg] fn text_direction_ttb(surface: &mut Surface) { - let font = Font::new(NOTO_SANS_CJK.clone(), 0).unwrap(); + let font = Font::new(NOTO_SANS_CJK.clone(), 0, true).unwrap(); surface.fill_text( Point::from_xy(100.0, 0.0), Fill::default(), @@ -703,7 +703,7 @@ mod tests { #[visreg] fn text_direction_btt(surface: &mut Surface) { - let font = Font::new(NOTO_SANS_CJK.clone(), 0).unwrap(); + let font = Font::new(NOTO_SANS_CJK.clone(), 0, true).unwrap(); surface.fill_text( Point::from_xy(100.0, 0.0), Fill::default(), @@ -783,7 +783,7 @@ mod tests { surface.fill_text( Point::from_xy(0.0, 50.0), Fill::default(), - Font::new(NOTO_SANS.clone(), 0).unwrap(), + Font::new(NOTO_SANS.clone(), 0, true).unwrap(), 16.0, &[], "hi there", @@ -797,7 +797,7 @@ mod tests { surface.stroke_text( Point::from_xy(0.0, 50.0), Stroke::default(), - Font::new(NOTO_SANS.clone(), 0).unwrap(), + Font::new(NOTO_SANS.clone(), 0, true).unwrap(), 16.0, &[], "hi there", @@ -811,7 +811,7 @@ mod tests { surface.fill_text( Point::from_xy(0.0, 50.0), Fill::default(), - Font::new(NOTO_SANS_DEVANAGARI.clone(), 0).unwrap(), + Font::new(NOTO_SANS_DEVANAGARI.clone(), 0, true).unwrap(), 16.0, &[], "यह कुछ जटिल पाठ है.", @@ -825,7 +825,7 @@ mod tests { surface.fill_text( Point::from_xy(0.0, 50.0), Fill::default(), - Font::new(NOTO_SANS_DEVANAGARI.clone(), 0).unwrap(), + Font::new(NOTO_SANS_DEVANAGARI.clone(), 0, true).unwrap(), 16.0, &[], "यु॒धा नर॑ ऋ॒ष्वा ", @@ -839,7 +839,7 @@ mod tests { surface.fill_text( Point::from_xy(0.0, 50.0), Fill::default(), - Font::new(NOTO_SANS_DEVANAGARI.clone(), 0).unwrap(), + Font::new(NOTO_SANS_DEVANAGARI.clone(), 0, true).unwrap(), 16.0, &[], "आ रु॒क्मैरा यु॒धा नर॑ ऋ॒ष्वा ऋ॒ष्टीर॑सृक्षत ।", @@ -853,7 +853,7 @@ mod tests { surface.fill_text( Point::from_xy(0.0, 50.0), Fill::default(), - Font::new(NOTO_SANS_DEVANAGARI.clone(), 0).unwrap(), + Font::new(NOTO_SANS_DEVANAGARI.clone(), 0, true).unwrap(), 16.0, &[], "अन्वे॑नाँ॒ अह॑ वि॒द्युतो॑ म॒रुतो॒ जज्झ॑तीरव भनर॑र्त॒ त्मना॑ दि॒वः ॥", @@ -956,7 +956,7 @@ mod tests { } fn text_with_fill_impl(surface: &mut Surface, outlined: bool) { - let font = Font::new(NOTO_SANS.clone(), 0).unwrap(); + let font = Font::new(NOTO_SANS.clone(), 0, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 80.0), red_fill(0.5), @@ -995,7 +995,7 @@ mod tests { TextDirection::Auto, ); - let noto_font = Font::new(NOTO_COLOR_EMOJI_COLR.clone(), 0).unwrap(); + let noto_font = Font::new(NOTO_COLOR_EMOJI_COLR.clone(), 0, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 140.0), @@ -1031,7 +1031,7 @@ mod tests { } fn text_with_stroke_impl(surface: &mut Surface, outlined: bool) { - let font = Font::new(NOTO_SANS.clone(), 0).unwrap(); + let font = Font::new(NOTO_SANS.clone(), 0, true).unwrap(); surface.stroke_text( Point::from_xy(0.0, 80.0), red_stroke(0.5, 1.0), @@ -1070,7 +1070,7 @@ mod tests { TextDirection::Auto, ); - let font = Font::new(NOTO_COLOR_EMOJI_COLR.clone(), 0).unwrap(); + let font = Font::new(NOTO_COLOR_EMOJI_COLR.clone(), 0, true).unwrap(); surface.stroke_text( Point::from_xy(0.0, 140.0), @@ -1091,7 +1091,7 @@ mod tests { #[visreg] fn text_zalgo(surface: &mut Surface) { - let font = Font::new(NOTO_SANS.clone(), 0).unwrap(); + let font = Font::new(NOTO_SANS.clone(), 0, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 100.0), Fill::default(), @@ -1106,7 +1106,7 @@ mod tests { #[visreg] fn text_zalgo_outlined(surface: &mut Surface) { - let font = Font::new(NOTO_SANS.clone(), 0).unwrap(); + let font = Font::new(NOTO_SANS.clone(), 0, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 100.0), Fill::default(), diff --git a/crates/krilla/src/tagging.rs b/crates/krilla/src/tagging.rs index 80ea0fe3..f7af4b3f 100644 --- a/crates/krilla/src/tagging.rs +++ b/crates/krilla/src/tagging.rs @@ -949,7 +949,7 @@ mod tests { impl SurfaceExt for Surface<'_> { fn fill_text_(&mut self, y: f32, content: &str) { let font_data = NOTO_SANS.clone(); - let font = Font::new(font_data, 0).unwrap(); + let font = Font::new(font_data, 0, true).unwrap(); self.fill_text( tiny_skia_path::Point::from_xy(0.0, y), diff --git a/crates/krilla/src/tests/mod.rs b/crates/krilla/src/tests/mod.rs index 29831144..853e1482 100644 --- a/crates/krilla/src/tests/mod.rs +++ b/crates/krilla/src/tests/mod.rs @@ -439,12 +439,13 @@ pub fn all_glyphs_to_pdf( font_data: Arc>, glyphs: Option>, color_cycling: bool, + allow_color: bool, d: &mut Document, ) { use crate::font::KrillaGlyph; use crate::geom::Transform; - let font = Font::new(font_data, 0).unwrap(); + let font = Font::new(font_data, 0, allow_color).unwrap(); let font_ref = font.font_ref(); let glyphs = glyphs.unwrap_or_else(|| { diff --git a/crates/krilla/src/validation/mod.rs b/crates/krilla/src/validation/mod.rs index 47ce0af4..d2a39d62 100644 --- a/crates/krilla/src/validation/mod.rs +++ b/crates/krilla/src/validation/mod.rs @@ -677,7 +677,7 @@ mod tests { let mut surface = page.surface(); let font_data = NOTO_SANS.clone(); - let font = Font::new(font_data, 0).unwrap(); + let font = Font::new(font_data, 0, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 100.0), @@ -705,7 +705,7 @@ mod tests { let mut surface = page.surface(); let font_data = NOTO_SANS.clone(); - let font = Font::new(font_data, 0).unwrap(); + let font = Font::new(font_data, 0, true).unwrap(); surface.fill_text( Point::from_xy(0.0, 100.0), @@ -729,7 +729,7 @@ mod tests { let mut surface = page.surface(); let font_data = NOTO_SANS.clone(); - let font = Font::new(font_data, 0).unwrap(); + let font = Font::new(font_data, 0, true).unwrap(); let id1 = surface.start_tagged(ContentTag::Span( "", @@ -792,7 +792,7 @@ mod tests { fn validation_pdfu_invalid_codepoint() { let mut document = Document::new_with(SerializeSettings::settings_9()); let font_data = NOTO_SANS.clone(); - let font = Font::new(font_data, 0).unwrap(); + let font = Font::new(font_data, 0, true).unwrap(); invalid_codepoint_impl(&mut document, font.clone(), "A\u{FEFF}B"); assert_eq!( @@ -809,7 +809,7 @@ mod tests { let metadata = Metadata::new().language("en".to_string()); document.set_metadata(metadata); let font_data = NOTO_SANS.clone(); - let font = Font::new(font_data, 0).unwrap(); + let font = Font::new(font_data, 0, true).unwrap(); invalid_codepoint_impl(&mut document, font.clone(), "A\u{E022}B"); assert_eq!( @@ -866,7 +866,7 @@ mod tests { let mut surface = page.surface(); let font_data = NOTO_SANS.clone(); - let font = Font::new(font_data, 0).unwrap(); + let font = Font::new(font_data, 0, true).unwrap(); let id1 = surface.start_tagged(ContentTag::Span("", None, None, None)); surface.fill_text( @@ -914,7 +914,7 @@ mod tests { let mut surface = page.surface(); let font_data = NOTO_SANS.clone(); - let font = Font::new(font_data, 0).unwrap(); + let font = Font::new(font_data, 0, true).unwrap(); let id1 = surface.start_tagged(ContentTag::Span("", None, None, None)); surface.fill_text( diff --git a/refs/visreg/twitter_color_emoji_no_color.png b/refs/visreg/twitter_color_emoji_no_color.png new file mode 100644 index 00000000..3e461f27 Binary files /dev/null and b/refs/visreg/twitter_color_emoji_no_color.png differ