[HarfBuzz] harfbuzz: Branch 'master' - 28 commits

Behdad Esfahbod behdad at kemper.freedesktop.org
Mon Feb 5 01:20:33 UTC 2018


 CMakeLists.txt                                                     |   14 
 configure.ac                                                       |    2 
 src/Makefile.am                                                    |   17 -
 src/Makefile.sources                                               |    9 
 src/harfbuzz-subset.pc.in                                          |   12 
 src/hb-subset.cc                                                   |  155 ++++++++++
 src/hb-subset.h                                                    |   89 +++++
 src/hb.h                                                           |    1 
 test/CMakeLists.txt                                                |    1 
 test/Makefile.am                                                   |    2 
 test/api/Makefile.am                                               |    1 
 test/api/test-subset.c                                             |   67 ++++
 test/subset/CMakeLists.txt                                         |    9 
 test/subset/Makefile.am                                            |   22 +
 test/subset/data/Makefile.am                                       |   23 +
 test/subset/data/Makefile.sources                                  |    9 
 test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf |binary
 test/subset/data/fonts/Roboto-Regular.abc.ttf                      |binary
 test/subset/data/tests/basics.tests                                |    8 
 test/subset/generate-expected-outputs.py                           |   41 ++
 test/subset/run-tests.py                                           |   86 +++++
 test/subset/subset_test_suite.py                                   |   82 +++++
 util/Makefile.am                                                   |    5 
 util/Makefile.sources                                              |    7 
 util/hb-subset.cc                                                  |  110 +++++++
 25 files changed, 767 insertions(+), 5 deletions(-)

New commits:
commit ae39fc8171b2c26d4ae64954a8055732c9241bcc
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Feb 4 20:18:52 2018 -0500

    [subset] Build hb-subset into separate libharfbuzz-subset.so

diff --git a/src/Makefile.am b/src/Makefile.am
index dd1c7ae6..59ca6482 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -27,9 +27,7 @@ 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
@@ -137,6 +135,15 @@ pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = harfbuzz.pc
 EXTRA_DIST += harfbuzz.pc.in
 
+lib_LTLIBRARIES += libharfbuzz-subset.la
+libharfbuzz_subset_la_SOURCES = $(HB_SUBSET_sources)
+libharfbuzz_subset_la_CPPFLAGS = $(libharfbuzz_la_CPPFLAGS)
+libharfbuzz_subset_la_LDFLAGS = $(libharfbuzz_la_LDFLAGS)
+libharfbuzz_subset_la_LIBADD = libharfbuzz.la
+pkginclude_HEADERS += $(HB_SUBSET_headers)
+pkgconfig_DATA += harfbuzz-subset.pc
+EXTRA_DIST += harfbuzz-subset.pc.in
+
 FUZZING_CPPFLAGS = \
 	-DHB_NDEBUG \
 	-DHB_MAX_NESTING_LEVEL=3 \
@@ -166,8 +173,8 @@ HBHEADERS += $(HB_ICU_headers)
 else
 lib_LTLIBRARIES += libharfbuzz-icu.la
 libharfbuzz_icu_la_SOURCES = $(HB_ICU_sources)
-libharfbuzz_icu_la_CPPFLAGS = $(ICU_CFLAGS)
-libharfbuzz_icu_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
+libharfbuzz_icu_la_CPPFLAGS = $(libharfbuzz_la_CPPFLAGS) $(ICU_CFLAGS)
+libharfbuzz_icu_la_LDFLAGS = $(libharfbuzz_la_LDFLAGS)
 libharfbuzz_icu_la_LIBADD = $(ICU_LIBS) libharfbuzz.la
 pkginclude_HEADERS += $(HB_ICU_headers)
 pkgconfig_DATA += harfbuzz-icu.pc
@@ -179,8 +186,8 @@ if HAVE_GOBJECT
 lib_LTLIBRARIES += libharfbuzz-gobject.la
 libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_sources)
 nodist_libharfbuzz_gobject_la_SOURCES = $(HB_GOBJECT_ENUM_sources)
-libharfbuzz_gobject_la_CPPFLAGS = $(GOBJECT_CFLAGS)
-libharfbuzz_gobject_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) -no-undefined
+libharfbuzz_gobject_la_CPPFLAGS = $(libharfbuzz_la_CPPFLAGS) $(GOBJECT_CFLAGS)
+libharfbuzz_gobject_la_LDFLAGS = $(libharfbuzz_la_LDFLAGS)
 libharfbuzz_gobject_la_LIBADD = $(GOBJECT_LIBS) libharfbuzz.la
 pkginclude_HEADERS += $(HB_GOBJECT_headers)
 nodist_pkginclude_HEADERS += $(HB_GOBJECT_ENUM_headers)
diff --git a/src/harfbuzz-subset.pc.in b/src/harfbuzz-subset.pc.in
new file mode 100644
index 00000000..5da64b3f
--- /dev/null
+++ b/src/harfbuzz-subset.pc.in
@@ -0,0 +1,12 @@
+prefix=%prefix%
+exec_prefix=%exec_prefix%
+libdir=%libdir%
+includedir=%includedir%
+
+Name: harfbuzz
+Description: HarfBuzz font subsetter
+Version: %VERSION%
+
+Requires: harfbuzz
+Libs: -L${libdir} -lharfbuzz-subset
+Cflags: -I${includedir}/harfbuzz
diff --git a/util/Makefile.am b/util/Makefile.am
index 283dd91f..3810e15e 100644
--- a/util/Makefile.am
+++ b/util/Makefile.am
@@ -47,6 +47,8 @@ hb_shape_SOURCES = $(HB_SHAPE_sources)
 bin_PROGRAMS += hb-shape
 
 hb_subset_SOURCES = $(HB_SUBSET_sources)
+hb_subset_SOURCES = $(HB_SUBSET_sources)
+hb_subset_LDADD = $(LDADD) $(top_builddir)/src/libharfbuzz-subset.la
 bin_PROGRAMS += hb-subset
 
 if HAVE_OT
commit eac20e459564134e0087497ed76b1c84195ad7e3
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Feb 4 20:08:50 2018 -0500

    [subset] Minor

diff --git a/util/Makefile.am b/util/Makefile.am
index cd5e31cb..283dd91f 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_CLI_sources)
+hb_subset_SOURCES = $(HB_SUBSET_sources)
 bin_PROGRAMS += hb-subset
 
 if HAVE_OT
diff --git a/util/Makefile.sources b/util/Makefile.sources
index 6c815d26..c4516ebc 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_CLI_sources = \
+HB_SUBSET_sources = \
 	hb-subset.cc \
 	options.cc \
 	options.hh \
diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index 808cb04f..21d0f767 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -52,8 +52,8 @@ struct subset_consumer_t
   {
   }
 
-  hb_bool_t 
-  write_file (const char *output_file, hb_blob_t *blob) {    
+  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);
 
@@ -68,10 +68,10 @@ struct subset_consumer_t
       return false;
     }
     if (bytes_written != data_length) {
-      fprintf(stderr, "Expected %u bytes written, got %ld\n", data_length, 
+      fprintf(stderr, "Expected %u bytes written, got %ld\n", data_length,
               bytes_written);
       return false;
-    } 
+    }
     return true;
   }
 
@@ -80,7 +80,7 @@ struct subset_consumer_t
     // 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_face_t *face = hb_font_get_face (font);
     hb_subset_face_t *subset_face = hb_subset_face_create(face);
 
     hb_blob_t *result = nullptr;
commit 257022b1789f928975b04b5d214bbe9192e11997
Merge: 3615f344 edcd3b80
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Sun Feb 4 20:01:35 2018 -0500

    Merge pull request #745 from googlefonts/master
    
    Interface for hb_subset, skeleton for the hb-subset cli, and basic testing rigging.

commit edcd3b80e9617ec8c4c4a55536938fb510b6aeba
Author: Rod Sheeter <rsheeter at google.com>
Date:   Sun Feb 4 12:31:24 2018 -0800

    Actually call hb_subset

diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index 3dda9219..808cb04f 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -28,6 +28,7 @@
 #include <unistd.h>
 
 #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
   {
   }
 
-  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);
+  hb_bool_t 
+  write_file (const char *output_file, hb_blob_t *blob) {    
     unsigned int data_length;
-    const char* data = hb_blob_get_data (result, &data_length);
+    const char* data = hb_blob_get_data (blob, &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 {
+    int fd_out = open(output_file, O_CREAT | O_WRONLY, S_IRWXU);
+    if (fd_out == -1) {
       fprintf(stderr, "Unable to open output file");
-      failed = true;
+      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)
+  {
+    // 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);
+
+    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);
   }
commit 7b01761adef6f64f1139b30c985aa5f52314073a
Author: Garret Rieger <grieger at google.com>
Date:   Fri Feb 2 17:54:11 2018 -0800

    Add CMakeLists.txt to dist files for subset test.

diff --git a/test/subset/Makefile.am b/test/subset/Makefile.am
index dfc312e6..336d33df 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)
commit f83a43b56134188c2e1f3496d40ec0cd9109f250
Author: Garret Rieger <grieger at google.com>
Date:   Fri Feb 2 17:50:45 2018 -0800

    Add CMake configuration for all of the new subsetting code.

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9e067edf..bfe0e306 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 f223fcfe..0b9beb96 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 2c97f4f8..d2b19942 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 00000000..0a1e8f95
--- /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 $<TARGET_FILE:hb-subset> "data/${test}"
+      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+  endforeach ()
+endif ()
diff --git a/util/Makefile.am b/util/Makefile.am
index 283dd91f..cd5e31cb 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 c4516ebc..6c815d26 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 \
commit 5bc0cda179bca452145d4523eeba415986edb6e3
Author: Garret Rieger <grieger at google.com>
Date:   Fri Feb 2 17:49:14 2018 -0800

    Add missing unistd header to hb-subset.

diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index f5e8aaf3..3dda9219 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -25,6 +25,8 @@
  * Google Author(s): Garret Rieger, Rod Sheeter
  */
 
+#include <unistd.h>
+
 #include "main-font-text.hh"
 
 /*
commit 06fe297f2a9fc6ee98179ddd26ef089b7fdb9e74
Author: Garret Rieger <grieger at google.com>
Date:   Thu Feb 1 18:36:15 2018 -0800

    Properly include subset test data files.

diff --git a/test/subset/data/Makefile.am b/test/subset/data/Makefile.am
index ecdab376..f1234db8 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:
commit 058b1260ad1105d0d8d6bf21f8f65a336e735bd2
Author: Garret Rieger <grieger at google.com>
Date:   Thu Feb 1 18:22:14 2018 -0800

    Re-write hb-subset utility to use main-font-text driver.

diff --git a/test/subset/run-tests.py b/test/subset/run-tests.py
index 0b4b96a6..b0054802 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 94a5fa8a..c4516ebc 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 4beb6a1c..f5e8aaf3 100644
--- a/util/hb-subset.cc
+++ b/util/hb-subset.cc
@@ -1,82 +1,91 @@
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
+/*
+ * 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"
 
-int
-main (int argc, char **argv)
-{
-  int exit_code = 0;
+/*
+ * Command line interface to the harfbuzz font subsetter.
+ */
 
-  if (argc != 4) {
-    fprintf(stderr, "Must have 4 args\n");
-    exit(1);
-  }
+struct subset_consumer_t
+{
+  subset_consumer_t (option_parser_t *parser)
+      : failed (false), options(parser) {}
 
-  int fd = open(argv[1], O_RDONLY);
-  if (fd == -1) {
-    perror("Unable to open font file");
-    exit(1);
+  void init (hb_buffer_t  *buffer_,
+             const font_options_t *font_opts)
+  {
+    font = hb_font_reference (font_opts->get_font ());
   }
 
-  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");
+  void consume_line (const char   *text,
+                     unsigned int  text_len,
+                     const char   *text_before,
+                     const char   *text_after)
+  {
   }
 
-  if (mapped_file != MAP_FAILED) {
-    hb_blob_t *font_blob = hb_blob_create(static_cast<const char*>(mapped_file),
-                                          stat.st_size,
-                                          HB_MEMORY_MODE_READONLY, nullptr,
-                                          nullptr);
+  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);
 
-    fd_out = open(argv[2], O_CREAT | O_WRONLY, S_IRWXU);
+    int fd_out = open(options.output_file, O_CREAT | O_WRONLY, S_IRWXU);
     if (fd_out != -1) {
-      ssize_t bytes_written = write(fd_out, mapped_file, stat.st_size);
+      ssize_t bytes_written = write(fd_out, data, data_length);
       if (bytes_written == -1) {
-        perror("Unable to write output file");
-        exit_code = 1;
-      } else if (bytes_written != stat.st_size) {
+        fprintf(stderr, "Unable to write output file");
+        failed = true;
+      } else if (bytes_written != data_length) {
         fprintf(stderr, "Wrong number of bytes written");
-        exit_code = 1;
+        failed = true;
       }
     } else {
-      perror("Unable to open output file");
-      exit_code = 1;
+      fprintf(stderr, "Unable to open output file");
+      failed = true;
     }
-  }
 
-  if (mapped_file != MAP_FAILED) {
-    if (munmap(mapped_file, stat.st_size) == -1) {
-      perror("Unable to unmap file");
-      exit_code = 1;
-    }
+    hb_blob_destroy (result);
+    hb_font_destroy (font);
   }
 
-  if (fd_out != -1 && close(fd_out) == -1) {
-    perror("Unable to close output file");
-    exit_code = 1;
-  }
+  public:
+  bool failed;
 
-  if (fd != -1 && close(fd) == -1) {
-    perror("Unable to close file");
-    exit_code = 1;
-  }
+  private:
+  output_options_t options;
+  hb_font_t *font;
+};
 
-  return exit_code;
+int
+main (int argc, char **argv)
+{
+  main_font_text_t<subset_consumer_t, 10, 0> driver;
+  return driver.main (argc, argv);
 }
commit ede84ffa426edb950c4ec4f89833c85475a1c64f
Author: Garret Rieger <grieger at google.com>
Date:   Thu Feb 1 17:17:36 2018 -0800

    Whitespace

diff --git a/util/hb-subset.cc b/util/hb-subset.cc
index 5aafbe19..4beb6a1c 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<const char*>(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<const char*>(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) {
commit 2763a2c5982c0db072697abe8ba01342d5977237
Author: Garret Rieger <grieger at google.com>
Date:   Thu Feb 1 17:14:51 2018 -0800

    Include subset test files in distribution.

diff --git a/test/subset/Makefile.am b/test/subset/Makefile.am
index 3037261b..dfc312e6 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 c74f7fd8..ecdab376 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
commit 34fa7b3ad23b544b0914bc6002d60525224c68e1
Author: Garret Rieger <grieger at google.com>
Date:   Thu Feb 1 16:50:18 2018 -0800

    Whitespace

diff --git a/src/hb-subset.h b/src/hb-subset.h
index 7ceac9a7..84c0c3c2 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
 
commit 8c3a6727377895f18e1b5c7076404d8aede17176
Author: Garret Rieger <grieger at google.com>
Date:   Wed Jan 31 15:43:24 2018 -0800

    Get test-subset to pass.

diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index 00146b9d..62f7f0ad 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;
 }
commit 74d39ed2639857d5f1a90d9c0d864227a6482b40
Author: Garret Rieger <grieger at google.com>
Date:   Wed Jan 31 15:20:52 2018 -0800

    Attach add a hb_face_t to hb_subset_face_t.

diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index cabae4c4..00146b9d 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<hb_subset_face_t>();
+  if (unlikely (!face))
+    face = hb_face_get_empty();
+
+  hb_subset_face_t *subset_face = hb_object_create<hb_subset_face_t> ();
+  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);
 }
 
 /**
commit 76b84c36b9560e132918adb4c0c5a0d9bdfb0978
Author: Garret Rieger <grieger at google.com>
Date:   Wed Jan 31 14:53:28 2018 -0800

    Whitespace

diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index 3a4eacae..99849fc9 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)
commit a2965f2ea5428c19be54f998ef9152c5ff6975ea
Author: Garret Rieger <grieger at google.com>
Date:   Wed Jan 31 14:53:09 2018 -0800

    Add a basic implementation of hb-subset to enable compilation of test-subset.

diff --git a/src/hb-subset.cc b/src/hb-subset.cc
index e69de29b..cabae4c4 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_t>();
+}
+
+/**
+ * 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_t>();
+}
+
+/**
+ * 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_t>();
+}
+
+/**
+ * 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 b7c1f569..7ceac9a7 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 e22d726a..3a4eacae 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 85b302d7..b6986ce2 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);
commit 38af23b8df1a84f24d379d27d1a1e20f9ce07f34
Author: Garret Rieger <grieger at google.com>
Date:   Wed Jan 31 11:32:23 2018 -0800

    Make the expected output for subsetting basics test be equal to the input file for now.

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 8e44886f..9d791f7f 100644
Binary files a/test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf and b/test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf differ
commit b59c08eb045db2b6c952de81510b8159a4f72fb1
Author: Garret Rieger <grieger at google.com>
Date:   Wed Jan 31 11:14:53 2018 -0800

    Add the beginning of a unit test for hb-subset

diff --git a/test/api/test-subset.c b/test/api/test-subset.c
new file mode 100644
index 00000000..85b302d7
--- /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();
+}
commit e9d154ac8ddd2712a34c53c95a17e469f95e5b30
Author: Rod Sheeter <rsheeter at google.com>
Date:   Tue Jan 30 19:27:11 2018 -0800

    tweak test failure output. write to a temp file not stdout. test still fails because expected is not just an identical copy of input

diff --git a/test/subset/run-tests.py b/test/subset/run-tests.py
index 90da9624..0b4b96a6 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
-
-	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 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()))
+	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 ad7ceb14..5aafbe19 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<char*>(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<const char*>(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;
 }
commit cf403e1a53381f293aceac5cdbe031bbb2a7af77
Author: Garret Rieger <grieger at google.com>
Date:   Tue Jan 30 18:40:23 2018 -0800

    Add hb-subset.h to hb.h

diff --git a/src/hb.h b/src/hb.h
index 7402034f..e55decfe 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"
 
commit c02573516c05ac97acb243ef5dec26af86086ded
Author: Garret Rieger <grieger at google.com>
Date:   Tue Jan 30 18:39:41 2018 -0800

    Fix typo in hb-subset.h

diff --git a/src/hb-subset.h b/src/hb-subset.h
index 8aaccd6e..b7c1f569 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
commit 76351518ca9bc88aa6fbc975e1e35bd86432d652
Author: Garret Rieger <grieger at google.com>
Date:   Tue Jan 30 14:03:16 2018 -0800

    Remove basic subset test from XFAIL

diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources
index 00bd2b9f..37550b63 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 = \
commit b029b7c19a733a2a39860238ad300e6c4a3f7802
Author: Garret Rieger <grieger at google.com>
Date:   Mon Jan 29 13:31:49 2018 -0800

    Whitespace

diff --git a/test/subset/data/Makefile.sources b/test/subset/data/Makefile.sources
index 574f9466..00bd2b9f 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)
commit 0853260e997aded264f42bb369d4fcb39cccb7d6
Author: Garret Rieger <grieger at google.com>
Date:   Mon Jan 29 13:30:02 2018 -0800

    Configure automake to run the new subset tests.

diff --git a/configure.ac b/configure.ac
index dec994eb..12401f00 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 ad496f55..66b3e6e2 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 00000000..3037261b
--- /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 00000000..c74f7fd8
--- /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 00000000..574f9466
--- /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
commit 5c63c37b2b5aba8bf2f8ff35b7da0d116ebfe8b5
Author: Garret Rieger <grieger at google.com>
Date:   Fri Jan 26 16:57:42 2018 -0800

    WIP test runner for subset tests.

diff --git a/test/subset/run-tests.py b/test/subset/run-tests.py
new file mode 100755
index 00000000..90da9624
--- /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.")
commit cc46cd88a1b84b02694fa6d88c4286e93336f096
Author: Garret Rieger <grieger at google.com>
Date:   Fri Jan 26 14:25:39 2018 -0800

    In generate-expected-outputs read the test definition with utf8 encoding.

diff --git a/test/subset/generate-expected-outputs.py b/test/subset/generate-expected-outputs.py
index 87db5d5b..f6636de7 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()
 
commit 4cdae914e2e2fff1ff91e2f42648a8acb82a5494
Author: Garret Rieger <grieger at google.com>
Date:   Fri Jan 26 13:57:48 2018 -0800

    Add basic directory structure for subsetter integration tests. Plus a utility for generating expected output files.

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 00000000..8e44886f
Binary files /dev/null and b/test/subset/data/expected/basics/Roboto-Regular.abc.default.62.ttf differ
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 00000000..9d791f7f
Binary files /dev/null and b/test/subset/data/fonts/Roboto-Regular.abc.ttf differ
diff --git a/test/subset/data/profiles/default.txt b/test/subset/data/profiles/default.txt
new file mode 100644
index 00000000..e69de29b
diff --git a/test/subset/data/tests/basics.txt b/test/subset/data/tests/basics.txt
new file mode 100644
index 00000000..8a7246b9
--- /dev/null
+++ b/test/subset/data/tests/basics.txt
@@ -0,0 +1,8 @@
+FONTS:
+Roboto-Regular.abc.ttf
+
+PROFILES:
+default.txt
+
+SUBSETS:
+b
diff --git a/test/subset/generate-expected-outputs.py b/test/subset/generate-expected-outputs.py
new file mode 100755
index 00000000..87db5d5b
--- /dev/null
+++ b/test/subset/generate-expected-outputs.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+# Pre-generates the expected output subset files (via fonttools) for
+# specified subset test suite(s).
+
+import os
+import sys
+
+from subprocess import check_call
+from subset_test_suite import SubsetTestSuite
+
+
+def usage():
+	print "Usage: generate-expected-outputs.py <test suite file> ..."
+
+
+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 00000000..256e2071
--- /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.")
commit 9ccb8366f603a9e4a7a3c3f96420a19d4f6fb390
Author: Rod Sheeter <rsheeter at google.com>
Date:   Wed Jan 17 22:09:07 2018 -0800

    Start to sketch APIs for subsetting

diff --git a/src/Makefile.am b/src/Makefile.am
index 833d1f95..dd1c7ae6 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 213aa22f..f223fcfe 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 00000000..e69de29b
diff --git a/src/hb-subset.h b/src/hb-subset.h
new file mode 100644
index 00000000..8aaccd6e
--- /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 <hb.h> 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 e6620a23..283dd91f 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 d6c00cc4..94a5fa8a 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 00000000..ad7ceb14
--- /dev/null
+++ b/util/hb-subset.cc
@@ -0,0 +1,61 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#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<char*>(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;
+}


More information about the HarfBuzz mailing list