From 7ab4e53b99bee4ec56c54f8bef4072f6216171ed Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 9 Feb 2024 08:08:00 -0500 Subject: [PATCH] LibPDF/CFF: Add code for fdselect parsing This is one of the two top dict entries we need for CID-keyed fonts. We don't send any CID-keyed font data into the CFF parser yet, so no behavior change. --- Userland/Libraries/LibPDF/Fonts/CFF.cpp | 57 ++++++++++++++++++++++++- Userland/Libraries/LibPDF/Fonts/CFF.h | 1 + 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibPDF/Fonts/CFF.cpp b/Userland/Libraries/LibPDF/Fonts/CFF.cpp index 1234cb7d2e..2d9575bc1a 100644 --- a/Userland/Libraries/LibPDF/Fonts/CFF.cpp +++ b/Userland/Libraries/LibPDF/Fonts/CFF.cpp @@ -128,6 +128,7 @@ PDFErrorOr> CFF::create(ReadonlyBytes const& cff_bytes, RefPt Vector local_subroutines; float defaultWidthX = 0; float nominalWidthX = 0; + int fdselect_offset = 0; TRY(parse_index(reader, [&](ReadonlyBytes const& element_data) { Reader element_reader { element_data }; return parse_dict(element_reader, [&](TopDictOperator op, Vector const& operands) -> PDFErrorOr { @@ -229,13 +230,16 @@ PDFErrorOr> CFF::create(ReadonlyBytes const& cff_bytes, RefPt })); break; } + case TopDictOperator::FDSelect: + if (!operands.is_empty()) + fdselect_offset = operands[0].get(); + break; case TopDictOperator::CIDFontVersion: case TopDictOperator::CIDFontRevision: case TopDictOperator::CIDFontType: case TopDictOperator::CIDCount: case TopDictOperator::UIDBase: case TopDictOperator::FDArray: - case TopDictOperator::FDSelect: case TopDictOperator::FontName: // Keys for CID-keyed fonts that we don't need, at least at the moment. break; @@ -306,6 +310,12 @@ PDFErrorOr> CFF::create(ReadonlyBytes const& cff_bytes, RefPt } } + // CFF spec, "19 FDSelect" + if (fdselect_offset != 0) { + auto fdselect = TRY(parse_fdselect(Reader { cff_bytes.slice(fdselect_offset) }, glyphs.size())); + dbgln_if(CFF_DEBUG, "CFF has {} FDSelect entries", fdselect.size()); + } + // Adjust glyphs' widths as they are deltas from nominalWidthX for (auto& glyph : glyphs) { if (!glyph.has_width()) @@ -807,6 +817,51 @@ PDFErrorOr> CFF::parse_charset(Reader&& reader, size_t glyph_co return names; } +PDFErrorOr> CFF::parse_fdselect(Reader&& reader, size_t glyph_count) +{ + Vector fd_selector_array; // Maps GIDs to their FD index. + + // CFF spec, "19 FDSelect" + auto format = TRY(reader.try_read()); + + if (format == 0) { + // CFF spec, "Table 27 Format 0" + // "(This format is identical to charset format 0 except that the notdef glyph is included in this case.)" + dbgln_if(CFF_DEBUG, "CFF fdselect format 0"); + fd_selector_array.ensure_capacity(glyph_count); + for (size_t i = 0; i < glyph_count; i++) + fd_selector_array.append(TRY(reader.try_read())); + } else if (format == 3) { + // CFF spec, "Table 28 Format 3" + dbgln_if(CFF_DEBUG, "CFF fdselect format 3"); + + // The spec presents this as n "Card16 first; Card8 fd;" struct entries followed by a Char16 sentinel value. + // But the code is shorter if we treat it as a Char16 start value followed by n "Card8 fd; Card16 end;" struct entries. + Card16 n_ranges = TRY(reader.try_read>()); + Card16 begin = TRY(reader.try_read>()); + + // "The first range must have a 'first' GID of 0." + if (begin != 0) + return error("CFF fdselect format 3 first range must have a 'first' GID of 0"); + + for (Card16 i = 0; i < n_ranges; i++) { + auto fd = TRY(reader.try_read()); + auto end = TRY(reader.try_read>()); + for (Card16 j = begin; j < end; j++) + fd_selector_array.append(fd); + begin = end; + } + + // "The sentinel GID is set equal to the number of glyphs in the font." + if (begin != glyph_count) + return error("CFF fdselect format 3 last range must end at the number of glyphs in the font"); + } else { + dbgln("CFF: Unknown fdselect format {}", format); + } + + return fd_selector_array; +} + PDFErrorOr> CFF::parse_charstrings(Reader&& reader, Vector const& local_subroutines, Vector const& global_subroutines) { // CFF spec, "14 CharStrings INDEX" diff --git a/Userland/Libraries/LibPDF/Fonts/CFF.h b/Userland/Libraries/LibPDF/Fonts/CFF.h index feac924d74..ad7bf9d9d6 100644 --- a/Userland/Libraries/LibPDF/Fonts/CFF.h +++ b/Userland/Libraries/LibPDF/Fonts/CFF.h @@ -124,6 +124,7 @@ public: static DeprecatedFlyString resolve_sid(SID, Vector const&); static PDFErrorOr> parse_charset(Reader&&, size_t); + static PDFErrorOr> parse_fdselect(Reader&&, size_t); static PDFErrorOr> parse_encoding(Reader&&, HashMap& supplemental); };