From cb04e4e9da7b26980e16a974e2a4ff7bb9810b99 Mon Sep 17 00:00:00 2001 From: Rodrigo Tobar Date: Wed, 1 Feb 2023 00:52:23 +0800 Subject: [PATCH] LibPDF: Refactor *Font classes The PDFFont class hierarchy was very simple (a top-level PDFFont class, followed by all the children classes that derived directly from it). While this design was good enough for some things, it didn't correctly model the actual organization of font types: * PDF fonts are first divided between "simple" and "composite" fonts. The latter is the Type0 font, while the rest are all simple. * PDF fonts yield a glyph per "character code". Simple fonts char codes are always 1 byte long, while Type0 char codes are of variable size. To this effect, this commit changes the hierarchy of Font classes, introducing a new SimpleFont class, deriving from PDFFont, and acting as the parent of Type1Font and TrueTypeFont, while Type0 still derives from PDFFont directly. This distinction allows us now to: * Model string rendering differently from simple and composite fonts: PDFFont now offers a generic draw_string method that takes a whole string to be rendered instead of a single char code. SimpleFont implements this as a loop over individual bytes of the string, with T1 and TT implementing draw_glyph for drawing a single char code. * Some common fields between T1 and TT fonts now live under SimpleFont instead of under PDFfont, where they previously resided. * Some other interfaces specific to SimpleFont have been cleaned up, with u16/u32 not appearing on these classes (or in PDFFont) anymore. * Type0Font's rendering still remains unimplemented. As part of this exercise I also took the chance to perform the following cleanups and restructurings: * Refactored the creation and initialisation of fonts. They are all centrally created at PDFFont::create, with a virtual "initialize" method that allows them to initialise their inner members in the correct order (parent first, child later) after creation. * Removed duplicated code. * Cleaned up some public interfaces: receive const refs, removed unnecessary ctro/dtors, etc. * Slightly changed how Type1 and TrueType fonts are implemented: if there's an embedded font that takes priority, otherwise we always look for a replacement. * This means we don't do anything special for the standard fonts. The only behavior previously associated to standard fonts was choosing an encoding, and even that was under questioning. --- Userland/Libraries/LibPDF/CMakeLists.txt | 1 + Userland/Libraries/LibPDF/Fonts/PDFFont.cpp | 79 +++++---------- Userland/Libraries/LibPDF/Fonts/PDFFont.h | 28 ++---- .../Libraries/LibPDF/Fonts/SimpleFont.cpp | 68 +++++++++++++ Userland/Libraries/LibPDF/Fonts/SimpleFont.h | 33 +++++++ .../Libraries/LibPDF/Fonts/TrueTypeFont.cpp | 46 ++------- .../Libraries/LibPDF/Fonts/TrueTypeFont.h | 22 ++--- Userland/Libraries/LibPDF/Fonts/Type0Font.cpp | 21 ++-- Userland/Libraries/LibPDF/Fonts/Type0Font.h | 16 ++-- Userland/Libraries/LibPDF/Fonts/Type1Font.cpp | 95 +++++++------------ Userland/Libraries/LibPDF/Fonts/Type1Font.h | 27 ++---- Userland/Libraries/LibPDF/Renderer.cpp | 17 +--- 12 files changed, 216 insertions(+), 237 deletions(-) create mode 100644 Userland/Libraries/LibPDF/Fonts/SimpleFont.cpp create mode 100644 Userland/Libraries/LibPDF/Fonts/SimpleFont.h diff --git a/Userland/Libraries/LibPDF/CMakeLists.txt b/Userland/Libraries/LibPDF/CMakeLists.txt index 955d3b7c37..2862b23cf8 100644 --- a/Userland/Libraries/LibPDF/CMakeLists.txt +++ b/Userland/Libraries/LibPDF/CMakeLists.txt @@ -9,6 +9,7 @@ set(SOURCES Fonts/CFF.cpp Fonts/PDFFont.cpp Fonts/PS1FontProgram.cpp + Fonts/SimpleFont.cpp Fonts/TrueTypeFont.cpp Fonts/Type0Font.cpp Fonts/Type1Font.cpp diff --git a/Userland/Libraries/LibPDF/Fonts/PDFFont.cpp b/Userland/Libraries/LibPDF/Fonts/PDFFont.cpp index ed38f7d650..30eccd2f81 100644 --- a/Userland/Libraries/LibPDF/Fonts/PDFFont.cpp +++ b/Userland/Libraries/LibPDF/Fonts/PDFFont.cpp @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -14,7 +15,7 @@ namespace PDF { -static bool is_standard_latin_font(DeprecatedFlyString const& font) +[[maybe_unused]] static bool is_standard_latin_font(DeprecatedFlyString const& font) { return font.is_one_of( "Times-Roman", "TimesNewRoman", @@ -31,65 +32,31 @@ static bool is_standard_latin_font(DeprecatedFlyString const& font) "Courier-BoldOblique", "CourierNew,BoldItalic"); } -PDFErrorOr PDFFont::CommonData::load_from_dict(Document* document, NonnullRefPtr dict, float font_size) -{ - auto base_font = TRY(dict->get_name(document, CommonNames::BaseFont))->name(); - if ((is_standard_font = is_standard_latin_font(base_font))) { - auto replacement = replacement_for_standard_latin_font(base_font); - float point_size = (font_size * POINTS_PER_INCH) / DEFAULT_DPI; - font = Gfx::FontDatabase::the().get(replacement.get<0>(), replacement.get<1>(), point_size); - if (!font) - return Error(Error::Type::Internal, DeprecatedString::formatted("Failed to load {} {} at {}pt", replacement.get<0>(), replacement.get<1>(), point_size)); - } - - if (dict->contains(CommonNames::Encoding)) { - auto encoding_object = MUST(dict->get_object(document, CommonNames::Encoding)); - encoding = TRY(Encoding::from_object(document, encoding_object)); - } else { - // FIXME: The spec doesn't specify what the encoding should be in this case - if (is_standard_font) - encoding = Encoding::standard_encoding(); - // Otherwise, use the built-in encoding of the font. - } - - if (dict->contains(CommonNames::ToUnicode)) - to_unicode = TRY(dict->get_stream(document, CommonNames::ToUnicode)); - - if (dict->contains(CommonNames::FirstChar) && dict->contains(CommonNames::LastChar) && dict->contains(CommonNames::Widths)) { - auto first_char = dict->get_value(CommonNames::FirstChar).get(); - auto last_char = dict->get_value(CommonNames::LastChar).get(); - auto widths_array = TRY(dict->get_array(document, CommonNames::Widths)); - - VERIFY(widths_array->size() == static_cast(last_char - first_char + 1)); - - for (size_t i = 0; i < widths_array->size(); i++) - widths.set(first_char + i, widths_array->at(i).to_int()); - } - - if (dict->contains(CommonNames::FontDescriptor)) { - auto descriptor = TRY(dict->get_dict(document, CommonNames::FontDescriptor)); - if (descriptor->contains(CommonNames::MissingWidth)) - missing_width = descriptor->get_value(CommonNames::MissingWidth).to_int(); - } - - return {}; -} - -PDFErrorOr> PDFFont::create(Document* document, NonnullRefPtr dict, float font_size) +PDFErrorOr> PDFFont::create(Document* document, NonnullRefPtr const& dict, float font_size) { auto subtype = TRY(dict->get_name(document, CommonNames::Subtype))->name(); - if (subtype == "Type0") - return TRY(Type0Font::create(document, dict)); + RefPtr font; if (subtype == "Type1") - return TRY(Type1Font::create(document, dict, font_size)); - if (subtype == "TrueType") - return TRY(TrueTypeFont::create(document, dict, font_size)); + font = adopt_ref(*new Type1Font()); + else if (subtype == "TrueType") + font = adopt_ref(*new TrueTypeFont()); + else if (subtype == "Type0") + font = adopt_ref(*new Type0Font()); + else + return Error::internal_error("Unhandled font subtype: {}", subtype); - return Error(Error::Type::Internal, TRY(String::formatted("Unhandled font subtype: {}", subtype)).to_deprecated_string()); + TRY(font->initialize(document, dict, font_size)); + return font.release_nonnull(); } -Tuple PDFFont::replacement_for_standard_latin_font(StringView name) +PDFErrorOr PDFFont::initialize(Document* document, NonnullRefPtr const& dict, float) +{ + m_base_font_name = TRY(dict->get_name(document, CommonNames::BaseFont))->name(); + return {}; +} + +PDFErrorOr> PDFFont::replacement_for(StringView name, float font_size) { bool is_bold = name.contains("bold"sv, CaseSensitivity::CaseInsensitive); bool is_italic = name.contains("italic"sv, CaseSensitivity::CaseInsensitive); @@ -115,7 +82,11 @@ Tuple PDFFont::replacement_for_standard_lati font_variant = "Regular"; } - return { font_family, font_variant }; + float point_size = (font_size * POINTS_PER_INCH) / DEFAULT_DPI; + auto font = Gfx::FontDatabase::the().get(font_family, font_variant, point_size); + if (!font) + Error::internal_error("Failed to load {} {} at {}pt", font_family, font_variant, point_size); + return font.release_nonnull(); } } diff --git a/Userland/Libraries/LibPDF/Fonts/PDFFont.h b/Userland/Libraries/LibPDF/Fonts/PDFFont.h index 57d1df9a40..8bd9d62737 100644 --- a/Userland/Libraries/LibPDF/Fonts/PDFFont.h +++ b/Userland/Libraries/LibPDF/Fonts/PDFFont.h @@ -22,35 +22,21 @@ public: TrueType }; - // This is used both by Type 1 and TrueType fonts. - struct CommonData { - DeprecatedFlyString base_font_name; - RefPtr font; - RefPtr to_unicode; - RefPtr encoding; - HashMap widths; - u16 missing_width; - bool is_standard_font; - - PDFErrorOr load_from_dict(Document*, NonnullRefPtr, float font_size); - }; - - static PDFErrorOr> create(Document*, NonnullRefPtr, float font_size); + static PDFErrorOr> create(Document*, NonnullRefPtr const&, float font_size); virtual ~PDFFont() = default; - virtual float get_char_width(u16 char_code) const = 0; + virtual PDFErrorOr draw_string(Gfx::Painter&, Gfx::FloatPoint, DeprecatedString const&, Color const&, float font_size, float character_spacing, float horizontal_scaling) = 0; - virtual void draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u32 char_code, Color color) = 0; - - virtual bool is_standard_font() const { return m_is_standard_font; } virtual Type type() const = 0; - virtual DeprecatedFlyString base_font_name() const = 0; + DeprecatedFlyString base_font_name() const { return m_base_font_name; }; protected: - static Tuple replacement_for_standard_latin_font(StringView); + virtual PDFErrorOr initialize(Document* document, NonnullRefPtr const& dict, float font_size); + static PDFErrorOr> replacement_for(StringView name, float font_size); - bool m_is_standard_font { false }; +private: + DeprecatedFlyString m_base_font_name; }; } diff --git a/Userland/Libraries/LibPDF/Fonts/SimpleFont.cpp b/Userland/Libraries/LibPDF/Fonts/SimpleFont.cpp new file mode 100644 index 0000000000..358e22024a --- /dev/null +++ b/Userland/Libraries/LibPDF/Fonts/SimpleFont.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023, Rodrigo Tobar . + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace PDF { + +PDFErrorOr SimpleFont::initialize(Document* document, NonnullRefPtr const& dict, float font_size) +{ + TRY(PDFFont::initialize(document, dict, font_size)); + if (dict->contains(CommonNames::Encoding)) { + auto encoding_object = MUST(dict->get_object(document, CommonNames::Encoding)); + m_encoding = TRY(Encoding::from_object(document, encoding_object)); + } + + if (dict->contains(CommonNames::ToUnicode)) + m_to_unicode = TRY(dict->get_stream(document, CommonNames::ToUnicode)); + + if (dict->contains(CommonNames::FirstChar) && dict->contains(CommonNames::LastChar) && dict->contains(CommonNames::Widths)) { + auto first_char = dict->get_value(CommonNames::FirstChar).get(); + auto last_char = dict->get_value(CommonNames::LastChar).get(); + auto widths_array = TRY(dict->get_array(document, CommonNames::Widths)); + + VERIFY(widths_array->size() == static_cast(last_char - first_char + 1)); + + for (size_t i = 0; i < widths_array->size(); i++) + m_widths.set(first_char + i, widths_array->at(i).to_int()); + } + + if (dict->contains(CommonNames::FontDescriptor)) { + auto descriptor = TRY(dict->get_dict(document, CommonNames::FontDescriptor)); + if (descriptor->contains(CommonNames::MissingWidth)) + m_missing_width = descriptor->get_value(CommonNames::MissingWidth).to_int(); + } + + return {}; +} + +float SimpleFont::get_char_width(u8 char_code) const +{ + return static_cast(m_widths.get(char_code).value_or(m_missing_width)) / 1000.0f; +} + +PDFErrorOr SimpleFont::draw_string(Gfx::Painter& painter, Gfx::FloatPoint glyph_position, DeprecatedString const& string, Color const& paint_color, float font_size, float character_spacing, float horizontal_scaling) +{ + auto so = make_object(string, true); + for (auto char_code : string.bytes()) { + auto char_width = get_char_width(char_code); + auto glyph_width = char_width * font_size; + draw_glyph(painter, glyph_position, glyph_width, char_code, paint_color); + auto tx = glyph_width; + tx += character_spacing; + tx *= horizontal_scaling; + glyph_position += { tx, 0.0f }; + } + return glyph_position; +} + +} diff --git a/Userland/Libraries/LibPDF/Fonts/SimpleFont.h b/Userland/Libraries/LibPDF/Fonts/SimpleFont.h new file mode 100644 index 0000000000..61bc7b2eed --- /dev/null +++ b/Userland/Libraries/LibPDF/Fonts/SimpleFont.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023, Rodrigo Tobar . + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace PDF { + +class SimpleFont : public PDFFont { +public: + PDFErrorOr draw_string(Gfx::Painter&, Gfx::FloatPoint, DeprecatedString const&, Color const&, float font_size, float character_spacing, float horizontal_scaling) override; + +protected: + PDFErrorOr initialize(Document* document, NonnullRefPtr const& dict, float font_size) override; + virtual void draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u8 char_code, Color color) = 0; + RefPtr& encoding() { return m_encoding; } + RefPtr const& encoding() const { return m_encoding; } + +private: + float get_char_width(u8 char_code) const; + + RefPtr m_encoding; + RefPtr m_to_unicode; + HashMap m_widths; + u16 m_missing_width; +}; + +} diff --git a/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.cpp b/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.cpp index 5cd2bc7bee..369830b1ed 100644 --- a/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.cpp +++ b/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.cpp @@ -13,57 +13,31 @@ namespace PDF { -PDFErrorOr TrueTypeFont::parse_data(Document* document, NonnullRefPtr dict, float font_size) +PDFErrorOr TrueTypeFont::initialize(Document* document, NonnullRefPtr const& dict, float font_size) { - PDFFont::CommonData data; - TRY(data.load_from_dict(document, dict, font_size)); + TRY(SimpleFont::initialize(document, dict, font_size)); - if (!data.is_standard_font) { - auto descriptor = TRY(dict->get_dict(document, CommonNames::FontDescriptor)); + 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())); float point_size = (font_size * POINTS_PER_INCH) / DEFAULT_DPI; - data.font = adopt_ref(*new Gfx::ScaledFont(*ttf_font, point_size, point_size)); + m_font = adopt_ref(*new Gfx::ScaledFont(*ttf_font, font_size, point_size)); } } - return data; + return {}; } -PDFErrorOr> TrueTypeFont::create(Document* document, NonnullRefPtr dict, float font_size) +void TrueTypeFont::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float, u8 char_code, Color color) { - auto data = TRY(parse_data(document, dict, font_size)); - return adopt_ref(*new TrueTypeFont(move(data))); -} - -TrueTypeFont::TrueTypeFont(PDFFont::CommonData data) - : m_data(data) -{ - m_is_standard_font = m_data.is_standard_font; -} - -float TrueTypeFont::get_char_width(u16 char_code) const -{ - u16 width; - if (auto char_code_width = m_data.widths.get(char_code); char_code_width.has_value()) { - width = char_code_width.value(); - } else { - // FIXME: Should we do something with m_data.missing_width here? - width = m_data.font->glyph_width(char_code); - } - - return static_cast(width) / 1000.0f; -} - -void TrueTypeFont::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float, u32 char_code, Color color) -{ - if (!m_data.font) + if (!m_font) return; // Account for the reversed font baseline - auto position = point.translated(0, -m_data.font->baseline()); - painter.draw_glyph(position, char_code, *m_data.font, color); + auto position = point.translated(0, -m_font->baseline()); + painter.draw_glyph(position, char_code, *m_font, color); } } diff --git a/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.h b/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.h index bdfeac2545..02fba97f5b 100644 --- a/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.h +++ b/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.h @@ -8,28 +8,20 @@ #include #include -#include +#include namespace PDF { -class TrueTypeFont : public PDFFont { +class TrueTypeFont : public SimpleFont { public: - static PDFErrorOr parse_data(Document* document, NonnullRefPtr dict, float font_size); - - static PDFErrorOr> create(Document*, NonnullRefPtr, float font_size); - - TrueTypeFont(PDFFont::CommonData); - ~TrueTypeFont() override = default; - - float get_char_width(u16 char_code) const override; - - void draw_glyph(Gfx::Painter&, Gfx::FloatPoint, float, u32, Color) override; - + void draw_glyph(Gfx::Painter&, Gfx::FloatPoint, float, u8, Color) override; Type type() const override { return PDFFont::Type::TrueType; } - DeprecatedFlyString base_font_name() const override { return m_data.base_font_name; } + +protected: + PDFErrorOr initialize(Document*, NonnullRefPtr const&, float font_size) override; private: - PDFFont::CommonData m_data; + RefPtr m_font; }; } diff --git a/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp b/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp index 0febd0934e..e88ee96937 100644 --- a/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp +++ b/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp @@ -9,8 +9,10 @@ namespace PDF { -PDFErrorOr> Type0Font::create(Document* document, NonnullRefPtr dict) +PDFErrorOr Type0Font::initialize(Document* document, NonnullRefPtr const& dict, float font_size) { + TRY(PDFFont::initialize(document, dict, font_size)); + // FIXME: Support arbitrary CMaps auto cmap_value = TRY(dict->get_object(document, CommonNames::Encoding)); if (!cmap_value->is() || cmap_value->cast()->name() != CommonNames::IdentityH) @@ -67,14 +69,10 @@ PDFErrorOr> Type0Font::create(Document* document, Nonnu } } - return adopt_ref(*new Type0Font(system_info, widths, default_width)); -} - -Type0Font::Type0Font(CIDSystemInfo const& system_info, HashMap const& widths, u16 missing_width) - : m_system_info(system_info) - , m_widths(widths) - , m_missing_width(missing_width) -{ + m_system_info = move(system_info); + m_widths = move(widths); + m_missing_width = default_width; + return {}; } float Type0Font::get_char_width(u16 char_code) const @@ -89,4 +87,9 @@ float Type0Font::get_char_width(u16 char_code) const return static_cast(width) / 1000.0f; } +PDFErrorOr Type0Font::draw_string(Gfx::Painter&, Gfx::FloatPoint, DeprecatedString const&, Color const&, float, float, float) +{ + return Error::rendering_unsupported_error("Type0 font not implemented yet"); +}; + } diff --git a/Userland/Libraries/LibPDF/Fonts/Type0Font.h b/Userland/Libraries/LibPDF/Fonts/Type0Font.h index d5745e899e..83bc785f78 100644 --- a/Userland/Libraries/LibPDF/Fonts/Type0Font.h +++ b/Userland/Libraries/LibPDF/Fonts/Type0Font.h @@ -19,19 +19,15 @@ struct CIDSystemInfo { class Type0Font : public PDFFont { public: - static PDFErrorOr> create(Document*, NonnullRefPtr); - - Type0Font(CIDSystemInfo const&, HashMap const& widths, u16 missing_width); - ~Type0Font() override = default; - - float get_char_width(u16 char_code) const override; - - void draw_glyph(Gfx::Painter&, Gfx::FloatPoint, float, u32, Color) override {}; - + PDFErrorOr draw_string(Gfx::Painter&, Gfx::FloatPoint pos, DeprecatedString const&, Color const&, float, float, float) override; Type type() const override { return PDFFont::Type::Type0; } - DeprecatedFlyString base_font_name() const override { return ""; } + +protected: + PDFErrorOr initialize(Document*, NonnullRefPtr const&, float) override; private: + float get_char_width(u16 char_code) const; + CIDSystemInfo m_system_info; HashMap m_widths; u16 m_missing_width; diff --git a/Userland/Libraries/LibPDF/Fonts/Type1Font.cpp b/Userland/Libraries/LibPDF/Fonts/Type1Font.cpp index 4c97c98701..49a828e3d8 100644 --- a/Userland/Libraries/LibPDF/Fonts/Type1Font.cpp +++ b/Userland/Libraries/LibPDF/Fonts/Type1Font.cpp @@ -13,81 +13,58 @@ namespace PDF { -PDFErrorOr Type1Font::parse_data(Document* document, NonnullRefPtr dict, float font_size) +PDFErrorOr Type1Font::initialize(Document* document, NonnullRefPtr const& dict, float font_size) { - Type1Font::Data data; - TRY(data.load_from_dict(document, dict, font_size)); + TRY(SimpleFont::initialize(document, dict, font_size)); - if (!data.is_standard_font) { + // auto is_standard_font = is_standard_latin_font(font->base_font_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 = TRY(dict->get_dict(document, CommonNames::FontDescriptor)); if (descriptor->contains(CommonNames::FontFile3)) { auto font_file_stream = TRY(descriptor->get_stream(document, CommonNames::FontFile3)); auto font_file_dict = font_file_stream->dict(); if (font_file_dict->contains(CommonNames::Subtype) && font_file_dict->get_name(CommonNames::Subtype)->name() == CommonNames::Type1C) { - data.font_program = TRY(CFF::create(font_file_stream->bytes(), data.encoding)); - if (!data.encoding) - data.encoding = data.font_program->encoding(); - return data; + m_font_program = TRY(CFF::create(font_file_stream->bytes(), encoding())); } + } else if (descriptor->contains(CommonNames::FontFile)) { + auto font_file_stream = TRY(descriptor->get_stream(document, CommonNames::FontFile)); + auto font_file_dict = font_file_stream->dict(); + + if (!font_file_dict->contains(CommonNames::Length1, CommonNames::Length2)) + return Error::parse_error("Embedded type 1 font is incomplete"sv); + + auto length1 = TRY(document->resolve(font_file_dict->get_value(CommonNames::Length1))).get(); + auto length2 = TRY(document->resolve(font_file_dict->get_value(CommonNames::Length2))).get(); + + m_font_program = TRY(PS1FontProgram::create(font_file_stream->bytes(), encoding(), length1, length2)); } - if (!descriptor->contains(CommonNames::FontFile)) - return data; - - auto font_file_stream = TRY(descriptor->get_stream(document, CommonNames::FontFile)); - auto font_file_dict = font_file_stream->dict(); - - if (!font_file_dict->contains(CommonNames::Length1, CommonNames::Length2)) - return Error { Error::Type::Parse, "Embedded type 1 font is incomplete" }; - - auto length1 = TRY(document->resolve(font_file_dict->get_value(CommonNames::Length1))).get(); - auto length2 = TRY(document->resolve(font_file_dict->get_value(CommonNames::Length2))).get(); - - data.font_program = TRY(PS1FontProgram::create(font_file_stream->bytes(), data.encoding, length1, length2)); - - if (!data.encoding) - data.encoding = data.font_program->encoding(); + } + if (!m_font_program) { + m_font = TRY(replacement_for(base_font_name().to_lowercase(), font_size)); } - return data; + VERIFY(m_font_program || m_font); + return {}; } -PDFErrorOr> Type1Font::create(Document* document, NonnullRefPtr dict, float font_size) +void Type1Font::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u8 char_code, Color color) { - auto data = TRY(Type1Font::parse_data(document, dict, font_size)); - return adopt_ref(*new Type1Font(data)); -} - -Type1Font::Type1Font(Data data) - : m_data(move(data)) -{ - m_is_standard_font = m_data.is_standard_font; -} - -float Type1Font::get_char_width(u16 char_code) const -{ - u16 width; - if (auto char_code_width = m_data.widths.get(char_code); char_code_width.has_value()) { - width = char_code_width.value(); - } else { - width = m_data.missing_width; - } - - return static_cast(width) / 1000.0f; -} - -void Type1Font::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u32 char_code, Color color) -{ - if (!m_data.font_program) { - if (m_data.font) { - // Account for the reversed font baseline - auto position = point.translated(0, -m_data.font->baseline()); - painter.draw_glyph(position, char_code, *m_data.font, color); - } + if (!m_font_program) { + // Account for the reversed font baseline + auto position = point.translated(0, -m_font->baseline()); + painter.draw_glyph(position, char_code, *m_font, color); return; } - auto char_name = m_data.encoding->get_name(char_code); - auto translation = m_data.font_program->glyph_translation(char_name, width); + auto effective_encoding = encoding(); + if (!effective_encoding) + effective_encoding = m_font_program->encoding(); + if (!effective_encoding) + effective_encoding = Encoding::standard_encoding(); + auto char_name = effective_encoding->get_name(char_code); + auto translation = m_font_program->glyph_translation(char_name, width); point = point.translated(translation); auto glyph_position = Gfx::GlyphRasterPosition::get_nearest_fit_for(point); @@ -98,7 +75,7 @@ void Type1Font::draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float w if (maybe_bitmap.has_value()) { bitmap = maybe_bitmap.value(); } else { - bitmap = m_data.font_program->rasterize_glyph(char_name, width, glyph_position.subpixel_offset); + bitmap = m_font_program->rasterize_glyph(char_name, width, glyph_position.subpixel_offset); m_glyph_cache.set(index, bitmap); } diff --git a/Userland/Libraries/LibPDF/Fonts/Type1Font.h b/Userland/Libraries/LibPDF/Fonts/Type1Font.h index 04d7c36ca6..c85df6d7b0 100644 --- a/Userland/Libraries/LibPDF/Fonts/Type1Font.h +++ b/Userland/Libraries/LibPDF/Fonts/Type1Font.h @@ -7,33 +7,22 @@ #pragma once #include -#include +#include #include namespace PDF { -class Type1Font : public PDFFont { +class Type1Font : public SimpleFont { public: - struct Data : PDFFont::CommonData { - RefPtr font_program; - }; - - static PDFErrorOr parse_data(Document*, NonnullRefPtr font_dict, float font_size); - - static PDFErrorOr> create(Document*, NonnullRefPtr, float font_size); - - Type1Font(Data); - ~Type1Font() override = default; - - float get_char_width(u16 char_code) const override; - - void draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u32 char_code, Color color) override; - + void draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u8 char_code, Color color) override; Type type() const override { return PDFFont::Type::Type1; } - DeprecatedFlyString base_font_name() const override { return m_data.base_font_name; }; + +protected: + PDFErrorOr initialize(Document*, NonnullRefPtr const&, float font_size) override; private: - Data m_data; + RefPtr m_font_program; + RefPtr m_font; HashMap> m_glyph_cache; }; diff --git a/Userland/Libraries/LibPDF/Renderer.cpp b/Userland/Libraries/LibPDF/Renderer.cpp index 91b93ed0de..98f2d2a206 100644 --- a/Userland/Libraries/LibPDF/Renderer.cpp +++ b/Userland/Libraries/LibPDF/Renderer.cpp @@ -730,22 +730,11 @@ PDFErrorOr Renderer::show_text(DeprecatedString const& string) auto font_size = text_rendering_matrix.x_scale() * text_state().font_size; - auto glyph_position = text_rendering_matrix.map(Gfx::FloatPoint { 0.0f, 0.0f }); - - auto original_position = glyph_position; - - for (auto char_code : string.bytes()) { - auto char_width = text_state().font->get_char_width(char_code); - auto glyph_width = char_width * font_size; - text_state().font->draw_glyph(m_painter, glyph_position, glyph_width, char_code, state().paint_color); - auto tx = glyph_width; - tx += text_state().character_spacing; - tx *= text_state().horizontal_scaling; - glyph_position += { tx, 0.0f }; - } + auto start_position = text_rendering_matrix.map(Gfx::FloatPoint { 0.0f, 0.0f }); + auto end_position = TRY(text_state().font->draw_string(m_painter, start_position, string, state().paint_color, font_size, text_state().character_spacing, text_state().horizontal_scaling)); // Update text matrix - auto delta_x = glyph_position.x() - original_position.x(); + auto delta_x = end_position.x() - start_position.x(); m_text_rendering_matrix_is_dirty = true; m_text_matrix.translate(delta_x / text_rendering_matrix.x_scale(), 0.0f); return {};