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 <beh...@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 <beh...@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 <beh...@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 <rshee...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <rshee...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <grie...@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 <rshee...@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; +} _______________________________________________ HarfBuzz mailing list HarfBuzz@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/harfbuzz