From 9ccb8366f603a9e4a7a3c3f96420a19d4f6fb390 Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Wed, 17 Jan 2018 22:09:07 -0800 Subject: [PATCH 01/25] Start to sketch APIs for subsetting --- src/Makefile.am | 2 + src/Makefile.sources | 4 ++ src/hb-subset.cc | 0 src/hb-subset.h | 88 +++++++++++++++++++++++++++++++++++++++++++ util/Makefile.am | 3 ++ util/Makefile.sources | 4 ++ util/hb-subset.cc | 61 ++++++++++++++++++++++++++++++ 7 files changed, 162 insertions(+) create mode 100644 src/hb-subset.cc create mode 100644 src/hb-subset.h create mode 100644 util/hb-subset.cc diff --git a/src/Makefile.am b/src/Makefile.am index 833d1f956..dd1c7ae66 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,7 +27,9 @@ HBNONPCLIBS = HBDEPS = HBSOURCES = $(HB_BASE_sources) HBSOURCES += $(HB_BASE_RAGEL_GENERATED_sources) +HBSOURCES += $(HB_SUBSET_sources) HBHEADERS = $(HB_BASE_headers) +HBHEADERS += $(HB_SUBSET_headers) HBNODISTHEADERS = $(HB_NODIST_headers) if HAVE_OT diff --git a/src/Makefile.sources b/src/Makefile.sources index 213aa22f6..f223fcfee 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -181,6 +181,10 @@ HB_UCDN_sources = hb-ucdn.cc HB_ICU_sources = hb-icu.cc HB_ICU_headers = hb-icu.h +# Sources for libharfbuzz-subset +HB_SUBSET_sources = hb-subset.cc +HB_SUBSET_headers = hb-subset.h + HB_GOBJECT_sources = hb-gobject-structs.cc HB_GOBJECT_STRUCTS_headers = hb-gobject-structs.h HB_GOBJECT_headers = hb-gobject.h $(HB_GOBJECT_STRUCTS_headers) diff --git a/src/hb-subset.cc b/src/hb-subset.cc new file mode 100644 index 000000000..e69de29bb diff --git a/src/hb-subset.h b/src/hb-subset.h new file mode 100644 index 000000000..8aaccd6e5 --- /dev/null +++ b/src/hb-subset.h @@ -0,0 +1,88 @@ +/* + * Copyright © 2018 Google + * + * 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): Rod Sheeter + */ + +#ifndef HB_H_IN +#error "Include instead." +#endif + +#ifndef HB_SUBSET_H +#define HB_SUBSET_H + +#include "hb-common.h" +#include "hb-face.h" + +HB_BEGIN_DECLS + +/* + * hb_subset_profile_t + * Things that change based on target environment, e.g. OS + */ + +typedef struct hb_subset_profile_t hb_subset_profile_t; + +HB_EXTERN hb_subset_profile_t * +hb_subset_profile_create(); + +HB_EXTERN void +hb_subset_profile_destroy(hb_subset_profile_t *profile); + +/* + * hb_subset_input_t + * Things that change based on the input. Characters to keep, etc. + */ + +typedef struct hb_subset_input_t hb_subset_input_t; + +HB_EXTERN hb_subset_input_t * +hb_subset_input_create(); + +HB_EXTERN void +hb_subset_input_destroy(hb_subset_input_t *subset_input); + +/* + * hb_subset_face_t + * Reusable subset-ready plan for a given face. Threadsafe for multiple + * concurrent subset operations. + */ + +typedef struct hb_subset_face_t hb_subset_face_t; + +HB_EXTERN hb_subset_face_t * +hb_subset_face_create(hb_face_t *face); + +HB_EXTERN void +hb_subset_face_destroy(hb_subset_face_t *face); + + +HB_EXTERN hb_bool_t +hb_subset(hb_subset_profile_t *profile, + hb_subset_input_t *input, + hb_subset_face_t *face + hb_blob_t *result /* OUT */); + +HB_END_DECLS + +#endif /* HB_SUBSET_H */ diff --git a/util/Makefile.am b/util/Makefile.am index e6620a237..283dd91ff 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -46,6 +46,9 @@ endif # HAVE_FREETYPE hb_shape_SOURCES = $(HB_SHAPE_sources) bin_PROGRAMS += hb-shape +hb_subset_SOURCES = $(HB_SUBSET_sources) +bin_PROGRAMS += hb-subset + if HAVE_OT hb_ot_shape_closure_SOURCES = $(HB_OT_SHAPE_CLOSURE_sources) bin_PROGRAMS += hb-ot-shape-closure diff --git a/util/Makefile.sources b/util/Makefile.sources index d6c00cc4d..94a5fa8a2 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -28,3 +28,7 @@ HB_OT_SHAPE_CLOSURE_sources = \ options.hh \ main-font-text.hh \ $(NULL) + +HB_SUBSET_sources = \ + hb-subset.cc \ + $(NULL) diff --git a/util/hb-subset.cc b/util/hb-subset.cc new file mode 100644 index 000000000..ad7ceb14b --- /dev/null +++ b/util/hb-subset.cc @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "hb-private.hh" +#include "hb-blob.h" + +int +main (int argc, char **argv) +{ + int exit_code = 0; + int fd = open("/tmp/Lobster-Regular.ttf", O_RDONLY); + if (fd == -1) { + perror("Unable to open file"); + exit(1); + } + + void *mapped_file = MAP_FAILED; + char *raw_font = nullptr; + + struct stat stat; + if (fstat(fd, &stat) != -1) { + printf("File is %zu bytes\n", stat.st_size); + + void *mapped_file = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (mapped_file != MAP_FAILED) { + raw_font = static_cast(mapped_file); + } else { + perror("Failed to map file"); + } + } else { + perror("Unable to fstat"); + } + + if (raw_font) { + printf("Mapped file\n"); + for (int i = 0; i < 4; i++) { + printf("%02x", *(raw_font + i)); + } + printf("\n"); + + hb_blob_t *font_blob = hb_blob_create(raw_font, stat.st_size, HB_MEMORY_MODE_READONLY, nullptr, nullptr); + } + + if (mapped_file != MAP_FAILED) { + if (munmap(mapped_file, stat.st_size) == -1) { + perror("Unable to unmap file"); + exit_code = 1; + } + } + + if (close(fd) == -1) { + perror("Unable to close file"); + exit_code = 1; + } + return exit_code; +} From 4cdae914e2e2fff1ff91e2f42648a8acb82a5494 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 26 Jan 2018 13:57:48 -0800 Subject: [PATCH 02/25] Add basic directory structure for subsetter integration tests. Plus a utility for generating expected output files. --- .../basics/Roboto-Regular.abc.default.62.ttf | Bin 0 -> 1996 bytes test/subset/data/fonts/Roboto-Regular.abc.ttf | Bin 0 -> 2460 bytes test/subset/data/profiles/default.txt | 0 test/subset/data/tests/basics.txt | 8 ++ test/subset/generate-expected-outputs.py | 40 +++++++++ test/subset/subset_test_suite.py | 82 ++++++++++++++++++ 6 files changed, 130 insertions(+) create mode 100644 test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf create mode 100644 test/subset/data/fonts/Roboto-Regular.abc.ttf create mode 100644 test/subset/data/profiles/default.txt create mode 100644 test/subset/data/tests/basics.txt create mode 100755 test/subset/generate-expected-outputs.py create mode 100644 test/subset/subset_test_suite.py diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf new file mode 100644 index 0000000000000000000000000000000000000000..8e44886f1f31dba3004b2ef0bff3d3afda4c22b4 GIT binary patch literal 1996 zcmZuyZERCj7=F*ax1TGlt!1)a7`N*_7;EVq%L223qU{C_SzU{{mnd7ebsw;?x(^s8 z=z=1%B8c+CU3$hX9re#GD!UXcp?x9o$WcEAqTgcV!17k|0`xmD3 z$dkw``iI6QtTsR`VFz;S;Ba>eI?w{afnXj=O=N_6(T4hWkSqIBLup~g{wIcTkFYT_ zJUUhwf*$18D4rQfXU_LNz<#9rShF1jI9|9TDR_gHLn-v;++Nw`-o)KbE?xs}4RMI6 z+!2ze9p25FPDCW4dy$xtn33d6RuSuRb_Y$|;hc6eZm(5+H{^R77oF2j{zL1YVjjU? zUTyKaHna~YQXW$yjAXX3j)KYChl-OvAc>aZCW zzJ4eFbAG|}<<$%Mz=f;0BYM^XoP-icf=Oqz6Lbj`_|z#1`u)=L{O+z_zxlYMIQ$os zTJ_N7yRP+QTxad~9>@#VZFSaBq&5`Lhj<)%q{DEWlq`dJyyv#<59IuFwz@@aBaKi1 zQF@ELhh2c_jA;Y5w#Jyn@1d9E7A1`eqcg*XmbSJQh(H%~nIR9*YdIr;0_+@xr6vZyj>VH-?^rW*7b#hd?X0 z#noxXPH3~NaFoBfdfFy->ADL-QYYK9SWUvCWvlp0&bMQk{df;_Zwkb3Zn#OwBs+zAgG_ zm&*b?GBvBfZgsi_qVgQnh_|EYchqvIg&cDVwJhgih%>IHx=Psz)>@ch+n!$U^Po8+ zsM|KK)h)68*@nsz@=1H157(BGWoitRmll&{e&yxndYLTCse>;y`N^`r))(L8CCiqK zHCE{=_5+=*jjPF5^hVE4BhPU&m+cOdZ<)vGu$jqP99R#lvguHAl*G4c*aFXC6L4jb6myJ`!I8}^O#dxl`jA{w~AXrm| zc^w2N3BDsJ5Ns*K1Sq4^D%tQP(kV1-0l!!d zXoq1ugTpX}zjjDNAB@8wq+kTI^ikML-|_z$3_mSa5b&HAqb{i#qXgN~MIRqp(SaTs zgm$Qd%^*dacO=2veQ0D5ynQ3-ec&BTjqL|7zK=Lim2%m>^vHf(#i(Q$BSl8*f=AEv iLOI$?>Pzd=Y-G}U_0aIxdKLP575aJ=kJdxsrSUiVPqUE# literal 0 HcmV?d00001 diff --git a/test/subset/data/fonts/Roboto-Regular.abc.ttf b/test/subset/data/fonts/Roboto-Regular.abc.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9d791f7fc5ba42bb66504843d5841f2b180beef7 GIT binary patch literal 2460 zcmZuyeQaA-6+idA=jUg~OP-Arw|;B#+?UVBUa}-LTZp;c*H zj{OkFuK=*ild<^}DDWxF|2&SnCTAX=2yc7$MH~wP2%}Sp=byFC*5E z#FFE}it}GMe+2OzsoD8-AqiNU@+gg`=EhUsow)WZ02$|C`#^wX;i~v|X+Km$BRrb( zHSKbH?&Lm|>u&;w9+Uf8w5-p+Ct3s&0?AVXfB+EW1fPUKxw2x%%UUozTfw-q*$n(i z`yiV0l=&#Bq-JZjth`Lk8e6dS_CS4uZ0xMx-BH`#zPqE|*eO>U9pP|$Tb0l4^;Ooj zg+IO9&?~)FVx#K(+D~43ow2`NOg|Vu{>uDoM=!l4)#uCZdEPjk|4~{WI{wqAesJkP zY(CL<_qWYUFXzj@WvVAf_rCMXd+x_=fD?tQk}d<3!*-a+dHfpD^Qc_Jnw6Gfr7kSa zwOO`^K-?@_Mj%t!Hbtbeoi6IQOU-(=%Hg7#^=-k{+nHA|b~e}zce^{Rd)y6Fpxs^7 z-WHbhch_G}InJ-8Smv*5-~7hP;DL)zoDtj~=KpZ|m<-o{@l^hg`Rlfyzi~bvJpTqT zIEXuT6?be0^yk#-8l2qPDHVySo0Js<3bM??77MOWiCk5=m*mX^X?@$vktDcxmzLeA zFYW>^xv(dGM!8@+`r47df`5HS)*&9cFT`0;*L$WToQ;LJ5aeuEAjHL>-pj?h-XYV7 z>KQ$=FOkuE^{H5bOLfH_2gWne7M;VeIgN6}4Djx#c0(PHMms}XqBIzR-kB&SI9y7A zebLl%J;ZInew~Z;gXY~PUy5kFI}+6b0lkN>51M>Eq6MPS5SMRoM6SoCw^?lMK`!qK zamC6oZ1Qf6Lo}12e8Z*@;7gf|mca@(@%qhpmBA+s-5)pL3iPb9r9n)Eoe|K;U<8Z+ zGK@w-+!5>_HhYj_04X|yys;<5%YwWKJ6ABa6`EPj%}}nmJ6r2C6_jRJ zVS7JwTfmFzieT=&qp_%oL!a-ct0J3p-I@TZFLAJ8T6Z;1!+&T?{~()CpecB&c|1z4A7wWZwg~ybk5^2Hs3o6Y!6S zr&V&`Nu*O~=>pzhE*OGYxF6p%zC$n$ldu3YcrxeEtJn&M>7V<58pG>F0Rhi>1?Ht} zR@;U;!-wt%vRfzLg}QZ{IH48xfE4Nd;t2TT56{hje{yd80r1bn(nr9L-y=56TJ(Ql zeC`O&Vns3>qkN3!#eY0`wo$H4u82R`EFMCM4RPAitz0yZ6Z3T3#HqH(skX?e_6Dbq GW&a ..." + + +def generate_expected_output(input_file, unicodes, output_path): + check_call(["fonttools", "subset", + input_file, + "--unicodes=%s" % unicodes, + "--output-file=%s" % output_path]) + + +args = sys.argv[1:] +if not args: + usage() + +for path in args: + with open(path, 'r') as f: + test_suite = SubsetTestSuite(path, f.read()) + output_directory = test_suite.get_output_directory() + + print "Generating output files for %s" % output_directory + for test in test_suite.tests(): + unicodes = test.unicodes() + font_name = test.get_font_name() + print "Creating subset %s/%s" % (output_directory, font_name) + generate_expected_output(test.font_path, unicodes, + os.path.join(output_directory, + font_name)) diff --git a/test/subset/subset_test_suite.py b/test/subset/subset_test_suite.py new file mode 100644 index 000000000..256e20713 --- /dev/null +++ b/test/subset/subset_test_suite.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +import os + +# A single test in a subset test suite. Identifies a font +# a subsetting profile, and a subset to be cut. +class Test: + def __init__(self, font_path, profile_path, subset): + self.font_path = font_path + self.profile_path = profile_path + self.subset = subset + + def unicodes(self): + return ",".join("%X" % ord(c) for (i, c) in enumerate(self.subset)) + + def get_font_name(self): + font_base_name = os.path.basename(self.font_path) + font_base_name_parts = os.path.splitext(font_base_name) + profile_name = os.path.splitext(os.path.basename(self.profile_path))[0] + + return "%s.%s.%s%s" % (font_base_name_parts[0], + profile_name, + self.unicodes(), + font_base_name_parts[1]) + +# A group of tests to perform on the subsetter. Each test +# Identifies a font a subsetting profile, and a subset to be cut. +class SubsetTestSuite: + + def __init__(self, test_path, definition): + self.test_path = test_path + self.fonts = set() + self.profiles = set() + self.subsets = set() + self._parse(definition) + + def get_output_directory(self): + test_name = os.path.splitext(os.path.basename(self.test_path))[0] + data_dir = os.path.join(os.path.dirname(self.test_path), "..") + + output_dir = os.path.normpath(os.path.join(data_dir, "expected", test_name)) + if not os.path.exists(output_dir): + os.mkdir(output_dir) + if not os.path.isdir(output_dir): + raise Error("%s is not a directory." % output_dir) + + return output_dir + + def tests(self): + for font in self.fonts: + font = os.path.join(self._base_path(), "fonts", font) + for profile in self.profiles: + profile = os.path.join(self._base_path(), "profiles", profile) + for subset in self.subsets: + yield Test(font, profile, subset) + + def _base_path(self): + return os.path.dirname(os.path.dirname(self.test_path)) + + def _parse(self, definition): + destinations = { + "FONTS:": self.fonts, + "PROFILES:": self.profiles, + "SUBSETS:": self.subsets + } + + current_destination = None + for line in definition.splitlines(): + line = line.strip() + + if line.startswith("#"): + continue + + if not line: + continue + + if line in destinations: + current_destination = destinations[line] + elif current_destination is not None: + current_destination.add(line) + else: + raise Exception("Failed to parse test suite file.") From cc46cd88a1b84b02694fa6d88c4286e93336f096 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 26 Jan 2018 14:25:39 -0800 Subject: [PATCH 03/25] In generate-expected-outputs read the test definition with utf8 encoding. --- test/subset/generate-expected-outputs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/subset/generate-expected-outputs.py b/test/subset/generate-expected-outputs.py index 87db5d5b1..f6636de7e 100755 --- a/test/subset/generate-expected-outputs.py +++ b/test/subset/generate-expected-outputs.py @@ -3,6 +3,7 @@ # Pre-generates the expected output subset files (via fonttools) for # specified subset test suite(s). +import io import os import sys @@ -26,7 +27,7 @@ if not args: usage() for path in args: - with open(path, 'r') as f: + with io.open(path, mode="r", encoding="utf-8") as f: test_suite = SubsetTestSuite(path, f.read()) output_directory = test_suite.get_output_directory() From 5c63c37b2b5aba8bf2f8ff35b7da0d116ebfe8b5 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 26 Jan 2018 16:57:42 -0800 Subject: [PATCH 04/25] WIP test runner for subset tests. --- test/subset/run-tests.py | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100755 test/subset/run-tests.py diff --git a/test/subset/run-tests.py b/test/subset/run-tests.py new file mode 100755 index 000000000..90da96249 --- /dev/null +++ b/test/subset/run-tests.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +# Runs a subsetting test suite. Compares the results of subsetting via harfbuz +# to subsetting via fonttools. + +from __future__ import print_function + +import io +import os +import subprocess +import sys + +from subset_test_suite import SubsetTestSuite + + +def cmd(command): + p = subprocess.Popen ( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p.wait () + print (p.stderr.read (), end="") # file=sys.stderr + return p.stdout.read (), p.returncode + + +def run_test(test): + result, return_code = cmd([hb_subset, + test.font_path, + "--unicodes=%s" % test.unicodes()]) + + if return_code: + print ("ERROR: hb-subset failed for %s, %s, %s" % (test.font_path, test.profile_path, test.unicodes())) + return 1 + + with open(os.path.join(test_suite.get_output_directory(), + test.get_font_name())) as expected: + if not result == expected.read(): + print ("ERROR: hb-subset %s, %s, %s does not match expected value." % (test.font_path, test.profile_path, test.unicodes())) + return 1 + + return 0 + + +args = sys.argv[1:] +if not args or sys.argv[1].find('hb-subset') == -1 or not os.path.exists (sys.argv[1]): + print ("First argument does not seem to point to usable hb-subset.") + sys.exit (1) +hb_subset, args = args[0], args[1:] + +if not len(args): + print ("No tests supplied.") + sys.exit (1) + +fails = 0 +for path in args: + with io.open(path, mode="r", encoding="utf-8") as f: + print ("Running tests in " + path) + test_suite = SubsetTestSuite(path, f.read()) + for test in test_suite.tests(): + fails += run_test(test) + +if fails != 0: + print (str (fails) + " test(s) failed.") + sys.exit(1) +else: + print ("All tests passed.") From 0853260e997aded264f42bb369d4fcb39cccb7d6 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Mon, 29 Jan 2018 13:30:02 -0800 Subject: [PATCH 05/25] Configure automake to run the new subset tests. --- configure.ac | 2 ++ test/Makefile.am | 2 +- test/subset/Makefile.am | 16 ++++++++++++++++ test/subset/data/Makefile.am | 16 ++++++++++++++++ test/subset/data/Makefile.sources | 10 ++++++++++ .../data/tests/{basics.txt => basics.tests} | 0 6 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 test/subset/Makefile.am create mode 100644 test/subset/data/Makefile.am create mode 100644 test/subset/data/Makefile.sources rename test/subset/data/tests/{basics.txt => basics.tests} (100%) diff --git a/configure.ac b/configure.ac index dec994ebd..12401f00b 100644 --- a/configure.ac +++ b/configure.ac @@ -500,6 +500,8 @@ test/shaping/Makefile test/shaping/data/Makefile test/shaping/data/in-house/Makefile test/shaping/data/text-rendering-tests/Makefile +test/subset/Makefile +test/subset/data/Makefile docs/Makefile docs/version.xml ]) diff --git a/test/Makefile.am b/test/Makefile.am index ad496f559..66b3e6e2e 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -2,7 +2,7 @@ NULL = EXTRA_DIST = -SUBDIRS = api shaping fuzzing +SUBDIRS = api shaping fuzzing subset EXTRA_DIST += \ CMakeLists.txt \ diff --git a/test/subset/Makefile.am b/test/subset/Makefile.am new file mode 100644 index 000000000..3037261bb --- /dev/null +++ b/test/subset/Makefile.am @@ -0,0 +1,16 @@ +# Process this file with automake to produce Makefile.in + +NULL = +EXTRA_DIST = +CLEANFILES = +SUBDIRS = data + +# Convenience targets: +lib: + @$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib + +CLEANFILES += \ + subset_test_suite.py[c0] \ + $(NULL) + +-include $(top_srcdir)/git.mk diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am new file mode 100644 index 000000000..c74f7fd86 --- /dev/null +++ b/test/subset/data/Makefile.am @@ -0,0 +1,16 @@ +# Process this file with automake to produce Makefile.in + +NULL = +EXTRA_DIST = +CLEANFILES = +SUBDIRS = + +# Convenience targets: +lib: + @$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib + +TEST_EXTENSIONS = .tests +TESTS_LOG_COMPILER = $(srcdir)/../run-tests.py $(top_builddir)/util/hb-subset$(EXEEXT) +include Makefile.sources + +-include $(top_srcdir)/git.mk diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources new file mode 100644 index 000000000..574f94665 --- /dev/null +++ b/test/subset/data/Makefile.sources @@ -0,0 +1,10 @@ +TESTS = \ + tests/basics.tests \ + $(NULL) + +XFAIL_TESTS = \ + tests/basics.tests \ + $(NULL) + +DISABLED_TESTS = \ + $(NULL) diff --git a/test/subset/data/tests/basics.txt b/test/subset/data/tests/basics.tests similarity index 100% rename from test/subset/data/tests/basics.txt rename to test/subset/data/tests/basics.tests From b029b7c19a733a2a39860238ad300e6c4a3f7802 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Mon, 29 Jan 2018 13:31:49 -0800 Subject: [PATCH 06/25] Whitespace --- test/subset/data/Makefile.sources | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources index 574f94665..00bd2b9f0 100644 --- a/test/subset/data/Makefile.sources +++ b/test/subset/data/Makefile.sources @@ -1,10 +1,10 @@ TESTS = \ tests/basics.tests \ - $(NULL) + $(NULL) XFAIL_TESTS = \ tests/basics.tests \ - $(NULL) + $(NULL) DISABLED_TESTS = \ $(NULL) From 76351518ca9bc88aa6fbc975e1e35bd86432d652 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Tue, 30 Jan 2018 14:03:16 -0800 Subject: [PATCH 07/25] Remove basic subset test from XFAIL --- test/subset/data/Makefile.sources | 1 - 1 file changed, 1 deletion(-) diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources index 00bd2b9f0..37550b639 100644 --- a/test/subset/data/Makefile.sources +++ b/test/subset/data/Makefile.sources @@ -3,7 +3,6 @@ TESTS = \ $(NULL) XFAIL_TESTS = \ - tests/basics.tests \ $(NULL) DISABLED_TESTS = \ From c02573516c05ac97acb243ef5dec26af86086ded Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Tue, 30 Jan 2018 18:39:41 -0800 Subject: [PATCH 08/25] Fix typo in hb-subset.h --- src/hb-subset.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-subset.h b/src/hb-subset.h index 8aaccd6e5..b7c1f569f 100644 --- a/src/hb-subset.h +++ b/src/hb-subset.h @@ -80,7 +80,7 @@ hb_subset_face_destroy(hb_subset_face_t *face); HB_EXTERN hb_bool_t hb_subset(hb_subset_profile_t *profile, hb_subset_input_t *input, - hb_subset_face_t *face + hb_subset_face_t *face, hb_blob_t *result /* OUT */); HB_END_DECLS From cf403e1a53381f293aceac5cdbe031bbb2a7af77 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Tue, 30 Jan 2018 18:40:23 -0800 Subject: [PATCH 09/25] Add hb-subset.h to hb.h --- src/hb.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hb.h b/src/hb.h index 7402034f4..e55decfea 100644 --- a/src/hb.h +++ b/src/hb.h @@ -41,6 +41,7 @@ #include "hb-set.h" #include "hb-shape.h" #include "hb-shape-plan.h" +#include "hb-subset.h" #include "hb-unicode.h" #include "hb-version.h" From e9d154ac8ddd2712a34c53c95a17e469f95e5b30 Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Tue, 30 Jan 2018 19:27:11 -0800 Subject: [PATCH 10/25] tweak test failure output. write to a temp file not stdout. test still fails because expected is not just an identical copy of input --- test/subset/run-tests.py | 39 +++++++++++++++++++++-------- util/hb-subset.cc | 53 ++++++++++++++++++++++++++++------------ 2 files changed, 66 insertions(+), 26 deletions(-) diff --git a/test/subset/run-tests.py b/test/subset/run-tests.py index 90da96249..0b4b96a6e 100755 --- a/test/subset/run-tests.py +++ b/test/subset/run-tests.py @@ -9,6 +9,7 @@ import io import os import subprocess import sys +import tempfile from subset_test_suite import SubsetTestSuite @@ -20,21 +21,39 @@ def cmd(command): print (p.stderr.read (), end="") # file=sys.stderr return p.stdout.read (), p.returncode +def read_binary(file_path): + with open(file_path, 'rb') as f: + return f.read() + +def fail_test(test, cli_args, message): + print ('ERROR: %s' % message) + print ('Test State:') + print (' test.font_path %s' % os.path.abspath(test.font_path)) + print (' test.profile_path %s' % os.path.abspath(test.profile_path)) + print (' test.unicodes %s' % test.unicodes()) + expected_file = os.path.join(test_suite.get_output_directory(), + test.get_font_name()) + print (' expected_file %s' % os.path.abspath(expected_file)) + return 1 def run_test(test): - result, return_code = cmd([hb_subset, - test.font_path, - "--unicodes=%s" % test.unicodes()]) + out_file = os.path.join(tempfile.mkdtemp(), test.get_font_name() + '-subset.ttf') + cli_args = [hb_subset, test.font_path, out_file, "--unicodes=%s" % test.unicodes()] + _, return_code = cmd(cli_args) if return_code: - print ("ERROR: hb-subset failed for %s, %s, %s" % (test.font_path, test.profile_path, test.unicodes())) - return 1 + return fail_test(test, cli_args, "%s returned %d" % (' '.join(cli_args), return_code)) - with open(os.path.join(test_suite.get_output_directory(), - test.get_font_name())) as expected: - if not result == expected.read(): - print ("ERROR: hb-subset %s, %s, %s does not match expected value." % (test.font_path, test.profile_path, test.unicodes())) - return 1 + expected = read_binary(os.path.join(test_suite.get_output_directory(), + test.get_font_name())) + actual = read_binary(out_file) + + if len(actual) != len(expected): + return fail_test(test, cli_args, "expected %d bytes, actual %d: %s" % ( + len(expected), len(actual), ' '.join(cli_args))) + + if not actual == expected: + return fail_test(test, cli_args, 'files are the same length but not the same bytes') return 0 diff --git a/util/hb-subset.cc b/util/hb-subset.cc index ad7ceb14b..5aafbe194 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -13,37 +13,52 @@ int main (int argc, char **argv) { int exit_code = 0; - int fd = open("/tmp/Lobster-Regular.ttf", O_RDONLY); + + if (argc != 4) { + fprintf(stderr, "Must have 4 args\n"); + exit(1); + } + + int fd = open(argv[1], O_RDONLY); if (fd == -1) { - perror("Unable to open file"); + perror("Unable to open font file"); exit(1); } void *mapped_file = MAP_FAILED; - char *raw_font = nullptr; + int fd_out = -1; struct stat stat; if (fstat(fd, &stat) != -1) { - printf("File is %zu bytes\n", stat.st_size); - void *mapped_file = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (mapped_file != MAP_FAILED) { - raw_font = static_cast(mapped_file); - } else { + mapped_file = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + if (mapped_file == MAP_FAILED) { perror("Failed to map file"); } } else { perror("Unable to fstat"); } - if (raw_font) { - printf("Mapped file\n"); - for (int i = 0; i < 4; i++) { - printf("%02x", *(raw_font + i)); - } - printf("\n"); + if (mapped_file != MAP_FAILED) { + hb_blob_t *font_blob = hb_blob_create(static_cast(mapped_file), + stat.st_size, + HB_MEMORY_MODE_READONLY, nullptr, + nullptr); - hb_blob_t *font_blob = hb_blob_create(raw_font, stat.st_size, HB_MEMORY_MODE_READONLY, nullptr, nullptr); + fd_out = open(argv[2], O_CREAT | O_WRONLY, S_IRWXU); + if (fd_out != -1) { + ssize_t bytes_written = write(fd_out, mapped_file, stat.st_size); + if (bytes_written == -1) { + perror("Unable to write output file"); + exit_code = 1; + } else if (bytes_written != stat.st_size) { + fprintf(stderr, "Wrong number of bytes written"); + exit_code = 1; + } + } else { + perror("Unable to open output file"); + exit_code = 1; + } } if (mapped_file != MAP_FAILED) { @@ -53,9 +68,15 @@ main (int argc, char **argv) } } - if (close(fd) == -1) { + if (fd_out != -1 && close(fd_out) == -1) { + perror("Unable to close output file"); + exit_code = 1; + } + + if (fd != -1 && close(fd) == -1) { perror("Unable to close file"); exit_code = 1; } + return exit_code; } From b59c08eb045db2b6c952de81510b8159a4f72fb1 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Wed, 31 Jan 2018 11:14:53 -0800 Subject: [PATCH 11/25] Add the beginning of a unit test for hb-subset --- test/api/test-subset.c | 67 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 test/api/test-subset.c diff --git a/test/api/test-subset.c b/test/api/test-subset.c new file mode 100644 index 000000000..85b302d72 --- /dev/null +++ b/test/api/test-subset.c @@ -0,0 +1,67 @@ +/* + * Copyright © 2011 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" + +/* Unit tests for hb-subset.h */ + +static const char test_data[] = "OTTO"; + +static void +test_subset (void) +{ + hb_blob_t *font_blob = hb_blob_create(test_data, sizeof(test_data), + HB_MEMORY_MODE_READONLY, NULL, NULL); + hb_face_t *face = hb_face_create(font_blob, 0); + + hb_subset_profile_t *profile = hb_subset_profile_create(); + hb_subset_input_t *input = hb_subset_input_create(); + hb_subset_face_t *subset_face = hb_subset_face_create(face); + + char output_data[100]; + hb_blob_t *output = hb_blob_create(output_data, sizeof(output_data), + HB_MEMORY_MODE_WRITABLE, NULL, NULL); + + g_assert(hb_subset(profile, input, subset_face, output)); + g_assert_cmpmem(test_data, 4, output_data, sizeof(output)); + + hb_blob_destroy(output); + hb_subset_face_destroy(subset_face); + hb_subset_input_destroy(input); + hb_subset_profile_destroy(profile); + hb_face_destroy(face); + hb_blob_destroy(font_blob); +} + +int +main (int argc, char **argv) +{ + hb_test_init (&argc, &argv); + + hb_test_add (test_subset); + + return hb_test_run(); +} From 38af23b8df1a84f24d379d27d1a1e20f9ce07f34 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Wed, 31 Jan 2018 11:32:23 -0800 Subject: [PATCH 12/25] Make the expected output for subsetting basics test be equal to the input file for now. --- .../basics/Roboto-Regular.abc.default.62.ttf | Bin 1996 -> 2460 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf b/test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf index 8e44886f1f31dba3004b2ef0bff3d3afda4c22b4..9d791f7fc5ba42bb66504843d5841f2b180beef7 100644 GIT binary patch delta 985 zcmZvbZ%7ki9LImpbGPlb$vmg?Dof|ire@*)Wp9K+TM=ZX(i#R`o4TUYmBUsTD+3cs zEY1kZBqGQt^0xkw3iU>!e_+vz4DwYkDvXLEqQdpNx!9ZT;CY_U@Bf#3?pgY9W&{WT zSaD&&?yjp}539fqz>T7{$i1bZQE&V-i9C%wYx~>kipVMhzDM3}29&+Jf!!!JoUXqyIesnQWK3B0=CF6B`U;KwA60 zLv5}_??%wM5`cOGemQJ0v08(p=;s5ilK;=%mxlqE+sLi}8cgH5E69J43j%T|%q!$R z@|+gGuhS=yt1yGC2{Or2I7q7)BXU2oy+ew~t@NJh8xBy1hMHh!S12w6s^faJfZ&1F z;N`Yoj{)@9glq%~a(s^Y$Zm#QD1x4tP`o0;x<(av)jka>)O%E+G$$%7sojwP7J;=U z0T2KoMgg4pI zGa&{ir?tKzZcNub)v^2S{O{1r(A}@x7&o|Q;KKRiVeigSKPe=TH}a$Z&CNY0o<0;O zp5RwYTxk{tAsd=NmmmPVLlkHM=#zXHX2_~!EETO@+iaR*r{!iZ$m|g~#g;*f(nLkV zDq*^ok-(=+Qqjm6b*xUO15n=4l44&xjuoVMJbo!zHBD;|OB3tD91@;=w`!Od%;H5j u0l}9^tP~wucp4DJVQ6p{;U5T40kya@c>4ddkWG^{v`VS7R*@(}hMK<>PUtWI delta 492 zcmXw$O-mbL6ot>cGm|eOLQDl)!NG`7l!`7w7TrYB20@4r1$R1=$%o=Jg9e)xijZ|F zg|~|?S_+|yf(sWe1QB)PLQqjx{s1?Eir_{hke*4DfjjrP_ngCA=2zEI=Nc3M5f%+b zI`!^7lJEh(2B(ARsZ3TszWE9G2gq(ZJ2Ng{)Vj$($ni{eWUR7K>i|TRJfCw+7q66C z@+EmTw^}iV{`r41FGZd%xJBpV!~8m+z6F$>qPeo>B7iOm?kETsmp&EtHvi%pd7$J}s(~PwB__#%rE<>196N|Gi_bBu zt{78;%)cfZAIvvSUTlT#x#N{cxaE~f(}7Js^#ty6-mMoN`9azPO%u`(dww?D3HeR! z>-54~6pL6_2Y= Date: Wed, 31 Jan 2018 14:53:09 -0800 Subject: [PATCH 13/25] Add a basic implementation of hb-subset to enable compilation of test-subset. --- src/hb-subset.cc | 146 +++++++++++++++++++++++++++++++++++++++++ src/hb-subset.h | 5 +- test/api/Makefile.am | 1 + test/api/test-subset.c | 12 ++-- 4 files changed, 156 insertions(+), 8 deletions(-) diff --git a/src/hb-subset.cc b/src/hb-subset.cc index e69de29bb..cabae4c4b 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -0,0 +1,146 @@ +/* + * Copyright © 2009 Red Hat, Inc. + * Copyright © 2012 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, Rod Sheeter + */ + +#include "hb-private.hh" + +#include "hb-object-private.hh" + + +struct hb_subset_profile_t { + hb_object_header_t header; + ASSERT_POD (); +}; + +struct hb_subset_input_t { + hb_object_header_t header; + ASSERT_POD (); +}; + +struct hb_subset_face_t { + hb_object_header_t header; + ASSERT_POD (); +}; + + +/** + * hb_subset_profile_create: + * + * Return value: New profile with default settings. + * + * Since: 1.7.5 + **/ +hb_subset_profile_t * +hb_subset_profile_create () +{ + return hb_object_create(); +} + +/** + * hb_subset_profile_destroy: + * + * Since: 1.7.5 + **/ +void +hb_subset_profile_destroy (hb_subset_profile_t *profile) +{ + if (!hb_object_destroy (profile)) return; + + free (profile); +} + +/** + * hb_subset_input_create: + * + * Return value: New subset input. + * + * Since: 1.7.5 + **/ +hb_subset_input_t * +hb_subset_input_create() +{ + return hb_object_create(); +} + +/** + * hb_subset_input_destroy: + * + * Since: 1.7.5 + **/ +void +hb_subset_input_destroy(hb_subset_input_t *subset_input) +{ + if (!hb_object_destroy (subset_input)) return; + + free (subset_input); +} + +/** + * hb_subset_face_create: + * + * Return value: New subset face. + * + * Since: 1.7.5 + **/ +hb_subset_face_t * +hb_subset_face_create(hb_face_t *face) +{ + return hb_object_create(); +} + +/** + * hb_subset_face_destroy: + * + * Since: 1.7.5 + **/ +void +hb_subset_face_destroy(hb_subset_face_t *face) +{ + if (!hb_object_destroy (face)) return; + + free (face); +} + +/** + * hb_subset: + * @profile: profile to use for the subsetting. + * @input: input to use for the subsetting. + * @face: font face data to be subset. + * @result: subsetting result. + * + * Subsets a font according to provided profile and input. + **/ +hb_bool_t +hb_subset(hb_subset_profile_t *profile, + hb_subset_input_t *input, + hb_subset_face_t *face, + hb_blob_t **result /* OUT */) +{ + if (!profile || !input || !face) return false; + + *result = hb_blob_get_empty(); + return true; +} diff --git a/src/hb-subset.h b/src/hb-subset.h index b7c1f569f..7ceac9a7d 100644 --- a/src/hb-subset.h +++ b/src/hb-subset.h @@ -38,7 +38,8 @@ HB_BEGIN_DECLS /* * hb_subset_profile_t - * Things that change based on target environment, e.g. OS + * Things that change based on target environment, e.g. OS. + * Threadsafe for multiple concurrent subset operations. */ typedef struct hb_subset_profile_t hb_subset_profile_t; @@ -81,7 +82,7 @@ HB_EXTERN hb_bool_t hb_subset(hb_subset_profile_t *profile, hb_subset_input_t *input, hb_subset_face_t *face, - hb_blob_t *result /* OUT */); + hb_blob_t **result /* OUT */); HB_END_DECLS diff --git a/test/api/Makefile.am b/test/api/Makefile.am index e22d726a8..3a4eacaec 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -29,6 +29,7 @@ TEST_PROGS = \ test-object \ test-set \ test-shape \ + test-subset \ test-unicode \ test-version \ $(NULL) diff --git a/test/api/test-subset.c b/test/api/test-subset.c index 85b302d72..b6986ce2b 100644 --- a/test/api/test-subset.c +++ b/test/api/test-subset.c @@ -28,7 +28,7 @@ /* Unit tests for hb-subset.h */ -static const char test_data[] = "OTTO"; +static const char test_data[] = { 0, 0, 1, 0 }; static void test_subset (void) @@ -41,12 +41,12 @@ test_subset (void) hb_subset_input_t *input = hb_subset_input_create(); hb_subset_face_t *subset_face = hb_subset_face_create(face); - char output_data[100]; - hb_blob_t *output = hb_blob_create(output_data, sizeof(output_data), - HB_MEMORY_MODE_WRITABLE, NULL, NULL); + hb_blob_t *output; + g_assert(hb_subset(profile, input, subset_face, &output)); - g_assert(hb_subset(profile, input, subset_face, output)); - g_assert_cmpmem(test_data, 4, output_data, sizeof(output)); + unsigned int output_length; + const char *output_data = hb_blob_get_data(output, &output_length); + g_assert_cmpmem(test_data, 4, output_data, output_length); hb_blob_destroy(output); hb_subset_face_destroy(subset_face); From 76b84c36b9560e132918adb4c0c5a0d9bdfb0978 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Wed, 31 Jan 2018 14:53:28 -0800 Subject: [PATCH 14/25] Whitespace --- test/api/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/api/Makefile.am b/test/api/Makefile.am index 3a4eacaec..99849fc99 100644 --- a/test/api/Makefile.am +++ b/test/api/Makefile.am @@ -29,7 +29,7 @@ TEST_PROGS = \ test-object \ test-set \ test-shape \ - test-subset \ + test-subset \ test-unicode \ test-version \ $(NULL) From 74d39ed2639857d5f1a90d9c0d864227a6482b40 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Wed, 31 Jan 2018 15:20:52 -0800 Subject: [PATCH 15/25] Attach add a hb_face_t to hb_subset_face_t. --- src/hb-subset.cc | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/hb-subset.cc b/src/hb-subset.cc index cabae4c4b..00146b9d9 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -43,6 +43,8 @@ struct hb_subset_input_t { struct hb_subset_face_t { hb_object_header_t header; ASSERT_POD (); + + hb_face_t *face; }; @@ -108,7 +110,13 @@ hb_subset_input_destroy(hb_subset_input_t *subset_input) hb_subset_face_t * hb_subset_face_create(hb_face_t *face) { - return hb_object_create(); + if (unlikely (!face)) + face = hb_face_get_empty(); + + hb_subset_face_t *subset_face = hb_object_create (); + subset_face->face = hb_face_reference (face); + + return subset_face; } /** @@ -117,11 +125,12 @@ hb_subset_face_create(hb_face_t *face) * Since: 1.7.5 **/ void -hb_subset_face_destroy(hb_subset_face_t *face) +hb_subset_face_destroy(hb_subset_face_t *subset_face) { - if (!hb_object_destroy (face)) return; + if (!hb_object_destroy (subset_face)) return; - free (face); + hb_face_destroy(subset_face->face); + free (subset_face); } /** From 8c3a6727377895f18e1b5c7076404d8aede17176 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Wed, 31 Jan 2018 15:43:24 -0800 Subject: [PATCH 16/25] Get test-subset to pass. --- src/hb-subset.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hb-subset.cc b/src/hb-subset.cc index 00146b9d9..62f7f0ad2 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -150,6 +150,6 @@ hb_subset(hb_subset_profile_t *profile, { if (!profile || !input || !face) return false; - *result = hb_blob_get_empty(); + *result = hb_face_reference_blob(face->face); return true; } From 34fa7b3ad23b544b0914bc6002d60525224c68e1 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Thu, 1 Feb 2018 16:50:18 -0800 Subject: [PATCH 17/25] Whitespace --- src/hb-subset.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/hb-subset.h b/src/hb-subset.h index 7ceac9a7d..84c0c3c20 100644 --- a/src/hb-subset.h +++ b/src/hb-subset.h @@ -45,10 +45,10 @@ HB_BEGIN_DECLS typedef struct hb_subset_profile_t hb_subset_profile_t; HB_EXTERN hb_subset_profile_t * -hb_subset_profile_create(); +hb_subset_profile_create (); HB_EXTERN void -hb_subset_profile_destroy(hb_subset_profile_t *profile); +hb_subset_profile_destroy (hb_subset_profile_t *profile); /* * hb_subset_input_t @@ -58,10 +58,10 @@ hb_subset_profile_destroy(hb_subset_profile_t *profile); typedef struct hb_subset_input_t hb_subset_input_t; HB_EXTERN hb_subset_input_t * -hb_subset_input_create(); +hb_subset_input_create (); HB_EXTERN void -hb_subset_input_destroy(hb_subset_input_t *subset_input); +hb_subset_input_destroy (hb_subset_input_t *subset_input); /* * hb_subset_face_t @@ -72,17 +72,17 @@ hb_subset_input_destroy(hb_subset_input_t *subset_input); typedef struct hb_subset_face_t hb_subset_face_t; HB_EXTERN hb_subset_face_t * -hb_subset_face_create(hb_face_t *face); +hb_subset_face_create (hb_face_t *face); HB_EXTERN void -hb_subset_face_destroy(hb_subset_face_t *face); +hb_subset_face_destroy (hb_subset_face_t *face); HB_EXTERN hb_bool_t -hb_subset(hb_subset_profile_t *profile, - hb_subset_input_t *input, - hb_subset_face_t *face, - hb_blob_t **result /* OUT */); +hb_subset (hb_subset_profile_t *profile, + hb_subset_input_t *input, + hb_subset_face_t *face, + hb_blob_t **result /* OUT */); HB_END_DECLS From 2763a2c5982c0db072697abe8ba01342d5977237 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Thu, 1 Feb 2018 17:14:51 -0800 Subject: [PATCH 18/25] Include subset test files in distribution. --- test/subset/Makefile.am | 7 ++++++- test/subset/data/Makefile.am | 7 +++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/test/subset/Makefile.am b/test/subset/Makefile.am index 3037261bb..dfc312e65 100644 --- a/test/subset/Makefile.am +++ b/test/subset/Makefile.am @@ -9,8 +9,13 @@ SUBDIRS = data lib: @$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib +EXTRA_DIST += \ + run-tests.py \ + subset_test_suite.py \ + $(NULL) + CLEANFILES += \ - subset_test_suite.py[c0] \ + subset_test_suite.py[co] \ $(NULL) -include $(top_srcdir)/git.mk diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am index c74f7fd86..ecdab3764 100644 --- a/test/subset/data/Makefile.am +++ b/test/subset/data/Makefile.am @@ -5,6 +5,13 @@ EXTRA_DIST = CLEANFILES = SUBDIRS = +EXTRA_DIST = \ + $(TESTS) \ + expected/basics/*.ttf \ + fonts/*.ttf \ + profiles/*.txt \ + $(NULL) + # Convenience targets: lib: @$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib From ede84ffa426edb950c4ec4f89833c85475a1c64f Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Thu, 1 Feb 2018 17:17:36 -0800 Subject: [PATCH 19/25] Whitespace --- util/hb-subset.cc | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 5aafbe194..4beb6a1c0 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -15,7 +15,7 @@ main (int argc, char **argv) int exit_code = 0; if (argc != 4) { - fprintf(stderr, "Must have 4 args\n"); + fprintf(stderr, "Must have 4 args\n"); exit(1); } @@ -39,26 +39,26 @@ main (int argc, char **argv) perror("Unable to fstat"); } - if (mapped_file != MAP_FAILED) { - hb_blob_t *font_blob = hb_blob_create(static_cast(mapped_file), - stat.st_size, - HB_MEMORY_MODE_READONLY, nullptr, - nullptr); + if (mapped_file != MAP_FAILED) { + hb_blob_t *font_blob = hb_blob_create(static_cast(mapped_file), + stat.st_size, + HB_MEMORY_MODE_READONLY, nullptr, + nullptr); - fd_out = open(argv[2], O_CREAT | O_WRONLY, S_IRWXU); - if (fd_out != -1) { - ssize_t bytes_written = write(fd_out, mapped_file, stat.st_size); - if (bytes_written == -1) { - perror("Unable to write output file"); - exit_code = 1; - } else if (bytes_written != stat.st_size) { - fprintf(stderr, "Wrong number of bytes written"); - exit_code = 1; - } - } else { - perror("Unable to open output file"); + fd_out = open(argv[2], O_CREAT | O_WRONLY, S_IRWXU); + if (fd_out != -1) { + ssize_t bytes_written = write(fd_out, mapped_file, stat.st_size); + if (bytes_written == -1) { + perror("Unable to write output file"); + exit_code = 1; + } else if (bytes_written != stat.st_size) { + fprintf(stderr, "Wrong number of bytes written"); exit_code = 1; } + } else { + perror("Unable to open output file"); + exit_code = 1; + } } if (mapped_file != MAP_FAILED) { From 058b1260ad1105d0d8d6bf21f8f65a336e735bd2 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Thu, 1 Feb 2018 18:22:14 -0800 Subject: [PATCH 20/25] Re-write hb-subset utility to use main-font-text driver. --- test/subset/run-tests.py | 15 ++-- util/Makefile.sources | 3 + util/hb-subset.cc | 161 +++++++++++++++++++++------------------ 3 files changed, 97 insertions(+), 82 deletions(-) diff --git a/test/subset/run-tests.py b/test/subset/run-tests.py index 0b4b96a6e..b0054802f 100755 --- a/test/subset/run-tests.py +++ b/test/subset/run-tests.py @@ -30,22 +30,25 @@ def fail_test(test, cli_args, message): print ('Test State:') print (' test.font_path %s' % os.path.abspath(test.font_path)) print (' test.profile_path %s' % os.path.abspath(test.profile_path)) - print (' test.unicodes %s' % test.unicodes()) + print (' test.unicodes %s' % test.unicodes()) expected_file = os.path.join(test_suite.get_output_directory(), - test.get_font_name()) - print (' expected_file %s' % os.path.abspath(expected_file)) + test.get_font_name()) + print (' expected_file %s' % os.path.abspath(expected_file)) return 1 def run_test(test): - out_file = os.path.join(tempfile.mkdtemp(), test.get_font_name() + '-subset.ttf') - cli_args = [hb_subset, test.font_path, out_file, "--unicodes=%s" % test.unicodes()] + out_file = os.path.join(tempfile.mkdtemp(), test.get_font_name() + '-subset.ttf') + cli_args = [hb_subset, + "--font-file=" + test.font_path, + "--output-file=" + out_file, + "--unicodes=%s" % test.unicodes()] _, return_code = cmd(cli_args) if return_code: return fail_test(test, cli_args, "%s returned %d" % (' '.join(cli_args), return_code)) expected = read_binary(os.path.join(test_suite.get_output_directory(), - test.get_font_name())) + test.get_font_name())) actual = read_binary(out_file) if len(actual) != len(expected): diff --git a/util/Makefile.sources b/util/Makefile.sources index 94a5fa8a2..c4516ebcb 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -31,4 +31,7 @@ HB_OT_SHAPE_CLOSURE_sources = \ HB_SUBSET_sources = \ hb-subset.cc \ + options.cc \ + options.hh \ + main-font-text.hh \ $(NULL) diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 4beb6a1c0..f5e8aaf3e 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -1,82 +1,91 @@ -#include -#include -#include -#include -#include -#include -#include +/* + * Copyright © 2010 Behdad Esfahbod + * Copyright © 2011,2012 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, Rod Sheeter + */ -#include "hb-private.hh" -#include "hb-blob.h" +#include "main-font-text.hh" + +/* + * Command line interface to the harfbuzz font subsetter. + */ + +struct subset_consumer_t +{ + subset_consumer_t (option_parser_t *parser) + : failed (false), options(parser) {} + + void init (hb_buffer_t *buffer_, + const font_options_t *font_opts) + { + font = hb_font_reference (font_opts->get_font ()); + } + + void consume_line (const char *text, + unsigned int text_len, + const char *text_before, + const char *text_after) + { + } + + void finish (const font_options_t *font_opts) + { + hb_face_t *face = hb_font_get_face (font); + hb_blob_t *result = hb_face_reference_blob (face); + unsigned int data_length; + const char* data = hb_blob_get_data (result, &data_length); + + int fd_out = open(options.output_file, O_CREAT | O_WRONLY, S_IRWXU); + if (fd_out != -1) { + ssize_t bytes_written = write(fd_out, data, data_length); + if (bytes_written == -1) { + fprintf(stderr, "Unable to write output file"); + failed = true; + } else if (bytes_written != data_length) { + fprintf(stderr, "Wrong number of bytes written"); + failed = true; + } + } else { + fprintf(stderr, "Unable to open output file"); + failed = true; + } + + hb_blob_destroy (result); + hb_font_destroy (font); + } + + public: + bool failed; + + private: + output_options_t options; + hb_font_t *font; +}; int main (int argc, char **argv) { - int exit_code = 0; - - if (argc != 4) { - fprintf(stderr, "Must have 4 args\n"); - exit(1); - } - - int fd = open(argv[1], O_RDONLY); - if (fd == -1) { - perror("Unable to open font file"); - exit(1); - } - - void *mapped_file = MAP_FAILED; - int fd_out = -1; - - struct stat stat; - if (fstat(fd, &stat) != -1) { - - mapped_file = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (mapped_file == MAP_FAILED) { - perror("Failed to map file"); - } - } else { - perror("Unable to fstat"); - } - - if (mapped_file != MAP_FAILED) { - hb_blob_t *font_blob = hb_blob_create(static_cast(mapped_file), - stat.st_size, - HB_MEMORY_MODE_READONLY, nullptr, - nullptr); - - fd_out = open(argv[2], O_CREAT | O_WRONLY, S_IRWXU); - if (fd_out != -1) { - ssize_t bytes_written = write(fd_out, mapped_file, stat.st_size); - if (bytes_written == -1) { - perror("Unable to write output file"); - exit_code = 1; - } else if (bytes_written != stat.st_size) { - fprintf(stderr, "Wrong number of bytes written"); - exit_code = 1; - } - } else { - perror("Unable to open output file"); - exit_code = 1; - } - } - - if (mapped_file != MAP_FAILED) { - if (munmap(mapped_file, stat.st_size) == -1) { - perror("Unable to unmap file"); - exit_code = 1; - } - } - - if (fd_out != -1 && close(fd_out) == -1) { - perror("Unable to close output file"); - exit_code = 1; - } - - if (fd != -1 && close(fd) == -1) { - perror("Unable to close file"); - exit_code = 1; - } - - return exit_code; + main_font_text_t driver; + return driver.main (argc, argv); } From 06fe297f2a9fc6ee98179ddd26ef089b7fdb9e74 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Thu, 1 Feb 2018 18:36:15 -0800 Subject: [PATCH 21/25] Properly include subset test data files. --- test/subset/data/Makefile.am | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am index ecdab3764..f1234db85 100644 --- a/test/subset/data/Makefile.am +++ b/test/subset/data/Makefile.am @@ -7,9 +7,9 @@ SUBDIRS = EXTRA_DIST = \ $(TESTS) \ - expected/basics/*.ttf \ - fonts/*.ttf \ - profiles/*.txt \ + expected/basics \ + fonts \ + profiles \ $(NULL) # Convenience targets: From 5bc0cda179bca452145d4523eeba415986edb6e3 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 2 Feb 2018 17:49:14 -0800 Subject: [PATCH 22/25] Add missing unistd header to hb-subset. --- util/hb-subset.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/hb-subset.cc b/util/hb-subset.cc index f5e8aaf3e..3dda92194 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -25,6 +25,8 @@ * Google Author(s): Garret Rieger, Rod Sheeter */ +#include + #include "main-font-text.hh" /* From f83a43b56134188c2e1f3496d40ec0cd9109f250 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 2 Feb 2018 17:50:45 -0800 Subject: [PATCH 23/25] Add CMake configuration for all of the new subsetting code. --- CMakeLists.txt | 14 ++++++++++++++ src/Makefile.sources | 9 +++++++-- test/CMakeLists.txt | 1 + test/subset/CMakeLists.txt | 9 +++++++++ util/Makefile.am | 2 +- util/Makefile.sources | 2 +- 6 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 test/subset/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e067edf8..bfe0e3062 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,12 @@ add_prefix_to_list(HB_OT_sources "${PROJECT_SOURCE_DIR}/src/") extract_make_variable(HB_OT_headers ${SRCSOURCES}) add_prefix_to_list(HB_OT_headers "${PROJECT_SOURCE_DIR}/src/") +extract_make_variable(HB_SUBSET_sources ${SRCSOURCES}) +add_prefix_to_list(HB_SUBSET_sources "${PROJECT_SOURCE_DIR}/src/") + +extract_make_variable(HB_SUBSET_headers ${SRCSOURCES}) +add_prefix_to_list(HB_SUBSET_headers "${PROJECT_SOURCE_DIR}/src/") + extract_make_variable(HB_BASE_RAGEL_GENERATED_sources ${SRCSOURCES}) extract_make_variable(HB_OT_RAGEL_GENERATED_sources ${SRCSOURCES}) if (IN_HB_DIST) @@ -185,6 +191,8 @@ extract_make_variable(HB_VIEW_sources ${UTILSOURCES}) add_prefix_to_list(HB_VIEW_sources "${PROJECT_SOURCE_DIR}/util/") extract_make_variable(HB_SHAPE_sources ${UTILSOURCES}) add_prefix_to_list(HB_SHAPE_sources "${PROJECT_SOURCE_DIR}/util/") +extract_make_variable(HB_SUBSET_CLI_sources ${UTILSOURCES}) +add_prefix_to_list(HB_SUBSET_CLI_sources "${PROJECT_SOURCE_DIR}/util/") extract_make_variable(HB_OT_SHAPE_CLOSURE_sources ${UTILSOURCES}) add_prefix_to_list(HB_OT_SHAPE_CLOSURE_sources "${PROJECT_SOURCE_DIR}/util/") @@ -246,6 +254,8 @@ set (project_sources ${HB_FALLBACK_sources} ${HB_OT_sources} ${HB_OT_RAGEL_GENERATED_sources} + + ${HB_SUBSET_sources} ) set (project_extra_sources) @@ -255,6 +265,7 @@ set (project_headers ${HB_BASE_headers} ${HB_OT_headers} + ${HB_SUBSET_headers} ) @@ -708,6 +719,9 @@ if (HB_BUILD_UTILS) add_executable(hb-shape ${HB_SHAPE_sources}) target_link_libraries(hb-shape harfbuzz) + add_executable(hb-subset ${HB_SUBSET_CLI_sources}) + target_link_libraries(hb-subset harfbuzz) + add_executable(hb-ot-shape-closure ${HB_OT_SHAPE_CLOSURE_sources}) target_link_libraries(hb-ot-shape-closure harfbuzz) diff --git a/src/Makefile.sources b/src/Makefile.sources index f223fcfee..0b9beb961 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -182,8 +182,13 @@ HB_ICU_sources = hb-icu.cc HB_ICU_headers = hb-icu.h # Sources for libharfbuzz-subset -HB_SUBSET_sources = hb-subset.cc -HB_SUBSET_headers = hb-subset.h +HB_SUBSET_sources = \ + hb-subset.cc \ + $(NULL) + +HB_SUBSET_headers = \ + hb-subset.h \ + $(NULL) HB_GOBJECT_sources = hb-gobject-structs.cc HB_GOBJECT_STRUCTS_headers = hb-gobject-structs.h diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2c97f4f81..d2b199428 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(api) add_subdirectory(shaping) +add_subdirectory(subset) add_subdirectory(fuzzing) diff --git a/test/subset/CMakeLists.txt b/test/subset/CMakeLists.txt new file mode 100644 index 000000000..0a1e8f953 --- /dev/null +++ b/test/subset/CMakeLists.txt @@ -0,0 +1,9 @@ +if (HB_BUILD_UTILS) + file (READ "${CMAKE_CURRENT_SOURCE_DIR}/data/Makefile.sources" SOURCES) + extract_make_variable (TESTS ${SOURCES}) + foreach (test IN ITEMS ${TESTS}) + add_test (NAME ${test} + COMMAND python run-tests.py $ "data/${test}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + endforeach () +endif () diff --git a/util/Makefile.am b/util/Makefile.am index 283dd91ff..cd5e31cbc 100644 --- a/util/Makefile.am +++ b/util/Makefile.am @@ -46,7 +46,7 @@ endif # HAVE_FREETYPE hb_shape_SOURCES = $(HB_SHAPE_sources) bin_PROGRAMS += hb-shape -hb_subset_SOURCES = $(HB_SUBSET_sources) +hb_subset_SOURCES = $(HB_SUBSET_CLI_sources) bin_PROGRAMS += hb-subset if HAVE_OT diff --git a/util/Makefile.sources b/util/Makefile.sources index c4516ebcb..6c815d26b 100644 --- a/util/Makefile.sources +++ b/util/Makefile.sources @@ -29,7 +29,7 @@ HB_OT_SHAPE_CLOSURE_sources = \ main-font-text.hh \ $(NULL) -HB_SUBSET_sources = \ +HB_SUBSET_CLI_sources = \ hb-subset.cc \ options.cc \ options.hh \ From 7b01761adef6f64f1139b30c985aa5f52314073a Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Fri, 2 Feb 2018 17:54:11 -0800 Subject: [PATCH 24/25] Add CMakeLists.txt to dist files for subset test. --- test/subset/Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/test/subset/Makefile.am b/test/subset/Makefile.am index dfc312e65..336d33dfb 100644 --- a/test/subset/Makefile.am +++ b/test/subset/Makefile.am @@ -10,6 +10,7 @@ lib: @$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/src lib EXTRA_DIST += \ + CMakeLists.txt \ run-tests.py \ subset_test_suite.py \ $(NULL) From edcd3b80e9617ec8c4c4a55536938fb510b6aeba Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Sun, 4 Feb 2018 12:31:24 -0800 Subject: [PATCH 25/25] Actually call hb_subset --- util/hb-subset.cc | 53 +++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/util/hb-subset.cc b/util/hb-subset.cc index 3dda92194..808cb04f2 100644 --- a/util/hb-subset.cc +++ b/util/hb-subset.cc @@ -28,6 +28,7 @@ #include #include "main-font-text.hh" +#include "hb-subset.h" /* * Command line interface to the harfbuzz font subsetter. @@ -51,28 +52,44 @@ struct subset_consumer_t { } + hb_bool_t + write_file (const char *output_file, hb_blob_t *blob) { + unsigned int data_length; + const char* data = hb_blob_get_data (blob, &data_length); + + int fd_out = open(output_file, O_CREAT | O_WRONLY, S_IRWXU); + if (fd_out == -1) { + fprintf(stderr, "Unable to open output file"); + return false; + } + ssize_t bytes_written = write(fd_out, data, data_length); + if (bytes_written == -1) { + fprintf(stderr, "Unable to write output file\n"); + return false; + } + if (bytes_written != data_length) { + fprintf(stderr, "Expected %u bytes written, got %ld\n", data_length, + bytes_written); + return false; + } + return true; + } + void finish (const font_options_t *font_opts) { - hb_face_t *face = hb_font_get_face (font); - hb_blob_t *result = hb_face_reference_blob (face); - unsigned int data_length; - const char* data = hb_blob_get_data (result, &data_length); + // TODO(Q1) check for errors from creates and such + hb_subset_profile_t *subset_profile = hb_subset_profile_create(); + hb_subset_input_t *subset_input = hb_subset_input_create(); + hb_face_t *face = hb_font_get_face (font); + hb_subset_face_t *subset_face = hb_subset_face_create(face); - int fd_out = open(options.output_file, O_CREAT | O_WRONLY, S_IRWXU); - if (fd_out != -1) { - ssize_t bytes_written = write(fd_out, data, data_length); - if (bytes_written == -1) { - fprintf(stderr, "Unable to write output file"); - failed = true; - } else if (bytes_written != data_length) { - fprintf(stderr, "Wrong number of bytes written"); - failed = true; - } - } else { - fprintf(stderr, "Unable to open output file"); - failed = true; - } + hb_blob_t *result = nullptr; + failed = !(hb_subset(subset_profile, subset_input, subset_face, &result) + && write_file(options.output_file, result)); + hb_subset_profile_destroy (subset_profile); + hb_subset_input_destroy (subset_input); + hb_subset_face_destroy (subset_face); hb_blob_destroy (result); hb_font_destroy (font); }