1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 23:47:45 +00:00

LibGfx: Use FourCC as OpenType::Tag type

The one deviation from the spec here is to use this in the WOFF
TableDirectoryEntry's tag field. However, *not* making that a Tag made
a lot of things more complicated than they need to be.
This commit is contained in:
Sam Atkins 2023-11-20 17:04:56 +00:00 committed by Andreas Kling
parent 0423225290
commit 54d0aafff0
5 changed files with 97 additions and 127 deletions

View file

@ -11,6 +11,7 @@
#include <AK/Endian.h>
#include <AK/Types.h>
#include <LibGfx/FourCC.h>
// https://learn.microsoft.com/en-us/typography/opentype/spec/otff#data-types
namespace OpenType {
@ -39,7 +40,7 @@ struct [[gnu::packed]] LongDateTime {
};
static_assert(AssertSize<LongDateTime, 8>());
using Tag = BigEndian<u32>;
using Tag = Gfx::FourCC;
using Offset16 = BigEndian<u16>;
// FIXME: Offset24

View file

@ -48,7 +48,6 @@ u16 be_u16(u8 const*);
u32 be_u32(u8 const*);
i16 be_i16(u8 const*);
float be_fword(u8 const*);
u32 tag_from_str(char const*);
u16 be_u16(u8 const* ptr)
{
@ -70,11 +69,6 @@ float be_fword(u8 const* ptr)
return (float)be_i16(ptr) / (float)(1 << 14);
}
u32 tag_from_str(char const* str)
{
return be_u32((u8 const*)str);
}
ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_resource(Core::Resource const& resource, unsigned index)
{
auto font = TRY(try_load_from_externally_owned_memory(resource.data(), index));
@ -87,7 +81,7 @@ ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_externally_owned_memory(Readonl
FixedMemoryStream stream { buffer };
auto tag = TRY(stream.read_value<Tag>());
if (tag == tag_from_str("ttcf")) {
if (tag == Tag("ttcf")) {
// It's a font collection
TRY(stream.seek(0, SeekMode::SetPosition));
auto ttc_header_v1 = TRY(stream.read_in_place<TTCHeaderV1>());
@ -100,10 +94,10 @@ ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_externally_owned_memory(Readonl
auto offset = TRY(stream.read_value<BigEndian<u32>>());
return try_load_from_offset(buffer, offset);
}
if (tag == tag_from_str("OTTO"))
if (tag == Tag("OTTO"))
return Error::from_string_literal("CFF fonts not supported yet");
if (tag != 0x00010000 && tag != tag_from_str("true"))
if (tag.to_u32() != 0x00010000 && tag != Tag("true"))
return Error::from_string_literal("Not a valid font");
return try_load_from_offset(buffer, 0);
@ -145,35 +139,35 @@ ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_offset(ReadonlyBytes buffer, u3
auto buffer_here = buffer.slice(table_record.offset, table_record.length);
// Get the table offsets we need.
if (table_record.table_tag == tag_from_str("head")) {
if (table_record.table_tag == Tag("head")) {
opt_head_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("name")) {
} else if (table_record.table_tag == Tag("name")) {
opt_name_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("hhea")) {
} else if (table_record.table_tag == Tag("hhea")) {
opt_hhea_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("maxp")) {
} else if (table_record.table_tag == Tag("maxp")) {
opt_maxp_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("hmtx")) {
} else if (table_record.table_tag == Tag("hmtx")) {
opt_hmtx_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("cmap")) {
} else if (table_record.table_tag == Tag("cmap")) {
opt_cmap_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("loca")) {
} else if (table_record.table_tag == Tag("loca")) {
opt_loca_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("glyf")) {
} else if (table_record.table_tag == Tag("glyf")) {
opt_glyf_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("OS/2")) {
} else if (table_record.table_tag == Tag("OS/2")) {
opt_os2_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("kern")) {
} else if (table_record.table_tag == Tag("kern")) {
opt_kern_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("fpgm")) {
} else if (table_record.table_tag == Tag("fpgm")) {
opt_fpgm_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("prep")) {
} else if (table_record.table_tag == Tag("prep")) {
opt_prep_slice = buffer_here;
} else if (table_record.table_tag == tag_from_str("CBLC")) {
} else if (table_record.table_tag == Tag("CBLC")) {
cblc = TRY(CBLC::from_slice(buffer_here));
} else if (table_record.table_tag == tag_from_str("CBDT")) {
} else if (table_record.table_tag == Tag("CBDT")) {
cbdt = TRY(CBDT::from_slice(buffer_here));
} else if (table_record.table_tag == tag_from_str("GPOS")) {
} else if (table_record.table_tag == Tag("GPOS")) {
gpos = TRY(GPOS::from_slice(buffer_here));
}
}

View file

@ -16,16 +16,6 @@
namespace OpenType {
static u32 be_u32(u8 const* ptr)
{
return (((u32)ptr[0]) << 24) | (((u32)ptr[1]) << 16) | (((u32)ptr[2]) << 8) | ((u32)ptr[3]);
}
static u32 tag_from_str(char const* str)
{
return be_u32((u8 const*)str);
}
ErrorOr<Head> Head::from_slice(ReadonlyBytes slice)
{
if (slice.size() < sizeof(FontHeaderTable))
@ -576,7 +566,7 @@ Optional<i16> GPOS::glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const
Optional<Offset16> kern_feature_offset;
for (auto const& feature_record : m_feature_records) {
if (feature_record.feature_tag == tag_from_str("kern")) {
if (feature_record.feature_tag == Tag("kern")) {
kern_feature_offset = feature_record.feature_offset;
break;
}

View file

@ -36,7 +36,7 @@ static_assert(AssertSize<Header, 44>());
// https://www.w3.org/TR/WOFF/#TableDirectory
struct [[gnu::packed]] TableDirectoryEntry {
BigEndian<u32> tag; // 4-byte sfnt table identifier.
OpenType::Tag tag; // 4-byte sfnt table identifier.
BigEndian<u32> offset; // Offset to the data, from beginning of WOFF file.
BigEndian<u32> comp_length; // Length of the compressed data, excluding padding.
BigEndian<u32> orig_length; // Length of the uncompressed table, excluding padding.

View file

@ -125,19 +125,10 @@ enum class TransformationVersion {
struct TableDirectoryEntry {
TransformationVersion transformation_version { TransformationVersion::Version0 };
DeprecatedString tag;
OpenType::Tag tag;
u32 original_length { 0 };
Optional<u32> transform_length;
u32 tag_to_u32() const
{
VERIFY(tag.length() == 4);
return (static_cast<u8>(tag[0]) << 24)
| (static_cast<u8>(tag[1]) << 16)
| (static_cast<u8>(tag[2]) << 8)
| static_cast<u8>(tag[3]);
}
bool has_transformation() const
{
return transform_length.has_value();
@ -145,70 +136,70 @@ struct TableDirectoryEntry {
};
// NOTE: Any tags less than 4 characters long are padded with spaces at the end.
static constexpr Array<StringView, 63> known_tag_names = {
"cmap"sv,
"head"sv,
"hhea"sv,
"hmtx"sv,
"maxp"sv,
"name"sv,
"OS/2"sv,
"post"sv,
"cvt "sv,
"fpgm"sv,
"glyf"sv,
"loca"sv,
"prep"sv,
"CFF "sv,
"VORG"sv,
"EBDT"sv,
"EBLC"sv,
"gasp"sv,
"hdmx"sv,
"kern"sv,
"LTSH"sv,
"PCLT"sv,
"VDMX"sv,
"vhea"sv,
"vmtx"sv,
"BASE"sv,
"GDEF"sv,
"GPOS"sv,
"GSUB"sv,
"EBSC"sv,
"JSTF"sv,
"MATH"sv,
"CBDT"sv,
"CBLC"sv,
"COLR"sv,
"CPAL"sv,
"SVG "sv,
"sbix"sv,
"acnt"sv,
"avar"sv,
"bdat"sv,
"bloc"sv,
"bsln"sv,
"cvar"sv,
"fdsc"sv,
"feat"sv,
"fmtx"sv,
"fvar"sv,
"gvar"sv,
"hsty"sv,
"just"sv,
"lcar"sv,
"mort"sv,
"morx"sv,
"opbd"sv,
"prop"sv,
"trak"sv,
"Zapf"sv,
"Silf"sv,
"Glat"sv,
"Gloc"sv,
"Feat"sv,
"Sill"sv,
static constexpr Array<OpenType::Tag, 63> known_tag_names = {
OpenType::Tag("cmap"),
OpenType::Tag("head"),
OpenType::Tag("hhea"),
OpenType::Tag("hmtx"),
OpenType::Tag("maxp"),
OpenType::Tag("name"),
OpenType::Tag("OS/2"),
OpenType::Tag("post"),
OpenType::Tag("cvt "),
OpenType::Tag("fpgm"),
OpenType::Tag("glyf"),
OpenType::Tag("loca"),
OpenType::Tag("prep"),
OpenType::Tag("CFF "),
OpenType::Tag("VORG"),
OpenType::Tag("EBDT"),
OpenType::Tag("EBLC"),
OpenType::Tag("gasp"),
OpenType::Tag("hdmx"),
OpenType::Tag("kern"),
OpenType::Tag("LTSH"),
OpenType::Tag("PCLT"),
OpenType::Tag("VDMX"),
OpenType::Tag("vhea"),
OpenType::Tag("vmtx"),
OpenType::Tag("BASE"),
OpenType::Tag("GDEF"),
OpenType::Tag("GPOS"),
OpenType::Tag("GSUB"),
OpenType::Tag("EBSC"),
OpenType::Tag("JSTF"),
OpenType::Tag("MATH"),
OpenType::Tag("CBDT"),
OpenType::Tag("CBLC"),
OpenType::Tag("COLR"),
OpenType::Tag("CPAL"),
OpenType::Tag("SVG "),
OpenType::Tag("sbix"),
OpenType::Tag("acnt"),
OpenType::Tag("avar"),
OpenType::Tag("bdat"),
OpenType::Tag("bloc"),
OpenType::Tag("bsln"),
OpenType::Tag("cvar"),
OpenType::Tag("fdsc"),
OpenType::Tag("feat"),
OpenType::Tag("fmtx"),
OpenType::Tag("fvar"),
OpenType::Tag("gvar"),
OpenType::Tag("hsty"),
OpenType::Tag("just"),
OpenType::Tag("lcar"),
OpenType::Tag("mort"),
OpenType::Tag("morx"),
OpenType::Tag("opbd"),
OpenType::Tag("prop"),
OpenType::Tag("trak"),
OpenType::Tag("Zapf"),
OpenType::Tag("Silf"),
OpenType::Tag("Glat"),
OpenType::Tag("Gloc"),
OpenType::Tag("Feat"),
OpenType::Tag("Sill"),
};
struct CoordinateTripletEncoding {
@ -916,16 +907,13 @@ ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_externally_owned_memory(Seekabl
if (tag_number != 0x3F) {
table_directory_entry.tag = known_tag_names[tag_number];
} else {
u8 tag_buffer[5] {};
TRY(stream.read_until_filled(Bytes { tag_buffer, 4 }));
table_directory_entry.tag = StringView { tag_buffer, 4 };
table_directory_entry.tag = TRY(stream.read_value<OpenType::Tag>());
}
VERIFY(table_directory_entry.tag.length() == 4);
table_directory_entry.original_length = TRY(read_uint_base_128(stream));
bool needs_to_read_transform_length = false;
if (table_directory_entry.tag.is_one_of("glyf"sv, "loca"sv))
if (table_directory_entry.tag == OpenType::Tag("glyf") || table_directory_entry.tag == OpenType::Tag("loca"))
needs_to_read_transform_length = table_directory_entry.transformation_version == TransformationVersion::Version0;
else
needs_to_read_transform_length = table_directory_entry.transformation_version != TransformationVersion::Version0;
@ -944,11 +932,11 @@ ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_externally_owned_memory(Seekabl
// FIXME: Read in collection header and entries.
auto glyf_table = table_entries.find_if([](TableDirectoryEntry const& entry) {
return entry.tag == "glyf"sv;
return entry.tag == OpenType::Tag("glyf");
});
auto loca_table = table_entries.find_if([](TableDirectoryEntry const& entry) {
return entry.tag == "loca"sv;
return entry.tag == OpenType::Tag("loca");
});
// "In other words, both glyf and loca tables must either be present in their transformed format or with null transform applied to both tables."
@ -991,17 +979,15 @@ ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_externally_owned_memory(Seekabl
size_t table_directory_offset = SFNT_HEADER_SIZE + table_entry_index * SFNT_TABLE_SIZE;
if (table_entry.has_transformation()) {
if (table_entry.tag == "glyf"sv) {
if (table_entry.tag == OpenType::Tag("glyf")) {
auto table_stream = FixedMemoryStream(table_bytes);
glyf_and_loca_buffer = TRY(create_glyf_and_loca_tables_from_transformed_glyf_table(table_stream));
constexpr u32 GLYF_TAG = 0x676C7966;
if (font_buffer.size() < (font_buffer_offset + glyf_and_loca_buffer->glyf_table.size()))
TRY(font_buffer.try_resize(font_buffer_offset + glyf_and_loca_buffer->glyf_table.size()));
OpenType::TableRecord table_record {
.table_tag = GLYF_TAG,
.table_tag = table_entry.tag,
.checksum = 0, // FIXME: WOFF2 does not give us the original checksum.
.offset = font_buffer_offset,
.length = glyf_and_loca_buffer->glyf_table.size(),
@ -1010,15 +996,14 @@ ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_externally_owned_memory(Seekabl
font_buffer.overwrite(font_buffer_offset, glyf_and_loca_buffer->glyf_table.data(), glyf_and_loca_buffer->glyf_table.size());
font_buffer_offset += glyf_and_loca_buffer->glyf_table.size();
} else if (table_entry.tag == "loca"sv) {
} else if (table_entry.tag == OpenType::Tag("loca")) {
// FIXME: Handle loca table coming before glyf table in input?
VERIFY(glyf_and_loca_buffer.has_value());
if (font_buffer.size() < (font_buffer_offset + glyf_and_loca_buffer->loca_table.size()))
TRY(font_buffer.try_resize(font_buffer_offset + glyf_and_loca_buffer->loca_table.size()));
constexpr u32 LOCA_TAG = 0x6C6F6361;
OpenType::TableRecord table_record {
.table_tag = LOCA_TAG,
.table_tag = table_entry.tag,
.checksum = 0, // FIXME: WOFF2 does not give us the original checksum.
.offset = font_buffer_offset,
.length = glyf_and_loca_buffer->loca_table.size(),
@ -1027,14 +1012,14 @@ ErrorOr<NonnullRefPtr<Font>> Font::try_load_from_externally_owned_memory(Seekabl
font_buffer.overwrite(font_buffer_offset, glyf_and_loca_buffer->loca_table.data(), glyf_and_loca_buffer->loca_table.size());
font_buffer_offset += glyf_and_loca_buffer->loca_table.size();
} else if (table_entry.tag == "hmtx"sv) {
} else if (table_entry.tag == OpenType::Tag("hmtx")) {
return Error::from_string_literal("Decoding transformed hmtx table not yet supported");
} else {
return Error::from_string_literal("Unknown transformation");
}
} else {
OpenType::TableRecord table_record {
.table_tag = table_entry.tag_to_u32(),
.table_tag = table_entry.tag,
.checksum = 0, // FIXME: WOFF2 does not give us the original checksum.
.offset = font_buffer_offset,
.length = length_to_read,