17 constexpr std::string_view kOptionalPrefix =
"optional:";
18 if (arg.substr(0, kOptionalPrefix.length()) == kOptionalPrefix) {
20 arg = arg.substr(kOptionalPrefix.length());
27 value = strtoul(arg.data() + 1,
nullptr, 16);
28 }
else if (arg[0] ==
'\\' && arg[1] ==
'u') {
29 value = strtoul(arg.data() + 2,
nullptr, 16);
31 value = strtoul(arg.data(),
nullptr, 0);
34 std::cerr <<
"The value '" << arg <<
"' (" <<
value
35 <<
") could not be parsed as a valid unicode codepoint; aborting."
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."
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)."
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."
57 std::cout <<
"Input terminates with a newline." << std::endl;
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'."
66template <
typename T,
typename =
void>
73 if (!hb_ot_var_has_data(face) || hb_ot_var_get_axis_count(face) == 0) {
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'));
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;
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."
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."
122 hb_set_t* desired_codepoints = hb_subset_input_unicode_set(input.get());
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;
131 std::cerr <<
"Invalid codepoint for " << raw_codepoint <<
"; exiting."
136 if (!hb_set_has(actual_codepoints.get(), codepoint)) {
142 std::cerr <<
"Codepoint " << raw_codepoint
143 <<
" not found in font, aborting." << std::endl;
146 hb_set_add(desired_codepoints, codepoint);
148 if (hb_set_is_empty(desired_codepoints)) {
149 std::cerr <<
"No codepoints specified, exiting." << std::endl;
157 if (!new_face || new_face.get() == hb_face_get_empty()) {
159 <<
"Failed to subset font; aborting. This error normally indicates "
160 "the current version of Harfbuzz is unable to process it."
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."
173 unsigned int data_length;
174 const char*
data = hb_blob_get_data(
result.get(), &data_length);
176 std::ofstream output_font_file;
177 output_font_file.open(output_file_path,
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."
186 output_font_file.write(
data, data_length);
187 output_font_file.flush();
188 output_font_file.close();
190 std::cout <<
"Wrote " << data_length <<
" bytes to " << output_file_path
int main(int argc, char **argv)
static float max(float r, float g, float b)
std::unique_ptr< hb_subset_input_t, hb_subset_input_deleter > HbSubsetInputPtr
std::unique_ptr< hb_blob_t, hb_blob_deleter > HbBlobPtr
std::unique_ptr< hb_face_t, hb_face_deleter > HbFacePtr
std::unique_ptr< hb_set_t, hb_set_deleter > HbSetPtr
SIN Vec< N, float > trunc(const Vec< N, float > &x)
static HarfbuzzWrappers::HbFacePtr Make(hb_face_t *face, T input)
std::shared_ptr< const fml::Mapping > data
hb_codepoint_t ParseCodepoint(std::string_view arg, bool &optional)