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.
This commit is contained in:
Rodrigo Tobar 2023-02-01 00:52:23 +08:00 committed by Linus Groh
parent e8f1a2ef02
commit cb04e4e9da
Notes: sideshowbarker 2024-07-17 03:03:44 +09:00
12 changed files with 216 additions and 237 deletions

View file

@ -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

View file

@ -5,6 +5,7 @@
*/
#include <AK/String.h>
#include <AK/StringView.h>
#include <LibGfx/Font/FontDatabase.h>
#include <LibPDF/CommonNames.h>
#include <LibPDF/Fonts/PDFFont.h>
@ -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<void> PDFFont::CommonData::load_from_dict(Document* document, NonnullRefPtr<DictObject> 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<int>();
auto last_char = dict->get_value(CommonNames::LastChar).get<int>();
auto widths_array = TRY(dict->get_array(document, CommonNames::Widths));
VERIFY(widths_array->size() == static_cast<size_t>(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<NonnullRefPtr<PDFFont>> PDFFont::create(Document* document, NonnullRefPtr<DictObject> dict, float font_size)
PDFErrorOr<NonnullRefPtr<PDFFont>> PDFFont::create(Document* document, NonnullRefPtr<DictObject> 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<PDFFont> 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<DeprecatedString, DeprecatedString> PDFFont::replacement_for_standard_latin_font(StringView name)
PDFErrorOr<void> PDFFont::initialize(Document* document, NonnullRefPtr<DictObject> const& dict, float)
{
m_base_font_name = TRY(dict->get_name(document, CommonNames::BaseFont))->name();
return {};
}
PDFErrorOr<NonnullRefPtr<Gfx::Font>> 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<DeprecatedString, DeprecatedString> 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();
}
}

View file

@ -22,35 +22,21 @@ public:
TrueType
};
// This is used both by Type 1 and TrueType fonts.
struct CommonData {
DeprecatedFlyString base_font_name;
RefPtr<Gfx::Font> font;
RefPtr<StreamObject> to_unicode;
RefPtr<Encoding> encoding;
HashMap<u16, u16> widths;
u16 missing_width;
bool is_standard_font;
PDFErrorOr<void> load_from_dict(Document*, NonnullRefPtr<DictObject>, float font_size);
};
static PDFErrorOr<NonnullRefPtr<PDFFont>> create(Document*, NonnullRefPtr<DictObject>, float font_size);
static PDFErrorOr<NonnullRefPtr<PDFFont>> create(Document*, NonnullRefPtr<DictObject> const&, float font_size);
virtual ~PDFFont() = default;
virtual float get_char_width(u16 char_code) const = 0;
virtual PDFErrorOr<Gfx::FloatPoint> 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<DeprecatedString, DeprecatedString> replacement_for_standard_latin_font(StringView);
virtual PDFErrorOr<void> initialize(Document* document, NonnullRefPtr<DictObject> const& dict, float font_size);
static PDFErrorOr<NonnullRefPtr<Gfx::Font>> replacement_for(StringView name, float font_size);
bool m_is_standard_font { false };
private:
DeprecatedFlyString m_base_font_name;
};
}

View file

@ -0,0 +1,68 @@
/*
* Copyright (c) 2023, Rodrigo Tobar <rtobarc@gmail.com>.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibGfx/Forward.h>
#include <LibPDF/CommonNames.h>
#include <LibPDF/Error.h>
#include <LibPDF/Fonts/PDFFont.h>
#include <LibPDF/Fonts/SimpleFont.h>
#include <LibPDF/Fonts/TrueTypeFont.h>
#include <LibPDF/Fonts/Type1Font.h>
namespace PDF {
PDFErrorOr<void> SimpleFont::initialize(Document* document, NonnullRefPtr<DictObject> 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<int>();
auto last_char = dict->get_value(CommonNames::LastChar).get<int>();
auto widths_array = TRY(dict->get_array(document, CommonNames::Widths));
VERIFY(widths_array->size() == static_cast<size_t>(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<float>(m_widths.get(char_code).value_or(m_missing_width)) / 1000.0f;
}
PDFErrorOr<Gfx::FloatPoint> 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<StringObject>(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;
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2023, Rodrigo Tobar <rtobarc@gmail.com>.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/Point.h>
#include <LibPDF/Fonts/PDFFont.h>
namespace PDF {
class SimpleFont : public PDFFont {
public:
PDFErrorOr<Gfx::FloatPoint> draw_string(Gfx::Painter&, Gfx::FloatPoint, DeprecatedString const&, Color const&, float font_size, float character_spacing, float horizontal_scaling) override;
protected:
PDFErrorOr<void> initialize(Document* document, NonnullRefPtr<DictObject> 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>& encoding() { return m_encoding; }
RefPtr<Encoding> const& encoding() const { return m_encoding; }
private:
float get_char_width(u8 char_code) const;
RefPtr<Encoding> m_encoding;
RefPtr<StreamObject> m_to_unicode;
HashMap<u8, u16> m_widths;
u16 m_missing_width;
};
}

View file

@ -13,57 +13,31 @@
namespace PDF {
PDFErrorOr<PDFFont::CommonData> TrueTypeFont::parse_data(Document* document, NonnullRefPtr<DictObject> dict, float font_size)
PDFErrorOr<void> TrueTypeFont::initialize(Document* document, NonnullRefPtr<DictObject> 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<NonnullRefPtr<PDFFont>> TrueTypeFont::create(Document* document, NonnullRefPtr<DictObject> 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<float>(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);
}
}

View file

@ -8,28 +8,20 @@
#include <AK/HashMap.h>
#include <LibGfx/Font/OpenType/Font.h>
#include <LibPDF/Fonts/PDFFont.h>
#include <LibPDF/Fonts/SimpleFont.h>
namespace PDF {
class TrueTypeFont : public PDFFont {
class TrueTypeFont : public SimpleFont {
public:
static PDFErrorOr<PDFFont::CommonData> parse_data(Document* document, NonnullRefPtr<DictObject> dict, float font_size);
static PDFErrorOr<NonnullRefPtr<PDFFont>> create(Document*, NonnullRefPtr<DictObject>, 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<void> initialize(Document*, NonnullRefPtr<DictObject> const&, float font_size) override;
private:
PDFFont::CommonData m_data;
RefPtr<Gfx::Font> m_font;
};
}

View file

@ -9,8 +9,10 @@
namespace PDF {
PDFErrorOr<NonnullRefPtr<Type0Font>> Type0Font::create(Document* document, NonnullRefPtr<DictObject> dict)
PDFErrorOr<void> Type0Font::initialize(Document* document, NonnullRefPtr<DictObject> 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<NameObject>() || cmap_value->cast<NameObject>()->name() != CommonNames::IdentityH)
@ -67,14 +69,10 @@ PDFErrorOr<NonnullRefPtr<Type0Font>> Type0Font::create(Document* document, Nonnu
}
}
return adopt_ref(*new Type0Font(system_info, widths, default_width));
}
Type0Font::Type0Font(CIDSystemInfo const& system_info, HashMap<u16, u16> 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<float>(width) / 1000.0f;
}
PDFErrorOr<Gfx::FloatPoint> Type0Font::draw_string(Gfx::Painter&, Gfx::FloatPoint, DeprecatedString const&, Color const&, float, float, float)
{
return Error::rendering_unsupported_error("Type0 font not implemented yet");
};
}

View file

@ -19,19 +19,15 @@ struct CIDSystemInfo {
class Type0Font : public PDFFont {
public:
static PDFErrorOr<NonnullRefPtr<Type0Font>> create(Document*, NonnullRefPtr<DictObject>);
Type0Font(CIDSystemInfo const&, HashMap<u16, u16> 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<Gfx::FloatPoint> 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<void> initialize(Document*, NonnullRefPtr<DictObject> const&, float) override;
private:
float get_char_width(u16 char_code) const;
CIDSystemInfo m_system_info;
HashMap<u16, u16> m_widths;
u16 m_missing_width;

View file

@ -13,81 +13,58 @@
namespace PDF {
PDFErrorOr<Type1Font::Data> Type1Font::parse_data(Document* document, NonnullRefPtr<DictObject> dict, float font_size)
PDFErrorOr<void> Type1Font::initialize(Document* document, NonnullRefPtr<DictObject> 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<int>();
auto length2 = TRY(document->resolve(font_file_dict->get_value(CommonNames::Length2))).get<int>();
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<int>();
auto length2 = TRY(document->resolve(font_file_dict->get_value(CommonNames::Length2))).get<int>();
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<NonnullRefPtr<Type1Font>> Type1Font::create(Document* document, NonnullRefPtr<DictObject> 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<float>(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);
}

View file

@ -7,33 +7,22 @@
#pragma once
#include <LibGfx/Font/ScaledFont.h>
#include <LibPDF/Fonts/PDFFont.h>
#include <LibPDF/Fonts/SimpleFont.h>
#include <LibPDF/Fonts/Type1FontProgram.h>
namespace PDF {
class Type1Font : public PDFFont {
class Type1Font : public SimpleFont {
public:
struct Data : PDFFont::CommonData {
RefPtr<Type1FontProgram> font_program;
};
static PDFErrorOr<Data> parse_data(Document*, NonnullRefPtr<DictObject> font_dict, float font_size);
static PDFErrorOr<NonnullRefPtr<Type1Font>> create(Document*, NonnullRefPtr<DictObject>, 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<void> initialize(Document*, NonnullRefPtr<DictObject> const&, float font_size) override;
private:
Data m_data;
RefPtr<Type1FontProgram> m_font_program;
RefPtr<Gfx::Font> m_font;
HashMap<Gfx::GlyphIndexWithSubpixelOffset, RefPtr<Gfx::Bitmap>> m_glyph_cache;
};

View file

@ -730,22 +730,11 @@ PDFErrorOr<void> 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 {};