mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-10 01:51:03 +09:00
LibGfx: Remove a bunch of no-longer-used OpenType font code
We're not quite ready to remove the old OpenType implementation yet, but let's carve off some unused stuff.
This commit is contained in:
parent
ca2cd86999
commit
aabad3b94e
Notes:
github-actions[bot]
2024-08-20 07:38:01 +00:00
Author: https://github.com/awesomekling
Commit: aabad3b94e
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1024
15 changed files with 5 additions and 1195 deletions
|
@ -456,7 +456,6 @@ if (ENABLE_GUI_TARGETS)
|
||||||
lagom_utility(animation SOURCES ../../Userland/Utilities/animation.cpp LIBS LibGfx LibMain)
|
lagom_utility(animation SOURCES ../../Userland/Utilities/animation.cpp LIBS LibGfx LibMain)
|
||||||
lagom_utility(icc SOURCES ../../Userland/Utilities/icc.cpp LIBS LibGfx LibMain LibURL)
|
lagom_utility(icc SOURCES ../../Userland/Utilities/icc.cpp LIBS LibGfx LibMain LibURL)
|
||||||
lagom_utility(image SOURCES ../../Userland/Utilities/image.cpp LIBS LibGfx LibMain)
|
lagom_utility(image SOURCES ../../Userland/Utilities/image.cpp LIBS LibGfx LibMain)
|
||||||
lagom_utility(ttfdisasm SOURCES ../../Userland/Utilities/ttfdisasm.cpp LIBS LibGfx LibMain)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
lagom_utility(js SOURCES ../../Userland/Utilities/js.cpp LIBS LibCrypto LibJS LibLine LibUnicode LibMain LibTextCodec Threads::Threads)
|
lagom_utility(js SOURCES ../../Userland/Utilities/js.cpp LIBS LibCrypto LibJS LibLine LibUnicode LibMain LibTextCodec Threads::Threads)
|
||||||
|
|
|
@ -158,85 +158,6 @@ void DeprecatedPath::elliptical_arc_to(FloatPoint point, FloatSize radii, float
|
||||||
theta_delta);
|
theta_delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeprecatedPath::text(Utf8View text, Font const& font)
|
|
||||||
{
|
|
||||||
if (!is<ScaledFont>(font)) {
|
|
||||||
// FIXME: This API only accepts Gfx::Font for ease of use.
|
|
||||||
dbgln("Cannot path-ify bitmap fonts!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& scaled_font = static_cast<ScaledFont const&>(font);
|
|
||||||
for_each_glyph_position(
|
|
||||||
last_point(), text, scaled_font, [&](DrawGlyphOrEmoji const& glyph_or_emoji) {
|
|
||||||
if (glyph_or_emoji.has<DrawGlyph>()) {
|
|
||||||
auto& glyph = glyph_or_emoji.get<DrawGlyph>();
|
|
||||||
move_to(glyph.position);
|
|
||||||
scaled_font.append_glyph_path_to(*this, glyph.glyph_id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
IncludeLeftBearing::Yes);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeprecatedPath DeprecatedPath::place_text_along(Utf8View text, Font const& font) const
|
|
||||||
{
|
|
||||||
if (!is<ScaledFont>(font)) {
|
|
||||||
// FIXME: This API only accepts Gfx::Font for ease of use.
|
|
||||||
dbgln("Cannot path-ify bitmap fonts!");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lines = split_lines();
|
|
||||||
auto next_point_for_offset = [&, line_index = 0U, distance_along_path = 0.0f, last_line_length = 0.0f](float offset) mutable -> Optional<FloatPoint> {
|
|
||||||
while (line_index < lines.size() && offset > distance_along_path) {
|
|
||||||
last_line_length = lines[line_index++].length();
|
|
||||||
distance_along_path += last_line_length;
|
|
||||||
}
|
|
||||||
if (offset > distance_along_path)
|
|
||||||
return {};
|
|
||||||
if (last_line_length > 1) {
|
|
||||||
// If the last line segment was fairly long, compute the point in the line.
|
|
||||||
float p = (last_line_length + offset - distance_along_path) / last_line_length;
|
|
||||||
auto current_line = lines[line_index - 1];
|
|
||||||
return current_line.a() + (current_line.b() - current_line.a()).scaled(p);
|
|
||||||
}
|
|
||||||
if (line_index >= lines.size())
|
|
||||||
return {};
|
|
||||||
return lines[line_index].a();
|
|
||||||
};
|
|
||||||
|
|
||||||
auto& scaled_font = static_cast<Gfx::ScaledFont const&>(font);
|
|
||||||
Gfx::DeprecatedPath result_path;
|
|
||||||
Gfx::for_each_glyph_position(
|
|
||||||
{}, text, font, [&](Gfx::DrawGlyphOrEmoji glyph_or_emoji) {
|
|
||||||
auto* glyph = glyph_or_emoji.get_pointer<Gfx::DrawGlyph>();
|
|
||||||
if (!glyph)
|
|
||||||
return;
|
|
||||||
auto offset = glyph->position.x();
|
|
||||||
auto width = scaled_font.glyph_metrics(glyph->glyph_id).advance_width;
|
|
||||||
auto start = next_point_for_offset(offset);
|
|
||||||
if (!start.has_value())
|
|
||||||
return;
|
|
||||||
auto end = next_point_for_offset(offset + width);
|
|
||||||
if (!end.has_value())
|
|
||||||
return;
|
|
||||||
// Find the angle between the start and end points on the path.
|
|
||||||
auto delta = *end - *start;
|
|
||||||
auto angle = AK::atan2(delta.y(), delta.x());
|
|
||||||
Gfx::DeprecatedPath glyph_path;
|
|
||||||
// Rotate the glyph then move it to start point.
|
|
||||||
scaled_font.append_glyph_path_to(glyph_path, glyph->glyph_id);
|
|
||||||
auto transform = Gfx::AffineTransform {}
|
|
||||||
.translate(*start)
|
|
||||||
.multiply(Gfx::AffineTransform {}.rotate_radians(angle))
|
|
||||||
.multiply(Gfx::AffineTransform {}.translate({ 0, -scaled_font.pixel_metrics().ascent }));
|
|
||||||
glyph_path = glyph_path.copy_transformed(transform);
|
|
||||||
result_path.append_path(glyph_path);
|
|
||||||
},
|
|
||||||
Gfx::IncludeLeftBearing::Yes);
|
|
||||||
return result_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeprecatedPath::close()
|
void DeprecatedPath::close()
|
||||||
{
|
{
|
||||||
// If there's no `moveto` starting this subpath assume the start is (0, 0).
|
// If there's no `moveto` starting this subpath assume the start is (0, 0).
|
||||||
|
|
|
@ -183,8 +183,6 @@ public:
|
||||||
elliptical_arc_to(point, { radius, radius }, 0, large_arc, sweep);
|
elliptical_arc_to(point, { radius, radius }, 0, large_arc, sweep);
|
||||||
}
|
}
|
||||||
|
|
||||||
void text(Utf8View, Font const&);
|
|
||||||
|
|
||||||
FloatPoint last_point()
|
FloatPoint last_point()
|
||||||
{
|
{
|
||||||
if (!m_points.is_empty())
|
if (!m_points.is_empty())
|
||||||
|
@ -196,9 +194,6 @@ public:
|
||||||
void close_all_subpaths();
|
void close_all_subpaths();
|
||||||
|
|
||||||
DeprecatedPath stroke_to_fill(float thickness) const;
|
DeprecatedPath stroke_to_fill(float thickness) const;
|
||||||
|
|
||||||
DeprecatedPath place_text_along(Utf8View text, Font const&) const;
|
|
||||||
|
|
||||||
DeprecatedPath copy_transformed(AffineTransform const&) const;
|
DeprecatedPath copy_transformed(AffineTransform const&) const;
|
||||||
|
|
||||||
ReadonlySpan<FloatLine> split_lines() const
|
ReadonlySpan<FloatLine> split_lines() const
|
||||||
|
|
|
@ -75,7 +75,6 @@ public:
|
||||||
virtual u16 weight() const = 0;
|
virtual u16 weight() const = 0;
|
||||||
virtual bool contains_glyph(u32 code_point) const = 0;
|
virtual bool contains_glyph(u32 code_point) const = 0;
|
||||||
|
|
||||||
virtual bool append_glyph_path_to(Gfx::DeprecatedPath&, u32 glyph_id) const = 0;
|
|
||||||
virtual u32 glyph_id_for_code_point(u32 code_point) const = 0;
|
virtual u32 glyph_id_for_code_point(u32 code_point) const = 0;
|
||||||
virtual float glyph_left_bearing(u32 code_point) const = 0;
|
virtual float glyph_left_bearing(u32 code_point) const = 0;
|
||||||
virtual float glyph_width(u32 code_point) const = 0;
|
virtual float glyph_width(u32 code_point) const = 0;
|
||||||
|
@ -97,8 +96,6 @@ public:
|
||||||
Font const& bold_variant() const;
|
Font const& bold_variant() const;
|
||||||
hb_font_t* harfbuzz_font() const;
|
hb_font_t* harfbuzz_font() const;
|
||||||
|
|
||||||
virtual bool has_color_bitmaps() const = 0;
|
|
||||||
|
|
||||||
virtual Typeface const& typeface() const = 0;
|
virtual Typeface const& typeface() const = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -4,9 +4,6 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <LibGfx/AntiAliasingPainter.h>
|
|
||||||
#include <LibGfx/DeprecatedPainter.h>
|
|
||||||
#include <LibGfx/DeprecatedPath.h>
|
|
||||||
#include <LibGfx/Font/OpenType/Glyf.h>
|
#include <LibGfx/Font/OpenType/Glyf.h>
|
||||||
#include <LibGfx/Point.h>
|
#include <LibGfx/Point.h>
|
||||||
|
|
||||||
|
@ -14,167 +11,6 @@ namespace OpenType {
|
||||||
|
|
||||||
extern u16 be_u16(u8 const* ptr);
|
extern u16 be_u16(u8 const* ptr);
|
||||||
extern u32 be_u32(u8 const* ptr);
|
extern u32 be_u32(u8 const* ptr);
|
||||||
extern i16 be_i16(u8 const* ptr);
|
|
||||||
extern float be_fword(u8 const* ptr);
|
|
||||||
|
|
||||||
enum class SimpleGlyfFlags {
|
|
||||||
// From spec.
|
|
||||||
OnCurve = 0x01,
|
|
||||||
XShortVector = 0x02,
|
|
||||||
YShortVector = 0x04,
|
|
||||||
RepeatFlag = 0x08,
|
|
||||||
XIsSameOrPositiveXShortVector = 0x10,
|
|
||||||
YIsSameOrPositiveYShortVector = 0x20,
|
|
||||||
// Combinations
|
|
||||||
XMask = 0x12,
|
|
||||||
YMask = 0x24,
|
|
||||||
XLongVector = 0x00,
|
|
||||||
YLongVector = 0x00,
|
|
||||||
XNegativeShortVector = 0x02,
|
|
||||||
YNegativeShortVector = 0x04,
|
|
||||||
XPositiveShortVector = 0x12,
|
|
||||||
YPositiveShortVector = 0x24,
|
|
||||||
};
|
|
||||||
|
|
||||||
class PointIterator {
|
|
||||||
public:
|
|
||||||
struct Item {
|
|
||||||
bool on_curve;
|
|
||||||
Gfx::FloatPoint point;
|
|
||||||
};
|
|
||||||
|
|
||||||
PointIterator(ReadonlyBytes slice, u16 num_points, u32 flags_offset, u32 x_offset, u32 y_offset, Gfx::AffineTransform affine)
|
|
||||||
: m_slice(slice)
|
|
||||||
, m_points_remaining(num_points)
|
|
||||||
, m_flags_offset(flags_offset)
|
|
||||||
, m_x_offset(x_offset)
|
|
||||||
, m_y_offset(y_offset)
|
|
||||||
, m_affine(affine)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<Item> next()
|
|
||||||
{
|
|
||||||
if (m_points_remaining == 0) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (m_flags_remaining > 0) {
|
|
||||||
m_flags_remaining--;
|
|
||||||
} else {
|
|
||||||
m_flag = m_slice[m_flags_offset++];
|
|
||||||
if (m_flag & (u8)SimpleGlyfFlags::RepeatFlag) {
|
|
||||||
m_flags_remaining = m_slice[m_flags_offset++];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch (m_flag & (u8)SimpleGlyfFlags::XMask) {
|
|
||||||
case (u8)SimpleGlyfFlags::XLongVector:
|
|
||||||
m_last_point.set_x(m_last_point.x() + be_i16(m_slice.offset(m_x_offset)));
|
|
||||||
m_x_offset += 2;
|
|
||||||
break;
|
|
||||||
case (u8)SimpleGlyfFlags::XNegativeShortVector:
|
|
||||||
m_last_point.set_x(m_last_point.x() - m_slice[m_x_offset++]);
|
|
||||||
break;
|
|
||||||
case (u8)SimpleGlyfFlags::XPositiveShortVector:
|
|
||||||
m_last_point.set_x(m_last_point.x() + m_slice[m_x_offset++]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
switch (m_flag & (u8)SimpleGlyfFlags::YMask) {
|
|
||||||
case (u8)SimpleGlyfFlags::YLongVector:
|
|
||||||
m_last_point.set_y(m_last_point.y() + be_i16(m_slice.offset(m_y_offset)));
|
|
||||||
m_y_offset += 2;
|
|
||||||
break;
|
|
||||||
case (u8)SimpleGlyfFlags::YNegativeShortVector:
|
|
||||||
m_last_point.set_y(m_last_point.y() - m_slice[m_y_offset++]);
|
|
||||||
break;
|
|
||||||
case (u8)SimpleGlyfFlags::YPositiveShortVector:
|
|
||||||
m_last_point.set_y(m_last_point.y() + m_slice[m_y_offset++]);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
m_points_remaining--;
|
|
||||||
Item ret = {
|
|
||||||
.on_curve = (m_flag & (u8)SimpleGlyfFlags::OnCurve) != 0,
|
|
||||||
.point = m_affine.map(m_last_point),
|
|
||||||
};
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ReadonlyBytes m_slice;
|
|
||||||
u16 m_points_remaining;
|
|
||||||
u8 m_flag { 0 };
|
|
||||||
Gfx::FloatPoint m_last_point = { 0.0f, 0.0f };
|
|
||||||
u32 m_flags_remaining = { 0 };
|
|
||||||
u32 m_flags_offset;
|
|
||||||
u32 m_x_offset;
|
|
||||||
u32 m_y_offset;
|
|
||||||
Gfx::AffineTransform m_affine;
|
|
||||||
};
|
|
||||||
|
|
||||||
Optional<Glyf::Glyph::ComponentIterator::Item> Glyf::Glyph::ComponentIterator::next()
|
|
||||||
{
|
|
||||||
if (!m_has_more) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
u16 flags = be_u16(m_slice.offset(m_offset));
|
|
||||||
m_offset += 2;
|
|
||||||
u16 glyph_id = be_u16(m_slice.offset(m_offset));
|
|
||||||
m_offset += 2;
|
|
||||||
i16 arg1 = 0, arg2 = 0;
|
|
||||||
if (flags & (u16)CompositeFlags::Arg1AndArg2AreWords) {
|
|
||||||
arg1 = be_i16(m_slice.offset(m_offset));
|
|
||||||
m_offset += 2;
|
|
||||||
arg2 = be_i16(m_slice.offset(m_offset));
|
|
||||||
m_offset += 2;
|
|
||||||
} else {
|
|
||||||
arg1 = (i8)m_slice[m_offset++];
|
|
||||||
arg2 = (i8)m_slice[m_offset++];
|
|
||||||
}
|
|
||||||
float a = 1.0, b = 0.0, c = 0.0, d = 1.0, e = 0.0, f = 0.0;
|
|
||||||
if (flags & (u16)CompositeFlags::WeHaveATwoByTwo) {
|
|
||||||
a = be_fword(m_slice.offset(m_offset));
|
|
||||||
m_offset += 2;
|
|
||||||
b = be_fword(m_slice.offset(m_offset));
|
|
||||||
m_offset += 2;
|
|
||||||
c = be_fword(m_slice.offset(m_offset));
|
|
||||||
m_offset += 2;
|
|
||||||
d = be_fword(m_slice.offset(m_offset));
|
|
||||||
m_offset += 2;
|
|
||||||
} else if (flags & (u16)CompositeFlags::WeHaveAnXAndYScale) {
|
|
||||||
a = be_fword(m_slice.offset(m_offset));
|
|
||||||
m_offset += 2;
|
|
||||||
d = be_fword(m_slice.offset(m_offset));
|
|
||||||
m_offset += 2;
|
|
||||||
} else if (flags & (u16)CompositeFlags::WeHaveAScale) {
|
|
||||||
a = be_fword(m_slice.offset(m_offset));
|
|
||||||
m_offset += 2;
|
|
||||||
d = a;
|
|
||||||
}
|
|
||||||
// FIXME: Handle UseMyMetrics, ScaledComponentOffset, UnscaledComponentOffset, non-ArgsAreXYValues
|
|
||||||
if (flags & (u16)CompositeFlags::ArgsAreXYValues) {
|
|
||||||
e = arg1;
|
|
||||||
f = arg2;
|
|
||||||
} else {
|
|
||||||
// FIXME: Implement this. There's no TODO() here since many fonts work just fine without this.
|
|
||||||
}
|
|
||||||
if (flags & (u16)CompositeFlags::UseMyMetrics) {
|
|
||||||
// FIXME: Implement this. There's no TODO() here since many fonts work just fine without this.
|
|
||||||
}
|
|
||||||
if (flags & (u16)CompositeFlags::ScaledComponentOffset) {
|
|
||||||
// FIXME: Implement this. There's no TODO() here since many fonts work just fine without this.
|
|
||||||
}
|
|
||||||
if (flags & (u16)CompositeFlags::UnscaledComponentOffset) {
|
|
||||||
// FIXME: Implement this. There's no TODO() here since many fonts work just fine without this.
|
|
||||||
}
|
|
||||||
m_has_more = (flags & (u16)CompositeFlags::MoreComponents);
|
|
||||||
return Item {
|
|
||||||
.glyph_id = glyph_id,
|
|
||||||
.affine = Gfx::AffineTransform(a, b, c, d, e, f),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<Loca> Loca::from_slice(ReadonlyBytes slice, u32 num_glyphs, IndexToLocFormat index_to_loc_format)
|
ErrorOr<Loca> Loca::from_slice(ReadonlyBytes slice, u32 num_glyphs, IndexToLocFormat index_to_loc_format)
|
||||||
{
|
{
|
||||||
|
@ -205,121 +41,6 @@ u32 Loca::get_glyph_offset(u32 glyph_id) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void get_ttglyph_offsets(ReadonlyBytes slice, u32 num_points, u32 flags_offset, u32* x_offset, u32* y_offset)
|
|
||||||
{
|
|
||||||
u32 flags_size = 0;
|
|
||||||
u32 x_size = 0;
|
|
||||||
u32 repeat_count;
|
|
||||||
while (num_points > 0) {
|
|
||||||
u8 flag = slice[flags_offset + flags_size];
|
|
||||||
if (flag & (u8)SimpleGlyfFlags::RepeatFlag) {
|
|
||||||
flags_size++;
|
|
||||||
repeat_count = slice[flags_offset + flags_size] + 1;
|
|
||||||
} else {
|
|
||||||
repeat_count = 1;
|
|
||||||
}
|
|
||||||
flags_size++;
|
|
||||||
switch (flag & (u8)SimpleGlyfFlags::XMask) {
|
|
||||||
case (u8)SimpleGlyfFlags::XLongVector:
|
|
||||||
x_size += repeat_count * 2;
|
|
||||||
break;
|
|
||||||
case (u8)SimpleGlyfFlags::XNegativeShortVector:
|
|
||||||
case (u8)SimpleGlyfFlags::XPositiveShortVector:
|
|
||||||
x_size += repeat_count;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
num_points -= repeat_count;
|
|
||||||
}
|
|
||||||
*x_offset = flags_offset + flags_size;
|
|
||||||
*y_offset = *x_offset + x_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadonlyBytes Glyf::Glyph::program() const
|
|
||||||
{
|
|
||||||
if (m_num_contours == 0)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
auto instructions_start = m_num_contours * 2;
|
|
||||||
u16 num_instructions = be_u16(m_slice.offset(instructions_start));
|
|
||||||
return m_slice.slice(instructions_start + 2, num_instructions);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Glyf::Glyph::append_path_impl(Gfx::DeprecatedPath& path, Gfx::AffineTransform const& transform) const
|
|
||||||
{
|
|
||||||
if (m_num_contours == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Get offset for flags, x, and y.
|
|
||||||
u16 num_points = be_u16(m_slice.offset((m_num_contours - 1) * 2)) + 1;
|
|
||||||
u16 num_instructions = be_u16(m_slice.offset(m_num_contours * 2));
|
|
||||||
u32 flags_offset = m_num_contours * 2 + 2 + num_instructions;
|
|
||||||
u32 x_offset = 0;
|
|
||||||
u32 y_offset = 0;
|
|
||||||
get_ttglyph_offsets(m_slice, num_points, flags_offset, &x_offset, &y_offset);
|
|
||||||
|
|
||||||
// Prepare to render glyph.
|
|
||||||
PointIterator point_iterator(m_slice, num_points, flags_offset, x_offset, y_offset, transform);
|
|
||||||
|
|
||||||
u32 current_point_index = 0;
|
|
||||||
for (u16 contour_index = 0; contour_index < m_num_contours; contour_index++) {
|
|
||||||
u32 current_contour_last_point_index = be_u16(m_slice.offset(contour_index * 2));
|
|
||||||
|
|
||||||
Vector<PointIterator::Item> points;
|
|
||||||
while (current_point_index <= current_contour_last_point_index) {
|
|
||||||
points.append(*point_iterator.next());
|
|
||||||
current_point_index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (points.is_empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto current = points.last();
|
|
||||||
auto next = points.first();
|
|
||||||
|
|
||||||
if (current.on_curve) {
|
|
||||||
path.move_to(current.point);
|
|
||||||
} else if (next.on_curve) {
|
|
||||||
path.move_to(next.point);
|
|
||||||
} else {
|
|
||||||
auto implied_point = (current.point + next.point) * 0.5f;
|
|
||||||
path.move_to(implied_point);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < points.size(); i++) {
|
|
||||||
current = next;
|
|
||||||
next = points[(i + 1) % points.size()];
|
|
||||||
if (current.on_curve) {
|
|
||||||
path.line_to(current.point);
|
|
||||||
} else if (next.on_curve) {
|
|
||||||
path.quadratic_bezier_curve_to(current.point, next.point);
|
|
||||||
} else {
|
|
||||||
auto implied_point = (current.point + next.point) * 0.5f;
|
|
||||||
path.quadratic_bezier_curve_to(current.point, implied_point);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Glyf::Glyph::append_simple_path(Gfx::DeprecatedPath& path, i16 font_ascender, i16 font_descender, float x_scale, float y_scale) const
|
|
||||||
{
|
|
||||||
if (m_xmin > m_xmax) [[unlikely]] {
|
|
||||||
dbgln("OpenType: Glyph has invalid xMin ({}) > xMax ({})", m_xmin, m_xmax);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (font_descender > font_ascender) [[unlikely]] {
|
|
||||||
dbgln("OpenType: Glyph has invalid ascender ({}) > descender ({})", font_ascender, font_descender);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto affine = Gfx::AffineTransform()
|
|
||||||
.translate(path.last_point())
|
|
||||||
.scale(x_scale, -y_scale)
|
|
||||||
.translate(-m_xmin, -font_ascender);
|
|
||||||
append_path_impl(path, affine);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<Glyf::Glyph> Glyf::glyph(u32 offset) const
|
Optional<Glyf::Glyph> Glyf::glyph(u32 offset) const
|
||||||
{
|
{
|
||||||
if (offset + sizeof(GlyphHeader) > m_slice.size())
|
if (offset + sizeof(GlyphHeader) > m_slice.size())
|
||||||
|
|
|
@ -9,9 +9,6 @@
|
||||||
#include <AK/Endian.h>
|
#include <AK/Endian.h>
|
||||||
#include <AK/Span.h>
|
#include <AK/Span.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <LibGfx/AffineTransform.h>
|
|
||||||
#include <LibGfx/Bitmap.h>
|
|
||||||
#include <LibGfx/DeprecatedPath.h>
|
|
||||||
#include <LibGfx/Font/Font.h>
|
#include <LibGfx/Font/Font.h>
|
||||||
#include <LibGfx/Font/OpenType/Tables.h>
|
#include <LibGfx/Font/OpenType/Tables.h>
|
||||||
#include <LibGfx/Size.h>
|
#include <LibGfx/Size.h>
|
||||||
|
@ -43,21 +40,6 @@ private:
|
||||||
// glyf: Glyph Data
|
// glyf: Glyph Data
|
||||||
class Glyf {
|
class Glyf {
|
||||||
public:
|
public:
|
||||||
enum class CompositeFlags {
|
|
||||||
Arg1AndArg2AreWords = 0x0001,
|
|
||||||
ArgsAreXYValues = 0x0002,
|
|
||||||
RoundXYToGrid = 0x0004,
|
|
||||||
WeHaveAScale = 0x0008,
|
|
||||||
MoreComponents = 0x0020,
|
|
||||||
WeHaveAnXAndYScale = 0x0040,
|
|
||||||
WeHaveATwoByTwo = 0x0080,
|
|
||||||
WeHaveInstructions = 0x0100,
|
|
||||||
UseMyMetrics = 0x0200,
|
|
||||||
OverlapCompound = 0x0400, // Not relevant - can overlap without this set
|
|
||||||
ScaledComponentOffset = 0x0800,
|
|
||||||
UnscaledComponentOffset = 0x1000,
|
|
||||||
};
|
|
||||||
|
|
||||||
class Glyph {
|
class Glyph {
|
||||||
public:
|
public:
|
||||||
Glyph(ReadonlyBytes slice, i16 xmin, i16 ymin, i16 xmax, i16 ymax, i16 num_contours = -1)
|
Glyph(ReadonlyBytes slice, i16 xmin, i16 ymin, i16 xmax, i16 ymax, i16 num_contours = -1)
|
||||||
|
@ -73,90 +55,18 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename GlyphCb>
|
|
||||||
bool append_path(Gfx::DeprecatedPath& path, i16 font_ascender, i16 font_descender, float x_scale, float y_scale, GlyphCb glyph_callback) const
|
|
||||||
{
|
|
||||||
switch (m_type) {
|
|
||||||
case Type::Simple:
|
|
||||||
return append_simple_path(path, font_ascender, font_descender, x_scale, y_scale);
|
|
||||||
case Type::Composite:
|
|
||||||
return append_composite_path(path, font_ascender, x_scale, y_scale, glyph_callback);
|
|
||||||
}
|
|
||||||
VERIFY_NOT_REACHED();
|
|
||||||
}
|
|
||||||
|
|
||||||
i16 xmax() const { return m_xmax; }
|
i16 xmax() const { return m_xmax; }
|
||||||
i16 xmin() const { return m_xmin; }
|
i16 xmin() const { return m_xmin; }
|
||||||
|
|
||||||
int ascender() const { return m_ymax; }
|
int ascender() const { return m_ymax; }
|
||||||
int descender() const { return m_ymin; }
|
int descender() const { return m_ymin; }
|
||||||
|
|
||||||
ReadonlyBytes program() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class Type {
|
enum class Type {
|
||||||
Simple,
|
Simple,
|
||||||
Composite,
|
Composite,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ComponentIterator {
|
|
||||||
public:
|
|
||||||
struct Item {
|
|
||||||
u16 glyph_id;
|
|
||||||
Gfx::AffineTransform affine;
|
|
||||||
};
|
|
||||||
|
|
||||||
ComponentIterator(ReadonlyBytes slice)
|
|
||||||
: m_slice(slice)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
Optional<Item> next();
|
|
||||||
|
|
||||||
private:
|
|
||||||
ReadonlyBytes m_slice;
|
|
||||||
bool m_has_more { true };
|
|
||||||
u32 m_offset { 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
void append_path_impl(Gfx::DeprecatedPath&, Gfx::AffineTransform const&) const;
|
|
||||||
bool append_simple_path(Gfx::DeprecatedPath&, i16 ascender, i16 descender, float x_scale, float y_scale) const;
|
|
||||||
|
|
||||||
template<typename GlyphCb>
|
|
||||||
void resolve_composite_path_loop(Gfx::DeprecatedPath& path, Gfx::AffineTransform const& transform, GlyphCb glyph_callback) const
|
|
||||||
{
|
|
||||||
ComponentIterator component_iterator(m_slice);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
auto opt_item = component_iterator.next();
|
|
||||||
if (!opt_item.has_value()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto item = opt_item.value();
|
|
||||||
Gfx::AffineTransform affine_here { transform };
|
|
||||||
affine_here.multiply(item.affine);
|
|
||||||
auto glyph = glyph_callback(item.glyph_id);
|
|
||||||
if (!glyph.has_value())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (glyph->m_type == Type::Simple) {
|
|
||||||
glyph->append_path_impl(path, affine_here);
|
|
||||||
} else {
|
|
||||||
glyph->resolve_composite_path_loop(path, transform, glyph_callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename GlyphCb>
|
|
||||||
bool append_composite_path(Gfx::DeprecatedPath& path, i16 font_ascender, float x_scale, float y_scale, GlyphCb glyph_callback) const
|
|
||||||
{
|
|
||||||
auto affine = Gfx::AffineTransform()
|
|
||||||
.translate(path.last_point())
|
|
||||||
.scale(x_scale, -y_scale)
|
|
||||||
.translate(-m_xmin, -font_ascender);
|
|
||||||
resolve_composite_path_loop(path, affine, glyph_callback);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type m_type { Type::Composite };
|
Type m_type { Type::Composite };
|
||||||
i16 m_xmin { 0 };
|
i16 m_xmin { 0 };
|
||||||
i16 m_ymin { 0 };
|
i16 m_ymin { 0 };
|
||||||
|
|
|
@ -443,85 +443,6 @@ Optional<i16> OS2::x_height() const
|
||||||
[](auto*) { return Optional<i16>(); });
|
[](auto*) { return Optional<i16>(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<CBLC> CBLC::from_slice(ReadonlyBytes slice)
|
|
||||||
{
|
|
||||||
if (slice.size() < sizeof(CblcHeader))
|
|
||||||
return Error::from_string_literal("CBLC table too small");
|
|
||||||
auto const& header = *bit_cast<CblcHeader const*>(slice.data());
|
|
||||||
|
|
||||||
size_t num_sizes = header.num_sizes;
|
|
||||||
Checked<size_t> size_used_by_bitmap_sizes = num_sizes;
|
|
||||||
size_used_by_bitmap_sizes *= sizeof(BitmapSize);
|
|
||||||
if (size_used_by_bitmap_sizes.has_overflow())
|
|
||||||
return Error::from_string_literal("Integer overflow in CBLC table");
|
|
||||||
|
|
||||||
Checked<size_t> total_size = sizeof(CblcHeader);
|
|
||||||
total_size += size_used_by_bitmap_sizes;
|
|
||||||
if (total_size.has_overflow())
|
|
||||||
return Error::from_string_literal("Integer overflow in CBLC table");
|
|
||||||
|
|
||||||
if (slice.size() < total_size)
|
|
||||||
return Error::from_string_literal("CBLC table too small");
|
|
||||||
|
|
||||||
ReadonlySpan<BitmapSize> bitmap_sizes { bit_cast<BitmapSize const*>(slice.data() + sizeof(CblcHeader)), num_sizes };
|
|
||||||
|
|
||||||
return CBLC { slice, header, bitmap_sizes };
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<CBLC::BitmapSize const&> CBLC::bitmap_size_for_glyph_id(u32 glyph_id) const
|
|
||||||
{
|
|
||||||
for (auto const& bitmap_size : m_bitmap_sizes) {
|
|
||||||
if (glyph_id >= bitmap_size.start_glyph_index && glyph_id <= bitmap_size.end_glyph_index) {
|
|
||||||
return bitmap_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<EBLC::IndexSubHeader const&> CBLC::index_subtable_for_glyph_id(u32 glyph_id, u16& first_glyph_index, u16& last_glyph_index) const
|
|
||||||
{
|
|
||||||
auto maybe_bitmap_size = bitmap_size_for_glyph_id(glyph_id);
|
|
||||||
if (!maybe_bitmap_size.has_value()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
auto const& bitmap_size = maybe_bitmap_size.value();
|
|
||||||
|
|
||||||
Checked<size_t> required_size = static_cast<u32>(bitmap_size.index_subtable_array_offset);
|
|
||||||
required_size += bitmap_size.index_tables_size;
|
|
||||||
|
|
||||||
if (m_slice.size() < required_size) {
|
|
||||||
dbgln("CBLC index subtable array goes out of bounds");
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto index_subtables_slice = m_slice.slice(bitmap_size.index_subtable_array_offset, bitmap_size.index_tables_size);
|
|
||||||
ReadonlySpan<EBLC::IndexSubTableArray> index_subtable_arrays {
|
|
||||||
bit_cast<EBLC::IndexSubTableArray const*>(index_subtables_slice.data()), bitmap_size.number_of_index_subtables
|
|
||||||
};
|
|
||||||
|
|
||||||
EBLC::IndexSubTableArray const* index_subtable_array = nullptr;
|
|
||||||
for (auto const& array : index_subtable_arrays) {
|
|
||||||
if (glyph_id >= array.first_glyph_index && glyph_id <= array.last_glyph_index)
|
|
||||||
index_subtable_array = &array;
|
|
||||||
}
|
|
||||||
if (!index_subtable_array) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto index_subtable_slice = m_slice.slice(bitmap_size.index_subtable_array_offset + index_subtable_array->additional_offset_to_index_subtable);
|
|
||||||
first_glyph_index = index_subtable_array->first_glyph_index;
|
|
||||||
last_glyph_index = index_subtable_array->last_glyph_index;
|
|
||||||
return *bit_cast<EBLC::IndexSubHeader const*>(index_subtable_slice.data());
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<CBDT> CBDT::from_slice(ReadonlyBytes slice)
|
|
||||||
{
|
|
||||||
if (slice.size() < sizeof(CbdtHeader))
|
|
||||||
return Error::from_string_literal("CBDT table too small");
|
|
||||||
auto const& header = *bit_cast<CbdtHeader*>(slice.data());
|
|
||||||
return CBDT { slice, header };
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<GPOS> GPOS::from_slice(ReadonlyBytes slice)
|
ErrorOr<GPOS> GPOS::from_slice(ReadonlyBytes slice)
|
||||||
{
|
{
|
||||||
FixedMemoryStream stream { slice };
|
FixedMemoryStream stream { slice };
|
||||||
|
|
|
@ -192,36 +192,6 @@ struct GlyphHorizontalMetrics {
|
||||||
i16 left_side_bearing;
|
i16 left_side_bearing;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/fpgm
|
|
||||||
// fpgm: Font Program
|
|
||||||
struct Fpgm {
|
|
||||||
public:
|
|
||||||
explicit Fpgm(ReadonlyBytes slice)
|
|
||||||
: m_slice(slice)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadonlyBytes program_data() const { return m_slice; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
ReadonlyBytes m_slice;
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/prep
|
|
||||||
// prep: Control Value Program
|
|
||||||
struct Prep {
|
|
||||||
public:
|
|
||||||
explicit Prep(ReadonlyBytes slice)
|
|
||||||
: m_slice(slice)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadonlyBytes program_data() const { return m_slice; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
ReadonlyBytes m_slice;
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/hmtx
|
// https://learn.microsoft.com/en-us/typography/opentype/spec/hmtx
|
||||||
// hmtx: Horizontal Metrics Table
|
// hmtx: Horizontal Metrics Table
|
||||||
class Hmtx {
|
class Hmtx {
|
||||||
|
@ -455,150 +425,6 @@ private:
|
||||||
Vector<Subtable> const m_subtables;
|
Vector<Subtable> const m_subtables;
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/eblc
|
|
||||||
// EBLC — Embedded Bitmap Location Table
|
|
||||||
class EBLC {
|
|
||||||
public:
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#sbitlinemetrics-record
|
|
||||||
struct [[gnu::packed]] SbitLineMetrics {
|
|
||||||
Int8 ascender {};
|
|
||||||
Int8 descender {};
|
|
||||||
Uint8 width_max {};
|
|
||||||
Int8 caret_slope_numerator {};
|
|
||||||
Int8 caret_slope_denominator {};
|
|
||||||
Int8 caret_offset {};
|
|
||||||
Int8 min_origin_sb {};
|
|
||||||
Int8 min_advance_sb {};
|
|
||||||
Int8 max_before_bl {};
|
|
||||||
Int8 min_after_bl {};
|
|
||||||
Int8 pad1 {};
|
|
||||||
Int8 pad2 {};
|
|
||||||
};
|
|
||||||
static_assert(AssertSize<SbitLineMetrics, 12>());
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtablearray
|
|
||||||
struct [[gnu::packed]] IndexSubTableArray {
|
|
||||||
Uint16 first_glyph_index;
|
|
||||||
Uint16 last_glyph_index;
|
|
||||||
Offset32 additional_offset_to_index_subtable;
|
|
||||||
};
|
|
||||||
static_assert(AssertSize<IndexSubTableArray, 8>());
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubheader
|
|
||||||
struct [[gnu::packed]] IndexSubHeader {
|
|
||||||
Uint16 index_format;
|
|
||||||
Uint16 image_format;
|
|
||||||
Offset32 image_data_offset;
|
|
||||||
};
|
|
||||||
static_assert(AssertSize<IndexSubHeader, 8>());
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/eblc#indexsubtable1-variable-metrics-glyphs-with-4-byte-offsets
|
|
||||||
// IndexSubTable1: variable-metrics glyphs with 4-byte offsets
|
|
||||||
struct [[gnu::packed]] IndexSubTable1 {
|
|
||||||
IndexSubHeader header;
|
|
||||||
Offset32 sbit_offsets[];
|
|
||||||
};
|
|
||||||
static_assert(AssertSize<IndexSubTable1, 8>());
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/cblc
|
|
||||||
// CBLC — Color Bitmap Location Table
|
|
||||||
class CBLC {
|
|
||||||
public:
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/cblc#bitmapsize-record
|
|
||||||
struct [[gnu::packed]] BitmapSize {
|
|
||||||
Offset32 index_subtable_array_offset;
|
|
||||||
Uint32 index_tables_size;
|
|
||||||
Uint32 number_of_index_subtables;
|
|
||||||
Uint32 color_ref;
|
|
||||||
EBLC::SbitLineMetrics hori;
|
|
||||||
EBLC::SbitLineMetrics vert;
|
|
||||||
Uint16 start_glyph_index;
|
|
||||||
Uint16 end_glyph_index;
|
|
||||||
Uint8 ppem_x {};
|
|
||||||
Uint8 ppem_y {};
|
|
||||||
Uint8 bit_depth {};
|
|
||||||
Int8 flags {};
|
|
||||||
};
|
|
||||||
static_assert(AssertSize<BitmapSize, 48>());
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/cblc#cblcheader
|
|
||||||
struct [[gnu::packed]] CblcHeader {
|
|
||||||
Uint16 major_version;
|
|
||||||
Uint16 minor_version;
|
|
||||||
Uint32 num_sizes;
|
|
||||||
// Stored in a separate span:
|
|
||||||
// BitmapSize bitmap_sizes[];
|
|
||||||
};
|
|
||||||
static_assert(AssertSize<CblcHeader, 8>());
|
|
||||||
|
|
||||||
static ErrorOr<CBLC> from_slice(ReadonlyBytes);
|
|
||||||
Optional<BitmapSize const&> bitmap_size_for_glyph_id(u32 glyph_id) const;
|
|
||||||
Optional<EBLC::IndexSubHeader const&> index_subtable_for_glyph_id(u32 glyph_id, u16& first_glyph_index, u16& last_glyph_index) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit CBLC(ReadonlyBytes slice, CblcHeader const& header, ReadonlySpan<BitmapSize> bitmap_sizes)
|
|
||||||
: m_slice(slice)
|
|
||||||
, m_header(header)
|
|
||||||
, m_bitmap_sizes(bitmap_sizes)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadonlyBytes m_slice;
|
|
||||||
CblcHeader const& m_header;
|
|
||||||
ReadonlySpan<BitmapSize> m_bitmap_sizes;
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt
|
|
||||||
// EBDT — Embedded Bitmap Data Table
|
|
||||||
class EBDT {
|
|
||||||
public:
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/ebdt#smallglyphmetrics
|
|
||||||
struct [[gnu::packed]] SmallGlyphMetrics {
|
|
||||||
Uint8 height {};
|
|
||||||
Uint8 width {};
|
|
||||||
Int8 bearing_x {};
|
|
||||||
Int8 bearing_y {};
|
|
||||||
Uint8 advance {};
|
|
||||||
};
|
|
||||||
static_assert(AssertSize<SmallGlyphMetrics, 5>());
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt
|
|
||||||
// CBDT — Color Bitmap Data Table
|
|
||||||
class CBDT {
|
|
||||||
public:
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt#table-structure
|
|
||||||
struct [[gnu::packed]] CbdtHeader {
|
|
||||||
Uint16 major_version;
|
|
||||||
Uint16 minor_version;
|
|
||||||
};
|
|
||||||
static_assert(AssertSize<CbdtHeader, 4>());
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/cbdt#format-17-small-metrics-png-image-data
|
|
||||||
struct [[gnu::packed]] Format17 {
|
|
||||||
EBDT::SmallGlyphMetrics glyph_metrics;
|
|
||||||
Uint32 data_len;
|
|
||||||
Uint8 data[];
|
|
||||||
};
|
|
||||||
static_assert(AssertSize<Format17, 9>());
|
|
||||||
|
|
||||||
static ErrorOr<CBDT> from_slice(ReadonlyBytes);
|
|
||||||
ReadonlyBytes bytes() const { return m_slice; }
|
|
||||||
|
|
||||||
CbdtHeader const& header() const { return m_header; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit CBDT(ReadonlyBytes slice, CbdtHeader const& header)
|
|
||||||
: m_slice(slice)
|
|
||||||
, m_header(header)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadonlyBytes m_slice;
|
|
||||||
CbdtHeader const& m_header;
|
|
||||||
};
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-list-table
|
// https://learn.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-list-table
|
||||||
struct [[gnu::packed]] FeatureRecord {
|
struct [[gnu::packed]] FeatureRecord {
|
||||||
Tag feature_tag;
|
Tag feature_tag;
|
||||||
|
|
|
@ -128,7 +128,6 @@ namespace OpenType {
|
||||||
u16 be_u16(u8 const*);
|
u16 be_u16(u8 const*);
|
||||||
u32 be_u32(u8 const*);
|
u32 be_u32(u8 const*);
|
||||||
i16 be_i16(u8 const*);
|
i16 be_i16(u8 const*);
|
||||||
float be_fword(u8 const*);
|
|
||||||
|
|
||||||
u16 be_u16(u8 const* ptr)
|
u16 be_u16(u8 const* ptr)
|
||||||
{
|
{
|
||||||
|
@ -145,11 +144,6 @@ i16 be_i16(u8 const* ptr)
|
||||||
return (((i16)ptr[0]) << 8) | ((i16)ptr[1]);
|
return (((i16)ptr[0]) << 8) | ((i16)ptr[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
float be_fword(u8 const* ptr)
|
|
||||||
{
|
|
||||||
return (float)be_i16(ptr) / (float)(1 << 14);
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<NonnullRefPtr<Typeface>> Typeface::try_load_from_resource(Core::Resource const& resource, unsigned index)
|
ErrorOr<NonnullRefPtr<Typeface>> Typeface::try_load_from_resource(Core::Resource const& resource, unsigned index)
|
||||||
{
|
{
|
||||||
auto font_data = Gfx::FontData::create_from_resource(resource);
|
auto font_data = Gfx::FontData::create_from_resource(resource);
|
||||||
|
@ -229,11 +223,7 @@ ErrorOr<NonnullRefPtr<Typeface>> Typeface::try_load_from_offset(ReadonlyBytes bu
|
||||||
Optional<ReadonlyBytes> opt_glyf_slice = {};
|
Optional<ReadonlyBytes> opt_glyf_slice = {};
|
||||||
Optional<ReadonlyBytes> opt_os2_slice = {};
|
Optional<ReadonlyBytes> opt_os2_slice = {};
|
||||||
Optional<ReadonlyBytes> opt_kern_slice = {};
|
Optional<ReadonlyBytes> opt_kern_slice = {};
|
||||||
Optional<ReadonlyBytes> opt_fpgm_slice = {};
|
|
||||||
Optional<ReadonlyBytes> opt_prep_slice = {};
|
|
||||||
|
|
||||||
Optional<CBLC> cblc;
|
|
||||||
Optional<CBDT> cbdt;
|
|
||||||
Optional<GPOS> gpos;
|
Optional<GPOS> gpos;
|
||||||
|
|
||||||
TRY(for_each_table_record(buffer, offset, [&](Tag table_tag, ReadonlyBytes tag_buffer) -> ErrorOr<void> {
|
TRY(for_each_table_record(buffer, offset, [&](Tag table_tag, ReadonlyBytes tag_buffer) -> ErrorOr<void> {
|
||||||
|
@ -258,14 +248,6 @@ ErrorOr<NonnullRefPtr<Typeface>> Typeface::try_load_from_offset(ReadonlyBytes bu
|
||||||
opt_os2_slice = tag_buffer;
|
opt_os2_slice = tag_buffer;
|
||||||
} else if (table_tag == Tag("kern")) {
|
} else if (table_tag == Tag("kern")) {
|
||||||
opt_kern_slice = tag_buffer;
|
opt_kern_slice = tag_buffer;
|
||||||
} else if (table_tag == Tag("fpgm")) {
|
|
||||||
opt_fpgm_slice = tag_buffer;
|
|
||||||
} else if (table_tag == Tag("prep")) {
|
|
||||||
opt_prep_slice = tag_buffer;
|
|
||||||
} else if (table_tag == Tag("CBLC")) {
|
|
||||||
cblc = TRY(CBLC::from_slice(tag_buffer));
|
|
||||||
} else if (table_tag == Tag("CBDT")) {
|
|
||||||
cbdt = TRY(CBDT::from_slice(tag_buffer));
|
|
||||||
} else if (table_tag == Tag("GPOS")) {
|
} else if (table_tag == Tag("GPOS")) {
|
||||||
gpos = TRY(GPOS::from_slice(tag_buffer));
|
gpos = TRY(GPOS::from_slice(tag_buffer));
|
||||||
}
|
}
|
||||||
|
@ -329,14 +311,6 @@ ErrorOr<NonnullRefPtr<Typeface>> Typeface::try_load_from_offset(ReadonlyBytes bu
|
||||||
if (opt_kern_slice.has_value())
|
if (opt_kern_slice.has_value())
|
||||||
kern = TRY(Kern::from_slice(opt_kern_slice.value()));
|
kern = TRY(Kern::from_slice(opt_kern_slice.value()));
|
||||||
|
|
||||||
Optional<Fpgm> fpgm;
|
|
||||||
if (opt_fpgm_slice.has_value())
|
|
||||||
fpgm = Fpgm(opt_fpgm_slice.value());
|
|
||||||
|
|
||||||
Optional<Prep> prep;
|
|
||||||
if (opt_prep_slice.has_value())
|
|
||||||
prep = Prep(opt_prep_slice.value());
|
|
||||||
|
|
||||||
return adopt_ref(*new Typeface(
|
return adopt_ref(*new Typeface(
|
||||||
move(head),
|
move(head),
|
||||||
move(name),
|
move(name),
|
||||||
|
@ -348,10 +322,6 @@ ErrorOr<NonnullRefPtr<Typeface>> Typeface::try_load_from_offset(ReadonlyBytes bu
|
||||||
move(glyf),
|
move(glyf),
|
||||||
move(os2),
|
move(os2),
|
||||||
move(kern),
|
move(kern),
|
||||||
move(fpgm),
|
|
||||||
move(prep),
|
|
||||||
move(cblc),
|
|
||||||
move(cbdt),
|
|
||||||
move(gpos),
|
move(gpos),
|
||||||
buffer.slice(offset),
|
buffer.slice(offset),
|
||||||
options.index));
|
options.index));
|
||||||
|
@ -387,46 +357,8 @@ Gfx::ScaledFontMetrics Typeface::metrics([[maybe_unused]] float x_scale, float y
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Typeface::EmbeddedBitmapData Typeface::embedded_bitmap_data_for_glyph(u32 glyph_id) const
|
float Typeface::glyph_advance(u32 glyph_id, float x_scale, float, float, float) const
|
||||||
{
|
{
|
||||||
if (!has_color_bitmaps())
|
|
||||||
return Empty {};
|
|
||||||
|
|
||||||
u16 first_glyph_index {};
|
|
||||||
u16 last_glyph_index {};
|
|
||||||
auto maybe_index_subtable = m_cblc->index_subtable_for_glyph_id(glyph_id, first_glyph_index, last_glyph_index);
|
|
||||||
if (!maybe_index_subtable.has_value())
|
|
||||||
return Empty {};
|
|
||||||
|
|
||||||
auto const& index_subtable = maybe_index_subtable.value();
|
|
||||||
auto const& bitmap_size = m_cblc->bitmap_size_for_glyph_id(glyph_id).value();
|
|
||||||
|
|
||||||
if (index_subtable.index_format == 1) {
|
|
||||||
auto const& index_subtable1 = *bit_cast<EBLC::IndexSubTable1 const*>(&index_subtable);
|
|
||||||
size_t size_of_array = (last_glyph_index - first_glyph_index + 1) + 1;
|
|
||||||
auto sbit_offsets = ReadonlySpan<Offset32> { index_subtable1.sbit_offsets, size_of_array };
|
|
||||||
auto sbit_offset = sbit_offsets[glyph_id - first_glyph_index];
|
|
||||||
size_t glyph_data_offset = sbit_offset + index_subtable.image_data_offset;
|
|
||||||
|
|
||||||
if (index_subtable.image_format == 17) {
|
|
||||||
return EmbeddedBitmapWithFormat17 {
|
|
||||||
.bitmap_size = bitmap_size,
|
|
||||||
.format17 = *bit_cast<CBDT::Format17 const*>(m_cbdt->bytes().slice(glyph_data_offset, size_of_array).data()),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
dbgln("FIXME: Implement OpenType embedded bitmap image format {}", index_subtable.image_format);
|
|
||||||
} else {
|
|
||||||
dbgln("FIXME: Implement OpenType embedded bitmap index format {}", index_subtable.index_format);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Empty {};
|
|
||||||
}
|
|
||||||
|
|
||||||
float Typeface::glyph_advance(u32 glyph_id, float x_scale, float y_scale, float point_width, float point_height) const
|
|
||||||
{
|
|
||||||
if (has_color_bitmaps())
|
|
||||||
return glyph_metrics(glyph_id, x_scale, y_scale, point_width, point_height).advance_width;
|
|
||||||
|
|
||||||
if (!m_hmtx.has_value())
|
if (!m_hmtx.has_value())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -437,33 +369,8 @@ float Typeface::glyph_advance(u32 glyph_id, float x_scale, float y_scale, float
|
||||||
return static_cast<float>(horizontal_metrics.advance_width) * x_scale;
|
return static_cast<float>(horizontal_metrics.advance_width) * x_scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
Gfx::ScaledGlyphMetrics Typeface::glyph_metrics(u32 glyph_id, float x_scale, float y_scale, float point_width, float point_height) const
|
Gfx::ScaledGlyphMetrics Typeface::glyph_metrics(u32 glyph_id, float x_scale, float y_scale, float, float) const
|
||||||
{
|
{
|
||||||
auto embedded_bitmap_metrics = embedded_bitmap_data_for_glyph(glyph_id).visit(
|
|
||||||
[&](EmbeddedBitmapWithFormat17 const& data) -> Optional<Gfx::ScaledGlyphMetrics> {
|
|
||||||
// FIXME: This is a pretty ugly hack to work out new scale factors based on the relationship between
|
|
||||||
// the pixels-per-em values and the font point size. It appears that bitmaps are not in the same
|
|
||||||
// coordinate space as the head table's "units per em" value.
|
|
||||||
// There's definitely some cleaner way to do this.
|
|
||||||
float x_scale = (point_width * DEFAULT_DPI) / (POINTS_PER_INCH * data.bitmap_size.ppem_x);
|
|
||||||
float y_scale = (point_height * DEFAULT_DPI) / (POINTS_PER_INCH * data.bitmap_size.ppem_y);
|
|
||||||
|
|
||||||
return Gfx::ScaledGlyphMetrics {
|
|
||||||
.ascender = static_cast<float>(data.bitmap_size.hori.ascender) * y_scale,
|
|
||||||
.descender = static_cast<float>(data.bitmap_size.hori.descender) * y_scale,
|
|
||||||
.advance_width = static_cast<float>(data.format17.glyph_metrics.advance) * x_scale,
|
|
||||||
.left_side_bearing = static_cast<float>(data.format17.glyph_metrics.bearing_x) * x_scale,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
[&](Empty) -> Optional<Gfx::ScaledGlyphMetrics> {
|
|
||||||
// Unsupported format or no embedded bitmap for this glyph ID.
|
|
||||||
return {};
|
|
||||||
});
|
|
||||||
|
|
||||||
if (embedded_bitmap_metrics.has_value()) {
|
|
||||||
return embedded_bitmap_metrics.release_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_loca.has_value() || !m_glyf.has_value() || !m_hmtx.has_value()) {
|
if (!m_loca.has_value() || !m_glyf.has_value() || !m_hmtx.has_value()) {
|
||||||
return Gfx::ScaledGlyphMetrics {};
|
return Gfx::ScaledGlyphMetrics {};
|
||||||
}
|
}
|
||||||
|
@ -511,61 +418,6 @@ float Typeface::glyphs_horizontal_kerning(u32 left_glyph_id, u32 right_glyph_id,
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
Typeface::AscenderAndDescender Typeface::resolve_ascender_and_descender() const
|
|
||||||
{
|
|
||||||
i16 ascender = 0;
|
|
||||||
i16 descender = 0;
|
|
||||||
|
|
||||||
if (m_os2.has_value() && m_os2->use_typographic_metrics()) {
|
|
||||||
ascender = m_os2->typographic_ascender();
|
|
||||||
descender = m_os2->typographic_descender();
|
|
||||||
} else {
|
|
||||||
ascender = m_hhea.ascender();
|
|
||||||
descender = m_hhea.descender();
|
|
||||||
}
|
|
||||||
return { ascender, descender };
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<Glyf::Glyph> Typeface::extract_and_append_glyph_path_to(Gfx::DeprecatedPath& path, u32 glyph_id, i16 ascender, i16 descender, float x_scale, float y_scale) const
|
|
||||||
{
|
|
||||||
if (!m_loca.has_value() || !m_glyf.has_value()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (glyph_id >= glyph_count()) {
|
|
||||||
glyph_id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto glyph_offset0 = m_loca->get_glyph_offset(glyph_id);
|
|
||||||
auto glyph_offset1 = m_loca->get_glyph_offset(glyph_id + 1);
|
|
||||||
|
|
||||||
// If a glyph has no outline, then loca[n] = loca [n+1].
|
|
||||||
if (glyph_offset0 == glyph_offset1)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
auto glyph = m_glyf->glyph(glyph_offset0);
|
|
||||||
if (!glyph.has_value())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
bool success = glyph->append_path(path, ascender, descender, x_scale, y_scale, [&](u16 glyph_id) {
|
|
||||||
if (glyph_id >= glyph_count()) {
|
|
||||||
glyph_id = 0;
|
|
||||||
}
|
|
||||||
auto glyph_offset = m_loca->get_glyph_offset(glyph_id);
|
|
||||||
return m_glyf->glyph(glyph_offset);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
return glyph;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Typeface::append_glyph_path_to(Gfx::DeprecatedPath& path, u32 glyph_id, float x_scale, float y_scale) const
|
|
||||||
{
|
|
||||||
auto ascender_and_descender = resolve_ascender_and_descender();
|
|
||||||
return extract_and_append_glyph_path_to(path, glyph_id, ascender_and_descender.ascender, ascender_and_descender.descender, x_scale, y_scale).has_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 Typeface::glyph_count() const
|
u32 Typeface::glyph_count() const
|
||||||
{
|
{
|
||||||
return m_maxp.num_glyphs();
|
return m_maxp.num_glyphs();
|
||||||
|
@ -652,40 +504,6 @@ u8 Typeface::slope() const
|
||||||
return *m_slope;
|
return *m_slope;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Typeface::is_fixed_width() const
|
|
||||||
{
|
|
||||||
// FIXME: Read this information from the font file itself.
|
|
||||||
// FIXME: Although, it appears some application do similar hacks
|
|
||||||
return glyph_metrics(glyph_id_for_code_point('.'), 1, 1, 1, 1).advance_width == glyph_metrics(glyph_id_for_code_point('X'), 1, 1, 1, 1).advance_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<ReadonlyBytes> Typeface::font_program() const
|
|
||||||
{
|
|
||||||
if (m_fpgm.has_value())
|
|
||||||
return m_fpgm->program_data();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<ReadonlyBytes> Typeface::control_value_program() const
|
|
||||||
{
|
|
||||||
if (m_prep.has_value())
|
|
||||||
return m_prep->program_data();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<ReadonlyBytes> Typeface::glyph_program(u32 glyph_id) const
|
|
||||||
{
|
|
||||||
if (!m_loca.has_value() || !m_glyf.has_value()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto glyph_offset = m_loca->get_glyph_offset(glyph_id);
|
|
||||||
auto glyph = m_glyf->glyph(glyph_offset);
|
|
||||||
if (!glyph.has_value())
|
|
||||||
return {};
|
|
||||||
return glyph->program();
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 Typeface::glyph_id_for_code_point(u32 code_point) const
|
u32 Typeface::glyph_id_for_code_point(u32 code_point) const
|
||||||
{
|
{
|
||||||
return glyph_page(code_point / GlyphPage::glyphs_per_page).glyph_ids[code_point % GlyphPage::glyphs_per_page];
|
return glyph_page(code_point / GlyphPage::glyphs_per_page).glyph_ids[code_point % GlyphPage::glyphs_per_page];
|
||||||
|
@ -719,28 +537,5 @@ void Typeface::populate_glyph_page(GlyphPage& glyph_page, size_t page_index) con
|
||||||
glyph_page.glyph_ids[i] = m_cmap->glyph_id_for_code_point(code_point);
|
glyph_page.glyph_ids[i] = m_cmap->glyph_id_for_code_point(code_point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool Typeface::has_color_bitmaps() const
|
|
||||||
{
|
|
||||||
return m_cblc.has_value() && m_cbdt.has_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<Gfx::Bitmap> Typeface::color_bitmap(u32 glyph_id) const
|
|
||||||
{
|
|
||||||
return embedded_bitmap_data_for_glyph(glyph_id).visit(
|
|
||||||
[&](EmbeddedBitmapWithFormat17 const& data) -> RefPtr<Gfx::Bitmap> {
|
|
||||||
auto data_slice = ReadonlyBytes { data.format17.data, static_cast<u32>(data.format17.data_len) };
|
|
||||||
auto decoder = Gfx::PNGImageDecoderPlugin::create(data_slice).release_value_but_fixme_should_propagate_errors();
|
|
||||||
auto frame = decoder->frame(0);
|
|
||||||
if (frame.is_error()) {
|
|
||||||
dbgln("PNG decode failed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return frame.value().image;
|
|
||||||
},
|
|
||||||
[&](Empty) -> RefPtr<Gfx::Bitmap> {
|
|
||||||
// Unsupported format or no image for this glyph ID.
|
|
||||||
return nullptr;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,6 @@ public:
|
||||||
virtual Gfx::ScaledGlyphMetrics glyph_metrics(u32 glyph_id, float x_scale, float y_scale, float point_width, float point_height) const override;
|
virtual Gfx::ScaledGlyphMetrics glyph_metrics(u32 glyph_id, float x_scale, float y_scale, float point_width, float point_height) const override;
|
||||||
virtual float glyph_advance(u32 glyph_id, float x_scale, float y_scale, float point_width, float point_height) const override;
|
virtual float glyph_advance(u32 glyph_id, float x_scale, float y_scale, float point_width, float point_height) const override;
|
||||||
virtual float glyphs_horizontal_kerning(u32 left_glyph_id, u32 right_glyph_id, float x_scale) const override;
|
virtual float glyphs_horizontal_kerning(u32 left_glyph_id, u32 right_glyph_id, float x_scale) const override;
|
||||||
virtual bool append_glyph_path_to(Gfx::DeprecatedPath&, u32 glyph_id, float x_scale, float y_scale) const override;
|
|
||||||
virtual u32 glyph_count() const override;
|
virtual u32 glyph_count() const override;
|
||||||
virtual u16 units_per_em() const override;
|
virtual u16 units_per_em() const override;
|
||||||
virtual u32 glyph_id_for_code_point(u32 code_point) const override;
|
virtual u32 glyph_id_for_code_point(u32 code_point) const override;
|
||||||
|
@ -67,12 +66,6 @@ public:
|
||||||
virtual u16 weight() const override;
|
virtual u16 weight() const override;
|
||||||
virtual u16 width() const override;
|
virtual u16 width() const override;
|
||||||
virtual u8 slope() const override;
|
virtual u8 slope() const override;
|
||||||
virtual bool is_fixed_width() const override;
|
|
||||||
virtual bool has_color_bitmaps() const override;
|
|
||||||
|
|
||||||
Optional<ReadonlyBytes> font_program() const;
|
|
||||||
Optional<ReadonlyBytes> control_value_program() const;
|
|
||||||
Optional<ReadonlyBytes> glyph_program(u32 glyph_id) const;
|
|
||||||
|
|
||||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/otff
|
// https://learn.microsoft.com/en-us/typography/opentype/spec/otff
|
||||||
// "OpenType fonts that contain TrueType outlines should use the value of 0x00010000 for the sfntVersion.
|
// "OpenType fonts that contain TrueType outlines should use the value of 0x00010000 for the sfntVersion.
|
||||||
|
@ -92,26 +85,6 @@ protected:
|
||||||
virtual unsigned ttc_index() const override { return m_ttc_index; }
|
virtual unsigned ttc_index() const override { return m_ttc_index; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct AscenderAndDescender {
|
|
||||||
i16 ascender;
|
|
||||||
i16 descender;
|
|
||||||
};
|
|
||||||
|
|
||||||
AscenderAndDescender resolve_ascender_and_descender() const;
|
|
||||||
|
|
||||||
Optional<Glyf::Glyph> extract_and_append_glyph_path_to(Gfx::DeprecatedPath&, u32 glyph_id, i16 ascender, i16 descender, float x_scale, float y_scale) const;
|
|
||||||
|
|
||||||
RefPtr<Gfx::Bitmap> color_bitmap(u32 glyph_id) const;
|
|
||||||
|
|
||||||
struct EmbeddedBitmapWithFormat17 {
|
|
||||||
CBLC::BitmapSize const& bitmap_size;
|
|
||||||
CBDT::Format17 const& format17;
|
|
||||||
};
|
|
||||||
|
|
||||||
using EmbeddedBitmapData = Variant<EmbeddedBitmapWithFormat17, Empty>;
|
|
||||||
|
|
||||||
EmbeddedBitmapData embedded_bitmap_data_for_glyph(u32 glyph_id) const;
|
|
||||||
|
|
||||||
static ErrorOr<NonnullRefPtr<Typeface>> try_load_from_offset(ReadonlyBytes, u32 offset, Options options);
|
static ErrorOr<NonnullRefPtr<Typeface>> try_load_from_offset(ReadonlyBytes, u32 offset, Options options);
|
||||||
|
|
||||||
Typeface(
|
Typeface(
|
||||||
|
@ -125,10 +98,6 @@ private:
|
||||||
Optional<Glyf>&& glyf,
|
Optional<Glyf>&& glyf,
|
||||||
Optional<OS2> os2,
|
Optional<OS2> os2,
|
||||||
Optional<Kern>&& kern,
|
Optional<Kern>&& kern,
|
||||||
Optional<Fpgm> fpgm,
|
|
||||||
Optional<Prep> prep,
|
|
||||||
Optional<CBLC> cblc,
|
|
||||||
Optional<CBDT> cbdt,
|
|
||||||
Optional<GPOS> gpos,
|
Optional<GPOS> gpos,
|
||||||
ReadonlyBytes buffer,
|
ReadonlyBytes buffer,
|
||||||
unsigned ttc_index)
|
unsigned ttc_index)
|
||||||
|
@ -144,10 +113,6 @@ private:
|
||||||
, m_cmap(move(cmap))
|
, m_cmap(move(cmap))
|
||||||
, m_os2(move(os2))
|
, m_os2(move(os2))
|
||||||
, m_kern(move(kern))
|
, m_kern(move(kern))
|
||||||
, m_fpgm(move(fpgm))
|
|
||||||
, m_prep(move(prep))
|
|
||||||
, m_cblc(move(cblc))
|
|
||||||
, m_cbdt(move(cbdt))
|
|
||||||
, m_gpos(move(gpos))
|
, m_gpos(move(gpos))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -167,10 +132,6 @@ private:
|
||||||
NonnullOwnPtr<CharCodeToGlyphIndex> m_cmap;
|
NonnullOwnPtr<CharCodeToGlyphIndex> m_cmap;
|
||||||
Optional<OS2> m_os2;
|
Optional<OS2> m_os2;
|
||||||
Optional<Kern> m_kern;
|
Optional<Kern> m_kern;
|
||||||
Optional<Fpgm> m_fpgm;
|
|
||||||
Optional<Prep> m_prep;
|
|
||||||
Optional<CBLC> m_cblc;
|
|
||||||
Optional<CBDT> m_cbdt;
|
|
||||||
Optional<GPOS> m_gpos;
|
Optional<GPOS> m_gpos;
|
||||||
|
|
||||||
// This cache stores information per code point.
|
// This cache stores information per code point.
|
||||||
|
|
|
@ -63,11 +63,6 @@ ALWAYS_INLINE float ScaledFont::unicode_view_width(T const& view) const
|
||||||
return longest_width;
|
return longest_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ScaledFont::append_glyph_path_to(Gfx::DeprecatedPath& path, u32 glyph_id) const
|
|
||||||
{
|
|
||||||
return m_typeface->append_glyph_path_to(path, glyph_id, m_x_scale, m_y_scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
float ScaledFont::glyph_left_bearing(u32 code_point) const
|
float ScaledFont::glyph_left_bearing(u32 code_point) const
|
||||||
{
|
{
|
||||||
auto id = glyph_id_for_code_point(code_point);
|
auto id = glyph_id_for_code_point(code_point);
|
||||||
|
@ -83,10 +78,8 @@ float ScaledFont::glyph_width(u32 code_point) const
|
||||||
template<typename CodePointIterator>
|
template<typename CodePointIterator>
|
||||||
static float glyph_or_emoji_width_impl(ScaledFont const& font, CodePointIterator& it)
|
static float glyph_or_emoji_width_impl(ScaledFont const& font, CodePointIterator& it)
|
||||||
{
|
{
|
||||||
if (!font.has_color_bitmaps()) {
|
if (auto const* emoji = Emoji::emoji_for_code_point_iterator(it))
|
||||||
if (auto const* emoji = Emoji::emoji_for_code_point_iterator(it))
|
return font.pixel_size() * emoji->width() / emoji->height();
|
||||||
return font.pixel_size() * emoji->width() / emoji->height();
|
|
||||||
}
|
|
||||||
|
|
||||||
return font.glyph_width(*it);
|
return font.glyph_width(*it);
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ public:
|
||||||
virtual float glyph_or_emoji_width(Utf8CodePointIterator&) const override;
|
virtual float glyph_or_emoji_width(Utf8CodePointIterator&) const override;
|
||||||
virtual float glyphs_horizontal_kerning(u32 left_code_point, u32 right_code_point) const override;
|
virtual float glyphs_horizontal_kerning(u32 left_code_point, u32 right_code_point) const override;
|
||||||
virtual u32 glyph_id_for_code_point(u32 code_point) const override { return m_typeface->glyph_id_for_code_point(code_point); }
|
virtual u32 glyph_id_for_code_point(u32 code_point) const override { return m_typeface->glyph_id_for_code_point(code_point); }
|
||||||
virtual bool append_glyph_path_to(Gfx::DeprecatedPath&, u32 glyph_id) const override;
|
|
||||||
virtual float preferred_line_height() const override { return metrics().height() + metrics().line_gap; }
|
virtual float preferred_line_height() const override { return metrics().height() + metrics().line_gap; }
|
||||||
virtual int x_height() const override { return m_point_height; } // FIXME: Read from font
|
virtual int x_height() const override { return m_point_height; } // FIXME: Read from font
|
||||||
virtual u8 baseline() const override { return m_point_height; } // FIXME: Read from font
|
virtual u8 baseline() const override { return m_point_height; } // FIXME: Read from font
|
||||||
|
@ -47,8 +46,6 @@ public:
|
||||||
virtual NonnullRefPtr<ScaledFont> scaled_with_size(float point_size) const;
|
virtual NonnullRefPtr<ScaledFont> scaled_with_size(float point_size) const;
|
||||||
virtual NonnullRefPtr<Font> with_size(float point_size) const override;
|
virtual NonnullRefPtr<Font> with_size(float point_size) const override;
|
||||||
|
|
||||||
virtual bool has_color_bitmaps() const override { return m_typeface->has_color_bitmaps(); }
|
|
||||||
|
|
||||||
virtual Typeface const& typeface() const override { return m_typeface; }
|
virtual Typeface const& typeface() const override { return m_typeface; }
|
||||||
|
|
||||||
SkFont skia_font(float scale) const;
|
SkFont skia_font(float scale) const;
|
||||||
|
|
|
@ -51,7 +51,6 @@ public:
|
||||||
virtual ScaledGlyphMetrics glyph_metrics(u32 glyph_id, float x_scale, float y_scale, float point_width, float point_height) const = 0;
|
virtual ScaledGlyphMetrics glyph_metrics(u32 glyph_id, float x_scale, float y_scale, float point_width, float point_height) const = 0;
|
||||||
virtual float glyph_advance(u32 glyph_id, float x_scale, float y_scale, float point_width, float point_height) const = 0;
|
virtual float glyph_advance(u32 glyph_id, float x_scale, float y_scale, float point_width, float point_height) const = 0;
|
||||||
virtual float glyphs_horizontal_kerning(u32 left_glyph_id, u32 right_glyph_id, float x_scale) const = 0;
|
virtual float glyphs_horizontal_kerning(u32 left_glyph_id, u32 right_glyph_id, float x_scale) const = 0;
|
||||||
virtual bool append_glyph_path_to(Gfx::DeprecatedPath&, u32 glyph_id, float x_scale, float y_scale) const = 0;
|
|
||||||
|
|
||||||
virtual u32 glyph_count() const = 0;
|
virtual u32 glyph_count() const = 0;
|
||||||
virtual u16 units_per_em() const = 0;
|
virtual u16 units_per_em() const = 0;
|
||||||
|
@ -61,8 +60,6 @@ public:
|
||||||
virtual u16 weight() const = 0;
|
virtual u16 weight() const = 0;
|
||||||
virtual u16 width() const = 0;
|
virtual u16 width() const = 0;
|
||||||
virtual u8 slope() const = 0;
|
virtual u8 slope() const = 0;
|
||||||
virtual bool is_fixed_width() const = 0;
|
|
||||||
virtual bool has_color_bitmaps() const = 0;
|
|
||||||
|
|
||||||
[[nodiscard]] NonnullRefPtr<ScaledFont> scaled_font(float point_size) const;
|
[[nodiscard]] NonnullRefPtr<ScaledFont> scaled_font(float point_size) const;
|
||||||
|
|
||||||
|
|
|
@ -14,33 +14,8 @@
|
||||||
|
|
||||||
namespace Gfx {
|
namespace Gfx {
|
||||||
|
|
||||||
static DrawGlyphOrEmoji construct_glyph_or_emoji(size_t index, FloatPoint const& position, Gfx::Font const& font, Span<hb_glyph_info_t const> glyph_info, Span<hb_glyph_info_t const> input_glyph_info)
|
static DrawGlyphOrEmoji construct_glyph_or_emoji(size_t index, FloatPoint const& position, Gfx::Font const&, Span<hb_glyph_info_t const> glyph_info, Span<hb_glyph_info_t const>)
|
||||||
{
|
{
|
||||||
if (font.has_color_bitmaps()) {
|
|
||||||
auto cluster_start = glyph_info[index].cluster;
|
|
||||||
auto cluster_end = [&]() -> u32 {
|
|
||||||
if (index + 1 < glyph_info.size())
|
|
||||||
return glyph_info[index + 1].cluster;
|
|
||||||
return input_glyph_info.last().cluster + 1;
|
|
||||||
}();
|
|
||||||
|
|
||||||
Vector<u32> cluster;
|
|
||||||
for (size_t j = 0; j < input_glyph_info.size(); ++j) {
|
|
||||||
auto const& glyph = input_glyph_info[j];
|
|
||||||
if (glyph.cluster >= cluster_end)
|
|
||||||
break;
|
|
||||||
if (glyph.cluster >= cluster_start)
|
|
||||||
cluster.append(glyph.codepoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auto const* emoji = Emoji::emoji_for_code_points(cluster)) {
|
|
||||||
return DrawEmoji {
|
|
||||||
.position = position,
|
|
||||||
.emoji = emoji,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DrawGlyph {
|
return DrawGlyph {
|
||||||
.position = position,
|
.position = position,
|
||||||
.glyph_id = glyph_info[index].codepoint,
|
.glyph_id = glyph_info[index].codepoint,
|
||||||
|
|
|
@ -1,198 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <AK/Format.h>
|
|
||||||
#include <AK/Utf8View.h>
|
|
||||||
#include <LibCore/ArgsParser.h>
|
|
||||||
#include <LibGfx/Font/OpenType/Hinting/Opcodes.h>
|
|
||||||
#include <LibGfx/Font/OpenType/Typeface.h>
|
|
||||||
#include <LibMain/Main.h>
|
|
||||||
|
|
||||||
using namespace OpenType::Hinting;
|
|
||||||
|
|
||||||
#define YELLOW "\e[33m"
|
|
||||||
#define CYAN "\e[36m"
|
|
||||||
#define PURPLE "\e[95m"
|
|
||||||
#define GREEN "\e[92m"
|
|
||||||
#define RESET "\e[0m"
|
|
||||||
#define GRAY "\e[90m"
|
|
||||||
|
|
||||||
struct InstructionPrinter : InstructionHandler {
|
|
||||||
InstructionPrinter(bool enable_highlighting)
|
|
||||||
: m_enable_highlighting(enable_highlighting)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void before_operation(InstructionStream& stream, Opcode opcode) override
|
|
||||||
{
|
|
||||||
if (opcode == Opcode::FDEF && stream.current_position() > 1 && m_indent_level == 1)
|
|
||||||
outln();
|
|
||||||
switch (opcode) {
|
|
||||||
case Opcode::EIF:
|
|
||||||
case Opcode::ELSE:
|
|
||||||
case Opcode::ENDF:
|
|
||||||
m_indent_level--;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto digits = int(AK::log10(float(stream.length()))) + 1;
|
|
||||||
if (m_enable_highlighting)
|
|
||||||
out(GRAY);
|
|
||||||
out("{:0{}}:", stream.current_position() - 1, digits);
|
|
||||||
if (m_enable_highlighting)
|
|
||||||
out(RESET);
|
|
||||||
out("{:{}}", ""sv, m_indent_level * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void after_operation(InstructionStream&, Opcode opcode) override
|
|
||||||
{
|
|
||||||
switch (opcode) {
|
|
||||||
case Opcode::IF:
|
|
||||||
case Opcode::ELSE:
|
|
||||||
case Opcode::IDEF:
|
|
||||||
case Opcode::FDEF:
|
|
||||||
m_indent_level++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_number(u16 value)
|
|
||||||
{
|
|
||||||
if (m_enable_highlighting)
|
|
||||||
return out(GREEN " {}" RESET, value);
|
|
||||||
return out(" {}", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_bytes(ReadonlyBytes bytes, bool first = true)
|
|
||||||
{
|
|
||||||
for (auto value : bytes) {
|
|
||||||
if (!first)
|
|
||||||
out(",");
|
|
||||||
print_number(value);
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_words(ReadonlyBytes bytes, bool first = true)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < bytes.size(); i += 2) {
|
|
||||||
if (!first)
|
|
||||||
out(",");
|
|
||||||
print_number(bytes[i] << 8 | bytes[i + 1]);
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void default_handler(Context context) override
|
|
||||||
{
|
|
||||||
auto instruction = context.instruction();
|
|
||||||
auto name = opcode_mnemonic(instruction.opcode());
|
|
||||||
if (m_enable_highlighting)
|
|
||||||
out(YELLOW);
|
|
||||||
out(name);
|
|
||||||
if (m_enable_highlighting)
|
|
||||||
out(CYAN);
|
|
||||||
out("[");
|
|
||||||
if (m_enable_highlighting)
|
|
||||||
out(PURPLE);
|
|
||||||
if (instruction.flag_bits() > 0)
|
|
||||||
out("{:0{}b}", to_underlying(instruction.opcode()) & ((1 << instruction.flag_bits()) - 1), instruction.flag_bits());
|
|
||||||
if (m_enable_highlighting)
|
|
||||||
out(CYAN);
|
|
||||||
out("]");
|
|
||||||
if (m_enable_highlighting)
|
|
||||||
out(RESET);
|
|
||||||
switch (instruction.opcode()) {
|
|
||||||
case Opcode::NPUSHB... Opcode::NPUSHB_MAX:
|
|
||||||
print_number(instruction.values().size());
|
|
||||||
print_bytes(instruction.values(), false);
|
|
||||||
break;
|
|
||||||
case Opcode::NPUSHW... Opcode::NPUSHW_MAX:
|
|
||||||
print_number(instruction.values().size() / 2);
|
|
||||||
print_words(instruction.values(), false);
|
|
||||||
break;
|
|
||||||
case Opcode::PUSHB... Opcode::PUSHB_MAX:
|
|
||||||
print_bytes(instruction.values());
|
|
||||||
break;
|
|
||||||
case Opcode::PUSHW... Opcode::PUSHW_MAX:
|
|
||||||
print_words(instruction.values());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
outln();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool m_enable_highlighting;
|
|
||||||
u32 m_indent_level { 1 };
|
|
||||||
};
|
|
||||||
|
|
||||||
static bool s_disassembly_attempted = false;
|
|
||||||
|
|
||||||
static void print_disassembly(StringView name, Optional<ReadonlyBytes> program, bool enable_highlighting, u32 code_point = 0)
|
|
||||||
{
|
|
||||||
s_disassembly_attempted = true;
|
|
||||||
if (!program.has_value()) {
|
|
||||||
out(name, code_point);
|
|
||||||
outln(": not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
out(name, code_point);
|
|
||||||
outln(": ({} bytes)\n", program->size());
|
|
||||||
InstructionPrinter printer { enable_highlighting };
|
|
||||||
InstructionStream stream { printer, *program };
|
|
||||||
while (!stream.at_end())
|
|
||||||
stream.process_next_instruction();
|
|
||||||
}
|
|
||||||
|
|
||||||
ErrorOr<int> serenity_main(Main::Arguments arguments)
|
|
||||||
{
|
|
||||||
Core::ArgsParser args_parser;
|
|
||||||
|
|
||||||
StringView font_path;
|
|
||||||
bool no_color = false;
|
|
||||||
bool dump_font_program = false;
|
|
||||||
bool dump_prep_program = false;
|
|
||||||
StringView text;
|
|
||||||
args_parser.add_positional_argument(font_path, "Path to font", "FILE");
|
|
||||||
args_parser.add_option(dump_font_program, "Disassemble font program (fpgm table)", "disasm-fpgm", 'f');
|
|
||||||
args_parser.add_option(dump_prep_program, "Disassemble CVT program (prep table)", "disasm-prep", 'p');
|
|
||||||
args_parser.add_option(text, "Disassemble glyph programs", "disasm-glyphs", 'g', "text");
|
|
||||||
args_parser.add_option(no_color, "Disable syntax highlighting", "no-color", 'n');
|
|
||||||
args_parser.parse(arguments);
|
|
||||||
|
|
||||||
auto resource = TRY(Core::Resource::load_from_filesystem(font_path));
|
|
||||||
auto font = TRY(OpenType::Typeface::try_load_from_resource(resource));
|
|
||||||
|
|
||||||
if (dump_font_program)
|
|
||||||
print_disassembly("Font program"sv, font->font_program(), !no_color);
|
|
||||||
if (dump_prep_program) {
|
|
||||||
if (dump_font_program)
|
|
||||||
outln();
|
|
||||||
print_disassembly("CVT program"sv, font->control_value_program(), !no_color);
|
|
||||||
}
|
|
||||||
if (!text.is_empty()) {
|
|
||||||
Utf8View utf8_view { text };
|
|
||||||
bool first = !(dump_font_program || dump_prep_program);
|
|
||||||
for (u32 code_point : utf8_view) {
|
|
||||||
if (!first)
|
|
||||||
outln();
|
|
||||||
auto glyph_id = font->glyph_id_for_code_point(code_point);
|
|
||||||
print_disassembly("Glyph program for codepoint {}"sv, font->glyph_program(glyph_id), !no_color, code_point);
|
|
||||||
first = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!s_disassembly_attempted) {
|
|
||||||
args_parser.print_usage(stderr, arguments.strings[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue