• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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