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