mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 04:47:34 +00:00
LibTTF: Make TTF::Font loading API return error strings
This commit is contained in:
parent
9321d9d83d
commit
560109bd42
4 changed files with 58 additions and 88 deletions
|
@ -11,6 +11,6 @@
|
||||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||||
{
|
{
|
||||||
ByteBuffer font_data = ByteBuffer::copy(data, size);
|
ByteBuffer font_data = ByteBuffer::copy(data, size);
|
||||||
TTF::Font::load_from_memory(font_data);
|
(void)TTF::Font::try_load_from_memory(font_data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,9 +99,10 @@ FontDatabase::FontDatabase()
|
||||||
}
|
}
|
||||||
} else if (path.ends_with(".ttf"sv)) {
|
} else if (path.ends_with(".ttf"sv)) {
|
||||||
// FIXME: What about .otf and .woff
|
// FIXME: What about .otf and .woff
|
||||||
if (auto font = TTF::Font::load_from_file(path)) {
|
if (auto font_or_error = TTF::Font::try_load_from_file(path); !font_or_error.is_error()) {
|
||||||
|
auto font = font_or_error.release_value();
|
||||||
auto typeface = get_or_create_typeface(font->family(), font->variant());
|
auto typeface = get_or_create_typeface(font->family(), font->variant());
|
||||||
typeface->set_ttf_font(font);
|
typeface->set_ttf_font(move(font));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -205,61 +205,51 @@ GlyphHorizontalMetrics Hmtx::get_glyph_horizontal_metrics(u32 glyph_id) const
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<Font> Font::load_from_file(String path, unsigned index)
|
Result<NonnullRefPtr<Font>, String> Font::try_load_from_file(String path, unsigned index)
|
||||||
{
|
{
|
||||||
auto file_or_error = Core::File::open(move(path), Core::OpenMode::ReadOnly);
|
auto file_or_error = Core::File::open(move(path), Core::OpenMode::ReadOnly);
|
||||||
if (file_or_error.is_error()) {
|
if (file_or_error.is_error())
|
||||||
dbgln("Could not open file: {}", file_or_error.error());
|
return file_or_error.error();
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto file = file_or_error.value();
|
auto file = file_or_error.value();
|
||||||
if (!file->open(Core::OpenMode::ReadOnly)) {
|
if (!file->open(Core::OpenMode::ReadOnly))
|
||||||
dbgln("Could not open file");
|
return String { "Could not open file" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto buffer = file->read_all();
|
auto buffer = file->read_all();
|
||||||
return load_from_memory(buffer, index);
|
return try_load_from_memory(buffer, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<Font> Font::load_from_memory(ByteBuffer& buffer, unsigned index)
|
Result<NonnullRefPtr<Font>, String> Font::try_load_from_memory(ByteBuffer& buffer, unsigned index)
|
||||||
{
|
{
|
||||||
if (buffer.size() < 4) {
|
if (buffer.size() < 4)
|
||||||
dbgln("Font file too small");
|
return String { "Font file too small" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
u32 tag = be_u32(buffer.data());
|
u32 tag = be_u32(buffer.data());
|
||||||
if (tag == tag_from_str("ttcf")) {
|
if (tag == tag_from_str("ttcf")) {
|
||||||
// It's a font collection
|
// It's a font collection
|
||||||
if (buffer.size() < (u32)Sizes::TTCHeaderV1 + sizeof(u32) * (index + 1)) {
|
if (buffer.size() < (u32)Sizes::TTCHeaderV1 + sizeof(u32) * (index + 1))
|
||||||
dbgln("Font file too small");
|
return String { "Font file too small" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
u32 offset = be_u32(buffer.offset_pointer((u32)Sizes::TTCHeaderV1 + sizeof(u32) * index));
|
u32 offset = be_u32(buffer.offset_pointer((u32)Sizes::TTCHeaderV1 + sizeof(u32) * index));
|
||||||
return load_from_offset(move(buffer), offset);
|
return try_load_from_offset(move(buffer), offset);
|
||||||
}
|
}
|
||||||
if (tag == tag_from_str("OTTO")) {
|
if (tag == tag_from_str("OTTO"))
|
||||||
dbgln("CFF fonts not supported yet");
|
return String { "CFF fonts not supported yet" };
|
||||||
return nullptr;
|
|
||||||
}
|
if (tag != 0x00010000)
|
||||||
if (tag != 0x00010000) {
|
return String { "Not a valid font" };
|
||||||
dbgln("Not a valid font");
|
|
||||||
return nullptr;
|
return try_load_from_offset(move(buffer), 0);
|
||||||
}
|
|
||||||
return load_from_offset(move(buffer), 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: "loca" and "glyf" are not available for CFF fonts.
|
// FIXME: "loca" and "glyf" are not available for CFF fonts.
|
||||||
RefPtr<Font> Font::load_from_offset(ByteBuffer&& buffer, u32 offset)
|
Result<NonnullRefPtr<Font>, String> Font::try_load_from_offset(ByteBuffer&& buffer, u32 offset)
|
||||||
{
|
{
|
||||||
if (Checked<u32>::addition_would_overflow(offset, (u32)Sizes::OffsetTable)) {
|
if (Checked<u32>::addition_would_overflow(offset, (u32)Sizes::OffsetTable))
|
||||||
dbgln("Invalid offset in font header");
|
return String { "Invalid offset in font header" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (buffer.size() < offset + (u32)Sizes::OffsetTable) {
|
if (buffer.size() < offset + (u32)Sizes::OffsetTable)
|
||||||
dbgln("Font file too small");
|
return String { "Font file too small" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<ReadonlyBytes> opt_head_slice = {};
|
Optional<ReadonlyBytes> opt_head_slice = {};
|
||||||
Optional<ReadonlyBytes> opt_name_slice = {};
|
Optional<ReadonlyBytes> opt_name_slice = {};
|
||||||
|
@ -279,10 +269,8 @@ RefPtr<Font> Font::load_from_offset(ByteBuffer&& buffer, u32 offset)
|
||||||
Optional<Loca> opt_loca = {};
|
Optional<Loca> opt_loca = {};
|
||||||
|
|
||||||
auto num_tables = be_u16(buffer.offset_pointer(offset + (u32)Offsets::NumTables));
|
auto num_tables = be_u16(buffer.offset_pointer(offset + (u32)Offsets::NumTables));
|
||||||
if (buffer.size() < offset + (u32)Sizes::OffsetTable + num_tables * (u32)Sizes::TableRecord) {
|
if (buffer.size() < offset + (u32)Sizes::OffsetTable + num_tables * (u32)Sizes::TableRecord)
|
||||||
dbgln("Font file too small");
|
return String { "Font file too small" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto i = 0; i < num_tables; i++) {
|
for (auto i = 0; i < num_tables; i++) {
|
||||||
u32 record_offset = offset + (u32)Sizes::OffsetTable + i * (u32)Sizes::TableRecord;
|
u32 record_offset = offset + (u32)Sizes::OffsetTable + i * (u32)Sizes::TableRecord;
|
||||||
|
@ -290,15 +278,12 @@ RefPtr<Font> Font::load_from_offset(ByteBuffer&& buffer, u32 offset)
|
||||||
u32 table_offset = be_u32(buffer.offset_pointer(record_offset + (u32)Offsets::TableRecord_Offset));
|
u32 table_offset = be_u32(buffer.offset_pointer(record_offset + (u32)Offsets::TableRecord_Offset));
|
||||||
u32 table_length = be_u32(buffer.offset_pointer(record_offset + (u32)Offsets::TableRecord_Length));
|
u32 table_length = be_u32(buffer.offset_pointer(record_offset + (u32)Offsets::TableRecord_Length));
|
||||||
|
|
||||||
if (Checked<u32>::addition_would_overflow(table_offset, table_length)) {
|
if (Checked<u32>::addition_would_overflow(table_offset, table_length))
|
||||||
dbgln("Invalid table offset/length in font.");
|
return String { "Invalid table offset/length in font." };
|
||||||
return nullptr;
|
|
||||||
}
|
if (buffer.size() < table_offset + table_length)
|
||||||
|
return String { "Font file too small" };
|
||||||
|
|
||||||
if (buffer.size() < table_offset + table_length) {
|
|
||||||
dbgln("Font file too small");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto buffer_here = ReadonlyBytes(buffer.offset_pointer(table_offset), table_length);
|
auto buffer_here = ReadonlyBytes(buffer.offset_pointer(table_offset), table_length);
|
||||||
|
|
||||||
// Get the table offsets we need.
|
// Get the table offsets we need.
|
||||||
|
@ -321,52 +306,36 @@ RefPtr<Font> Font::load_from_offset(ByteBuffer&& buffer, u32 offset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opt_head_slice.has_value() || !(opt_head = Head::from_slice(opt_head_slice.value())).has_value()) {
|
if (!opt_head_slice.has_value() || !(opt_head = Head::from_slice(opt_head_slice.value())).has_value())
|
||||||
dbgln("Could not load Head");
|
return String { "Could not load Head" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto head = opt_head.value();
|
auto head = opt_head.value();
|
||||||
|
|
||||||
if (!opt_name_slice.has_value() || !(opt_name = Name::from_slice(opt_name_slice.value())).has_value()) {
|
if (!opt_name_slice.has_value() || !(opt_name = Name::from_slice(opt_name_slice.value())).has_value())
|
||||||
dbgln("Could not load Name");
|
return String { "Could not load Name" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto name = opt_name.value();
|
auto name = opt_name.value();
|
||||||
|
|
||||||
if (!opt_hhea_slice.has_value() || !(opt_hhea = Hhea::from_slice(opt_hhea_slice.value())).has_value()) {
|
if (!opt_hhea_slice.has_value() || !(opt_hhea = Hhea::from_slice(opt_hhea_slice.value())).has_value())
|
||||||
dbgln("Could not load Hhea");
|
return String { "Could not load Hhea" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto hhea = opt_hhea.value();
|
auto hhea = opt_hhea.value();
|
||||||
|
|
||||||
if (!opt_maxp_slice.has_value() || !(opt_maxp = Maxp::from_slice(opt_maxp_slice.value())).has_value()) {
|
if (!opt_maxp_slice.has_value() || !(opt_maxp = Maxp::from_slice(opt_maxp_slice.value())).has_value())
|
||||||
dbgln("Could not load Maxp");
|
return String { "Could not load Maxp" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto maxp = opt_maxp.value();
|
auto maxp = opt_maxp.value();
|
||||||
|
|
||||||
if (!opt_hmtx_slice.has_value() || !(opt_hmtx = Hmtx::from_slice(opt_hmtx_slice.value(), maxp.num_glyphs(), hhea.number_of_h_metrics())).has_value()) {
|
if (!opt_hmtx_slice.has_value() || !(opt_hmtx = Hmtx::from_slice(opt_hmtx_slice.value(), maxp.num_glyphs(), hhea.number_of_h_metrics())).has_value())
|
||||||
dbgln("Could not load Hmtx");
|
return String { "Could not load Hmtx" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto hmtx = opt_hmtx.value();
|
auto hmtx = opt_hmtx.value();
|
||||||
|
|
||||||
if (!opt_cmap_slice.has_value() || !(opt_cmap = Cmap::from_slice(opt_cmap_slice.value())).has_value()) {
|
if (!opt_cmap_slice.has_value() || !(opt_cmap = Cmap::from_slice(opt_cmap_slice.value())).has_value())
|
||||||
dbgln("Could not load Cmap");
|
return String { "Could not load Cmap" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto cmap = opt_cmap.value();
|
auto cmap = opt_cmap.value();
|
||||||
|
|
||||||
if (!opt_loca_slice.has_value() || !(opt_loca = Loca::from_slice(opt_loca_slice.value(), maxp.num_glyphs(), head.index_to_loc_format())).has_value()) {
|
if (!opt_loca_slice.has_value() || !(opt_loca = Loca::from_slice(opt_loca_slice.value(), maxp.num_glyphs(), head.index_to_loc_format())).has_value())
|
||||||
dbgln("Could not load Loca");
|
return String { "Could not load Loca" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto loca = opt_loca.value();
|
auto loca = opt_loca.value();
|
||||||
|
|
||||||
if (!opt_glyf_slice.has_value()) {
|
if (!opt_glyf_slice.has_value())
|
||||||
dbgln("Could not load Glyf");
|
return String { "Could not load Glyf" };
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto glyf = Glyf(opt_glyf_slice.value());
|
auto glyf = Glyf(opt_glyf_slice.value());
|
||||||
|
|
||||||
// Select cmap table. FIXME: Do this better. Right now, just looks for platform "Windows"
|
// Select cmap table. FIXME: Do this better. Right now, just looks for platform "Windows"
|
||||||
|
|
|
@ -46,8 +46,8 @@ class Font : public RefCounted<Font> {
|
||||||
AK_MAKE_NONCOPYABLE(Font);
|
AK_MAKE_NONCOPYABLE(Font);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static RefPtr<Font> load_from_file(String path, unsigned index = 0);
|
static Result<NonnullRefPtr<Font>, String> try_load_from_file(String path, unsigned index = 0);
|
||||||
static RefPtr<Font> load_from_memory(ByteBuffer&, unsigned index = 0);
|
static Result<NonnullRefPtr<Font>, String> try_load_from_memory(ByteBuffer&, unsigned index = 0);
|
||||||
|
|
||||||
ScaledFontMetrics metrics(float x_scale, float y_scale) const;
|
ScaledFontMetrics metrics(float x_scale, float y_scale) const;
|
||||||
ScaledGlyphMetrics glyph_metrics(u32 glyph_id, float x_scale, float y_scale) const;
|
ScaledGlyphMetrics glyph_metrics(u32 glyph_id, float x_scale, float y_scale) const;
|
||||||
|
@ -72,7 +72,7 @@ private:
|
||||||
TableRecord = 16,
|
TableRecord = 16,
|
||||||
};
|
};
|
||||||
|
|
||||||
static RefPtr<Font> load_from_offset(ByteBuffer&&, unsigned index = 0);
|
static Result<NonnullRefPtr<Font>, String> try_load_from_offset(ByteBuffer&&, unsigned index = 0);
|
||||||
Font(ByteBuffer&& buffer, Head&& head, Name&& name, Hhea&& hhea, Maxp&& maxp, Hmtx&& hmtx, Cmap&& cmap, Loca&& loca, Glyf&& glyf)
|
Font(ByteBuffer&& buffer, Head&& head, Name&& name, Hhea&& hhea, Maxp&& maxp, Hmtx&& hmtx, Cmap&& cmap, Loca&& loca, Glyf&& glyf)
|
||||||
: m_buffer(move(buffer))
|
: m_buffer(move(buffer))
|
||||||
, m_head(move(head))
|
, m_head(move(head))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue