/src/ots/src/cff_charstring.cc
Line | Count | Source |
1 | | // Copyright (c) 2010-2017 The OTS Authors. All rights reserved. |
2 | | // Use of this source code is governed by a BSD-style license that can be |
3 | | // found in the LICENSE file. |
4 | | |
5 | | // A parser for the Type 2 Charstring Format. |
6 | | // http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf |
7 | | |
8 | | #include "cff_charstring.h" |
9 | | |
10 | | #include <climits> |
11 | | #include <cstdio> |
12 | | #include <cstring> |
13 | | #include <stack> |
14 | | #include <string> |
15 | | #include <utility> |
16 | | |
17 | | #define TABLE_NAME "CFF" |
18 | | |
19 | | namespace { |
20 | | |
21 | | // Type 2 Charstring Implementation Limits. See Appendix. B in Adobe Technical |
22 | | // Note #5177. |
23 | | const int32_t kMaxSubrsCount = 65536; |
24 | | const size_t kMaxCharStringLength = 65535; |
25 | | const size_t kMaxNumberOfStemHints = 96; |
26 | | const size_t kMaxSubrNesting = 10; |
27 | | |
28 | | // We reject the table if any charstring results in executing too many ops. |
29 | | // This should be more than enough for any realistic use case; only a malicious |
30 | | // font would run millions of ops for a single glyph. |
31 | | const uint32_t kMaxCharStringOps = 1024 * 1024 * 64; |
32 | | |
33 | | // |dummy_result| should be a huge positive integer so callsubr and callgsubr |
34 | | // will fail with the dummy value. |
35 | | const int32_t dummy_result = INT_MAX; |
36 | | |
37 | | bool ExecuteCharString(ots::OpenTypeCFF& cff, |
38 | | size_t call_depth, |
39 | | const ots::CFFIndex& global_subrs_index, |
40 | | const ots::CFFIndex& local_subrs_index, |
41 | | ots::Buffer *cff_table, |
42 | | ots::Buffer *char_string, |
43 | | std::stack<int32_t> *argument_stack, |
44 | | ots::CharStringContext& cs_ctx); |
45 | | |
46 | 55.2M | bool ArgumentStackOverflows(std::stack<int32_t> *argument_stack, bool cff2) { |
47 | 55.2M | if ((cff2 && argument_stack->size() > ots::kMaxCFF2ArgumentStack) || |
48 | 55.2M | (!cff2 && argument_stack->size() > ots::kMaxCFF1ArgumentStack)) { |
49 | 9 | return true; |
50 | 9 | } |
51 | 55.2M | return false; |
52 | 55.2M | } |
53 | | |
54 | | #ifdef DUMP_T2CHARSTRING |
55 | | // Converts |op| to a string and returns it. |
56 | | const char *CharStringOperatorToString(ots::CharStringOperator op) { |
57 | | switch (op) { |
58 | | case ots::kHStem: |
59 | | return "hstem"; |
60 | | case ots::kVStem: |
61 | | return "vstem"; |
62 | | case ots::kVMoveTo: |
63 | | return "vmoveto"; |
64 | | case ots::kRLineTo: |
65 | | return "rlineto"; |
66 | | case ots::kHLineTo: |
67 | | return "hlineto"; |
68 | | case ots::kVLineTo: |
69 | | return "vlineto"; |
70 | | case ots::kRRCurveTo: |
71 | | return "rrcurveto"; |
72 | | case ots::kCallSubr: |
73 | | return "callsubr"; |
74 | | case ots::kReturn: |
75 | | return "return"; |
76 | | case ots::kEndChar: |
77 | | return "endchar"; |
78 | | case ots::kVSIndex: |
79 | | return "vsindex"; |
80 | | case ots::kBlend: |
81 | | return "blend"; |
82 | | case ots::kHStemHm: |
83 | | return "hstemhm"; |
84 | | case ots::kHintMask: |
85 | | return "hintmask"; |
86 | | case ots::kCntrMask: |
87 | | return "cntrmask"; |
88 | | case ots::kRMoveTo: |
89 | | return "rmoveto"; |
90 | | case ots::kHMoveTo: |
91 | | return "hmoveto"; |
92 | | case ots::kVStemHm: |
93 | | return "vstemhm"; |
94 | | case ots::kRCurveLine: |
95 | | return "rcurveline"; |
96 | | case ots::kRLineCurve: |
97 | | return "rlinecurve"; |
98 | | case ots::kVVCurveTo: |
99 | | return "VVCurveTo"; |
100 | | case ots::kHHCurveTo: |
101 | | return "hhcurveto"; |
102 | | case ots::kCallGSubr: |
103 | | return "callgsubr"; |
104 | | case ots::kVHCurveTo: |
105 | | return "vhcurveto"; |
106 | | case ots::kHVCurveTo: |
107 | | return "HVCurveTo"; |
108 | | case ots::kDotSection: |
109 | | return "dotsection"; |
110 | | case ots::kAnd: |
111 | | return "and"; |
112 | | case ots::kOr: |
113 | | return "or"; |
114 | | case ots::kNot: |
115 | | return "not"; |
116 | | case ots::kAbs: |
117 | | return "abs"; |
118 | | case ots::kAdd: |
119 | | return "add"; |
120 | | case ots::kSub: |
121 | | return "sub"; |
122 | | case ots::kDiv: |
123 | | return "div"; |
124 | | case ots::kNeg: |
125 | | return "neg"; |
126 | | case ots::kEq: |
127 | | return "eq"; |
128 | | case ots::kDrop: |
129 | | return "drop"; |
130 | | case ots::kPut: |
131 | | return "put"; |
132 | | case ots::kGet: |
133 | | return "get"; |
134 | | case ots::kIfElse: |
135 | | return "ifelse"; |
136 | | case ots::kRandom: |
137 | | return "random"; |
138 | | case ots::kMul: |
139 | | return "mul"; |
140 | | case ots::kSqrt: |
141 | | return "sqrt"; |
142 | | case ots::kDup: |
143 | | return "dup"; |
144 | | case ots::kExch: |
145 | | return "exch"; |
146 | | case ots::kIndex: |
147 | | return "index"; |
148 | | case ots::kRoll: |
149 | | return "roll"; |
150 | | case ots::kHFlex: |
151 | | return "hflex"; |
152 | | case ots::kFlex: |
153 | | return "flex"; |
154 | | case ots::kHFlex1: |
155 | | return "hflex1"; |
156 | | case ots::kFlex1: |
157 | | return "flex1"; |
158 | | } |
159 | | |
160 | | return "UNKNOWN"; |
161 | | } |
162 | | #endif |
163 | | |
164 | | // Read one or more bytes from the |char_string| buffer and stores the number |
165 | | // read on |out_number|. If the number read is an operator (ex 'vstem'), sets |
166 | | // true on |out_is_operator|. Returns true if the function read a number. |
167 | | bool ReadNextNumberFromCharString(ots::Buffer *char_string, |
168 | | int32_t *out_number, |
169 | 153M | bool *out_is_operator) { |
170 | 153M | uint8_t v = 0; |
171 | 153M | if (!char_string->ReadU8(&v)) { |
172 | 0 | return OTS_FAILURE(); |
173 | 0 | } |
174 | 153M | *out_is_operator = false; |
175 | | |
176 | | // The conversion algorithm is described in Adobe Technical Note #5177, page |
177 | | // 13, Table 1. |
178 | 153M | if (v <= 11) { |
179 | 97.5M | *out_number = v; |
180 | 97.5M | *out_is_operator = true; |
181 | 97.5M | } else if (v == 12) { |
182 | 3.08k | uint16_t result = (v << 8); |
183 | 3.08k | if (!char_string->ReadU8(&v)) { |
184 | 1 | return OTS_FAILURE(); |
185 | 1 | } |
186 | 3.08k | result += v; |
187 | 3.08k | *out_number = result; |
188 | 3.08k | *out_is_operator = true; |
189 | 56.0M | } else if (v <= 27) { |
190 | | // Special handling for v==19 and v==20 are implemented in |
191 | | // ExecuteCharStringOperator(). |
192 | 566k | *out_number = v; |
193 | 566k | *out_is_operator = true; |
194 | 55.5M | } else if (v == 28) { |
195 | 61.7k | if (!char_string->ReadU8(&v)) { |
196 | 1 | return OTS_FAILURE(); |
197 | 1 | } |
198 | 61.7k | uint16_t result = (v << 8); |
199 | 61.7k | if (!char_string->ReadU8(&v)) { |
200 | 2 | return OTS_FAILURE(); |
201 | 2 | } |
202 | 61.7k | result += v; |
203 | 61.7k | *out_number = static_cast<int16_t>(result); |
204 | 55.4M | } else if (v <= 31) { |
205 | 251k | *out_number = v; |
206 | 251k | *out_is_operator = true; |
207 | 55.1M | } else if (v <= 246) { |
208 | 53.9M | *out_number = static_cast<int32_t>(v) - 139; |
209 | 53.9M | } else if (v <= 250) { |
210 | 653k | uint8_t w = 0; |
211 | 653k | if (!char_string->ReadU8(&w)) { |
212 | 1 | return OTS_FAILURE(); |
213 | 1 | } |
214 | 653k | *out_number = ((static_cast<int32_t>(v) - 247) * 256) + |
215 | 653k | static_cast<int32_t>(w) + 108; |
216 | 653k | } else if (v <= 254) { |
217 | 535k | uint8_t w = 0; |
218 | 535k | if (!char_string->ReadU8(&w)) { |
219 | 4 | return OTS_FAILURE(); |
220 | 4 | } |
221 | 535k | *out_number = -((static_cast<int32_t>(v) - 251) * 256) - |
222 | 535k | static_cast<int32_t>(w) - 108; |
223 | 535k | } else if (v == 255) { |
224 | | // TODO(yusukes): We should not skip the 4 bytes. Note that when v is 255, |
225 | | // we should treat the following 4-bytes as a 16.16 fixed-point number |
226 | | // rather than 32bit signed int. |
227 | 2.96k | if (!char_string->Skip(4)) { |
228 | 4 | return OTS_FAILURE(); |
229 | 4 | } |
230 | 2.95k | *out_number = dummy_result; |
231 | 2.95k | } else { |
232 | 0 | return OTS_FAILURE(); |
233 | 0 | } |
234 | | |
235 | 153M | return true; |
236 | 153M | } |
237 | | |
238 | 1.22M | bool ValidCFF2Operator(int32_t op) { |
239 | 1.22M | switch (op) { |
240 | 2 | case ots::kReturn: |
241 | 3 | case ots::kEndChar: |
242 | 4 | case ots::kAbs: |
243 | 5 | case ots::kAdd: |
244 | 6 | case ots::kSub: |
245 | 7 | case ots::kDiv: |
246 | 8 | case ots::kNeg: |
247 | 9 | case ots::kRandom: |
248 | 10 | case ots::kMul: |
249 | 11 | case ots::kSqrt: |
250 | 12 | case ots::kDrop: |
251 | 13 | case ots::kExch: |
252 | 14 | case ots::kIndex: |
253 | 15 | case ots::kRoll: |
254 | 16 | case ots::kDup: |
255 | 17 | case ots::kPut: |
256 | 18 | case ots::kGet: |
257 | 19 | case ots::kDotSection: |
258 | 20 | case ots::kAnd: |
259 | 21 | case ots::kOr: |
260 | 23 | case ots::kNot: |
261 | 24 | case ots::kEq: |
262 | 25 | case ots::kIfElse: |
263 | 25 | return false; |
264 | 1.22M | } |
265 | | |
266 | 1.22M | return true; |
267 | 1.22M | } |
268 | | |
269 | | // Executes |op| and updates |argument_stack|. Returns true if the execution |
270 | | // succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively |
271 | | // calls ExecuteCharString() function. The |cs_ctx| argument holds values that |
272 | | // need to persist through these calls (see CharStringContext for details) |
273 | | bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, |
274 | | int32_t op, |
275 | | size_t call_depth, |
276 | | const ots::CFFIndex& global_subrs_index, |
277 | | const ots::CFFIndex& local_subrs_index, |
278 | | ots::Buffer *cff_table, |
279 | | ots::Buffer *char_string, |
280 | | std::stack<int32_t> *argument_stack, |
281 | 98.3M | ots::CharStringContext& cs_ctx) { |
282 | 98.3M | ots::Font* font = cff.GetFont(); |
283 | 98.3M | const size_t stack_size = argument_stack->size(); |
284 | | |
285 | 98.3M | if (cs_ctx.cff2 && !ValidCFF2Operator(op)) { |
286 | 25 | return OTS_FAILURE(); |
287 | 25 | } |
288 | | |
289 | 98.3M | switch (op) { |
290 | 48.7M | case ots::kCallSubr: |
291 | 48.7M | case ots::kCallGSubr: { |
292 | 48.7M | const ots::CFFIndex& subrs_index = |
293 | 48.7M | (op == ots::kCallSubr ? local_subrs_index : global_subrs_index); |
294 | | |
295 | 48.7M | if (stack_size < 1) { |
296 | 10 | return OTS_FAILURE(); |
297 | 10 | } |
298 | 48.7M | int32_t subr_number = argument_stack->top(); |
299 | 48.7M | argument_stack->pop(); |
300 | 48.7M | if (subr_number == dummy_result) { |
301 | | // For safety, we allow subr calls only with immediate subr numbers for |
302 | | // now. For example, we allow "123 callgsubr", but does not allow "100 12 |
303 | | // add callgsubr". Please note that arithmetic and conditional operators |
304 | | // always push the |dummy_result| in this implementation. |
305 | 2 | return OTS_FAILURE(); |
306 | 2 | } |
307 | | |
308 | | // See Adobe Technical Note #5176 (CFF), "16. Local/GlobalSubrs INDEXes." |
309 | 48.7M | int32_t bias = 32768; |
310 | 48.7M | if (subrs_index.count < 1240) { |
311 | 48.5M | bias = 107; |
312 | 48.5M | } else if (subrs_index.count < 33900) { |
313 | 161k | bias = 1131; |
314 | 161k | } |
315 | 48.7M | subr_number += bias; |
316 | | |
317 | | // Sanity checks of |subr_number|. |
318 | 48.7M | if (subr_number < 0) { |
319 | 2 | return OTS_FAILURE(); |
320 | 2 | } |
321 | 48.7M | if (subr_number >= kMaxSubrsCount) { |
322 | 0 | return OTS_FAILURE(); |
323 | 0 | } |
324 | 48.7M | if (subrs_index.offsets.size() <= static_cast<size_t>(subr_number + 1)) { |
325 | 18 | return OTS_FAILURE(); // The number is out-of-bounds. |
326 | 18 | } |
327 | | |
328 | | // Prepare ots::Buffer where we're going to jump. |
329 | 48.7M | const size_t length = |
330 | 48.7M | subrs_index.offsets[subr_number + 1] - subrs_index.offsets[subr_number]; |
331 | 48.7M | if (length > kMaxCharStringLength) { |
332 | 0 | return OTS_FAILURE(); |
333 | 0 | } |
334 | 48.7M | const size_t offset = subrs_index.offsets[subr_number]; |
335 | 48.7M | cff_table->set_offset(offset); |
336 | 48.7M | if (!cff_table->Skip(length)) { |
337 | 0 | return OTS_FAILURE(); |
338 | 0 | } |
339 | 48.7M | ots::Buffer char_string_to_jump(cff_table->buffer() + offset, length); |
340 | | |
341 | 48.7M | return ExecuteCharString(cff, |
342 | 48.7M | call_depth + 1, |
343 | 48.7M | global_subrs_index, |
344 | 48.7M | local_subrs_index, |
345 | 48.7M | cff_table, |
346 | 48.7M | &char_string_to_jump, |
347 | 48.7M | argument_stack, |
348 | 48.7M | cs_ctx); |
349 | 48.7M | } |
350 | | |
351 | 48.4M | case ots::kReturn: |
352 | 48.4M | return true; |
353 | | |
354 | 19.6k | case ots::kEndChar: |
355 | 19.6k | cs_ctx.endchar_seen = true; |
356 | 19.6k | cs_ctx.width_seen = true; // just in case. |
357 | 19.6k | return true; |
358 | | |
359 | 565 | case ots::kVSIndex: { |
360 | 565 | if (!cs_ctx.cff2) { |
361 | 1 | return OTS_FAILURE(); |
362 | 1 | } |
363 | 564 | if (stack_size != 1) { |
364 | 1 | return OTS_FAILURE(); |
365 | 1 | } |
366 | 563 | if (cs_ctx.blend_seen || cs_ctx.vsindex_seen) { |
367 | 2 | return OTS_FAILURE(); |
368 | 2 | } |
369 | 561 | if (argument_stack->top() < 0 || |
370 | 558 | argument_stack->top() >= (int32_t)cff.region_index_count.size()) { |
371 | 4 | return OTS_FAILURE(); |
372 | 4 | } |
373 | 557 | cs_ctx.vsindex_seen = true; |
374 | 557 | cs_ctx.vsindex = argument_stack->top(); |
375 | 1.11k | while (!argument_stack->empty()) |
376 | 557 | argument_stack->pop(); |
377 | 557 | return true; |
378 | 561 | } |
379 | | |
380 | 180k | case ots::kBlend: { |
381 | 180k | if (!cs_ctx.cff2) { |
382 | 4 | return OTS_FAILURE(); |
383 | 4 | } |
384 | 180k | if (stack_size < 1) { |
385 | 1 | return OTS_FAILURE(); |
386 | 1 | } |
387 | 180k | if (cs_ctx.vsindex >= (int32_t)cff.region_index_count.size()) { |
388 | 2 | return OTS_FAILURE(); |
389 | 2 | } |
390 | 180k | uint16_t k = cff.region_index_count.at(cs_ctx.vsindex); |
391 | 180k | uint16_t n = argument_stack->top(); |
392 | 180k | if (stack_size < n * (k + 1u) + 1u) { |
393 | 10 | return OTS_FAILURE(); |
394 | 10 | } |
395 | | |
396 | | // Keep the 1st n operands on the stack for the next operator to use and |
397 | | // pop the rest. There can be multiple consecutive blend operators, so this |
398 | | // makes sure the operands of all of them are kept on the stack. |
399 | 1.62M | while (argument_stack->size() > stack_size - ((n * k) + 1)) |
400 | 1.44M | argument_stack->pop(); |
401 | 180k | cs_ctx.blend_seen = true; |
402 | 180k | return true; |
403 | 180k | } |
404 | | |
405 | 454 | case ots::kHStem: |
406 | 1.05k | case ots::kVStem: |
407 | 1.60k | case ots::kHStemHm: |
408 | 2.19k | case ots::kVStemHm: { |
409 | 2.19k | bool successful = false; |
410 | 2.19k | if (stack_size < 2) { |
411 | 8 | return OTS_FAILURE(); |
412 | 8 | } |
413 | 2.18k | if (op == ots::kHStem || op == ots::kHStemHm) { |
414 | 1.00k | if (cs_ctx.hint_state > ots::kHs) { |
415 | 5 | return OTS_FAILURE(); |
416 | 5 | } |
417 | 1.18k | } else { |
418 | 1.18k | if (cs_ctx.hint_state > ots::kVs) { |
419 | 5 | return OTS_FAILURE(); |
420 | 5 | } |
421 | 1.17k | cs_ctx.hint_state = ots::kVs; |
422 | 1.17k | } |
423 | 2.17k | if ((stack_size % 2) == 0) { |
424 | 1.50k | successful = true; |
425 | 1.50k | } else if ((!(cs_ctx.width_seen)) && (((stack_size - 1) % 2) == 0)) { |
426 | | // The -1 is for "width" argument. For details, see Adobe Technical Note |
427 | | // #5177, page 16, note 4. |
428 | 664 | successful = true; |
429 | 664 | } |
430 | 2.17k | cs_ctx.num_stems += (stack_size / 2); |
431 | 2.17k | if ((cs_ctx.num_stems) > kMaxNumberOfStemHints) { |
432 | 12 | return OTS_FAILURE(); |
433 | 12 | } |
434 | 15.1k | while (!argument_stack->empty()) |
435 | 13.0k | argument_stack->pop(); |
436 | 2.16k | cs_ctx.width_seen = true; // always set true since "w" might be 0 byte. |
437 | 2.16k | return successful ? true : OTS_FAILURE(); |
438 | 2.17k | } |
439 | | |
440 | 150k | case ots::kRMoveTo: { |
441 | 150k | bool successful = false; |
442 | 150k | if (stack_size == 2) { |
443 | 135k | successful = true; |
444 | 135k | } else if ((!(cs_ctx.width_seen)) && (stack_size - 1 == 2)) { |
445 | 15.1k | successful = true; |
446 | 15.1k | } |
447 | 467k | while (!argument_stack->empty()) |
448 | 316k | argument_stack->pop(); |
449 | 150k | cs_ctx.width_seen = true; |
450 | 150k | cs_ctx.hint_state = ots::kHm; |
451 | 150k | return successful ? true : OTS_FAILURE(); |
452 | 2.17k | } |
453 | | |
454 | 4.94k | case ots::kVMoveTo: |
455 | 22.5k | case ots::kHMoveTo: { |
456 | 22.5k | bool successful = false; |
457 | 22.5k | if (stack_size == 1) { |
458 | 19.9k | successful = true; |
459 | 19.9k | } else if ((!(cs_ctx.width_seen)) && (stack_size - 1 == 1)) { |
460 | 2.62k | successful = true; |
461 | 2.62k | } |
462 | 48.0k | while (!argument_stack->empty()) |
463 | 25.4k | argument_stack->pop(); |
464 | 22.5k | cs_ctx.width_seen = true; |
465 | 22.5k | cs_ctx.hint_state = ots::kHm; |
466 | 22.5k | return successful ? true : OTS_FAILURE(); |
467 | 4.94k | } |
468 | | |
469 | 1.45k | case ots::kHintMask: |
470 | 1.82k | case ots::kCntrMask: { |
471 | 1.82k | bool successful = false; |
472 | 1.82k | if (stack_size == 0) { |
473 | 1.19k | successful = true; |
474 | 1.19k | } else if ((!(cs_ctx.width_seen)) && (stack_size == 1)) { |
475 | | // A number for "width" is found. |
476 | 1 | successful = true; |
477 | 626 | } else if ((!(cs_ctx.width_seen)) || // in this case, any sizes are ok. |
478 | 621 | ((stack_size % 2) == 0)) { |
479 | | // The numbers are vstem definition. |
480 | | // See Adobe Technical Note #5177, page 24, hintmask. |
481 | 621 | cs_ctx.num_stems += (stack_size / 2); |
482 | 621 | if ((cs_ctx.num_stems) > kMaxNumberOfStemHints) { |
483 | 1 | return OTS_FAILURE(); |
484 | 1 | } |
485 | 620 | successful = true; |
486 | 620 | } |
487 | 1.82k | if (!successful) { |
488 | 5 | return OTS_FAILURE(); |
489 | 5 | } |
490 | 1.81k | if (op == ots::kHintMask) { |
491 | 1.44k | cs_ctx.hint_state = ots::kHm; |
492 | 1.44k | } else { |
493 | 366 | if (cs_ctx.hint_state > ots::kCm) { |
494 | 4 | return OTS_FAILURE(); |
495 | 4 | } |
496 | 362 | cs_ctx.hint_state = ots::kCm; |
497 | 362 | } |
498 | 1.81k | if ((cs_ctx.num_stems) == 0) { |
499 | 4 | return OTS_FAILURE(); |
500 | 4 | } |
501 | 1.80k | const size_t mask_bytes = (cs_ctx.num_stems + 7) / 8; |
502 | 1.80k | if (!char_string->Skip(mask_bytes)) { |
503 | 2 | return OTS_FAILURE(); |
504 | 2 | } |
505 | 6.08k | while (!argument_stack->empty()) |
506 | 4.27k | argument_stack->pop(); |
507 | 1.80k | cs_ctx.width_seen = true; |
508 | 1.80k | return true; |
509 | 1.80k | } |
510 | | |
511 | 113k | case ots::kRLineTo: |
512 | 113k | if (!(cs_ctx.width_seen)) { |
513 | | // The first stack-clearing operator should be one of hstem, hstemhm, |
514 | | // vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, rmoveto, or |
515 | | // endchar. For details, see Adobe Technical Note #5177, page 16, note 4. |
516 | 1 | return OTS_FAILURE(); |
517 | 1 | } |
518 | 113k | if (stack_size < 2) { |
519 | 4 | return OTS_FAILURE(); |
520 | 4 | } |
521 | 113k | if ((stack_size % 2) != 0) { |
522 | 2 | return OTS_FAILURE(); |
523 | 2 | } |
524 | 546k | while (!argument_stack->empty()) |
525 | 433k | argument_stack->pop(); |
526 | 113k | return true; |
527 | | |
528 | 116k | case ots::kHLineTo: |
529 | 183k | case ots::kVLineTo: |
530 | 183k | if (!(cs_ctx.width_seen)) { |
531 | 4 | return OTS_FAILURE(); |
532 | 4 | } |
533 | 183k | if (stack_size < 1) { |
534 | 3 | return OTS_FAILURE(); |
535 | 3 | } |
536 | 482k | while (!argument_stack->empty()) |
537 | 298k | argument_stack->pop(); |
538 | 183k | return true; |
539 | | |
540 | 83.2k | case ots::kRRCurveTo: |
541 | 83.2k | if (!(cs_ctx.width_seen)) { |
542 | 2 | return OTS_FAILURE(); |
543 | 2 | } |
544 | 83.2k | if (stack_size < 6) { |
545 | 8 | return OTS_FAILURE(); |
546 | 8 | } |
547 | 83.1k | if ((stack_size % 6) != 0) { |
548 | 7 | return OTS_FAILURE(); |
549 | 7 | } |
550 | 1.28M | while (!argument_stack->empty()) |
551 | 1.20M | argument_stack->pop(); |
552 | 83.1k | return true; |
553 | | |
554 | 23.4k | case ots::kRCurveLine: |
555 | 23.4k | if (!(cs_ctx.width_seen)) { |
556 | 1 | return OTS_FAILURE(); |
557 | 1 | } |
558 | 23.4k | if (stack_size < 8) { |
559 | 1 | return OTS_FAILURE(); |
560 | 1 | } |
561 | 23.4k | if (((stack_size - 2) % 6) != 0) { |
562 | 2 | return OTS_FAILURE(); |
563 | 2 | } |
564 | 273k | while (!argument_stack->empty()) |
565 | 250k | argument_stack->pop(); |
566 | 23.4k | return true; |
567 | | |
568 | 19.4k | case ots::kRLineCurve: |
569 | 19.4k | if (!(cs_ctx.width_seen)) { |
570 | 2 | return OTS_FAILURE(); |
571 | 2 | } |
572 | 19.4k | if (stack_size < 8) { |
573 | 3 | return OTS_FAILURE(); |
574 | 3 | } |
575 | 19.3k | if (((stack_size - 6) % 2) != 0) { |
576 | 2 | return OTS_FAILURE(); |
577 | 2 | } |
578 | 193k | while (!argument_stack->empty()) |
579 | 174k | argument_stack->pop(); |
580 | 19.3k | return true; |
581 | | |
582 | 74.2k | case ots::kVVCurveTo: |
583 | 74.2k | if (!(cs_ctx.width_seen)) { |
584 | 1 | return OTS_FAILURE(); |
585 | 1 | } |
586 | 74.2k | if (stack_size < 4) { |
587 | 1 | return OTS_FAILURE(); |
588 | 1 | } |
589 | 74.2k | if (((stack_size % 4) != 0) && |
590 | 66.2k | (((stack_size - 1) % 4) != 0)) { |
591 | 3 | return OTS_FAILURE(); |
592 | 3 | } |
593 | 446k | while (!argument_stack->empty()) |
594 | 372k | argument_stack->pop(); |
595 | 74.2k | return true; |
596 | | |
597 | 77.5k | case ots::kHHCurveTo: { |
598 | 77.5k | bool successful = false; |
599 | 77.5k | if (!(cs_ctx.width_seen)) { |
600 | 1 | return OTS_FAILURE(); |
601 | 1 | } |
602 | 77.5k | if (stack_size < 4) { |
603 | 2 | return OTS_FAILURE(); |
604 | 2 | } |
605 | 77.5k | if ((stack_size % 4) == 0) { |
606 | | // {dxa dxb dyb dxc}+ |
607 | 7.22k | successful = true; |
608 | 70.3k | } else if (((stack_size - 1) % 4) == 0) { |
609 | | // dy1? {dxa dxb dyb dxc}+ |
610 | 70.3k | successful = true; |
611 | 70.3k | } |
612 | 478k | while (!argument_stack->empty()) |
613 | 401k | argument_stack->pop(); |
614 | 77.5k | return successful ? true : OTS_FAILURE(); |
615 | 77.5k | } |
616 | | |
617 | 124k | case ots::kVHCurveTo: |
618 | 238k | case ots::kHVCurveTo: { |
619 | 238k | bool successful = false; |
620 | 238k | if (!(cs_ctx.width_seen)) { |
621 | 2 | return OTS_FAILURE(); |
622 | 2 | } |
623 | 238k | if (stack_size < 4) { |
624 | 3 | return OTS_FAILURE(); |
625 | 3 | } |
626 | 238k | if (((stack_size - 4) % 8) == 0) { |
627 | | // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* |
628 | 58.1k | successful = true; |
629 | 180k | } else if ((stack_size >= 5) && |
630 | 180k | ((stack_size - 5) % 8) == 0) { |
631 | | // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf |
632 | 118k | successful = true; |
633 | 118k | } else if ((stack_size >= 8) && |
634 | 62.0k | ((stack_size - 8) % 8) == 0) { |
635 | | // {dxa dxb dyb dyc dyd dxe dye dxf}+ |
636 | 41.2k | successful = true; |
637 | 41.2k | } else if ((stack_size >= 9) && |
638 | 20.7k | ((stack_size - 9) % 8) == 0) { |
639 | | // {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf? |
640 | 20.7k | successful = true; |
641 | 20.7k | } |
642 | 1.80M | while (!argument_stack->empty()) |
643 | 1.56M | argument_stack->pop(); |
644 | 238k | return successful ? true : OTS_FAILURE(); |
645 | 238k | } |
646 | | |
647 | 22 | case ots::kDotSection: |
648 | | // Deprecated operator but harmless, we probably should drop it some how. |
649 | 22 | if (stack_size != 0) { |
650 | 1 | return OTS_FAILURE(); |
651 | 1 | } |
652 | 21 | return true; |
653 | | |
654 | 134 | case ots::kAnd: |
655 | 342 | case ots::kOr: |
656 | 467 | case ots::kEq: |
657 | 597 | case ots::kAdd: |
658 | 700 | case ots::kSub: |
659 | 700 | if (stack_size < 2) { |
660 | 2 | return OTS_FAILURE(); |
661 | 2 | } |
662 | 698 | argument_stack->pop(); |
663 | 698 | argument_stack->pop(); |
664 | 698 | argument_stack->push(dummy_result); |
665 | | // TODO(yusukes): Implement this. We should push a real value for all |
666 | | // arithmetic and conditional operations. |
667 | 698 | return true; |
668 | | |
669 | 188 | case ots::kNot: |
670 | 349 | case ots::kAbs: |
671 | 608 | case ots::kNeg: |
672 | 608 | if (stack_size < 1) { |
673 | 1 | return OTS_FAILURE(); |
674 | 1 | } |
675 | 607 | argument_stack->pop(); |
676 | 607 | argument_stack->push(dummy_result); |
677 | | // TODO(yusukes): Implement this. We should push a real value for all |
678 | | // arithmetic and conditional operations. |
679 | 607 | return true; |
680 | | |
681 | 452 | case ots::kDiv: |
682 | | // TODO(yusukes): Should detect div-by-zero errors. |
683 | 452 | if (stack_size < 2) { |
684 | 1 | return OTS_FAILURE(); |
685 | 1 | } |
686 | 451 | argument_stack->pop(); |
687 | 451 | argument_stack->pop(); |
688 | 451 | argument_stack->push(dummy_result); |
689 | | // TODO(yusukes): Implement this. We should push a real value for all |
690 | | // arithmetic and conditional operations. |
691 | 451 | return true; |
692 | | |
693 | 173 | case ots::kDrop: |
694 | 173 | if (stack_size < 1) { |
695 | 1 | return OTS_FAILURE(); |
696 | 1 | } |
697 | 172 | argument_stack->pop(); |
698 | 172 | return true; |
699 | | |
700 | 1 | case ots::kPut: |
701 | 2 | case ots::kGet: |
702 | 3 | case ots::kIndex: |
703 | | // For now, just call OTS_FAILURE since there is no way to check whether the |
704 | | // index argument, |i|, is out-of-bounds or not. Fortunately, no OpenType |
705 | | // fonts I have (except malicious ones!) use the operators. |
706 | | // TODO(yusukes): Implement them in a secure way. |
707 | 3 | return OTS_FAILURE(); |
708 | | |
709 | 1 | case ots::kRoll: |
710 | | // Likewise, just call OTS_FAILURE for kRoll since there is no way to check |
711 | | // whether |N| is smaller than the current stack depth or not. |
712 | | // TODO(yusukes): Implement them in a secure way. |
713 | 1 | return OTS_FAILURE(); |
714 | | |
715 | 2 | case ots::kRandom: |
716 | | // For now, we don't handle the 'random' operator since the operator makes |
717 | | // it hard to analyze hinting code statically. |
718 | 2 | return OTS_FAILURE(); |
719 | | |
720 | 241 | case ots::kIfElse: |
721 | 241 | if (stack_size < 4) { |
722 | 1 | return OTS_FAILURE(); |
723 | 1 | } |
724 | 240 | argument_stack->pop(); |
725 | 240 | argument_stack->pop(); |
726 | 240 | argument_stack->pop(); |
727 | 240 | argument_stack->pop(); |
728 | 240 | argument_stack->push(dummy_result); |
729 | | // TODO(yusukes): Implement this. We should push a real value for all |
730 | | // arithmetic and conditional operations. |
731 | 240 | return true; |
732 | | |
733 | 136 | case ots::kMul: |
734 | | // TODO(yusukes): Should detect overflows. |
735 | 136 | if (stack_size < 2) { |
736 | 1 | return OTS_FAILURE(); |
737 | 1 | } |
738 | 135 | argument_stack->pop(); |
739 | 135 | argument_stack->pop(); |
740 | 135 | argument_stack->push(dummy_result); |
741 | | // TODO(yusukes): Implement this. We should push a real value for all |
742 | | // arithmetic and conditional operations. |
743 | 135 | return true; |
744 | | |
745 | 231 | case ots::kSqrt: |
746 | | // TODO(yusukes): Should check if the argument is negative. |
747 | 231 | if (stack_size < 1) { |
748 | 2 | return OTS_FAILURE(); |
749 | 2 | } |
750 | 229 | argument_stack->pop(); |
751 | 229 | argument_stack->push(dummy_result); |
752 | | // TODO(yusukes): Implement this. We should push a real value for all |
753 | | // arithmetic and conditional operations. |
754 | 229 | return true; |
755 | | |
756 | 171 | case ots::kDup: |
757 | 171 | if (stack_size < 1) { |
758 | 1 | return OTS_FAILURE(); |
759 | 1 | } |
760 | 170 | argument_stack->pop(); |
761 | 170 | argument_stack->push(dummy_result); |
762 | 170 | argument_stack->push(dummy_result); |
763 | 170 | if (ArgumentStackOverflows(argument_stack, cs_ctx.cff2)) { |
764 | 1 | return OTS_FAILURE(); |
765 | 1 | } |
766 | | // TODO(yusukes): Implement this. We should push a real value for all |
767 | | // arithmetic and conditional operations. |
768 | 169 | return true; |
769 | | |
770 | 106 | case ots::kExch: |
771 | 106 | if (stack_size < 2) { |
772 | 1 | return OTS_FAILURE(); |
773 | 1 | } |
774 | 105 | argument_stack->pop(); |
775 | 105 | argument_stack->pop(); |
776 | 105 | argument_stack->push(dummy_result); |
777 | 105 | argument_stack->push(dummy_result); |
778 | | // TODO(yusukes): Implement this. We should push a real value for all |
779 | | // arithmetic and conditional operations. |
780 | 105 | return true; |
781 | | |
782 | 85 | case ots::kHFlex: |
783 | 85 | if (!(cs_ctx.width_seen)) { |
784 | 1 | return OTS_FAILURE(); |
785 | 1 | } |
786 | 84 | if (stack_size != 7) { |
787 | 1 | return OTS_FAILURE(); |
788 | 1 | } |
789 | 664 | while (!argument_stack->empty()) |
790 | 581 | argument_stack->pop(); |
791 | 83 | return true; |
792 | | |
793 | 43 | case ots::kFlex: |
794 | 43 | if (!(cs_ctx.width_seen)) { |
795 | 1 | return OTS_FAILURE(); |
796 | 1 | } |
797 | 42 | if (stack_size != 13) { |
798 | 1 | return OTS_FAILURE(); |
799 | 1 | } |
800 | 574 | while (!argument_stack->empty()) |
801 | 533 | argument_stack->pop(); |
802 | 41 | return true; |
803 | | |
804 | 38 | case ots::kHFlex1: |
805 | 38 | if (!(cs_ctx.width_seen)) { |
806 | 1 | return OTS_FAILURE(); |
807 | 1 | } |
808 | 37 | if (stack_size != 9) { |
809 | 2 | return OTS_FAILURE(); |
810 | 2 | } |
811 | 350 | while (!argument_stack->empty()) |
812 | 315 | argument_stack->pop(); |
813 | 35 | return true; |
814 | | |
815 | 42 | case ots::kFlex1: |
816 | 42 | if (!(cs_ctx.width_seen)) { |
817 | 1 | return OTS_FAILURE(); |
818 | 1 | } |
819 | 41 | if (stack_size != 11) { |
820 | 2 | return OTS_FAILURE(); |
821 | 2 | } |
822 | 468 | while (!argument_stack->empty()) |
823 | 429 | argument_stack->pop(); |
824 | 39 | return true; |
825 | 98.3M | } |
826 | | |
827 | 54 | return OTS_FAILURE_MSG("Undefined operator: %d (0x%x)", op, op); |
828 | 98.3M | } |
829 | | |
830 | | // Executes |char_string| and updates |argument_stack|. |
831 | | // |
832 | | // cff: parent OpenTypeCFF reference |
833 | | // call_depth: The current call depth. Initial value is zero. |
834 | | // global_subrs_index: Global subroutines. |
835 | | // local_subrs_index: Local subroutines for the current glyph. |
836 | | // cff_table: A whole CFF table which contains all global and local subroutines. |
837 | | // char_string: A charstring we'll execute. |char_string| can be a main routine |
838 | | // in CharString INDEX, or a subroutine in GlobalSubr/LocalSubr. |
839 | | // argument_stack: The stack which an operator in |char_string| operates. |
840 | | // cs_ctx: a CharStringContext holding values to persist across subrs, etc. |
841 | | // endchar_seen: true is set if |char_string| contains 'endchar'. |
842 | | // width_seen: true is set if |char_string| contains 'width' byte (which |
843 | | // is 0 or 1 byte) or if cff2 |
844 | | // num_stems: total number of hstems and vstems processed so far. |
845 | | // cff2: true if this is a CFF2 table |
846 | | // blend_seen: initially false; set to true if 'blend' operator encountered. |
847 | | // vsindex_seen: initially false; set to true if 'vsindex' encountered. |
848 | | // vsindex: initially = PrivateDICT's vsindex; may be changed by 'vsindex' |
849 | | // operator in CharString |
850 | | bool ExecuteCharString(ots::OpenTypeCFF& cff, |
851 | | size_t call_depth, |
852 | | const ots::CFFIndex& global_subrs_index, |
853 | | const ots::CFFIndex& local_subrs_index, |
854 | | ots::Buffer *cff_table, |
855 | | ots::Buffer *char_string, |
856 | | std::stack<int32_t> *argument_stack, |
857 | 48.8M | ots::CharStringContext& cs_ctx) { |
858 | 48.8M | if (call_depth > kMaxSubrNesting) { |
859 | 38 | return OTS_FAILURE(); |
860 | 38 | } |
861 | 48.8M | cs_ctx.endchar_seen = false; |
862 | | |
863 | 48.8M | const size_t length = char_string->length(); |
864 | 153M | while (char_string->offset() < length) { |
865 | 153M | int32_t operator_or_operand = 0; |
866 | 153M | bool is_operator = false; |
867 | 153M | if (!ReadNextNumberFromCharString(char_string, |
868 | 153M | &operator_or_operand, |
869 | 153M | &is_operator)) { |
870 | 13 | return OTS_FAILURE(); |
871 | 13 | } |
872 | | |
873 | | #ifdef DUMP_T2CHARSTRING |
874 | | /* |
875 | | You can dump all operators and operands (except mask bytes for hintmask |
876 | | and cntrmask) by the following code: |
877 | | */ |
878 | | |
879 | | if (!is_operator) { |
880 | | std::fprintf(stderr, "%d ", operator_or_operand); |
881 | | } else { |
882 | | std::fprintf(stderr, "%s\n", |
883 | | CharStringOperatorToString( |
884 | | ots::CharStringOperator(operator_or_operand)) |
885 | | ); |
886 | | } |
887 | | #endif |
888 | | |
889 | 153M | if (!is_operator) { |
890 | 55.2M | argument_stack->push(operator_or_operand); |
891 | 55.2M | if (ArgumentStackOverflows(argument_stack, cs_ctx.cff2)) { |
892 | 8 | return OTS_FAILURE(); |
893 | 8 | } |
894 | 55.2M | continue; |
895 | 55.2M | } |
896 | | |
897 | 98.3M | if (++cs_ctx.num_ops > kMaxCharStringOps) { |
898 | 0 | ots::Font* font = cff.GetFont(); |
899 | 0 | return OTS_FAILURE_MSG("charstring executes too many ops"); |
900 | 0 | } |
901 | | |
902 | | // An operator is found. Execute it. |
903 | 98.3M | if (!ExecuteCharStringOperator(cff, |
904 | 98.3M | operator_or_operand, |
905 | 98.3M | call_depth, |
906 | 98.3M | global_subrs_index, |
907 | 98.3M | local_subrs_index, |
908 | 98.3M | cff_table, |
909 | 98.3M | char_string, |
910 | 98.3M | argument_stack, |
911 | 98.3M | cs_ctx)) { |
912 | 1.04k | return OTS_FAILURE(); |
913 | 1.04k | } |
914 | 98.3M | if (cs_ctx.endchar_seen) { |
915 | 28.7k | return true; |
916 | 28.7k | } |
917 | 98.3M | if (operator_or_operand == ots::kReturn) { |
918 | 48.4M | return true; |
919 | 48.4M | } |
920 | 98.3M | } |
921 | | |
922 | | // No endchar operator is found (CFF1 only) |
923 | 348k | if (cs_ctx.cff2) |
924 | 348k | return true; |
925 | 34 | return OTS_FAILURE(); |
926 | 348k | } |
927 | | |
928 | | // Selects a set of subroutines for |glyph_index| from |cff| and sets it on |
929 | | // |out_local_subrs_to_use|. Returns true on success. |
930 | | bool SelectLocalSubr(const ots::OpenTypeCFF& cff, |
931 | | uint16_t glyph_index, // 0-origin |
932 | 125k | const ots::CFFIndex **out_local_subrs_to_use) { |
933 | 125k | bool cff2 = (cff.major == 2); |
934 | 125k | *out_local_subrs_to_use = NULL; |
935 | | |
936 | | // First, find local subrs from |local_subrs_per_font|. |
937 | 125k | if ((cff.fd_select.size() > 0) && |
938 | 521 | (!cff.local_subrs_per_font.empty())) { |
939 | | // Look up FDArray index for the glyph. |
940 | 471 | const auto& iter = cff.fd_select.find(glyph_index); |
941 | 471 | if (iter == cff.fd_select.end()) { |
942 | 2 | return OTS_FAILURE(); |
943 | 2 | } |
944 | 469 | const auto fd_index = iter->second; |
945 | 469 | if (fd_index >= cff.local_subrs_per_font.size()) { |
946 | 2 | return OTS_FAILURE(); |
947 | 2 | } |
948 | 467 | *out_local_subrs_to_use = cff.local_subrs_per_font.at(fd_index); |
949 | 125k | } else if (cff.local_subrs) { |
950 | | // Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect |
951 | | // entries. If The font has a local subrs index associated with the Top |
952 | | // DICT (not FDArrays), use it. |
953 | 19.2k | *out_local_subrs_to_use = cff.local_subrs; |
954 | 106k | } else if (cff2 && cff.local_subrs_per_font.size() == 1) { |
955 | 105k | *out_local_subrs_to_use = cff.local_subrs_per_font.at(0); |
956 | 105k | } else { |
957 | | // Just return NULL. |
958 | 344 | *out_local_subrs_to_use = NULL; |
959 | 344 | } |
960 | | |
961 | 125k | return true; |
962 | 125k | } |
963 | | |
964 | | } // namespace |
965 | | |
966 | | namespace ots { |
967 | | |
968 | | bool ValidateCFFCharStrings( |
969 | | ots::OpenTypeCFF& cff, |
970 | | const CFFIndex& global_subrs_index, |
971 | 609 | Buffer* cff_table) { |
972 | 609 | const CFFIndex& char_strings_index = *(cff.charstrings_index); |
973 | 609 | if (char_strings_index.offsets.size() == 0) { |
974 | 8 | return OTS_FAILURE(); // no charstring. |
975 | 8 | } |
976 | | |
977 | | // For each glyph, validate the corresponding charstring. |
978 | 126k | for (unsigned i = 1; i < char_strings_index.offsets.size(); ++i) { |
979 | | // Prepare a Buffer object, |char_string|, which contains the charstring |
980 | | // for the |i|-th glyph. |
981 | 125k | const size_t length = |
982 | 125k | char_strings_index.offsets[i] - char_strings_index.offsets[i - 1]; |
983 | 125k | if (length > kMaxCharStringLength) { |
984 | 0 | return OTS_FAILURE(); |
985 | 0 | } |
986 | 125k | const size_t offset = char_strings_index.offsets[i - 1]; |
987 | 125k | cff_table->set_offset(offset); |
988 | 125k | if (!cff_table->Skip(length)) { |
989 | 0 | return OTS_FAILURE(); |
990 | 0 | } |
991 | 125k | Buffer char_string(cff_table->buffer() + offset, length); |
992 | | |
993 | | // Get a local subrs for the glyph. |
994 | 125k | const unsigned glyph_index = i - 1; // index in the map is 0-origin. |
995 | 125k | const CFFIndex *local_subrs_to_use = NULL; |
996 | 125k | if (!SelectLocalSubr(cff, |
997 | 125k | glyph_index, |
998 | 125k | &local_subrs_to_use)) { |
999 | 4 | return OTS_FAILURE(); |
1000 | 4 | } |
1001 | | // If |local_subrs_to_use| is still NULL, use an empty one. |
1002 | 125k | CFFIndex default_empty_subrs; |
1003 | 125k | if (!local_subrs_to_use){ |
1004 | 344 | local_subrs_to_use = &default_empty_subrs; |
1005 | 344 | } |
1006 | | |
1007 | | // Check a charstring for the |i|-th glyph. |
1008 | 125k | std::stack<int32_t> argument_stack; |
1009 | | // Context to store values that must persist across subrs, etc. |
1010 | 125k | CharStringContext cs_ctx; |
1011 | 125k | cs_ctx.cff2 = (cff.major == 2); |
1012 | | // CFF2 CharString has no value for width, so we start with true here to |
1013 | | // error out if width is found. |
1014 | 125k | cs_ctx.width_seen = cs_ctx.cff2; |
1015 | | // CFF2 CharStrings' default vsindex comes from the associated PrivateDICT |
1016 | 125k | if (cs_ctx.cff2) { |
1017 | 105k | const auto& iter = cff.fd_select.find(glyph_index); |
1018 | 105k | auto fd_index = 0; |
1019 | 105k | if (iter != cff.fd_select.end()) { |
1020 | 164 | fd_index = iter->second; |
1021 | 164 | } |
1022 | 105k | if (fd_index >= (int32_t)cff.vsindex_per_font.size()) { |
1023 | | // shouldn't get this far with a font in this condition, but just in case |
1024 | 1 | return OTS_FAILURE(); // fd_index out-of-range |
1025 | 1 | } |
1026 | 105k | cs_ctx.vsindex = cff.vsindex_per_font.at(fd_index); |
1027 | 105k | } |
1028 | | |
1029 | | #ifdef DUMP_T2CHARSTRING |
1030 | | fprintf(stderr, "\n---- CharString %*d ----\n", 5, glyph_index); |
1031 | | #endif |
1032 | | |
1033 | 125k | if (!ExecuteCharString(cff, |
1034 | 125k | 0 /* initial call_depth is zero */, |
1035 | 125k | global_subrs_index, *local_subrs_to_use, |
1036 | 125k | cff_table, &char_string, &argument_stack, |
1037 | 125k | cs_ctx)) { |
1038 | 422 | return OTS_FAILURE(); |
1039 | 422 | } |
1040 | 125k | if (!cs_ctx.cff2 && !cs_ctx.endchar_seen) { |
1041 | 3 | return OTS_FAILURE(); |
1042 | 3 | } |
1043 | 125k | } |
1044 | 171 | return true; |
1045 | 601 | } |
1046 | | |
1047 | | } // namespace ots |
1048 | | |
1049 | | #undef TABLE_NAME |