mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-12-28 13:43:45 +03:00
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.
This commit is contained in:
parent
6ebddab448
commit
7ab4e53b99
Notes:
sideshowbarker
2024-07-16 18:06:41 +09:00
Author: https://github.com/nico Commit: https://github.com/SerenityOS/serenity/commit/7ab4e53b99 Pull-request: https://github.com/SerenityOS/serenity/pull/23136
@ -128,6 +128,7 @@ PDFErrorOr<NonnullRefPtr<CFF>> CFF::create(ReadonlyBytes const& cff_bytes, RefPt
|
||||
Vector<ByteBuffer> 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<TopDictOperator>(element_reader, [&](TopDictOperator op, Vector<DictOperand> const& operands) -> PDFErrorOr<void> {
|
||||
@ -229,13 +230,16 @@ PDFErrorOr<NonnullRefPtr<CFF>> CFF::create(ReadonlyBytes const& cff_bytes, RefPt
|
||||
}));
|
||||
break;
|
||||
}
|
||||
case TopDictOperator::FDSelect:
|
||||
if (!operands.is_empty())
|
||||
fdselect_offset = operands[0].get<int>();
|
||||
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<NonnullRefPtr<CFF>> 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<Vector<CFF::SID>> CFF::parse_charset(Reader&& reader, size_t glyph_co
|
||||
return names;
|
||||
}
|
||||
|
||||
PDFErrorOr<Vector<u8>> CFF::parse_fdselect(Reader&& reader, size_t glyph_count)
|
||||
{
|
||||
Vector<u8> fd_selector_array; // Maps GIDs to their FD index.
|
||||
|
||||
// CFF spec, "19 FDSelect"
|
||||
auto format = TRY(reader.try_read<Card8>());
|
||||
|
||||
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<Card8>()));
|
||||
} 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<BigEndian<Card16>>());
|
||||
Card16 begin = TRY(reader.try_read<BigEndian<Card16>>());
|
||||
|
||||
// "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<Card8>());
|
||||
auto end = TRY(reader.try_read<BigEndian<Card16>>());
|
||||
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<Vector<CFF::Glyph>> CFF::parse_charstrings(Reader&& reader, Vector<ByteBuffer> const& local_subroutines, Vector<ByteBuffer> const& global_subroutines)
|
||||
{
|
||||
// CFF spec, "14 CharStrings INDEX"
|
||||
|
@ -124,6 +124,7 @@ public:
|
||||
|
||||
static DeprecatedFlyString resolve_sid(SID, Vector<StringView> const&);
|
||||
static PDFErrorOr<Vector<SID>> parse_charset(Reader&&, size_t);
|
||||
static PDFErrorOr<Vector<u8>> parse_fdselect(Reader&&, size_t);
|
||||
static PDFErrorOr<Vector<u8>> parse_encoding(Reader&&, HashMap<Card8, SID>& supplemental);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user