From 2185a98b3669fa1c43f9da9c613e7f90cb5739e6 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 19 Dec 2022 23:35:49 +0100 Subject: [PATCH] LibGfx/OpenType: Clean up "kern" table reading - Use C++ structs for the header and subtable headers. - Use AK::binary_search to search for kerning pairs. --- .../Libraries/LibGfx/Font/OpenType/Font.cpp | 75 +++++++++---------- .../Libraries/LibGfx/Font/OpenType/Tables.h | 28 ++++++- 2 files changed, 59 insertions(+), 44 deletions(-) diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp b/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp index 0527d09a56..94b12a3df1 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp +++ b/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp @@ -1,11 +1,12 @@ /* * Copyright (c) 2020, Srimanta Barua - * Copyright (c) 2021, Andreas Kling + * Copyright (c) 2021-2022, Andreas Kling * Copyright (c) 2022, Jelle Raaijmakers * * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include @@ -166,12 +167,13 @@ Optional Name::from_slice(ReadonlyBytes slice) ErrorOr Kern::from_slice(ReadonlyBytes slice) { - if (slice.size() < sizeof(u32)) + if (slice.size() < sizeof(Header)) return Error::from_string_literal("Invalid kern table header"); // We only support the old (2x u16) version of the header - auto version = be_u16(slice.data()); - auto number_of_subtables = be_u16(slice.offset(sizeof(u16))); + auto const& header = *bit_cast
(slice.data()); + auto version = header.version; + auto number_of_subtables = header.n_tables; if (version != 0) return Error::from_string_literal("Unsupported kern table version"); if (number_of_subtables == 0) @@ -179,14 +181,13 @@ ErrorOr Kern::from_slice(ReadonlyBytes slice) // Read all subtable offsets auto subtable_offsets = TRY(FixedArray::try_create(number_of_subtables)); - size_t offset = 2 * sizeof(u16); + size_t offset = sizeof(Header); for (size_t i = 0; i < number_of_subtables; ++i) { - if (slice.size() < offset + Sizes::SubtableHeader) + 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; - auto subtable_size = be_u16(slice.offset(offset + sizeof(u16))); - offset += subtable_size; + offset += subtable_header.length; } return Kern(slice, move(subtable_offsets)); @@ -199,10 +200,11 @@ i16 Kern::get_glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const 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 = be_u16(subtable_slice.data()); - auto length = be_u16(subtable_slice.offset(sizeof(u16))); - auto coverage = be_u16(subtable_slice.offset(2 * sizeof(u16))); + auto version = subtable_header.version; + auto length = subtable_header.version; + auto coverage = subtable_header.coverage; if (version != 0) { dbgln("OpenType::Kern: unsupported subtable version {}", version); @@ -231,7 +233,7 @@ i16 Kern::get_glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const Optional subtable_kerning; switch (format) { case 0: - subtable_kerning = read_glyph_kerning_format0(subtable_slice.slice(Sizes::SubtableHeader), left_glyph_id, right_glyph_id); + 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); @@ -251,48 +253,39 @@ i16 Kern::get_glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const Optional Kern::read_glyph_kerning_format0(ReadonlyBytes slice, u16 left_glyph_id, u16 right_glyph_id) { - if (slice.size() < 4 * sizeof(u16)) + if (slice.size() < sizeof(Format0)) return {}; - u16 number_of_pairs = be_u16(slice.data()); - u16 search_range = be_u16(slice.offset_pointer(sizeof(u16))); - u16 entry_selector = be_u16(slice.offset_pointer(2 * sizeof(u16))); - u16 range_shift = be_u16(slice.offset_pointer(3 * sizeof(u16))); + 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; // Sanity checks for this table format - auto pairs_in_search_range = search_range / Sizes::Format0Entry; + auto pairs_in_search_range = search_range / sizeof(Format0Pair); if (number_of_pairs == 0) return {}; if (pairs_in_search_range > number_of_pairs) return {}; - if ((1 << entry_selector) * Sizes::Format0Entry != search_range) + if ((1 << entry_selector) * sizeof(Format0Pair) != search_range) return {}; - if ((number_of_pairs - pairs_in_search_range) * Sizes::Format0Entry != range_shift) + if ((number_of_pairs - pairs_in_search_range) * sizeof(Format0Pair) != range_shift) return {}; // FIXME: implement a possibly slightly more efficient binary search using the parameters above - auto search_slice = slice.slice(4 * sizeof(u16)); - size_t left_idx = 0; - size_t right_idx = number_of_pairs - 1; - for (auto i = 0; i < 16; ++i) { - size_t pivot_idx = (left_idx + right_idx) / 2; + Span pairs { bit_cast(slice.slice(sizeof(Format0)).data()), number_of_pairs }; - u16 pivot_left_glyph_id = be_u16(search_slice.offset(pivot_idx * Sizes::Format0Entry + 0)); - u16 pivot_right_glyph_id = be_u16(search_slice.offset(pivot_idx * Sizes::Format0Entry + 2)); + // 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 as_u32 = (static_cast(pair.left) << 16u) | static_cast(pair.right); + return needle - as_u32; + }); - // Match - if (pivot_left_glyph_id == left_glyph_id && pivot_right_glyph_id == right_glyph_id) - return be_i16(search_slice.offset(pivot_idx * Sizes::Format0Entry + 4)); - - // Narrow search area - if (pivot_left_glyph_id < left_glyph_id || (pivot_left_glyph_id == left_glyph_id && pivot_right_glyph_id < right_glyph_id)) - left_idx = pivot_idx + 1; - else if (pivot_idx == left_idx) - break; - else - right_idx = pivot_idx - 1; - } - return 0; + if (!pair) + return 0; + return pair->value; } DeprecatedString Name::string_for_id(NameId id) const diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Tables.h b/Userland/Libraries/LibGfx/Font/OpenType/Tables.h index 8e0f013a54..fadb0636d5 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Tables.h +++ b/Userland/Libraries/LibGfx/Font/OpenType/Tables.h @@ -327,11 +327,33 @@ public: i16 get_glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const; private: - enum Sizes : size_t { - SubtableHeader = 6, - Format0Entry = 6, + struct Header { + BigEndian version; + BigEndian n_tables; }; + struct SubtableHeader { + BigEndian version; + BigEndian length; + BigEndian coverage; + }; + + // https://learn.microsoft.com/en-us/typography/opentype/spec/kern#format-0 + struct Format0 { + BigEndian n_pairs; + BigEndian search_range; + BigEndian entry_selector; + BigEndian range_shift; + }; + + struct Format0Pair { + BigEndian left; + BigEndian right; + FWord value; + }; + + Header const& header() const { return *bit_cast
(m_slice.data()); } + Kern(ReadonlyBytes slice, FixedArray subtable_offsets) : m_slice(slice) , m_subtable_offsets(move(subtable_offsets))