Skip to content

Commit 2c22d50

Browse files
committed
Add override_url_template_prefix setting to segmentation plan.
This allows the url template used in the IFT font to be customized.
1 parent 4ca571c commit 2c22d50

File tree

10 files changed

+186
-19
lines changed

10 files changed

+186
-19
lines changed

ift/common/font_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct CompareTableOffsets {
4343
class FontHelper {
4444
public:
4545
constexpr static hb_tag_t kIFT = HB_TAG('I', 'F', 'T', ' ');
46+
constexpr static hb_tag_t kIFTX = HB_TAG('I', 'F', 'T', 'X');
4647
constexpr static hb_tag_t kLoca = HB_TAG('l', 'o', 'c', 'a');
4748
constexpr static hb_tag_t kGlyf = HB_TAG('g', 'l', 'y', 'f');
4849
constexpr static hb_tag_t kHead = HB_TAG('h', 'e', 'a', 'd');

ift/config/BUILD

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,18 @@ cc_library(
149149
]
150150
)
151151

152+
cc_test(
153+
name = "config_compiler_test",
154+
size = "small",
155+
srcs = [
156+
"config_compiler_test.cc",
157+
],
158+
deps = [
159+
":config_compiler",
160+
"@googletest//:gtest_main",
161+
],
162+
)
163+
152164
cc_test(
153165
name = "auto_segmenter_config_test",
154166
size = "small",

ift/config/config_compiler.cc

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ static StatusOr<design_space_t> ToDesignSpace(const DesignSpace& proto) {
4242
return result;
4343
}
4444

45-
static ActivationCondition FromProto(const ActivationConditionProto& condition) {
45+
static ActivationCondition FromProto(
46+
const ActivationConditionProto& condition) {
4647
// TODO(garretrieger): once glyph segmentation activation conditions can
4748
// support features copy those here.
4849
std::vector<SegmentSet> groups;
@@ -150,6 +151,16 @@ Status ConfigCompiler::Configure(const SegmentationPlan& plan,
150151
}
151152
compiler.SetUsePrefetchLists(plan.use_prefetch_lists());
152153

154+
if (plan.has_advanced_settings()) {
155+
const auto& advanced = plan.advanced_settings();
156+
if (!advanced.override_url_template_prefix().empty()) {
157+
std::vector<uint8_t> prefix(
158+
advanced.override_url_template_prefix().begin(),
159+
advanced.override_url_template_prefix().end());
160+
compiler.SetOverrideUrlTemplatePrefix(prefix);
161+
}
162+
}
163+
153164
// Check for unsupported settings
154165
if (plan.include_all_segment_patches()) {
155166
return absl::UnimplementedError(

ift/config/config_compiler_test.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include "ift/config/config_compiler.h"
2+
3+
#include <cstdint>
4+
#include <vector>
5+
6+
#include "gtest/gtest.h"
7+
#include "ift/config/segmentation_plan.pb.h"
8+
#include "ift/encoder/compiler.h"
9+
10+
using ift::encoder::Compiler;
11+
12+
namespace ift::config {
13+
14+
TEST(ConfigCompilerTest, ConfigureOverrideUrlTemplatePrefix) {
15+
Compiler compiler;
16+
SegmentationPlan plan;
17+
std::string prefix_str = "https://example.com/";
18+
plan.mutable_advanced_settings()->set_override_url_template_prefix(
19+
prefix_str);
20+
21+
absl::Status status = ConfigCompiler::Configure(plan, compiler);
22+
ASSERT_TRUE(status.ok()) << status;
23+
24+
std::vector<uint8_t> expected(prefix_str.begin(), prefix_str.end());
25+
EXPECT_EQ(compiler.override_url_template_prefix(), expected);
26+
}
27+
28+
} // namespace ift::config

ift/config/segmentation_plan.proto

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,27 @@ message SegmentationPlan {
152152
// for in a single jump.
153153
repeated DesignSpace non_glyph_design_space_segmentation = 15;
154154

155-
// next = 17
155+
// These settings are for advanced usage and typically shouldn't need to be configured.
156+
AdvancedSettings advanced_settings = 17;
157+
158+
// next = 18
159+
}
160+
161+
message AdvancedSettings {
162+
// In the output font the URL template byte string is replaced with these bytes.
163+
//
164+
// The template bytes should not include the patch file extension (.ift_tk, .ift_gk),
165+
// as that will be added automatically during compilation based on the type of patch.
166+
//
167+
// If unset the compiler uses the byte string [128] as the URL template based which sets the
168+
// patch file name to the id32 value for each patch.
169+
//
170+
// URL template specification: https://w3c.github.io/IFT/Overview.html#url-templates
171+
// It's up to the caller to ensure the provided byte string is a valid URL template.
172+
//
173+
// Note: patches will be output to directory specified by the provided template.
174+
// it's up to the caller to ensure the location exists.
175+
bytes override_url_template_prefix = 1;
156176
}
157177

158178
// Activated when at least one set in every group is matched and all required_features match.

ift/dep_graph/dependency_graph_test.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -523,7 +523,8 @@ TEST_F(DependencyGraphTest, StronglyConnectedComponents_TopologicalSorting) {
523523
EXPECT_LT(liga_f, gffi);
524524
}
525525

526-
TEST_F(DependencyGraphTest, StronglyConnectedComponents_TopologicalSorting_InitFontFilter) {
526+
TEST_F(DependencyGraphTest,
527+
StronglyConnectedComponents_TopologicalSorting_InitFontFilter) {
527528
SubsetDefinition liga;
528529
liga.feature_tags = {HB_TAG('l', 'i', 'g', 'a')};
529530
SubsetDefinition dlig;
@@ -550,7 +551,8 @@ TEST_F(DependencyGraphTest, StronglyConnectedComponents_TopologicalSorting_InitF
550551
Node::Feature(HB_TAG('d', 'l', 'i', 'g'))));
551552
}
552553

553-
TEST_F(DependencyGraphTest, StronglyConnectedComponents_TopologicalSorting_InitFontFeatures) {
554+
TEST_F(DependencyGraphTest,
555+
StronglyConnectedComponents_TopologicalSorting_InitFontFeatures) {
554556
Reconfigure(WithDefaultFeatures({}),
555557
{
556558
/* 0 */ {{'a'}, ProbabilityBound::Zero()},
@@ -599,10 +601,8 @@ TEST_F(DependencyGraphTest,
599601
uint32_t num_cycles = 0;
600602
for (const auto& scc : *sccs_or) {
601603
if (scc.size() > 1) {
602-
EXPECT_THAT(scc, UnorderedElementsAre(
603-
Node::Glyph(129 /* AE */),
604-
Node::Glyph(811 /* AEacute */)
605-
));
604+
EXPECT_THAT(scc, UnorderedElementsAre(Node::Glyph(129 /* AE */),
605+
Node::Glyph(811 /* AEacute */)));
606606
num_cycles++;
607607
}
608608
}

ift/encoder/compiler.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ class Compiler {
5858

5959
void SetWoff2Encode(bool value) { this->woff2_encode_ = value; }
6060

61+
void SetOverrideUrlTemplatePrefix(const std::vector<uint8_t>& prefix) {
62+
override_url_template_prefix_ = prefix;
63+
}
64+
65+
const std::vector<uint8_t>& override_url_template_prefix() const {
66+
return override_url_template_prefix_;
67+
}
68+
6169
/*
6270
* Adds a segmentation of glyph data.
6371
*
@@ -247,15 +255,19 @@ class Compiler {
247255
constexpr uint8_t insert_id_op_code = 128;
248256

249257
std::vector<uint8_t> out;
258+
if (!override_url_template_prefix_.empty()) {
259+
out = override_url_template_prefix_;
260+
} else {
261+
out.push_back(insert_id_op_code);
262+
}
263+
250264
if (patch_set_id == 0) {
251265
// patch_set_id 0 is always used for table keyed patches
252-
out.push_back(insert_id_op_code);
253266
AppendLiteralToTemplate(".ift_tk", out);
254267
return out;
255268
}
256269

257270
// All other ids are for glyph keyed.
258-
out.push_back(insert_id_op_code);
259271
AppendLiteralToTemplate(absl::StrCat(".", patch_set_id, ".ift_gk"), out);
260272
return out;
261273
}
@@ -363,6 +375,7 @@ class Compiler {
363375
uint32_t next_id_ = 0;
364376
bool use_prefetch_lists_ = false;
365377
bool woff2_encode_ = false;
378+
std::vector<uint8_t> override_url_template_prefix_;
366379

367380
struct ProcessingContext {
368381
ProcessingContext(uint32_t next_id)

ift/encoder/compiler_test.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,4 +1000,34 @@ TEST_F(CompilerTest, RoundTripWoff2_Fails) {
10001000
ASSERT_TRUE(absl::IsInternal(ttf.status())) << ttf.status();
10011001
}
10021002

1003+
TEST_F(CompilerTest, UrlTemplateOverride) {
1004+
Compiler compiler;
1005+
hb_face_t* face = font.reference_face();
1006+
compiler.SetFace(face);
1007+
1008+
auto s = compiler.SetInitSubset(IntSet{'a', 'd'});
1009+
ASSERT_TRUE(s.ok()) << s;
1010+
compiler.AddNonGlyphDataSegment(IntSet{'b', 'c'});
1011+
1012+
// 3 (length) + 'p' + 'r' + 'e' + 128 (id opcode)
1013+
std::vector<uint8_t> prefix = {3, 'p', 'r', 'e', 133};
1014+
compiler.SetOverrideUrlTemplatePrefix(prefix);
1015+
1016+
auto encoding = compiler.Compile();
1017+
hb_face_destroy(face);
1018+
1019+
ASSERT_TRUE(encoding.ok()) << encoding.status();
1020+
auto encoded_face = encoding->init_font.face();
1021+
1022+
auto ift_table = FontHelper::TableData(encoded_face.get(), FontHelper::kIFT);
1023+
if (ift_table.empty()) {
1024+
ift_table = FontHelper::TableData(encoded_face.get(), FontHelper::kIFTX);
1025+
}
1026+
ASSERT_FALSE(ift_table.empty());
1027+
1028+
std::string expected_template = {3, 'p', 'r', 'e', (char)133, 7, '.',
1029+
'i', 'f', 't', '_', 't', 'k'};
1030+
ASSERT_TRUE(ift_table.str().find(expected_template) != std::string::npos);
1031+
}
1032+
10031033
} // namespace ift::encoder

ift/encoder/dependency_closure_test.cc

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -317,9 +317,10 @@ TEST_F(DependencyClosureTest, ExtractAllGlyphConditions_FullFont) {
317317
uint32_t parenleft = cp_to_seg.at('(');
318318
uint32_t parenright = cp_to_seg.at(')');
319319

320-
EXPECT_EQ(conditions->at(12 /* parenleft */).ToString(),
321-
// '(' is accesible from either '(' or ')' due to unicode mirroring.
322-
absl::StrCat("if ((s", parenleft, " OR s", parenright,")) then p0"));
320+
EXPECT_EQ(
321+
conditions->at(12 /* parenleft */).ToString(),
322+
// '(' is accesible from either '(' or ')' due to unicode mirroring.
323+
absl::StrCat("if ((s", parenleft, " OR s", parenright, ")) then p0"));
323324

324325
// small caps AE is accesible via numerous pathways and forms a complex
325326
// composite condition (smcp, c2sc, and glyph component substitutions)
@@ -329,12 +330,12 @@ TEST_F(DependencyClosureTest, ExtractAllGlyphConditions_FullFont) {
329330
uint32_t aeacute = cp_to_seg.at(0x1FD);
330331
uint32_t smcp = layout_to_seg.at(HB_TAG('s', 'm', 'c', 'p'));
331332
uint32_t c2sc = layout_to_seg.at(HB_TAG('c', '2', 's', 'c'));
332-
EXPECT_EQ(conditions->at(627 /* small caps AE */).ToString(),
333-
absl::StrCat(
334-
"if ((s", AE, " OR s", ae," OR s", AEacute, " OR s", aeacute, ") ",
335-
"AND (s", AE, " OR s", AEacute, " OR s", smcp, ") ",
336-
"AND (s", ae," OR s", aeacute," OR s", c2sc,") ",
337-
"AND (s", c2sc," OR s", smcp, ")) then p0"));
333+
EXPECT_EQ(
334+
conditions->at(627 /* small caps AE */).ToString(),
335+
absl::StrCat("if ((s", AE, " OR s", ae, " OR s", AEacute, " OR s",
336+
aeacute, ") ", "AND (s", AE, " OR s", AEacute, " OR s", smcp,
337+
") ", "AND (s", ae, " OR s", aeacute, " OR s", c2sc, ") ",
338+
"AND (s", c2sc, " OR s", smcp, ")) then p0"));
338339
}
339340

340341
TEST_F(DependencyClosureTest, ExtractAllGlyphConditions_PhaseCycle) {

ift/integration_test.cc

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1800,4 +1800,55 @@ TEST_F(IntegrationTest, MixedMode_Cff2) {
18001800
FontHelper::Cff2Data(desubroutinized_face.get(), 35).span());
18011801
}
18021802

1803+
TEST_F(IntegrationTest, UrlTemplateOverride) {
1804+
Compiler compiler;
1805+
auto init_gids = InitEncoderForMixedMode(compiler);
1806+
ASSERT_TRUE(init_gids.ok()) << init_gids.status();
1807+
1808+
auto face = noto_sans_jp_.face();
1809+
auto segment_0 = FontHelper::GidsToUnicodes(face.get(), *init_gids);
1810+
auto segment_1 = FontHelper::GidsToUnicodes(face.get(), TestSegment1());
1811+
1812+
auto sc = compiler.SetInitSubset(segment_0);
1813+
compiler.AddNonGlyphDataSegment(segment_1);
1814+
sc.Update(compiler.AddGlyphDataPatchCondition(
1815+
PatchMap::Entry(segment_1, 1, PatchEncoding::GLYPH_KEYED)));
1816+
ASSERT_TRUE(sc.ok()) << sc;
1817+
1818+
// 4 (length) + 'p' + 'a' + 't' + 'h' + 128 (id opcode)
1819+
std::vector<uint8_t> prefix = {4, 'p', 'a', 't', 'h', 128};
1820+
compiler.SetOverrideUrlTemplatePrefix(prefix);
1821+
1822+
auto encoding = compiler.Compile();
1823+
ASSERT_TRUE(encoding.ok()) << encoding.status();
1824+
1825+
// Verify the keys in the patches map.
1826+
for (const auto& [uri, data] : encoding->patches) {
1827+
EXPECT_THAT(uri, testing::StartsWith("path"));
1828+
}
1829+
1830+
// Request codepoint extension
1831+
btree_set<std::string> fetched_uris;
1832+
auto extended = ExtendWithDesignSpace(*encoding, {chunk1_cp}, {}, {},
1833+
&fetched_uris, 1, 2);
1834+
ASSERT_TRUE(extended.ok()) << extended.status();
1835+
1836+
// Should have fetched one table keyed and one glyph keyed patch.
1837+
// Table keyed: path<id>.ift_tk
1838+
// Glyph keyed: path<id>.ift_gk
1839+
ASSERT_EQ(fetched_uris.size(), 2);
1840+
bool found_tk = false;
1841+
bool found_gk = false;
1842+
for (const std::string& uri : fetched_uris) {
1843+
EXPECT_THAT(uri, testing::StartsWith("path"));
1844+
if (uri.find(".ift_tk") != std::string::npos) {
1845+
found_tk = true;
1846+
} else if (uri.find(".ift_gk") != std::string::npos) {
1847+
found_gk = true;
1848+
}
1849+
}
1850+
EXPECT_TRUE(found_tk);
1851+
EXPECT_TRUE(found_gk);
1852+
}
1853+
18031854
} // namespace ift

0 commit comments

Comments
 (0)