diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index 93ac7d631..8bed148ec 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh @@ -155,6 +155,43 @@ struct subset_offset_array_t const void *_dest_base; }; + +template +struct subset_offset_array_arg_t +{ + subset_offset_array_arg_t + (hb_subset_context_t *subset_context, + OutputArray& out, + const void *src_base, + const void *dest_base, + Arg &&arg) + : _subset_context(subset_context), _out (out), _src_base (src_base), _dest_base (dest_base), _arg (arg) {} + + template + bool + operator () + (T&& offset) + { + auto *o = _out.serialize_append (_subset_context->serializer); + if (unlikely (!o)) return false; + auto snap = _subset_context->serializer->snapshot (); + bool ret = o->serialize_subset (_subset_context, offset, _src_base, _dest_base, _arg); + if (!ret) + { + _out.pop (); + _subset_context->serializer->revert (snap); + } + return ret; + } + + private: + hb_subset_context_t *_subset_context; + OutputArray &_out; + const void *_src_base; + const void *_dest_base; + Arg &&_arg; +}; + /* * Helper to subset an array of offsets. Subsets the thing pointed to by each offset * and discards the offset in the array if the subset operation results in an empty @@ -172,6 +209,19 @@ struct { return subset_offset_array_t (subset_context, out, src_base, dest_base); } + + /* Variant with one extra argument passed to serialize_subset */ + template + subset_offset_array_arg_t + operator () + (hb_subset_context_t *subset_context, + OutputArray& out, + const void *src_base, + const void *dest_base, + Arg &&arg) const + { + return subset_offset_array_arg_t (subset_context, out, src_base, dest_base, arg); + } } HB_FUNCOBJ (subset_offset_array); @@ -1006,17 +1056,17 @@ struct Lookup bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - auto *out = c->serializer->embed (*this); - if (unlikely (!out)) return_trace (false); + auto *out = c->serializer->start_embed (*this); + if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false); + out->lookupType = lookupType; + out->lookupFlag = lookupFlag; - /* Subset the actual subtables. */ - /* TODO Drop empty ones, either by calling intersects() beforehand, - * or just dropping null offsets after. */ - const OffsetArrayOf& subtables = get_subtables (); - OffsetArrayOf& out_subtables = out->get_subtables (); - unsigned int count = subTable.len; - for (unsigned int i = 0; i < count; i++) - out_subtables[i].serialize_subset (c, subtables[i], this, out, get_type ()); + const hb_set_t *glyphset = c->plan->glyphset (); + unsigned int lookup_type = get_type (); + + hb_iter (get_subtables ()) + | hb_filter ([=] (const OffsetTo &_) { return (this+_).intersects (glyphset, lookup_type); }) + | hb_apply (subset_offset_array (c, out->get_subtables (), this, out, lookup_type)) + ; return_trace (true); } diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh index c469366d3..e9cf0435a 100644 --- a/src/hb-ot-layout-gpos-table.hh +++ b/src/hb-ot-layout-gpos-table.hh @@ -1931,6 +1931,12 @@ struct PosLookupSubTable } } + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } + protected: union { SinglePos single; diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh index 37dc2d9d1..90666d394 100644 --- a/src/hb-ot-layout-gsub-table.hh +++ b/src/hb-ot-layout-gsub-table.hh @@ -1324,6 +1324,12 @@ struct SubstLookupSubTable } } + bool intersects (const hb_set_t *glyphs, unsigned int lookup_type) const + { + hb_intersects_context_t c (glyphs); + return dispatch (&c, lookup_type); + } + protected: union { SingleSubst single; diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 08df2a50d..18a6468bc 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -56,6 +56,7 @@ TEST_PROGS = \ test-subset-hvar \ test-subset-vvar \ test-subset-sbix \ + test-subset-gpos \ test-unicode \ test-version \ test-subset-nameids \ @@ -77,6 +78,7 @@ test_subset_hvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_vvar_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_sbix_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_unicode_CPPFLAGS = \ $(AM_CPPFLAGS) \ diff --git a/test/api/fonts/Roboto-Regular-gpos-.aw.ttf b/test/api/fonts/Roboto-Regular-gpos-.aw.ttf new file mode 100644 index 000000000..74ab2bd98 Binary files /dev/null and b/test/api/fonts/Roboto-Regular-gpos-.aw.ttf differ diff --git a/test/api/fonts/Roboto-Regular-gpos-aw.ttf b/test/api/fonts/Roboto-Regular-gpos-aw.ttf new file mode 100644 index 000000000..1b34d0551 Binary files /dev/null and b/test/api/fonts/Roboto-Regular-gpos-aw.ttf differ diff --git a/test/api/test-subset-gpos.c b/test/api/test-subset-gpos.c new file mode 100644 index 000000000..ac460b395 --- /dev/null +++ b/test/api/test-subset-gpos.c @@ -0,0 +1,65 @@ +/* + * Copyright © 2020 Adobe 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. + * + * Adobe Author(s): Michiharu Ariza + */ + +#include "hb-test.h" +#include "hb-subset-test.h" + +/* Unit tests for GPOS subsetting */ + +static void +test_subset_gpos_lookup_subtable (void) +{ + hb_face_t *face_pwa = hb_test_open_font_file ("fonts/Roboto-Regular-gpos-.aw.ttf"); + hb_face_t *face_wa = hb_test_open_font_file ("fonts/Roboto-Regular-gpos-aw.ttf"); + + hb_set_t *codepoints = hb_set_create (); + hb_face_t *face_pwa_subset; + hb_set_add (codepoints, 'a'); + hb_set_add (codepoints, 'w'); + + hb_subset_input_t *input = hb_subset_test_create_input (codepoints); + + hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG ('G', 'P', 'O', 'S')); + + face_pwa_subset = hb_subset_test_create_subset (face_pwa, input); + hb_set_destroy (codepoints); + + hb_subset_test_check (face_wa, face_pwa_subset, HB_TAG ('G','P','O','S')); + + hb_face_destroy (face_pwa_subset); + hb_face_destroy (face_pwa); + hb_face_destroy (face_wa); +} + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_subset_gpos_lookup_subtable); + + return hb_test_run (); +}