[go: nahoru, domu]

blob: fe2d7ba31a161811c8d2fc2c9f8ae270420b34d6 [file] [log] [blame]
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -07001/*
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>
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -070021#include <minikin/CmapCoverage.h>
Seigo Nonaka91961942017-03-17 18:35:24 -070022#include <minikin/SparseBitSet.h>
23
24#include "MinikinInternal.h"
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -070025
26namespace minikin {
27
Seigo Nonaka91961942017-03-17 18:35:24 -070028static constexpr uint16_t VS_PLATFORM_ID = 0;
29static constexpr uint16_t VS_ENCODING_ID = 5;
30
31size_t writeU8(uint8_t x, uint8_t* out, size_t offset) {
32 out[offset] = x;
33 return offset + 1;
34}
35
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -070036size_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
42size_t writeI16(int16_t sx, uint8_t* out, size_t offset) {
43 return writeU16(static_cast<uint16_t>(sx), out, offset);
44}
45
Seigo Nonaka91961942017-03-17 18:35:24 -070046size_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
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -070053size_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.
64static 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.
115static 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
Seigo Nonaka91961942017-03-17 18:35:24 -0700145struct VariationSelectorRecord {
146 uint32_t codePoint;
147 std::vector<uint32_t> defaultUVSRanges;
148 std::vector<uint32_t> nonDefaultUVS;
149
150 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
171 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
190static 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
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700228class CmapBuilder {
229public:
230 static constexpr size_t kEncodingTableHead = 4;
231 static constexpr size_t kEncodingTableSize = 8;
232
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
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
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700247 std::vector<uint8_t> build() {
248 LOG_ALWAYS_FATAL_IF(mCurrentTableIndex != mNumTables);
249 return out;
250 }
251
252 // Helper functions.
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
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
267private:
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
285TEST(CmapCoverageTest, SingleFormat4_brokenCmap) {
Seigo Nonaka91961942017-03-17 18:35:24 -0700286 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700287 {
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700292 SparseBitSet coverage =
293 CmapCoverage::getCoverage(cmap.data(), 3 /* too small */, &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700294 EXPECT_EQ(0U, coverage.length());
Seigo Nonaka91961942017-03-17 18:35:24 -0700295 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700296 }
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 */);
Seigo Nonaka91961942017-03-17 18:35:24 -0700303 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700304 EXPECT_EQ(0U, coverage.length());
Seigo Nonaka91961942017-03-17 18:35:24 -0700305 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700306 }
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 */);
Seigo Nonaka91961942017-03-17 18:35:24 -0700313 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700314 EXPECT_EQ(0U, coverage.length());
Seigo Nonaka91961942017-03-17 18:35:24 -0700315 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700316 }
Seigo Nonaka0dfb3522017-07-07 11:39:58 -0700317 {
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 }
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700335}
336
337TEST(CmapCoverageTest, SingleFormat4) {
Seigo Nonaka91961942017-03-17 18:35:24 -0700338 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700339 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'}));
Seigo Nonaka91961942017-03-17 18:35:24 -0700355 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700356 EXPECT_TRUE(coverage.get('a'));
357 EXPECT_FALSE(coverage.get('b'));
Seigo Nonaka91961942017-03-17 18:35:24 -0700358 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700359 }
360}
361
362TEST(CmapCoverageTest, SingleFormat12) {
Seigo Nonaka91961942017-03-17 18:35:24 -0700363 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700364
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'}));
Seigo Nonaka91961942017-03-17 18:35:24 -0700379 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700380 EXPECT_TRUE(coverage.get('a'));
381 EXPECT_FALSE(coverage.get('b'));
Seigo Nonaka91961942017-03-17 18:35:24 -0700382 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700383 }
384}
385
Seigo Nonaka818fbee2017-04-14 11:08:12 -0700386TEST(CmapCoverageTest, Format12_beyondTheUnicodeLimit) {
Seigo Nonaka91961942017-03-17 18:35:24 -0700387 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
Seigo Nonaka818fbee2017-04-14 11:08:12 -0700388 {
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700393 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonaka818fbee2017-04-14 11:08:12 -0700394 EXPECT_TRUE(coverage.get('a'));
395 EXPECT_FALSE(coverage.get(0x110000));
Seigo Nonaka91961942017-03-17 18:35:24 -0700396 EXPECT_TRUE(vsTables.empty());
Seigo Nonaka818fbee2017-04-14 11:08:12 -0700397 }
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700403 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonaka818fbee2017-04-14 11:08:12 -0700404 EXPECT_TRUE(coverage.get('a'));
405 EXPECT_TRUE(coverage.get(0x10FF00));
406 EXPECT_TRUE(coverage.get(0x10FFFF));
407 EXPECT_FALSE(coverage.get(0x110000));
Seigo Nonaka91961942017-03-17 18:35:24 -0700408 EXPECT_TRUE(vsTables.empty());
Seigo Nonaka818fbee2017-04-14 11:08:12 -0700409 }
410}
411
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700412TEST(CmapCoverageTest, notSupportedEncodings) {
Seigo Nonaka91961942017-03-17 18:35:24 -0700413 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700414
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'}));
Seigo Nonaka91961942017-03-17 18:35:24 -0700447 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700448 EXPECT_EQ(0U, coverage.length());
Seigo Nonaka91961942017-03-17 18:35:24 -0700449 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700450 }
451}
452
453TEST(CmapCoverageTest, brokenFormat4Table) {
Seigo Nonaka91961942017-03-17 18:35:24 -0700454 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700455 {
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700464 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700465 EXPECT_EQ(0U, coverage.length());
Seigo Nonaka91961942017-03-17 18:35:24 -0700466 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700467 }
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700476 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700477 EXPECT_EQ(0U, coverage.length());
Seigo Nonaka91961942017-03-17 18:35:24 -0700478 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700479 }
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700489 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700490 EXPECT_EQ(0U, coverage.length());
Seigo Nonaka91961942017-03-17 18:35:24 -0700491 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700492 }
493}
494
495TEST(CmapCoverageTest, brokenFormat12Table) {
Seigo Nonaka91961942017-03-17 18:35:24 -0700496 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700497 {
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700506 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700507 EXPECT_EQ(0U, coverage.length());
Seigo Nonaka91961942017-03-17 18:35:24 -0700508 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700509 }
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700519 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700520 EXPECT_EQ(0U, coverage.length());
Seigo Nonaka91961942017-03-17 18:35:24 -0700521 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700522 }
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700533 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700534 EXPECT_EQ(0U, coverage.length());
Seigo Nonaka91961942017-03-17 18:35:24 -0700535 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700536 }
Seigo Nonaka818fbee2017-04-14 11:08:12 -0700537 {
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700542 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonaka818fbee2017-04-14 11:08:12 -0700543 EXPECT_EQ(0U, coverage.length());
Seigo Nonaka91961942017-03-17 18:35:24 -0700544 EXPECT_TRUE(vsTables.empty());
Seigo Nonaka818fbee2017-04-14 11:08:12 -0700545 }
Seigo Nonaka0dfb3522017-07-07 11:39:58 -0700546 {
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 }
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700562}
563
564TEST(CmapCoverageTest, TableSelection_Priority) {
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700565 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
Seigo Nonaka91961942017-03-17 18:35:24 -0700572 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700573 {
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700596 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700597 EXPECT_TRUE(coverage.get('a')); // comes from highest table
598 EXPECT_FALSE(coverage.get('b')); // should not use other table.
Seigo Nonaka91961942017-03-17 18:35:24 -0700599 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700600 }
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
Seigo Nonaka91961942017-03-17 18:35:24 -0700622 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700623 EXPECT_TRUE(coverage.get('a')); // comes from highest table
624 EXPECT_FALSE(coverage.get('b')); // should not use other table.
Seigo Nonaka91961942017-03-17 18:35:24 -0700625 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700626 }
627 }
628}
629
630TEST(CmapCoverageTest, TableSelection_SkipBrokenFormat4Table) {
Seigo Nonaka91961942017-03-17 18:35:24 -0700631 std::vector<uint8_t> validTable = buildCmapFormat4Table(std::vector<uint16_t>({'a', 'a'}));
632 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700633 {
634 SCOPED_TRACE("Unsupported format");
635 CmapBuilder builder(2);
Seigo Nonaka91961942017-03-17 18:35:24 -0700636 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700637 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
Seigo Nonaka91961942017-03-17 18:35:24 -0700642 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700643 EXPECT_TRUE(coverage.get('a')); // comes from valid table
644 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
Seigo Nonaka91961942017-03-17 18:35:24 -0700645 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700646 }
647 {
648 SCOPED_TRACE("Invalid language");
649 CmapBuilder builder(2);
Seigo Nonaka91961942017-03-17 18:35:24 -0700650 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700651 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
Seigo Nonaka91961942017-03-17 18:35:24 -0700656 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700657 EXPECT_TRUE(coverage.get('a')); // comes from valid table
658 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
Seigo Nonaka91961942017-03-17 18:35:24 -0700659 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700660 }
661 {
662 SCOPED_TRACE("Invalid length");
663 CmapBuilder builder(2);
Seigo Nonaka91961942017-03-17 18:35:24 -0700664 std::vector<uint8_t> table = buildCmapFormat4Table(std::vector<uint16_t>({'b', 'b'}));
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700665 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
Seigo Nonaka91961942017-03-17 18:35:24 -0700670 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700671 EXPECT_TRUE(coverage.get('a')); // comes from valid table
672 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
Seigo Nonaka91961942017-03-17 18:35:24 -0700673 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700674 }
675}
676
677TEST(CmapCoverageTest, TableSelection_SkipBrokenFormat12Table) {
Seigo Nonaka91961942017-03-17 18:35:24 -0700678 std::vector<std::unique_ptr<SparseBitSet>> vsTables;
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700679 std::vector<uint8_t> validTable =
680 buildCmapFormat12Table(std::vector<uint32_t>({'a', 'a'}));
681 {
682 SCOPED_TRACE("Unsupported format");
683 CmapBuilder builder(2);
Seigo Nonaka91961942017-03-17 18:35:24 -0700684 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700685 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
Seigo Nonaka91961942017-03-17 18:35:24 -0700690 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700691 EXPECT_TRUE(coverage.get('a')); // comes from valid table
692 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
Seigo Nonaka91961942017-03-17 18:35:24 -0700693 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700694 }
695 {
696 SCOPED_TRACE("Invalid language");
697 CmapBuilder builder(2);
Seigo Nonaka91961942017-03-17 18:35:24 -0700698 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700699 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
Seigo Nonaka91961942017-03-17 18:35:24 -0700704 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700705 EXPECT_TRUE(coverage.get('a')); // comes from valid table
706 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
Seigo Nonaka91961942017-03-17 18:35:24 -0700707 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700708 }
709 {
710 SCOPED_TRACE("Invalid length");
711 CmapBuilder builder(2);
Seigo Nonaka91961942017-03-17 18:35:24 -0700712 std::vector<uint8_t> table = buildCmapFormat12Table(std::vector<uint32_t>({'b', 'b'}));
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700713 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
Seigo Nonaka91961942017-03-17 18:35:24 -0700718 SparseBitSet coverage = CmapCoverage::getCoverage(cmap.data(), cmap.size(), &vsTables);
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700719 EXPECT_TRUE(coverage.get('a')); // comes from valid table
720 EXPECT_FALSE(coverage.get('b')); // should not use invalid table.
Seigo Nonaka91961942017-03-17 18:35:24 -0700721 EXPECT_TRUE(vsTables.empty());
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -0700722 }
723}
724
Seigo Nonaka91961942017-03-17 18:35:24 -0700725TEST(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
762TEST(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
840TEST(CmapCoverageTest, TableSelection_brokenVSTable) {
Seigo Nonaka0dfb3522017-07-07 11:39:58 -0700841 std::vector<uint8_t> cmap12Table = buildCmapFormat12Table(std::vector<uint32_t>({'a', 'z'}));
Seigo Nonaka91961942017-03-17 18:35:24 -0700842 {
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 }
Seigo Nonaka0dfb3522017-07-07 11:39:58 -0700935 {
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 }
Seigo Nonaka91961942017-03-17 18:35:24 -0700991}
992
993TEST(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
1068TEST(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
1095TEST(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}
Seigo Nonakadb1b6cb2017-03-24 16:57:20 -07001111} // namespace minikin