mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 18:27:35 +00:00
LibPDF: Implement support for callgsubr in CFF font programs
Font programs are bytecode programs defining glyphs. If several glyphs share a piece of outline, that opcode sequence can be put in a subroutine ("subr") table and the definition of those glyphs can then call that subroutine by number, to reduce file size. CFF fonts can in theory contain multiple fonts, and so there's a global subr table shared by all the fonts in one CFF, and a local per-fornt subr table. We used to only implement the local subr table, now we implement both. (We only support one font per CFF, and at least in PDF files, that's all that's ever used. So a global subr table isn't very useful. But the spec explicitly allows it -- "Global subroutines may be used in a FontSet even if it only contains one font." -- and it happens in practice.)
This commit is contained in:
parent
185573c03f
commit
3907374621
5 changed files with 15 additions and 11 deletions
|
@ -221,11 +221,9 @@ PDFErrorOr<NonnullRefPtr<CFF>> CFF::create(ReadonlyBytes const& cff_bytes, RefPt
|
|||
TRY(parse_index(reader, [&](ReadonlyBytes const& subroutine_bytes) -> PDFErrorOr<void> {
|
||||
return TRY(global_subroutines.try_append(TRY(ByteBuffer::copy(subroutine_bytes))));
|
||||
}));
|
||||
if (!global_subroutines.is_empty())
|
||||
dbgln("CFF data contains Global subrs, which aren't implemented yet"); // FIXME
|
||||
|
||||
// Create glyphs (now that we have the subroutines) and associate missing information to store them and their encoding
|
||||
auto glyphs = TRY(parse_charstrings(Reader(cff_bytes.slice(charstrings_offset)), local_subroutines));
|
||||
auto glyphs = TRY(parse_charstrings(Reader(cff_bytes.slice(charstrings_offset)), local_subroutines, global_subroutines));
|
||||
|
||||
// CFF spec, "Table 16 Encoding ID"
|
||||
// FIXME: Only read this if the built-in encoding is actually needed? (ie. `if (!encoding)`)
|
||||
|
@ -765,13 +763,13 @@ PDFErrorOr<Vector<DeprecatedFlyString>> CFF::parse_charset(Reader&& reader, size
|
|||
return names;
|
||||
}
|
||||
|
||||
PDFErrorOr<Vector<CFF::Glyph>> CFF::parse_charstrings(Reader&& reader, Vector<ByteBuffer> const& subroutines)
|
||||
PDFErrorOr<Vector<CFF::Glyph>> CFF::parse_charstrings(Reader&& reader, Vector<ByteBuffer> const& local_subroutines, Vector<ByteBuffer> const& global_subroutines)
|
||||
{
|
||||
// CFF spec, "14 CharStrings INDEX"
|
||||
Vector<Glyph> glyphs;
|
||||
TRY(parse_index(reader, [&](ReadonlyBytes const& charstring_data) -> PDFErrorOr<void> {
|
||||
GlyphParserState state;
|
||||
auto glyph = TRY(parse_glyph(charstring_data, subroutines, state, true));
|
||||
auto glyph = TRY(parse_glyph(charstring_data, local_subroutines, global_subroutines, state, true));
|
||||
return TRY(glyphs.try_append(glyph));
|
||||
}));
|
||||
return glyphs;
|
||||
|
|
|
@ -94,7 +94,7 @@ public:
|
|||
|
||||
static PDFErrorOr<Vector<StringView>> parse_strings(Reader&);
|
||||
|
||||
static PDFErrorOr<Vector<CFF::Glyph>> parse_charstrings(Reader&&, Vector<ByteBuffer> const& subroutines);
|
||||
static PDFErrorOr<Vector<CFF::Glyph>> parse_charstrings(Reader&&, Vector<ByteBuffer> const& local_subroutines, Vector<ByteBuffer> const& global_subroutines);
|
||||
|
||||
static DeprecatedFlyString resolve_sid(SID, Vector<StringView> const&);
|
||||
static PDFErrorOr<Vector<DeprecatedFlyString>> parse_charset(Reader&&, size_t, Vector<StringView> const&);
|
||||
|
|
|
@ -92,7 +92,7 @@ PDFErrorOr<void> PS1FontProgram::parse_encrypted_portion(ByteBuffer const& buffe
|
|||
reader.move_by(encrypted_size);
|
||||
auto glyph_name = word.substring_view(1);
|
||||
GlyphParserState state;
|
||||
TRY(add_glyph(glyph_name, TRY(parse_glyph(line, subroutines, state, false))));
|
||||
TRY(add_glyph(glyph_name, TRY(parse_glyph(line, subroutines, {}, state, false))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ enum Command {
|
|||
RLineCurve,
|
||||
VVCurveTo,
|
||||
HHCurveTo,
|
||||
VHCurveTo = 30,
|
||||
CallGsubr = 29, // Type 2 only
|
||||
VHCurveTo,
|
||||
HVCurveTo
|
||||
};
|
||||
|
||||
|
@ -133,7 +134,7 @@ void Type1FontProgram::consolidate_glyphs()
|
|||
}
|
||||
}
|
||||
|
||||
PDFErrorOr<Type1FontProgram::Glyph> Type1FontProgram::parse_glyph(ReadonlyBytes const& data, Vector<ByteBuffer> const& subroutines, GlyphParserState& state, bool is_type2)
|
||||
PDFErrorOr<Type1FontProgram::Glyph> Type1FontProgram::parse_glyph(ReadonlyBytes const& data, Vector<ByteBuffer> const& local_subroutines, Vector<ByteBuffer> const& global_subroutines, GlyphParserState& state, bool is_type2)
|
||||
{
|
||||
// Type 1 Font Format: https://adobe-type-tools.github.io/font-tech-notes/pdfs/T1_SPEC.pdf (Chapter 6: CharStrings dictionary)
|
||||
// Type 2 Charstring Format: https://adobe-type-tools.github.io/font-tech-notes/pdfs/5177.Type2.pdf
|
||||
|
@ -371,7 +372,12 @@ PDFErrorOr<Type1FontProgram::Glyph> Type1FontProgram::parse_glyph(ReadonlyBytes
|
|||
state.sp = 0;
|
||||
break;
|
||||
|
||||
case CallGsubr:
|
||||
if (!is_type2)
|
||||
return error(DeprecatedString::formatted("CFF Gsubr only valid in type2 data"));
|
||||
[[fallthrough]];
|
||||
case CallSubr: {
|
||||
Vector<ByteBuffer> const& subroutines = v == CallSubr ? local_subroutines : global_subroutines;
|
||||
auto subr_number = pop();
|
||||
|
||||
if (is_type2) {
|
||||
|
@ -420,7 +426,7 @@ PDFErrorOr<Type1FontProgram::Glyph> Type1FontProgram::parse_glyph(ReadonlyBytes
|
|||
if (subr.is_empty())
|
||||
return error("Empty subroutine");
|
||||
|
||||
TRY(parse_glyph(subr, subroutines, state, is_type2));
|
||||
TRY(parse_glyph(subr, local_subroutines, global_subroutines, state, is_type2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ protected:
|
|||
Array<float, 24> postscript_stack;
|
||||
};
|
||||
|
||||
static PDFErrorOr<Glyph> parse_glyph(ReadonlyBytes const&, Vector<ByteBuffer> const&, GlyphParserState&, bool is_type2);
|
||||
static PDFErrorOr<Glyph> parse_glyph(ReadonlyBytes const&, Vector<ByteBuffer> const& local_subroutines, Vector<ByteBuffer> const& global_subroutines, GlyphParserState&, bool is_type2);
|
||||
|
||||
static Error error(
|
||||
DeprecatedString const& message
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue