diff --git a/Userland/Libraries/LibPDF/Fonts/PDFFont.cpp b/Userland/Libraries/LibPDF/Fonts/PDFFont.cpp index 5a9711f132..a35cdb05ca 100644 --- a/Userland/Libraries/LibPDF/Fonts/PDFFont.cpp +++ b/Userland/Libraries/LibPDF/Fonts/PDFFont.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -29,10 +30,14 @@ static bool is_standard_latin_font(FlyString const& font) "Courier-BoldOblique"); } -PDFErrorOr PDFFont::CommonData::load_from_dict(Document* document, NonnullRefPtr dict) +PDFErrorOr PDFFont::CommonData::load_from_dict(Document* document, NonnullRefPtr dict, float font_size) { auto base_font = TRY(dict->get_name(document, CommonNames::BaseFont))->name(); - is_standard_font = is_standard_latin_font(base_font); + if ((is_standard_font = is_standard_latin_font(base_font))) { + auto replacement = replacement_for_standard_latin_font(base_font.to_lowercase()); + font = Gfx::FontDatabase::the().get(replacement.get<0>(), replacement.get<1>(), font_size); + VERIFY(font); + } if (dict->contains(CommonNames::Encoding)) { auto encoding_object = MUST(dict->get_object(document, CommonNames::Encoding)); @@ -67,19 +72,39 @@ PDFErrorOr PDFFont::CommonData::load_from_dict(Document* document, Nonnull return {}; } -PDFErrorOr> PDFFont::create(Document* document, NonnullRefPtr dict) +PDFErrorOr> PDFFont::create(Document* document, NonnullRefPtr dict, float font_size) { auto subtype = TRY(dict->get_name(document, CommonNames::Subtype))->name(); if (subtype == "Type0") return TRY(Type0Font::create(document, dict)); if (subtype == "Type1") - return TRY(Type1Font::create(document, dict)); + return TRY(Type1Font::create(document, dict, font_size)); if (subtype == "TrueType") - return TRY(TrueTypeFont::create(document, dict)); + return TRY(TrueTypeFont::create(document, dict, font_size)); dbgln("Unknown font subtype: {}", subtype); TODO(); } +Tuple PDFFont::replacement_for_standard_latin_font(StringView name) +{ + bool is_bold = name.contains("bold"sv); + bool is_italic = name.contains("italic"sv); + + String font_variant; + + if (is_bold && is_italic) { + font_variant = "BoldItalic"; + } else if (is_bold) { + font_variant = "Bold"; + } else if (is_italic) { + font_variant = "Italic"; + } else { + font_variant = "Regular"; + } + + return { "Liberation Serif", font_variant }; +} + } diff --git a/Userland/Libraries/LibPDF/Fonts/PDFFont.h b/Userland/Libraries/LibPDF/Fonts/PDFFont.h index 48823b918c..c6dc8f7509 100644 --- a/Userland/Libraries/LibPDF/Fonts/PDFFont.h +++ b/Userland/Libraries/LibPDF/Fonts/PDFFont.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include #include @@ -23,21 +24,22 @@ public: // This is used both by Type 1 and TrueType fonts. struct CommonData { + RefPtr font; RefPtr to_unicode; RefPtr encoding; HashMap widths; u16 missing_width; bool is_standard_font; - PDFErrorOr load_from_dict(Document*, NonnullRefPtr); + PDFErrorOr load_from_dict(Document*, NonnullRefPtr, float font_size); }; - static PDFErrorOr> create(Document*, NonnullRefPtr); + static PDFErrorOr> create(Document*, NonnullRefPtr, float font_size); virtual ~PDFFont() = default; virtual u32 char_code_to_code_point(u16 char_code) const = 0; - virtual float get_char_width(u16 char_code, float font_size) const = 0; + virtual float get_char_width(u16 char_code) const = 0; virtual void draw_glyph(Gfx::Painter& painter, Gfx::IntPoint const& point, float width, u32 char_code, Color color) = 0; @@ -45,6 +47,8 @@ public: virtual Type type() const = 0; protected: + static Tuple replacement_for_standard_latin_font(StringView); + bool m_is_standard_font { false }; }; diff --git a/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.cpp b/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.cpp index 69ce4d3ac7..89b7cfc623 100644 --- a/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.cpp +++ b/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.cpp @@ -1,38 +1,46 @@ /* * Copyright (c) 2022, Matthew Olsson + * Copyright (c) 2022, Julian Offenhäuser * * SPDX-License-Identifier: BSD-2-Clause */ #include +#include +#include #include #include -#include namespace PDF { -PDFErrorOr> TrueTypeFont::create(Document* document, NonnullRefPtr dict) +PDFErrorOr TrueTypeFont::parse_data(Document* document, NonnullRefPtr dict, float font_size) { - auto font_descriptor = TRY(dict->get_dict(document, CommonNames::FontDescriptor)); + PDFFont::CommonData data; + TRY(data.load_from_dict(document, dict, font_size)); - if (!dict->contains(CommonNames::FontFile2)) { - // FIXME: The TTF is one of the standard 14 fonts. These should be built into - // the system, and their attributes hardcoded. Until we have them, just - // treat this as a Type1 font (which are very similar to TTF fonts) - return TRY(Type1Font::create(document, dict)); + if (!data.is_standard_font) { + auto descriptor = MUST(dict->get_dict(document, CommonNames::FontDescriptor)); + if (!descriptor->contains(CommonNames::FontFile2)) + return data; + + auto font_file_stream = TRY(descriptor->get_stream(document, CommonNames::FontFile2)); + auto ttf_font = TRY(TTF::Font::try_load_from_externally_owned_memory(font_file_stream->bytes())); + data.font = adopt_ref(*new Gfx::ScaledFont(*ttf_font, font_size, font_size)); } - auto font_file = TRY(dict->get_stream(document, CommonNames::FontFile2)); - auto ttf_font = TRY(TTF::Font::try_load_from_externally_owned_memory(font_file->bytes())); - auto data = TRY(Type1Font::parse_data(document, dict)); - - return adopt_ref(*new TrueTypeFont(ttf_font, move(data))); + return data; } -TrueTypeFont::TrueTypeFont(NonnullRefPtr ttf_font, Type1Font::Data data) - : m_ttf_font(ttf_font) - , m_data(data) +PDFErrorOr> TrueTypeFont::create(Document* document, NonnullRefPtr dict, float font_size) { + 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 = data.is_standard_font; } u32 TrueTypeFont::char_code_to_code_point(u16 char_code) const @@ -44,23 +52,27 @@ u32 TrueTypeFont::char_code_to_code_point(u16 char_code) const return descriptor.code_point; } -float TrueTypeFont::get_char_width(u16 char_code, float font_size) const +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? - float units_per_em = m_ttf_font->units_per_em(); - auto scale = (font_size * DEFAULT_DPI) / (POINTS_PER_INCH * units_per_em); - - auto code_point = char_code_to_code_point(char_code); - auto id = m_ttf_font->glyph_id_for_code_point(code_point); - auto metrics = m_ttf_font->glyph_metrics(id, scale, scale); - width = metrics.advance_width; + width = m_data.font->glyph_width(char_code); } return static_cast(width) / 1000.0f; } +void TrueTypeFont::draw_glyph(Gfx::Painter& painter, Gfx::IntPoint const& point, float, u32 char_code, Color color) +{ + if (!m_data.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); +} + } diff --git a/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.h b/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.h index b2ffcbce75..68c3df28d4 100644 --- a/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.h +++ b/Userland/Libraries/LibPDF/Fonts/TrueTypeFont.h @@ -6,28 +6,30 @@ #pragma once +#include #include -#include +#include namespace PDF { class TrueTypeFont : public PDFFont { public: - static PDFErrorOr> create(Document*, NonnullRefPtr); + static PDFErrorOr parse_data(Document* document, NonnullRefPtr dict, float font_size); - TrueTypeFont(NonnullRefPtr ttf_font, Type1Font::Data); + static PDFErrorOr> create(Document*, NonnullRefPtr, float font_size); + + TrueTypeFont(PDFFont::CommonData); ~TrueTypeFont() override = default; u32 char_code_to_code_point(u16 char_code) const override; - float get_char_width(u16 char_code, float font_size) const override; + float get_char_width(u16 char_code) const override; - void draw_glyph(Gfx::Painter&, Gfx::IntPoint const&, float, u32, Color) override {}; + void draw_glyph(Gfx::Painter&, Gfx::IntPoint const&, float, u32, Color) override; Type type() const override { return PDFFont::Type::TrueType; } private: - NonnullRefPtr m_ttf_font; - Type1Font::Data m_data; + PDFFont::CommonData m_data; }; } diff --git a/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp b/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp index dbebb02610..402cb702cf 100644 --- a/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp +++ b/Userland/Libraries/LibPDF/Fonts/Type0Font.cpp @@ -82,7 +82,7 @@ u32 Type0Font::char_code_to_code_point(u16 char_code) const return char_code; } -float Type0Font::get_char_width(u16 char_code, float) const +float Type0Font::get_char_width(u16 char_code) const { u16 width; if (auto char_code_width = m_widths.get(char_code); char_code_width.has_value()) { diff --git a/Userland/Libraries/LibPDF/Fonts/Type0Font.h b/Userland/Libraries/LibPDF/Fonts/Type0Font.h index 9221867d78..08561c60fd 100644 --- a/Userland/Libraries/LibPDF/Fonts/Type0Font.h +++ b/Userland/Libraries/LibPDF/Fonts/Type0Font.h @@ -24,7 +24,7 @@ public: ~Type0Font() override = default; u32 char_code_to_code_point(u16 char_code) const override; - float get_char_width(u16 char_code, float font_size) const override; + float get_char_width(u16 char_code) const override; void draw_glyph(Gfx::Painter&, Gfx::IntPoint const&, float, u32, Color) override {}; diff --git a/Userland/Libraries/LibPDF/Fonts/Type1Font.cpp b/Userland/Libraries/LibPDF/Fonts/Type1Font.cpp index a8b5afc0aa..bc4533bcc1 100644 --- a/Userland/Libraries/LibPDF/Fonts/Type1Font.cpp +++ b/Userland/Libraries/LibPDF/Fonts/Type1Font.cpp @@ -5,91 +5,42 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include +#include #include #include namespace PDF { -static bool is_standard_latin_font(FlyString const& font) +PDFErrorOr Type1Font::parse_data(Document* document, NonnullRefPtr dict, float font_size) { - return font.is_one_of( - "Times-Roman", - "Helvetica", - "Courier", - "Times-Bold", - "Helvetica-Bold", - "Courier-Bold", - "Times-Italic", - "Helvetica-Oblique", - "Courier-Oblique", - "Times-BoldItalic", - "Helvetica-BoldOblique", - "Courier-BoldOblique"); -} + Type1Font::Data data; + TRY(data.load_from_dict(document, dict, font_size)); -PDFErrorOr Type1Font::parse_data(Document* document, NonnullRefPtr dict) -{ - // FIXME: "Required except for the standard 14 fonts"... - // "Beginning with PDF 1.5, the special treatment given to the standard 14 - // fonts is deprecated. [...] For backwards capability, conforming readers - // shall still provide the special treatment identifier for the standard - // 14 fonts." + if (!data.is_standard_font) { + auto descriptor = TRY(dict->get_dict(document, CommonNames::FontDescriptor)); + if (!descriptor->contains(CommonNames::FontFile)) + return data; - RefPtr encoding; + auto font_file_stream = TRY(descriptor->get_stream(document, CommonNames::FontFile)); + auto font_file_dict = font_file_stream->dict(); - if (dict->contains(CommonNames::Encoding)) { - auto encoding_object = MUST(dict->get_object(document, CommonNames::Encoding)); - encoding = TRY(Encoding::from_object(document, encoding_object)); - } else { - auto base_font = MUST(dict->get_name(document, CommonNames::BaseFont))->name(); - if (is_standard_latin_font(base_font)) { - // FIXME: The spec doesn't specify what the encoding should be in this case - encoding = Encoding::standard_encoding(); - } - // Otherwise, use the built-in encoding of the font program. + if (!font_file_dict->contains(CommonNames::Length1, CommonNames::Length2)) + return Error { Error::Type::Parse, "Embedded type 1 font is incomplete" }; + + auto length1 = font_file_dict->get_value(CommonNames::Length1).get(); + auto length2 = font_file_dict->get_value(CommonNames::Length2).get(); + + data.font_program = adopt_ref(*new PS1FontProgram()); + TRY(data.font_program->parse(font_file_stream->bytes(), length1, length2)); + data.encoding = data.font_program->encoding(); } - RefPtr to_unicode; - if (dict->contains(CommonNames::ToUnicode)) - to_unicode = MUST(dict->get_stream(document, CommonNames::ToUnicode)); - - auto first_char = dict->get_value(CommonNames::FirstChar).get(); - auto last_char = dict->get_value(CommonNames::LastChar).get(); - auto widths_array = MUST(dict->get_array(document, CommonNames::Widths)); - - VERIFY(widths_array->size() == static_cast(last_char - first_char + 1)); - - HashMap widths; - for (size_t i = 0; i < widths_array->size(); i++) - widths.set(first_char + i, widths_array->at(i).to_int()); - - u16 missing_width = 0; - auto descriptor = MUST(dict->get_dict(document, CommonNames::FontDescriptor)); - if (descriptor->contains(CommonNames::MissingWidth)) - missing_width = descriptor->get_value(CommonNames::MissingWidth).to_int(); - if (!descriptor->contains(CommonNames::FontFile)) - return Type1Font::Data { {}, to_unicode, encoding.release_nonnull(), move(widths), missing_width, true }; - - 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 = font_file_dict->get_value(CommonNames::Length1).get(); - auto length2 = font_file_dict->get_value(CommonNames::Length2).get(); - - auto font_program = adopt_ref(*new PS1FontProgram()); - TRY(font_program->parse(font_file_stream->bytes(), length1, length2)); - encoding = font_program->encoding(); - - return Type1Font::Data { font_program, to_unicode, encoding.release_nonnull(), move(widths), missing_width, false }; + return data; } -PDFErrorOr> Type1Font::create(Document* document, NonnullRefPtr dict) +PDFErrorOr> Type1Font::create(Document* document, NonnullRefPtr dict, float font_size) { - auto data = TRY(Type1Font::parse_data(document, dict)); + auto data = TRY(Type1Font::parse_data(document, dict, font_size)); return adopt_ref(*new Type1Font(data)); } @@ -108,7 +59,7 @@ u32 Type1Font::char_code_to_code_point(u16 char_code) const return descriptor.code_point; } -float Type1Font::get_char_width(u16 char_code, float) const +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()) { diff --git a/Userland/Libraries/LibPDF/Fonts/Type1Font.h b/Userland/Libraries/LibPDF/Fonts/Type1Font.h index 6be048a69f..a9b07048f1 100644 --- a/Userland/Libraries/LibPDF/Fonts/Type1Font.h +++ b/Userland/Libraries/LibPDF/Fonts/Type1Font.h @@ -6,7 +6,6 @@ #pragma once -#include #include #include @@ -14,25 +13,19 @@ namespace PDF { class Type1Font : public PDFFont { public: - // Also used by TrueTypeFont, which is very similar to Type1 - struct Data { + struct Data : PDFFont::CommonData { RefPtr font_program; - RefPtr to_unicode; - NonnullRefPtr encoding; - HashMap widths; - u16 missing_width; - bool is_standard_font; }; - static PDFErrorOr parse_data(Document*, NonnullRefPtr font_dict); + static PDFErrorOr parse_data(Document*, NonnullRefPtr font_dict, float font_size); - static PDFErrorOr> create(Document*, NonnullRefPtr); + static PDFErrorOr> create(Document*, NonnullRefPtr, float font_size); Type1Font(Data); ~Type1Font() override = default; u32 char_code_to_code_point(u16 char_code) const override; - float get_char_width(u16 char_code, float font_size) const override; + float get_char_width(u16 char_code) const override; void draw_glyph(Gfx::Painter& painter, Gfx::IntPoint const& point, float width, u32 char_code, Color color) override; diff --git a/Userland/Libraries/LibPDF/Renderer.cpp b/Userland/Libraries/LibPDF/Renderer.cpp index b692be016e..df7b5b09f6 100644 --- a/Userland/Libraries/LibPDF/Renderer.cpp +++ b/Userland/Libraries/LibPDF/Renderer.cpp @@ -347,31 +347,13 @@ RENDERER_HANDLER(text_set_font) auto target_font_name = MUST(m_document->resolve_to(args[0]))->name(); auto fonts_dictionary = MUST(m_page.resources->get_dict(m_document, CommonNames::Font)); auto font_dictionary = MUST(fonts_dictionary->get_dict(m_document, target_font_name)); - auto font = TRY(PDFFont::create(m_document, font_dictionary)); - text_state().font = font; - - // FIXME: We do not yet have the standard 14 fonts, as some of them are not open fonts, - // so we just use LiberationSerif for everything - - auto font_name = MUST(font_dictionary->get_name(m_document, CommonNames::BaseFont))->name().to_lowercase(); - auto font_view = font_name.view(); - bool is_bold = font_view.contains("bold"sv); - bool is_italic = font_view.contains("italic"sv); - - String font_variant; - - if (is_bold && is_italic) { - font_variant = "BoldItalic"; - } else if (is_bold) { - font_variant = "Bold"; - } else if (is_italic) { - font_variant = "Italic"; - } else { - font_variant = "Regular"; - } text_state().font_size = args[1].to_float(); - text_state().font_variant = font_variant; + + auto& text_rendering_matrix = calculate_text_rendering_matrix(); + auto font_size = text_rendering_matrix.x_scale() * text_state().font_size; + auto font = TRY(PDFFont::create(m_document, font_dictionary, font_size)); + text_state().font = font; m_text_rendering_matrix_is_dirty = true; return {}; @@ -643,36 +625,19 @@ void Renderer::show_text(String const& string) { auto& text_rendering_matrix = calculate_text_rendering_matrix(); - auto font_type = text_state().font->type(); 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 }); - RefPtr font; - - // For types other than Type 1 and the standard 14 fonts, use Liberation Serif for now - if (font_type != PDFFont::Type::Type1 || text_state().font->is_standard_font()) { - font = Gfx::FontDatabase::the().get(text_state().font_family, text_state().font_variant, font_size); - VERIFY(font); - - // Account for the reversed font baseline - glyph_position.set_y(glyph_position.y() - static_cast(font->baseline())); - } - auto original_position = glyph_position; for (auto char_code : string.bytes()) { auto code_point = text_state().font->char_code_to_code_point(char_code); - auto char_width = text_state().font->get_char_width(char_code, font_size); + auto char_width = text_state().font->get_char_width(char_code); auto glyph_width = char_width * font_size; - if (code_point != 0x20) { - if (font.is_null()) { - text_state().font->draw_glyph(m_painter, glyph_position.to_type(), glyph_width, code_point, state().paint_color); - } else { - m_painter.draw_glyph(glyph_position.to_type(), code_point, *font, state().paint_color); - } - } + if (code_point != 0x20) + text_state().font->draw_glyph(m_painter, glyph_position.to_type(), glyph_width, char_code, state().paint_color); auto tx = glyph_width; tx += text_state().character_spacing; diff --git a/Userland/Libraries/LibPDF/Renderer.h b/Userland/Libraries/LibPDF/Renderer.h index 75e57a80c3..9fb36c9d37 100644 --- a/Userland/Libraries/LibPDF/Renderer.h +++ b/Userland/Libraries/LibPDF/Renderer.h @@ -57,8 +57,6 @@ struct TextState { float word_spacing { 0.0f }; float horizontal_scaling { 1.0f }; float leading { 0.0f }; - FlyString font_family { "Liberation Serif" }; - String font_variant { "Regular" }; float font_size { 12.0f }; RefPtr font; TextRenderingMode rendering_mode { TextRenderingMode::Fill }; @@ -221,8 +219,6 @@ struct Formatter : Formatter { builder.appendff(" word_spacing={}\n", state.word_spacing); builder.appendff(" horizontal_scaling={}\n", state.horizontal_scaling); builder.appendff(" leading={}\n", state.leading); - builder.appendff(" font_family={}\n", state.font_family); - builder.appendff(" font_variant={}\n", state.font_variant); builder.appendff(" font_size={}\n", state.font_size); builder.appendff(" rendering_mode={}\n", state.rendering_mode); builder.appendff(" rise={}\n", state.rise);