mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-22 23:50:19 +00:00
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:
parent
e8f1a2ef02
commit
cb04e4e9da
Notes:
sideshowbarker
2024-07-17 03:03:44 +09:00
Author: https://github.com/rtobar Commit: https://github.com/SerenityOS/serenity/commit/cb04e4e9da Pull-request: https://github.com/SerenityOS/serenity/pull/17446 Reviewed-by: https://github.com/linusg
12 changed files with 216 additions and 237 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
68
Userland/Libraries/LibPDF/Fonts/SimpleFont.cpp
Normal file
68
Userland/Libraries/LibPDF/Fonts/SimpleFont.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
33
Userland/Libraries/LibPDF/Fonts/SimpleFont.h
Normal file
33
Userland/Libraries/LibPDF/Fonts/SimpleFont.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
if (!descriptor->contains(CommonNames::FontFile))
|
||||
return data;
|
||||
|
||||
} 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 { Error::Type::Parse, "Embedded type 1 font is incomplete" };
|
||||
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>();
|
||||
|
||||
data.font_program = TRY(PS1FontProgram::create(font_file_stream->bytes(), data.encoding, length1, length2));
|
||||
|
||||
if (!data.encoding)
|
||||
data.encoding = data.font_program->encoding();
|
||||
m_font_program = TRY(PS1FontProgram::create(font_file_stream->bytes(), encoding(), length1, length2));
|
||||
}
|
||||
}
|
||||
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) {
|
||||
if (!m_font_program) {
|
||||
// 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);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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 {};
|
||||
|
|
Loading…
Reference in a new issue