mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00: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: https://github.com/LadybirdBrowser/ladybird/commit/aabad3b94e1 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(icc SOURCES ../../Userland/Utilities/icc.cpp LIBS LibGfx LibMain LibURL)
|
||||
lagom_utility(image SOURCES ../../Userland/Utilities/image.cpp LIBS LibGfx LibMain)
|
||||
lagom_utility(ttfdisasm SOURCES ../../Userland/Utilities/ttfdisasm.cpp LIBS LibGfx LibMain)
|
||||
endif()
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
void text(Utf8View, Font const&);
|
||||
|
||||
FloatPoint last_point()
|
||||
{
|
||||
if (!m_points.is_empty())
|
||||
|
@ -196,9 +194,6 @@ public:
|
|||
void close_all_subpaths();
|
||||
|
||||
DeprecatedPath stroke_to_fill(float thickness) const;
|
||||
|
||||
DeprecatedPath place_text_along(Utf8View text, Font const&) const;
|
||||
|
||||
DeprecatedPath copy_transformed(AffineTransform const&) const;
|
||||
|
||||
ReadonlySpan<FloatLine> split_lines() const
|
||||
|
|
|
@ -75,7 +75,6 @@ public:
|
|||
virtual u16 weight() 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 float glyph_left_bearing(u32 code_point) const = 0;
|
||||
virtual float glyph_width(u32 code_point) const = 0;
|
||||
|
@ -97,8 +96,6 @@ public:
|
|||
Font const& bold_variant() const;
|
||||
hb_font_t* harfbuzz_font() const;
|
||||
|
||||
virtual bool has_color_bitmaps() const = 0;
|
||||
|
||||
virtual Typeface const& typeface() const = 0;
|
||||
|
||||
private:
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
* 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/Point.h>
|
||||
|
||||
|
@ -14,167 +11,6 @@ namespace OpenType {
|
|||
|
||||
extern u16 be_u16(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)
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
if (offset + sizeof(GlyphHeader) > m_slice.size())
|
||||
|
|
|
@ -9,9 +9,6 @@
|
|||
#include <AK/Endian.h>
|
||||
#include <AK/Span.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/OpenType/Tables.h>
|
||||
#include <LibGfx/Size.h>
|
||||
|
@ -43,21 +40,6 @@ private:
|
|||
// glyf: Glyph Data
|
||||
class Glyf {
|
||||
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 {
|
||||
public:
|
||||
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 xmin() const { return m_xmin; }
|
||||
|
||||
int ascender() const { return m_ymax; }
|
||||
int descender() const { return m_ymin; }
|
||||
|
||||
ReadonlyBytes program() const;
|
||||
|
||||
private:
|
||||
enum class Type {
|
||||
Simple,
|
||||
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 };
|
||||
i16 m_xmin { 0 };
|
||||
i16 m_ymin { 0 };
|
||||
|
|
|
@ -443,85 +443,6 @@ Optional<i16> OS2::x_height() const
|
|||
[](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)
|
||||
{
|
||||
FixedMemoryStream stream { slice };
|
||||
|
|
|
@ -192,36 +192,6 @@ struct GlyphHorizontalMetrics {
|
|||
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
|
||||
// hmtx: Horizontal Metrics Table
|
||||
class Hmtx {
|
||||
|
@ -455,150 +425,6 @@ private:
|
|||
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
|
||||
struct [[gnu::packed]] FeatureRecord {
|
||||
Tag feature_tag;
|
||||
|
|
|
@ -128,7 +128,6 @@ namespace OpenType {
|
|||
u16 be_u16(u8 const*);
|
||||
u32 be_u32(u8 const*);
|
||||
i16 be_i16(u8 const*);
|
||||
float be_fword(u8 const*);
|
||||
|
||||
u16 be_u16(u8 const* ptr)
|
||||
{
|
||||
|
@ -145,11 +144,6 @@ i16 be_i16(u8 const* ptr)
|
|||
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)
|
||||
{
|
||||
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_os2_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;
|
||||
|
||||
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;
|
||||
} else if (table_tag == Tag("kern")) {
|
||||
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")) {
|
||||
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())
|
||||
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(
|
||||
move(head),
|
||||
move(name),
|
||||
|
@ -348,10 +322,6 @@ ErrorOr<NonnullRefPtr<Typeface>> Typeface::try_load_from_offset(ReadonlyBytes bu
|
|||
move(glyf),
|
||||
move(os2),
|
||||
move(kern),
|
||||
move(fpgm),
|
||||
move(prep),
|
||||
move(cblc),
|
||||
move(cbdt),
|
||||
move(gpos),
|
||||
buffer.slice(offset),
|
||||
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())
|
||||
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;
|
||||
}
|
||||
|
||||
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()) {
|
||||
return Gfx::ScaledGlyphMetrics {};
|
||||
}
|
||||
|
@ -511,61 +418,6 @@ float Typeface::glyphs_horizontal_kerning(u32 left_glyph_id, u32 right_glyph_id,
|
|||
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
|
||||
{
|
||||
return m_maxp.num_glyphs();
|
||||
|
@ -652,40 +504,6 @@ u8 Typeface::slope() const
|
|||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 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 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 u16 units_per_em() 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 width() 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
|
||||
// "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; }
|
||||
|
||||
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);
|
||||
|
||||
Typeface(
|
||||
|
@ -125,10 +98,6 @@ private:
|
|||
Optional<Glyf>&& glyf,
|
||||
Optional<OS2> os2,
|
||||
Optional<Kern>&& kern,
|
||||
Optional<Fpgm> fpgm,
|
||||
Optional<Prep> prep,
|
||||
Optional<CBLC> cblc,
|
||||
Optional<CBDT> cbdt,
|
||||
Optional<GPOS> gpos,
|
||||
ReadonlyBytes buffer,
|
||||
unsigned ttc_index)
|
||||
|
@ -144,10 +113,6 @@ private:
|
|||
, m_cmap(move(cmap))
|
||||
, m_os2(move(os2))
|
||||
, m_kern(move(kern))
|
||||
, m_fpgm(move(fpgm))
|
||||
, m_prep(move(prep))
|
||||
, m_cblc(move(cblc))
|
||||
, m_cbdt(move(cbdt))
|
||||
, m_gpos(move(gpos))
|
||||
{
|
||||
}
|
||||
|
@ -167,10 +132,6 @@ private:
|
|||
NonnullOwnPtr<CharCodeToGlyphIndex> m_cmap;
|
||||
Optional<OS2> m_os2;
|
||||
Optional<Kern> m_kern;
|
||||
Optional<Fpgm> m_fpgm;
|
||||
Optional<Prep> m_prep;
|
||||
Optional<CBLC> m_cblc;
|
||||
Optional<CBDT> m_cbdt;
|
||||
Optional<GPOS> m_gpos;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
auto id = glyph_id_for_code_point(code_point);
|
||||
|
@ -83,10 +78,8 @@ float ScaledFont::glyph_width(u32 code_point) const
|
|||
template<typename CodePointIterator>
|
||||
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))
|
||||
return font.pixel_size() * emoji->width() / emoji->height();
|
||||
}
|
||||
if (auto const* emoji = Emoji::emoji_for_code_point_iterator(it))
|
||||
return font.pixel_size() * emoji->width() / emoji->height();
|
||||
|
||||
return font.glyph_width(*it);
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ public:
|
|||
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 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 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
|
||||
|
@ -47,8 +46,6 @@ public:
|
|||
virtual NonnullRefPtr<ScaledFont> scaled_with_size(float point_size) const;
|
||||
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; }
|
||||
|
||||
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 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 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 u16 units_per_em() const = 0;
|
||||
|
@ -61,8 +60,6 @@ public:
|
|||
virtual u16 weight() const = 0;
|
||||
virtual u16 width() 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;
|
||||
|
||||
|
|
|
@ -14,33 +14,8 @@
|
|||
|
||||
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 {
|
||||
.position = position,
|
||||
.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…
Reference in a new issue