From ed6d287d1105219b246a2810685097918f974497 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 2 Feb 2022 14:10:16 -0600 Subject: [PATCH] [ot-face] Load num-glyphs from `loca` table before `maxp` Implements [boring-expansion] [maxp] Relax https://github.com/be-fonts/boring-expansion-spec/issues/6 --- src/hb-ot-hmtx-table.hh | 2 +- src/hb-static.cc | 54 ++++++++++++++++++++++++--- test/api/Makefile.am | 1 + test/api/hb-test.h | 7 ++++ test/api/meson.build | 1 + test/api/test-be-num-glyphs.c | 69 +++++++++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 test/api/test-be-num-glyphs.c diff --git a/src/hb-ot-hmtx-table.hh b/src/hb-ot-hmtx-table.hh index 739474f2f..fdf82ee60 100644 --- a/src/hb-ot-hmtx-table.hh +++ b/src/hb-ot-hmtx-table.hh @@ -185,7 +185,7 @@ struct hmtxvmtx table = hb_sanitize_context_t ().reference_table (face, T::tableTag); - /* Cap num_metrics() and num_advances() based on table length. */ + /* Cap num_metrics and num_advances based on table length. */ unsigned int len = table.get_length (); if (unlikely (num_advances * 4 > len)) num_advances = len / 4; diff --git a/src/hb-static.cc b/src/hb-static.cc index ec4b24147..5d0dd07ba 100644 --- a/src/hb-static.cc +++ b/src/hb-static.cc @@ -33,6 +33,7 @@ #include "hb-aat-layout-feat-table.hh" #include "hb-ot-layout-common.hh" #include "hb-ot-cmap-table.hh" +#include "hb-ot-glyf-table.hh" #include "hb-ot-head-table.hh" #include "hb-ot-maxp-table.hh" @@ -55,17 +56,58 @@ const unsigned char _hb_Null_AAT_Lookup[2] = {0xFF, 0xFF}; /* hb_face_t */ +static inline unsigned +load_num_glyphs_from_loca (const hb_face_t *face) +{ + unsigned ret = 0; + hb_sanitize_context_t c = hb_sanitize_context_t (); + c.set_num_glyphs (0); /* So we don't recurse ad infinitum. */ + + /* We cannot use table.head because that would use sanitizer, + * which would try accessing face.num_glyphs, which would + * recurse here again... */ + hb_blob_t *head_blob = c.reference_table (face); + const OT::head *head_table = head_blob->as (); + unsigned indexToLocFormat = head_table->indexToLocFormat; + hb_blob_destroy (head_blob); + + if (indexToLocFormat <= 1) + { + bool short_offset = 0 == indexToLocFormat; + hb_blob_t *loca_blob = c.reference_table (face); + ret = hb_max (1u, loca_blob->length / (short_offset ? 2 : 4)) - 1; + hb_blob_destroy (loca_blob); + } + + return ret; +} + +static inline unsigned +load_num_glyphs_from_maxp (const hb_face_t *face) +{ + unsigned ret = 0; + hb_sanitize_context_t c = hb_sanitize_context_t (); + c.set_num_glyphs (0); /* So we don't recurse ad infinitum. */ + + hb_blob_t *maxp_blob = c.reference_table (face); + const OT::maxp *maxp_table = maxp_blob->as (); + ret = maxp_table->get_num_glyphs (); + hb_blob_destroy (maxp_blob); + + return ret; +} + unsigned int hb_face_t::load_num_glyphs () const { - hb_sanitize_context_t c = hb_sanitize_context_t (); - c.set_num_glyphs (0); /* So we don't recurse ad infinitum. */ - hb_blob_t *maxp_blob = c.reference_table (this); - const OT::maxp *maxp_table = maxp_blob->as (); + unsigned ret = 0; + + ret = load_num_glyphs_from_loca (this); + + if (!ret) + ret = load_num_glyphs_from_maxp (this); - unsigned int ret = maxp_table->get_num_glyphs (); num_glyphs.set_relaxed (ret); - hb_blob_destroy (maxp_blob); return ret; } diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 883464cea..8226cf019 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -30,6 +30,7 @@ noinst_PROGRAMS = $(TEST_PROGS) TEST_PROGS = \ test-aat-layout \ test-baseline \ + test-be-num-glyphs \ test-blob \ test-buffer \ test-c \ diff --git a/test/api/hb-test.h b/test/api/hb-test.h index 7390e57a7..8e3cc1e88 100644 --- a/test/api/hb-test.h +++ b/test/api/hb-test.h @@ -52,6 +52,13 @@ HB_BEGIN_DECLS ((const char *) s)[2], \ ((const char *) s)[3])) +#define HB_FACE_ADD_TABLE(face, tag, data) \ + hb_face_builder_add_table ((face), \ + HB_TAG_CHAR4(tag), \ + hb_blob_create_or_fail ((data), \ + sizeof (data), \ + HB_MEMORY_MODE_READONLY, \ + NULL, NULL)) static inline const char * srcdir (void) diff --git a/test/api/meson.build b/test/api/meson.build index 7ea295420..5a1d4a7ab 100644 --- a/test/api/meson.build +++ b/test/api/meson.build @@ -6,6 +6,7 @@ endif tests = [ 'test-aat-layout.c', 'test-baseline.c', + 'test-be-num-glyphs.c', 'test-blob.c', 'test-buffer.c', 'test-c.c', diff --git a/test/api/test-be-num-glyphs.c b/test/api/test-be-num-glyphs.c new file mode 100644 index 000000000..9ba42eb7f --- /dev/null +++ b/test/api/test-be-num-glyphs.c @@ -0,0 +1,69 @@ +/* + * Copyright © 2022 Behdad Esfahbod + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb-test.h" + +#include + +static void +test_maxp_and_loca (void) +{ + hb_face_t *face; + + const char maxp_data[] = "\x00\x00\x50\x00" // version + "\x00\x05" // numGlyphs + ; + const char loca_data[18] = ""; + + face = hb_face_builder_create (); + g_assert_cmpuint (hb_face_get_glyph_count (face), ==, 0); + hb_face_destroy (face); + + face = hb_face_builder_create (); + HB_FACE_ADD_TABLE (face, "maxp", maxp_data); + g_assert_cmpuint (hb_face_get_glyph_count (face), ==, 5); + hb_face_destroy (face); + + face = hb_face_builder_create (); + HB_FACE_ADD_TABLE (face, "maxp", maxp_data); + HB_FACE_ADD_TABLE (face, "loca", loca_data); + g_assert_cmpuint (hb_face_get_glyph_count (face), ==, 8); + hb_face_destroy (face); + + face = hb_face_builder_create (); + HB_FACE_ADD_TABLE (face, "loca", loca_data); + g_assert_cmpuint (hb_face_get_glyph_count (face), ==, 8); + hb_face_destroy (face); +} + + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_maxp_and_loca); + + return hb_test_run(); +}