From 93ee01041f2123d5e6b9ffa9547169296aa7a695 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Wed, 7 Feb 2024 20:23:18 -0500 Subject: [PATCH] LibGfx/OpenType: Validate we can read the active cmap subtable format We now reject fonts where the active cmap subtable is in a format we can't read yet, instead of silently drawing squares for all glyphs. This doesn't fire at all for my 1000-file PDF test set, but seems like a good thing to check. (Instead of duplicating the switch, I first tried making a glyph_id_for_code_point_or_else() that returns ErrorOr and then make both glyph_id_for_code_point() and validate_format_can_be_read() call that, but I liked less how that worked out -- felt too clever.) --- .../Libraries/LibGfx/Font/OpenType/Cmap.cpp | 22 +++++++++++++++++++ .../Libraries/LibGfx/Font/OpenType/Cmap.h | 4 ++++ .../Libraries/LibGfx/Font/OpenType/Font.cpp | 1 + 3 files changed, 27 insertions(+) diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Cmap.cpp b/Userland/Libraries/LibGfx/Font/OpenType/Cmap.cpp index e88f33750b..1a0aa3f1f9 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Cmap.cpp +++ b/Userland/Libraries/LibGfx/Font/OpenType/Cmap.cpp @@ -79,9 +79,31 @@ Optional Cmap::subtable(u32 index) const return Subtable(subtable_slice, platform_id, encoding_id); } +ErrorOr Cmap::validate_active_cmap_format() const +{ + auto opt_subtable = subtable(m_active_index); + VERIFY(opt_subtable.has_value()); + return opt_subtable.value().validate_format_can_be_read(); +} + +ErrorOr Cmap::Subtable::validate_format_can_be_read() const +{ + // Keep in sync with switch in glyph_id_for_code_point(). + switch (format()) { + case Format::ByteEncoding: + case Format::SegmentToDelta: + case Format::TrimmedTable: + case Format::SegmentedCoverage: + return {}; + default: + return Error::from_string_view("Unimplemented cmap format"sv); + } +} + // FIXME: Implement the missing formats. u32 Cmap::Subtable::glyph_id_for_code_point(u32 code_point) const { + // Keep in sync with switch in validate_format_can_be_read(). switch (format()) { case Format::ByteEncoding: return glyph_id_for_code_point_table_0(code_point); diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Cmap.h b/Userland/Libraries/LibGfx/Font/OpenType/Cmap.h index f7701916cd..a72506dad2 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Cmap.h +++ b/Userland/Libraries/LibGfx/Font/OpenType/Cmap.h @@ -55,6 +55,9 @@ public: , m_encoding_id(encoding_id) { } + + ErrorOr validate_format_can_be_read() const; + // Returns 0 if glyph not found. This corresponds to the "missing glyph" u32 glyph_id_for_code_point(u32 code_point) const; Optional platform_id() const; @@ -107,6 +110,7 @@ public: u32 num_subtables() const; Optional subtable(u32 index) const; void set_active_index(u32 index) { m_active_index = index; } + ErrorOr validate_active_cmap_format() const; // Returns 0 if glyph not found. This corresponds to the "missing glyph" u32 glyph_id_for_code_point(u32 code_point) const; diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp b/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp index 5821518a59..9ee9ae49af 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp +++ b/Userland/Libraries/LibGfx/Font/OpenType/Font.cpp @@ -264,6 +264,7 @@ ErrorOr> Font::try_load_from_offset(ReadonlyBytes buffer, u3 } if (!active_cmap_index.has_value()) return Error::from_string_literal("No suitable cmap subtable found"); + TRY(cmap.subtable(active_cmap_index.value()).value().validate_format_can_be_read()); cmap.set_active_index(active_cmap_index.value()); return adopt_ref(*new Font(