blob: ddb9657981848b2ab2a36a24b52e9c228cbcc165 [file] [log] [blame]
yusukes@chromium.org6263d062010-08-06 03:27:28 +00001// Copyright (c) 2010 The Chromium 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_type2_charstring.h"
9
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +000010#include <climits>
yusukes@chromium.org6263d062010-08-06 03:27:28 +000011#include <cstdio>
12#include <cstring>
yusukes@chromium.org6263d062010-08-06 03:27:28 +000013#include <stack>
14#include <string>
15#include <utility>
16
17namespace {
18
yusukes@chromium.org6263d062010-08-06 03:27:28 +000019// Type 2 Charstring Implementation Limits. See Appendix. B in Adobe Technical
20// Note #5177.
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +000021const int32_t kMaxSubrsCount = 65536;
yusukes@chromium.org6263d062010-08-06 03:27:28 +000022const size_t kMaxCharStringLength = 65535;
23const size_t kMaxArgumentStack = 48;
24const size_t kMaxNumberOfStemHints = 96;
25const size_t kMaxSubrNesting = 10;
26
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +000027// |dummy_result| should be a huge positive integer so callsubr and callgsubr
28// will fail with the dummy value.
29const int32_t dummy_result = INT_MAX;
30
yusukes@chromium.org6263d062010-08-06 03:27:28 +000031bool ExecuteType2CharString(size_t call_depth,
32 const ots::CFFIndex& global_subrs_index,
33 const ots::CFFIndex& local_subrs_index,
34 ots::Buffer *cff_table,
35 ots::Buffer *char_string,
36 std::stack<int32_t> *argument_stack,
37 bool *out_found_endchar,
38 bool *out_found_width,
39 size_t *in_out_num_stems);
40
yusukes@chromium.org6263d062010-08-06 03:27:28 +000041// Read one or more bytes from the |char_string| buffer and stores the number
42// read on |out_number|. If the number read is an operator (ex 'vstem'), sets
43// true on |out_is_operator|. Returns true if the function read a number.
44bool ReadNextNumberFromType2CharString(ots::Buffer *char_string,
45 int32_t *out_number,
46 bool *out_is_operator) {
47 uint8_t v = 0;
48 if (!char_string->ReadU8(&v)) {
49 return OTS_FAILURE();
50 }
51 *out_is_operator = false;
52
53 // The conversion algorithm is described in Adobe Technical Note #5177, page
54 // 13, Table 1.
55 if (v <= 11) {
56 *out_number = v;
57 *out_is_operator = true;
58 } else if (v == 12) {
59 uint16_t result = (v << 8);
60 if (!char_string->ReadU8(&v)) {
61 return OTS_FAILURE();
62 }
63 result += v;
64 *out_number = result;
65 *out_is_operator = true;
66 } else if (v <= 27) {
67 // Special handling for v==19 and v==20 are implemented in
68 // ExecuteType2CharStringOperator().
69 *out_number = v;
70 *out_is_operator = true;
71 } else if (v == 28) {
72 if (!char_string->ReadU8(&v)) {
73 return OTS_FAILURE();
74 }
75 uint16_t result = (v << 8);
76 if (!char_string->ReadU8(&v)) {
77 return OTS_FAILURE();
78 }
79 result += v;
80 *out_number = result;
81 } else if (v <= 31) {
82 *out_number = v;
83 *out_is_operator = true;
84 } else if (v <= 246) {
85 *out_number = static_cast<int32_t>(v) - 139;
86 } else if (v <= 250) {
87 uint8_t w = 0;
88 if (!char_string->ReadU8(&w)) {
89 return OTS_FAILURE();
90 }
91 *out_number = ((static_cast<int32_t>(v) - 247) * 256) +
92 static_cast<int32_t>(w) + 108;
93 } else if (v <= 254) {
94 uint8_t w = 0;
95 if (!char_string->ReadU8(&w)) {
96 return OTS_FAILURE();
97 }
98 *out_number = -((static_cast<int32_t>(v) - 251) * 256) -
99 static_cast<int32_t>(w) - 108;
100 } else if (v == 255) {
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000101 // TODO(yusukes): We should not skip the 4 bytes. Note that when v is 255,
102 // we should treat the following 4-bytes as a 16.16 fixed-point number
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000103 // rather than 32bit signed int.
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000104 if (!char_string->Skip(4)) {
105 return OTS_FAILURE();
106 }
107 *out_number = dummy_result;
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000108 } else {
109 return OTS_FAILURE();
110 }
111
112 return true;
113}
114
115// Executes |op| and updates |argument_stack|. Returns true if the execution
116// succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively
117// calls ExecuteType2CharString() function. The arguments other than |op| and
118// |argument_stack| are passed for that reason.
119bool ExecuteType2CharStringOperator(int32_t op,
120 size_t call_depth,
121 const ots::CFFIndex& global_subrs_index,
122 const ots::CFFIndex& local_subrs_index,
123 ots::Buffer *cff_table,
124 ots::Buffer *char_string,
125 std::stack<int32_t> *argument_stack,
126 bool *out_found_endchar,
127 bool *in_out_found_width,
128 size_t *in_out_num_stems) {
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000129 const size_t stack_size = argument_stack->size();
130
131 switch (op) {
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000132 case ots::kCallSubr:
133 case ots::kCallGSubr: {
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000134 const ots::CFFIndex& subrs_index =
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000135 (op == ots::kCallSubr ? local_subrs_index : global_subrs_index);
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000136
137 if (stack_size < 1) {
138 return OTS_FAILURE();
139 }
140 int32_t subr_number = argument_stack->top();
141 argument_stack->pop();
142 if (subr_number == dummy_result) {
143 // For safety, we allow subr calls only with immediate subr numbers for
144 // now. For example, we allow "123 callgsubr", but does not allow "100 12
145 // add callgsubr". Please note that arithmetic and conditional operators
146 // always push the |dummy_result| in this implementation.
147 return OTS_FAILURE();
148 }
149
150 // See Adobe Technical Note #5176 (CFF), "16. Local/GlobalSubrs INDEXes."
151 int32_t bias = 32768;
152 if (subrs_index.count < 1240) {
153 bias = 107;
154 } else if (subrs_index.count < 33900) {
155 bias = 1131;
156 }
157 subr_number += bias;
158
159 // Sanity checks of |subr_number|.
160 if (subr_number < 0) {
161 return OTS_FAILURE();
162 }
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000163 if (subr_number >= kMaxSubrsCount) {
164 return OTS_FAILURE();
165 }
166 if (subrs_index.offsets.size() <= static_cast<size_t>(subr_number + 1)) {
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000167 return OTS_FAILURE(); // The number is out-of-bounds.
168 }
169
170 // Prepare ots::Buffer where we're going to jump.
171 const size_t length =
172 subrs_index.offsets[subr_number + 1] - subrs_index.offsets[subr_number];
173 if (length > kMaxCharStringLength) {
174 return OTS_FAILURE();
175 }
176 const size_t offset = subrs_index.offsets[subr_number];
177 cff_table->set_offset(offset);
178 if (!cff_table->Skip(length)) {
179 return OTS_FAILURE();
180 }
181 ots::Buffer char_string_to_jump(cff_table->buffer() + offset, length);
182
183 return ExecuteType2CharString(call_depth + 1,
184 global_subrs_index,
185 local_subrs_index,
186 cff_table,
187 &char_string_to_jump,
188 argument_stack,
189 out_found_endchar,
190 in_out_found_width,
191 in_out_num_stems);
192 }
193
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000194 case ots::kReturn:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000195 return true;
196
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000197 case ots::kEndChar:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000198 *out_found_endchar = true;
199 *in_out_found_width = true; // just in case.
200 return true;
201
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000202 case ots::kHStem:
203 case ots::kVStem:
204 case ots::kHStemHm:
205 case ots::kVStemHm: {
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000206 bool successful = false;
207 if (stack_size < 2) {
208 return OTS_FAILURE();
209 }
210 if ((stack_size % 2) == 0) {
211 successful = true;
212 } else if ((!(*in_out_found_width)) && (((stack_size - 1) % 2) == 0)) {
213 // The -1 is for "width" argument. For details, see Adobe Technical Note
214 // #5177, page 16, note 4.
215 successful = true;
216 }
217 (*in_out_num_stems) += (stack_size / 2);
218 if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
219 return OTS_FAILURE();
220 }
221 while (!argument_stack->empty())
222 argument_stack->pop();
223 *in_out_found_width = true; // always set true since "w" might be 0 byte.
224 return successful ? true : OTS_FAILURE();
225 }
226
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000227 case ots::kRMoveTo: {
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000228 bool successful = false;
229 if (stack_size == 2) {
230 successful = true;
231 } else if ((!(*in_out_found_width)) && (stack_size - 1 == 2)) {
232 successful = true;
233 }
234 while (!argument_stack->empty())
235 argument_stack->pop();
236 *in_out_found_width = true;
237 return successful ? true : OTS_FAILURE();
238 }
239
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000240 case ots::kVMoveTo:
241 case ots::kHMoveTo: {
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000242 bool successful = false;
243 if (stack_size == 1) {
244 successful = true;
245 } else if ((!(*in_out_found_width)) && (stack_size - 1 == 1)) {
246 successful = true;
247 }
248 while (!argument_stack->empty())
249 argument_stack->pop();
250 *in_out_found_width = true;
251 return successful ? true : OTS_FAILURE();
252 }
253
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000254 case ots::kHintMask:
255 case ots::kCntrMask: {
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000256 bool successful = false;
257 if (stack_size == 0) {
258 successful = true;
259 } else if ((!(*in_out_found_width)) && (stack_size == 1)) {
260 // A number for "width" is found.
261 successful = true;
262 } else if ((!(*in_out_found_width)) || // in this case, any sizes are ok.
263 ((stack_size % 2) == 0)) {
264 // The numbers are vstem definition.
265 // See Adobe Technical Note #5177, page 24, hintmask.
266 (*in_out_num_stems) += (stack_size / 2);
267 if ((*in_out_num_stems) > kMaxNumberOfStemHints) {
268 return OTS_FAILURE();
269 }
270 successful = true;
271 }
272 if (!successful) {
273 return OTS_FAILURE();
274 }
275
276 if ((*in_out_num_stems) == 0) {
277 return OTS_FAILURE();
278 }
279 const size_t mask_bytes = (*in_out_num_stems + 7) / 8;
280 if (!char_string->Skip(mask_bytes)) {
281 return OTS_FAILURE();
282 }
283 while (!argument_stack->empty())
284 argument_stack->pop();
285 *in_out_found_width = true;
286 return true;
287 }
288
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000289 case ots::kRLineTo:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000290 if (!(*in_out_found_width)) {
291 // The first stack-clearing operator should be one of hstem, hstemhm,
292 // vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, rmoveto, or
293 // endchar. For details, see Adobe Technical Note #5177, page 16, note 4.
294 return OTS_FAILURE();
295 }
296 if (stack_size < 2) {
297 return OTS_FAILURE();
298 }
299 if ((stack_size % 2) != 0) {
300 return OTS_FAILURE();
301 }
302 while (!argument_stack->empty())
303 argument_stack->pop();
304 return true;
305
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000306 case ots::kHLineTo:
307 case ots::kVLineTo:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000308 if (!(*in_out_found_width)) {
309 return OTS_FAILURE();
310 }
311 if (stack_size < 1) {
312 return OTS_FAILURE();
313 }
314 while (!argument_stack->empty())
315 argument_stack->pop();
316 return true;
317
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000318 case ots::kRRCurveTo:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000319 if (!(*in_out_found_width)) {
320 return OTS_FAILURE();
321 }
322 if (stack_size < 6) {
323 return OTS_FAILURE();
324 }
325 if ((stack_size % 6) != 0) {
326 return OTS_FAILURE();
327 }
328 while (!argument_stack->empty())
329 argument_stack->pop();
330 return true;
331
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000332 case ots::kRCurveLine:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000333 if (!(*in_out_found_width)) {
334 return OTS_FAILURE();
335 }
336 if (stack_size < 8) {
337 return OTS_FAILURE();
338 }
339 if (((stack_size - 2) % 6) != 0) {
340 return OTS_FAILURE();
341 }
342 while (!argument_stack->empty())
343 argument_stack->pop();
344 return true;
345
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000346 case ots::kRLineCurve:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000347 if (!(*in_out_found_width)) {
348 return OTS_FAILURE();
349 }
350 if (stack_size < 8) {
351 return OTS_FAILURE();
352 }
353 if (((stack_size - 6) % 2) != 0) {
354 return OTS_FAILURE();
355 }
356 while (!argument_stack->empty())
357 argument_stack->pop();
358 return true;
359
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000360 case ots::kVVCurveTo:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000361 if (!(*in_out_found_width)) {
362 return OTS_FAILURE();
363 }
364 if (stack_size < 4) {
365 return OTS_FAILURE();
366 }
367 if (((stack_size % 4) != 0) &&
368 (((stack_size - 1) % 4) != 0)) {
369 return OTS_FAILURE();
370 }
371 while (!argument_stack->empty())
372 argument_stack->pop();
373 return true;
374
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000375 case ots::kHHCurveTo: {
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000376 bool successful = false;
377 if (!(*in_out_found_width)) {
378 return OTS_FAILURE();
379 }
380 if (stack_size < 4) {
381 return OTS_FAILURE();
382 }
383 if ((stack_size % 4) == 0) {
384 // {dxa dxb dyb dxc}+
385 successful = true;
386 } else if (((stack_size - 1) % 4) == 0) {
387 // dy1? {dxa dxb dyb dxc}+
388 successful = true;
389 }
390 while (!argument_stack->empty())
391 argument_stack->pop();
392 return successful ? true : OTS_FAILURE();
393 }
394
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000395 case ots::kVHCurveTo:
396 case ots::kHVCurveTo: {
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000397 bool successful = false;
398 if (!(*in_out_found_width)) {
399 return OTS_FAILURE();
400 }
401 if (stack_size < 4) {
402 return OTS_FAILURE();
403 }
404 if (((stack_size - 4) % 8) == 0) {
405 // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}*
406 successful = true;
407 } else if ((stack_size >= 5) &&
408 ((stack_size - 5) % 8) == 0) {
409 // dx1 dx2 dy2 dy3 {dya dxb dyb dxc dxd dxe dye dyf}* dxf
410 successful = true;
411 } else if ((stack_size >= 8) &&
412 ((stack_size - 8) % 8) == 0) {
413 // {dxa dxb dyb dyc dyd dxe dye dxf}+
414 successful = true;
415 } else if ((stack_size >= 9) &&
416 ((stack_size - 9) % 8) == 0) {
417 // {dxa dxb dyb dyc dyd dxe dye dxf}+ dyf?
418 successful = true;
419 }
420 while (!argument_stack->empty())
421 argument_stack->pop();
422 return successful ? true : OTS_FAILURE();
423 }
424
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000425 case ots::kAnd:
426 case ots::kOr:
427 case ots::kEq:
428 case ots::kAdd:
429 case ots::kSub:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000430 if (stack_size < 2) {
431 return OTS_FAILURE();
432 }
433 argument_stack->pop();
434 argument_stack->pop();
435 argument_stack->push(dummy_result);
436 // TODO(yusukes): Implement this. We should push a real value for all
437 // arithmetic and conditional operations.
438 return true;
439
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000440 case ots::kNot:
441 case ots::kAbs:
442 case ots::kNeg:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000443 if (stack_size < 1) {
444 return OTS_FAILURE();
445 }
446 argument_stack->pop();
447 argument_stack->push(dummy_result);
448 // TODO(yusukes): Implement this. We should push a real value for all
449 // arithmetic and conditional operations.
450 return true;
451
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000452 case ots::kDiv:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000453 // TODO(yusukes): Should detect div-by-zero errors.
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000454 if (stack_size < 2) {
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000455 return OTS_FAILURE();
456 }
457 argument_stack->pop();
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000458 argument_stack->pop();
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000459 argument_stack->push(dummy_result);
460 // TODO(yusukes): Implement this. We should push a real value for all
461 // arithmetic and conditional operations.
462 return true;
463
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000464 case ots::kDrop:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000465 if (stack_size < 1) {
466 return OTS_FAILURE();
467 }
468 argument_stack->pop();
469 return true;
470
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000471 case ots::kPut:
472 case ots::kGet:
473 case ots::kIndex:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000474 // For now, just call OTS_FAILURE since there is no way to check whether the
475 // index argument, |i|, is out-of-bounds or not. Fortunately, no OpenType
476 // fonts I have (except malicious ones!) use the operators.
477 // TODO(yusukes): Implement them in a secure way.
478 return OTS_FAILURE();
479
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000480 case ots::kRoll:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000481 // Likewise, just call OTS_FAILURE for kRoll since there is no way to check
482 // whether |N| is smaller than the current stack depth or not.
483 // TODO(yusukes): Implement them in a secure way.
484 return OTS_FAILURE();
485
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000486 case ots::kRandom:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000487 // For now, we don't handle the 'random' operator since the operator makes
488 // it hard to analyze hinting code statically.
489 return OTS_FAILURE();
490
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000491 case ots::kIfElse:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000492 if (stack_size < 4) {
493 return OTS_FAILURE();
494 }
495 argument_stack->pop();
496 argument_stack->pop();
497 argument_stack->pop();
498 argument_stack->pop();
499 argument_stack->push(dummy_result);
500 // TODO(yusukes): Implement this. We should push a real value for all
501 // arithmetic and conditional operations.
502 return true;
503
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000504 case ots::kMul:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000505 // TODO(yusukes): Should detect overflows.
506 if (stack_size < 2) {
507 return OTS_FAILURE();
508 }
509 argument_stack->pop();
510 argument_stack->pop();
511 argument_stack->push(dummy_result);
512 // TODO(yusukes): Implement this. We should push a real value for all
513 // arithmetic and conditional operations.
514 return true;
515
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000516 case ots::kSqrt:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000517 // TODO(yusukes): Should check if the argument is negative.
518 if (stack_size < 1) {
519 return OTS_FAILURE();
520 }
521 argument_stack->pop();
522 argument_stack->push(dummy_result);
523 // TODO(yusukes): Implement this. We should push a real value for all
524 // arithmetic and conditional operations.
525 return true;
526
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000527 case ots::kDup:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000528 if (stack_size < 1) {
529 return OTS_FAILURE();
530 }
531 argument_stack->pop();
532 argument_stack->push(dummy_result);
533 argument_stack->push(dummy_result);
534 if (argument_stack->size() > kMaxArgumentStack) {
535 return OTS_FAILURE();
536 }
537 // TODO(yusukes): Implement this. We should push a real value for all
538 // arithmetic and conditional operations.
539 return true;
540
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000541 case ots::kExch:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000542 if (stack_size < 2) {
543 return OTS_FAILURE();
544 }
545 argument_stack->pop();
546 argument_stack->pop();
547 argument_stack->push(dummy_result);
548 argument_stack->push(dummy_result);
549 // TODO(yusukes): Implement this. We should push a real value for all
550 // arithmetic and conditional operations.
551 return true;
552
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000553 case ots::kHFlex:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000554 if (!(*in_out_found_width)) {
555 return OTS_FAILURE();
556 }
557 if (stack_size != 7) {
558 return OTS_FAILURE();
559 }
560 while (!argument_stack->empty())
561 argument_stack->pop();
562 return true;
563
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000564 case ots::kFlex:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000565 if (!(*in_out_found_width)) {
566 return OTS_FAILURE();
567 }
568 if (stack_size != 13) {
569 return OTS_FAILURE();
570 }
571 while (!argument_stack->empty())
572 argument_stack->pop();
573 return true;
574
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000575 case ots::kHFlex1:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000576 if (!(*in_out_found_width)) {
577 return OTS_FAILURE();
578 }
579 if (stack_size != 9) {
580 return OTS_FAILURE();
581 }
582 while (!argument_stack->empty())
583 argument_stack->pop();
584 return true;
585
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000586 case ots::kFlex1:
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000587 if (!(*in_out_found_width)) {
588 return OTS_FAILURE();
589 }
590 if (stack_size != 11) {
591 return OTS_FAILURE();
592 }
593 while (!argument_stack->empty())
594 argument_stack->pop();
595 return true;
596 }
597
598 OTS_WARNING("Undefined operator: %d (0x%x)", op, op);
599 return OTS_FAILURE();
600}
601
602// Executes |char_string| and updates |argument_stack|.
603//
604// call_depth: The current call depth. Initial value is zero.
605// global_subrs_index: Global subroutines.
606// local_subrs_index: Local subroutines for the current glyph.
607// cff_table: A whole CFF table which contains all global and local subroutines.
608// char_string: A charstring we'll execute. |char_string| can be a main routine
609// in CharString INDEX, or a subroutine in GlobalSubr/LocalSubr.
610// argument_stack: The stack which an operator in |char_string| operates.
611// out_found_endchar: true is set if |char_string| contains 'endchar'.
612// in_out_found_width: true is set if |char_string| contains 'width' byte (which
613// is 0 or 1 byte.)
614// in_out_num_stems: total number of hstems and vstems processed so far.
615bool ExecuteType2CharString(size_t call_depth,
616 const ots::CFFIndex& global_subrs_index,
617 const ots::CFFIndex& local_subrs_index,
618 ots::Buffer *cff_table,
619 ots::Buffer *char_string,
620 std::stack<int32_t> *argument_stack,
621 bool *out_found_endchar,
622 bool *in_out_found_width,
623 size_t *in_out_num_stems) {
624 if (call_depth > kMaxSubrNesting) {
625 return OTS_FAILURE();
626 }
627 *out_found_endchar = false;
628
629 const size_t length = char_string->length();
630 while (char_string->offset() < length) {
631 int32_t operator_or_operand = 0;
632 bool is_operator = false;
633 if (!ReadNextNumberFromType2CharString(char_string,
634 &operator_or_operand,
635 &is_operator)) {
636 return OTS_FAILURE();
637 }
638
639 /*
640 You can dump all operators and operands (except mask bytes for hintmask
641 and cntrmask) by the following code:
642
643 if (!is_operator) {
644 std::fprintf(stderr, "#%d# ", operator_or_operand);
645 } else {
646 std::fprintf(stderr, "#%s#\n",
647 Type2CharStringOperatorToString(
648 Type2CharStringOperator(operator_or_operand)),
649 operator_or_operand);
650 }
651 */
652
653 if (!is_operator) {
654 argument_stack->push(operator_or_operand);
655 if (argument_stack->size() > kMaxArgumentStack) {
656 return OTS_FAILURE();
657 }
658 continue;
659 }
660
661 // An operator is found. Execute it.
662 if (!ExecuteType2CharStringOperator(operator_or_operand,
663 call_depth,
664 global_subrs_index,
665 local_subrs_index,
666 cff_table,
667 char_string,
668 argument_stack,
669 out_found_endchar,
670 in_out_found_width,
671 in_out_num_stems)) {
672 return OTS_FAILURE();
673 }
674 if (*out_found_endchar) {
675 return true;
676 }
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000677 if (operator_or_operand == ots::kReturn) {
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000678 return true;
679 }
680 }
681
682 // No endchar operator is found.
683 return OTS_FAILURE();
684}
685
686// Selects a set of subroutings for |glyph_index| from |cff| and sets it on
687// |out_local_subrs_to_use|. Returns true on success.
688bool SelectLocalSubr(const std::map<uint16_t, uint8_t> &fd_select,
689 const std::vector<ots::CFFIndex *> &local_subrs_per_font,
690 const ots::CFFIndex *local_subrs,
691 uint16_t glyph_index, // 0-origin
692 const ots::CFFIndex **out_local_subrs_to_use) {
693 *out_local_subrs_to_use = NULL;
694
695 // First, find local subrs from |local_subrs_per_font|.
696 if ((fd_select.size() > 0) &&
697 (!local_subrs_per_font.empty())) {
698 // Look up FDArray index for the glyph.
699 std::map<uint16_t, uint8_t>::const_iterator iter =
700 fd_select.find(glyph_index);
701 if (iter == fd_select.end()) {
702 return OTS_FAILURE();
703 }
704 const uint8_t fd_index = iter->second;
705 if (fd_index >= local_subrs_per_font.size()) {
706 return OTS_FAILURE();
707 }
708 *out_local_subrs_to_use = local_subrs_per_font.at(fd_index);
709 } else if (local_subrs) {
710 // Second, try to use |local_subrs|. Most Latin fonts don't have FDSelect
711 // entries. If The font has a local subrs index associated with the Top
712 // DICT (not FDArrays), use it.
713 *out_local_subrs_to_use = local_subrs;
714 } else {
715 // Just return NULL.
716 *out_local_subrs_to_use = NULL;
717 }
718
719 return true;
720}
721
722} // namespace
723
724namespace ots {
725
726bool ValidateType2CharStringIndex(
727 const CFFIndex& char_strings_index,
728 const CFFIndex& global_subrs_index,
729 const std::map<uint16_t, uint8_t> &fd_select,
730 const std::vector<CFFIndex *> &local_subrs_per_font,
731 const CFFIndex *local_subrs,
732 Buffer* cff_table) {
733 if (char_strings_index.offsets.size() == 0) {
734 return OTS_FAILURE(); // no charstring.
735 }
736
737 // For each glyph, validate the corresponding charstring.
738 for (unsigned i = 1; i < char_strings_index.offsets.size(); ++i) {
739 // Prepare a Buffer object, |char_string|, which contains the charstring
740 // for the |i|-th glyph.
741 const size_t length =
742 char_strings_index.offsets[i] - char_strings_index.offsets[i - 1];
743 if (length > kMaxCharStringLength) {
744 return OTS_FAILURE();
745 }
746 const size_t offset = char_strings_index.offsets[i - 1];
747 cff_table->set_offset(offset);
748 if (!cff_table->Skip(length)) {
749 return OTS_FAILURE();
750 }
751 Buffer char_string(cff_table->buffer() + offset, length);
752
753 // Get a local subrs for the glyph.
754 const unsigned glyph_index = i - 1; // index in the map is 0-origin.
755 const CFFIndex *local_subrs_to_use = NULL;
756 if (!SelectLocalSubr(fd_select,
757 local_subrs_per_font,
758 local_subrs,
759 glyph_index,
760 &local_subrs_to_use)) {
761 return OTS_FAILURE();
762 }
763 // If |local_subrs_to_use| is still NULL, use an empty one.
764 CFFIndex default_empty_subrs;
765 if (!local_subrs_to_use){
766 local_subrs_to_use = &default_empty_subrs;
767 }
768
769 // Check a charstring for the |i|-th glyph.
770 std::stack<int32_t> argument_stack;
771 bool found_endchar = false;
772 bool found_width = false;
773 size_t num_stems = 0;
774 if (!ExecuteType2CharString(0 /* initial call_depth is zero */,
775 global_subrs_index, *local_subrs_to_use,
776 cff_table, &char_string, &argument_stack,
777 &found_endchar, &found_width, &num_stems)) {
778 return OTS_FAILURE();
779 }
yusukes@chromium.orgfbb12962010-08-10 01:36:03 +0000780 if (!found_endchar) {
781 return OTS_FAILURE();
782 }
yusukes@chromium.org6263d062010-08-06 03:27:28 +0000783 }
784 return true;
785}
786
787} // namespace ots