• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 #include <vector>
22 using std::vector;
23 
24 #include <log/log.h>
25 
26 #include <minikin/CmapCoverage.h>
27 #include <minikin/SparseBitSet.h>
28 #include "MinikinInternal.h"
29 
30 namespace minikin {
31 
32 // These could perhaps be optimized to use __builtin_bswap16 and friends.
readU16(const uint8_t * data,size_t offset)33 static uint32_t readU16(const uint8_t* data, size_t offset) {
34   return ((uint32_t)data[offset]) << 8 | ((uint32_t)data[offset + 1]);
35 }
36 
readU32(const uint8_t * data,size_t offset)37 static uint32_t readU32(const uint8_t* data, size_t offset) {
38   return ((uint32_t)data[offset]) << 24 | ((uint32_t)data[offset + 1]) << 16 |
39          ((uint32_t)data[offset + 2]) << 8 | ((uint32_t)data[offset + 3]);
40 }
41 
addRange(vector<uint32_t> & coverage,uint32_t start,uint32_t end)42 static void addRange(vector<uint32_t>& coverage, uint32_t start, uint32_t end) {
43 #ifdef VERBOSE_DEBUG
44   ALOGD("adding range %d-%d\n", start, end);
45 #endif
46   if (coverage.empty() || coverage.back() < start) {
47     coverage.push_back(start);
48     coverage.push_back(end);
49   } else {
50     coverage.back() = end;
51   }
52 }
53 
54 // Get the coverage information out of a Format 4 subtable, storing it in the
55 // coverage vector
getCoverageFormat4(vector<uint32_t> & coverage,const uint8_t * data,size_t size)56 static bool getCoverageFormat4(vector<uint32_t>& coverage,
57                                const uint8_t* data,
58                                size_t size) {
59   const size_t kSegCountOffset = 6;
60   const size_t kEndCountOffset = 14;
61   const size_t kHeaderSize = 16;
62   const size_t kSegmentSize =
63       8;  // total size of array elements for one segment
64   if (kEndCountOffset > size) {
65     return false;
66   }
67   size_t segCount = readU16(data, kSegCountOffset) >> 1;
68   if (kHeaderSize + segCount * kSegmentSize > size) {
69     return false;
70   }
71   for (size_t i = 0; i < segCount; i++) {
72     uint32_t end = readU16(data, kEndCountOffset + 2 * i);
73     uint32_t start = readU16(data, kHeaderSize + 2 * (segCount + i));
74     if (end < start) {
75       // invalid segment range: size must be positive
76       android_errorWriteLog(0x534e4554, "26413177");
77       return false;
78     }
79     uint32_t rangeOffset = readU16(data, kHeaderSize + 2 * (3 * segCount + i));
80     if (rangeOffset == 0) {
81       uint32_t delta = readU16(data, kHeaderSize + 2 * (2 * segCount + i));
82       if (((end + delta) & 0xffff) > end - start) {
83         addRange(coverage, start, end + 1);
84       } else {
85         for (uint32_t j = start; j < end + 1; j++) {
86           if (((j + delta) & 0xffff) != 0) {
87             addRange(coverage, j, j + 1);
88           }
89         }
90       }
91     } else {
92       for (uint32_t j = start; j < end + 1; j++) {
93         uint32_t actualRangeOffset =
94             kHeaderSize + 6 * segCount + rangeOffset + (i + j - start) * 2;
95         if (actualRangeOffset + 2 > size) {
96           // invalid rangeOffset is considered a "warning" by OpenType Sanitizer
97           continue;
98         }
99         uint32_t glyphId = readU16(data, actualRangeOffset);
100         if (glyphId != 0) {
101           addRange(coverage, j, j + 1);
102         }
103       }
104     }
105   }
106   return true;
107 }
108 
109 // Get the coverage information out of a Format 12 subtable, storing it in the
110 // coverage vector
getCoverageFormat12(vector<uint32_t> & coverage,const uint8_t * data,size_t size)111 static bool getCoverageFormat12(vector<uint32_t>& coverage,
112                                 const uint8_t* data,
113                                 size_t size) {
114   const size_t kNGroupsOffset = 12;
115   const size_t kFirstGroupOffset = 16;
116   const size_t kGroupSize = 12;
117   const size_t kStartCharCodeOffset = 0;
118   const size_t kEndCharCodeOffset = 4;
119   const size_t kMaxNGroups =
120       0xfffffff0 / kGroupSize;  // protection against overflow
121   // For all values < kMaxNGroups, kFirstGroupOffset + nGroups * kGroupSize fits
122   // in 32 bits.
123   if (kFirstGroupOffset > size) {
124     return false;
125   }
126   uint32_t nGroups = readU32(data, kNGroupsOffset);
127   if (nGroups >= kMaxNGroups ||
128       kFirstGroupOffset + nGroups * kGroupSize > size) {
129     android_errorWriteLog(0x534e4554, "25645298");
130     return false;
131   }
132   for (uint32_t i = 0; i < nGroups; i++) {
133     uint32_t groupOffset = kFirstGroupOffset + i * kGroupSize;
134     uint32_t start = readU32(data, groupOffset + kStartCharCodeOffset);
135     uint32_t end = readU32(data, groupOffset + kEndCharCodeOffset);
136     if (end < start) {
137       // invalid group range: size must be positive
138       android_errorWriteLog(0x534e4554, "26413177");
139       return false;
140     }
141 
142     // No need to read outside of Unicode code point range.
143     if (start > MAX_UNICODE_CODE_POINT) {
144       return true;
145     }
146     if (end > MAX_UNICODE_CODE_POINT) {
147       // file is inclusive, vector is exclusive
148       addRange(coverage, start, MAX_UNICODE_CODE_POINT + 1);
149       return true;
150     }
151     addRange(coverage, start,
152              end + 1);  // file is inclusive, vector is exclusive
153   }
154   return true;
155 }
156 
157 // Lower value has higher priority. 0 for the highest priority table.
158 // kLowestPriority for unsupported tables.
159 // This order comes from HarfBuzz's hb-ot-font.cc and needs to be kept in sync
160 // with it.
161 constexpr uint8_t kLowestPriority = 255;
getTablePriority(uint16_t platformId,uint16_t encodingId)162 uint8_t getTablePriority(uint16_t platformId, uint16_t encodingId) {
163   if (platformId == 3 && encodingId == 10) {
164     return 0;
165   }
166   if (platformId == 0 && encodingId == 6) {
167     return 1;
168   }
169   if (platformId == 0 && encodingId == 4) {
170     return 2;
171   }
172   if (platformId == 3 && encodingId == 1) {
173     return 3;
174   }
175   if (platformId == 0 && encodingId == 3) {
176     return 4;
177   }
178   if (platformId == 0 && encodingId == 2) {
179     return 5;
180   }
181   if (platformId == 0 && encodingId == 1) {
182     return 6;
183   }
184   if (platformId == 0 && encodingId == 0) {
185     return 7;
186   }
187   // Tables other than above are not supported.
188   return kLowestPriority;
189 }
190 
getCoverage(const uint8_t * cmap_data,size_t cmap_size,bool * has_cmap_format14_subtable)191 SparseBitSet CmapCoverage::getCoverage(const uint8_t* cmap_data,
192                                        size_t cmap_size,
193                                        bool* has_cmap_format14_subtable) {
194   constexpr size_t kHeaderSize = 4;
195   constexpr size_t kNumTablesOffset = 2;
196   constexpr size_t kTableSize = 8;
197   constexpr size_t kPlatformIdOffset = 0;
198   constexpr size_t kEncodingIdOffset = 2;
199   constexpr size_t kOffsetOffset = 4;
200   constexpr size_t kFormatOffset = 0;
201   constexpr uint32_t kInvalidOffset = UINT32_MAX;
202 
203   if (kHeaderSize > cmap_size) {
204     return SparseBitSet();
205   }
206   uint32_t numTables = readU16(cmap_data, kNumTablesOffset);
207   if (kHeaderSize + numTables * kTableSize > cmap_size) {
208     return SparseBitSet();
209   }
210 
211   uint32_t bestTableOffset = kInvalidOffset;
212   uint16_t bestTableFormat = 0;
213   uint8_t bestTablePriority = kLowestPriority;
214   *has_cmap_format14_subtable = false;
215   for (uint32_t i = 0; i < numTables; ++i) {
216     const uint32_t tableHeadOffset = kHeaderSize + i * kTableSize;
217     const uint16_t platformId =
218         readU16(cmap_data, tableHeadOffset + kPlatformIdOffset);
219     const uint16_t encodingId =
220         readU16(cmap_data, tableHeadOffset + kEncodingIdOffset);
221     const uint32_t offset = readU32(cmap_data, tableHeadOffset + kOffsetOffset);
222 
223     if (offset > cmap_size - 2) {
224       continue;  // Invalid table: not enough space to read.
225     }
226     const uint16_t format = readU16(cmap_data, offset + kFormatOffset);
227 
228     if (platformId == 0 /* Unicode */ &&
229         encodingId == 5 /* Variation Sequences */) {
230       if (!(*has_cmap_format14_subtable) && format == 14) {
231         *has_cmap_format14_subtable = true;
232       } else {
233         // Ignore the (0, 5) table if we have already seen another valid one or
234         // it's in a format we don't understand.
235       }
236     } else {
237       uint32_t length;
238       uint32_t language;
239 
240       if (format == 4) {
241         constexpr size_t lengthOffset = 2;
242         constexpr size_t languageOffset = 4;
243         constexpr size_t minTableSize = languageOffset + 2;
244         if (offset > cmap_size - minTableSize) {
245           continue;  // Invalid table: not enough space to read.
246         }
247         length = readU16(cmap_data, offset + lengthOffset);
248         language = readU16(cmap_data, offset + languageOffset);
249       } else if (format == 12) {
250         constexpr size_t lengthOffset = 4;
251         constexpr size_t languageOffset = 8;
252         constexpr size_t minTableSize = languageOffset + 4;
253         if (offset > cmap_size - minTableSize) {
254           continue;  // Invalid table: not enough space to read.
255         }
256         length = readU32(cmap_data, offset + lengthOffset);
257         language = readU32(cmap_data, offset + languageOffset);
258       } else {
259         continue;
260       }
261 
262       if (length > cmap_size - offset) {
263         continue;  // Invalid table: table length is larger than whole cmap data
264                    // size.
265       }
266       if (language != 0) {
267         // Unsupported or invalid table: this is either a subtable for the
268         // Macintosh platform (which we don't support), or an invalid subtable
269         // since language field should be zero for non-Macintosh subtables.
270         continue;
271       }
272       const uint8_t priority = getTablePriority(platformId, encodingId);
273       if (priority < bestTablePriority) {
274         bestTableOffset = offset;
275         bestTablePriority = priority;
276         bestTableFormat = format;
277       }
278     }
279     if (*has_cmap_format14_subtable &&
280         bestTablePriority == 0 /* highest priority */) {
281       // Already found the highest priority table and variation sequences table.
282       // No need to look at remaining tables.
283       break;
284     }
285   }
286   if (bestTableOffset == kInvalidOffset) {
287     return SparseBitSet();
288   }
289   const uint8_t* tableData = cmap_data + bestTableOffset;
290   const size_t tableSize = cmap_size - bestTableOffset;
291   vector<uint32_t> coverageVec;
292   bool success;
293   if (bestTableFormat == 4) {
294     success = getCoverageFormat4(coverageVec, tableData, tableSize);
295   } else {
296     success = getCoverageFormat12(coverageVec, tableData, tableSize);
297   }
298   if (success && (coverageVec.size() != 0)) {
299     return SparseBitSet(&coverageVec.front(), coverageVec.size() >> 1);
300   } else {
301     return SparseBitSet();
302   }
303 }
304 
305 }  // namespace minikin
306