1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <random>
18
19 #include <log/log.h>
20 #include <gtest/gtest.h>
21 #include <minikin/CmapCoverage.h>
22 #include <minikin/SparseBitSet.h>
23
24 #include "MinikinInternal.h"
25
26 namespace minikin {
27
28 static constexpr uint16_t VS_PLATFORM_ID = 0;
29 static constexpr uint16_t VS_ENCODING_ID = 5;
30
writeU8(uint8_t x,uint8_t * out,size_t offset)31 size_t writeU8(uint8_t x, uint8_t* out, size_t offset) {
32 out[offset] = x;
33 return offset + 1;
34 }
35
writeU16(uint16_t x,uint8_t * out,size_t offset)36 size_t writeU16(uint16_t x, uint8_t* out, size_t offset) {
37 out[offset] = x >> 8;
38 out[offset + 1] = x;
39 return offset + 2;
40 }
41
writeI16(int16_t sx,uint8_t * out,size_t offset)42 size_t writeI16(int16_t sx, uint8_t* out, size_t offset) {
43 return writeU16(static_cast<uint16_t>(sx), out, offset);
44 }
45
writeU24(uint32_t x,uint8_t * out,size_t offset)46 size_t writeU24(uint32_t x, uint8_t* out, size_t offset) {
47 out[offset] = x >> 16;
48 out[offset + 1] = x >> 8;
49 out[offset + 2] = x;
50 return offset + 3;
51 }
52
writeU32(uint32_t x,uint8_t * out,size_t offset)53 size_t writeU32(uint32_t x, uint8_t* out, size_t offset) {
54 out[offset] = x >> 24;
55 out[offset + 1] = x >> 16;
56 out[offset + 2] = x >> 8;
57 out[offset + 3] = x;
58 return offset + 4;
59 }
60
61 // Returns valid cmap format 4 table contents. All glyph ID is same value as code point. (e.g.
62 // 'a' (U+0061) is mapped to Glyph ID = 0x0061).
63 // 'range' should be specified with inclusive-inclusive values.
buildCmapFormat4Table(const std::vector<uint16_t> & ranges)64 static std::vector<uint8_t> buildCmapFormat4Table(const std::vector<uint16_t>& ranges) {
65 uint16_t segmentCount = ranges.size() / 2 + 1 /* +1 for end marker */;
66
67 const size_t numOfUint16 =
68 8 /* format, length, languages, segCountX2, searchRange, entrySelector, rangeShift, pad */ +
69 segmentCount * 4 /* endCount, startCount, idRange, idRangeOffset */;
70 const size_t finalLength = sizeof(uint16_t) * numOfUint16;
71
72 std::vector<uint8_t> out(finalLength);
73 size_t head = 0;
74 head = writeU16(4, out.data(), head); // format
75 head = writeU16(finalLength, out.data(), head); // length
76 head = writeU16(0, out.data(), head); // langauge
77
78 const uint16_t searchRange = 2 * (1 << static_cast<int>(floor(log2(segmentCount))));
79
80 head = writeU16(segmentCount * 2, out.data(), head); // segCountX2
81 head = writeU16(searchRange, out.data(), head); // searchRange
82 head = writeU16(__builtin_ctz(searchRange) - 1, out.data(), head); // entrySelector
83 head = writeU16(segmentCount * 2 - searchRange, out.data(), head); // rangeShift
84
85 size_t endCountHead = head;
86 size_t startCountHead = head + segmentCount * sizeof(uint16_t) + 2 /* padding */;
87 size_t idDeltaHead = startCountHead + segmentCount * sizeof(uint16_t);
88 size_t idRangeOffsetHead = idDeltaHead + segmentCount * sizeof(uint16_t);
89
90 for (size_t i = 0; i < ranges.size() / 2; ++i) {
91 const uint16_t begin = ranges[i * 2];
92 const uint16_t end = ranges[i * 2 + 1];
93 startCountHead = writeU16(begin, out.data(), startCountHead);
94 endCountHead = writeU16(end, out.data(), endCountHead);
95 // map glyph ID as the same value of the code point.
96 idDeltaHead = writeU16(0, out.data(), idDeltaHead);
97 idRangeOffsetHead = writeU16(0 /* we don't use this */, out.data(), idRangeOffsetHead);
98 }
99
100 // fill end marker
101 endCountHead = writeU16(0xFFFF, out.data(), endCountHead);
102 startCountHead = writeU16(0xFFFF, out.data(), startCountHead);
103 idDeltaHead = writeU16(1, out.data(), idDeltaHead);
104 idRangeOffsetHead = writeU16(0, out.data(), idRangeOffsetHead);
105 LOG_ALWAYS_FATAL_IF(endCountHead > finalLength);
106 LOG_ALWAYS_FATAL_IF(startCountHead > finalLength);
107 LOG_ALWAYS_FATAL_IF(idDeltaHead > finalLength);
108 LOG_ALWAYS_FATAL_IF(idRangeOffsetHead != finalLength);
109 return out;
110 }
111
112 // Returns valid cmap format 4 table contents. All glyph ID is same value as code point. (e.g.
113 // 'a' (U+0061) is mapped to Glyph ID = 0x0061).
114 // 'range' should be specified with inclusive-inclusive values.
buildCmapFormat12Table(const std::vector<uint32_t> & ranges)115 static std::vector<uint8_t> buildCmapFormat12Table(const std::vector<uint32_t>& ranges) {
116 uint32_t numGroups = ranges.size() / 2;
117
118 const size_t finalLength = 2 /* format */ + 2 /* reserved */ + 4 /* length */ +
119 4 /* languages */ + 4 /* numGroups */ + 12 /* size of a group */ * numGroups;
120
121 std::vector<uint8_t> out(finalLength);
122 size_t head = 0;
123 head = writeU16(12, out.data(), head); // format
124 head = writeU16(0, out.data(), head); // reserved
125 head = writeU32(finalLength, out.data(), head); // length
126 head = writeU32(0, out.data(), head); // langauge
127 head = writeU32(numGroups, out.data(), head); // numGroups
128
129 for (uint32_t i = 0; i < numGroups; ++i) {
130 const uint32_t start = ranges[2 * i];
131 const uint32_t end = ranges[2 * i + 1];
132 head = writeU32(start, out.data(), head);
133 head = writeU32(end, out.data(), head);
134 // map glyph ID as the same value of the code point.
135 // TODO: Use glyph IDs lower than 65535.
136 // Cmap can store 32 bit glyph ID but due to the size of numGlyph, a font file can contain
137 // up to 65535 glyphs in a file.
138 head = writeU32(start, out.data(), head);
139 }
140
141 LOG_ALWAYS_FATAL_IF(head != finalLength);
142 return out;
143 }
144
145 struct VariationSelectorRecord {
146 uint32_t codePoint;
147 std::vector<uint32_t> defaultUVSRanges;
148 std::vector<uint32_t> nonDefaultUVS;
149
getDefaultUVSAsBinaryminikin::VariationSelectorRecord150 std::vector<uint8_t> getDefaultUVSAsBinary() const {
151 if (defaultUVSRanges.empty()) {
152 return std::vector<uint8_t>();
153 }
154 const size_t numOfRanges = defaultUVSRanges.size() / 2;
155 const size_t length = sizeof(uint32_t) /* numUnicodeValueRanges */ +
156 numOfRanges * 4 /* size of Unicode Range Table */;
157
158 std::vector<uint8_t> out(length);
159 size_t head = 0;
160 head = writeU32(numOfRanges, out.data(), head);
161 for (size_t i = 0; i < numOfRanges; ++i) {
162 const uint32_t startUnicodeValue = defaultUVSRanges[i * 2];
163 const uint32_t endUnicodeValue = defaultUVSRanges[i * 2 + 1];
164 head = writeU24(startUnicodeValue, out.data(), head);
165 head = writeU8(endUnicodeValue - startUnicodeValue, out.data(), head);
166 }
167 LOG_ALWAYS_FATAL_IF(head != length);
168 return out;
169 }
170
getNonDefaultUVSAsBinaryminikin::VariationSelectorRecord171 std::vector<uint8_t> getNonDefaultUVSAsBinary() const {
172 if (nonDefaultUVS.empty()) {
173 return std::vector<uint8_t>();
174 }
175 const size_t length = sizeof(uint32_t) /* numUnicodeValueRanges */ +
176 nonDefaultUVS.size() * 5 /* size of UVS Mapping Record */;
177
178 std::vector<uint8_t> out(length);
179 size_t head = 0;
180 head = writeU32(nonDefaultUVS.size(), out.data(), head);
181 for (uint32_t codePoint : nonDefaultUVS) {
182 head = writeU24(codePoint, out.data(), head);
183 head = writeU16(4 /* fixed glyph id */, out.data(), head);
184 }
185 LOG_ALWAYS_FATAL_IF(head != length);
186 return out;
187 }
188 };
189
buildCmapFormat14Table(const std::vector<VariationSelectorRecord> & vsRecords)190 static std::vector<uint8_t> buildCmapFormat14Table(
191 const std::vector<VariationSelectorRecord>& vsRecords) {
192
193 const size_t headerLength = sizeof(uint16_t) /* format */ + sizeof(uint32_t) /* length */ +
194 sizeof(uint32_t) /* numVarSelectorRecords */ +
195 11 /* size of variation selector record */ * vsRecords.size();
196
197 std::vector<uint8_t> out(headerLength);
198 size_t head = 0;
199 head = writeU16(14, out.data(), head); // format
200 head += sizeof(uint32_t); // length will be filled later
201 head = writeU32(vsRecords.size(), out.data(), head); // numVarSelectorRecords;
202
203 for (const auto& record : vsRecords) {
204 const uint32_t vsCodePoint = record.codePoint;
205 head = writeU24(vsCodePoint, out.data(), head);
206
207 std::vector<uint8_t> defaultUVS = record.getDefaultUVSAsBinary();
208 if (defaultUVS.empty()) {
209 head = writeU32(0, out.data(), head);
210 } else {
211 head = writeU32(out.size(), out.data(), head);
212 out.insert(out.end(), defaultUVS.begin(), defaultUVS.end());
213 }
214
215 std::vector<uint8_t> nonDefaultUVS = record.getNonDefaultUVSAsBinary();
216 if (nonDefaultUVS.empty()) {
217 head = writeU32(0, out.data(), head);
218 } else {
219 head = writeU32(out.size(), out.data(), head);
220 out.insert(out.end(), nonDefaultUVS.begin(), nonDefaultUVS.end());
221 }
222 }
223 LOG_ALWAYS_FATAL_IF(head != headerLength);
224 writeU32(out.size(), out.data(), 2); // fill the length.
225 return out;
226 }
227
228 class CmapBuilder {
229 public:
230 static constexpr size_t kEncodingTableHead = 4;
231 static constexpr size_t kEncodingTableSize = 8;
232
CmapBuilder(int numTables)233 CmapBuilder(int numTables) : mNumTables(numTables), mCurrentTableIndex(0) {
234 const size_t headerSize =
235 2 /* version */ + 2 /* numTables */ + kEncodingTableSize * numTables;
236 out.resize(headerSize);
237 writeU16(0, out.data(), 0);
238 writeU16(numTables, out.data(), 2);
239 }
240
appendTable(uint16_t platformId,uint16_t encodingId,const std::vector<uint8_t> & table)241 void appendTable(uint16_t platformId, uint16_t encodingId,
242 const std::vector<uint8_t>& table) {
243 appendEncodingTable(platformId, encodingId, out.size());
244 out.insert(out.end(), table.begin(), table.end());
245 }
246
build()247 std::vector<uint8_t> build() {
248 LOG_ALWAYS_FATAL_IF(mCurrentTableIndex != mNumTables);
249 return out;
250 }
251
252 // Helper functions.
buildSingleFormat4Cmap(uint16_t platformId,uint16_t encodingId,const std::vector<uint16_t> & ranges)253 static std::vector<uint8_t> buildSingleFormat4Cmap(uint16_t platformId, uint16_t encodingId,
254 const std::vector<uint16_t>& ranges) {
255 CmapBuilder builder(1);
256 builder.appendTable(platformId, encodingId, buildCmapFormat4Table(ranges));
257 return builder.build();
258 }
259
buildSingleFormat12Cmap(uint16_t platformId,uint16_t encodingId,const std::vector<uint32_t> & ranges)260 static std::vector<uint8_t> buildSingleFormat12Cmap(uint16_t platformId, uint16_t encodingId,
261 const std::vector<uint32_t>& ranges) {
262 CmapBuilder builder(1);
263 builder.appendTable(platformId, encodingId, buildCmapFormat12Table(ranges));
264 return builder.build();
265 }
266
267 private:
appendEncodingTable(uint16_t platformId,uint16_t encodingId,uint32_t offset)268 void appendEncodingTable(uint16_t platformId, uint16_t encodingId, uint32_t offset) {
269 LOG_ALWAYS_FATAL_IF(mCurrentTableIndex == mNumTables);
270
271 const size_t currentEncodingTableHead =
272 kEncodingTableHead + mCurrentTableIndex * kEncodingTableSize;
273 size_t head = writeU16(platformId, out.data(), currentEncodingTableHead);
274 head = writeU16(encodingId, out.data(), head);
275 head = writeU32(offset, out.data(), head);
276 LOG_ALWAYS_FATAL_IF((head - currentEncodingTableHead) != kEncodingTableSize);
277 mCurrentTableIndex++;
278 }
279
280 int mNumTables;
281 int mCurrentTableIndex;
282 std::vector<uint8_t> out;
283 };
284
TEST(CmapCoverageTest,SingleFormat4_brokenCmap)285 TEST(CmapCoverageTest, SingleFormat4_brokenCmap) {
286 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
287 {
288 SCOPED_TRACE("Reading beyond buffer size - Too small cmap size");
289 std::vector<uint8_t> cmap =
290 CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>({'a', 'a'}));
291
292 SparseBitSet coverage =
293 CmapCoverage::getCoverage(cmap.data(), 3 /* too small */, &vsTables);
294 EXPECT_EQ(0U, coverage.length());
295 EXPECT_TRUE(vsTables.empty());
296 }
297 {
298 SCOPED_TRACE("Reading beyond buffer size - space needed for tables goes beyond cmap size");
299 std::vector<uint8_t> cmap =
300 CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>({'a', 'a'}));
301
302 writeU16(1000, cmap.data(), 2 /* offset of num tables in cmap header */);
303 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
304 EXPECT_EQ(0U, coverage.length());
305 EXPECT_TRUE(vsTables.empty());
306 }
307 {
308 SCOPED_TRACE("Reading beyond buffer size - Invalid offset in encoding table");
309 std::vector<uint8_t> cmap =
310 CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>({'a', 'a'}));
311
312 writeU16(1000, cmap.data(), 8 /* offset of the offset in the first encoding record */);
313 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
314 EXPECT_EQ(0U, coverage.length());
315 EXPECT_TRUE(vsTables.empty());
316 }
317 {
318 SCOPED_TRACE("Reversed range");
319 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>(
320 {'b', 'b', 'a', 'a'}));
321
322 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
323 EXPECT_EQ(0U, coverage.length());
324 EXPECT_TRUE(vsTables.empty());
325 }
326 {
327 SCOPED_TRACE("Reversed range - partially readable");
328 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>(
329 { 'a', 'a', 'c', 'c', 'b', 'b'}));
330
331 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
332 EXPECT_EQ(0U, coverage.length());
333 EXPECT_TRUE(vsTables.empty());
334 }
335 }
336
TEST(CmapCoverageTest,SingleFormat4)337 TEST(CmapCoverageTest, SingleFormat4) {
338 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
339 struct TestCast {
340 std::string testTitle;
341 uint16_t platformId;
342 uint16_t encodingId;
343 } TEST_CASES[] = {
344 { "Platform 0, Encoding 0", 0, 0 },
345 { "Platform 0, Encoding 1", 0, 1 },
346 { "Platform 0, Encoding 2", 0, 2 },
347 { "Platform 0, Encoding 3", 0, 3 },
348 { "Platform 3, Encoding 1", 3, 1 },
349 };
350
351 for (const auto& testCase : TEST_CASES) {
352 SCOPED_TRACE(testCase.testTitle.c_str());
353 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat4Cmap(
354 testCase.platformId, testCase.encodingId, std::vector<uint16_t>({'a', 'a'}));
355 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
356 EXPECT_TRUE(coverage.get('a'));
357 EXPECT_FALSE(coverage.get('b'));
358 EXPECT_TRUE(vsTables.empty());
359 }
360 }
361
TEST(CmapCoverageTest,SingleFormat12)362 TEST(CmapCoverageTest, SingleFormat12) {
363 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
364
365 struct TestCast {
366 std::string testTitle;
367 uint16_t platformId;
368 uint16_t encodingId;
369 } TEST_CASES[] = {
370 { "Platform 0, Encoding 4", 0, 4 },
371 { "Platform 0, Encoding 6", 0, 6 },
372 { "Platform 3, Encoding 10", 3, 10 },
373 };
374
375 for (const auto& testCase : TEST_CASES) {
376 SCOPED_TRACE(testCase.testTitle.c_str());
377 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
378 testCase.platformId, testCase.encodingId, std::vector<uint32_t>({'a', 'a'}));
379 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
380 EXPECT_TRUE(coverage.get('a'));
381 EXPECT_FALSE(coverage.get('b'));
382 EXPECT_TRUE(vsTables.empty());
383 }
384 }
385
TEST(CmapCoverageTest,Format12_beyondTheUnicodeLimit)386 TEST(CmapCoverageTest, Format12_beyondTheUnicodeLimit) {
387 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
388 {
389 SCOPED_TRACE("Starting range is out of Unicode code point. Should be ignored.");
390 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
391 0, 0, std::vector<uint32_t>({'a', 'a', 0x110000, 0x110000}));
392
393 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
394 EXPECT_TRUE(coverage.get('a'));
395 EXPECT_FALSE(coverage.get(0x110000));
396 EXPECT_TRUE(vsTables.empty());
397 }
398 {
399 SCOPED_TRACE("Ending range is out of Unicode code point. Should be ignored.");
400 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
401 0, 0, std::vector<uint32_t>({'a', 'a', 0x10FF00, 0x110000}));
402
403 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
404 EXPECT_TRUE(coverage.get('a'));
405 EXPECT_TRUE(coverage.get(0x10FF00));
406 EXPECT_TRUE(coverage.get(0x10FFFF));
407 EXPECT_FALSE(coverage.get(0x110000));
408 EXPECT_TRUE(vsTables.empty());
409 }
410 }
411
TEST(CmapCoverageTest,notSupportedEncodings)412 TEST(CmapCoverageTest, notSupportedEncodings) {
413 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
414
415 struct TestCast {
416 std::string testTitle;
417 uint16_t platformId;
418 uint16_t encodingId;
419 } TEST_CASES[] = {
420 // Any encodings with platform 2 is not supported.
421 { "Platform 2, Encoding 0", 2, 0 },
422 { "Platform 2, Encoding 1", 2, 1 },
423 { "Platform 2, Encoding 2", 2, 2 },
424 { "Platform 2, Encoding 3", 2, 3 },
425 // UCS-2 or UCS-4 are supported on Platform == 3. Others are not supported.
426 { "Platform 3, Encoding 0", 3, 0 }, // Symbol
427 { "Platform 3, Encoding 2", 3, 2 }, // ShiftJIS
428 { "Platform 3, Encoding 3", 3, 3 }, // RPC
429 { "Platform 3, Encoding 4", 3, 4 }, // Big5
430 { "Platform 3, Encoding 5", 3, 5 }, // Wansung
431 { "Platform 3, Encoding 6", 3, 6 }, // Johab
432 { "Platform 3, Encoding 7", 3, 7 }, // Reserved
433 { "Platform 3, Encoding 8", 3, 8 }, // Reserved
434 { "Platform 3, Encoding 9", 3, 9 }, // Reserved
435 // Uknown platforms
436 { "Platform 4, Encoding 0", 4, 0 },
437 { "Platform 5, Encoding 1", 5, 1 },
438 { "Platform 6, Encoding 0", 6, 0 },
439 { "Platform 7, Encoding 1", 7, 1 },
440 };
441
442 for (const auto& testCase : TEST_CASES) {
443 SCOPED_TRACE(testCase.testTitle.c_str());
444 CmapBuilder builder(1);
445 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat4Cmap(
446 testCase.platformId, testCase.encodingId, std::vector<uint16_t>({'a', 'a'}));
447 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
448 EXPECT_EQ(0U, coverage.length());
449 EXPECT_TRUE(vsTables.empty());
450 }
451 }
452
TEST(CmapCoverageTest,brokenFormat4Table)453 TEST(CmapCoverageTest, brokenFormat4Table) {
454 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
455 {
456 SCOPED_TRACE("Too small table cmap size");
457 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
458 table.resize(2); // Remove trailing data.
459
460 CmapBuilder builder(1);
461 builder.appendTable(0, 0, table);
462 std::vector<uint8_t> cmap = builder.build();
463
464 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
465 EXPECT_EQ(0U, coverage.length());
466 EXPECT_TRUE(vsTables.empty());
467 }
468 {
469 SCOPED_TRACE("Too many segments");
470 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
471 writeU16(5000, table.data(), 6 /* segment count offset */); // 5000 segments.
472 CmapBuilder builder(1);
473 builder.appendTable(0, 0, table);
474 std::vector<uint8_t> cmap = builder.build();
475
476 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
477 EXPECT_EQ(0U, coverage.length());
478 EXPECT_TRUE(vsTables.empty());
479 }
480 {
481 SCOPED_TRACE("Inversed range");
482 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
483 // Put smaller end code point to inverse the range.
484 writeU16('a', table.data(), 14 /* the first element of endCount offset */);
485 CmapBuilder builder(1);
486 builder.appendTable(0, 0, table);
487 std::vector<uint8_t> cmap = builder.build();
488
489 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
490 EXPECT_EQ(0U, coverage.length());
491 EXPECT_TRUE(vsTables.empty());
492 }
493 }
494
TEST(CmapCoverageTest,brokenFormat12Table)495 TEST(CmapCoverageTest, brokenFormat12Table) {
496 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
497 {
498 SCOPED_TRACE("Too small cmap size");
499 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
500 table.resize(2); // Remove trailing data.
501
502 CmapBuilder builder(1);
503 builder.appendTable(0, 0, table);
504 std::vector<uint8_t> cmap = builder.build();
505
506 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
507 EXPECT_EQ(0U, coverage.length());
508 EXPECT_TRUE(vsTables.empty());
509 }
510 {
511 SCOPED_TRACE("Too many groups");
512 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
513 writeU32(5000, table.data(), 12 /* num group offset */); // 5000 groups.
514
515 CmapBuilder builder(1);
516 builder.appendTable(0, 0, table);
517 std::vector<uint8_t> cmap = builder.build();
518
519 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
520 EXPECT_EQ(0U, coverage.length());
521 EXPECT_TRUE(vsTables.empty());
522 }
523 {
524 SCOPED_TRACE("Inversed range.");
525 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
526 // Put larger start code point to inverse the range.
527 writeU32('b', table.data(), 16 /* start code point offset in the first group */);
528
529 CmapBuilder builder(1);
530 builder.appendTable(0, 0, table);
531 std::vector<uint8_t> cmap = builder.build();
532
533 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
534 EXPECT_EQ(0U, coverage.length());
535 EXPECT_TRUE(vsTables.empty());
536 }
537 {
538 SCOPED_TRACE("Too large code point");
539 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
540 0, 0, std::vector<uint32_t>({0x110000, 0x110000}));
541
542 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
543 EXPECT_EQ(0U, coverage.length());
544 EXPECT_TRUE(vsTables.empty());
545 }
546 {
547 SCOPED_TRACE("Reversed range");
548 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
549 0, 0, std::vector<uint32_t>({'b', 'b', 'a', 'a'}));
550 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
551 EXPECT_EQ(0U, coverage.length());
552 EXPECT_TRUE(vsTables.empty());
553 }
554 {
555 SCOPED_TRACE("Reversed range - partially readable");
556 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
557 0, 0, std::vector<uint32_t>({'a', 'a', 'c', 'c', 'b', 'b'}));
558 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
559 EXPECT_EQ(0U, coverage.length());
560 EXPECT_TRUE(vsTables.empty());
561 }
562 }
563
TEST(CmapCoverageTest,TableSelection_Priority)564 TEST(CmapCoverageTest, TableSelection_Priority) {
565 std::vector<uint8_t> highestFormat12Table =
566 buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
567 std::vector<uint8_t> highestFormat4Table =
568 buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
569 std::vector<uint8_t> format4 = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
570 std::vector<uint8_t> format12 = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
571
572 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
573 {
574 SCOPED_TRACE("(platform, encoding) = (3, 10) is the highest priority.");
575
576 struct LowerPriorityTable {
577 uint16_t platformId;
578 uint16_t encodingId;
579 const std::vector<uint8_t>& table;
580 } LOWER_PRIORITY_TABLES[] = {
581 { 0, 0, format4 },
582 { 0, 1, format4 },
583 { 0, 2, format4 },
584 { 0, 3, format4 },
585 { 0, 4, format12 },
586 { 0, 6, format12 },
587 { 3, 1, format4 },
588 };
589
590 for (const auto& table : LOWER_PRIORITY_TABLES) {
591 CmapBuilder builder(2);
592 builder.appendTable(table.platformId, table.encodingId, table.table);
593 builder.appendTable(3, 10, highestFormat12Table);
594 std::vector<uint8_t> cmap = builder.build();
595
596 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
597 EXPECT_TRUE(coverage.get('a')); // comes from highest table
598 EXPECT_FALSE(coverage.get('b')); // should not use other table.
599 EXPECT_TRUE(vsTables.empty());
600 }
601 }
602 {
603 SCOPED_TRACE("(platform, encoding) = (3, 1) case");
604
605 struct LowerPriorityTable {
606 uint16_t platformId;
607 uint16_t encodingId;
608 const std::vector<uint8_t>& table;
609 } LOWER_PRIORITY_TABLES[] = {
610 { 0, 0, format4 },
611 { 0, 1, format4 },
612 { 0, 2, format4 },
613 { 0, 3, format4 },
614 };
615
616 for (const auto& table : LOWER_PRIORITY_TABLES) {
617 CmapBuilder builder(2);
618 builder.appendTable(table.platformId, table.encodingId, table.table);
619 builder.appendTable(3, 1, highestFormat4Table);
620 std::vector<uint8_t> cmap = builder.build();
621
622 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
623 EXPECT_TRUE(coverage.get('a')); // comes from highest table
624 EXPECT_FALSE(coverage.get('b')); // should not use other table.
625 EXPECT_TRUE(vsTables.empty());
626 }
627 }
628 }
629
TEST(CmapCoverageTest,TableSelection_SkipBrokenFormat4Table)630 TEST(CmapCoverageTest, TableSelection_SkipBrokenFormat4Table) {
631 std::vector<uint8_t> validTable = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
632 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
633 {
634 SCOPED_TRACE("Unsupported format");
635 CmapBuilder builder(2);
636 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
637 writeU16(0, table.data(), 0 /* format offset */);
638 builder.appendTable(3, 1, table);
639 builder.appendTable(0, 0, validTable);
640 std::vector<uint8_t> cmap = builder.build();
641
642 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
643 EXPECT_TRUE(coverage.get('a')); // comes from valid table
644 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
645 EXPECT_TRUE(vsTables.empty());
646 }
647 {
648 SCOPED_TRACE("Invalid language");
649 CmapBuilder builder(2);
650 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
651 writeU16(1, table.data(), 4 /* language offset */);
652 builder.appendTable(3, 1, table);
653 builder.appendTable(0, 0, validTable);
654 std::vector<uint8_t> cmap = builder.build();
655
656 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
657 EXPECT_TRUE(coverage.get('a')); // comes from valid table
658 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
659 EXPECT_TRUE(vsTables.empty());
660 }
661 {
662 SCOPED_TRACE("Invalid length");
663 CmapBuilder builder(2);
664 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
665 writeU16(5000, table.data(), 2 /* length offset */);
666 builder.appendTable(3, 1, table);
667 builder.appendTable(0, 0, validTable);
668 std::vector<uint8_t> cmap = builder.build();
669
670 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
671 EXPECT_TRUE(coverage.get('a')); // comes from valid table
672 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
673 EXPECT_TRUE(vsTables.empty());
674 }
675 }
676
TEST(CmapCoverageTest,TableSelection_SkipBrokenFormat12Table)677 TEST(CmapCoverageTest, TableSelection_SkipBrokenFormat12Table) {
678 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
679 std::vector<uint8_t> validTable =
680 buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
681 {
682 SCOPED_TRACE("Unsupported format");
683 CmapBuilder builder(2);
684 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
685 writeU16(0, table.data(), 0 /* format offset */);
686 builder.appendTable(3, 1, table);
687 builder.appendTable(0, 0, validTable);
688 std::vector<uint8_t> cmap = builder.build();
689
690 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
691 EXPECT_TRUE(coverage.get('a')); // comes from valid table
692 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
693 EXPECT_TRUE(vsTables.empty());
694 }
695 {
696 SCOPED_TRACE("Invalid language");
697 CmapBuilder builder(2);
698 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
699 writeU32(1, table.data(), 8 /* language offset */);
700 builder.appendTable(3, 1, table);
701 builder.appendTable(0, 0, validTable);
702 std::vector<uint8_t> cmap = builder.build();
703
704 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
705 EXPECT_TRUE(coverage.get('a')); // comes from valid table
706 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
707 EXPECT_TRUE(vsTables.empty());
708 }
709 {
710 SCOPED_TRACE("Invalid length");
711 CmapBuilder builder(2);
712 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
713 writeU32(5000, table.data(), 4 /* length offset */);
714 builder.appendTable(3, 1, table);
715 builder.appendTable(0, 0, validTable);
716 std::vector<uint8_t> cmap = builder.build();
717
718 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
719 EXPECT_TRUE(coverage.get('a')); // comes from valid table
720 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
721 EXPECT_TRUE(vsTables.empty());
722 }
723 }
724
TEST(CmapCoverageTest,TableSelection_VSTable)725 TEST(CmapCoverageTest, TableSelection_VSTable) {
726 std::vector<uint8_t> smallLetterTable =
727 buildCmapFormat12Table(std::vector<uint32_t>({'a', 'z'}));
728 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
729 { 0xFE0E, { 'a', 'b' }, {} /* no non-default UVS table */ },
730 { 0xFE0F, {} /* no default UVS table */, { 'a', 'b'} },
731 { 0xE0100, { 'a', 'a' }, { 'b'} },
732 }));
733 CmapBuilder builder(2);
734 builder.appendTable(3, 1, smallLetterTable);
735 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
736 std::vector<uint8_t> cmap = builder.build();
737
738 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
739 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
740 EXPECT_TRUE(coverage.get('a'));
741 ASSERT_FALSE(vsTables.empty());
742
743 const uint16_t vs15Index = getVsIndex(0xFE0E);
744 ASSERT_LT(vs15Index, vsTables.size());
745 ASSERT_TRUE(vsTables[vs15Index]);
746 EXPECT_TRUE(vsTables[vs15Index]->get('a'));
747 EXPECT_TRUE(vsTables[vs15Index]->get('b'));
748
749 const uint16_t vs16Index = getVsIndex(0xFE0F);
750 ASSERT_LT(vs16Index, vsTables.size());
751 ASSERT_TRUE(vsTables[vs16Index]);
752 EXPECT_TRUE(vsTables[vs16Index]->get('a'));
753 EXPECT_TRUE(vsTables[vs16Index]->get('b'));
754
755 const uint16_t vs17Index = getVsIndex(0xE0100);
756 ASSERT_LT(vs17Index, vsTables.size());
757 ASSERT_TRUE(vsTables[vs17Index]);
758 EXPECT_TRUE(vsTables[vs17Index]->get('a'));
759 EXPECT_TRUE(vsTables[vs17Index]->get('b'));
760 }
761
TEST(CmapCoverageTest,TableSelection_InterSection)762 TEST(CmapCoverageTest, TableSelection_InterSection) {
763 std::vector<uint8_t> smallLetterTable =
764 buildCmapFormat12Table(std::vector<uint32_t>({'a', 'z'}));
765 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
766 { 0xFE0E, { 'a', 'e' }, { 'c', 'd', } },
767 { 0xFE0F, { 'c', 'e'} , { 'a', 'b', 'c', 'd', 'e'} },
768 { 0xE0100, { 'a', 'c' }, { 'b', 'c', 'd' } },
769 { 0xE0101, { 'b', 'd'} , { 'a', 'b', 'c', 'd'} },
770 { 0xE0102, { 'a', 'c', 'd', 'g'} , { 'b', 'c', 'd', 'e', 'f', 'g', 'h'} },
771 { 0xE0103, { 'a', 'f'} , { 'b', 'd', } },
772 }));
773 CmapBuilder builder(2);
774 builder.appendTable(3, 1, smallLetterTable);
775 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
776 std::vector<uint8_t> cmap = builder.build();
777
778 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
779 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
780 EXPECT_TRUE(coverage.get('a'));
781 ASSERT_FALSE(vsTables.empty());
782
783 const uint16_t vs15Index = getVsIndex(0xFE0E);
784 ASSERT_LT(vs15Index, vsTables.size());
785 ASSERT_TRUE(vsTables[vs15Index]);
786 EXPECT_TRUE(vsTables[vs15Index]->get('a'));
787 EXPECT_TRUE(vsTables[vs15Index]->get('b'));
788 EXPECT_TRUE(vsTables[vs15Index]->get('c'));
789 EXPECT_TRUE(vsTables[vs15Index]->get('d'));
790 EXPECT_TRUE(vsTables[vs15Index]->get('e'));
791
792 const uint16_t vs16Index = getVsIndex(0xFE0F);
793 ASSERT_LT(vs16Index, vsTables.size());
794 ASSERT_TRUE(vsTables[vs16Index]);
795 EXPECT_TRUE(vsTables[vs16Index]->get('a'));
796 EXPECT_TRUE(vsTables[vs16Index]->get('b'));
797 EXPECT_TRUE(vsTables[vs16Index]->get('c'));
798 EXPECT_TRUE(vsTables[vs16Index]->get('d'));
799 EXPECT_TRUE(vsTables[vs16Index]->get('e'));
800
801 const uint16_t vs17Index = getVsIndex(0xE0100);
802 ASSERT_LT(vs17Index, vsTables.size());
803 ASSERT_TRUE(vsTables[vs17Index]);
804 EXPECT_TRUE(vsTables[vs17Index]->get('a'));
805 EXPECT_TRUE(vsTables[vs17Index]->get('b'));
806 EXPECT_TRUE(vsTables[vs17Index]->get('c'));
807 EXPECT_TRUE(vsTables[vs17Index]->get('d'));
808
809 const uint16_t vs18Index = getVsIndex(0xE0101);
810 ASSERT_LT(vs18Index, vsTables.size());
811 ASSERT_TRUE(vsTables[vs18Index]);
812 EXPECT_TRUE(vsTables[vs18Index]->get('a'));
813 EXPECT_TRUE(vsTables[vs18Index]->get('b'));
814 EXPECT_TRUE(vsTables[vs18Index]->get('c'));
815 EXPECT_TRUE(vsTables[vs18Index]->get('d'));
816
817 const uint16_t vs19Index = getVsIndex(0xE0102);
818 ASSERT_LT(vs19Index, vsTables.size());
819 ASSERT_TRUE(vsTables[vs19Index]);
820 EXPECT_TRUE(vsTables[vs19Index]->get('a'));
821 EXPECT_TRUE(vsTables[vs19Index]->get('b'));
822 EXPECT_TRUE(vsTables[vs19Index]->get('c'));
823 EXPECT_TRUE(vsTables[vs19Index]->get('d'));
824 EXPECT_TRUE(vsTables[vs19Index]->get('e'));
825 EXPECT_TRUE(vsTables[vs19Index]->get('f'));
826 EXPECT_TRUE(vsTables[vs19Index]->get('g'));
827 EXPECT_TRUE(vsTables[vs19Index]->get('h'));
828
829 const uint16_t vs20Index = getVsIndex(0xE0103);
830 ASSERT_LT(vs20Index, vsTables.size());
831 ASSERT_TRUE(vsTables[vs20Index]);
832 EXPECT_TRUE(vsTables[vs20Index]->get('a'));
833 EXPECT_TRUE(vsTables[vs20Index]->get('b'));
834 EXPECT_TRUE(vsTables[vs20Index]->get('c'));
835 EXPECT_TRUE(vsTables[vs20Index]->get('d'));
836 EXPECT_TRUE(vsTables[vs20Index]->get('e'));
837 EXPECT_TRUE(vsTables[vs20Index]->get('f'));
838 }
839
TEST(CmapCoverageTest,TableSelection_brokenVSTable)840 TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
841 std::vector<uint8_t> cmap12Table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'z'}));
842 {
843 SCOPED_TRACE("Too small cmap size");
844 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
845 { 0xFE0E, { 'a', 'a' }, { 'b' } }
846 }));
847 CmapBuilder builder(2);
848 builder.appendTable(3, 1, cmap12Table);
849 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
850 std::vector<uint8_t> cmap = builder.build();
851
852 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
853 SparseBitSet coverage = CmapCoverage::getCoverage(
854 cmap.data(), 3 /* too small size */, &vsTables);
855 EXPECT_FALSE(coverage.get('a'));
856 ASSERT_TRUE(vsTables.empty());
857 }
858 {
859 SCOPED_TRACE("Too many variation records");
860 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
861 { 0xFE0F, { 'a', 'a' }, { 'b' } }
862 }));
863 writeU32(5000, vsTable.data(), 6 /* numVarSelectorRecord offset */);
864 CmapBuilder builder(2);
865 builder.appendTable(3, 1, cmap12Table);
866 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
867 std::vector<uint8_t> cmap = builder.build();
868
869 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
870 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
871 ASSERT_TRUE(vsTables.empty());
872 }
873 {
874 SCOPED_TRACE("Invalid default UVS offset in variation records");
875 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
876 { 0xFE0F, { 'a', 'a' }, { 'b' } }
877 }));
878 writeU32(5000, vsTable.data(), 13 /* defaultUVSffset offset in the first record */);
879 CmapBuilder builder(2);
880 builder.appendTable(3, 1, cmap12Table);
881 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
882 std::vector<uint8_t> cmap = builder.build();
883
884 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
885 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
886 ASSERT_TRUE(vsTables.empty());
887 }
888 {
889 SCOPED_TRACE("Invalid non default UVS offset in variation records");
890 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
891 { 0xFE0F, { 'a', 'a' }, { 'b' } }
892 }));
893 writeU32(5000, vsTable.data(), 17 /* nonDefaultUVSffset offset in the first record */);
894 CmapBuilder builder(2);
895 builder.appendTable(3, 1, cmap12Table);
896 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
897 std::vector<uint8_t> cmap = builder.build();
898
899 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
900 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
901 ASSERT_TRUE(vsTables.empty());
902 }
903 {
904 SCOPED_TRACE("Too many ranges entry in default UVS table");
905 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
906 { 0xFE0F, { 'a', 'a' }, { 'b' } }
907 }));
908 // 21 is the offset of the numUnicodeValueRanges in the fist defulat UVS table.
909 writeU32(5000, vsTable.data(), 21);
910 CmapBuilder builder(2);
911 builder.appendTable(3, 1, cmap12Table);
912 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
913 std::vector<uint8_t> cmap = builder.build();
914
915 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
916 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
917 ASSERT_TRUE(vsTables.empty());
918 }
919 {
920 SCOPED_TRACE("Too many ranges entry in non default UVS table");
921 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
922 { 0xFE0F, { 'a', 'a' }, { 'b' } }
923 }));
924 // 29 is the offset of the numUnicodeValueRanges in the fist defulat UVS table.
925 writeU32(5000, vsTable.data(), 29);
926 CmapBuilder builder(2);
927 builder.appendTable(3, 1, cmap12Table);
928 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
929 std::vector<uint8_t> cmap = builder.build();
930
931 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
932 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
933 ASSERT_TRUE(vsTables.empty());
934 }
935 {
936 SCOPED_TRACE("Reversed range in default UVS table");
937 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
938 { 0xFE0F, { 'b', 'b', 'a', 'a' }, { } }
939 }));
940 CmapBuilder builder(2);
941 builder.appendTable(3, 1, cmap12Table);
942 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
943 std::vector<uint8_t> cmap = builder.build();
944
945 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
946 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
947 ASSERT_TRUE(vsTables.empty());
948 }
949 {
950 SCOPED_TRACE("Reversed range in default UVS table - partially readable");
951 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
952 { 0xFE0F, { 'a', 'a', 'c', 'c', 'b', 'b' }, { } }
953 }));
954 CmapBuilder builder(2);
955 builder.appendTable(3, 1, cmap12Table);
956 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
957 std::vector<uint8_t> cmap = builder.build();
958
959 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
960 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
961 ASSERT_TRUE(vsTables.empty());
962 }
963 {
964 SCOPED_TRACE("Reversed mapping entries in non default UVS table");
965 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
966 { 0xFE0F, { }, { 'b', 'a' } }
967 }));
968 CmapBuilder builder(2);
969 builder.appendTable(3, 1, cmap12Table);
970 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
971 std::vector<uint8_t> cmap = builder.build();
972
973 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
974 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
975 ASSERT_TRUE(vsTables.empty());
976 }
977 {
978 SCOPED_TRACE("Reversed mapping entries in non default UVS table");
979 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
980 { 0xFE0F, { }, { 'a', 'c', 'b' } }
981 }));
982 CmapBuilder builder(2);
983 builder.appendTable(3, 1, cmap12Table);
984 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
985 std::vector<uint8_t> cmap = builder.build();
986
987 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
988 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
989 ASSERT_TRUE(vsTables.empty());
990 }
991 }
992
TEST(CmapCoverageTest,TableSelection_brokenVSTable_bestEffort)993 TEST(CmapCoverageTest, TableSelection_brokenVSTable_bestEffort) {
994 std::vector<uint8_t> cmap12Table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
995 {
996 SCOPED_TRACE("Invalid default UVS offset in variation records");
997 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
998 { 0xFE0E, { 'a', 'a' }, { 'b' } },
999 { 0xFE0F, { 'a', 'a' }, { 'b' } },
1000 }));
1001 writeU32(5000, vsTable.data(), 13 /* defaultUVSffset offset in the record for 0xFE0E */);
1002 CmapBuilder builder(2);
1003 builder.appendTable(3, 1, cmap12Table);
1004 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1005 std::vector<uint8_t> cmap = builder.build();
1006
1007 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1008 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1009
1010 const uint16_t vs16Index = getVsIndex(0xFE0F);
1011 ASSERT_LT(vs16Index, vsTables.size());
1012 ASSERT_TRUE(vsTables[vs16Index]);
1013 EXPECT_TRUE(vsTables[vs16Index]->get('a'));
1014 EXPECT_TRUE(vsTables[vs16Index]->get('b'));
1015
1016 const uint16_t vs15Index = getVsIndex(0xFE0E);
1017 EXPECT_FALSE(vsTables[vs15Index]);
1018 }
1019 {
1020 SCOPED_TRACE("Invalid non default UVS offset in variation records");
1021 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
1022 { 0xFE0E, { 'a', 'a' }, { 'b' } },
1023 { 0xFE0F, { 'a', 'a' }, { 'b' } },
1024 }));
1025 writeU32(5000, vsTable.data(), 17 /* nonDefaultUVSffset offset in the first record */);
1026 CmapBuilder builder(2);
1027 builder.appendTable(3, 1, cmap12Table);
1028 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1029 std::vector<uint8_t> cmap = builder.build();
1030
1031 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1032 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1033
1034 const uint16_t vs16Index = getVsIndex(0xFE0F);
1035 ASSERT_LT(vs16Index, vsTables.size());
1036 ASSERT_TRUE(vsTables[vs16Index]);
1037 EXPECT_TRUE(vsTables[vs16Index]->get('a'));
1038 EXPECT_TRUE(vsTables[vs16Index]->get('b'));
1039
1040 const uint16_t vs15Index = getVsIndex(0xFE0E);
1041 EXPECT_FALSE(vsTables[vs15Index]);
1042 }
1043 {
1044 SCOPED_TRACE("Unknown variation selectors.");
1045 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
1046 { 0xFE0F, { 'a', 'a' }, { 'b' } },
1047 { 0xEFFFF, { 'a', 'a' }, { 'b' } },
1048 }));
1049 CmapBuilder builder(2);
1050 builder.appendTable(3, 1, cmap12Table);
1051 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1052 std::vector<uint8_t> cmap = builder.build();
1053
1054 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1055 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1056
1057 const uint16_t vs16Index = getVsIndex(0xFE0F);
1058 ASSERT_LT(vs16Index, vsTables.size());
1059 ASSERT_TRUE(vsTables[vs16Index]);
1060 EXPECT_TRUE(vsTables[vs16Index]->get('a'));
1061 EXPECT_TRUE(vsTables[vs16Index]->get('b'));
1062 }
1063 }
1064
1065 // Used only for better looking of range definition.
1066 #define RANGE(x, y) x, y
1067
TEST(CmapCoverageTest,TableSelection_defaultUVSPointMissingGlyph)1068 TEST(CmapCoverageTest, TableSelection_defaultUVSPointMissingGlyph) {
1069 std::vector<uint8_t> baseTable = buildCmapFormat12Table(std::vector<uint32_t>(
1070 {RANGE('a', 'e'), RANGE('g', 'h'), RANGE('j', 'j'), RANGE('m', 'z')}));
1071 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
1072 { 0xFE0F, { 'a', 'z' }, { } }
1073 }));
1074
1075 CmapBuilder builder(2);
1076 builder.appendTable(3, 1, baseTable);
1077 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1078 std::vector<uint8_t> cmap = builder.build();
1079
1080 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1081 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1082 const uint16_t vsIndex = getVsIndex(0xFE0F);
1083 ASSERT_LT(vsIndex, vsTables.size());
1084 ASSERT_TRUE(vsTables[vsIndex]);
1085
1086 for (char c = 'a'; c <= 'z'; ++c) {
1087 // Default UVS table points the variation sequence to the glyph of the base code point.
1088 // Thus, if the base code point is not supported, we should exclude them.
1089 EXPECT_EQ(coverage.get(c), vsTables[vsIndex]->get(c)) << c;
1090 }
1091 }
1092
1093 #undef RANGE
1094
TEST(CmapCoverageTest,TableSelection_vsTableOnly)1095 TEST(CmapCoverageTest, TableSelection_vsTableOnly) {
1096 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
1097 { 0xFE0F, { }, { 'a' } }
1098 }));
1099
1100 CmapBuilder builder(1);
1101 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1102 std::vector<uint8_t> cmap = builder.build();
1103
1104 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1105 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1106 const uint16_t vsIndex = getVsIndex(0xFE0F);
1107 ASSERT_LT(vsIndex, vsTables.size());
1108 ASSERT_TRUE(vsTables[vsIndex]);
1109 EXPECT_TRUE(vsTables[vsIndex]->get('a'));
1110 }
1111 } // namespace minikin
1112