From 012f6d46e755fc92d44ab9840138b97225cc8f08 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 16 Feb 2024 21:39:24 -0500 Subject: [PATCH] LibPDF: Implement stream CIDToGIDMaps for Type0 CIDFontType2 fonts Of my 1000 test files, 73 have stream Type0 truetype fonts with stream CIDToGIDMaps. This makes that work. (With this patch, the number of files in my 1000 test files complaining "Font is missing Name" increases from 41 to 75, so a bit under half of the fonts using stream CIDToGIDMaps also have no 'name' table. So that's next.) Increases files without issues from 652 to 681. --- Userland/Libraries/LibPDF/Fonts/Type0Font.cpp | 63 ++++++++++++++----- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp b/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp index 56c8ce390a..1346e649e1 100644 --- a/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp +++ b/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp @@ -129,32 +129,61 @@ private: RefPtr m_font; }; +static PDFErrorOr> create_cid_to_gid_map(Document* document, NonnullRefPtr const& dict) +{ + // "If the value is a stream, the bytes in the stream contain the mapping from CIDs to glyph indices: + // the glyph index for a particular CID value c is a 2-byte value stored in bytes 2×c and 2×c+1, where the first byte is the high-order byte. + // If the value of CIDToGIDMap is a name, it must be Identity, indicating that the mapping between CIDs and glyph indices is the identity mapping. + // Default value: Identity." + + class IdentityCIDToGIDMap : public OpenType::CharCodeToGlyphIndex { + public: + virtual u32 glyph_id_for_code_point(u32 char_code) const override { return char_code; } + }; + + class StreamCIDToGIDMap : public OpenType::CharCodeToGlyphIndex { + public: + StreamCIDToGIDMap(ReadonlyBytes bytes) + : m_bytes(move(bytes)) + { + } + virtual u32 glyph_id_for_code_point(u32 char_code) const override + { + u32 index = char_code * 2; + if (index + 1 >= m_bytes.size()) { + // This can happen because Font::populate_glyph_page() with CIDs not used on the page and hence not in the font. + return 0; + } + return (m_bytes[index] << 8) | m_bytes[index + 1]; + } + + private: + ReadonlyBytes m_bytes; + }; + + if (!dict->contains(CommonNames::CIDToGIDMap)) + return make(); + + auto value = TRY(dict->get_object(document, CommonNames::CIDToGIDMap)); + if (value->is()) + return make(value->cast()->bytes()); + + if (value->cast()->name() != "Identity") + return Error::rendering_unsupported_error("Type0 font: The only valid CIDToGIDMap name is 'Identity'"); + return make(); +} + PDFErrorOr> CIDFontType2::create(Document* document, NonnullRefPtr const& descendant, float font_size) { auto descriptor = TRY(descendant->get_dict(document, CommonNames::FontDescriptor)); - if (descendant->contains(CommonNames::CIDToGIDMap)) { - auto value = TRY(descendant->get_object(document, CommonNames::CIDToGIDMap)); - if (value->is()) { - return Error::rendering_unsupported_error("Type0 font subtype 2: support for stream cid maps not yet implemented"); - } else if (value->cast()->name() != "Identity") { - return Error::rendering_unsupported_error("Type0 font: support for non-Identity named cid maps not yet implemented"); - } - } - - class IdentityCIDToGIDMap : public OpenType::CharCodeToGlyphIndex { - public: - virtual u32 glyph_id_for_code_point(u32 char_code) const override - { - return char_code; - } - }; + auto cid_to_gid_map = TRY(create_cid_to_gid_map(document, descendant)); RefPtr font; if (descriptor->contains(CommonNames::FontFile2)) { auto font_file_stream = TRY(descriptor->get_stream(document, CommonNames::FontFile2)); float point_size = (font_size * POINTS_PER_INCH) / DEFAULT_DPI; - auto ttf_font = TRY(OpenType::Font::try_load_from_externally_owned_memory(font_file_stream->bytes(), 0, make())); + auto ttf_font = TRY(OpenType::Font::try_load_from_externally_owned_memory(font_file_stream->bytes(), 0, move(cid_to_gid_map))); font = adopt_ref(*new Gfx::ScaledFont(*ttf_font, point_size, point_size)); }