diff --git a/src/Makefile.sources b/src/Makefile.sources index ce6501443..40fbd99a6 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -275,6 +275,7 @@ HB_SUBSET_sources = \ hb-subset-input.hh \ hb-subset-plan.cc \ hb-subset-plan.hh \ + hb-subset-repacker.cc \ hb-subset.cc \ hb-subset.hh \ hb-repacker.hh \ @@ -282,6 +283,7 @@ HB_SUBSET_sources = \ HB_SUBSET_headers = \ hb-subset.h \ + hb-subset-repacker.h \ $(NULL) HB_GOBJECT_DIST_sources = hb-gobject-structs.cc diff --git a/src/gen-def.py b/src/gen-def.py index 7b609a97a..205ed7ebd 100755 --- a/src/gen-def.py +++ b/src/gen-def.py @@ -19,7 +19,7 @@ symbols = sorted (re.findall (r"^hb_\w+(?= \()", "\n".join (headers_content), re if '--experimental-api' not in sys.argv: # Move these to harfbuzz-sections.txt when got stable experimental_symbols = \ -"""""".splitlines () +"""hb_subset_repack_or_fail""".splitlines () symbols = [x for x in symbols if x not in experimental_symbols] symbols = "\n".join (symbols) diff --git a/src/hb-repacker.hh b/src/hb-repacker.hh index b1726d8be..2a9e75c45 100644 --- a/src/hb-repacker.hh +++ b/src/hb-repacker.hh @@ -37,7 +37,6 @@ * For a detailed writeup on the overflow resolution algorithm see: * docs/repacker.md */ - struct graph_t { struct vertex_t @@ -140,7 +139,8 @@ struct graph_t * the 'packed' object stack used internally in the * serializer */ - graph_t (const hb_vector_t& objects) + template + graph_t (const T& objects) : parents_invalid (true), distance_invalid (true), positions_invalid (true), @@ -1098,8 +1098,9 @@ struct graph_t hb_vector_t num_roots_for_space_; }; -static bool _try_isolating_subgraphs (const hb_vector_t& overflows, - graph_t& sorted_graph) +static inline +bool _try_isolating_subgraphs (const hb_vector_t& overflows, + graph_t& sorted_graph) { unsigned space = 0; hb_set_t roots_to_isolate; @@ -1147,9 +1148,10 @@ static bool _try_isolating_subgraphs (const hb_vector_t& overflows, - hb_set_t& priority_bumped_parents, - graph_t& sorted_graph) +static inline +bool _process_overflows (const hb_vector_t& overflows, + hb_set_t& priority_bumped_parents, + graph_t& sorted_graph) { bool resolution_attempted = false; @@ -1207,8 +1209,9 @@ static bool _process_overflows (const hb_vector_t& o * For a detailed writeup describing how the algorithm operates see: * docs/repacker.md */ +template inline hb_blob_t* -hb_resolve_overflows (const hb_vector_t& packed, +hb_resolve_overflows (const T& packed, hb_tag_t table_tag, unsigned max_rounds = 20) { // Kahn sort is ~twice as fast as shortest distance sort and works for many fonts diff --git a/src/hb-serialize.hh b/src/hb-serialize.hh index 6615f033c..40895a454 100644 --- a/src/hb-serialize.hh +++ b/src/hb-serialize.hh @@ -36,6 +36,9 @@ #include "hb-map.hh" #include "hb-pool.hh" +#ifdef HB_EXPERIMENTAL_API +#include "hb-subset-repacker.h" +#endif /* * Serialize @@ -70,6 +73,24 @@ struct hb_serialize_context_t virtual_links.fini (); } + object_t () = default; + +#ifdef HB_EXPERIMENTAL_API + object_t (const hb_object_t &o) + { + head = o.head; + tail = o.tail; + next = nullptr; + real_links.alloc (o.num_real_links); + for (unsigned i = 0 ; i < o.num_real_links; i++) + real_links.push (o.real_links[i]); + + virtual_links.alloc (o.num_virtual_links); + for (unsigned i = 0; i < o.num_virtual_links; i++) + virtual_links.push (o.virtual_links[i]); + } +#endif + bool operator == (const object_t &o) const { // Virtual links aren't considered for equality since they don't affect the functionality @@ -95,6 +116,20 @@ struct hb_serialize_context_t unsigned position: 28; unsigned bias; objidx_t objidx; + + link_t () = default; + +#ifdef HB_EXPERIMENTAL_API + link_t (const hb_link_t &o) + { + width = o.width; + is_signed = 0; + whence = 0; + position = o.position; + bias = 0; + objidx = o.objidx; + } +#endif }; char *head; diff --git a/src/hb-subset-repacker.cc b/src/hb-subset-repacker.cc new file mode 100644 index 000000000..2447d296b --- /dev/null +++ b/src/hb-subset-repacker.cc @@ -0,0 +1,49 @@ +/* + * Copyright © 2022 Google, Inc. + * + * 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-repacker.hh" + +#ifdef HB_EXPERIMENTAL_API +/** + * hb_subset_repack_or_fail: + * @hb_objects: raw array of struct hb_object_t, which provides + * object graph info + * @num_hb_objs: number of hb_object_t in the hb_objects array. + * + * Given the input object graph info, repack a table to eliminate + * offset overflows. A nullptr is returned if the repacking attempt fails. + * + * Since: EXPERIMENTAL + **/ +hb_blob_t* hb_subset_repack_or_fail (hb_object_t* hb_objects, unsigned num_hb_objs) +{ + hb_vector_t packed; + packed.alloc (num_hb_objs + 1); + packed.push (nullptr); + for (unsigned i = 0 ; i < num_hb_objs ; i++) + packed.push (&(hb_objects[i])); + return hb_resolve_overflows (packed, HB_OT_TAG_GSUB); +} +#endif + diff --git a/src/hb-subset-repacker.h b/src/hb-subset-repacker.h new file mode 100644 index 000000000..f9a238369 --- /dev/null +++ b/src/hb-subset-repacker.h @@ -0,0 +1,80 @@ +/* + * Copyright © 2022 Google, Inc. + * + * 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. + * + */ + +#ifndef HB_SUBSET_REPACKER_H +#define HB_SUBSET_REPACKER_H + +#include "hb.h" + +HB_BEGIN_DECLS + +#ifdef HB_EXPERIMENTAL_API +/** + * struct hb_link_t + * width: offsetSize in bytes + * position: position of the offset field in bytes + * from beginning of subtable + * objidx: index of subtable + **/ +struct hb_link_t +{ + unsigned width; + unsigned position; + unsigned objidx; +}; + +typedef struct hb_link_t hb_link_t; + +/** + * struct hb_object_t + * head: start of object data + * tail: end of object data + * num_real_links: num of offset field in the object + * real_links: pointer to array of offset info + * num_virtual_links: num of objects that must be packed + * after current object in the final serialized order + * virtual_links: array of virtual link info + **/ +struct hb_object_t +{ + char *head; + char *tail; + unsigned num_real_links; + hb_link_t *real_links; + unsigned num_virtual_links; + hb_link_t *virtual_links; +}; + +typedef struct hb_object_t hb_object_t; + +HB_EXTERN hb_blob_t* +hb_subset_repack_or_fail (hb_object_t* hb_objects, + unsigned num_hb_objs); + +#endif + +HB_END_DECLS + +#endif /* HB_SUBSET_REPACKER_H */ diff --git a/src/meson.build b/src/meson.build index c9d3f1788..e6a5f221a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -276,11 +276,15 @@ hb_subset_sources = files( 'hb-subset-input.hh', 'hb-subset-plan.cc', 'hb-subset-plan.hh', + 'hb-subset-repacker.cc', 'hb-subset.cc', 'hb-subset.hh', ) -hb_subset_headers = files('hb-subset.h') +hb_subset_headers = files( + 'hb-subset.h', + 'hb-subset-repacker.h' +) hb_gobject_sources = files( 'hb-gobject-structs.cc' diff --git a/test/api/Makefile.am b/test/api/Makefile.am index a2412f9f1..ffd4a0fc6 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -78,6 +78,7 @@ TEST_PROGS = \ test-subset-gpos \ test-subset-colr \ test-subset-cbdt \ + test-subset-repacker \ test-unicode \ test-var-coords \ test-version \ @@ -105,6 +106,7 @@ test_subset_cbdt_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_nameids_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_gpos_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_colr_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la +test_subset_repacker_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_unicode_CPPFLAGS = \ $(AM_CPPFLAGS) \ diff --git a/test/api/fonts/repacker_expected.otf b/test/api/fonts/repacker_expected.otf new file mode 100644 index 000000000..d8ed67835 Binary files /dev/null and b/test/api/fonts/repacker_expected.otf differ diff --git a/test/api/meson.build b/test/api/meson.build index ab6433860..f5cdce805 100644 --- a/test/api/meson.build +++ b/test/api/meson.build @@ -32,6 +32,7 @@ tests = [ 'test-ot-tag.c', 'test-ot-extents-cff.c', 'test-ot-metrics-tt-var.c', + 'test-subset-repacker.c', 'test-set.c', 'test-shape.c', 'test-style.c', diff --git a/test/api/test-subset-repacker.c b/test/api/test-subset-repacker.c new file mode 100644 index 000000000..e92683d27 --- /dev/null +++ b/test/api/test-subset-repacker.c @@ -0,0 +1,225 @@ +/* + * Copyright © 2022 Google, Inc. + * + * 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 "hb-subset-test.h" + +#ifdef HB_EXPERIMENTAL_API +#include "hb-subset-repacker.h" + +char test_gsub_data[106] = "\x0\x1\x0\x0\x0\xa\x0\x1e\x0\x2c\x0\x1\x6c\x61\x74\x6e\x0\x8\x0\x4\x0\x0\x0\x0\xff\xff\x0\x1\x0\x0\x0\x1\x74\x65\x73\x74\x0\x8\x0\x0\x0\x1\x0\x1\x0\x2\x0\x2a\x0\x6\x0\x5\x0\x0\x0\x1\x0\x8\x0\x1\x0\x8\x0\x1\x0\xe\x0\x1\x0\x1\x0\x1\x0\x1\x0\x4\x0\x2\x0\x1\x0\x2\x0\x1\x0\x0\x0\x1\x0\x0\x0\x1\x0\x8\x0\x1\x0\x6\x0\x1\x0\x1\x0\x1\x0\x2"; + +static void +test_hb_repack_with_cy_struct (void) +{ + hb_object_t *hb_objs = calloc (15, sizeof (hb_object_t)); + + hb_objs[0].head = &(test_gsub_data[100]); + hb_objs[0].tail = &(test_gsub_data[105]) + 1; + hb_objs[0].num_real_links = 0; + hb_objs[0].num_virtual_links = 0; + hb_objs[0].real_links = NULL; + hb_objs[0].virtual_links = NULL; + + hb_objs[1].head = &(test_gsub_data[94]); + hb_objs[1].tail = &(test_gsub_data[100]); + hb_objs[1].num_real_links = 1; + hb_objs[1].num_virtual_links = 0; + hb_objs[1].real_links = malloc (sizeof (hb_link_t)); + hb_objs[1].real_links[0].width = 2; + hb_objs[1].real_links[0].position = 2; + hb_objs[1].real_links[0].objidx = 1; + hb_objs[1].virtual_links = NULL; + + + hb_objs[2].head = &(test_gsub_data[86]); + hb_objs[2].tail = &(test_gsub_data[94]); + hb_objs[2].num_real_links = 1; + hb_objs[2].num_virtual_links = 0; + hb_objs[2].real_links = malloc (sizeof (hb_link_t)); + hb_objs[2].real_links[0].width = 2; + hb_objs[2].real_links[0].position = 6; + hb_objs[2].real_links[0].objidx = 2; + hb_objs[2].virtual_links = NULL; + + hb_objs[3].head = &(test_gsub_data[76]); + hb_objs[3].tail = &(test_gsub_data[86]); + hb_objs[3].num_real_links = 0; + hb_objs[3].num_virtual_links = 0; + hb_objs[3].real_links = NULL; + hb_objs[3].virtual_links = NULL; + + hb_objs[4].head = &(test_gsub_data[72]); + hb_objs[4].tail = &(test_gsub_data[76]); + hb_objs[4].num_real_links = 1; + hb_objs[4].num_virtual_links = 0; + hb_objs[4].real_links = malloc (sizeof (hb_link_t)); + hb_objs[4].real_links[0].width = 2; + hb_objs[4].real_links[0].position = 2; + hb_objs[4].real_links[0].objidx = 4; + hb_objs[4].virtual_links = NULL; + + hb_objs[5].head = &(test_gsub_data[66]); + hb_objs[5].tail = &(test_gsub_data[72]); + hb_objs[5].num_real_links = 0; + hb_objs[5].num_virtual_links = 0; + hb_objs[5].real_links = NULL; + hb_objs[5].virtual_links = NULL; + + hb_objs[6].head = &(test_gsub_data[58]); + hb_objs[6].tail = &(test_gsub_data[66]); + hb_objs[6].num_real_links = 2; + hb_objs[6].num_virtual_links = 0; + hb_objs[6].real_links = calloc (2, sizeof (hb_link_t)); + hb_objs[6].real_links[0].width = 2; + hb_objs[6].real_links[0].position = 6; + hb_objs[6].real_links[0].objidx = 5; + hb_objs[6].real_links[1].width = 2; + hb_objs[6].real_links[1].position = 2; + hb_objs[6].real_links[1].objidx = 6; + hb_objs[6].virtual_links = NULL; + + hb_objs[7].head = &(test_gsub_data[50]); + hb_objs[7].tail = &(test_gsub_data[58]); + hb_objs[7].num_real_links = 1; + hb_objs[7].num_virtual_links = 0; + hb_objs[7].real_links = malloc (sizeof (hb_link_t)); + hb_objs[7].real_links[0].width = 2; + hb_objs[7].real_links[0].position = 6; + hb_objs[7].real_links[0].objidx = 7; + hb_objs[7].virtual_links = NULL; + + hb_objs[8].head = &(test_gsub_data[44]); + hb_objs[8].tail = &(test_gsub_data[50]); + hb_objs[8].num_real_links = 2; + hb_objs[8].num_virtual_links = 0; + hb_objs[8].real_links = calloc (2, sizeof (hb_link_t)); + hb_objs[8].real_links[0].width = 2; + hb_objs[8].real_links[0].position = 2; + hb_objs[8].real_links[0].objidx = 3; + hb_objs[8].real_links[1].width = 2; + hb_objs[8].real_links[1].position = 4; + hb_objs[8].real_links[1].objidx = 8; + hb_objs[8].virtual_links = NULL; + + hb_objs[9].head = &(test_gsub_data[38]); + hb_objs[9].tail = &(test_gsub_data[44]); + hb_objs[9].num_real_links = 0; + hb_objs[9].num_virtual_links = 0; + hb_objs[9].real_links = NULL; + hb_objs[9].virtual_links = NULL; + + hb_objs[10].head = &(test_gsub_data[30]); + hb_objs[10].tail = &(test_gsub_data[38]); + hb_objs[10].num_real_links = 1; + hb_objs[10].num_virtual_links = 0; + hb_objs[10].real_links = malloc (sizeof (hb_link_t)); + hb_objs[10].real_links[0].width = 2; + hb_objs[10].real_links[0].position = 6; + hb_objs[10].real_links[0].objidx = 10; + hb_objs[10].virtual_links = NULL; + + hb_objs[11].head = &(test_gsub_data[22]); + hb_objs[11].tail = &(test_gsub_data[30]); + hb_objs[11].num_real_links = 0; + hb_objs[11].num_virtual_links = 0; + hb_objs[11].real_links = NULL; + hb_objs[11].virtual_links = NULL; + + hb_objs[12].head = &(test_gsub_data[18]); + hb_objs[12].tail = &(test_gsub_data[22]); + hb_objs[12].num_real_links = 1; + hb_objs[12].num_virtual_links = 0; + hb_objs[12].real_links = malloc (sizeof (hb_link_t)); + hb_objs[12].real_links[0].width = 2; + hb_objs[12].real_links[0].position = 0; + hb_objs[12].real_links[0].objidx = 12; + hb_objs[12].virtual_links = NULL; + + hb_objs[13].head = &(test_gsub_data[10]); + hb_objs[13].tail = &(test_gsub_data[18]); + hb_objs[13].num_real_links = 1; + hb_objs[13].num_virtual_links = 0; + hb_objs[13].real_links = malloc (sizeof (hb_link_t)); + hb_objs[13].real_links[0].width = 2; + hb_objs[13].real_links[0].position = 6; + hb_objs[13].real_links[0].objidx = 13; + hb_objs[13].virtual_links = NULL; + + hb_objs[14].head = &(test_gsub_data[0]); + hb_objs[14].tail = &(test_gsub_data[10]); + hb_objs[14].num_real_links = 3; + hb_objs[14].num_virtual_links = 0; + hb_objs[14].real_links = calloc (3, sizeof (hb_link_t)); + hb_objs[14].real_links[0].width = 2; + hb_objs[14].real_links[0].position = 8; + hb_objs[14].real_links[0].objidx = 9; + hb_objs[14].real_links[1].width = 2; + hb_objs[14].real_links[1].position = 6; + hb_objs[14].real_links[1].objidx = 11; + hb_objs[14].real_links[2].width = 2; + hb_objs[14].real_links[2].position = 4; + hb_objs[14].real_links[2].objidx = 14; + hb_objs[14].virtual_links = NULL; + + hb_blob_t *result = hb_subset_repack_or_fail (hb_objs, 15); + + hb_face_t *face_expected = hb_test_open_font_file ("fonts/repacker_expected.otf"); + hb_blob_t *expected_blob = hb_face_reference_table (face_expected, HB_TAG ('G','S','U','B')); + fprintf(stderr, "expected %d bytes, actual %d bytes\n", hb_blob_get_length(expected_blob), hb_blob_get_length (result)); + + if (hb_blob_get_length (expected_blob) != 0 || + hb_blob_get_length (result) != 0) + hb_test_assert_blobs_equal (expected_blob, result); + + hb_face_destroy (face_expected); + hb_blob_destroy (expected_blob); + hb_blob_destroy (result); + + for (unsigned i = 0 ; i < 15; i++) + { + if (hb_objs[i].real_links != NULL) + free (hb_objs[i].real_links); + } + + free (hb_objs); +} + + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_hb_repack_with_cy_struct); + + return hb_test_run(); +} +#else +int main (int argc HB_UNUSED, char **argv HB_UNUSED) +{ + return 0; +} +#endif