1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 12:37:44 +00:00

LibPDF: Move TrueType painting into a new class

No behavior change.
This commit is contained in:
Nico Weber 2024-02-25 13:10:54 -05:00 committed by Andreas Kling
parent 84d1e3956f
commit 86a7753d65
2 changed files with 99 additions and 48 deletions

View file

@ -15,65 +15,47 @@
namespace PDF { namespace PDF {
PDFErrorOr<void> TrueTypeFont::initialize(Document* document, NonnullRefPtr<DictObject> const& dict, float font_size) TrueTypePainter::TrueTypePainter(AK::NonnullRefPtr<Gfx::Font> font, NonnullRefPtr<Encoding> encoding, bool encoding_is_mac_roman_or_win_ansi, bool is_nonsymbolic, Optional<u8> high_byte)
: m_font(move(font))
, m_encoding(move(encoding))
, m_encoding_is_mac_roman_or_win_ansi(encoding_is_mac_roman_or_win_ansi)
, m_is_nonsymbolic(is_nonsymbolic)
, m_high_byte(high_byte)
{ {
TRY(SimpleFont::initialize(document, dict, font_size)); }
NonnullOwnPtr<TrueTypePainter> TrueTypePainter::create(Document* document, NonnullRefPtr<DictObject> const& dict, SimpleFont const& containing_pdf_font, AK::NonnullRefPtr<Gfx::Font> font, NonnullRefPtr<Encoding> encoding)
{
bool encoding_is_mac_roman_or_win_ansi = false;
if (dict->contains(CommonNames::Encoding)) { if (dict->contains(CommonNames::Encoding)) {
auto encoding_object = MUST(dict->get_object(document, CommonNames::Encoding)); auto encoding_object = MUST(dict->get_object(document, CommonNames::Encoding));
if (encoding_object->is<NameObject>()) { if (encoding_object->is<NameObject>()) {
auto name = encoding_object->cast<NameObject>()->name(); auto name = encoding_object->cast<NameObject>()->name();
if (name == "MacRomanEncoding" || name == "WinAnsiEncoding") if (name == "MacRomanEncoding" || name == "WinAnsiEncoding")
m_encoding_is_mac_roman_or_win_ansi = true; encoding_is_mac_roman_or_win_ansi = true;
} }
} }
m_base_font_name = TRY(dict->get_name(document, CommonNames::BaseFont))->name();
// If there's an embedded font program we use that; otherwise we try to find a replacement font
if (dict->contains(CommonNames::FontDescriptor)) {
auto descriptor = MUST(dict->get_dict(document, CommonNames::FontDescriptor));
if (descriptor->contains(CommonNames::FontFile2)) {
auto font_file_stream = TRY(descriptor->get_stream(document, CommonNames::FontFile2));
auto ttf_font = TRY(OpenType::Font::try_load_from_externally_owned_memory(font_file_stream->bytes(), { .skip_tables = pdf_skipped_opentype_tables }));
float point_size = (font_size * POINTS_PER_INCH) / DEFAULT_DPI;
m_font = adopt_ref(*new Gfx::ScaledFont(*ttf_font, point_size, point_size));
}
}
if (!m_font) {
m_font = TRY(replacement_for(base_font_name().to_lowercase(), font_size));
}
VERIFY(m_font);
// See long spec comment in TrueTypeFont::draw_glyp(). // See long spec comment in TrueTypeFont::draw_glyp().
if (!dict->contains(CommonNames::Encoding) || is_symbolic()) { Optional<u8> high_byte;
if (!dict->contains(CommonNames::Encoding) || containing_pdf_font.is_symbolic()) {
// FIXME: Looks like this is never hit in the test set (?). // FIXME: Looks like this is never hit in the test set (?).
for (u8 prefix : { 0x00, 0xF0, 0xF1, 0xF2 }) { for (u8 prefix : { 0x00, 0xF0, 0xF1, 0xF2 }) {
bool has_all = true; bool has_all = true;
for (unsigned suffix = 0x00; suffix <= 0xFF; ++suffix) { for (unsigned suffix = 0x00; suffix <= 0xFF; ++suffix) {
if (!m_font->contains_glyph((prefix << 8) | suffix)) { if (!font->contains_glyph((prefix << 8) | suffix)) {
has_all = false; has_all = false;
break; break;
} }
} }
if (has_all) { if (has_all) {
m_high_byte = prefix; high_byte = prefix;
break; break;
} }
} }
} }
return {}; return adopt_own(*new TrueTypePainter { move(font), move(encoding), encoding_is_mac_roman_or_win_ansi, containing_pdf_font.is_nonsymbolic(), high_byte });
}
Optional<float> TrueTypeFont::get_glyph_width(u8 char_code) const
{
return m_font->glyph_width(char_code);
}
void TrueTypeFont::set_font_size(float font_size)
{
m_font = m_font->with_size((font_size * POINTS_PER_INCH) / DEFAULT_DPI);
} }
static void do_draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u32 unicode, Gfx::Font const& font, ColorOrStyle const& style) static void do_draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u32 unicode, Gfx::Font const& font, ColorOrStyle const& style)
@ -91,7 +73,7 @@ static void do_draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float wi
} }
} }
PDFErrorOr<void> TrueTypeFont::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u8 char_code, Renderer const& renderer) PDFErrorOr<void> TrueTypePainter::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u8 char_code, Renderer const& renderer)
{ {
auto style = renderer.state().paint_style; auto style = renderer.state().paint_style;
@ -101,17 +83,14 @@ PDFErrorOr<void> TrueTypeFont::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint
// "If the font has a named Encoding entry of either MacRomanEncoding or WinAnsiEncoding, // "If the font has a named Encoding entry of either MacRomanEncoding or WinAnsiEncoding,
// or if the font descriptors Nonsymbolic flag (see Table 5.20) is set, the viewer creates // or if the font descriptors Nonsymbolic flag (see Table 5.20) is set, the viewer creates
// a table that maps from character codes to glyph names: // a table that maps from character codes to glyph names:
if (m_encoding_is_mac_roman_or_win_ansi || is_nonsymbolic()) { if (m_encoding_is_mac_roman_or_win_ansi || m_is_nonsymbolic) {
// • If the Encoding entry is one of the names MacRomanEncoding or WinAnsiEncoding, // • If the Encoding entry is one of the names MacRomanEncoding or WinAnsiEncoding,
// the table is initialized with the mappings described in Appendix D. // the table is initialized with the mappings described in Appendix D.
// • If the Encoding entry is a dictionary, the table is initialized with the entries // • If the Encoding entry is a dictionary, the table is initialized with the entries
// from the dictionarys BaseEncoding entry (see Table 5.11). Any entries in the // from the dictionarys BaseEncoding entry (see Table 5.11). Any entries in the
// Differences array are used to update the table. Finally, any undefined entries in // Differences array are used to update the table. Finally, any undefined entries in
// the table are filled using StandardEncoding." // the table are filled using StandardEncoding."
// Implementor's note: This is (mostly) done in SimpleFont::initialize() and encoding() returns the result. // Implementor's note: This is (mostly) done in SimpleFont::initialize() and m_encoding stores the result.
auto effective_encoding = encoding();
if (!effective_encoding)
effective_encoding = Encoding::standard_encoding();
// "If a (3, 1) “cmap” subtable (Microsoft Unicode) is present: // "If a (3, 1) “cmap” subtable (Microsoft Unicode) is present:
// • A character code is first mapped to a glyph name using the table described above. // • A character code is first mapped to a glyph name using the table described above.
@ -126,7 +105,7 @@ PDFErrorOr<void> TrueTypeFont::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint
// Implementor's note: We currently don't know which tables are present, so we for now we always // Implementor's note: We currently don't know which tables are present, so we for now we always
// use the (3, 1) algorithm. // use the (3, 1) algorithm.
// FIXME: Implement (1, 0) subtable support. // FIXME: Implement (1, 0) subtable support.
auto char_name = effective_encoding->get_name(char_code); auto char_name = m_encoding->get_name(char_code);
u32 unicode = glyph_name_to_unicode(char_name).value_or(char_code); u32 unicode = glyph_name_to_unicode(char_name).value_or(char_code);
if (m_font->contains_glyph(unicode)) { if (m_font->contains_glyph(unicode)) {
do_draw_glyph(painter, point, width, unicode, *m_font, style); do_draw_glyph(painter, point, width, unicode, *m_font, style);
@ -152,10 +131,7 @@ PDFErrorOr<void> TrueTypeFont::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint
} else { } else {
// "If a character cannot be mapped in any of the ways described above, the results are implementation-dependent." // "If a character cannot be mapped in any of the ways described above, the results are implementation-dependent."
// FIXME: Do something smarter? // FIXME: Do something smarter?
auto effective_encoding = encoding(); auto char_name = m_encoding->get_name(char_code);
if (!effective_encoding)
effective_encoding = Encoding::standard_encoding();
auto char_name = effective_encoding->get_name(char_code);
unicode = glyph_name_to_unicode(char_name).value_or(char_code); unicode = glyph_name_to_unicode(char_name).value_or(char_code);
} }
@ -163,4 +139,60 @@ PDFErrorOr<void> TrueTypeFont::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint
return {}; return {};
} }
Optional<float> TrueTypePainter::get_glyph_width(u8 char_code) const
{
// FIXME: Make this use the char_code lookup method used in draw_glyph().
return m_font->glyph_width(char_code);
}
void TrueTypePainter::set_font_size(float font_size)
{
// Guaranteed non-null for ScaledFonts.
m_font = *m_font->with_size((font_size * POINTS_PER_INCH) / DEFAULT_DPI);
}
PDFErrorOr<void> TrueTypeFont::initialize(Document* document, NonnullRefPtr<DictObject> const& dict, float font_size)
{
TRY(SimpleFont::initialize(document, dict, font_size));
m_base_font_name = TRY(dict->get_name(document, CommonNames::BaseFont))->name();
// If there's an embedded font program we use that; otherwise we try to find a replacement font
RefPtr<Gfx::Font> font;
if (dict->contains(CommonNames::FontDescriptor)) {
auto descriptor = MUST(dict->get_dict(document, CommonNames::FontDescriptor));
if (descriptor->contains(CommonNames::FontFile2)) {
auto font_file_stream = TRY(descriptor->get_stream(document, CommonNames::FontFile2));
auto ttf_font = TRY(OpenType::Font::try_load_from_externally_owned_memory(font_file_stream->bytes(), { .skip_tables = pdf_skipped_opentype_tables }));
float point_size = (font_size * POINTS_PER_INCH) / DEFAULT_DPI;
font = adopt_ref(*new Gfx::ScaledFont(*ttf_font, point_size, point_size));
}
}
if (!font)
font = TRY(replacement_for(base_font_name().to_lowercase(), font_size));
auto effective_encoding = encoding();
if (!effective_encoding)
effective_encoding = Encoding::standard_encoding();
m_font_painter = TrueTypePainter::create(document, dict, *this, *font, *effective_encoding);
return {};
}
Optional<float> TrueTypeFont::get_glyph_width(u8 char_code) const
{
return m_font_painter->get_glyph_width(char_code);
}
void TrueTypeFont::set_font_size(float font_size)
{
m_font_painter->set_font_size(font_size);
}
PDFErrorOr<void> TrueTypeFont::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u8 char_code, Renderer const& renderer)
{
return m_font_painter->draw_glyph(painter, point, width, char_code, renderer);
}
} }

View file

@ -12,6 +12,24 @@
namespace PDF { namespace PDF {
class TrueTypePainter {
public:
static NonnullOwnPtr<TrueTypePainter> create(Document*, NonnullRefPtr<DictObject> const&, SimpleFont const& containing_pdf_font, AK::NonnullRefPtr<Gfx::Font>, NonnullRefPtr<Encoding>);
PDFErrorOr<void> draw_glyph(Gfx::Painter&, Gfx::FloatPoint, float width, u8 char_code, Renderer const&);
Optional<float> get_glyph_width(u8 char_code) const;
void set_font_size(float font_size);
private:
TrueTypePainter(AK::NonnullRefPtr<Gfx::Font>, NonnullRefPtr<Encoding>, bool encoding_is_mac_roman_or_win_ansi, bool is_nonsymbolic, Optional<u8> high_byte);
NonnullRefPtr<Gfx::Font> m_font;
NonnullRefPtr<Encoding> m_encoding;
bool m_encoding_is_mac_roman_or_win_ansi { false };
bool m_is_nonsymbolic { false };
Optional<u8> m_high_byte;
};
class TrueTypeFont : public SimpleFont { class TrueTypeFont : public SimpleFont {
public: public:
Optional<float> get_glyph_width(u8 char_code) const override; Optional<float> get_glyph_width(u8 char_code) const override;
@ -25,9 +43,10 @@ protected:
private: private:
DeprecatedFlyString m_base_font_name; DeprecatedFlyString m_base_font_name;
RefPtr<Gfx::Font> m_font;
bool m_encoding_is_mac_roman_or_win_ansi { false }; // Always non-null once initialize() has completed.
Optional<u8> m_high_byte; // FIXME: Move this class hierarchy to the usual fallible construction pattern and make this a NonnullOwnPtr.
OwnPtr<TrueTypePainter> m_font_painter;
}; };
} }