Merge pull request #745 from googlefonts/master
Interface for hb_subset, skeleton for the hb-subset cli, and basic testing rigging.pull/754/head
commit
257022b178
25 changed files with 742 additions and 1 deletions
@ -0,0 +1,155 @@ |
||||
/*
|
||||
* 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_face_t *face; |
||||
}; |
||||
|
||||
|
||||
/**
|
||||
* 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) |
||||
{ |
||||
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; |
||||
} |
||||
|
||||
/**
|
||||
* hb_subset_face_destroy: |
||||
* |
||||
* Since: 1.7.5 |
||||
**/ |
||||
void |
||||
hb_subset_face_destroy(hb_subset_face_t *subset_face) |
||||
{ |
||||
if (!hb_object_destroy (subset_face)) return; |
||||
|
||||
hb_face_destroy(subset_face->face); |
||||
free (subset_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_face_reference_blob(face->face); |
||||
return true; |
||||
} |
@ -0,0 +1,89 @@ |
||||
/*
|
||||
* 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. |
||||
* Threadsafe for multiple concurrent subset operations. |
||||
*/ |
||||
|
||||
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 */ |
@ -1,3 +1,4 @@ |
||||
add_subdirectory(api) |
||||
add_subdirectory(shaping) |
||||
add_subdirectory(subset) |
||||
add_subdirectory(fuzzing) |
||||
|
@ -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[] = { 0, 0, 1, 0 }; |
||||
|
||||
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); |
||||
|
||||
hb_blob_t *output; |
||||
g_assert(hb_subset(profile, input, subset_face, &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); |
||||
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(); |
||||
} |
@ -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 () |
@ -0,0 +1,22 @@ |
||||
# 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
|
||||
|
||||
EXTRA_DIST += \
|
||||
CMakeLists.txt \
|
||||
run-tests.py \
|
||||
subset_test_suite.py \
|
||||
$(NULL)
|
||||
|
||||
CLEANFILES += \
|
||||
subset_test_suite.py[co] \
|
||||
$(NULL)
|
||||
|
||||
-include $(top_srcdir)/git.mk |
@ -0,0 +1,23 @@ |
||||
# Process this file with automake to produce Makefile.in
|
||||
|
||||
NULL =
|
||||
EXTRA_DIST =
|
||||
CLEANFILES =
|
||||
SUBDIRS =
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(TESTS) \
|
||||
expected/basics \
|
||||
fonts \
|
||||
profiles \
|
||||
$(NULL)
|
||||
|
||||
# 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 |
@ -0,0 +1,9 @@ |
||||
TESTS = \
|
||||
tests/basics.tests \
|
||||
$(NULL)
|
||||
|
||||
XFAIL_TESTS = \
|
||||
$(NULL)
|
||||
|
||||
DISABLED_TESTS = \
|
||||
$(NULL)
|
Binary file not shown.
Binary file not shown.
@ -0,0 +1,8 @@ |
||||
FONTS: |
||||
Roboto-Regular.abc.ttf |
||||
|
||||
PROFILES: |
||||
default.txt |
||||
|
||||
SUBSETS: |
||||
b |
@ -0,0 +1,41 @@ |
||||
#!/usr/bin/env python |
||||
|
||||
# Pre-generates the expected output subset files (via fonttools) for |
||||
# specified subset test suite(s). |
||||
|
||||
import io |
||||
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 io.open(path, mode="r", encoding="utf-8") 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)) |
@ -0,0 +1,86 @@ |
||||
#!/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 |
||||
import tempfile |
||||
|
||||
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 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): |
||||
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())) |
||||
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 |
||||
|
||||
|
||||
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.") |
@ -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.") |
@ -0,0 +1,110 @@ |
||||
/*
|
||||
* 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 <unistd.h> |
||||
|
||||
#include "main-font-text.hh" |
||||
#include "hb-subset.h" |
||||
|
||||
/*
|
||||
* Command line interface to the harfbuzz font subsetter. |
||||
*/ |
||||
|
||||
struct subset_consumer_t |
||||
{ |
||||
subset_consumer_t (option_parser_t *parser) |
||||
: failed (false), options(parser) {} |
||||
|
||||
void init (hb_buffer_t *buffer_, |
||||
const font_options_t *font_opts) |
||||
{ |
||||
font = hb_font_reference (font_opts->get_font ()); |
||||
} |
||||
|
||||
void consume_line (const char *text, |
||||
unsigned int text_len, |
||||
const char *text_before, |
||||
const char *text_after) |
||||
{ |
||||
} |
||||
|
||||
hb_bool_t
|
||||
write_file (const char *output_file, hb_blob_t *blob) {
|
||||
unsigned int data_length; |
||||
const char* data = hb_blob_get_data (blob, &data_length); |
||||
|
||||
int fd_out = open(output_file, O_CREAT | O_WRONLY, S_IRWXU); |
||||
if (fd_out == -1) { |
||||
fprintf(stderr, "Unable to open output file"); |
||||
return false; |
||||
} |
||||
ssize_t bytes_written = write(fd_out, data, data_length); |
||||
if (bytes_written == -1) { |
||||
fprintf(stderr, "Unable to write output file\n"); |
||||
return false; |
||||
} |
||||
if (bytes_written != data_length) { |
||||
fprintf(stderr, "Expected %u bytes written, got %ld\n", data_length,
|
||||
bytes_written); |
||||
return false; |
||||
}
|
||||
return true; |
||||
} |
||||
|
||||
void finish (const font_options_t *font_opts) |
||||
{ |
||||
// 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); |
||||
} |
||||
|
||||
public: |
||||
bool failed; |
||||
|
||||
private: |
||||
output_options_t options; |
||||
hb_font_t *font; |
||||
}; |
||||
|
||||
int |
||||
main (int argc, char **argv) |
||||
{ |
||||
main_font_text_t<subset_consumer_t, 10, 0> driver; |
||||
return driver.main (argc, argv); |
||||
} |
Loading…
Reference in new issue