Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4349 | Serge | 1 | /***************************************************************************/ |
2 | /* */ |
||
3 | /* cf2intrp.c */ |
||
4 | /* */ |
||
5 | /* Adobe's CFF Interpreter (body). */ |
||
6 | /* */ |
||
7 | /* Copyright 2007-2013 Adobe Systems Incorporated. */ |
||
8 | /* */ |
||
9 | /* This software, and all works of authorship, whether in source or */ |
||
10 | /* object code form as indicated by the copyright notice(s) included */ |
||
11 | /* herein (collectively, the "Work") is made available, and may only be */ |
||
12 | /* used, modified, and distributed under the FreeType Project License, */ |
||
13 | /* LICENSE.TXT. Additionally, subject to the terms and conditions of the */ |
||
14 | /* FreeType Project License, each contributor to the Work hereby grants */ |
||
15 | /* to any individual or legal entity exercising permissions granted by */ |
||
16 | /* the FreeType Project License and this section (hereafter, "You" or */ |
||
17 | /* "Your") a perpetual, worldwide, non-exclusive, no-charge, */ |
||
18 | /* royalty-free, irrevocable (except as stated in this section) patent */ |
||
19 | /* license to make, have made, use, offer to sell, sell, import, and */ |
||
20 | /* otherwise transfer the Work, where such license applies only to those */ |
||
21 | /* patent claims licensable by such contributor that are necessarily */ |
||
22 | /* infringed by their contribution(s) alone or by combination of their */ |
||
23 | /* contribution(s) with the Work to which such contribution(s) was */ |
||
24 | /* submitted. If You institute patent litigation against any entity */ |
||
25 | /* (including a cross-claim or counterclaim in a lawsuit) alleging that */ |
||
26 | /* the Work or a contribution incorporated within the Work constitutes */ |
||
27 | /* direct or contributory patent infringement, then any patent licenses */ |
||
28 | /* granted to You under this License for that Work shall terminate as of */ |
||
29 | /* the date such litigation is filed. */ |
||
30 | /* */ |
||
31 | /* By using, modifying, or distributing the Work you indicate that you */ |
||
32 | /* have read and understood the terms and conditions of the */ |
||
33 | /* FreeType Project License as well as those provided in this section, */ |
||
34 | /* and you accept them fully. */ |
||
35 | /* */ |
||
36 | /***************************************************************************/ |
||
37 | |||
38 | |||
39 | #include "cf2ft.h" |
||
40 | #include FT_INTERNAL_DEBUG_H |
||
41 | |||
42 | #include "cf2glue.h" |
||
43 | #include "cf2font.h" |
||
44 | #include "cf2stack.h" |
||
45 | #include "cf2hints.h" |
||
46 | |||
47 | #include "cf2error.h" |
||
48 | |||
49 | |||
50 | /*************************************************************************/ |
||
51 | /* */ |
||
52 | /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ |
||
53 | /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ |
||
54 | /* messages during execution. */ |
||
55 | /* */ |
||
56 | #undef FT_COMPONENT |
||
57 | #define FT_COMPONENT trace_cf2interp |
||
58 | |||
59 | |||
60 | /* some operators are not implemented yet */ |
||
61 | #define CF2_FIXME FT_TRACE4(( "cf2_interpT2CharString:" \ |
||
62 | " operator not implemented yet\n" )) |
||
63 | |||
64 | |||
65 | |||
66 | FT_LOCAL_DEF( void ) |
||
67 | cf2_hintmask_init( CF2_HintMask hintmask, |
||
68 | FT_Error* error ) |
||
69 | { |
||
70 | FT_ZERO( hintmask ); |
||
71 | |||
72 | hintmask->error = error; |
||
73 | } |
||
74 | |||
75 | |||
76 | FT_LOCAL_DEF( FT_Bool ) |
||
77 | cf2_hintmask_isValid( const CF2_HintMask hintmask ) |
||
78 | { |
||
79 | return hintmask->isValid; |
||
80 | } |
||
81 | |||
82 | |||
83 | FT_LOCAL_DEF( FT_Bool ) |
||
84 | cf2_hintmask_isNew( const CF2_HintMask hintmask ) |
||
85 | { |
||
86 | return hintmask->isNew; |
||
87 | } |
||
88 | |||
89 | |||
90 | FT_LOCAL_DEF( void ) |
||
91 | cf2_hintmask_setNew( CF2_HintMask hintmask, |
||
92 | FT_Bool val ) |
||
93 | { |
||
94 | hintmask->isNew = val; |
||
95 | } |
||
96 | |||
97 | |||
98 | /* clients call `getMaskPtr' in order to iterate */ |
||
99 | /* through hint mask */ |
||
100 | |||
101 | FT_LOCAL_DEF( FT_Byte* ) |
||
102 | cf2_hintmask_getMaskPtr( CF2_HintMask hintmask ) |
||
103 | { |
||
104 | return hintmask->mask; |
||
105 | } |
||
106 | |||
107 | |||
108 | static size_t |
||
109 | cf2_hintmask_setCounts( CF2_HintMask hintmask, |
||
110 | size_t bitCount ) |
||
111 | { |
||
112 | if ( bitCount > CF2_MAX_HINTS ) |
||
113 | { |
||
114 | /* total of h and v stems must be <= 96 */ |
||
115 | CF2_SET_ERROR( hintmask->error, Invalid_Glyph_Format ); |
||
116 | return 0; |
||
117 | } |
||
118 | |||
119 | hintmask->bitCount = bitCount; |
||
120 | hintmask->byteCount = ( hintmask->bitCount + 7 ) / 8; |
||
121 | |||
122 | hintmask->isValid = TRUE; |
||
123 | hintmask->isNew = TRUE; |
||
124 | |||
125 | return bitCount; |
||
126 | } |
||
127 | |||
128 | |||
129 | /* consume the hintmask bytes from the charstring, advancing the src */ |
||
130 | /* pointer */ |
||
131 | static void |
||
132 | cf2_hintmask_read( CF2_HintMask hintmask, |
||
133 | CF2_Buffer charstring, |
||
134 | size_t bitCount ) |
||
135 | { |
||
136 | size_t i; |
||
137 | |||
138 | #ifndef CF2_NDEBUG |
||
139 | /* these are the bits in the final mask byte that should be zero */ |
||
140 | /* Note: this variable is only used in an assert expression below */ |
||
141 | /* and then only if CF2_NDEBUG is not defined */ |
||
142 | CF2_UInt mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1; |
||
143 | #endif |
||
144 | |||
145 | |||
146 | /* initialize counts and isValid */ |
||
147 | if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 ) |
||
148 | return; |
||
149 | |||
150 | FT_ASSERT( hintmask->byteCount > 0 ); |
||
151 | |||
152 | FT_TRACE4(( " (maskbytes:" )); |
||
153 | |||
154 | /* set mask and advance interpreter's charstring pointer */ |
||
155 | for ( i = 0; i < hintmask->byteCount; i++ ) |
||
156 | { |
||
157 | hintmask->mask[i] = (FT_Byte)cf2_buf_readByte( charstring ); |
||
158 | FT_TRACE4(( " 0x%02X", hintmask->mask[i] )); |
||
159 | } |
||
160 | |||
161 | FT_TRACE4(( ")\n" )); |
||
162 | |||
163 | /* assert any unused bits in last byte are zero unless there's a prior */ |
||
164 | /* error */ |
||
165 | /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1 */ |
||
166 | #ifndef CF2_NDEBUG |
||
167 | FT_ASSERT( ( hintmask->mask[hintmask->byteCount - 1] & mask ) == 0 || |
||
168 | *hintmask->error ); |
||
169 | #endif |
||
170 | } |
||
171 | |||
172 | |||
173 | FT_LOCAL_DEF( void ) |
||
174 | cf2_hintmask_setAll( CF2_HintMask hintmask, |
||
175 | size_t bitCount ) |
||
176 | { |
||
177 | size_t i; |
||
178 | CF2_UInt mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1; |
||
179 | |||
180 | |||
181 | /* initialize counts and isValid */ |
||
182 | if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 ) |
||
183 | return; |
||
184 | |||
185 | FT_ASSERT( hintmask->byteCount > 0 ); |
||
186 | FT_ASSERT( hintmask->byteCount < |
||
187 | sizeof ( hintmask->mask ) / sizeof ( hintmask->mask[0] ) ); |
||
188 | |||
189 | /* set mask to all ones */ |
||
190 | for ( i = 0; i < hintmask->byteCount; i++ ) |
||
191 | hintmask->mask[i] = 0xFF; |
||
192 | |||
193 | /* clear unused bits */ |
||
194 | /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1 */ |
||
195 | hintmask->mask[hintmask->byteCount - 1] &= ~mask; |
||
196 | } |
||
197 | |||
198 | |||
199 | /* Type2 charstring opcodes */ |
||
200 | enum |
||
201 | { |
||
202 | cf2_cmdRESERVED_0, /* 0 */ |
||
203 | cf2_cmdHSTEM, /* 1 */ |
||
204 | cf2_cmdRESERVED_2, /* 2 */ |
||
205 | cf2_cmdVSTEM, /* 3 */ |
||
206 | cf2_cmdVMOVETO, /* 4 */ |
||
207 | cf2_cmdRLINETO, /* 5 */ |
||
208 | cf2_cmdHLINETO, /* 6 */ |
||
209 | cf2_cmdVLINETO, /* 7 */ |
||
210 | cf2_cmdRRCURVETO, /* 8 */ |
||
211 | cf2_cmdRESERVED_9, /* 9 */ |
||
212 | cf2_cmdCALLSUBR, /* 10 */ |
||
213 | cf2_cmdRETURN, /* 11 */ |
||
214 | cf2_cmdESC, /* 12 */ |
||
215 | cf2_cmdRESERVED_13, /* 13 */ |
||
216 | cf2_cmdENDCHAR, /* 14 */ |
||
217 | cf2_cmdRESERVED_15, /* 15 */ |
||
218 | cf2_cmdRESERVED_16, /* 16 */ |
||
219 | cf2_cmdRESERVED_17, /* 17 */ |
||
220 | cf2_cmdHSTEMHM, /* 18 */ |
||
221 | cf2_cmdHINTMASK, /* 19 */ |
||
222 | cf2_cmdCNTRMASK, /* 20 */ |
||
223 | cf2_cmdRMOVETO, /* 21 */ |
||
224 | cf2_cmdHMOVETO, /* 22 */ |
||
225 | cf2_cmdVSTEMHM, /* 23 */ |
||
226 | cf2_cmdRCURVELINE, /* 24 */ |
||
227 | cf2_cmdRLINECURVE, /* 25 */ |
||
228 | cf2_cmdVVCURVETO, /* 26 */ |
||
229 | cf2_cmdHHCURVETO, /* 27 */ |
||
230 | cf2_cmdEXTENDEDNMBR, /* 28 */ |
||
231 | cf2_cmdCALLGSUBR, /* 29 */ |
||
232 | cf2_cmdVHCURVETO, /* 30 */ |
||
233 | cf2_cmdHVCURVETO /* 31 */ |
||
234 | }; |
||
235 | |||
236 | enum |
||
237 | { |
||
238 | cf2_escDOTSECTION, /* 0 */ |
||
239 | cf2_escRESERVED_1, /* 1 */ |
||
240 | cf2_escRESERVED_2, /* 2 */ |
||
241 | cf2_escAND, /* 3 */ |
||
242 | cf2_escOR, /* 4 */ |
||
243 | cf2_escNOT, /* 5 */ |
||
244 | cf2_escRESERVED_6, /* 6 */ |
||
245 | cf2_escRESERVED_7, /* 7 */ |
||
246 | cf2_escRESERVED_8, /* 8 */ |
||
247 | cf2_escABS, /* 9 */ |
||
248 | cf2_escADD, /* 10 like otherADD */ |
||
249 | cf2_escSUB, /* 11 like otherSUB */ |
||
250 | cf2_escDIV, /* 12 */ |
||
251 | cf2_escRESERVED_13, /* 13 */ |
||
252 | cf2_escNEG, /* 14 */ |
||
253 | cf2_escEQ, /* 15 */ |
||
254 | cf2_escRESERVED_16, /* 16 */ |
||
255 | cf2_escRESERVED_17, /* 17 */ |
||
256 | cf2_escDROP, /* 18 */ |
||
257 | cf2_escRESERVED_19, /* 19 */ |
||
258 | cf2_escPUT, /* 20 like otherPUT */ |
||
259 | cf2_escGET, /* 21 like otherGET */ |
||
260 | cf2_escIFELSE, /* 22 like otherIFELSE */ |
||
261 | cf2_escRANDOM, /* 23 like otherRANDOM */ |
||
262 | cf2_escMUL, /* 24 like otherMUL */ |
||
263 | cf2_escRESERVED_25, /* 25 */ |
||
264 | cf2_escSQRT, /* 26 */ |
||
265 | cf2_escDUP, /* 27 like otherDUP */ |
||
266 | cf2_escEXCH, /* 28 like otherEXCH */ |
||
267 | cf2_escINDEX, /* 29 */ |
||
268 | cf2_escROLL, /* 30 */ |
||
269 | cf2_escRESERVED_31, /* 31 */ |
||
270 | cf2_escRESERVED_32, /* 32 */ |
||
271 | cf2_escRESERVED_33, /* 33 */ |
||
272 | cf2_escHFLEX, /* 34 */ |
||
273 | cf2_escFLEX, /* 35 */ |
||
274 | cf2_escHFLEX1, /* 36 */ |
||
275 | cf2_escFLEX1 /* 37 */ |
||
276 | }; |
||
277 | |||
278 | |||
279 | /* `stemHintArray' does not change once we start drawing the outline. */ |
||
280 | static void |
||
281 | cf2_doStems( const CF2_Font font, |
||
282 | CF2_Stack opStack, |
||
283 | CF2_ArrStack stemHintArray, |
||
284 | CF2_Fixed* width, |
||
285 | FT_Bool* haveWidth, |
||
286 | CF2_Fixed hintOffset ) |
||
287 | { |
||
288 | CF2_UInt i; |
||
289 | CF2_UInt count = cf2_stack_count( opStack ); |
||
290 | FT_Bool hasWidthArg = (FT_Bool)( count & 1 ); |
||
291 | |||
292 | /* variable accumulates delta values from operand stack */ |
||
293 | CF2_Fixed position = hintOffset; |
||
294 | |||
295 | if ( hasWidthArg && ! *haveWidth ) |
||
296 | *width = cf2_stack_getReal( opStack, 0 ) + |
||
297 | cf2_getNominalWidthX( font->decoder ); |
||
298 | |||
299 | if ( font->decoder->width_only ) |
||
300 | goto exit; |
||
301 | |||
302 | for ( i = hasWidthArg ? 1 : 0; i < count; i += 2 ) |
||
303 | { |
||
304 | /* construct a CF2_StemHint and push it onto the list */ |
||
305 | CF2_StemHintRec stemhint; |
||
306 | |||
307 | |||
308 | stemhint.min = |
||
309 | position += cf2_stack_getReal( opStack, i ); |
||
310 | stemhint.max = |
||
311 | position += cf2_stack_getReal( opStack, i + 1 ); |
||
312 | |||
313 | stemhint.used = FALSE; |
||
314 | stemhint.maxDS = |
||
315 | stemhint.minDS = 0; |
||
316 | |||
317 | cf2_arrstack_push( stemHintArray, &stemhint ); /* defer error check */ |
||
318 | } |
||
319 | |||
320 | cf2_stack_clear( opStack ); |
||
321 | |||
322 | exit: |
||
323 | /* cf2_doStems must define a width (may be default) */ |
||
324 | *haveWidth = TRUE; |
||
325 | } |
||
326 | |||
327 | |||
328 | static void |
||
329 | cf2_doFlex( CF2_Stack opStack, |
||
330 | CF2_Fixed* curX, |
||
331 | CF2_Fixed* curY, |
||
332 | CF2_GlyphPath glyphPath, |
||
333 | const FT_Bool* readFromStack, |
||
334 | FT_Bool doConditionalLastRead ) |
||
335 | { |
||
336 | CF2_Fixed vals[14]; |
||
337 | CF2_UInt index; |
||
338 | FT_Bool isHFlex; |
||
339 | CF2_Int top, i, j; |
||
340 | |||
341 | |||
342 | vals[0] = *curX; |
||
343 | vals[1] = *curY; |
||
344 | index = 0; |
||
345 | isHFlex = readFromStack[9] == FALSE; |
||
346 | top = isHFlex ? 9 : 10; |
||
347 | |||
348 | for ( i = 0; i < top; i++ ) |
||
349 | { |
||
350 | vals[i + 2] = vals[i]; |
||
351 | if ( readFromStack[i] ) |
||
352 | vals[i + 2] += cf2_stack_getReal( opStack, index++ ); |
||
353 | } |
||
354 | |||
355 | if ( isHFlex ) |
||
356 | vals[9 + 2] = *curY; |
||
357 | |||
358 | if ( doConditionalLastRead ) |
||
359 | { |
||
360 | FT_Bool lastIsX = (FT_Bool)( cf2_fixedAbs( vals[10] - *curX ) > |
||
361 | cf2_fixedAbs( vals[11] - *curY ) ); |
||
362 | CF2_Fixed lastVal = cf2_stack_getReal( opStack, index ); |
||
363 | |||
364 | |||
365 | if ( lastIsX ) |
||
366 | { |
||
367 | vals[12] = vals[10] + lastVal; |
||
368 | vals[13] = *curY; |
||
369 | } |
||
370 | else |
||
371 | { |
||
372 | vals[12] = *curX; |
||
373 | vals[13] = vals[11] + lastVal; |
||
374 | } |
||
375 | } |
||
376 | else |
||
377 | { |
||
378 | if ( readFromStack[10] ) |
||
379 | vals[12] = vals[10] + cf2_stack_getReal( opStack, index++ ); |
||
380 | else |
||
381 | vals[12] = *curX; |
||
382 | |||
383 | if ( readFromStack[11] ) |
||
384 | vals[13] = vals[11] + cf2_stack_getReal( opStack, index ); |
||
385 | else |
||
386 | vals[13] = *curY; |
||
387 | } |
||
388 | |||
389 | for ( j = 0; j < 2; j++ ) |
||
390 | cf2_glyphpath_curveTo( glyphPath, vals[j * 6 + 2], |
||
391 | vals[j * 6 + 3], |
||
392 | vals[j * 6 + 4], |
||
393 | vals[j * 6 + 5], |
||
394 | vals[j * 6 + 6], |
||
395 | vals[j * 6 + 7] ); |
||
396 | |||
397 | cf2_stack_clear( opStack ); |
||
398 | |||
399 | *curX = vals[12]; |
||
400 | *curY = vals[13]; |
||
401 | } |
||
402 | |||
403 | |||
404 | /* |
||
405 | * `error' is a shared error code used by many objects in this |
||
406 | * routine. Before the code continues from an error, it must check and |
||
407 | * record the error in `*error'. The idea is that this shared |
||
408 | * error code will record the first error encountered. If testing |
||
409 | * for an error anyway, the cost of `goto exit' is small, so we do it, |
||
410 | * even if continuing would be safe. In this case, `lastError' is |
||
411 | * set, so the testing and storing can be done in one place, at `exit'. |
||
412 | * |
||
413 | * Continuing after an error is intended for objects which do their own |
||
414 | * testing of `*error', e.g., array stack functions. This allows us to |
||
415 | * avoid an extra test after the call. |
||
416 | * |
||
417 | * Unimplemented opcodes are ignored. |
||
418 | * |
||
419 | */ |
||
420 | FT_LOCAL_DEF( void ) |
||
421 | cf2_interpT2CharString( CF2_Font font, |
||
422 | CF2_Buffer buf, |
||
423 | CF2_OutlineCallbacks callbacks, |
||
424 | const FT_Vector* translation, |
||
425 | FT_Bool doingSeac, |
||
426 | CF2_Fixed curX, |
||
427 | CF2_Fixed curY, |
||
428 | CF2_Fixed* width ) |
||
429 | { |
||
430 | /* lastError is used for errors that are immediately tested */ |
||
431 | FT_Error lastError = FT_Err_Ok; |
||
432 | |||
433 | /* pointer to parsed font object */ |
||
434 | CFF_Decoder* decoder = font->decoder; |
||
435 | |||
436 | FT_Error* error = &font->error; |
||
437 | FT_Memory memory = font->memory; |
||
438 | |||
439 | CF2_Fixed scaleY = font->innerTransform.d; |
||
440 | CF2_Fixed nominalWidthX = cf2_getNominalWidthX( decoder ); |
||
441 | |||
442 | /* save this for hinting seac accents */ |
||
443 | CF2_Fixed hintOriginY = curY; |
||
444 | |||
445 | CF2_Stack opStack = NULL; |
||
446 | FT_Byte op1; /* first opcode byte */ |
||
447 | |||
448 | /* instruction limit; 20,000,000 matches Avalon */ |
||
449 | FT_UInt32 instructionLimit = 20000000UL; |
||
450 | |||
451 | CF2_ArrStackRec subrStack; |
||
452 | |||
453 | FT_Bool haveWidth; |
||
454 | CF2_Buffer charstring = NULL; |
||
455 | |||
456 | CF2_Int charstringIndex = -1; /* initialize to empty */ |
||
457 | |||
458 | /* TODO: placeholders for hint structures */ |
||
459 | |||
460 | /* objects used for hinting */ |
||
461 | CF2_ArrStackRec hStemHintArray; |
||
462 | CF2_ArrStackRec vStemHintArray; |
||
463 | |||
464 | CF2_HintMaskRec hintMask; |
||
465 | CF2_GlyphPathRec glyphPath; |
||
466 | |||
467 | |||
468 | /* initialize the remaining objects */ |
||
469 | cf2_arrstack_init( &subrStack, |
||
470 | memory, |
||
471 | error, |
||
472 | sizeof ( CF2_BufferRec ) ); |
||
473 | cf2_arrstack_init( &hStemHintArray, |
||
474 | memory, |
||
475 | error, |
||
476 | sizeof ( CF2_StemHintRec ) ); |
||
477 | cf2_arrstack_init( &vStemHintArray, |
||
478 | memory, |
||
479 | error, |
||
480 | sizeof ( CF2_StemHintRec ) ); |
||
481 | |||
482 | /* initialize CF2_StemHint arrays */ |
||
483 | cf2_hintmask_init( &hintMask, error ); |
||
484 | |||
485 | /* initialize path map to manage drawing operations */ |
||
486 | |||
487 | /* Note: last 4 params are used to handle `MoveToPermissive', which */ |
||
488 | /* may need to call `hintMap.Build' */ |
||
489 | /* TODO: MoveToPermissive is gone; are these still needed? */ |
||
490 | cf2_glyphpath_init( &glyphPath, |
||
491 | font, |
||
492 | callbacks, |
||
493 | scaleY, |
||
494 | /* hShift, */ |
||
495 | &hStemHintArray, |
||
496 | &vStemHintArray, |
||
497 | &hintMask, |
||
498 | hintOriginY, |
||
499 | &font->blues, |
||
500 | translation ); |
||
501 | |||
502 | /* |
||
503 | * Initialize state for width parsing. From the CFF Spec: |
||
504 | * |
||
505 | * The first stack-clearing operator, which must be one of hstem, |
||
506 | * hstemhm, vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, |
||
507 | * rmoveto, or endchar, takes an additional argument - the width (as |
||
508 | * described earlier), which may be expressed as zero or one numeric |
||
509 | * argument. |
||
510 | * |
||
511 | * What we implement here uses the first validly specified width, but |
||
512 | * does not detect errors for specifying more than one width. |
||
513 | * |
||
514 | * If one of the above operators occurs without explicitly specifying |
||
515 | * a width, we assume the default width. |
||
516 | * |
||
517 | */ |
||
518 | haveWidth = FALSE; |
||
519 | *width = cf2_getDefaultWidthX( decoder ); |
||
520 | |||
521 | /* |
||
522 | * Note: at this point, all pointers to resources must be NULL |
||
523 | * and all local objects must be initialized. |
||
524 | * There must be no branches to exit: above this point. |
||
525 | * |
||
526 | */ |
||
527 | |||
528 | /* allocate an operand stack */ |
||
529 | opStack = cf2_stack_init( memory, error ); |
||
530 | if ( !opStack ) |
||
531 | { |
||
532 | lastError = FT_THROW( Out_Of_Memory ); |
||
533 | goto exit; |
||
534 | } |
||
535 | |||
536 | /* initialize subroutine stack by placing top level charstring as */ |
||
537 | /* first element (max depth plus one for the charstring) */ |
||
538 | /* Note: Caller owns and must finalize the first charstring. */ |
||
539 | /* Our copy of it does not change that requirement. */ |
||
540 | cf2_arrstack_setCount( &subrStack, CF2_MAX_SUBR + 1 ); |
||
541 | |||
542 | charstring = (CF2_Buffer)cf2_arrstack_getBuffer( &subrStack ); |
||
543 | *charstring = *buf; /* structure copy */ |
||
544 | |||
545 | charstringIndex = 0; /* entry is valid now */ |
||
546 | |||
547 | /* catch errors so far */ |
||
548 | if ( *error ) |
||
549 | goto exit; |
||
550 | |||
551 | /* main interpreter loop */ |
||
552 | while ( 1 ) |
||
553 | { |
||
554 | if ( cf2_buf_isEnd( charstring ) ) |
||
555 | { |
||
556 | /* If we've reached the end of the charstring, simulate a */ |
||
557 | /* cf2_cmdRETURN or cf2_cmdENDCHAR. */ |
||
558 | if ( charstringIndex ) |
||
559 | op1 = cf2_cmdRETURN; /* end of buffer for subroutine */ |
||
560 | else |
||
561 | op1 = cf2_cmdENDCHAR; /* end of buffer for top level charstring */ |
||
562 | } |
||
563 | else |
||
564 | op1 = (FT_Byte)cf2_buf_readByte( charstring ); |
||
565 | |||
566 | /* check for errors once per loop */ |
||
567 | if ( *error ) |
||
568 | goto exit; |
||
569 | |||
570 | instructionLimit--; |
||
571 | if ( instructionLimit == 0 ) |
||
572 | { |
||
573 | lastError = FT_THROW( Invalid_Glyph_Format ); |
||
574 | goto exit; |
||
575 | } |
||
576 | |||
577 | switch( op1 ) |
||
578 | { |
||
579 | case cf2_cmdRESERVED_0: |
||
580 | case cf2_cmdRESERVED_2: |
||
581 | case cf2_cmdRESERVED_9: |
||
582 | case cf2_cmdRESERVED_13: |
||
583 | case cf2_cmdRESERVED_15: |
||
584 | case cf2_cmdRESERVED_16: |
||
585 | case cf2_cmdRESERVED_17: |
||
586 | /* we may get here if we have a prior error */ |
||
587 | FT_TRACE4(( " unknown op (%d)\n", op1 )); |
||
588 | break; |
||
589 | |||
590 | case cf2_cmdHSTEMHM: |
||
591 | case cf2_cmdHSTEM: |
||
592 | FT_TRACE4(( op1 == cf2_cmdHSTEMHM ? " hstemhm\n" : " hstem\n" )); |
||
593 | |||
594 | /* never add hints after the mask is computed */ |
||
595 | if ( cf2_hintmask_isValid( &hintMask ) ) |
||
596 | FT_TRACE4(( "cf2_interpT2CharString:" |
||
597 | " invalid horizontal hint mask\n" )); |
||
598 | |||
599 | cf2_doStems( font, |
||
600 | opStack, |
||
601 | &hStemHintArray, |
||
602 | width, |
||
603 | &haveWidth, |
||
604 | |||
605 | |||
606 | if ( font->decoder->width_only ) |
||
607 | goto exit; |
||
608 | |||
609 | break; |
||
610 | |||
611 | case cf2_cmdVSTEMHM: |
||
612 | case cf2_cmdVSTEM: |
||
613 | FT_TRACE4(( op1 == cf2_cmdVSTEMHM ? " vstemhm\n" : " vstem\n" )); |
||
614 | |||
615 | /* never add hints after the mask is computed */ |
||
616 | if ( cf2_hintmask_isValid( &hintMask ) ) |
||
617 | FT_TRACE4(( "cf2_interpT2CharString:" |
||
618 | " invalid vertical hint mask\n" )); |
||
619 | |||
620 | cf2_doStems( font, |
||
621 | opStack, |
||
622 | &vStemHintArray, |
||
623 | width, |
||
624 | &haveWidth, |
||
625 | |||
626 | |||
627 | if ( font->decoder->width_only ) |
||
628 | goto exit; |
||
629 | |||
630 | break; |
||
631 | |||
632 | case cf2_cmdVMOVETO: |
||
633 | FT_TRACE4(( " vmoveto\n" )); |
||
634 | |||
635 | if ( cf2_stack_count( opStack ) > 1 && !haveWidth ) |
||
636 | *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX; |
||
637 | |||
638 | /* width is defined or default after this */ |
||
639 | haveWidth = TRUE; |
||
640 | |||
641 | if ( font->decoder->width_only ) |
||
642 | goto exit; |
||
643 | |||
644 | curY += cf2_stack_popFixed( opStack ); |
||
645 | |||
646 | cf2_glyphpath_moveTo( &glyphPath, curX, curY ); |
||
647 | |||
648 | break; |
||
649 | |||
650 | case cf2_cmdRLINETO: |
||
651 | { |
||
652 | CF2_UInt index; |
||
653 | CF2_UInt count = cf2_stack_count( opStack ); |
||
654 | |||
655 | |||
656 | FT_TRACE4(( " rlineto\n" )); |
||
657 | |||
658 | for ( index = 0; index < count; index += 2 ) |
||
659 | { |
||
660 | curX += cf2_stack_getReal( opStack, index + 0 ); |
||
661 | curY += cf2_stack_getReal( opStack, index + 1 ); |
||
662 | |||
663 | cf2_glyphpath_lineTo( &glyphPath, curX, curY ); |
||
664 | } |
||
665 | |||
666 | cf2_stack_clear( opStack ); |
||
667 | } |
||
668 | continue; /* no need to clear stack again */ |
||
669 | |||
670 | case cf2_cmdHLINETO: |
||
671 | case cf2_cmdVLINETO: |
||
672 | { |
||
673 | CF2_UInt index; |
||
674 | CF2_UInt count = cf2_stack_count( opStack ); |
||
675 | |||
676 | FT_Bool isX = op1 == cf2_cmdHLINETO; |
||
677 | |||
678 | |||
679 | FT_TRACE4(( isX ? " hlineto\n" : " vlineto\n" )); |
||
680 | |||
681 | for ( index = 0; index < count; index++ ) |
||
682 | { |
||
683 | CF2_Fixed v = cf2_stack_getReal( opStack, index ); |
||
684 | |||
685 | |||
686 | if ( isX ) |
||
687 | curX += v; |
||
688 | else |
||
689 | curY += v; |
||
690 | |||
691 | isX = !isX; |
||
692 | |||
693 | cf2_glyphpath_lineTo( &glyphPath, curX, curY ); |
||
694 | } |
||
695 | |||
696 | cf2_stack_clear( opStack ); |
||
697 | } |
||
698 | continue; |
||
699 | |||
700 | case cf2_cmdRCURVELINE: |
||
701 | case cf2_cmdRRCURVETO: |
||
702 | { |
||
703 | CF2_UInt count = cf2_stack_count( opStack ); |
||
704 | CF2_UInt index = 0; |
||
705 | |||
706 | |||
707 | FT_TRACE4(( op1 == cf2_cmdRCURVELINE ? " rcurveline\n" |
||
708 | : " rrcurveto\n" )); |
||
709 | |||
710 | while ( index + 6 <= count ) |
||
711 | { |
||
712 | CF2_Fixed x1 = cf2_stack_getReal( opStack, index + 0 ) + curX; |
||
713 | CF2_Fixed y1 = cf2_stack_getReal( opStack, index + 1 ) + curY; |
||
714 | CF2_Fixed x2 = cf2_stack_getReal( opStack, index + 2 ) + x1; |
||
715 | CF2_Fixed y2 = cf2_stack_getReal( opStack, index + 3 ) + y1; |
||
716 | CF2_Fixed x3 = cf2_stack_getReal( opStack, index + 4 ) + x2; |
||
717 | CF2_Fixed y3 = cf2_stack_getReal( opStack, index + 5 ) + y2; |
||
718 | |||
719 | |||
720 | cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 ); |
||
721 | |||
722 | curX = x3; |
||
723 | curY = y3; |
||
724 | index += 6; |
||
725 | } |
||
726 | |||
727 | if ( op1 == cf2_cmdRCURVELINE ) |
||
728 | { |
||
729 | curX += cf2_stack_getReal( opStack, index + 0 ); |
||
730 | curY += cf2_stack_getReal( opStack, index + 1 ); |
||
731 | |||
732 | cf2_glyphpath_lineTo( &glyphPath, curX, curY ); |
||
733 | } |
||
734 | |||
735 | cf2_stack_clear( opStack ); |
||
736 | } |
||
737 | continue; /* no need to clear stack again */ |
||
738 | |||
739 | case cf2_cmdCALLGSUBR: |
||
740 | case cf2_cmdCALLSUBR: |
||
741 | { |
||
742 | CF2_UInt subrIndex; |
||
743 | |||
744 | |||
745 | FT_TRACE4(( op1 == cf2_cmdCALLGSUBR ? " callgsubr" |
||
746 | : " callsubr" )); |
||
747 | |||
748 | if ( charstringIndex > CF2_MAX_SUBR ) |
||
749 | { |
||
750 | /* max subr plus one for charstring */ |
||
751 | lastError = FT_THROW( Invalid_Glyph_Format ); |
||
752 | goto exit; /* overflow of stack */ |
||
753 | } |
||
754 | |||
755 | /* push our current CFF charstring region on subrStack */ |
||
756 | charstring = (CF2_Buffer) |
||
757 | cf2_arrstack_getPointer( &subrStack, |
||
758 | charstringIndex + 1 ); |
||
759 | |||
760 | /* set up the new CFF region and pointer */ |
||
761 | subrIndex = cf2_stack_popInt( opStack ); |
||
762 | |||
763 | switch ( op1 ) |
||
764 | { |
||
765 | case cf2_cmdCALLGSUBR: |
||
766 | FT_TRACE4(( "(%d)\n", subrIndex + decoder->globals_bias )); |
||
767 | |||
768 | if ( cf2_initGlobalRegionBuffer( decoder, |
||
769 | subrIndex, |
||
770 | charstring ) ) |
||
771 | { |
||
772 | lastError = FT_THROW( Invalid_Glyph_Format ); |
||
773 | goto exit; /* subroutine lookup or stream error */ |
||
774 | } |
||
775 | break; |
||
776 | |||
777 | default: |
||
778 | /* cf2_cmdCALLSUBR */ |
||
779 | FT_TRACE4(( "(%d)\n", subrIndex + decoder->locals_bias )); |
||
780 | |||
781 | if ( cf2_initLocalRegionBuffer( decoder, |
||
782 | subrIndex, |
||
783 | charstring ) ) |
||
784 | { |
||
785 | lastError = FT_THROW( Invalid_Glyph_Format ); |
||
786 | goto exit; /* subroutine lookup or stream error */ |
||
787 | } |
||
788 | } |
||
789 | |||
790 | charstringIndex += 1; /* entry is valid now */ |
||
791 | } |
||
792 | continue; /* do not clear the stack */ |
||
793 | |||
794 | case cf2_cmdRETURN: |
||
795 | FT_TRACE4(( " return\n" )); |
||
796 | |||
797 | if ( charstringIndex < 1 ) |
||
798 | { |
||
799 | /* Note: cannot return from top charstring */ |
||
800 | lastError = FT_THROW( Invalid_Glyph_Format ); |
||
801 | goto exit; /* underflow of stack */ |
||
802 | } |
||
803 | |||
804 | /* restore position in previous charstring */ |
||
805 | charstring = (CF2_Buffer) |
||
806 | cf2_arrstack_getPointer( &subrStack, |
||
807 | --charstringIndex ); |
||
808 | continue; /* do not clear the stack */ |
||
809 | |||
810 | case cf2_cmdESC: |
||
811 | { |
||
812 | FT_Byte op2 = (FT_Byte)cf2_buf_readByte( charstring ); |
||
813 | |||
814 | |||
815 | switch ( op2 ) |
||
816 | { |
||
817 | case cf2_escDOTSECTION: |
||
818 | /* something about `flip type of locking' -- ignore it */ |
||
819 | FT_TRACE4(( " dotsection\n" )); |
||
820 | |||
821 | break; |
||
822 | |||
823 | /* TODO: should these operators be supported? */ |
||
824 | case cf2_escAND: /* in spec */ |
||
825 | FT_TRACE4(( " and\n" )); |
||
826 | |||
827 | CF2_FIXME; |
||
828 | break; |
||
829 | |||
830 | case cf2_escOR: /* in spec */ |
||
831 | FT_TRACE4(( " or\n" )); |
||
832 | |||
833 | CF2_FIXME; |
||
834 | break; |
||
835 | |||
836 | case cf2_escNOT: /* in spec */ |
||
837 | FT_TRACE4(( " not\n" )); |
||
838 | |||
839 | CF2_FIXME; |
||
840 | break; |
||
841 | |||
842 | case cf2_escABS: /* in spec */ |
||
843 | FT_TRACE4(( " abs\n" )); |
||
844 | |||
845 | CF2_FIXME; |
||
846 | break; |
||
847 | |||
848 | case cf2_escADD: /* in spec */ |
||
849 | FT_TRACE4(( " add\n" )); |
||
850 | |||
851 | CF2_FIXME; |
||
852 | break; |
||
853 | |||
854 | case cf2_escSUB: /* in spec */ |
||
855 | FT_TRACE4(( " sub\n" )); |
||
856 | |||
857 | CF2_FIXME; |
||
858 | break; |
||
859 | |||
860 | case cf2_escDIV: /* in spec */ |
||
861 | FT_TRACE4(( " div\n" )); |
||
862 | |||
863 | CF2_FIXME; |
||
864 | break; |
||
865 | |||
866 | case cf2_escNEG: /* in spec */ |
||
867 | FT_TRACE4(( " neg\n" )); |
||
868 | |||
869 | CF2_FIXME; |
||
870 | break; |
||
871 | |||
872 | case cf2_escEQ: /* in spec */ |
||
873 | FT_TRACE4(( " eq\n" )); |
||
874 | |||
875 | CF2_FIXME; |
||
876 | break; |
||
877 | |||
878 | case cf2_escDROP: /* in spec */ |
||
879 | FT_TRACE4(( " drop\n" )); |
||
880 | |||
881 | CF2_FIXME; |
||
882 | break; |
||
883 | |||
884 | case cf2_escPUT: /* in spec */ |
||
885 | FT_TRACE4(( " put\n" )); |
||
886 | |||
887 | CF2_FIXME; |
||
888 | break; |
||
889 | |||
890 | case cf2_escGET: /* in spec */ |
||
891 | FT_TRACE4(( " get\n" )); |
||
892 | |||
893 | CF2_FIXME; |
||
894 | break; |
||
895 | |||
896 | case cf2_escIFELSE: /* in spec */ |
||
897 | FT_TRACE4(( " ifelse\n" )); |
||
898 | |||
899 | CF2_FIXME; |
||
900 | break; |
||
901 | |||
902 | case cf2_escRANDOM: /* in spec */ |
||
903 | FT_TRACE4(( " random\n" )); |
||
904 | |||
905 | CF2_FIXME; |
||
906 | break; |
||
907 | |||
908 | case cf2_escMUL: /* in spec */ |
||
909 | FT_TRACE4(( " mul\n" )); |
||
910 | |||
911 | CF2_FIXME; |
||
912 | break; |
||
913 | |||
914 | case cf2_escSQRT: /* in spec */ |
||
915 | FT_TRACE4(( " sqrt\n" )); |
||
916 | |||
917 | CF2_FIXME; |
||
918 | break; |
||
919 | |||
920 | case cf2_escDUP: /* in spec */ |
||
921 | FT_TRACE4(( " dup\n" )); |
||
922 | |||
923 | CF2_FIXME; |
||
924 | break; |
||
925 | |||
926 | case cf2_escEXCH: /* in spec */ |
||
927 | FT_TRACE4(( " exch\n" )); |
||
928 | |||
929 | CF2_FIXME; |
||
930 | break; |
||
931 | |||
932 | case cf2_escINDEX: /* in spec */ |
||
933 | FT_TRACE4(( " index\n" )); |
||
934 | |||
935 | CF2_FIXME; |
||
936 | break; |
||
937 | |||
938 | case cf2_escROLL: /* in spec */ |
||
939 | FT_TRACE4(( " roll\n" )); |
||
940 | |||
941 | CF2_FIXME; |
||
942 | break; |
||
943 | |||
944 | case cf2_escHFLEX: |
||
945 | { |
||
946 | static const FT_Bool readFromStack[12] = |
||
947 | { |
||
948 | TRUE /* dx1 */, FALSE /* dy1 */, |
||
949 | TRUE /* dx2 */, TRUE /* dy2 */, |
||
950 | TRUE /* dx3 */, FALSE /* dy3 */, |
||
951 | TRUE /* dx4 */, FALSE /* dy4 */, |
||
952 | TRUE /* dx5 */, FALSE /* dy5 */, |
||
953 | TRUE /* dx6 */, FALSE /* dy6 */ |
||
954 | }; |
||
955 | |||
956 | |||
957 | FT_TRACE4(( " hflex\n" )); |
||
958 | |||
959 | cf2_doFlex( opStack, |
||
960 | &curX, |
||
961 | &curY, |
||
962 | &glyphPath, |
||
963 | readFromStack, |
||
964 | FALSE /* doConditionalLastRead */ ); |
||
965 | } |
||
966 | continue; |
||
967 | |||
968 | case cf2_escFLEX: |
||
969 | { |
||
970 | static const FT_Bool readFromStack[12] = |
||
971 | { |
||
972 | TRUE /* dx1 */, TRUE /* dy1 */, |
||
973 | TRUE /* dx2 */, TRUE /* dy2 */, |
||
974 | TRUE /* dx3 */, TRUE /* dy3 */, |
||
975 | TRUE /* dx4 */, TRUE /* dy4 */, |
||
976 | TRUE /* dx5 */, TRUE /* dy5 */, |
||
977 | TRUE /* dx6 */, TRUE /* dy6 */ |
||
978 | }; |
||
979 | |||
980 | |||
981 | FT_TRACE4(( " flex\n" )); |
||
982 | |||
983 | cf2_doFlex( opStack, |
||
984 | &curX, |
||
985 | &curY, |
||
986 | &glyphPath, |
||
987 | readFromStack, |
||
988 | FALSE /* doConditionalLastRead */ ); |
||
989 | } |
||
990 | break; /* TODO: why is this not a continue? */ |
||
991 | |||
992 | case cf2_escHFLEX1: |
||
993 | { |
||
994 | static const FT_Bool readFromStack[12] = |
||
995 | { |
||
996 | TRUE /* dx1 */, TRUE /* dy1 */, |
||
997 | TRUE /* dx2 */, TRUE /* dy2 */, |
||
998 | TRUE /* dx3 */, FALSE /* dy3 */, |
||
999 | TRUE /* dx4 */, FALSE /* dy4 */, |
||
1000 | TRUE /* dx5 */, TRUE /* dy5 */, |
||
1001 | TRUE /* dx6 */, FALSE /* dy6 */ |
||
1002 | }; |
||
1003 | |||
1004 | |||
1005 | FT_TRACE4(( " hflex1\n" )); |
||
1006 | |||
1007 | cf2_doFlex( opStack, |
||
1008 | &curX, |
||
1009 | &curY, |
||
1010 | &glyphPath, |
||
1011 | readFromStack, |
||
1012 | FALSE /* doConditionalLastRead */ ); |
||
1013 | } |
||
1014 | continue; |
||
1015 | |||
1016 | case cf2_escFLEX1: |
||
1017 | { |
||
1018 | static const FT_Bool readFromStack[12] = |
||
1019 | { |
||
1020 | TRUE /* dx1 */, TRUE /* dy1 */, |
||
1021 | TRUE /* dx2 */, TRUE /* dy2 */, |
||
1022 | TRUE /* dx3 */, TRUE /* dy3 */, |
||
1023 | TRUE /* dx4 */, TRUE /* dy4 */, |
||
1024 | TRUE /* dx5 */, TRUE /* dy5 */, |
||
1025 | FALSE /* dx6 */, FALSE /* dy6 */ |
||
1026 | }; |
||
1027 | |||
1028 | |||
1029 | FT_TRACE4(( " flex1\n" )); |
||
1030 | |||
1031 | cf2_doFlex( opStack, |
||
1032 | &curX, |
||
1033 | &curY, |
||
1034 | &glyphPath, |
||
1035 | readFromStack, |
||
1036 | TRUE /* doConditionalLastRead */ ); |
||
1037 | } |
||
1038 | continue; |
||
1039 | |||
1040 | case cf2_escRESERVED_1: |
||
1041 | case cf2_escRESERVED_2: |
||
1042 | case cf2_escRESERVED_6: |
||
1043 | case cf2_escRESERVED_7: |
||
1044 | case cf2_escRESERVED_8: |
||
1045 | case cf2_escRESERVED_13: |
||
1046 | case cf2_escRESERVED_16: |
||
1047 | case cf2_escRESERVED_17: |
||
1048 | case cf2_escRESERVED_19: |
||
1049 | case cf2_escRESERVED_25: |
||
1050 | case cf2_escRESERVED_31: |
||
1051 | case cf2_escRESERVED_32: |
||
1052 | case cf2_escRESERVED_33: |
||
1053 | default: |
||
1054 | FT_TRACE4(( " unknown op (12, %d)\n", op2 )); |
||
1055 | |||
1056 | }; /* end of switch statement checking `op2' */ |
||
1057 | |||
1058 | } /* case cf2_cmdESC */ |
||
1059 | break; |
||
1060 | |||
1061 | case cf2_cmdENDCHAR: |
||
1062 | FT_TRACE4(( " endchar\n" )); |
||
1063 | |||
1064 | if ( cf2_stack_count( opStack ) == 1 || |
||
1065 | cf2_stack_count( opStack ) == 5 ) |
||
1066 | { |
||
1067 | if ( !haveWidth ) |
||
1068 | *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX; |
||
1069 | } |
||
1070 | |||
1071 | /* width is defined or default after this */ |
||
1072 | haveWidth = TRUE; |
||
1073 | |||
1074 | if ( font->decoder->width_only ) |
||
1075 | goto exit; |
||
1076 | |||
1077 | /* close path if still open */ |
||
1078 | cf2_glyphpath_closeOpenPath( &glyphPath ); |
||
1079 | |||
1080 | if ( cf2_stack_count( opStack ) > 1 ) |
||
1081 | { |
||
1082 | /* must be either 4 or 5 -- */ |
||
1083 | /* this is a (deprecated) implied `seac' operator */ |
||
1084 | |||
1085 | CF2_UInt achar; |
||
1086 | CF2_UInt bchar; |
||
1087 | CF2_BufferRec component; |
||
1088 | CF2_Fixed dummyWidth; /* ignore component width */ |
||
1089 | FT_Error error2; |
||
1090 | |||
1091 | |||
1092 | if ( doingSeac ) |
||
1093 | { |
||
1094 | lastError = FT_THROW( Invalid_Glyph_Format ); |
||
1095 | goto exit; /* nested seac */ |
||
1096 | } |
||
1097 | |||
1098 | achar = cf2_stack_popInt( opStack ); |
||
1099 | bchar = cf2_stack_popInt( opStack ); |
||
1100 | |||
1101 | curY = cf2_stack_popFixed( opStack ); |
||
1102 | curX = cf2_stack_popFixed( opStack ); |
||
1103 | |||
1104 | error2 = cf2_getSeacComponent( decoder, achar, &component ); |
||
1105 | if ( error2 ) |
||
1106 | { |
||
1107 | lastError = error2; /* pass FreeType error through */ |
||
1108 | goto exit; |
||
1109 | } |
||
1110 | cf2_interpT2CharString( font, |
||
1111 | &component, |
||
1112 | callbacks, |
||
1113 | translation, |
||
1114 | TRUE, |
||
1115 | curX, |
||
1116 | curY, |
||
1117 | &dummyWidth ); |
||
1118 | cf2_freeSeacComponent( decoder, &component ); |
||
1119 | |||
1120 | error2 = cf2_getSeacComponent( decoder, bchar, &component ); |
||
1121 | if ( error2 ) |
||
1122 | { |
||
1123 | lastError = error2; /* pass FreeType error through */ |
||
1124 | goto exit; |
||
1125 | } |
||
1126 | cf2_interpT2CharString( font, |
||
1127 | &component, |
||
1128 | callbacks, |
||
1129 | translation, |
||
1130 | TRUE, |
||
1131 | 0, |
||
1132 | 0, |
||
1133 | &dummyWidth ); |
||
1134 | cf2_freeSeacComponent( decoder, &component ); |
||
1135 | } |
||
1136 | goto exit; |
||
1137 | |||
1138 | case cf2_cmdCNTRMASK: |
||
1139 | case cf2_cmdHINTMASK: |
||
1140 | /* the final \n in the tracing message gets added in */ |
||
1141 | /* `cf2_hintmask_read' (which also traces the mask bytes) */ |
||
1142 | FT_TRACE4(( op1 == cf2_cmdCNTRMASK ? " cntrmask" : " hintmask" )); |
||
1143 | |||
1144 | /* if there are arguments on the stack, there this is an */ |
||
1145 | /* implied cf2_cmdVSTEMHM */ |
||
1146 | if ( cf2_stack_count( opStack ) != 0 ) |
||
1147 | { |
||
1148 | /* never add hints after the mask is computed */ |
||
1149 | if ( cf2_hintmask_isValid( &hintMask ) ) |
||
1150 | FT_TRACE4(( "cf2_interpT2CharString: invalid hint mask\n" )); |
||
1151 | } |
||
1152 | |||
1153 | cf2_doStems( font, |
||
1154 | opStack, |
||
1155 | &vStemHintArray, |
||
1156 | width, |
||
1157 | &haveWidth, |
||
1158 | |||
1159 | |||
1160 | if ( font->decoder->width_only ) |
||
1161 | goto exit; |
||
1162 | |||
1163 | if ( op1 == cf2_cmdHINTMASK ) |
||
1164 | { |
||
1165 | /* consume the hint mask bytes which follow the operator */ |
||
1166 | cf2_hintmask_read( &hintMask, |
||
1167 | charstring, |
||
1168 | cf2_arrstack_size( &hStemHintArray ) + |
||
1169 | cf2_arrstack_size( &vStemHintArray ) ); |
||
1170 | } |
||
1171 | else |
||
1172 | { |
||
1173 | /* |
||
1174 | * Consume the counter mask bytes which follow the operator: |
||
1175 | * Build a temporary hint map, just to place and lock those |
||
1176 | * stems participating in the counter mask. These are most |
||
1177 | * likely the dominant hstems, and are grouped together in a |
||
1178 | * few counter groups, not necessarily in correspondence |
||
1179 | * with the hint groups. This reduces the chances of |
||
1180 | * conflicts between hstems that are initially placed in |
||
1181 | * separate hint groups and then brought together. The |
||
1182 | * positions are copied back to `hStemHintArray', so we can |
||
1183 | * discard `counterMask' and `counterHintMap'. |
||
1184 | * |
||
1185 | */ |
||
1186 | CF2_HintMapRec counterHintMap; |
||
1187 | CF2_HintMaskRec counterMask; |
||
1188 | |||
1189 | |||
1190 | cf2_hintmap_init( &counterHintMap, |
||
1191 | font, |
||
1192 | &glyphPath.initialHintMap, |
||
1193 | &glyphPath.hintMoves, |
||
1194 | scaleY ); |
||
1195 | cf2_hintmask_init( &counterMask, error ); |
||
1196 | |||
1197 | cf2_hintmask_read( &counterMask, |
||
1198 | charstring, |
||
1199 | cf2_arrstack_size( &hStemHintArray ) + |
||
1200 | cf2_arrstack_size( &vStemHintArray ) ); |
||
1201 | cf2_hintmap_build( &counterHintMap, |
||
1202 | &hStemHintArray, |
||
1203 | &vStemHintArray, |
||
1204 | &counterMask, |
||
1205 | 0, |
||
1206 | FALSE ); |
||
1207 | } |
||
1208 | break; |
||
1209 | |||
1210 | case cf2_cmdRMOVETO: |
||
1211 | FT_TRACE4(( " rmoveto\n" )); |
||
1212 | |||
1213 | if ( cf2_stack_count( opStack ) > 2 && !haveWidth ) |
||
1214 | *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX; |
||
1215 | |||
1216 | /* width is defined or default after this */ |
||
1217 | haveWidth = TRUE; |
||
1218 | |||
1219 | if ( font->decoder->width_only ) |
||
1220 | goto exit; |
||
1221 | |||
1222 | curY += cf2_stack_popFixed( opStack ); |
||
1223 | curX += cf2_stack_popFixed( opStack ); |
||
1224 | |||
1225 | cf2_glyphpath_moveTo( &glyphPath, curX, curY ); |
||
1226 | |||
1227 | break; |
||
1228 | |||
1229 | case cf2_cmdHMOVETO: |
||
1230 | FT_TRACE4(( " hmoveto\n" )); |
||
1231 | |||
1232 | if ( cf2_stack_count( opStack ) > 1 && !haveWidth ) |
||
1233 | *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX; |
||
1234 | |||
1235 | /* width is defined or default after this */ |
||
1236 | haveWidth = TRUE; |
||
1237 | |||
1238 | if ( font->decoder->width_only ) |
||
1239 | goto exit; |
||
1240 | |||
1241 | curX += cf2_stack_popFixed( opStack ); |
||
1242 | |||
1243 | cf2_glyphpath_moveTo( &glyphPath, curX, curY ); |
||
1244 | |||
1245 | break; |
||
1246 | |||
1247 | case cf2_cmdRLINECURVE: |
||
1248 | { |
||
1249 | CF2_UInt count = cf2_stack_count( opStack ); |
||
1250 | CF2_UInt index = 0; |
||
1251 | |||
1252 | |||
1253 | FT_TRACE4(( " rlinecurve\n" )); |
||
1254 | |||
1255 | while ( index + 6 < count ) |
||
1256 | { |
||
1257 | curX += cf2_stack_getReal( opStack, index + 0 ); |
||
1258 | curY += cf2_stack_getReal( opStack, index + 1 ); |
||
1259 | |||
1260 | cf2_glyphpath_lineTo( &glyphPath, curX, curY ); |
||
1261 | index += 2; |
||
1262 | } |
||
1263 | |||
1264 | while ( index < count ) |
||
1265 | { |
||
1266 | CF2_Fixed x1 = cf2_stack_getReal( opStack, index + 0 ) + curX; |
||
1267 | CF2_Fixed y1 = cf2_stack_getReal( opStack, index + 1 ) + curY; |
||
1268 | CF2_Fixed x2 = cf2_stack_getReal( opStack, index + 2 ) + x1; |
||
1269 | CF2_Fixed y2 = cf2_stack_getReal( opStack, index + 3 ) + y1; |
||
1270 | CF2_Fixed x3 = cf2_stack_getReal( opStack, index + 4 ) + x2; |
||
1271 | CF2_Fixed y3 = cf2_stack_getReal( opStack, index + 5 ) + y2; |
||
1272 | |||
1273 | |||
1274 | cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 ); |
||
1275 | |||
1276 | curX = x3; |
||
1277 | curY = y3; |
||
1278 | index += 6; |
||
1279 | } |
||
1280 | |||
1281 | cf2_stack_clear( opStack ); |
||
1282 | } |
||
1283 | continue; /* no need to clear stack again */ |
||
1284 | |||
1285 | case cf2_cmdVVCURVETO: |
||
1286 | { |
||
1287 | CF2_UInt count = cf2_stack_count( opStack ); |
||
1288 | CF2_UInt index = 0; |
||
1289 | |||
1290 | |||
1291 | FT_TRACE4(( " vvcurveto\n" )); |
||
1292 | |||
1293 | while ( index < count ) |
||
1294 | { |
||
1295 | CF2_Fixed x1, y1, x2, y2, x3, y3; |
||
1296 | |||
1297 | |||
1298 | if ( ( count - index ) & 1 ) |
||
1299 | { |
||
1300 | x1 = cf2_stack_getReal( opStack, index ) + curX; |
||
1301 | |||
1302 | ++index; |
||
1303 | } |
||
1304 | else |
||
1305 | x1 = curX; |
||
1306 | |||
1307 | y1 = cf2_stack_getReal( opStack, index + 0 ) + curY; |
||
1308 | x2 = cf2_stack_getReal( opStack, index + 1 ) + x1; |
||
1309 | y2 = cf2_stack_getReal( opStack, index + 2 ) + y1; |
||
1310 | x3 = x2; |
||
1311 | y3 = cf2_stack_getReal( opStack, index + 3 ) + y2; |
||
1312 | |||
1313 | cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 ); |
||
1314 | |||
1315 | curX = x3; |
||
1316 | curY = y3; |
||
1317 | index += 4; |
||
1318 | } |
||
1319 | |||
1320 | cf2_stack_clear( opStack ); |
||
1321 | } |
||
1322 | continue; /* no need to clear stack again */ |
||
1323 | |||
1324 | case cf2_cmdHHCURVETO: |
||
1325 | { |
||
1326 | CF2_UInt count = cf2_stack_count( opStack ); |
||
1327 | CF2_UInt index = 0; |
||
1328 | |||
1329 | |||
1330 | FT_TRACE4(( " hhcurveto\n" )); |
||
1331 | |||
1332 | while ( index < count ) |
||
1333 | { |
||
1334 | CF2_Fixed x1, y1, x2, y2, x3, y3; |
||
1335 | |||
1336 | |||
1337 | if ( ( count - index ) & 1 ) |
||
1338 | { |
||
1339 | y1 = cf2_stack_getReal( opStack, index ) + curY; |
||
1340 | |||
1341 | ++index; |
||
1342 | } |
||
1343 | else |
||
1344 | y1 = curY; |
||
1345 | |||
1346 | x1 = cf2_stack_getReal( opStack, index + 0 ) + curX; |
||
1347 | x2 = cf2_stack_getReal( opStack, index + 1 ) + x1; |
||
1348 | y2 = cf2_stack_getReal( opStack, index + 2 ) + y1; |
||
1349 | x3 = cf2_stack_getReal( opStack, index + 3 ) + x2; |
||
1350 | y3 = y2; |
||
1351 | |||
1352 | cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 ); |
||
1353 | |||
1354 | curX = x3; |
||
1355 | curY = y3; |
||
1356 | index += 4; |
||
1357 | } |
||
1358 | |||
1359 | cf2_stack_clear( opStack ); |
||
1360 | } |
||
1361 | continue; /* no need to clear stack again */ |
||
1362 | |||
1363 | case cf2_cmdVHCURVETO: |
||
1364 | case cf2_cmdHVCURVETO: |
||
1365 | { |
||
1366 | CF2_UInt count = cf2_stack_count( opStack ); |
||
1367 | CF2_UInt index = 0; |
||
1368 | |||
1369 | FT_Bool alternate = op1 == cf2_cmdHVCURVETO; |
||
1370 | |||
1371 | |||
1372 | FT_TRACE4(( alternate ? " hvcurveto\n" : " vhcurveto\n" )); |
||
1373 | |||
1374 | while ( index < count ) |
||
1375 | { |
||
1376 | CF2_Fixed x1, x2, x3, y1, y2, y3; |
||
1377 | |||
1378 | |||
1379 | if ( alternate ) |
||
1380 | { |
||
1381 | x1 = cf2_stack_getReal( opStack, index + 0 ) + curX; |
||
1382 | y1 = curY; |
||
1383 | x2 = cf2_stack_getReal( opStack, index + 1 ) + x1; |
||
1384 | y2 = cf2_stack_getReal( opStack, index + 2 ) + y1; |
||
1385 | y3 = cf2_stack_getReal( opStack, index + 3 ) + y2; |
||
1386 | |||
1387 | if ( count - index == 5 ) |
||
1388 | { |
||
1389 | x3 = cf2_stack_getReal( opStack, index + 4 ) + x2; |
||
1390 | |||
1391 | ++index; |
||
1392 | } |
||
1393 | else |
||
1394 | x3 = x2; |
||
1395 | |||
1396 | alternate = FALSE; |
||
1397 | } |
||
1398 | else |
||
1399 | { |
||
1400 | x1 = curX; |
||
1401 | y1 = cf2_stack_getReal( opStack, index + 0 ) + curY; |
||
1402 | x2 = cf2_stack_getReal( opStack, index + 1 ) + x1; |
||
1403 | y2 = cf2_stack_getReal( opStack, index + 2 ) + y1; |
||
1404 | x3 = cf2_stack_getReal( opStack, index + 3 ) + x2; |
||
1405 | |||
1406 | if ( count - index == 5 ) |
||
1407 | { |
||
1408 | y3 = cf2_stack_getReal( opStack, index + 4 ) + y2; |
||
1409 | |||
1410 | ++index; |
||
1411 | } |
||
1412 | else |
||
1413 | y3 = y2; |
||
1414 | |||
1415 | alternate = TRUE; |
||
1416 | } |
||
1417 | |||
1418 | cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 ); |
||
1419 | |||
1420 | curX = x3; |
||
1421 | curY = y3; |
||
1422 | index += 4; |
||
1423 | } |
||
1424 | |||
1425 | cf2_stack_clear( opStack ); |
||
1426 | } |
||
1427 | continue; /* no need to clear stack again */ |
||
1428 | |||
1429 | case cf2_cmdEXTENDEDNMBR: |
||
1430 | { |
||
1431 | CF2_Int v; |
||
1432 | |||
1433 | |||
1434 | v = (FT_Short)( ( cf2_buf_readByte( charstring ) << 8 ) | |
||
1435 | cf2_buf_readByte( charstring ) ); |
||
1436 | |||
1437 | FT_TRACE4(( " %d", v )); |
||
1438 | |||
1439 | cf2_stack_pushInt( opStack, v ); |
||
1440 | } |
||
1441 | continue; |
||
1442 | |||
1443 | default: |
||
1444 | /* numbers */ |
||
1445 | { |
||
1446 | if ( /* op1 >= 32 && */ op1 <= 246 ) |
||
1447 | { |
||
1448 | CF2_Int v; |
||
1449 | |||
1450 | |||
1451 | v = op1 - 139; |
||
1452 | |||
1453 | FT_TRACE4(( " %d", v )); |
||
1454 | |||
1455 | /* -107 .. 107 */ |
||
1456 | cf2_stack_pushInt( opStack, v ); |
||
1457 | } |
||
1458 | |||
1459 | else if ( /* op1 >= 247 && */ op1 <= 250 ) |
||
1460 | { |
||
1461 | CF2_Int v; |
||
1462 | |||
1463 | |||
1464 | v = op1; |
||
1465 | v -= 247; |
||
1466 | v *= 256; |
||
1467 | v += cf2_buf_readByte( charstring ); |
||
1468 | v += 108; |
||
1469 | |||
1470 | FT_TRACE4(( " %d", v )); |
||
1471 | |||
1472 | /* 108 .. 1131 */ |
||
1473 | cf2_stack_pushInt( opStack, v ); |
||
1474 | } |
||
1475 | |||
1476 | else if ( /* op1 >= 251 && */ op1 <= 254 ) |
||
1477 | { |
||
1478 | CF2_Int v; |
||
1479 | |||
1480 | |||
1481 | v = op1; |
||
1482 | v -= 251; |
||
1483 | v *= 256; |
||
1484 | v += cf2_buf_readByte( charstring ); |
||
1485 | v = -v - 108; |
||
1486 | |||
1487 | FT_TRACE4(( " %d", v )); |
||
1488 | |||
1489 | /* -1131 .. -108 */ |
||
1490 | cf2_stack_pushInt( opStack, v ); |
||
1491 | } |
||
1492 | |||
1493 | else /* op1 == 255 */ |
||
1494 | { |
||
1495 | CF2_Fixed v; |
||
1496 | |||
1497 | |||
1498 | v = (CF2_Fixed) |
||
1499 | ( ( (FT_UInt32)cf2_buf_readByte( charstring ) << 24 ) | |
||
1500 | ( (FT_UInt32)cf2_buf_readByte( charstring ) << 16 ) | |
||
1501 | ( (FT_UInt32)cf2_buf_readByte( charstring ) << 8 ) | |
||
1502 | (FT_UInt32)cf2_buf_readByte( charstring ) ); |
||
1503 | |||
1504 | FT_TRACE4(( " %.2f", v / 65536.0 )); |
||
1505 | |||
1506 | cf2_stack_pushFixed( opStack, v ); |
||
1507 | } |
||
1508 | } |
||
1509 | continue; /* don't clear stack */ |
||
1510 | |||
1511 | } /* end of switch statement checking `op1' */ |
||
1512 | |||
1513 | cf2_stack_clear( opStack ); |
||
1514 | |||
1515 | } /* end of main interpreter loop */ |
||
1516 | |||
1517 | /* we get here if the charstring ends without cf2_cmdENDCHAR */ |
||
1518 | FT_TRACE4(( "cf2_interpT2CharString:" |
||
1519 | " charstring ends without ENDCHAR\n" )); |
||
1520 | |||
1521 | exit: |
||
1522 | /* check whether last error seen is also the first one */ |
||
1523 | cf2_setError( error, lastError ); |
||
1524 | |||
1525 | /* free resources from objects we've used */ |
||
1526 | cf2_glyphpath_finalize( &glyphPath ); |
||
1527 | cf2_arrstack_finalize( &vStemHintArray ); |
||
1528 | cf2_arrstack_finalize( &hStemHintArray ); |
||
1529 | cf2_arrstack_finalize( &subrStack ); |
||
1530 | cf2_stack_free( opStack ); |
||
1531 | |||
1532 | FT_TRACE4(( "\n" )); |
||
1533 | |||
1534 | return; |
||
1535 | } |
||
1536 | |||
1537 | |||
1538 | /* END */><>><>><>=>=>=>><>>>>>>>=>>>>>>> |