LibPDF: Move text rendering strategy selection into SimpleFont

Seems a little neater. This excludes Type0Fonts for now, but it looks
like not much of the implementation would be shared.

No behaviour change intended.
This commit is contained in:
MacDue authored and Nico Weber committed 2026-07-20 22:17:51 -04:00
1 parent 1892b09b9f
commit 4bd3d4bf95
5 files changed
+46 -38

No files matched your search

@@ -36,10 +36,6 @@ public:
virtual void set_font_size(float font_size) = 0;
virtual PDFErrorOr<Gfx::FloatPoint> draw_string(Gfx::Painter&, Gfx::FloatPoint, ByteString const&, Renderer const&) = 0;
virtual PDFErrorOr<Gfx::FloatPoint> append_text_path(Gfx::Path&, Gfx::FloatPoint, ByteString const&, Renderer const&)
{
return Error { Error::Type::RenderingUnsupported, "append_text_path not implemented for font" };
}
virtual WritingMode writing_mode() const { return WritingMode::Horizontal; }
+30 -1
View File
@@ -100,6 +100,36 @@ PDFErrorOr<Gfx::FloatPoint> SimpleFont::append_text_path(Gfx::Path& path, Gfx::F
}
PDFErrorOr<Gfx::FloatPoint> SimpleFont::draw_string(Gfx::Painter& painter, Gfx::FloatPoint position, ByteString const& string, Renderer const& renderer)
{
auto const& text_rendering_matrix = renderer.calculate_text_rendering_matrix();
// Fast path: Use cached bitmap glyphs.
if (text_rendering_matrix.is_identity_or_translation_or_scale(Gfx::AffineTransform::AllowNegativeScaling::Yes))
return draw_axis_aligned_glyphs(painter, position, string, renderer);
// Slow path: Create a Gfx::Path for the string, transform it, then draw it. This handles arbitrary transforms.
if (auto end_position = draw_transformed_glyphs(painter, position, string, renderer); !end_position.is_error())
return end_position;
// Fallback to axis aligned glyphs in case `append_path` is unimplemented. This won't look correct.
return draw_axis_aligned_glyphs(painter, position, string, renderer);
}
PDFErrorOr<Gfx::FloatPoint> SimpleFont::draw_transformed_glyphs(Gfx::Painter& painter, Gfx::FloatPoint position, ByteString const& string, Renderer const& renderer)
{
Gfx::Path text_path;
Gfx::AntiAliasingPainter aa_painter(painter);
auto horizontal_scaling = renderer.text_state().horizontal_scaling;
auto text_rendering_matrix = renderer.calculate_text_rendering_matrix();
auto end_position = TRY(append_text_path(text_path, position, string, renderer));
// FIXME: This is a bit janky, but the font is already scaled by `m_text_rendering_matrix.x_scale() / horizontal_scaling`,
// which is included in `m_text_rendering_matrix`. So, we must scale it by the reciprocal. Also, the y-axis is flipped.
text_path.transform(text_rendering_matrix.multiply(
Gfx::AffineTransform {}
.set_scale(1 / text_rendering_matrix.x_scale() * horizontal_scaling,
-1 / text_rendering_matrix.x_scale() * horizontal_scaling)));
Renderer::fill_path_with_style(aa_painter, text_path, renderer.state().paint_style, renderer.state().paint_alpha_constant);
return end_position;
}
PDFErrorOr<Gfx::FloatPoint> SimpleFont::draw_axis_aligned_glyphs(Gfx::Painter& painter, Gfx::FloatPoint position, ByteString const& string, Renderer const& renderer)
{
auto horizontal_scaling = renderer.text_state().horizontal_scaling;
auto const& text_rendering_matrix = renderer.calculate_text_rendering_matrix();
@@ -110,5 +140,4 @@ PDFErrorOr<Gfx::FloatPoint> SimpleFont::draw_string(Gfx::Painter& painter, Gfx::
return draw_glyph(painter, glyph_render_position, glyph_width, char_code, renderer);
});
}
}
+5 -1
View File
@@ -14,12 +14,12 @@ namespace PDF {
class SimpleFont : public PDFFont {
public:
PDFErrorOr<Gfx::FloatPoint> draw_string(Gfx::Painter&, Gfx::FloatPoint, ByteString const&, Renderer const&) override;
PDFErrorOr<Gfx::FloatPoint> append_text_path(Gfx::Path&, Gfx::FloatPoint, ByteString const&, Renderer const&) override;
protected:
PDFErrorOr<void> initialize(Document* document, NonnullRefPtr<DictObject> const& dict, float font_size) override;
virtual Optional<float> get_glyph_width(u8 char_code) const = 0;
virtual PDFErrorOr<void> draw_glyph(Gfx::Painter& painter, Gfx::FloatPoint point, float width, u8 char_code, Renderer const&) = 0;
virtual PDFErrorOr<void> append_glyph_path(Gfx::Path&, Gfx::FloatPoint, u8, Renderer const&)
{
return Error { Error::Type::RenderingUnsupported, "append_glyph_path not implemented for font" };
@@ -34,6 +34,10 @@ private:
template<typename Callback>
PDFErrorOr<Gfx::FloatPoint> for_each_glyph_position(Gfx::FloatPoint, ByteString const&, Renderer const&, Callback callback);
PDFErrorOr<Gfx::FloatPoint> append_text_path(Gfx::Path&, Gfx::FloatPoint, ByteString const&, Renderer const&);
PDFErrorOr<Gfx::FloatPoint> draw_transformed_glyphs(Gfx::Painter&, Gfx::FloatPoint, ByteString const&, Renderer const&);
PDFErrorOr<Gfx::FloatPoint> draw_axis_aligned_glyphs(Gfx::Painter&, Gfx::FloatPoint, ByteString const&, Renderer const&);
RefPtr<Encoding> m_encoding;
RefPtr<StreamObject> m_to_unicode;
HashMap<u8, u16> m_widths;
+2 -32
View File
@@ -451,20 +451,11 @@ void Renderer::stroke_current_path()
}
}
static void do_fill_path(Gfx::AntiAliasingPainter& painter, Gfx::Path const& path, ColorOrStyle const& style, float paint_style_opacity = 1.0f, Gfx::WindingRule winding_rule = Gfx::WindingRule::Nonzero)
{
if (auto* paint_style = style.get_pointer<NonnullRefPtr<Gfx::PaintStyle>>()) {
painter.fill_path(path, *paint_style, paint_style_opacity, winding_rule);
} else {
painter.fill_path(path, style.get<Color>(), winding_rule);
}
}
void Renderer::fill_current_path(Gfx::WindingRule winding_rule)
{
auto path_end = m_current_path.end();
m_current_path.close_all_subpaths();
do_fill_path(anti_aliasing_painter(), m_current_path, state().paint_style, state().paint_alpha_constant, winding_rule);
fill_path_with_style(anti_aliasing_painter(), m_current_path, state().paint_style, state().paint_alpha_constant, winding_rule);
// .close_all_subpaths() only adds to the end of the path, so we can .trim() the path to remove any changes.
m_current_path.trim(path_end);
}
@@ -1380,28 +1371,7 @@ PDFErrorOr<void> Renderer::show_text(ByteString const& string)
return Error::rendering_unsupported_error("Can't draw text because an invalid font was in use");
auto start_position = Gfx::FloatPoint { 0.0f, 0.0f };
auto end_position = start_position;
if (m_text_rendering_matrix.is_identity_or_translation_or_scale(Gfx::AffineTransform::AllowNegativeScaling::No)) {
// Fast path: Use cached bitmap glyphs.
end_position = TRY(text_state().font->draw_string(painter(), start_position, string, *this));
} else {
Gfx::Path text_path;
// Slow path: Create a Gfx::Path for the string, transform it, then draw it. This handles arbitrary transforms.
if (auto maybe_end = text_state().font->append_text_path(text_path, start_position, string, *this); !maybe_end.is_error()) {
end_position = maybe_end.release_value();
// FIXME: This is a bit janky, but the font is already scaled by `m_text_rendering_matrix.x_scale() / horizontal_scaling`,
// which is included in `m_text_rendering_matrix`. So, we must scale it by the reciprocal. Also, the y-axis is flipped.
text_path.transform(m_text_rendering_matrix.multiply(
Gfx::AffineTransform {}
.set_scale(1 / m_text_rendering_matrix.x_scale() * text_state().horizontal_scaling,
-1 / m_text_rendering_matrix.x_scale() * text_state().horizontal_scaling)));
do_fill_path(anti_aliasing_painter(), text_path, state().paint_style, state().paint_alpha_constant);
} else {
// Fallback to the fast path in case `append_path` is unimplemented.
end_position = TRY(text_state().font->draw_string(painter(), start_position, string, *this));
}
}
auto end_position = TRY(text_state().font->draw_string(painter(), start_position, string, *this));
// Update text matrix.
auto delta = end_position - start_position;
+9
View File
@@ -179,6 +179,15 @@ public:
bool show_hidden_text() const { return m_rendering_preferences.show_hidden_text; }
static void fill_path_with_style(Gfx::AntiAliasingPainter& painter, Gfx::Path const& path, ColorOrStyle const& style, float paint_style_opacity = 1.0f, Gfx::WindingRule winding_rule = Gfx::WindingRule::Nonzero)
{
if (auto* paint_style = style.get_pointer<NonnullRefPtr<Gfx::PaintStyle>>()) {
painter.fill_path(path, *paint_style, paint_style_opacity, winding_rule);
} else {
painter.fill_path(path, style.get<Color>(), winding_rule);
}
}
private:
Renderer(RefPtr<Document>, Page const&, RefPtr<Gfx::Bitmap>, Color background_color, RenderingPreferences);