mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 19:27:45 +00:00
LibPDF: Use Gfx::PathRasterizer for Adobe Type 1 font rendering
This gives much better visual results than painting the path directly. It also has the nice side effect that Type 1 fonts will now look much more similar to TrueType fonts, which use the same class :^) In addition, we can now cache glyph bitmaps for repeated use.
This commit is contained in:
parent
0b6299849e
commit
7c4f5b58be
4 changed files with 69 additions and 11 deletions
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <LibGfx/Font/PathRasterizer.h>
|
||||||
#include <LibPDF/CommonNames.h>
|
#include <LibPDF/CommonNames.h>
|
||||||
#include <LibPDF/Encoding.h>
|
#include <LibPDF/Encoding.h>
|
||||||
#include <LibPDF/Fonts/PS1FontProgram.h>
|
#include <LibPDF/Fonts/PS1FontProgram.h>
|
||||||
|
@ -86,20 +87,61 @@ PDFErrorOr<void> PS1FontProgram::parse(ReadonlyBytes const& bytes, size_t cleart
|
||||||
return parse_encrypted_portion(decrypted);
|
return parse_encrypted_portion(decrypted);
|
||||||
}
|
}
|
||||||
|
|
||||||
Gfx::Path PS1FontProgram::build_char(u32 code_point, Gfx::FloatPoint const& point, float width)
|
RefPtr<Gfx::Bitmap> PS1FontProgram::rasterize_glyph(u32 code_point, float width)
|
||||||
{
|
{
|
||||||
if (!m_glyph_map.contains(code_point))
|
auto path = build_char(code_point, width);
|
||||||
|
auto bounding_box = path.bounding_box().size();
|
||||||
|
|
||||||
|
u32 w = (u32)ceilf(bounding_box.width()) + 1;
|
||||||
|
u32 h = (u32)ceilf(bounding_box.height()) + 1;
|
||||||
|
|
||||||
|
Gfx::PathRasterizer rasterizer(Gfx::IntSize(w, h));
|
||||||
|
rasterizer.draw_path(path);
|
||||||
|
return rasterizer.accumulate();
|
||||||
|
}
|
||||||
|
|
||||||
|
Gfx::Path PS1FontProgram::build_char(u32 code_point, float width)
|
||||||
|
{
|
||||||
|
auto maybe_glyph = m_glyph_map.get(code_point);
|
||||||
|
if (!maybe_glyph.has_value())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
auto glyph = m_glyph_map.get(code_point).value();
|
auto& glyph = maybe_glyph.value();
|
||||||
|
auto transform = glyph_transform_to_device_space(glyph, width);
|
||||||
|
|
||||||
|
// Translate such that the top-left point is at [0, 0].
|
||||||
|
auto bounding_box = glyph.path.bounding_box();
|
||||||
|
Gfx::FloatPoint translation(-bounding_box.x(), -(bounding_box.y() + bounding_box.height()));
|
||||||
|
transform.translate(translation);
|
||||||
|
|
||||||
|
return glyph.path.copy_transformed(transform);
|
||||||
|
}
|
||||||
|
|
||||||
|
Gfx::FloatPoint PS1FontProgram::glyph_translation(u32 code_point, float width) const
|
||||||
|
{
|
||||||
|
auto maybe_glyph = m_glyph_map.get(code_point);
|
||||||
|
if (!maybe_glyph.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto& glyph = maybe_glyph.value();
|
||||||
|
auto transform = glyph_transform_to_device_space(glyph, width);
|
||||||
|
|
||||||
|
// Undo the translation we applied earlier.
|
||||||
|
auto bounding_box = glyph.path.bounding_box();
|
||||||
|
Gfx::FloatPoint translation(bounding_box.x(), bounding_box.y() + bounding_box.height());
|
||||||
|
|
||||||
|
return transform.map(translation);
|
||||||
|
}
|
||||||
|
|
||||||
|
Gfx::AffineTransform PS1FontProgram::glyph_transform_to_device_space(Glyph const& glyph, float width) const
|
||||||
|
{
|
||||||
auto scale = width / (m_font_matrix.a() * glyph.width + m_font_matrix.e());
|
auto scale = width / (m_font_matrix.a() * glyph.width + m_font_matrix.e());
|
||||||
auto transform = m_font_matrix;
|
auto transform = m_font_matrix;
|
||||||
|
|
||||||
// Convert character space to device space.
|
// Convert character space to device space.
|
||||||
transform.scale(scale, -scale);
|
transform.scale(scale, -scale);
|
||||||
transform.set_translation(point);
|
|
||||||
|
|
||||||
return glyph.path.copy_transformed(transform);
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFErrorOr<PS1FontProgram::Glyph> PS1FontProgram::parse_glyph(ReadonlyBytes const& data, GlyphParserState& state)
|
PDFErrorOr<PS1FontProgram::Glyph> PS1FontProgram::parse_glyph(ReadonlyBytes const& data, GlyphParserState& state)
|
||||||
|
|
|
@ -20,9 +20,11 @@ class PS1FontProgram : public RefCounted<PS1FontProgram> {
|
||||||
public:
|
public:
|
||||||
PDFErrorOr<void> parse(ReadonlyBytes const&, size_t cleartext_length, size_t encrypted_length);
|
PDFErrorOr<void> parse(ReadonlyBytes const&, size_t cleartext_length, size_t encrypted_length);
|
||||||
|
|
||||||
Gfx::Path build_char(u32 code_point, Gfx::FloatPoint const& point, float width);
|
RefPtr<Gfx::Bitmap> rasterize_glyph(u32 code_point, float width);
|
||||||
|
Gfx::Path build_char(u32 code_point, float width);
|
||||||
|
|
||||||
RefPtr<Encoding> encoding() const { return m_encoding; }
|
RefPtr<Encoding> encoding() const { return m_encoding; }
|
||||||
|
Gfx::FloatPoint glyph_translation(u32 code_point, float width) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Glyph {
|
struct Glyph {
|
||||||
|
@ -46,6 +48,8 @@ private:
|
||||||
Array<float, 24> postscript_stack;
|
Array<float, 24> postscript_stack;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Gfx::AffineTransform glyph_transform_to_device_space(Glyph const&, float width) const;
|
||||||
|
|
||||||
PDFErrorOr<Glyph> parse_glyph(ReadonlyBytes const&, GlyphParserState&);
|
PDFErrorOr<Glyph> parse_glyph(ReadonlyBytes const&, GlyphParserState&);
|
||||||
PDFErrorOr<void> parse_encrypted_portion(ByteBuffer const&);
|
PDFErrorOr<void> parse_encrypted_portion(ByteBuffer const&);
|
||||||
PDFErrorOr<Vector<ByteBuffer>> parse_subroutines(Reader&);
|
PDFErrorOr<Vector<ByteBuffer>> parse_subroutines(Reader&);
|
||||||
|
|
|
@ -122,11 +122,22 @@ float Type1Font::get_char_width(u16 char_code, float) const
|
||||||
|
|
||||||
void Type1Font::draw_glyph(Gfx::Painter& painter, Gfx::IntPoint const& point, float width, u32 code_point, Color color)
|
void Type1Font::draw_glyph(Gfx::Painter& painter, Gfx::IntPoint const& point, float width, u32 code_point, Color color)
|
||||||
{
|
{
|
||||||
// FIXME: Make a glyph cache
|
if (!m_data.font_program)
|
||||||
if (m_data.font_program) {
|
return;
|
||||||
auto path = m_data.font_program->build_char(code_point, { point.x(), point.y() }, width);
|
|
||||||
Gfx::AntiAliasingPainter aa_painter(painter);
|
RefPtr<Gfx::Bitmap> bitmap;
|
||||||
aa_painter.fill_path(path, color, Gfx::Painter::WindingRule::EvenOdd);
|
|
||||||
|
auto maybe_bitmap = m_glyph_cache.get(code_point);
|
||||||
|
if (maybe_bitmap.has_value()) {
|
||||||
|
bitmap = maybe_bitmap.value();
|
||||||
|
} else {
|
||||||
|
bitmap = m_data.font_program->rasterize_glyph(code_point, width);
|
||||||
|
m_glyph_cache.set(code_point, bitmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto translation = m_data.font_program->glyph_translation(code_point, width);
|
||||||
|
painter.blit_filtered(point.translated(translation.to_rounded<int>()), *bitmap, bitmap->rect(), [color](Color pixel) -> Color {
|
||||||
|
return pixel.multiply(color);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Data m_data;
|
Data m_data;
|
||||||
|
HashMap<u32, RefPtr<Gfx::Bitmap>> m_glyph_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue