Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
main.cc
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <hb-subset.h>
6
7#include <cstdlib>
8#include <fstream>
9#include <iostream>
10#include <limits>
11#include <set>
12#include <string>
13
14#include "hb_wrappers.h"
15
16hb_codepoint_t ParseCodepoint(std::string_view arg, bool& optional) {
17 constexpr std::string_view kOptionalPrefix = "optional:";
18 if (arg.substr(0, kOptionalPrefix.length()) == kOptionalPrefix) {
19 optional = true;
20 arg = arg.substr(kOptionalPrefix.length());
21 } else {
22 optional = false;
23 }
24 uint64_t value = 0;
25 // Check for \u123, u123, otherwise let strtoul work it out.
26 if (arg[0] == 'u') {
27 value = strtoul(arg.data() + 1, nullptr, 16);
28 } else if (arg[0] == '\\' && arg[1] == 'u') {
29 value = strtoul(arg.data() + 2, nullptr, 16);
30 } else {
31 value = strtoul(arg.data(), nullptr, 0);
32 }
33 if (value == 0 || value > std::numeric_limits<hb_codepoint_t>::max()) {
34 std::cerr << "The value '" << arg << "' (" << value
35 << ") could not be parsed as a valid unicode codepoint; aborting."
36 << std::endl;
37 exit(-1);
38 }
39 return value;
40}
41
42void Usage() {
43 std::cout << "Usage:" << std::endl;
44 std::cout << "font-subset <output.ttf> <input.ttf>" << std::endl;
45 std::cout << std::endl;
46 std::cout << "The output.ttf file will be overwritten if it exists already "
47 "and the subsetting operation succeeds."
48 << std::endl;
49 std::cout << "Codepoints should be specified on stdin, separated by spaces, "
50 "and must be input as decimal numbers (123), hexadecimal "
51 "numbers (0x7B), or unicode hexadecimal characters (\\u7B)."
52 << std::endl;
53 std::cout << "Codepoints can be prefixed by the string \"optional:\" to "
54 "specify that the codepoint can be omitted if it isn't found "
55 "in the input font file."
56 << std::endl;
57 std::cout << "Input terminates with a newline." << std::endl;
58 std::cout
59 << "This program will de-duplicate codepoints if the same codepoint is "
60 "specified multiple times, e.g. '123 123' will be treated as '123'."
61 << std::endl;
62}
63
64template <typename...>
65using void_t = void;
66template <typename T, typename = void>
68 // This is the HarfBuzz 3.0 interface.
69 static HarfbuzzWrappers::HbFacePtr Make(hb_face_t* face, T input) {
70 // The prior version of harfbuzz automatically dropped layout tables,
71 // but in the new version they are kept by default. So re-add them to the
72 // drop list to retain the same behaviour.
73 if (!hb_ot_var_has_data(face) || hb_ot_var_get_axis_count(face) == 0) {
74 // we can only drop GSUB/GPOS/GDEF for non variable fonts, they may be
75 // needed for variable fonts (guessing we need to keep all of these, but
76 // in Material Symbols Icon variable fonts if we drop the GSUB table (they
77 // do not have GPOS/DEF) then the Fill=1,Weight=100 variation is rendered
78 // incorrect. (and other variations are probably less noticibly
79 // incorrect))
80 hb_set_add(hb_subset_input_set(input, HB_SUBSET_SETS_DROP_TABLE_TAG),
81 HB_TAG('G', 'S', 'U', 'B'));
82 hb_set_add(hb_subset_input_set(input, HB_SUBSET_SETS_DROP_TABLE_TAG),
83 HB_TAG('G', 'P', 'O', 'S'));
84 hb_set_add(hb_subset_input_set(input, HB_SUBSET_SETS_DROP_TABLE_TAG),
85 HB_TAG('G', 'D', 'E', 'F'));
86 }
87 return HarfbuzzWrappers::HbFacePtr(hb_subset_or_fail(face, input));
88 }
89};
90
91int main(int argc, char** argv) {
92 if (argc != 3) {
93 Usage();
94 return -1;
95 }
96 std::string output_file_path(argv[1]);
97 std::string input_file_path(argv[2]);
98 std::cout << "Using output file: " << output_file_path << std::endl;
99 std::cout << "Using source file: " << input_file_path << std::endl;
100
102 hb_blob_create_from_file(input_file_path.c_str()));
103 if (!hb_blob_get_length(font_blob.get())) {
104 std::cerr << "Failed to load input font " << input_file_path
105 << "; aborting. This error indicates that the font is invalid or "
106 "the current version of Harfbuzz is unable to process it."
107 << std::endl;
108 return -1;
109 }
110
111 HarfbuzzWrappers::HbFacePtr font_face(hb_face_create(font_blob.get(), 0));
112 if (font_face.get() == hb_face_get_empty()) {
113 std::cerr << "Failed to load input font face " << input_file_path
114 << "; aborting. This error indicates that the font is invalid or "
115 "the current version of Harfbuzz is unable to process it."
116 << std::endl;
117 return -1;
118 }
119
120 HarfbuzzWrappers::HbSubsetInputPtr input(hb_subset_input_create_or_fail());
121 {
122 hb_set_t* desired_codepoints = hb_subset_input_unicode_set(input.get());
123 HarfbuzzWrappers::HbSetPtr actual_codepoints(hb_set_create());
124 hb_face_collect_unicodes(font_face.get(), actual_codepoints.get());
125 std::string raw_codepoint;
126 while (std::cin >> raw_codepoint) {
127 bool optional = false;
128 auto codepoint =
129 ParseCodepoint(std::string_view(raw_codepoint), optional);
130 if (!codepoint) {
131 std::cerr << "Invalid codepoint for " << raw_codepoint << "; exiting."
132 << std::endl;
133 return -1;
134 }
135
136 if (!hb_set_has(actual_codepoints.get(), codepoint)) {
137 if (optional) {
138 // Code point is optional, so omit it.
139 continue;
140 }
141
142 std::cerr << "Codepoint " << raw_codepoint
143 << " not found in font, aborting." << std::endl;
144 return -1;
145 }
146 hb_set_add(desired_codepoints, codepoint);
147 }
148 if (hb_set_is_empty(desired_codepoints)) {
149 std::cerr << "No codepoints specified, exiting." << std::endl;
150 return -1;
151 }
152 }
153
155 HarfBuzzSubset<hb_subset_input_t*>::Make(font_face.get(), input.get());
156
157 if (!new_face || new_face.get() == hb_face_get_empty()) {
158 std::cerr
159 << "Failed to subset font; aborting. This error normally indicates "
160 "the current version of Harfbuzz is unable to process it."
161 << std::endl;
162 return -1;
163 }
164
165 HarfbuzzWrappers::HbBlobPtr result(hb_face_reference_blob(new_face.get()));
166 if (!hb_blob_get_length(result.get())) {
167 std::cerr << "Failed get new font bytes; aborting. This error may indicate "
168 "low availability of memory or a bug in Harfbuzz."
169 << std::endl;
170 return -1;
171 }
172
173 unsigned int data_length;
174 const char* data = hb_blob_get_data(result.get(), &data_length);
175
176 std::ofstream output_font_file;
177 output_font_file.open(output_file_path,
178 std::ios::out | std::ios::trunc | std::ios::binary);
179 if (!output_font_file.is_open()) {
180 std::cerr << "Failed to open output file '" << output_file_path
181 << "'. The parent directory may not exist, or the user does not "
182 "have permission to create this file."
183 << std::endl;
184 return -1;
185 }
186 output_font_file.write(data, data_length);
187 output_font_file.flush();
188 output_font_file.close();
189
190 std::cout << "Wrote " << data_length << " bytes to " << output_file_path
191 << std::endl;
192 return 0;
193}
uint8_t value
GAsyncResult * result
char ** argv
Definition library.h:9
std::unique_ptr< hb_subset_input_t, hb_subset_input_deleter > HbSubsetInputPtr
Definition hb_wrappers.h:32
std::unique_ptr< hb_blob_t, hb_blob_deleter > HbBlobPtr
Definition hb_wrappers.h:29
std::unique_ptr< hb_face_t, hb_face_deleter > HbFacePtr
Definition hb_wrappers.h:30
std::unique_ptr< hb_set_t, hb_set_deleter > HbSetPtr
Definition hb_wrappers.h:33
Definition main.py:1
#define T
static HarfbuzzWrappers::HbFacePtr Make(hb_face_t *face, T input)
Definition main.cc:69
void Usage()
Definition main.cc:42
hb_codepoint_t ParseCodepoint(std::string_view arg, bool &optional)
Definition main.cc:16
void void_t
Definition main.cc:65