Skip to content

Commit 9495896

Browse files
committed
Update GlyphKeyedDiff to handle CFF2.
1 parent 931f0cb commit 9495896

File tree

5 files changed

+103
-13
lines changed

5 files changed

+103
-13
lines changed

common/font_helper_test.cc

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class FontHelperTest : public ::testing::Test {
6868
protected:
6969
FontHelperTest()
7070
: noto_sans_jp_otf(make_hb_face(nullptr)),
71+
noto_sans_vf_jp_otf(make_hb_face(nullptr)),
7172
noto_sans_ift_ttf(make_hb_face(nullptr)),
7273
roboto_ab(make_hb_face(nullptr)),
7374
roboto_Awesome(make_hb_face(nullptr)),
@@ -98,12 +99,17 @@ class FontHelperTest : public ::testing::Test {
9899
hb_blob_create_from_file("common/testdata/NotoSansJP-Regular.otf"));
99100
noto_sans_jp_otf = make_hb_face(hb_face_create(blob.get(), 0));
100101

102+
blob = make_hb_blob(
103+
hb_blob_create_from_file("common/testdata/NotoSansJP-VF.subset.otf"));
104+
noto_sans_vf_jp_otf = make_hb_face(hb_face_create(blob.get(), 0));
105+
101106
blob = make_hb_blob(
102107
hb_blob_create_from_file("ift/testdata/NotoSansJP-Regular.ift.ttf"));
103108
noto_sans_ift_ttf = make_hb_face(hb_face_create(blob.get(), 0));
104109
}
105110

106111
hb_face_unique_ptr noto_sans_jp_otf;
112+
hb_face_unique_ptr noto_sans_vf_jp_otf;
107113
hb_face_unique_ptr noto_sans_ift_ttf;
108114
hb_face_unique_ptr roboto_ab;
109115
hb_face_unique_ptr roboto_Awesome;
@@ -375,6 +381,29 @@ TEST_F(FontHelperTest, CffData) {
375381
ASSERT_EQ(data.size(), 0);
376382
}
377383

384+
TEST_F(FontHelperTest, Cff2Data) {
385+
auto data = FontHelper::Cff2Data(noto_sans_vf_jp_otf.get(), 34);
386+
387+
// this was manually pulled out of the CFF2 table.
388+
const uint8_t expected[96] = {
389+
0x96, 0x78, 0x8c, 0x10, 0x16, 0xb0, 0xf7, 0x25, 0x8c, 0x10, 0x06, 0xf7,
390+
0x2d, 0xf8, 0x47, 0xaa, 0xe3, 0xa5, 0xd5, 0xa6, 0xe6, 0x4f, 0x4c, 0x7f,
391+
0x7d, 0x85, 0x98, 0x82, 0x7e, 0x93, 0x10, 0x19, 0x8f, 0x06, 0xa6, 0x30,
392+
0xa4, 0x41, 0xaa, 0x33, 0x84, 0x9a, 0x85, 0x7c, 0x80, 0x99, 0x91, 0x10,
393+
0x08, 0xf7, 0x2b, 0xfc, 0x47, 0xb3, 0x51, 0xca, 0xf7, 0x28, 0x8e, 0x10,
394+
0x8b, 0xfb, 0x98, 0xf9, 0x6a, 0xa8, 0x9e, 0x8d, 0x10, 0x05, 0x6a, 0xfb,
395+
0x49, 0x8c, 0x10, 0x06, 0xfb, 0x27, 0xfc, 0x68, 0xd4, 0x21, 0x8d, 0x10,
396+
0x15, 0xf7, 0xd8, 0xab, 0xfb, 0xd8, 0xaf, 0xf5, 0x67, 0x8e, 0x10, 0x06};
397+
string_view expected_str((const char*)expected, 96);
398+
399+
ASSERT_EQ(data.size(), 96);
400+
ASSERT_EQ(data.str(), expected_str);
401+
402+
// undefined glyph is empty blob.
403+
data = FontHelper::Cff2Data(noto_sans_jp_otf.get(), 20000);
404+
ASSERT_EQ(data.size(), 0);
405+
}
406+
378407
TEST_F(FontHelperTest, GvarSharedTupleCount) {
379408
auto count = FontHelper::GvarSharedTupleCount(roboto_vf.get());
380409
ASSERT_TRUE(count.ok()) << count.status();
4.65 KB
Binary file not shown.

ift/encoder/encoder.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -623,8 +623,7 @@ StatusOr<FontData> Encoder::CutSubset(const ProcessingContext& context,
623623
}
624624

625625
auto tags = FontHelper::GetTags(font);
626-
if (IsMixedMode() && def.IsVariable() &&
627-
tags.contains(FontHelper::kGvar)) {
626+
if (IsMixedMode() && def.IsVariable() && tags.contains(FontHelper::kGvar)) {
628627
// In mixed mode glyph keyed patches handles gvar, except for when design
629628
// space is expanded, in which case a gvar table should be patched in that
630629
// only has coverage of the base (root) subset definition + the current

ift/glyph_keyed_diff.cc

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,16 @@ struct CffDataOperator {
111111
}
112112
};
113113

114+
struct Cff2DataOperator {
115+
Cff2DataOperator(hb_face_t* face) : face_(face) {}
116+
hb_face_t* face_;
117+
118+
StatusOr<std::string> operator()(hb_codepoint_t gid) {
119+
FontData data = FontHelper::Cff2Data(face_, gid);
120+
return data.string();
121+
}
122+
};
123+
114124
template <typename Operator>
115125
Status PopulateTableData(const absl::btree_set<uint32_t>& gids,
116126
uint32_t offset_bias, Operator glyph_data_lookup,
@@ -133,7 +143,7 @@ StatusOr<FontData> GlyphKeyedDiff::CreateDataStream(
133143
// check for unsupported tags.
134144
for (auto tag : tags_) {
135145
if (tag != FontHelper::kGlyf && tag != FontHelper::kGvar &&
136-
tag != FontHelper::kCFF) {
146+
tag != FontHelper::kCFF && tag != FontHelper::kCFF2) {
137147
return absl::InvalidArgumentError(
138148
"Unsupported table type for glyph keyed diff.");
139149
}
@@ -153,21 +163,16 @@ StatusOr<FontData> GlyphKeyedDiff::CreateDataStream(
153163
face_tags.contains(FontHelper::kGvar);
154164
bool include_cff =
155165
tags_.contains(FontHelper::kCFF) && face_tags.contains(FontHelper::kCFF);
166+
bool include_cff2 = tags_.contains(FontHelper::kCFF2) &&
167+
face_tags.contains(FontHelper::kCFF2);
156168

157169
uint32_t glyph_count = gids.size();
158170
uint32_t glyph_id_width = u16_gids ? 2 : 3;
159-
uint32_t table_count =
160-
(include_glyf ? 1 : 0) + (include_gvar ? 1 : 0) + (include_cff ? 1 : 0);
171+
uint32_t table_count = (include_glyf ? 1 : 0) + (include_gvar ? 1 : 0) +
172+
(include_cff ? 1 : 0) + (include_cff2 ? 1 : 0);
161173
uint32_t header_size = 5 + glyph_id_width * glyph_count + table_count * 4 +
162174
4 * glyph_count * table_count + 4;
163175

164-
if (tags_.contains(FontHelper::kCFF2) &&
165-
face_tags.contains(FontHelper::kCFF2)) {
166-
// TODO(garretrieger): add CFF2 support
167-
return absl::UnimplementedError(
168-
"CFF2 glyph keyed patching not yet implemented.");
169-
}
170-
171176
if (include_glyf) {
172177
processed_tags.insert(FontHelper::kGlyf);
173178
GlyfDataOperator data_lookup(face.get());
@@ -189,6 +194,13 @@ StatusOr<FontData> GlyphKeyedDiff::CreateDataStream(
189194
offset_data));
190195
}
191196

197+
if (include_cff2) {
198+
processed_tags.insert(FontHelper::kCFF2);
199+
Cff2DataOperator data_lookup(face.get());
200+
TRYV(PopulateTableData(gids, header_size, data_lookup, per_glyph_data,
201+
offset_data));
202+
}
203+
192204
// Add the trailing offset
193205
FontHelper::WriteUInt32(header_size + per_glyph_data.size(), offset_data);
194206

ift/glyph_keyed_diff_test.cc

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ const uint8_t data_stream_u16_short_loca[] = {
9898
0xfb, 0xc6, 0x03, 0x34, 0xfc, 0xcc, 0x04, 0x3a, 0xfc, 0xd6, 0x03, 0x2a};
9999

100100
const uint8_t data_stream_u16_cff[57] = {
101-
// num header bytes = 37
102101
0x00, 0x00, 0x00, 0x01, // glyphCount
103102
0x01, // table count
104103

@@ -118,6 +117,30 @@ const uint8_t data_stream_u16_cff[57] = {
118117
0xa0, 0xb4, 0x6e, 0xa9, 0x66, 0x67, 0x6d, 0x6d, 0x62, 0x1e, 0x13, 0x60,
119118
0xf4, 0x0a};
120119

120+
const uint8_t data_stream_u16_cff2[115] = {
121+
0x00, 0x00, 0x00, 0x01, // glyphCount
122+
0x01, // table count
123+
124+
// glyphIds[1]
125+
0x00, 0x22, // gid 34
126+
127+
// tables[1]
128+
'C', 'F', 'F', '2',
129+
130+
// offset stream
131+
0x00, 0x00, 0x00, 0x13, // gid 34
132+
0x00, 0x00, 0x00, 0x73, // end (+96 bytes)
133+
134+
// gid 34 - A (96 bytes)
135+
0x96, 0x78, 0x8c, 0x10, 0x16, 0xb0, 0xf7, 0x25, 0x8c, 0x10, 0x06, 0xf7,
136+
0x2d, 0xf8, 0x47, 0xaa, 0xe3, 0xa5, 0xd5, 0xa6, 0xe6, 0x4f, 0x4c, 0x7f,
137+
0x7d, 0x85, 0x98, 0x82, 0x7e, 0x93, 0x10, 0x19, 0x8f, 0x06, 0xa6, 0x30,
138+
0xa4, 0x41, 0xaa, 0x33, 0x84, 0x9a, 0x85, 0x7c, 0x80, 0x99, 0x91, 0x10,
139+
0x08, 0xf7, 0x2b, 0xfc, 0x47, 0xb3, 0x51, 0xca, 0xf7, 0x28, 0x8e, 0x10,
140+
0x8b, 0xfb, 0x98, 0xf9, 0x6a, 0xa8, 0x9e, 0x8d, 0x10, 0x05, 0x6a, 0xfb,
141+
0x49, 0x8c, 0x10, 0x06, 0xfb, 0x27, 0xfc, 0x68, 0xd4, 0x21, 0x8d, 0x10,
142+
0x15, 0xf7, 0xd8, 0xab, 0xfb, 0xd8, 0xaf, 0xf5, 0x67, 0x8e, 0x10, 0x06};
143+
121144
namespace ift {
122145

123146
class GlyphKeyedDiffTest : public ::testing::Test {
@@ -128,6 +151,7 @@ class GlyphKeyedDiffTest : public ::testing::Test {
128151
roboto = from_file("common/testdata/Roboto-Regular.Awesome.ttf");
129152
roboto_vf = from_file("common/testdata/Roboto[wdth,wght].abcd.ttf");
130153
noto_sans_jp_cff = from_file("common/testdata/NotoSansJP-Regular.otf");
154+
noto_sans_jp_cff2 = from_file("common/testdata/NotoSansJP-VF.subset.otf");
131155
}
132156

133157
FontData from_file(const char* filename) {
@@ -164,6 +188,7 @@ class GlyphKeyedDiffTest : public ::testing::Test {
164188
FontData roboto;
165189
FontData roboto_vf;
166190
FontData noto_sans_jp_cff;
191+
FontData noto_sans_jp_cff2;
167192
BrotliBinaryPatch unbrotli;
168193
};
169194

@@ -272,6 +297,31 @@ TEST_F(GlyphKeyedDiffTest, CreatePatch_Cff) {
272297
absl::string_view((const char*)data_stream_u16_cff, 57));
273298
}
274299

300+
TEST_F(GlyphKeyedDiffTest, CreatePatch_Cff2) {
301+
const uint8_t header[29] = {
302+
'i', 'f', 'g', 'k', 0x00, 0x00, 0x00, 0x00, // reserved
303+
0x00, // flags
304+
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02,
305+
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, // compat id
306+
0x00, 0x00, 0x00, 0x73, // max uncompressed length (115 bytes)
307+
};
308+
309+
GlyphKeyedDiff differ(noto_sans_jp_cff2, CompatId(1, 2, 3, 4),
310+
{FontHelper::kCFF2});
311+
auto patch = differ.CreatePatch({34});
312+
ASSERT_TRUE(patch.ok()) << patch.status();
313+
314+
ASSERT_EQ(patch->str(0, 29), absl::string_view((const char*)header, 29));
315+
316+
FontData empty;
317+
FontData compressed_stream(patch->str(29));
318+
FontData uncompressed_stream;
319+
auto status = unbrotli.Patch(empty, compressed_stream, &uncompressed_stream);
320+
ASSERT_TRUE(status.ok()) << status;
321+
ASSERT_EQ(uncompressed_stream.str(),
322+
absl::string_view((const char*)data_stream_u16_cff2, 115));
323+
}
324+
275325
TEST_F(GlyphKeyedDiffTest, CreatePatch_Gvar) {
276326
const uint8_t data_stream_header[] = {
277327
0x00,

0 commit comments

Comments
 (0)