diff --git a/TESTING.md b/TESTING.md index ba9e32f2c..4efc64ca5 100644 --- a/TESTING.md +++ b/TESTING.md @@ -24,7 +24,14 @@ make distcheck ### Run tests with asan +**NOTE**: this sometimes yields harder to read results than the full fuzzer + ```shell +# For nice symbols tell asan how to symoblize. Note that it doesn't like versioned copies like llvm-symbolizer-3.8 +# export ASAN_SYMBOLIZER_PATH=path to version-less llvm-symbolizer +# ex +export ASAN_SYMBOLIZER_PATH=/usr/lib/llvm-3.8/bin/llvm-symbolizer + ./configure CC=clang CXX=clang++ CPPFLAGS=-fsanitize=address LDFLAGS=-fsanitize=address # make/run tests as usual ``` @@ -58,7 +65,11 @@ cmake -DHB_CHECK=ON -Bbuild -H. -GNinja && ninja -Cbuild && CTEST_OUTPUT_ON_FAIL # push your changs to a branch on googlefonts/harfbuzz # In a local copy of oss-fuzz, edit projects/harfbuzz/Dockerfile # Change the git clone to pull your branch + +# Do this periodically sudo python infra/helper.py build_image harfbuzz + +# Do these to update/run sudo python infra/helper.py build_fuzzers --sanitizer address harfbuzz sudo python infra/helper.py run_fuzzer harfbuzz hb-subset-fuzzer ``` diff --git a/src/gen-ucd.py b/src/gen-ucd.py index d97085e69..1160cb58c 100755 --- a/src/gen-ucd.py +++ b/src/gen-ucd.py @@ -2,7 +2,7 @@ from __future__ import print_function, division, absolute_import -import io, os.path, sys +import io, os.path, sys, re if len (sys.argv) != 2: print ("usage: ./gen-ucd ucdxml-file", file=sys.stderr) @@ -29,18 +29,68 @@ dm1 = set(v[0] for i,v in dm.items() if len(v) == 1) dmx = set(v for v in dm.values() if len(v) not in (1,2)) assert not dmx -print(len(sorted(gc_set))) -print(len(sorted(gc_ccc_non0))) -print(len(sorted(gc_bmg_non0))) -print("GC, CCC, and BMG fit in one byte. Compress together.") -print() +#print(len(sorted(gc_set))) +#print(len(sorted(gc_ccc_non0))) +#print(len(sorted(gc_bmg_non0))) +#print("GC, CCC, and BMG fit in one byte. Compress together.") +#print() -print(len(sorted(sc_set))) -print("SC fits in one byte. Compress separately.") -print() +#print(len(sorted(sc_set))) +#print("SC fits in one byte. Compress separately.") +#print() -print(len(dm)) -print(len(dm1), min(dm1), max(dm1)) -print(len(dm2)) -#print(sorted(dm2diff)) -print(len(sorted(set(v // 512 for v in dm1)))) +#print(len(dm)) +#print(len(dm1), min(dm1), max(dm1)) +#print(len(dm2)) +##print(sorted(dm2diff)) +#print(len(sorted(set(v // 512 for v in dm1)))) + +gc_order = packTab.AutoMapping() +for _ in ('Cc', 'Cf', 'Cn', 'Co', 'Cs', 'Ll', 'Lm', 'Lo', 'Lt', 'Lu', + 'Mc', 'Me', 'Mn', 'Nd', 'Nl', 'No', 'Pc', 'Pd', 'Pe', 'Pf', + 'Pi', 'Po', 'Ps', 'Sc', 'Sk', 'Sm', 'So', 'Zl', 'Zp', 'Zs',): + gc_order[_] + +sc_order = packTab.AutoMapping() +sc_array = [] +sc_re = re.compile(" (HB_SCRIPT_[_A-Z]*).*HB_TAG [(]'(.)','(.)','(.)','(.)'[)]") +for line in open('hb-common.h'): + m = sc_re.search (line) + if not m: continue + name = m.group(1) + tag = ''.join(m.group(i) for i in range(2, 6)) + i = sc_order[tag] + assert i == len(sc_array) + sc_array.append(name) + +DEFAULT = 1 +COMPACT = 3 +print() +print('#include ') +print() +code = packTab.Code('_hb_ucd') +sc_array, _, _ = code.addArray('hb_script_t', 'sc_map', sc_array) +code.print_c(linkage='static inline') + +for compression in (DEFAULT, COMPACT): + print() + if compression == DEFAULT: + print('#ifdef HB_OPTIMIZE_SIZE') + else: + print('#else') + print() + + code = packTab.Code('_hb_ucd') + + packTab.pack_table(gc, 'Cn', mapping=gc_order, compression=compression).genCode(code, 'gc') + packTab.pack_table(ccc, 0, compression=compression).genCode(code, 'ccc') + packTab.pack_table(bmg, 0, compression=compression).genCode(code, 'bmg') + packTab.pack_table(sc, 'Zzzz', mapping=sc_order, compression=compression).genCode(code, 'sc') + + code.print_c(linkage='static inline') + + + if compression != DEFAULT: + print() + print('#endif') + print() diff --git a/src/hb-blob.cc b/src/hb-blob.cc index 699f66b1a..5783fb294 100644 --- a/src/hb-blob.cc +++ b/src/hb-blob.cc @@ -30,7 +30,7 @@ * http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html * https://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */ -#if !defined(_POSIX_C_SOURCE) && !defined(_MSC_VER) +#if !defined(_POSIX_C_SOURCE) && !defined(_MSC_VER) && !defined(__NetBSD__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-macros" #define _POSIX_C_SOURCE 200809L diff --git a/src/hb-config.hh b/src/hb-config.hh index caff9d83e..fd5a71341 100644 --- a/src/hb-config.hh +++ b/src/hb-config.hh @@ -43,6 +43,9 @@ #ifndef NDEBUG #define NDEBUG #endif +#ifndef __OPTIMIZE_SIZE__ +#define __OPTIMIZE_SIZE__ +#endif #endif #ifdef HB_LEAN @@ -113,6 +116,11 @@ #endif #endif +#ifdef __OPTIMIZE_SIZE__ +#ifndef HB_OPTIMIZE_SIZE +#define HB_OPTIMIZE_SIZE +#endif +#endif #ifdef HAVE_CONFIG_OVERRIDE_H #include "config-override.h" diff --git a/src/hb-ot-cff-common.hh b/src/hb-ot-cff-common.hh index ef72feddd..ddec517a1 100644 --- a/src/hb-ot-cff-common.hh +++ b/src/hb-ot-cff-common.hh @@ -132,7 +132,7 @@ struct CFFIndex if (unlikely (!c->extend_min (*this))) return_trace (false); this->count = byteArray.length; this->offSize = offSize_; - if (!unlikely (c->allocate_size (offSize_ * (byteArray.length + 1)))) + if (unlikely (!c->allocate_size (offSize_ * (byteArray.length + 1)))) return_trace (false); /* serialize indices */ @@ -277,7 +277,7 @@ struct CFFIndexOf : CFFIndex if (unlikely (!c->extend_min (*this))) return_trace (false); this->count = dataArrayLen; this->offSize = offSize_; - if (!unlikely (c->allocate_size (offSize_ * (dataArrayLen + 1)))) + if (unlikely (!c->allocate_size (offSize_ * (dataArrayLen + 1)))) return_trace (false); /* serialize indices */ @@ -479,7 +479,7 @@ struct FDArray : CFFIndexOf if (unlikely (!c->extend_min (*this))) return_trace (false); this->count = fontDicts.length; this->offSize = offSize_; - if (!unlikely (c->allocate_size (offSize_ * (fontDicts.length + 1)))) + if (unlikely (!c->allocate_size (offSize_ * (fontDicts.length + 1)))) return_trace (false); /* serialize font dict offsets */ @@ -516,7 +516,7 @@ struct FDArray : CFFIndexOf if (unlikely (!c->extend_min (*this))) return_trace (false); this->count = fdCount; this->offSize = offSize_; - if (!unlikely (c->allocate_size (offSize_ * (fdCount + 1)))) + if (unlikely (!c->allocate_size (offSize_ * (fdCount + 1)))) return_trace (false); /* serialize font dict offsets */ diff --git a/src/hb-ot-cff1-table.hh b/src/hb-ot-cff1-table.hh index 8aac5486c..3a887d3d0 100644 --- a/src/hb-ot-cff1-table.hh +++ b/src/hb-ot-cff1-table.hh @@ -576,7 +576,7 @@ struct CFF1StringIndex : CFF1Index TRACE_SERIALIZE (this); if (unlikely ((strings.count == 0) || (sidmap.get_count () == 0))) { - if (!unlikely (c->extend_min (this->count))) + if (unlikely (!c->extend_min (this->count))) return_trace (false); count = 0; return_trace (true); diff --git a/src/hb-ot-stat-table.hh b/src/hb-ot-stat-table.hh index 8f9ff5ab2..f12b51ce3 100644 --- a/src/hb-ot-stat-table.hh +++ b/src/hb-ot-stat-table.hh @@ -186,7 +186,7 @@ struct AxisValue bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (unlikely (c->check_struct (this))) + if (unlikely (!c->check_struct (this))) return_trace (false); switch (u.format) diff --git a/src/hb-set.hh b/src/hb-set.hh index 3b30fa4d4..ad449d0ce 100644 --- a/src/hb-set.hh +++ b/src/hb-set.hh @@ -28,6 +28,7 @@ #define HB_SET_HH #include "hb.hh" +#include "hb-machinery.hh" /* @@ -308,7 +309,7 @@ struct hb_set_t { page->add (g); - array = (const T *) ((const char *) array + stride); + array = &StructAtOffsetUnaligned (array, stride); count--; } while (count && (g = *array, start <= g && g < end)); diff --git a/src/hb-subset-input.cc b/src/hb-subset-input.cc index b3b27d427..8bd6058d0 100644 --- a/src/hb-subset-input.cc +++ b/src/hb-subset-input.cc @@ -45,11 +45,39 @@ hb_subset_input_create_or_fail () input->unicodes = hb_set_create (); input->glyphs = hb_set_create (); input->name_ids = hb_set_create (); + input->drop_tables = hb_set_create (); input->drop_hints = false; - input->drop_layout = true; input->desubroutinize = false; input->retain_gids = false; + hb_tag_t default_drop_tables[] = { + // Layout disabled by default + HB_TAG ('G', 'S', 'U', 'B'), + HB_TAG ('G', 'P', 'O', 'S'), + HB_TAG ('G', 'D', 'E', 'F'), + + // Copied from fontTools: + HB_TAG ('B', 'A', 'S', 'E'), + HB_TAG ('J', 'S', 'T', 'F'), + HB_TAG ('D', 'S', 'I', 'G'), + HB_TAG ('E', 'B', 'D', 'T'), + HB_TAG ('E', 'B', 'L', 'C'), + HB_TAG ('E', 'B', 'S', 'C'), + HB_TAG ('S', 'V', 'G', ' '), + HB_TAG ('P', 'C', 'L', 'T'), + HB_TAG ('L', 'T', 'S', 'H'), + // Graphite tables + HB_TAG ('F', 'e', 'a', 't'), + HB_TAG ('G', 'l', 'a', 't'), + HB_TAG ('G', 'l', 'o', 'c'), + HB_TAG ('S', 'i', 'l', 'f'), + HB_TAG ('S', 'i', 'l', 'l'), + // Colour + HB_TAG ('s', 'b', 'i', 'x') + }; + + input->drop_tables->add_array (default_drop_tables, ARRAY_LENGTH (default_drop_tables)); + return input; } @@ -83,6 +111,7 @@ hb_subset_input_destroy (hb_subset_input_t *subset_input) hb_set_destroy (subset_input->unicodes); hb_set_destroy (subset_input->glyphs); hb_set_destroy (subset_input->name_ids); + hb_set_destroy (subset_input->drop_tables); free (subset_input); } @@ -117,6 +146,12 @@ hb_subset_input_nameid_set (hb_subset_input_t *subset_input) return subset_input->name_ids; } +HB_EXTERN hb_set_t * +hb_subset_input_drop_tables_set (hb_subset_input_t *subset_input) +{ + return subset_input->drop_tables; +} + HB_EXTERN void hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input, hb_bool_t drop_hints) @@ -130,19 +165,6 @@ hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input) return subset_input->drop_hints; } -HB_EXTERN void -hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input, - hb_bool_t drop_layout) -{ - subset_input->drop_layout = drop_layout; -} - -HB_EXTERN hb_bool_t -hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input) -{ - return subset_input->drop_layout; -} - HB_EXTERN void hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input, hb_bool_t desubroutinize) diff --git a/src/hb-subset-input.hh b/src/hb-subset-input.hh index d01fecee0..f6dd4ac31 100644 --- a/src/hb-subset-input.hh +++ b/src/hb-subset-input.hh @@ -41,11 +41,11 @@ struct hb_subset_input_t hb_set_t *unicodes; hb_set_t *glyphs; hb_set_t *name_ids; + hb_set_t *drop_tables; - bool drop_hints : 1; - bool drop_layout : 1; - bool desubroutinize : 1; - bool retain_gids : 1; + bool drop_hints; + bool desubroutinize; + bool retain_gids; /* TODO * * features diff --git a/src/hb-subset-plan.cc b/src/hb-subset-plan.cc index 2e7b71b8e..5ea51c4ab 100644 --- a/src/hb-subset-plan.cc +++ b/src/hb-subset-plan.cc @@ -236,11 +236,12 @@ hb_subset_plan_create (hb_face_t *face, hb_subset_plan_t *plan = hb_object_create (); plan->drop_hints = input->drop_hints; - plan->drop_layout = input->drop_layout; plan->desubroutinize = input->desubroutinize; plan->retain_gids = input->retain_gids; plan->unicodes = hb_set_create (); plan->name_ids = hb_set_reference (input->name_ids); + plan->drop_tables = hb_set_reference (input->drop_tables); + /* TODO Clean this up... */ if (hb_set_is_empty (plan->name_ids)) hb_set_add_range (plan->name_ids, 0, 0x7FFF); @@ -253,7 +254,7 @@ hb_subset_plan_create (hb_face_t *face, plan->_glyphset = _populate_gids_to_retain (face, input->unicodes, input->glyphs, - !plan->drop_layout, + !input->drop_tables->has (HB_OT_TAG_GSUB), plan->unicodes, plan->codepoint_to_glyph); @@ -279,6 +280,7 @@ hb_subset_plan_destroy (hb_subset_plan_t *plan) hb_set_destroy (plan->unicodes); hb_set_destroy (plan->name_ids); + hb_set_destroy (plan->drop_tables); hb_face_destroy (plan->source); hb_face_destroy (plan->dest); hb_map_destroy (plan->codepoint_to_glyph); diff --git a/src/hb-subset-plan.hh b/src/hb-subset-plan.hh index bdc710101..9b34d7a60 100644 --- a/src/hb-subset-plan.hh +++ b/src/hb-subset-plan.hh @@ -40,16 +40,18 @@ struct hb_subset_plan_t hb_object_header_t header; bool drop_hints : 1; - bool drop_layout : 1; bool desubroutinize : 1; bool retain_gids : 1; // For each cp that we'd like to retain maps to the corresponding gid. hb_set_t *unicodes; - //name_ids we would like to retain + // name_ids we would like to retain hb_set_t *name_ids; + // Tables which should be dropped. + hb_set_t *drop_tables; + // The glyph subset hb_map_t *codepoint_to_glyph; diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 9e48005b2..bfe00bebf 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -236,6 +236,9 @@ _subset_table (hb_subset_plan_t *plan, static bool _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) { + if (plan->drop_tables->has (tag)) + return true; + switch (tag) { case HB_TAG ('c', 'v', 'a', 'r'): /* hint table, fallthrough */ case HB_TAG ('c', 'v', 't', ' '): /* hint table, fallthrough */ @@ -245,35 +248,14 @@ _should_drop_table (hb_subset_plan_t *plan, hb_tag_t tag) case HB_TAG ('V', 'D', 'M', 'X'): /* hint table, fallthrough */ return plan->drop_hints; +#ifdef HB_NO_SUBSET_LAYOUT // Drop Layout Tables if requested. case HB_OT_TAG_GDEF: case HB_OT_TAG_GPOS: case HB_OT_TAG_GSUB: -#ifdef HB_NO_SUBSET_LAYOUT return true; #endif - return plan->drop_layout; - // Drop these tables below by default, list pulled - // from fontTools: - case HB_TAG ('B', 'A', 'S', 'E'): - case HB_TAG ('J', 'S', 'T', 'F'): - case HB_TAG ('D', 'S', 'I', 'G'): - case HB_TAG ('E', 'B', 'D', 'T'): - case HB_TAG ('E', 'B', 'L', 'C'): - case HB_TAG ('E', 'B', 'S', 'C'): - case HB_TAG ('S', 'V', 'G', ' '): - case HB_TAG ('P', 'C', 'L', 'T'): - case HB_TAG ('L', 'T', 'S', 'H'): - // Graphite tables: - case HB_TAG ('F', 'e', 'a', 't'): - case HB_TAG ('G', 'l', 'a', 't'): - case HB_TAG ('G', 'l', 'o', 'c'): - case HB_TAG ('S', 'i', 'l', 'f'): - case HB_TAG ('S', 'i', 'l', 'l'): - // Colour - case HB_TAG ('s', 'b', 'i', 'x'): - return true; default: return false; } diff --git a/src/hb-subset.h b/src/hb-subset.h index 50345061c..c778896a2 100644 --- a/src/hb-subset.h +++ b/src/hb-subset.h @@ -57,18 +57,15 @@ hb_subset_input_glyph_set (hb_subset_input_t *subset_input); HB_EXTERN hb_set_t * hb_subset_input_nameid_set (hb_subset_input_t *subset_input); +HB_EXTERN hb_set_t * +hb_subset_input_drop_tables_set (hb_subset_input_t *subset_input); + HB_EXTERN void hb_subset_input_set_drop_hints (hb_subset_input_t *subset_input, hb_bool_t drop_hints); HB_EXTERN hb_bool_t hb_subset_input_get_drop_hints (hb_subset_input_t *subset_input); -HB_EXTERN void -hb_subset_input_set_drop_layout (hb_subset_input_t *subset_input, - hb_bool_t drop_layout); -HB_EXTERN hb_bool_t -hb_subset_input_get_drop_layout (hb_subset_input_t *subset_input); - HB_EXTERN void hb_subset_input_set_desubroutinize (hb_subset_input_t *subset_input, hb_bool_t desubroutinize); diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 9d7b3198c..b9d4d7928 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -42,6 +42,7 @@ TEST_PROGS = \ test-shape \ test-subset \ test-subset-cmap \ + test-subset-drop-tables \ test-subset-glyf \ test-subset-hdmx \ test-subset-hmtx \ @@ -57,6 +58,7 @@ TEST_PROGS = \ test_subset_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_cmap_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la +test_subset_drop_tables_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_glyf_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_hdmx_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la test_subset_hmtx_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la diff --git a/test/api/test-subset-drop-tables.c b/test/api/test-subset-drop-tables.c new file mode 100644 index 000000000..e23408008 --- /dev/null +++ b/test/api/test-subset-drop-tables.c @@ -0,0 +1,71 @@ +/* + * Copyright © 2019 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. + * + * Google Author(s): Garret Rieger + */ + +#include "hb-test.h" +#include "hb-subset-test.h" + +/* Unit tests for hb-subset.cc drop tables functionality */ + +static void +test_subset_drop_tables (void) +{ + hb_face_t *face = hb_test_open_font_file ("fonts/Roboto-Regular.abc.ttf"); + + hb_set_t *codepoints = hb_set_create(); + hb_set_add (codepoints, 97); + hb_set_add (codepoints, 99); + hb_subset_input_t *input = hb_subset_test_create_input (codepoints); + hb_set_add (hb_subset_input_drop_tables_set (input), HB_TAG ('h', 'd', 'm', 'x')); + hb_set_add (hb_subset_input_drop_tables_set (input), HB_TAG ('h', 'm', 't', 'x')); + hb_set_destroy (codepoints); + + hb_face_t* subset = hb_subset (face, input); + + hb_blob_t *hdmx = hb_face_reference_table (subset, HB_TAG ('h', 'd', 'm', 'x')); + hb_blob_t *hmtx = hb_face_reference_table (subset, HB_TAG ('h', 'm', 't', 'x')); + hb_blob_t *cmap = hb_face_reference_table (subset, HB_TAG ('c', 'm', 'a', 'p')); + g_assert (!hb_blob_get_length (hdmx)); + g_assert (!hb_blob_get_length (hmtx)); + g_assert ( hb_blob_get_length (cmap)); + hb_blob_destroy (hdmx); + hb_blob_destroy (hmtx); + hb_blob_destroy (cmap); + + hb_face_destroy (subset); + hb_subset_input_destroy (input); + hb_face_destroy (face); +} + + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_subset_drop_tables); + + return hb_test_run(); +} diff --git a/test/api/test-subset-glyf.c b/test/api/test-subset-glyf.c index af71f27e3..2b330ced2 100644 --- a/test/api/test-subset-glyf.c +++ b/test/api/test-subset-glyf.c @@ -137,7 +137,9 @@ test_subset_glyf_with_gsub (void) input = hb_subset_test_create_input (codepoints); hb_set_destroy (codepoints); - hb_subset_input_set_drop_layout (input, false); + hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'S', 'U', 'B')); + hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'P', 'O', 'S')); + hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'D', 'E', 'F')); face_subset = hb_subset_test_create_subset (face_fil, input); @@ -164,7 +166,9 @@ test_subset_glyf_without_gsub (void) input = hb_subset_test_create_input (codepoints); hb_set_destroy (codepoints); - hb_subset_input_set_drop_layout (input, true); + hb_set_add (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'S', 'U', 'B')); + hb_set_add (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'P', 'O', 'S')); + hb_set_add (hb_subset_input_drop_tables_set (input), HB_TAG('G', 'D', 'E', 'F')); face_subset = hb_subset_test_create_subset (face_fil, input); diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5634197349203968 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5634197349203968 new file mode 100644 index 000000000..39e8bd942 Binary files /dev/null and b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5634197349203968 differ diff --git a/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5696825891225600 b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5696825891225600 new file mode 100644 index 000000000..30515a4c3 Binary files /dev/null and b/test/fuzzing/fonts/clusterfuzz-testcase-minimized-hb-subset-fuzzer-5696825891225600 differ diff --git a/test/fuzzing/hb-subset-fuzzer.cc b/test/fuzzing/hb-subset-fuzzer.cc index 56ffd2271..42ef0071e 100644 --- a/test/fuzzing/hb-subset-fuzzer.cc +++ b/test/fuzzing/hb-subset-fuzzer.cc @@ -16,10 +16,16 @@ trySubset (hb_face_t *face, { hb_subset_input_t *input = hb_subset_input_create_or_fail (); hb_subset_input_set_drop_hints (input, drop_hints); - hb_subset_input_set_drop_layout (input, drop_layout); hb_subset_input_set_retain_gids (input, retain_gids); hb_set_t *codepoints = hb_subset_input_unicode_set (input); + if (!drop_layout) + { + hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG ('G', 'S', 'U', 'B')); + hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG ('G', 'P', 'O', 'S')); + hb_set_del (hb_subset_input_drop_tables_set (input), HB_TAG ('G', 'D', 'E', 'F')); + } + for (int i = 0; i < text_length; i++) { hb_set_add (codepoints, text[i]); diff --git a/util/Makefile.am b/util/Makefile.am index e24a6f3dc..5298e7732 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -50,8 +50,9 @@ bin_PROGRAMS += hb-shape hb_subset_SOURCES = $(HB_SUBSET_CLI_sources) hb_subset_LDADD = \ - $(LDADD) \ - $(top_builddir)/src/libharfbuzz-subset.la + $(top_builddir)/src/libharfbuzz-subset.la \ + $(LDADD) + bin_PROGRAMS += hb-subset hb_ot_shape_closure_SOURCES = $(HB_OT_SHAPE_CLOSURE_sources) diff --git a/util/Makefile.sources b/util/Makefile.sources index 6c815d26b..bcf85f562 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -32,6 +32,7 @@ HB_OT_SHAPE_CLOSURE_sources = \ HB_SUBSET_CLI_sources = \ hb-subset.cc \ options.cc \ + options-subset.cc \ options.hh \ main-font-text.hh \ $(NULL) diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 682ca4c34..4b7af8e9a 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -43,7 +43,7 @@ struct subset_consumer_t const font_options_t *font_opts) { font = hb_font_reference (font_opts->get_font ()); - input = hb_subset_input_create_or_fail (); + input = hb_subset_input_reference (subset_options.input); } void consume_line (const char *text, @@ -89,12 +89,6 @@ struct subset_consumer_t void finish (const font_options_t *font_opts) { - hb_subset_input_set_drop_layout (input, !subset_options.keep_layout); - hb_subset_input_set_drop_hints (input, subset_options.drop_hints); - hb_subset_input_set_retain_gids (input, subset_options.retain_gids); - hb_subset_input_set_desubroutinize (input, subset_options.desubroutinize); - hb_set_set (hb_subset_input_nameid_set (input), subset_options.name_ids); - hb_face_t *face = hb_font_get_face (font); hb_face_t *new_face = hb_subset (face, input); diff --git a/util/options-subset.cc b/util/options-subset.cc new file mode 100644 index 000000000..8ce194604 --- /dev/null +++ b/util/options-subset.cc @@ -0,0 +1,127 @@ +/* + * Copyright © 2019 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. + * + * Google Author(s): Garret Rieger + */ + +#include "options.hh" + +#include "hb-subset-input.hh" + +static gboolean +parse_nameids (const char *name G_GNUC_UNUSED, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + subset_options_t *subset_opts = (subset_options_t *) data; + hb_set_t *name_ids = subset_opts->input->name_ids; + + hb_set_clear (name_ids); + + char *s = (char *) arg; + char *p; + + while (s && *s) + { + while (*s && strchr (", ", *s)) + s++; + if (!*s) + break; + + errno = 0; + hb_codepoint_t u = strtoul (s, &p, 10); + if (errno || s == p) + { + hb_set_destroy (name_ids); + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing nameID values at: '%s'", s); + return false; + } + + hb_set_add (name_ids, u); + + s = p; + } + + return true; +} + +static gboolean +parse_drop_tables (const char *name, + const char *arg, + gpointer data, + GError **error G_GNUC_UNUSED) +{ + subset_options_t *subset_opts = (subset_options_t *) data; + hb_set_t *drop_tables = subset_opts->input->drop_tables; + + char last_name_char = name[strlen (name) - 1]; + + if (last_name_char != '+' && last_name_char != '-') + hb_set_clear (drop_tables); + + char *s = strtok((char *) arg, ", "); + while (s) + { + if (strlen (s) > 4) // Table tags are at most 4 bytes. + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Failed parsing table tag values at: '%s'", s); + return false; + } + + hb_tag_t tag = hb_tag_from_string (s, strlen (s)); + + if (last_name_char != '-') + hb_set_add (drop_tables, tag); + else + hb_set_del (drop_tables, tag); + + s = strtok(NULL, ", "); + } + + return true; +} + +void +subset_options_t::add_options (option_parser_t *parser) +{ + GOptionEntry entries[] = + { + {"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->input->drop_hints, "Whether to drop hints", nullptr}, + {"retain-gids", 0, 0, G_OPTION_ARG_NONE, &this->input->retain_gids, "If set don't renumber glyph ids in the subset.", nullptr}, + {"desubroutinize", 0, 0, G_OPTION_ARG_NONE, &this->input->desubroutinize, "Remove CFF/CFF2 use of subroutines", nullptr}, + {"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers"}, + {"drop-tables", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."}, + {"drop-tables+", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."}, + {"drop-tables-", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_drop_tables, "Drop the specified tables.", "list of string table tags."}, + + {nullptr} + }; + parser->add_group (entries, + "subset", + "Subset options:", + "Options subsetting", + this); +} diff --git a/util/options.cc b/util/options.cc index a9b3fc77e..4e6ca57e7 100644 --- a/util/options.cc +++ b/util/options.cc @@ -31,6 +31,8 @@ #endif #include +#define DELIMITERS "<+>{},;&#\\xXuUnNiI\n\t\v\f\r " + static struct supported_font_funcs_t { char name[4]; void (*func) (hb_font_t *); @@ -352,7 +354,7 @@ parse_unicodes (const char *name G_GNUC_UNUSED, while (s && *s) { - while (*s && strchr ("<+>{},;&#\\xXuUnNiI\n\t\v\f\r ", *s)) + while (*s && strchr (DELIMITERS, *s)) s++; if (!*s) break; @@ -970,67 +972,3 @@ format_options_t::serialize_buffer_of_glyphs (hb_buffer_t *buffer, serialize_glyphs (buffer, font, output_format, format_flags, gs); g_string_append_c (gs, '\n'); } - -static gboolean -parse_nameids (const char *name G_GNUC_UNUSED, - const char *arg, - gpointer data, - GError **error G_GNUC_UNUSED) -{ - subset_options_t *subset_opts = (subset_options_t *) data; - - hb_set_t *name_ids = hb_set_create (); - char *s = (char *) arg; - char *p; - - while (s && *s) - { - while (*s && strchr ("<+>{},;&#\\xXuUnNiI\n\t\v\f\r ", *s)) - s++; - if (!*s) - break; - - errno = 0; - hb_codepoint_t u = strtoul (s, &p, 10); - if (errno || s == p) - { - hb_set_destroy (name_ids); - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - "Failed parsing nameID values at: '%s'", s); - return false; - } - - hb_set_add (name_ids, u); - - s = p; - } - - hb_set_t *prev = subset_opts->name_ids; - subset_opts->name_ids = hb_set_reference (name_ids); - hb_set_destroy (prev); - hb_set_destroy (name_ids); - - return true; -} - - -void -subset_options_t::add_options (option_parser_t *parser) -{ - GOptionEntry entries[] = - { - {"layout", 0, 0, G_OPTION_ARG_NONE, &this->keep_layout, "Keep OpenType Layout tables", nullptr}, - {"no-hinting", 0, 0, G_OPTION_ARG_NONE, &this->drop_hints, "Whether to drop hints", nullptr}, - {"retain-gids", 0, 0, G_OPTION_ARG_NONE, &this->retain_gids, "If set don't renumber glyph ids in the subset.", nullptr}, - {"desubroutinize", 0, 0, G_OPTION_ARG_NONE, &this->desubroutinize, "Remove CFF/CFF2 use of subroutines", nullptr}, - {"name-IDs", 0, 0, G_OPTION_ARG_CALLBACK, (gpointer) &parse_nameids, "Subset specified nameids", "list of int numbers"}, - - {nullptr} - }; - parser->add_group (entries, - "subset", - "Subset options:", - "Options subsetting", - this); -} - diff --git a/util/options.hh b/util/options.hh index 2691e2269..83dbe3402 100644 --- a/util/options.hh +++ b/util/options.hh @@ -28,6 +28,7 @@ #define OPTIONS_HH #include "hb.hh" +#include "hb-subset.h" #include #include @@ -673,28 +674,18 @@ struct subset_options_t : option_group_t { subset_options_t (option_parser_t *parser) { - keep_layout = false; - drop_hints = false; - retain_gids = false; - desubroutinize = false; - name_ids = hb_set_create (); - + input = hb_subset_input_create_or_fail (); add_options (parser); } virtual ~subset_options_t () { - hb_set_destroy (name_ids); + hb_subset_input_destroy (input); } - void add_options (option_parser_t *parser); - hb_bool_t keep_layout; - hb_bool_t drop_hints; - hb_bool_t retain_gids; - hb_bool_t desubroutinize; - hb_set_t *name_ids; + hb_subset_input_t *input; }; /* fallback implementation for scalbn()/scalbnf() for pre-2013 MSVC */