From 6f1fb172db9781bbacb3ff4957b57b0bc2e49704 Mon Sep 17 00:00:00 2001 From: Jackson Gardner Date: Fri, 18 Aug 2023 15:08:10 -0700 Subject: [PATCH] Allow optional codepoints to be expressed to the font subset generator. (#44864) This is the engine side change to fix https://github.com/flutter/flutter/issues/132711. There will be a subsequent framework change to express the space character as an "optional" character. --- tools/font-subset/main.cc | 29 ++++++++++++++++++++++++----- tools/font-subset/test.py | 7 +++++++ 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/tools/font-subset/main.cc b/tools/font-subset/main.cc index fc9765b6161cf..346cb2f008eee 100644 --- a/tools/font-subset/main.cc +++ b/tools/font-subset/main.cc @@ -13,15 +13,22 @@ #include "hb_wrappers.h" -hb_codepoint_t ParseCodepoint(const std::string& arg) { +hb_codepoint_t ParseCodepoint(std::string_view arg, bool& optional) { + constexpr std::string_view kOptionalPrefix = "optional:"; + if (arg.substr(0, kOptionalPrefix.length()) == kOptionalPrefix) { + optional = true; + arg = arg.substr(kOptionalPrefix.length()); + } else { + optional = false; + } uint64_t value = 0; // Check for \u123, u123, otherwise let strtoul work it out. if (arg[0] == 'u') { - value = strtoul(arg.c_str() + 1, nullptr, 16); + value = strtoul(arg.data() + 1, nullptr, 16); } else if (arg[0] == '\\' && arg[1] == 'u') { - value = strtoul(arg.c_str() + 2, nullptr, 16); + value = strtoul(arg.data() + 2, nullptr, 16); } else { - value = strtoul(arg.c_str(), nullptr, 0); + value = strtoul(arg.data(), nullptr, 0); } if (value == 0 || value > std::numeric_limits::max()) { std::cerr << "The value '" << arg << "' (" << value @@ -43,6 +50,10 @@ void Usage() { "and must be input as decimal numbers (123), hexadecimal " "numbers (0x7B), or unicode hexadecimal characters (\\u7B)." << std::endl; + std::cout << "Codepoints can be prefixed by the string \"optional:\" to " + "specify that the codepoint can be omitted if it isn't found " + "in the input font file." + << std::endl; std::cout << "Input terminates with a newline." << std::endl; std::cout << "This program will de-duplicate codepoints if the same codepoint is " @@ -113,13 +124,21 @@ int main(int argc, char** argv) { hb_face_collect_unicodes(font_face.get(), actual_codepoints.get()); std::string raw_codepoint; while (std::cin >> raw_codepoint) { - auto codepoint = ParseCodepoint(raw_codepoint); + bool optional = false; + auto codepoint = + ParseCodepoint(std::string_view(raw_codepoint), optional); if (!codepoint) { std::cerr << "Invalid codepoint for " << raw_codepoint << "; exiting." << std::endl; return -1; } + if (!hb_set_has(actual_codepoints.get(), codepoint)) { + if (optional) { + // Code point is optional, so omit it. + continue; + } + std::cerr << "Codepoint " << raw_codepoint << " not found in font, aborting." << std::endl; return -1; diff --git a/tools/font-subset/test.py b/tools/font-subset/test.py index 540b27c02744f..b8ad5051a6833 100755 --- a/tools/font-subset/test.py +++ b/tools/font-subset/test.py @@ -60,6 +60,13 @@ (False, '1.ttf', MATERIAL_TTF, [r'57348' ]), # False because different codepoint (True, '2.ttf', MATERIAL_TTF, [r'0xE003', r'0xE004']), + (True, '2.ttf', MATERIAL_TTF, [r'0xE003', r'optional:0xE004' + ]), # Optional codepoint that is found + (True, '2.ttf', MATERIAL_TTF, [ + r'0xE003', + r'0xE004', + r'optional:0x12', + ]), # Optional codepoint that is not found (True, '2.ttf', MATERIAL_TTF, [ r'0xE003', r'0xE004',