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 "minikin/CmapCoverage.h"
18
19 #include <random>
20
21 #include <gtest/gtest.h>
22 #include <log/log.h>
23
24 #include "minikin/SparseBitSet.h"
25
26 #include "MinikinInternal.h"
27
28 namespace minikin {
29
30 static constexpr uint16_t VS_PLATFORM_ID = 0;
31 static constexpr uint16_t VS_ENCODING_ID = 5;
32
writeU8(uint8_t x,uint8_t * out,size_t offset)33 size_t writeU8(uint8_t x, uint8_t* out, size_t offset) {
34 out[offset] = x;
35 return offset + 1;
36 }
37
writeU16(uint16_t x,uint8_t * out,size_t offset)38 size_t writeU16(uint16_t x, uint8_t* out, size_t offset) {
39 out[offset] = x >> 8;
40 out[offset + 1] = x;
41 return offset + 2;
42 }
43
writeI16(int16_t sx,uint8_t * out,size_t offset)44 size_t writeI16(int16_t sx, uint8_t* out, size_t offset) {
45 return writeU16(static_cast<uint16_t>(sx), out, offset);
46 }
47
writeU24(uint32_t x,uint8_t * out,size_t offset)48 size_t writeU24(uint32_t x, uint8_t* out, size_t offset) {
49 out[offset] = x >> 16;
50 out[offset + 1] = x >> 8;
51 out[offset + 2] = x;
52 return offset + 3;
53 }
54
writeU32(uint32_t x,uint8_t * out,size_t offset)55 size_t writeU32(uint32_t x, uint8_t* out, size_t offset) {
56 out[offset] = x >> 24;
57 out[offset + 1] = x >> 16;
58 out[offset + 2] = x >> 8;
59 out[offset + 3] = x;
60 return offset + 4;
61 }
62
63 // Returns valid cmap format 4 table contents. All glyph ID is same value as code point. (e.g.
64 // 'a' (U+0061) is mapped to Glyph ID = 0x0061).
65 // 'range' should be specified with inclusive-inclusive values.
buildCmapFormat4Table(const std::vector<uint16_t> & ranges)66 static std::vector<uint8_t> buildCmapFormat4Table(const std::vector<uint16_t>& ranges) {
67 uint16_t segmentCount = ranges.size() / 2 + 1 /* +1 for end marker */;
68
69 const size_t numOfUint16 = 8 /* format, length, languages, segCountX2, searchRange,
70 entrySelector, rangeShift, pad */
71 +
72 segmentCount * 4 /* endCount, startCount, idRange, idRangeOffset */;
73 const size_t finalLength = sizeof(uint16_t) * numOfUint16;
74
75 std::vector<uint8_t> out(finalLength);
76 size_t head = 0;
77 head = writeU16(4, out.data(), head); // format
78 head = writeU16(finalLength, out.data(), head); // length
79 head = writeU16(0, out.data(), head); // langauge
80
81 const uint16_t searchRange = 2 * (1 << static_cast<int>(floor(log2(segmentCount))));
82
83 head = writeU16(segmentCount * 2, out.data(), head); // segCountX2
84 head = writeU16(searchRange, out.data(), head); // searchRange
85 head = writeU16(__builtin_ctz(searchRange) - 1, out.data(), head); // entrySelector
86 head = writeU16(segmentCount * 2 - searchRange, out.data(), head); // rangeShift
87
88 size_t endCountHead = head;
89 size_t startCountHead = head + segmentCount * sizeof(uint16_t) + 2 /* padding */;
90 size_t idDeltaHead = startCountHead + segmentCount * sizeof(uint16_t);
91 size_t idRangeOffsetHead = idDeltaHead + segmentCount * sizeof(uint16_t);
92
93 for (size_t i = 0; i < ranges.size() / 2; ++i) {
94 const uint16_t begin = ranges[i * 2];
95 const uint16_t end = ranges[i * 2 + 1];
96 startCountHead = writeU16(begin, out.data(), startCountHead);
97 endCountHead = writeU16(end, out.data(), endCountHead);
98 // map glyph ID as the same value of the code point.
99 idDeltaHead = writeU16(0, out.data(), idDeltaHead);
100 idRangeOffsetHead = writeU16(0 /* we don't use this */, out.data(), idRangeOffsetHead);
101 }
102
103 // fill end marker
104 endCountHead = writeU16(0xFFFF, out.data(), endCountHead);
105 startCountHead = writeU16(0xFFFF, out.data(), startCountHead);
106 idDeltaHead = writeU16(1, out.data(), idDeltaHead);
107 idRangeOffsetHead = writeU16(0, out.data(), idRangeOffsetHead);
108 LOG_ALWAYS_FATAL_IF(endCountHead > finalLength);
109 LOG_ALWAYS_FATAL_IF(startCountHead > finalLength);
110 LOG_ALWAYS_FATAL_IF(idDeltaHead > finalLength);
111 LOG_ALWAYS_FATAL_IF(idRangeOffsetHead != finalLength);
112 return out;
113 }
114
115 // Returns valid cmap format 4 table contents. All glyph ID is same value as code point. (e.g.
116 // 'a' (U+0061) is mapped to Glyph ID = 0x0061).
117 // 'range' should be specified with inclusive-inclusive values.
buildCmapFormat12Table(const std::vector<uint32_t> & ranges)118 static std::vector<uint8_t> buildCmapFormat12Table(const std::vector<uint32_t>& ranges) {
119 uint32_t numGroups = ranges.size() / 2;
120
121 const size_t finalLength = 2 /* format */ + 2 /* reserved */ + 4 /* length */ +
122 4 /* languages */ + 4 /* numGroups */ +
123 12 /* size of a group */ * numGroups;
124
125 std::vector<uint8_t> out(finalLength);
126 size_t head = 0;
127 head = writeU16(12, out.data(), head); // format
128 head = writeU16(0, out.data(), head); // reserved
129 head = writeU32(finalLength, out.data(), head); // length
130 head = writeU32(0, out.data(), head); // langauge
131 head = writeU32(numGroups, out.data(), head); // numGroups
132
133 for (uint32_t i = 0; i < numGroups; ++i) {
134 const uint32_t start = ranges[2 * i];
135 const uint32_t end = ranges[2 * i + 1];
136 head = writeU32(start, out.data(), head);
137 head = writeU32(end, out.data(), head);
138 // map glyph ID as the same value of the code point.
139 // TODO: Use glyph IDs lower than 65535.
140 // Cmap can store 32 bit glyph ID but due to the size of numGlyph, a font file can contain
141 // up to 65535 glyphs in a file.
142 head = writeU32(start, out.data(), head);
143 }
144
145 LOG_ALWAYS_FATAL_IF(head != finalLength);
146 return out;
147 }
148
149 struct VariationSelectorRecord {
150 uint32_t codePoint;
151 std::vector<uint32_t> defaultUVSRanges;
152 std::vector<uint32_t> nonDefaultUVS;
153
getDefaultUVSAsBinaryminikin::VariationSelectorRecord154 std::vector<uint8_t> getDefaultUVSAsBinary() const {
155 if (defaultUVSRanges.empty()) {
156 return std::vector<uint8_t>();
157 }
158 const size_t numOfRanges = defaultUVSRanges.size() / 2;
159 const size_t length = sizeof(uint32_t) /* numUnicodeValueRanges */ +
160 numOfRanges * 4 /* size of Unicode Range Table */;
161
162 std::vector<uint8_t> out(length);
163 size_t head = 0;
164 head = writeU32(numOfRanges, out.data(), head);
165 for (size_t i = 0; i < numOfRanges; ++i) {
166 const uint32_t startUnicodeValue = defaultUVSRanges[i * 2];
167 const uint32_t endUnicodeValue = defaultUVSRanges[i * 2 + 1];
168 head = writeU24(startUnicodeValue, out.data(), head);
169 head = writeU8(endUnicodeValue - startUnicodeValue, out.data(), head);
170 }
171 LOG_ALWAYS_FATAL_IF(head != length);
172 return out;
173 }
174
getNonDefaultUVSAsBinaryminikin::VariationSelectorRecord175 std::vector<uint8_t> getNonDefaultUVSAsBinary() const {
176 if (nonDefaultUVS.empty()) {
177 return std::vector<uint8_t>();
178 }
179 const size_t length = sizeof(uint32_t) /* numUnicodeValueRanges */ +
180 nonDefaultUVS.size() * 5 /* size of UVS Mapping Record */;
181
182 std::vector<uint8_t> out(length);
183 size_t head = 0;
184 head = writeU32(nonDefaultUVS.size(), out.data(), head);
185 for (uint32_t codePoint : nonDefaultUVS) {
186 head = writeU24(codePoint, out.data(), head);
187 head = writeU16(4 /* fixed glyph id */, out.data(), head);
188 }
189 LOG_ALWAYS_FATAL_IF(head != length);
190 return out;
191 }
192 };
193
buildCmapFormat14Table(const std::vector<VariationSelectorRecord> & vsRecords)194 static std::vector<uint8_t> buildCmapFormat14Table(
195 const std::vector<VariationSelectorRecord>& vsRecords) {
196 const size_t headerLength = sizeof(uint16_t) /* format */ + sizeof(uint32_t) /* length */ +
197 sizeof(uint32_t) /* numVarSelectorRecords */ +
198 11 /* size of variation selector record */ * vsRecords.size();
199
200 std::vector<uint8_t> out(headerLength);
201 size_t head = 0;
202 head = writeU16(14, out.data(), head); // format
203 head += sizeof(uint32_t); // length will be filled later
204 head = writeU32(vsRecords.size(), out.data(), head); // numVarSelectorRecords;
205
206 for (const auto& record : vsRecords) {
207 const uint32_t vsCodePoint = record.codePoint;
208 head = writeU24(vsCodePoint, out.data(), head);
209
210 std::vector<uint8_t> defaultUVS = record.getDefaultUVSAsBinary();
211 if (defaultUVS.empty()) {
212 head = writeU32(0, out.data(), head);
213 } else {
214 head = writeU32(out.size(), out.data(), head);
215 out.insert(out.end(), defaultUVS.begin(), defaultUVS.end());
216 }
217
218 std::vector<uint8_t> nonDefaultUVS = record.getNonDefaultUVSAsBinary();
219 if (nonDefaultUVS.empty()) {
220 head = writeU32(0, out.data(), head);
221 } else {
222 head = writeU32(out.size(), out.data(), head);
223 out.insert(out.end(), nonDefaultUVS.begin(), nonDefaultUVS.end());
224 }
225 }
226 LOG_ALWAYS_FATAL_IF(head != headerLength);
227 writeU32(out.size(), out.data(), 2); // fill the length.
228 return out;
229 }
230
231 class CmapBuilder {
232 public:
233 static constexpr size_t kEncodingTableHead = 4;
234 static constexpr size_t kEncodingTableSize = 8;
235
CmapBuilder(int numTables)236 CmapBuilder(int numTables) : mNumTables(numTables), mCurrentTableIndex(0) {
237 const size_t headerSize =
238 2 /* version */ + 2 /* numTables */ + kEncodingTableSize * numTables;
239 out.resize(headerSize);
240 writeU16(0, out.data(), 0);
241 writeU16(numTables, out.data(), 2);
242 }
243
appendTable(uint16_t platformId,uint16_t encodingId,const std::vector<uint8_t> & table)244 void appendTable(uint16_t platformId, uint16_t encodingId, const std::vector<uint8_t>& table) {
245 appendEncodingTable(platformId, encodingId, out.size());
246 out.insert(out.end(), table.begin(), table.end());
247 }
248
build()249 std::vector<uint8_t> build() {
250 LOG_ALWAYS_FATAL_IF(mCurrentTableIndex != mNumTables);
251 return out;
252 }
253
254 // Helper functions.
buildSingleFormat4Cmap(uint16_t platformId,uint16_t encodingId,const std::vector<uint16_t> & ranges)255 static std::vector<uint8_t> buildSingleFormat4Cmap(uint16_t platformId, uint16_t encodingId,
256 const std::vector<uint16_t>& ranges) {
257 CmapBuilder builder(1);
258 builder.appendTable(platformId, encodingId, buildCmapFormat4Table(ranges));
259 return builder.build();
260 }
261
buildSingleFormat12Cmap(uint16_t platformId,uint16_t encodingId,const std::vector<uint32_t> & ranges)262 static std::vector<uint8_t> buildSingleFormat12Cmap(uint16_t platformId, uint16_t encodingId,
263 const std::vector<uint32_t>& ranges) {
264 CmapBuilder builder(1);
265 builder.appendTable(platformId, encodingId, buildCmapFormat12Table(ranges));
266 return builder.build();
267 }
268
269 private:
appendEncodingTable(uint16_t platformId,uint16_t encodingId,uint32_t offset)270 void appendEncodingTable(uint16_t platformId, uint16_t encodingId, uint32_t offset) {
271 LOG_ALWAYS_FATAL_IF(mCurrentTableIndex == mNumTables);
272
273 const size_t currentEncodingTableHead =
274 kEncodingTableHead + mCurrentTableIndex * kEncodingTableSize;
275 size_t head = writeU16(platformId, out.data(), currentEncodingTableHead);
276 head = writeU16(encodingId, out.data(), head);
277 head = writeU32(offset, out.data(), head);
278 LOG_ALWAYS_FATAL_IF((head - currentEncodingTableHead) != kEncodingTableSize);
279 mCurrentTableIndex++;
280 }
281
282 int mNumTables;
283 int mCurrentTableIndex;
284 std::vector<uint8_t> out;
285 };
286
TEST(CmapCoverageTest,SingleFormat4_brokenCmap)287 TEST(CmapCoverageTest, SingleFormat4_brokenCmap) {
288 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
289 {
290 SCOPED_TRACE("Reading beyond buffer size - Too small cmap size");
291 std::vector<uint8_t> cmap =
292 CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>({'a', 'a'}));
293
294 SparseBitSet coverage =
295 CmapCoverage::getCoverage(cmap.data(), 3 /* too small */, &vsTables);
296 EXPECT_EQ(0U, coverage.length());
297 EXPECT_TRUE(vsTables.empty());
298 }
299 {
300 SCOPED_TRACE("Reading beyond buffer size - space needed for tables goes beyond cmap size");
301 std::vector<uint8_t> cmap =
302 CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>({'a', 'a'}));
303
304 writeU16(1000, cmap.data(), 2 /* offset of num tables in cmap header */);
305 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
306 EXPECT_EQ(0U, coverage.length());
307 EXPECT_TRUE(vsTables.empty());
308 }
309 {
310 SCOPED_TRACE("Reading beyond buffer size - Invalid offset in encoding table");
311 std::vector<uint8_t> cmap =
312 CmapBuilder::buildSingleFormat4Cmap(0, 0, std::vector<uint16_t>({'a', 'a'}));
313
314 writeU16(1000, cmap.data(), 8 /* offset of the offset in the first encoding record */);
315 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
316 EXPECT_EQ(0U, coverage.length());
317 EXPECT_TRUE(vsTables.empty());
318 }
319 {
320 SCOPED_TRACE("Reversed range");
321 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat4Cmap(
322 0, 0, std::vector<uint16_t>({'b', 'b', 'a', 'a'}));
323
324 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
325 EXPECT_EQ(0U, coverage.length());
326 EXPECT_TRUE(vsTables.empty());
327 }
328 {
329 SCOPED_TRACE("Reversed range - partially readable");
330 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat4Cmap(
331 0, 0, std::vector<uint16_t>({'a', 'a', 'c', 'c', 'b', 'b'}));
332
333 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
334 EXPECT_EQ(0U, coverage.length());
335 EXPECT_TRUE(vsTables.empty());
336 }
337 }
338
TEST(CmapCoverageTest,SingleFormat4)339 TEST(CmapCoverageTest, SingleFormat4) {
340 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
341 struct TestCast {
342 std::string testTitle;
343 uint16_t platformId;
344 uint16_t encodingId;
345 } TEST_CASES[] = {
346 {"Platform 0, Encoding 0", 0, 0}, {"Platform 0, Encoding 1", 0, 1},
347 {"Platform 0, Encoding 2", 0, 2}, {"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 SCOPED_TRACE("Reversed end code points");
495 std::vector<uint8_t> table =
496 buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b', 'a', 'a'}));
497 CmapBuilder builder(1);
498 builder.appendTable(0, 0, table);
499 std::vector<uint8_t> cmap = builder.build();
500
501 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
502 EXPECT_EQ(0U, coverage.length());
503 EXPECT_TRUE(vsTables.empty());
504 }
505 }
506
TEST(CmapCoverageTest,duplicatedCmap4EntryTest)507 TEST(CmapCoverageTest, duplicatedCmap4EntryTest) {
508 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
509 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'b', 'b', 'b'}));
510 CmapBuilder builder(1);
511 builder.appendTable(0, 0, table);
512 std::vector<uint8_t> cmap = builder.build();
513
514 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
515 EXPECT_TRUE(coverage.get('a'));
516 EXPECT_TRUE(coverage.get('b'));
517 EXPECT_TRUE(vsTables.empty());
518 }
519
TEST(CmapCoverageTest,brokenFormat12Table)520 TEST(CmapCoverageTest, brokenFormat12Table) {
521 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
522 {
523 SCOPED_TRACE("Too small cmap size");
524 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
525 table.resize(2); // Remove trailing data.
526
527 CmapBuilder builder(1);
528 builder.appendTable(0, 0, table);
529 std::vector<uint8_t> cmap = builder.build();
530
531 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
532 EXPECT_EQ(0U, coverage.length());
533 EXPECT_TRUE(vsTables.empty());
534 }
535 {
536 SCOPED_TRACE("Too many groups");
537 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
538 writeU32(5000, table.data(), 12 /* num group offset */); // 5000 groups.
539
540 CmapBuilder builder(1);
541 builder.appendTable(0, 0, table);
542 std::vector<uint8_t> cmap = builder.build();
543
544 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
545 EXPECT_EQ(0U, coverage.length());
546 EXPECT_TRUE(vsTables.empty());
547 }
548 {
549 SCOPED_TRACE("Inversed range.");
550 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
551 // Put larger start code point to inverse the range.
552 writeU32('b', table.data(), 16 /* start code point offset in the first group */);
553
554 CmapBuilder builder(1);
555 builder.appendTable(0, 0, table);
556 std::vector<uint8_t> cmap = builder.build();
557
558 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
559 EXPECT_EQ(0U, coverage.length());
560 EXPECT_TRUE(vsTables.empty());
561 }
562 {
563 SCOPED_TRACE("Too large code point");
564 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
565 0, 0, std::vector<uint32_t>({0x110000, 0x110000}));
566
567 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
568 EXPECT_EQ(0U, coverage.length());
569 EXPECT_TRUE(vsTables.empty());
570 }
571 {
572 SCOPED_TRACE("Reversed range");
573 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
574 0, 0, std::vector<uint32_t>({'b', 'b', 'a', 'a'}));
575 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
576 EXPECT_EQ(0U, coverage.length());
577 EXPECT_TRUE(vsTables.empty());
578 }
579 {
580 SCOPED_TRACE("Reversed range - partially readable");
581 std::vector<uint8_t> cmap = CmapBuilder::buildSingleFormat12Cmap(
582 0, 0, std::vector<uint32_t>({'a', 'a', 'c', 'c', 'b', 'b'}));
583 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
584 EXPECT_EQ(0U, coverage.length());
585 EXPECT_TRUE(vsTables.empty());
586 }
587 }
588
TEST(CmapCoverageTest,TableSelection_Priority)589 TEST(CmapCoverageTest, TableSelection_Priority) {
590 std::vector<uint8_t> highestFormat12Table =
591 buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
592 std::vector<uint8_t> highestFormat4Table =
593 buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
594 std::vector<uint8_t> format4 = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
595 std::vector<uint8_t> format12 = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
596
597 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
598 {
599 SCOPED_TRACE("(platform, encoding) = (3, 10) is the highest priority.");
600
601 struct LowerPriorityTable {
602 uint16_t platformId;
603 uint16_t encodingId;
604 const std::vector<uint8_t>& table;
605 } LOWER_PRIORITY_TABLES[] = {
606 {0, 0, format4}, {0, 1, format4}, {0, 2, format4}, {0, 3, format4},
607 {0, 4, format12}, {0, 6, format12}, {3, 1, format4},
608 };
609
610 for (const auto& table : LOWER_PRIORITY_TABLES) {
611 CmapBuilder builder(2);
612 builder.appendTable(table.platformId, table.encodingId, table.table);
613 builder.appendTable(3, 10, highestFormat12Table);
614 std::vector<uint8_t> cmap = builder.build();
615
616 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
617 EXPECT_TRUE(coverage.get('a')); // comes from highest table
618 EXPECT_FALSE(coverage.get('b')); // should not use other table.
619 EXPECT_TRUE(vsTables.empty());
620 }
621 }
622 {
623 SCOPED_TRACE("(platform, encoding) = (3, 1) case");
624
625 struct LowerPriorityTable {
626 uint16_t platformId;
627 uint16_t encodingId;
628 const std::vector<uint8_t>& table;
629 } LOWER_PRIORITY_TABLES[] = {
630 {0, 0, format4}, {0, 1, format4}, {0, 2, format4}, {0, 3, format4},
631 };
632
633 for (const auto& table : LOWER_PRIORITY_TABLES) {
634 CmapBuilder builder(2);
635 builder.appendTable(table.platformId, table.encodingId, table.table);
636 builder.appendTable(3, 1, highestFormat4Table);
637 std::vector<uint8_t> cmap = builder.build();
638
639 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
640 EXPECT_TRUE(coverage.get('a')); // comes from highest table
641 EXPECT_FALSE(coverage.get('b')); // should not use other table.
642 EXPECT_TRUE(vsTables.empty());
643 }
644 }
645 }
646
TEST(CmapCoverageTest,TableSelection_SkipBrokenFormat4Table)647 TEST(CmapCoverageTest, TableSelection_SkipBrokenFormat4Table) {
648 std::vector<uint8_t> validTable = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
649 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
650 {
651 SCOPED_TRACE("Unsupported format");
652 CmapBuilder builder(2);
653 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
654 writeU16(0, table.data(), 0 /* format offset */);
655 builder.appendTable(3, 1, table);
656 builder.appendTable(0, 0, validTable);
657 std::vector<uint8_t> cmap = builder.build();
658
659 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
660 EXPECT_TRUE(coverage.get('a')); // comes from valid table
661 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
662 EXPECT_TRUE(vsTables.empty());
663 }
664 {
665 SCOPED_TRACE("Invalid language");
666 CmapBuilder builder(2);
667 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
668 writeU16(1, table.data(), 4 /* language offset */);
669 builder.appendTable(3, 1, table);
670 builder.appendTable(0, 0, validTable);
671 std::vector<uint8_t> cmap = builder.build();
672
673 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
674 EXPECT_TRUE(coverage.get('a')); // comes from valid table
675 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
676 EXPECT_TRUE(vsTables.empty());
677 }
678 {
679 SCOPED_TRACE("Invalid length");
680 CmapBuilder builder(2);
681 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
682 writeU16(5000, table.data(), 2 /* length offset */);
683 builder.appendTable(3, 1, table);
684 builder.appendTable(0, 0, validTable);
685 std::vector<uint8_t> cmap = builder.build();
686
687 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
688 EXPECT_TRUE(coverage.get('a')); // comes from valid table
689 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
690 EXPECT_TRUE(vsTables.empty());
691 }
692 }
693
TEST(CmapCoverageTest,TableSelection_SkipBrokenFormat12Table)694 TEST(CmapCoverageTest, TableSelection_SkipBrokenFormat12Table) {
695 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
696 std::vector<uint8_t> validTable = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
697 {
698 SCOPED_TRACE("Unsupported format");
699 CmapBuilder builder(2);
700 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
701 writeU16(0, table.data(), 0 /* format offset */);
702 builder.appendTable(3, 1, table);
703 builder.appendTable(0, 0, validTable);
704 std::vector<uint8_t> cmap = builder.build();
705
706 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
707 EXPECT_TRUE(coverage.get('a')); // comes from valid table
708 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
709 EXPECT_TRUE(vsTables.empty());
710 }
711 {
712 SCOPED_TRACE("Invalid language");
713 CmapBuilder builder(2);
714 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
715 writeU32(1, table.data(), 8 /* language offset */);
716 builder.appendTable(3, 1, table);
717 builder.appendTable(0, 0, validTable);
718 std::vector<uint8_t> cmap = builder.build();
719
720 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
721 EXPECT_TRUE(coverage.get('a')); // comes from valid table
722 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
723 EXPECT_TRUE(vsTables.empty());
724 }
725 {
726 SCOPED_TRACE("Invalid length");
727 CmapBuilder builder(2);
728 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
729 writeU32(5000, table.data(), 4 /* length offset */);
730 builder.appendTable(3, 1, table);
731 builder.appendTable(0, 0, validTable);
732 std::vector<uint8_t> cmap = builder.build();
733
734 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
735 EXPECT_TRUE(coverage.get('a')); // comes from valid table
736 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
737 EXPECT_TRUE(vsTables.empty());
738 }
739 }
740
TEST(CmapCoverageTest,TableSelection_VSTable)741 TEST(CmapCoverageTest, TableSelection_VSTable) {
742 std::vector<uint8_t> smallLetterTable =
743 buildCmapFormat12Table(std::vector<uint32_t>({'a', 'z'}));
744 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
745 {0xFE0E, {'a', 'b'}, {} /* no non-default UVS table */},
746 {0xFE0F, {} /* no default UVS table */, {'a', 'b'}},
747 {0xE0100, {'a', 'a'}, {'b'}},
748 }));
749 CmapBuilder builder(2);
750 builder.appendTable(3, 1, smallLetterTable);
751 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
752 std::vector<uint8_t> cmap = builder.build();
753
754 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
755 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
756 EXPECT_TRUE(coverage.get('a'));
757 ASSERT_FALSE(vsTables.empty());
758
759 const uint16_t vs15Index = getVsIndex(0xFE0E);
760 ASSERT_LT(vs15Index, vsTables.size());
761 ASSERT_TRUE(vsTables[vs15Index]);
762 EXPECT_TRUE(vsTables[vs15Index]->get('a'));
763 EXPECT_TRUE(vsTables[vs15Index]->get('b'));
764
765 const uint16_t vs16Index = getVsIndex(0xFE0F);
766 ASSERT_LT(vs16Index, vsTables.size());
767 ASSERT_TRUE(vsTables[vs16Index]);
768 EXPECT_TRUE(vsTables[vs16Index]->get('a'));
769 EXPECT_TRUE(vsTables[vs16Index]->get('b'));
770
771 const uint16_t vs17Index = getVsIndex(0xE0100);
772 ASSERT_LT(vs17Index, vsTables.size());
773 ASSERT_TRUE(vsTables[vs17Index]);
774 EXPECT_TRUE(vsTables[vs17Index]->get('a'));
775 EXPECT_TRUE(vsTables[vs17Index]->get('b'));
776 }
777
TEST(CmapCoverageTest,TableSelection_InterSection)778 TEST(CmapCoverageTest, TableSelection_InterSection) {
779 std::vector<uint8_t> smallLetterTable =
780 buildCmapFormat12Table(std::vector<uint32_t>({'a', 'z'}));
781 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
782 {0xFE0E,
783 {'a', 'e'},
784 {
785 'c', 'd',
786 }},
787 {0xFE0F, {'c', 'e'}, {'a', 'b', 'c', 'd', 'e'}},
788 {0xE0100, {'a', 'c'}, {'b', 'c', 'd'}},
789 {0xE0101, {'b', 'd'}, {'a', 'b', 'c', 'd'}},
790 {0xE0102, {'a', 'c', 'd', 'g'}, {'b', 'c', 'd', 'e', 'f', 'g', 'h'}},
791 {0xE0103,
792 {'a', 'f'},
793 {
794 'b', 'd',
795 }},
796 }));
797 CmapBuilder builder(2);
798 builder.appendTable(3, 1, smallLetterTable);
799 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
800 std::vector<uint8_t> cmap = builder.build();
801
802 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
803 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
804 EXPECT_TRUE(coverage.get('a'));
805 ASSERT_FALSE(vsTables.empty());
806
807 const uint16_t vs15Index = getVsIndex(0xFE0E);
808 ASSERT_LT(vs15Index, vsTables.size());
809 ASSERT_TRUE(vsTables[vs15Index]);
810 EXPECT_TRUE(vsTables[vs15Index]->get('a'));
811 EXPECT_TRUE(vsTables[vs15Index]->get('b'));
812 EXPECT_TRUE(vsTables[vs15Index]->get('c'));
813 EXPECT_TRUE(vsTables[vs15Index]->get('d'));
814 EXPECT_TRUE(vsTables[vs15Index]->get('e'));
815
816 const uint16_t vs16Index = getVsIndex(0xFE0F);
817 ASSERT_LT(vs16Index, vsTables.size());
818 ASSERT_TRUE(vsTables[vs16Index]);
819 EXPECT_TRUE(vsTables[vs16Index]->get('a'));
820 EXPECT_TRUE(vsTables[vs16Index]->get('b'));
821 EXPECT_TRUE(vsTables[vs16Index]->get('c'));
822 EXPECT_TRUE(vsTables[vs16Index]->get('d'));
823 EXPECT_TRUE(vsTables[vs16Index]->get('e'));
824
825 const uint16_t vs17Index = getVsIndex(0xE0100);
826 ASSERT_LT(vs17Index, vsTables.size());
827 ASSERT_TRUE(vsTables[vs17Index]);
828 EXPECT_TRUE(vsTables[vs17Index]->get('a'));
829 EXPECT_TRUE(vsTables[vs17Index]->get('b'));
830 EXPECT_TRUE(vsTables[vs17Index]->get('c'));
831 EXPECT_TRUE(vsTables[vs17Index]->get('d'));
832
833 const uint16_t vs18Index = getVsIndex(0xE0101);
834 ASSERT_LT(vs18Index, vsTables.size());
835 ASSERT_TRUE(vsTables[vs18Index]);
836 EXPECT_TRUE(vsTables[vs18Index]->get('a'));
837 EXPECT_TRUE(vsTables[vs18Index]->get('b'));
838 EXPECT_TRUE(vsTables[vs18Index]->get('c'));
839 EXPECT_TRUE(vsTables[vs18Index]->get('d'));
840
841 const uint16_t vs19Index = getVsIndex(0xE0102);
842 ASSERT_LT(vs19Index, vsTables.size());
843 ASSERT_TRUE(vsTables[vs19Index]);
844 EXPECT_TRUE(vsTables[vs19Index]->get('a'));
845 EXPECT_TRUE(vsTables[vs19Index]->get('b'));
846 EXPECT_TRUE(vsTables[vs19Index]->get('c'));
847 EXPECT_TRUE(vsTables[vs19Index]->get('d'));
848 EXPECT_TRUE(vsTables[vs19Index]->get('e'));
849 EXPECT_TRUE(vsTables[vs19Index]->get('f'));
850 EXPECT_TRUE(vsTables[vs19Index]->get('g'));
851 EXPECT_TRUE(vsTables[vs19Index]->get('h'));
852
853 const uint16_t vs20Index = getVsIndex(0xE0103);
854 ASSERT_LT(vs20Index, vsTables.size());
855 ASSERT_TRUE(vsTables[vs20Index]);
856 EXPECT_TRUE(vsTables[vs20Index]->get('a'));
857 EXPECT_TRUE(vsTables[vs20Index]->get('b'));
858 EXPECT_TRUE(vsTables[vs20Index]->get('c'));
859 EXPECT_TRUE(vsTables[vs20Index]->get('d'));
860 EXPECT_TRUE(vsTables[vs20Index]->get('e'));
861 EXPECT_TRUE(vsTables[vs20Index]->get('f'));
862 }
863
TEST(CmapCoverageTest,TableSelection_brokenVSTable)864 TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
865 std::vector<uint8_t> cmap12Table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'z'}));
866 {
867 SCOPED_TRACE("Too small cmap size");
868 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
869 std::vector<VariationSelectorRecord>({{0xFE0E, {'a', 'a'}, {'b'}}}));
870 CmapBuilder builder(2);
871 builder.appendTable(3, 1, cmap12Table);
872 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
873 std::vector<uint8_t> cmap = builder.build();
874
875 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
876 SparseBitSet coverage =
877 CmapCoverage::getCoverage(cmap.data(), 3 /* too small size */, &vsTables);
878 EXPECT_FALSE(coverage.get('a'));
879 ASSERT_TRUE(vsTables.empty());
880 }
881 {
882 SCOPED_TRACE("Too many variation records");
883 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
884 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'a'}, {'b'}}}));
885 writeU32(5000, vsTable.data(), 6 /* numVarSelectorRecord offset */);
886 CmapBuilder builder(2);
887 builder.appendTable(3, 1, cmap12Table);
888 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
889 std::vector<uint8_t> cmap = builder.build();
890
891 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
892 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
893 ASSERT_TRUE(vsTables.empty());
894 }
895 {
896 SCOPED_TRACE("Invalid default UVS offset in variation records");
897 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
898 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'a'}, {'b'}}}));
899 writeU32(5000, vsTable.data(), 13 /* defaultUVSffset offset in the first record */);
900 CmapBuilder builder(2);
901 builder.appendTable(3, 1, cmap12Table);
902 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
903 std::vector<uint8_t> cmap = builder.build();
904
905 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
906 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
907 ASSERT_TRUE(vsTables.empty());
908 }
909 {
910 SCOPED_TRACE("Invalid non default UVS offset in variation records");
911 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
912 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'a'}, {'b'}}}));
913 writeU32(5000, vsTable.data(), 17 /* nonDefaultUVSffset offset in the first record */);
914 CmapBuilder builder(2);
915 builder.appendTable(3, 1, cmap12Table);
916 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
917 std::vector<uint8_t> cmap = builder.build();
918
919 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
920 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
921 ASSERT_TRUE(vsTables.empty());
922 }
923 {
924 SCOPED_TRACE("Too many ranges entry in default UVS table");
925 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
926 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'a'}, {'b'}}}));
927 // 21 is the offset of the numUnicodeValueRanges in the fist defulat UVS table.
928 writeU32(5000, vsTable.data(), 21);
929 CmapBuilder builder(2);
930 builder.appendTable(3, 1, cmap12Table);
931 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
932 std::vector<uint8_t> cmap = builder.build();
933
934 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
935 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
936 ASSERT_TRUE(vsTables.empty());
937 }
938 {
939 SCOPED_TRACE("Too many ranges entry in non default UVS table");
940 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
941 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'a'}, {'b'}}}));
942 // 29 is the offset of the numUnicodeValueRanges in the fist defulat UVS table.
943 writeU32(5000, vsTable.data(), 29);
944 CmapBuilder builder(2);
945 builder.appendTable(3, 1, cmap12Table);
946 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
947 std::vector<uint8_t> cmap = builder.build();
948
949 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
950 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
951 ASSERT_TRUE(vsTables.empty());
952 }
953 {
954 SCOPED_TRACE("Reversed range in default UVS table");
955 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
956 std::vector<VariationSelectorRecord>({{0xFE0F, {'b', 'b', 'a', 'a'}, {}}}));
957 CmapBuilder builder(2);
958 builder.appendTable(3, 1, cmap12Table);
959 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
960 std::vector<uint8_t> cmap = builder.build();
961
962 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
963 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
964 ASSERT_TRUE(vsTables.empty());
965 }
966 {
967 SCOPED_TRACE("Reversed range in default UVS table - partially readable");
968 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>(
969 {{0xFE0F, {'a', 'a', 'c', 'c', 'b', 'b'}, {}}}));
970 CmapBuilder builder(2);
971 builder.appendTable(3, 1, cmap12Table);
972 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
973 std::vector<uint8_t> cmap = builder.build();
974
975 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
976 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
977 ASSERT_TRUE(vsTables.empty());
978 }
979 {
980 SCOPED_TRACE("Reversed mapping entries in non default UVS table");
981 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
982 std::vector<VariationSelectorRecord>({{0xFE0F, {}, {'b', 'a'}}}));
983 CmapBuilder builder(2);
984 builder.appendTable(3, 1, cmap12Table);
985 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
986 std::vector<uint8_t> cmap = builder.build();
987
988 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
989 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
990 ASSERT_TRUE(vsTables.empty());
991 }
992 {
993 SCOPED_TRACE("Reversed mapping entries in non default UVS table");
994 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
995 std::vector<VariationSelectorRecord>({{0xFE0F, {}, {'a', 'c', 'b'}}}));
996 CmapBuilder builder(2);
997 builder.appendTable(3, 1, cmap12Table);
998 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
999 std::vector<uint8_t> cmap = builder.build();
1000
1001 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1002 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1003 ASSERT_TRUE(vsTables.empty());
1004 }
1005 {
1006 // http://b/70808908
1007 SCOPED_TRACE("OOB access due to integer overflow in non default UVS table");
1008 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
1009 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'a'}, {'b'}}}));
1010 // 6 is the offset of the numRecords in the Cmap format14 subtable header.
1011 writeU32(0x1745d174 /* 2^32 / kRecordSize(=11) */, vsTable.data(), 6);
1012 CmapBuilder builder(2);
1013 builder.appendTable(3, 1, cmap12Table);
1014 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1015 std::vector<uint8_t> cmap = builder.build();
1016
1017 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1018 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1019 ASSERT_TRUE(vsTables.empty());
1020 }
1021 {
1022 // http://b/70808908
1023 SCOPED_TRACE("OOB access due to integer overflow in non default UVS table");
1024 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
1025 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'a'}, {'b'}}}));
1026 // 29 is the offset of the numUVSMappings in the fist non defulat UVS table.
1027 writeU32(0x33333333 /* 2^32 / kUVSMappingRecordSize(=5) */, vsTable.data(), 29);
1028 CmapBuilder builder(2);
1029 builder.appendTable(3, 1, cmap12Table);
1030 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1031 std::vector<uint8_t> cmap = builder.build();
1032
1033 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1034 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1035 ASSERT_TRUE(vsTables.empty());
1036 }
1037 {
1038 // http://b/70808908
1039 SCOPED_TRACE("OOB access due to integer overflow in default UVS table");
1040 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
1041 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'a'}, {'b'}}}));
1042 // 21 is the offset of the numUnicodeValueRanges in the fist defulat UVS table.
1043 writeU32(0x40000000 /* 2^32 / kUnicodeRangeRecordSize(=4) */, vsTable.data(), 21);
1044 CmapBuilder builder(2);
1045 builder.appendTable(3, 1, cmap12Table);
1046 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1047 std::vector<uint8_t> cmap = builder.build();
1048
1049 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1050 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1051 ASSERT_TRUE(vsTables.empty());
1052 }
1053 {
1054 // http://b/70808908
1055 SCOPED_TRACE("OOB access due to integer overflow in non default UVS table");
1056 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
1057 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'a'}, {'b'}}}));
1058 // 6 is the offset of the numRecords in the Cmap format14 subtable header.
1059 writeU32(0x1745d174 /* 2^32 / kRecordSize(=11) */, vsTable.data(), 6);
1060 CmapBuilder builder(2);
1061 builder.appendTable(3, 1, cmap12Table);
1062 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1063 std::vector<uint8_t> cmap = builder.build();
1064
1065 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1066 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1067 ASSERT_TRUE(vsTables.empty());
1068 }
1069 {
1070 // http://b/70808908
1071 SCOPED_TRACE("OOB access due to integer overflow in non default UVS table");
1072 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
1073 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'a'}, {'b'}}}));
1074 // 29 is the offset of the numUVSMappings in the fist non defulat UVS table.
1075 writeU32(0x33333333 /* 2^32 / kUVSMappingRecordSize(=5) */, vsTable.data(), 29);
1076 CmapBuilder builder(2);
1077 builder.appendTable(3, 1, cmap12Table);
1078 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1079 std::vector<uint8_t> cmap = builder.build();
1080
1081 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1082 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1083 ASSERT_TRUE(vsTables.empty());
1084 }
1085 {
1086 // http://b/70808908
1087 SCOPED_TRACE("OOB access due to integer overflow in default UVS table");
1088 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
1089 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'a'}, {'b'}}}));
1090 // 21 is the offset of the numUnicodeValueRanges in the fist defulat UVS table.
1091 writeU32(0x40000000 /* 2^32 / kUnicodeRangeRecordSize(=4) */, vsTable.data(), 21);
1092 CmapBuilder builder(2);
1093 builder.appendTable(3, 1, cmap12Table);
1094 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1095 std::vector<uint8_t> cmap = builder.build();
1096
1097 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1098 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1099 ASSERT_TRUE(vsTables.empty());
1100 }
1101 }
1102
TEST(CmapCoverageTest,TableSelection_brokenVSTable_bestEffort)1103 TEST(CmapCoverageTest, TableSelection_brokenVSTable_bestEffort) {
1104 std::vector<uint8_t> cmap12Table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
1105 {
1106 SCOPED_TRACE("Invalid default UVS offset in variation records");
1107 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
1108 {0xFE0E, {'a', 'a'}, {'b'}}, {0xFE0F, {'a', 'a'}, {'b'}},
1109 }));
1110 writeU32(5000, vsTable.data(), 13 /* defaultUVSffset offset in the record for 0xFE0E */);
1111 CmapBuilder builder(2);
1112 builder.appendTable(3, 1, cmap12Table);
1113 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1114 std::vector<uint8_t> cmap = builder.build();
1115
1116 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1117 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1118
1119 const uint16_t vs16Index = getVsIndex(0xFE0F);
1120 ASSERT_LT(vs16Index, vsTables.size());
1121 ASSERT_TRUE(vsTables[vs16Index]);
1122 EXPECT_TRUE(vsTables[vs16Index]->get('a'));
1123 EXPECT_TRUE(vsTables[vs16Index]->get('b'));
1124
1125 const uint16_t vs15Index = getVsIndex(0xFE0E);
1126 EXPECT_FALSE(vsTables[vs15Index]);
1127 }
1128 {
1129 SCOPED_TRACE("Invalid non default UVS offset in variation records");
1130 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
1131 {0xFE0E, {'a', 'a'}, {'b'}}, {0xFE0F, {'a', 'a'}, {'b'}},
1132 }));
1133 writeU32(5000, vsTable.data(), 17 /* nonDefaultUVSffset offset in the first record */);
1134 CmapBuilder builder(2);
1135 builder.appendTable(3, 1, cmap12Table);
1136 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1137 std::vector<uint8_t> cmap = builder.build();
1138
1139 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1140 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1141
1142 const uint16_t vs16Index = getVsIndex(0xFE0F);
1143 ASSERT_LT(vs16Index, vsTables.size());
1144 ASSERT_TRUE(vsTables[vs16Index]);
1145 EXPECT_TRUE(vsTables[vs16Index]->get('a'));
1146 EXPECT_TRUE(vsTables[vs16Index]->get('b'));
1147
1148 const uint16_t vs15Index = getVsIndex(0xFE0E);
1149 EXPECT_FALSE(vsTables[vs15Index]);
1150 }
1151 {
1152 SCOPED_TRACE("Unknown variation selectors.");
1153 std::vector<uint8_t> vsTable = buildCmapFormat14Table(std::vector<VariationSelectorRecord>({
1154 {0xFE0F, {'a', 'a'}, {'b'}}, {0xEFFFF, {'a', 'a'}, {'b'}},
1155 }));
1156 CmapBuilder builder(2);
1157 builder.appendTable(3, 1, cmap12Table);
1158 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1159 std::vector<uint8_t> cmap = builder.build();
1160
1161 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1162 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1163
1164 const uint16_t vs16Index = getVsIndex(0xFE0F);
1165 ASSERT_LT(vs16Index, vsTables.size());
1166 ASSERT_TRUE(vsTables[vs16Index]);
1167 EXPECT_TRUE(vsTables[vs16Index]->get('a'));
1168 EXPECT_TRUE(vsTables[vs16Index]->get('b'));
1169 }
1170 }
1171
1172 // Used only for better looking of range definition.
1173 #define RANGE(x, y) x, y
1174
TEST(CmapCoverageTest,TableSelection_defaultUVSPointMissingGlyph)1175 TEST(CmapCoverageTest, TableSelection_defaultUVSPointMissingGlyph) {
1176 std::vector<uint8_t> baseTable = buildCmapFormat12Table(std::vector<uint32_t>(
1177 {RANGE('a', 'e'), RANGE('g', 'h'), RANGE('j', 'j'), RANGE('m', 'z')}));
1178 std::vector<uint8_t> vsTable = buildCmapFormat14Table(
1179 std::vector<VariationSelectorRecord>({{0xFE0F, {'a', 'z'}, {}}}));
1180
1181 CmapBuilder builder(2);
1182 builder.appendTable(3, 1, baseTable);
1183 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1184 std::vector<uint8_t> cmap = builder.build();
1185
1186 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1187 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1188 const uint16_t vsIndex = getVsIndex(0xFE0F);
1189 ASSERT_LT(vsIndex, vsTables.size());
1190 ASSERT_TRUE(vsTables[vsIndex]);
1191
1192 for (char c = 'a'; c <= 'z'; ++c) {
1193 // Default UVS table points the variation sequence to the glyph of the base code point.
1194 // Thus, if the base code point is not supported, we should exclude them.
1195 EXPECT_EQ(coverage.get(c), vsTables[vsIndex]->get(c)) << c;
1196 }
1197 }
1198
1199 #undef RANGE
1200
TEST(CmapCoverageTest,TableSelection_vsTableOnly)1201 TEST(CmapCoverageTest, TableSelection_vsTableOnly) {
1202 std::vector<uint8_t> vsTable =
1203 buildCmapFormat14Table(std::vector<VariationSelectorRecord>({{0xFE0F, {}, {'a'}}}));
1204
1205 CmapBuilder builder(1);
1206 builder.appendTable(VS_PLATFORM_ID, VS_ENCODING_ID, vsTable);
1207 std::vector<uint8_t> cmap = builder.build();
1208
1209 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
1210 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
1211 const uint16_t vsIndex = getVsIndex(0xFE0F);
1212 ASSERT_LT(vsIndex, vsTables.size());
1213 ASSERT_TRUE(vsTables[vsIndex]);
1214 EXPECT_TRUE(vsTables[vsIndex]->get('a'));
1215 }
1216 } // namespace minikin
1217