Skip to content

Commit

Permalink
[ttf] properly parse head table when parsing ttf. still lots more tab…
Browse files Browse the repository at this point in the history
…les to go...
  • Loading branch information
harrand committed Sep 23, 2023
1 parent ab006a3 commit 9c039f8
Show file tree
Hide file tree
Showing 2 changed files with 111 additions and 2 deletions.
85 changes: 84 additions & 1 deletion src/tz/io/ttf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@

namespace tz::io
{
template<std::integral T>
T ttf_read_value(const char* data)
{
return tz::big_endian(*reinterpret_cast<const T*>(data));
}

ttf ttf::from_memory(std::string_view sv)
{
TZ_PROFZONE("ttf - from memory", 0xFFFF2222);
Expand All @@ -28,6 +34,9 @@ namespace tz::io
std::string_view ttf_minus_header = this->parse_header(ttf_data);
this->parse_header(ttf_data);
this->parse_table_info(ttf_minus_header, ttf_data);
// by the time we've parsed tables, we expect all the tables to be sorted.
// let's do a sanity check ensuring that the canary of the head table has been set to true.
tz::assert(this->head.canary, "TTF Head Table canary value was never set to true, this means that a head table was not located. Most likely the TTF is malformed or corrupted.");
}

std::string_view ttf::parse_header(std::string_view str)
Expand Down Expand Up @@ -66,10 +75,16 @@ namespace tz::io
tbl.length = tz::big_endian(*reinterpret_cast<const std::uint32_t*>(ptr));
ptr += sizeof(std::uint32_t);

if(tbl.tag[0] != 'h' || tbl.tag[1] != 'e' || tbl.tag[2] != 'a' || tbl.tag[3] != 'd')
if(std::string{tbl.tag} == "head")
{
this->parse_head_table(full_data, tbl);
}
else
{
std::uint32_t calc = calculate_table_checksum(full_data, tbl.offset, tbl.length);
tz::assert(calc == tbl.checksum);

// parse other table types...
}
}
}
Expand All @@ -83,4 +98,72 @@ namespace tz::io
}
return sum;
}

void ttf::parse_head_table(std::string_view data, ttf_table table_descriptor)
{
tz::assert(data.size() > table_descriptor.offset + table_descriptor.length);
data.remove_prefix(table_descriptor.offset);
data.remove_suffix(data.size() - table_descriptor.length);
tz::assert(data.size() == (table_descriptor.length));

tz::assert(!this->head.canary, "When parsing head table, noticed canary already switched to true. Double head table discovery? Most likely malformed TTF.");

const char* ptr = data.data();

this->head.major_version = ttf_read_value<std::uint16_t>(ptr);
ptr += sizeof(this->head.major_version);

this->head.minor_version = ttf_read_value<std::uint16_t>(ptr);
ptr += sizeof(this->head.minor_version);

this->head.font_revision_fixed_point = ttf_read_value<std::int32_t>(ptr);
// fixed-point conversion: divide by (1 >> 16)
this->head.font_revision_fixed_point /= (1 << 16);
ptr += sizeof(this->head.font_revision_fixed_point);

this->head.checksum_adjustment = ttf_read_value<std::uint32_t>(ptr);
ptr += sizeof(this->head.checksum_adjustment);

this->head.magic = ttf_read_value<std::uint32_t>(ptr);
ptr += sizeof(this->head.magic);

this->head.flags = ttf_read_value<std::uint16_t>(ptr);
ptr += sizeof(this->head.flags);

this->head.units_per_em = ttf_read_value<std::uint16_t>(ptr);
ptr += sizeof(this->head.units_per_em);

// ignore created and modified date for now. aids.
ptr += sizeof(std::uint64_t) * 2;

this->head.xmin = ttf_read_value<std::int16_t>(ptr);
ptr += sizeof(this->head.xmin);

this->head.ymin = ttf_read_value<std::int16_t>(ptr);
ptr += sizeof(this->head.ymin);

this->head.xmax = ttf_read_value<std::int16_t>(ptr);
ptr += sizeof(this->head.xmax);

this->head.ymax = ttf_read_value<std::int16_t>(ptr);
ptr += sizeof(this->head.ymax);

this->head.mac_style = ttf_read_value<std::uint16_t>(ptr);
ptr += sizeof(this->head.mac_style);

this->head.lowest_rec_ppem = ttf_read_value<std::uint16_t>(ptr);
ptr += sizeof(this->head.lowest_rec_ppem);

this->head.font_direction_hint = ttf_read_value<std::int16_t>(ptr);
ptr += sizeof(this->head.font_direction_hint);

this->head.index_to_loc_format = ttf_read_value<std::int16_t>(ptr);
ptr += sizeof(this->head.index_to_loc_format);

this->head.glyph_data_format = ttf_read_value<std::int16_t>(ptr);
ptr += sizeof(this->head.glyph_data_format);

// set canary to true, meaning we did indeed set the head table.
this->head.canary = true;
}
}
28 changes: 27 additions & 1 deletion src/tz/io/ttf.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,35 @@ namespace tz::io

struct ttf_table
{
char tag[4];
char tag[5] = "DEAD";
std::uint32_t checksum = 0u;
std::uint32_t offset = 0u;
std::uint32_t length = 0u;
};

struct ttf_head_table
{
std::uint16_t major_version = 0u;
std::uint16_t minor_version = 0u;
std::int32_t font_revision_fixed_point = 0;
std::uint32_t checksum_adjustment = 0u;
std::uint32_t magic = 0u;
std::uint16_t flags = 0u;
std::uint16_t units_per_em = 0u;
std::uint64_t created_date = 0u;
std::uint64_t modified_date = 0u;
std::int16_t xmin = 0;
std::int16_t ymin = 0;
std::int16_t xmax = std::numeric_limits<std::int16_t>::max();
std::int16_t ymax = std::numeric_limits<std::int16_t>::max();
std::uint16_t mac_style = 0u;
std::uint16_t lowest_rec_ppem = 0u;
std::int16_t font_direction_hint = 0;
std::int16_t index_to_loc_format = 0;
std::int16_t glyph_data_format = 0;
bool canary = false;
};

class ttf
{
public:
Expand All @@ -33,8 +56,11 @@ namespace tz::io
std::string_view parse_header(std::string_view str);
void parse_table_info(std::string_view str, std::string_view full_data);
std::uint32_t calculate_table_checksum(std::string_view data, std::uint32_t offset, std::uint32_t length) const;
void parse_head_table(std::string_view data, ttf_table table_descriptor);
ttf_header header = {};

std::vector<ttf_table> tables = {};
ttf_head_table head = {};
};
}

Expand Down

0 comments on commit 9c039f8

Please sign in to comment.