From 3c7d6541828d17b5566424530e6b9bc167379b64 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 2 Nov 2023 11:08:35 +0000 Subject: [PATCH] LibGfx: Remove bit casting in OpenType Kern table after construction Do more checks at load time, including categorizing the subtables and producing our own directory of them. The format for Kern is a little complicated, so use a Stream instead of manual offsets. --- .../Libraries/LibGfx/Font/OpenType/Tables.cpp | 97 +++++++++---------- .../Libraries/LibGfx/Font/OpenType/Tables.h | 44 +++++++-- 2 files changed, 84 insertions(+), 57 deletions(-) diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Tables.cpp b/Userland/Libraries/LibGfx/Font/OpenType/Tables.cpp index 9a5dd1b365..d96db972be 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Tables.cpp +++ b/Userland/Libraries/LibGfx/Font/OpenType/Tables.cpp @@ -239,11 +239,10 @@ String Name::string_for_id(NameId id) const ErrorOr Kern::from_slice(ReadonlyBytes slice) { - if (slice.size() < sizeof(Header)) - return Error::from_string_literal("Invalid kern table header"); + FixedMemoryStream stream { slice }; // We only support the old (2x u16) version of the header - auto const& header = *bit_cast
(slice.data()); + auto const& header = *TRY(stream.read_in_place
()); auto version = header.version; auto number_of_subtables = header.n_tables; if (version != 0) @@ -251,18 +250,41 @@ ErrorOr Kern::from_slice(ReadonlyBytes slice) if (number_of_subtables == 0) return Error::from_string_literal("Kern table does not contain any subtables"); - // Read all subtable offsets - auto subtable_offsets = TRY(FixedArray::create(number_of_subtables)); - size_t offset = sizeof(Header); + // Read subtables + Vector subtables; + TRY(subtables.try_ensure_capacity(number_of_subtables)); for (size_t i = 0; i < number_of_subtables; ++i) { - if (slice.size() < offset + sizeof(SubtableHeader)) - return Error::from_string_literal("Invalid kern subtable header"); - auto const& subtable_header = *bit_cast(slice.offset_pointer(offset)); - subtable_offsets[i] = offset; - offset += subtable_header.length; + auto const& subtable_header = *TRY(stream.read_in_place()); + + if (subtable_header.version != 0) + return Error::from_string_literal("Unsupported Kern subtable version"); + + if (stream.remaining() + sizeof(SubtableHeader) < subtable_header.length) + return Error::from_string_literal("Kern subtable is truncated"); + + auto subtable_format = (subtable_header.coverage & 0xFF00) >> 8; + if (subtable_format == 0) { + auto const& format0_header = *TRY(stream.read_in_place()); + auto pairs = TRY(stream.read_in_place(5)); + + subtables.append(Subtable { + .header = subtable_header, + .table = Format0Table { + .header = format0_header, + .pairs = pairs, + }, + }); + } else { + dbgln("OpenType::Kern: FIXME: subtable format {} is unsupported", subtable_format); + TRY(stream.discard(subtable_header.length - sizeof(SubtableHeader))); + subtables.append(Subtable { + .header = subtable_header, + .table = UnsupportedTable {}, + }); + } } - return Kern(slice, move(subtable_offsets)); + return Kern(header, move(subtables)); } i16 Kern::get_glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const @@ -270,30 +292,14 @@ i16 Kern::get_glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const VERIFY(left_glyph_id > 0 && right_glyph_id > 0); i16 glyph_kerning = 0; - for (auto subtable_offset : m_subtable_offsets) { - auto subtable_slice = m_slice.slice(subtable_offset); - auto const& subtable_header = *bit_cast(subtable_slice.data()); - - auto version = subtable_header.version; - auto length = subtable_header.length; - auto coverage = subtable_header.coverage; - - if (version != 0) { - dbgln("OpenType::Kern: unsupported subtable version {}", version); - continue; - } - - if (subtable_slice.size() < length) { - dbgln("OpenType::Kern: subtable has an invalid size {}", length); - continue; - } + for (auto const& subtable : m_subtables) { + auto coverage = subtable.header.coverage; auto is_horizontal = (coverage & (1 << 0)) > 0; auto is_minimum = (coverage & (1 << 1)) > 0; auto is_cross_stream = (coverage & (1 << 2)) > 0; auto is_override = (coverage & (1 << 3)) > 0; auto reserved_bits = (coverage & 0xF0); - auto format = (coverage & 0xFF00) >> 8; // FIXME: implement support for these features if (!is_horizontal || is_minimum || is_cross_stream || (reserved_bits > 0)) { @@ -303,14 +309,12 @@ i16 Kern::get_glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const // FIXME: implement support for subtable formats other than 0 Optional subtable_kerning; - switch (format) { - case 0: - subtable_kerning = read_glyph_kerning_format0(subtable_slice.slice(sizeof(SubtableHeader)), left_glyph_id, right_glyph_id); - break; - default: - dbgln("OpenType::Kern: FIXME: subtable format {} is unsupported", format); - continue; - } + subtable.table.visit( + [&](Format0Table const& format0) { + subtable_kerning = read_glyph_kerning_format0(format0, left_glyph_id, right_glyph_id); + }, + [&](auto&) {}); + if (!subtable_kerning.has_value()) continue; auto kerning_value = subtable_kerning.release_value(); @@ -323,16 +327,12 @@ i16 Kern::get_glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const return glyph_kerning; } -Optional Kern::read_glyph_kerning_format0(ReadonlyBytes slice, u16 left_glyph_id, u16 right_glyph_id) +Optional Kern::read_glyph_kerning_format0(Format0Table const& format0, u16 left_glyph_id, u16 right_glyph_id) { - if (slice.size() < sizeof(Format0)) - return {}; - - auto const& format0 = *bit_cast(slice.data()); - u16 number_of_pairs = format0.n_pairs; - u16 search_range = format0.search_range; - u16 entry_selector = format0.entry_selector; - u16 range_shift = format0.range_shift; + u16 number_of_pairs = format0.header.n_pairs; + u16 search_range = format0.header.search_range; + u16 entry_selector = format0.header.entry_selector; + u16 range_shift = format0.header.range_shift; // Sanity checks for this table format auto pairs_in_search_range = search_range / sizeof(Format0Pair); @@ -346,11 +346,10 @@ Optional Kern::read_glyph_kerning_format0(ReadonlyBytes slice, u16 left_gly return {}; // FIXME: implement a possibly slightly more efficient binary search using the parameters above - ReadonlySpan pairs { bit_cast(slice.slice(sizeof(Format0)).data()), number_of_pairs }; // The left and right halves of the kerning pair make an unsigned 32-bit number, which is then used to order the kerning pairs numerically. auto needle = (static_cast(left_glyph_id) << 16u) | static_cast(right_glyph_id); - auto* pair = binary_search(pairs, nullptr, nullptr, [&](void*, Format0Pair const& pair) { + auto* pair = binary_search(format0.pairs, nullptr, nullptr, [&](void*, Format0Pair const& pair) { auto as_u32 = (static_cast(pair.left) << 16u) | static_cast(pair.right); return needle - as_u32; }); diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Tables.h b/Userland/Libraries/LibGfx/Font/OpenType/Tables.h index 63ce23b769..97b97d0fa1 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Tables.h +++ b/Userland/Libraries/LibGfx/Font/OpenType/Tables.h @@ -424,7 +424,6 @@ public: static ErrorOr from_slice(ReadonlyBytes); i16 get_glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const; -private: struct [[gnu::packed]] Header { BigEndian version; BigEndian n_tables; @@ -454,18 +453,28 @@ private: }; static_assert(AssertSize()); - Header const& header() const { return *bit_cast
(m_slice.data()); } +private: + // Non-spec structs for easier reference + struct Format0Table { + Format0 const& header; + ReadonlySpan pairs; + }; + struct UnsupportedTable { }; + struct Subtable { + SubtableHeader const& header; + Variant table; + }; - Kern(ReadonlyBytes slice, FixedArray subtable_offsets) - : m_slice(slice) - , m_subtable_offsets(move(subtable_offsets)) + Kern(Header const& header, Vector subtables) + : m_header(header) + , m_subtables(move(subtables)) { } - static Optional read_glyph_kerning_format0(ReadonlyBytes slice, u16 left_glyph_id, u16 right_glyph_id); + static Optional read_glyph_kerning_format0(Format0Table const& format0, u16 left_glyph_id, u16 right_glyph_id); - ReadonlyBytes m_slice; - FixedArray m_subtable_offsets; + Header const& m_header; + Vector const m_subtables; }; // https://learn.microsoft.com/en-us/typography/opentype/spec/eblc @@ -764,3 +773,22 @@ private: ReadonlyBytes m_slice; }; } + +namespace AK { +template<> +struct Traits : public GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; +template<> +struct Traits : public GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; +template<> +struct Traits : public GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; +template<> +struct Traits : public GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; +}