From 72cbfb9059ac4f041cffaef86a1d8067a93b16ec Mon Sep 17 00:00:00 2001 From: ariza Date: Sat, 18 Jan 2020 16:35:52 -0800 Subject: [PATCH] remove empty lookup subtables Added a variant of subset_offset_array which takes an extra arg passed to serialize_subset for this impl. Added a new api test "test-subset-gpos" for this. --- src/hb-ot-layout-common.hh | 70 ++++++++++++++++++--- src/hb-ot-layout-gpos-table.hh | 6 ++ src/hb-ot-layout-gsub-table.hh | 6 ++ test/api/Makefile.am | 2 + test/api/fonts/Roboto-Regular-gpos-.aw.ttf | Bin 0 -> 2368 bytes test/api/fonts/Roboto-Regular-gpos-aw.ttf | Bin 0 -> 2232 bytes test/api/test-subset-gpos.c | 65 +++++++++++++++++++ 7 files changed, 139 insertions(+), 10 deletions(-) create mode 100644 test/api/fonts/Roboto-Regular-gpos-.aw.ttf create mode 100644 test/api/fonts/Roboto-Regular-gpos-aw.ttf create mode 100644 test/api/test-subset-gpos.c 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 0000000000000000000000000000000000000000..74ab2bd98931492a6b74eed932f7f1355bf62741 GIT binary patch literal 2368 zcmZuzdu&rx82`?_x9_rbquZe6al39~k9I3tsWO&PwcB{f#x~@ZD6H%WtgLJu1LI>N z;!=5x7?2>uM~KmA;%U(^9*O(`7!!>kVB{bF@()CfCTfBRt>1U=Hqdyw^L^j%d;IQq zPVYG#FaXF!A%WZ!YG?!x$NQZq{N(`R7~-Q%ZEGKYJ#TkDfXRqB(A-v4 z9bdG&5I{VNxT7-~iNPN6GbE$^r6FiR(BF{ z9C1-!G``a&v3-cUD8GN8Gs3KJ9I=ep9F6RZ34RgzMT8)4i$uGH)8^l?zaJ1k8XFjj zXQF_$#yJ#^4R*)g?Ya8}fIfu%EC&YTnLpVMxC$ni0~{I@Zm4o54Y~ERFbx=JMKP|{wOZD?)~(&56-QP40ScFc|$pOGM#l;vF%^K z?EBBxZNjaC3IN?TsTQozk{}(4F76o&a9DETsf1zbR_*(29BgJPu#e7I3q6k`?Bc2_oY;O?_04P zd&Ace4njV(gHA;NS@bmtMWCO=h4>EL#yrXu+_~mcM~-dGuKN>a(`ycJ@wc2x(l=P^ zA9tPiZA%rF*QHQt>x&$YTOY0%37jZ9;$Izd)V#@Nn&h!eCZ}K@>I!OscX)hbc zjzD)zSA&n9uW9ir^bqYNwN^oWO^>GL<|YV2C5-4H4bWpaCV&j=ElQATbsT}L9ssH` znhcVlGpNOMC}_?>Niou9)T^)?A8I3h=&_X$fsst+ft9(Ic_*{)p*7&jaQIcJank+_pu zY26w;Twm_xMvb9O;Xx;da3Vo`ZHn8)M-vHW0#`7FFZ>%%F!-+{_^<8u@E7NkPulQgUBzs&bOBG{>}<01+n;Hy zc9LZ{H1c$Z%va$ssE{=r5bA*4kPg)4Bq@Ee*H;?R!ftb+Dn-MiMWQsB*A)s+q(9iGw6QCXpdA z-@%5dVwft1sbV;lJ&$Th{7Pc}Jj~lj;vk8yNn}VYorlQ-B&uZ08$f}F0m|V7j6wo- z!gc&5S`F*rRp@}Hz%SInc}T$lxQJQybO+dlH$fI%V8MIUF6{>!o^88+7UZ$_AV+@z zvh+9c2GfRszczTPB@>=NI-Qy=;7wBf dlGHlI{}7-WZ>@*>DpLC@Qu`{J?rWL`;2-%e2VVdH literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1b34d05515c8f20a2021f241204a1863b4d8c479 GIT binary patch literal 2232 zcmZuyeQZ-z6hHU9*YC1*pB*iXSGtXTv}|l;!dRecw*f;o)*-J%VP&6;m6fgID}GEw zBt^brzyu)@B}OCByQZHV!v_nh-P_uSt( z_uhAJfdN1!3JK(fKwUkQfec`{jo#vh=BA+T+ApU8j5fs4hT!_gUd`E;i+C7ubyKjs zGS;(iK7hCeaa(&N9EJCV9}!O=?rYx}lgr*QoI$<{#I0S??#Qk`+up-u#QdoUI5Ag)jjAiU(mgU zu(RkUicK_iQgCr?~V#pq6zT?VtIQw(kYCZf5-g$5ig1M z55`gv=s>KFM+Z8iZ+6}L3_yqZ*eVbplKNTvNm>i}Pz=u`>?NfcnM=9d#^vk4p-$oU za_5NMu{@*#34xS31pomcNC%1FEWLoS*D%nQnJ@9gi zZN&WmRxENAEzrB?7cKS`R8=kZ6}ji@bKJh_>Z;0Idq%cBr?9g6k;QH~Cp%XxwwYh} z@a*S|-T5fCqy4~z!7Dp2-jIrtmJO%f$CKw{a?62_550SFeR!~=Vcl!W#dFED6N>Hd z#+BcHvSAA@8@~w9eJ$0171|QIS!r0DwpNBKV$*9iA}~$^3j)hD5D|#eyP!v)r(FPx zNV}lZlv!03iwd)T77*ydF5pCuN0h2ULCtRc_Jk)`}?Zso4h8~OnxJl zVr_WTa1?T(6?7^Bz^_FBp#bz#n9iT#1b5~s>ksD5>9v2rY$G}VF8`LXkn~N~a`&F| z{!LSbaS)*i3DhRO0v*yui0DP!1LQFDC9OET>Yg*X+Eg*cuZ5BnL6i=#V^|63PU)&} z>*i@%{1Uo|a*9@)K{d^^hNh+l@Ix4O=^+WwWjG~(4D1aGkZN=sfsFig8u%TX43f@3 zLzGkNbI_tO(q_~Fyw7we^r#soM&?usv8q#NQ-{=Obl|B{qgZNGVtA?(->#`HaPTy8 zQ?oL{*pbi}OTEPR&mA#|n>Tqm^T=}D)&OU1UM_ezD|LCf=#lHWSXkepxI=PWj<4#7 z%k^?kxPwcDs>XrNc&J?F5LC9JY*k#`A9Bu^ouN>TmrF#0GcYAv z-^n2qj}u=|al80%JnoF+3a0U;hw&JL|2q8tcHj=wjj`coq(bF(ImzL6xm{RcDB$HL zPh(K2!xCLsqS?cX>%83J;U%c7o{<9 z7P_+092Jz6ONv!XYH111S%%}go~m(^Wqhu? zd8vaeQ)^vyk;-D;+16M+mwc8NJJxD()?0~qdoB59W!tPKJy~hBl?&!%kd^NDmCRM= z+L|BLsselw4Y&a9+!8AsY%sBz#PcM|9W0h;BGFIcD2We9q)5zjupyc;L^FnH#!w2Klv)S7_*D0qV< z6W&X@k(w>wYi5NO=!Y%XW7u1u6S`pt`tX(xV3gWYz4V{;KaFm_5d^&X>FO(hZ=>4s z5YZS((%LIvMO%Ae%uoT#K?>BbX$437o`F7abPsfH14mytwjCV!Jz_&vHGW&?z;?`{ u&15)6e2nr!o*J2tYr`j*=BQ1}`alA{Se4*=C|`k^uRzULFq3bZ0rfvHy!2fF literal 0 HcmV?d00001 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 (); +}