CmapCoverage.cpp revision ca8ac8acdad662230ae37998c6c4091bb39402b6
1/*
2 * Copyright (C) 2013 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// Determine coverage of font given its raw "cmap" OpenType table
18
19#ifdef PRINTF_DEBUG
20#include <stdio.h>
21#endif
22
23#include <vector>
24using std::vector;
25
26#include <minikin/SparseBitSet.h>
27#include <minikin/CmapCoverage.h>
28
29namespace android {
30
31// These could perhaps be optimized to use __builtin_bswap16 and friends.
32static uint32_t readU16(const uint8_t* data, size_t offset) {
33    return ((uint32_t)data[offset]) << 8 | ((uint32_t)data[offset + 1]);
34}
35
36static uint32_t readU32(const uint8_t* data, size_t offset) {
37    return ((uint32_t)data[offset]) << 24 | ((uint32_t)data[offset + 1]) << 16 |
38        ((uint32_t)data[offset + 2]) << 8 | ((uint32_t)data[offset + 3]);
39}
40
41static void addRange(vector<uint32_t> &coverage, uint32_t start, uint32_t end) {
42#ifdef PRINTF_DEBUG
43    printf("adding range %d-%d\n", start, end);
44#endif
45    if (coverage.empty() || coverage.back() < start) {
46        coverage.push_back(start);
47        coverage.push_back(end);
48    } else {
49        coverage.back() = end;
50    }
51}
52
53// Get the coverage information out of a Format 4 subtable, storing it in the coverage vector
54static bool getCoverageFormat4(vector<uint32_t>& coverage, const uint8_t* data, size_t size) {
55    const size_t kSegCountOffset = 6;
56    const size_t kEndCountOffset = 14;
57    const size_t kHeaderSize = 16;
58    const size_t kSegmentSize = 8;  // total size of array elements for one segment
59    if (kEndCountOffset > size) {
60        return false;
61    }
62    size_t segCount = readU16(data, kSegCountOffset) >> 1;
63    if (kHeaderSize + segCount * kSegmentSize > size) {
64        return false;
65    }
66    for (size_t i = 0; i < segCount; i++) {
67        uint32_t end = readU16(data, kEndCountOffset + 2 * i);
68        uint32_t start = readU16(data, kHeaderSize + 2 * (segCount + i));
69        if (end < start) {
70            // invalid segment range: size must be positive
71            return false;
72        }
73        uint32_t rangeOffset = readU16(data, kHeaderSize + 2 * (3 * segCount + i));
74        if (rangeOffset == 0) {
75            uint32_t delta = readU16(data, kHeaderSize + 2 * (2 * segCount + i));
76            if (((end + delta) & 0xffff) > end - start) {
77                addRange(coverage, start, end + 1);
78            } else {
79                for (uint32_t j = start; j < end + 1; j++) {
80                    if (((j + delta) & 0xffff) != 0) {
81                        addRange(coverage, j, j + 1);
82                    }
83                }
84            }
85        } else {
86            for (uint32_t j = start; j < end + 1; j++) {
87                uint32_t actualRangeOffset = kHeaderSize + 6 * segCount + rangeOffset +
88                    (i + j - start) * 2;
89                if (actualRangeOffset + 2 > size) {
90                    return false;
91                }
92                uint32_t glyphId = readU16(data, actualRangeOffset);
93                if (glyphId != 0) {
94                    addRange(coverage, j, j + 1);
95                }
96            }
97        }
98    }
99    return true;
100}
101
102// Get the coverage information out of a Format 12 subtable, storing it in the coverage vector
103static bool getCoverageFormat12(vector<uint32_t>& coverage, const uint8_t* data, size_t size) {
104    const size_t kNGroupsOffset = 12;
105    const size_t kFirstGroupOffset = 16;
106    const size_t kGroupSize = 12;
107    const size_t kStartCharCodeOffset = 0;
108    const size_t kEndCharCodeOffset = 4;
109    const size_t kMaxNGroups = 0xfffffff0 / kGroupSize;  // protection against overflow
110    // For all values < kMaxNGroups, kFirstGroupOffset + nGroups * kGroupSize fits in 32 bits.
111    if (kFirstGroupOffset > size) {
112        return false;
113    }
114    uint32_t nGroups = readU32(data, kNGroupsOffset);
115    if (nGroups >= kMaxNGroups || kFirstGroupOffset + nGroups * kGroupSize > size) {
116        return false;
117    }
118    for (uint32_t i = 0; i < nGroups; i++) {
119        uint32_t groupOffset = kFirstGroupOffset + i * kGroupSize;
120        uint32_t start = readU32(data, groupOffset + kStartCharCodeOffset);
121        uint32_t end = readU32(data, groupOffset + kEndCharCodeOffset);
122        if (end < start) {
123            // invalid group range: size must be positive
124            return false;
125        }
126        addRange(coverage, start, end + 1);  // file is inclusive, vector is exclusive
127    }
128    return true;
129}
130
131bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data, size_t cmap_size) {
132    vector<uint32_t> coverageVec;
133    const size_t kHeaderSize = 4;
134    const size_t kNumTablesOffset = 2;
135    const size_t kTableSize = 8;
136    const size_t kPlatformIdOffset = 0;
137    const size_t kEncodingIdOffset = 2;
138    const size_t kOffsetOffset = 4;
139    const uint16_t kMicrosoftPlatformId = 3;
140    const uint16_t kUnicodeBmpEncodingId = 1;
141    const uint16_t kUnicodeUcs4EncodingId = 10;
142    const uint32_t kNoTable = UINT32_MAX;
143    if (kHeaderSize > cmap_size) {
144        return false;
145    }
146    uint32_t numTables = readU16(cmap_data, kNumTablesOffset);
147    if (kHeaderSize + numTables * kTableSize > cmap_size) {
148        return false;
149    }
150    uint32_t bestTable = kNoTable;
151    for (uint32_t i = 0; i < numTables; i++) {
152        uint16_t platformId = readU16(cmap_data, kHeaderSize + i * kTableSize + kPlatformIdOffset);
153        uint16_t encodingId = readU16(cmap_data, kHeaderSize + i * kTableSize + kEncodingIdOffset);
154        if (platformId == kMicrosoftPlatformId && encodingId == kUnicodeUcs4EncodingId) {
155            bestTable = i;
156            break;
157        } else if (platformId == kMicrosoftPlatformId && encodingId == kUnicodeBmpEncodingId) {
158            bestTable = i;
159        }
160    }
161#ifdef PRINTF_DEBUG
162    printf("best table = %d\n", bestTable);
163#endif
164    if (bestTable == kNoTable) {
165        return false;
166    }
167    uint32_t offset = readU32(cmap_data, kHeaderSize + bestTable * kTableSize + kOffsetOffset);
168    if (offset > cmap_size - 2) {
169        return false;
170    }
171    uint16_t format = readU16(cmap_data, offset);
172    bool success = false;
173    const uint8_t* tableData = cmap_data + offset;
174    const size_t tableSize = cmap_size - offset;
175    if (format == 4) {
176        success = getCoverageFormat4(coverageVec, tableData, tableSize);
177    } else if (format == 12) {
178        success = getCoverageFormat12(coverageVec, tableData, tableSize);
179    }
180    if (success) {
181        coverage.initFromRanges(&coverageVec.front(), coverageVec.size() >> 1);
182    }
183#ifdef PRINTF_DEBUG
184    for (int i = 0; i < coverageVec.size(); i += 2) {
185        printf("%x:%x\n", coverageVec[i], coverageVec[i + 1]);
186    }
187#endif
188    return success;
189}
190
191}  // namespace android
192