]>
Commit | Line | Data |
---|---|---|
1 | /***************************************************************************/ | |
2 | /* */ | |
3 | /* t1parse.c */ | |
4 | /* */ | |
5 | /* Type 1 parser (body). */ | |
6 | /* */ | |
7 | /* Copyright 1996-2000 by */ | |
8 | /* David Turner, Robert Wilhelm, and Werner Lemberg. */ | |
9 | /* */ | |
10 | /* This file is part of the FreeType project, and may only be used, */ | |
11 | /* modified, and distributed under the terms of the FreeType project */ | |
12 | /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ | |
13 | /* this file you indicate that you have read the license and */ | |
14 | /* understand and accept it fully. */ | |
15 | /* */ | |
16 | /***************************************************************************/ | |
17 | ||
18 | ||
19 | /*************************************************************************/ | |
20 | /* */ | |
21 | /* The tokenizer is in charge of loading and reading a Type1 font file */ | |
22 | /* (either in PFB or PFA format), and extracting successive tokens and */ | |
23 | /* keywords from its two streams (i.e. the font program, and the private */ | |
24 | /* dictionary). */ | |
25 | /* */ | |
26 | /* Eexec decryption is performed automatically when entering the private */ | |
27 | /* dictionary, or when retrieving char strings. */ | |
28 | /* */ | |
29 | /*************************************************************************/ | |
30 | ||
31 | ||
32 | #include <freetype/internal/ftstream.h> | |
33 | #include <freetype/internal/ftdebug.h> | |
34 | ||
35 | ||
36 | #ifdef FT_FLAT_COMPILE | |
37 | ||
38 | #include "t1tokens.h" | |
39 | #include "t1load.h" | |
40 | ||
41 | #else | |
42 | ||
43 | #include <type1/t1tokens.h> | |
44 | #include <type1/t1load.h> | |
45 | ||
46 | #endif | |
47 | ||
48 | ||
49 | #include <string.h> /* for strncmp() */ | |
50 | ||
51 | ||
52 | #undef READ_BUFFER_INCREMENT | |
53 | #define READ_BUFFER_INCREMENT 0x400 | |
54 | ||
55 | ||
56 | /*************************************************************************/ | |
57 | /* */ | |
58 | /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ | |
59 | /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ | |
60 | /* messages during execution. */ | |
61 | /* */ | |
62 | #undef FT_COMPONENT | |
63 | #define FT_COMPONENT trace_t1load | |
64 | ||
65 | ||
66 | /* An array of Type1 keywords supported by this engine. This table */ | |
67 | /* places the keyword in lexicographical order. It should always */ | |
68 | /* correspond to the enums `key_xxx'! */ | |
69 | /* */ | |
70 | const char* t1_keywords[key_max - key_first_] = | |
71 | { | |
72 | "-|", "ExpertEncoding", "ND", "NP", "RD", "StandardEncoding", "array", | |
73 | "begin", "closefile", "currentdict", "currentfile", "def", "dict", "dup", | |
74 | "eexec", "end", "executeonly", "false", "for", "index", "noaccess", | |
75 | "put", "readonly", "true", "userdict", "|", "|-" | |
76 | }; | |
77 | ||
78 | ||
79 | const char* t1_immediates[imm_max - imm_first_] = | |
80 | { | |
81 | "-|", ".notdef", "BlendAxisTypes", "BlueFuzz", "BlueScale", "BlueShift", | |
82 | "BlueValues", "CharStrings", "Encoding", "FamilyBlues", "FamilyName", | |
83 | "FamilyOtherBlues", "FID", "FontBBox", "FontID", "FontInfo", "FontMatrix", | |
84 | "FontName", "FontType", "ForceBold", "FullName", "ItalicAngle", | |
85 | "LanguageGroup", "Metrics", "MinFeature", "ND", "NP", "Notice", | |
86 | "OtherBlues", "OtherSubrs", "PaintType", "Private", "RD", "RndStemUp", | |
87 | "StdHW", "StdVW", "StemSnapH", "StemSnapV", "StrokeWidth", "Subrs", | |
88 | "UnderlinePosition", "UnderlineThickness", "UniqueID", "Weight", | |
89 | "isFixedPitch", "lenIV", "password", "version", "|", "|-" | |
90 | }; | |
91 | ||
92 | ||
93 | /* lexicographic comparison of two strings */ | |
94 | static | |
95 | int lexico_strcmp( const char* str1, | |
96 | int str1_len, | |
97 | const char* str2 ) | |
98 | { | |
99 | int c2 = 0; | |
100 | ||
101 | ||
102 | for ( ; str1_len > 0; str1_len-- ) | |
103 | { | |
104 | int c1, diff; | |
105 | ||
106 | ||
107 | c1 = *str1++; | |
108 | c2 = *str2++; | |
109 | ||
110 | diff = c1 - c2; | |
111 | if ( diff ) | |
112 | return diff; | |
113 | }; | |
114 | ||
115 | return -*str2; | |
116 | } | |
117 | ||
118 | ||
119 | /* find a given token/name, performing binary search */ | |
120 | static | |
121 | int Find_Name( char* base, | |
122 | int length, | |
123 | const char** table, | |
124 | int table_len ) | |
125 | { | |
126 | int left, right; | |
127 | ||
128 | ||
129 | left = 0; | |
130 | right = table_len - 1; | |
131 | ||
132 | while ( right - left > 1 ) | |
133 | { | |
134 | int middle = left + ( ( right - left ) >> 1 ); | |
135 | int cmp; | |
136 | ||
137 | ||
138 | cmp = lexico_strcmp( base, length, table[middle] ); | |
139 | if ( !cmp ) | |
140 | return middle; | |
141 | ||
142 | if ( cmp < 0 ) | |
143 | right = middle; | |
144 | else | |
145 | left = middle; | |
146 | } | |
147 | ||
148 | if ( !lexico_strcmp( base, length, table[left ] ) ) | |
149 | return left; | |
150 | if ( !lexico_strcmp( base, length, table[right] ) ) | |
151 | return right; | |
152 | ||
153 | return -1; | |
154 | } | |
155 | ||
156 | ||
157 | /* read the small PFB section header */ | |
158 | static | |
159 | FT_Error Read_PFB_Tag( FT_Stream stream, | |
160 | FT_UShort* atag, | |
161 | FT_ULong* asize ) | |
162 | { | |
163 | FT_UShort tag; | |
164 | FT_ULong size; | |
165 | FT_Error error; | |
166 | ||
167 | ||
168 | FT_TRACE2(( "Read_PFB_Tag: reading\n" )); | |
169 | ||
170 | if ( ACCESS_Frame( 6L ) ) | |
171 | return error; | |
172 | ||
173 | tag = GET_UShort(); | |
174 | size = GET_ULong(); | |
175 | ||
176 | FORGET_Frame(); | |
177 | ||
178 | *atag = tag; | |
179 | *asize = ( ( size & 0xFF ) << 24 ) | | |
180 | ( ( ( size >> 8 ) & 0xFF ) << 16 ) | | |
181 | ( ( ( size >> 16 ) & 0xFF ) << 8 ) | | |
182 | ( ( ( size >> 24 ) & 0xFF ) ); | |
183 | ||
184 | FT_TRACE2(( " tag = %04x\n", tag )); | |
185 | FT_TRACE4(( " asze = %08x\n", size )); | |
186 | FT_TRACE2(( " size = %08x\n", *asize )); | |
187 | ||
188 | return T1_Err_Ok; | |
189 | } | |
190 | ||
191 | ||
192 | static | |
193 | FT_Error grow( T1_Tokenizer tokzer ) | |
194 | { | |
195 | FT_Error error; | |
196 | FT_Long left_bytes; | |
197 | FT_Memory memory = tokzer->memory; | |
198 | ||
199 | ||
200 | left_bytes = tokzer->max - tokzer->limit; | |
201 | ||
202 | if ( left_bytes > 0 ) | |
203 | { | |
204 | FT_Stream stream = tokzer->stream; | |
205 | ||
206 | ||
207 | if ( left_bytes > READ_BUFFER_INCREMENT ) | |
208 | left_bytes = READ_BUFFER_INCREMENT; | |
209 | ||
210 | FT_TRACE2(( "Growing tokenizer buffer by %d bytes\n", left_bytes )); | |
211 | ||
212 | if ( !REALLOC( tokzer->base, tokzer->limit, | |
213 | tokzer->limit + left_bytes ) && | |
214 | !FILE_Read( tokzer->base + tokzer->limit, left_bytes ) ) | |
215 | tokzer->limit += left_bytes; | |
216 | } | |
217 | else | |
218 | { | |
219 | FT_ERROR(( "Unexpected end of Type1 fragment!\n" )); | |
220 | error = T1_Err_Invalid_File_Format; | |
221 | } | |
222 | ||
223 | tokzer->error = error; | |
224 | return error; | |
225 | } | |
226 | ||
227 | ||
228 | /*************************************************************************/ | |
229 | /* */ | |
230 | /* <Function> */ | |
231 | /* t1_decrypt */ | |
232 | /* */ | |
233 | /* <Description> */ | |
234 | /* Performs the Type 1 charstring decryption process. */ | |
235 | /* */ | |
236 | /* <Input> */ | |
237 | /* buffer :: The base address of the data to decrypt. */ | |
238 | /* length :: The number of bytes to decrypt (beginning from the base */ | |
239 | /* address. */ | |
240 | /* seed :: The encryption seed (4330 for charstrings). */ | |
241 | /* */ | |
242 | LOCAL_FUNC | |
243 | void t1_decrypt( FT_Byte* buffer, | |
244 | FT_Int length, | |
245 | FT_UShort seed ) | |
246 | { | |
247 | while ( length > 0 ) | |
248 | { | |
249 | FT_Byte plain; | |
250 | ||
251 | ||
252 | plain = ( *buffer ^ ( seed >> 8 ) ); | |
253 | seed = ( *buffer + seed ) * 52845 + 22719; | |
254 | *buffer++ = plain; | |
255 | length--; | |
256 | } | |
257 | } | |
258 | ||
259 | ||
260 | /*************************************************************************/ | |
261 | /* */ | |
262 | /* <Function> */ | |
263 | /* New_Tokenizer */ | |
264 | /* */ | |
265 | /* <Description> */ | |
266 | /* Creates a new tokenizer from a given input stream. This function */ | |
267 | /* automatically recognizes `pfa' or `pfb' files. The function */ | |
268 | /* Read_Token() can then be used to extract successive tokens from */ | |
269 | /* the stream. */ | |
270 | /* */ | |
271 | /* <Input> */ | |
272 | /* stream :: The input stream. */ | |
273 | /* */ | |
274 | /* <Output> */ | |
275 | /* tokenizer :: A handle to a new tokenizer object. */ | |
276 | /* */ | |
277 | /* <Return> */ | |
278 | /* FreeType error code. 0 means success. */ | |
279 | /* */ | |
280 | /* <Note> */ | |
281 | /* This function copies the stream handle within the object. Callers */ | |
282 | /* should not discard `stream'. This is done by the Done_Tokenizer() */ | |
283 | /* function. */ | |
284 | /* */ | |
285 | LOCAL_FUNC | |
286 | FT_Error New_Tokenizer( FT_Stream stream, | |
287 | T1_Tokenizer* tokenizer ) | |
288 | { | |
289 | FT_Memory memory = stream->memory; | |
290 | T1_Tokenizer tokzer; | |
291 | FT_Error error; | |
292 | FT_UShort tag; | |
293 | FT_ULong size; | |
294 | ||
295 | FT_Byte* tok_base; | |
296 | FT_ULong tok_limit; | |
297 | FT_ULong tok_max; | |
298 | ||
299 | ||
300 | *tokenizer = 0; | |
301 | ||
302 | /* allocate object */ | |
303 | if ( FILE_Seek( 0L ) || | |
304 | ALLOC( tokzer, sizeof ( *tokzer ) ) ) | |
305 | return error; | |
306 | ||
307 | tokzer->stream = stream; | |
308 | tokzer->memory = stream->memory; | |
309 | ||
310 | tokzer->in_pfb = 0; | |
311 | tokzer->in_private = 0; | |
312 | ||
313 | tok_base = 0; | |
314 | tok_limit = 0; | |
315 | tok_max = stream->size; | |
316 | ||
317 | error = Read_PFB_Tag( stream, &tag, &size ); | |
318 | if ( error ) | |
319 | goto Fail; | |
320 | ||
321 | if ( tag != 0x8001 ) | |
322 | { | |
323 | /* assume that it is a PFA file -- an error will be produced later */ | |
324 | /* if a character with value > 127 is encountered */ | |
325 | ||
326 | /* rewind to start of file */ | |
327 | if ( FILE_Seek( 0L ) ) | |
328 | goto Fail; | |
329 | ||
330 | size = stream->size; | |
331 | } | |
332 | else | |
333 | tokzer->in_pfb = 1; | |
334 | ||
335 | /* if it is a memory-based resource, set up pointer */ | |
336 | if ( !stream->read ) | |
337 | { | |
338 | tok_base = (FT_Byte*)stream->base + stream->pos; | |
339 | tok_limit = size; | |
340 | tok_max = size; | |
341 | ||
342 | /* check that the `size' field is valid */ | |
343 | if ( FILE_Skip( size ) ) | |
344 | goto Fail; | |
345 | } | |
346 | else if ( tag == 0x8001 ) | |
347 | { | |
348 | /* read segment in memory */ | |
349 | if ( ALLOC( tok_base, size ) ) | |
350 | goto Fail; | |
351 | ||
352 | if ( FILE_Read( tok_base, size ) ) | |
353 | { | |
354 | FREE( tok_base ); | |
355 | goto Fail; | |
356 | } | |
357 | ||
358 | tok_limit = size; | |
359 | tok_max = size; | |
360 | } | |
361 | ||
362 | tokzer->base = tok_base; | |
363 | tokzer->limit = tok_limit; | |
364 | tokzer->max = tok_max; | |
365 | tokzer->cursor = 0; | |
366 | ||
367 | *tokenizer = tokzer; | |
368 | ||
369 | /* now check font format; we must see `%!PS-AdobeFont-1' */ | |
370 | /* or `%!FontType' */ | |
371 | { | |
372 | if ( 16 > tokzer->limit ) | |
373 | grow( tokzer ); | |
374 | ||
375 | if ( tokzer->limit <= 16 || | |
376 | ( strncmp( (const char*)tokzer->base, "%!PS-AdobeFont-1", 16 ) && | |
377 | strncmp( (const char*)tokzer->base, "%!FontType", 10 ) ) ) | |
378 | { | |
379 | FT_TRACE2(( "[not a Type1 font]\n" )); | |
380 | error = FT_Err_Unknown_File_Format; | |
381 | goto Fail; | |
382 | } | |
383 | } | |
384 | return T1_Err_Ok; | |
385 | ||
386 | Fail: | |
387 | FREE( tokzer->base ); | |
388 | FREE( tokzer ); | |
389 | return error; | |
390 | } | |
391 | ||
392 | ||
393 | /* return the value of an hexadecimal digit */ | |
394 | static | |
395 | int hexa_value( char c ) | |
396 | { | |
397 | unsigned int d; | |
398 | ||
399 | ||
400 | d = (unsigned int)( c - '0' ); | |
401 | if ( d <= 9 ) | |
402 | return (int)d; | |
403 | ||
404 | d = (unsigned int)( c - 'a' ); | |
405 | if ( d <= 5 ) | |
406 | return (int)( d + 10 ); | |
407 | ||
408 | d = (unsigned int)( c - 'A' ); | |
409 | if ( d <= 5 ) | |
410 | return (int)( d + 10 ); | |
411 | ||
412 | return -1; | |
413 | } | |
414 | ||
415 | ||
416 | /*************************************************************************/ | |
417 | /* */ | |
418 | /* <Function> */ | |
419 | /* Done_Tokenizer */ | |
420 | /* */ | |
421 | /* <Description> */ | |
422 | /* Closes a given tokenizer. This function will also close the */ | |
423 | /* stream embedded in the object. */ | |
424 | /* */ | |
425 | /* <Input> */ | |
426 | /* tokenizer :: The target tokenizer object. */ | |
427 | /* */ | |
428 | /* <Return> */ | |
429 | /* FreeType error code. 0 means success. */ | |
430 | /* */ | |
431 | LOCAL_FUNC | |
432 | FT_Error Done_Tokenizer( T1_Tokenizer tokenizer ) | |
433 | { | |
434 | FT_Memory memory = tokenizer->memory; | |
435 | ||
436 | ||
437 | /* clear read buffer if needed (disk-based resources) */ | |
438 | if ( tokenizer->in_private || !tokenizer->stream->base ) | |
439 | FREE( tokenizer->base ); | |
440 | ||
441 | FREE( tokenizer ); | |
442 | return T1_Err_Ok; | |
443 | } | |
444 | ||
445 | ||
446 | /*************************************************************************/ | |
447 | /* */ | |
448 | /* <Function> */ | |
449 | /* Open_PrivateDict */ | |
450 | /* */ | |
451 | /* <Description> */ | |
452 | /* This function must be called to set the tokenizer to the private */ | |
453 | /* section of the Type1 file. It recognizes automatically the */ | |
454 | /* the kind of eexec encryption used (ascii or binary). */ | |
455 | /* */ | |
456 | /* <Input> */ | |
457 | /* tokenizer :: The target tokenizer object. */ | |
458 | /* lenIV :: The value of the `lenIV' variable. */ | |
459 | /* */ | |
460 | /* <Return> */ | |
461 | /* FreeType error code. 0 means success. */ | |
462 | /* */ | |
463 | LOCAL_FUNC | |
464 | FT_Error Open_PrivateDict( T1_Tokenizer tokenizer ) | |
465 | { | |
466 | T1_Tokenizer tokzer = tokenizer; | |
467 | FT_Stream stream = tokzer->stream; | |
468 | FT_Memory memory = tokzer->memory; | |
469 | FT_Error error = 0; | |
470 | ||
471 | FT_UShort tag; | |
472 | FT_ULong size; | |
473 | ||
474 | FT_Byte* private_dict; | |
475 | ||
476 | /* are we already in the private dictionary ? */ | |
477 | if ( tokzer->in_private ) | |
478 | return 0; | |
479 | ||
480 | if ( tokzer->in_pfb ) | |
481 | { | |
482 | /* in the case of the PFB format, the private dictionary can be */ | |
483 | /* made of several segments. We thus first read the number of */ | |
484 | /* segments to compute the total size of the private dictionary */ | |
485 | /* then re-read them into memory. */ | |
486 | FT_Long start_pos = FILE_Pos(); | |
487 | FT_ULong private_dict_size = 0; | |
488 | ||
489 | ||
490 | for (;;) | |
491 | { | |
492 | error = Read_PFB_Tag( stream, &tag, &size ); | |
493 | if ( error || tag != 0x8002 ) | |
494 | break; | |
495 | ||
496 | private_dict_size += size; | |
497 | ||
498 | if ( FILE_Skip( size ) ) | |
499 | goto Fail; | |
500 | } | |
501 | ||
502 | /* check that we have a private dictionary there */ | |
503 | /* and allocate private dictionary buffer */ | |
504 | if ( private_dict_size == 0 ) | |
505 | { | |
506 | FT_ERROR(( "Open_PrivateDict:" )); | |
507 | FT_ERROR(( " invalid private dictionary section\n" )); | |
508 | error = T1_Err_Invalid_File_Format; | |
509 | goto Fail; | |
510 | } | |
511 | ||
512 | if ( ALLOC( private_dict, private_dict_size ) ) | |
513 | goto Fail; | |
514 | ||
515 | /* read all sections into buffer */ | |
516 | if ( FILE_Seek( start_pos ) ) | |
517 | goto Fail_Private; | |
518 | ||
519 | private_dict_size = 0; | |
520 | for (;;) | |
521 | { | |
522 | error = Read_PFB_Tag( stream, &tag, &size ); | |
523 | if ( error || tag != 0x8002 ) | |
524 | { | |
525 | error = 0; | |
526 | break; | |
527 | } | |
528 | ||
529 | if ( FILE_Read( private_dict + private_dict_size, size ) ) | |
530 | goto Fail_Private; | |
531 | ||
532 | private_dict_size += size; | |
533 | } | |
534 | ||
535 | /* we must free the field `tokzer.base' if we are in a disk-based */ | |
536 | /* PFB file. */ | |
537 | if ( stream->read ) | |
538 | FREE( tokzer->base ); | |
539 | ||
540 | tokzer->base = private_dict; | |
541 | tokzer->cursor = 0; | |
542 | tokzer->limit = private_dict_size; | |
543 | tokzer->max = private_dict_size; | |
544 | } | |
545 | else | |
546 | { | |
547 | char* base; | |
548 | ||
549 | ||
550 | /* we are in a PFA file; read each token until we find `eexec' */ | |
551 | while ( tokzer->token.kind2 != key_eexec ) | |
552 | { | |
553 | error = Read_Token( tokzer ); | |
554 | if ( error ) | |
555 | goto Fail; | |
556 | } | |
557 | ||
558 | /* now determine whether the private dictionary is encoded in binary */ | |
559 | /* or hexadecimal ASCII format. */ | |
560 | ||
561 | /* we need to access the next 4 bytes (after the final \r following */ | |
562 | /* the `eexec' keyword); if they all are hexadecimal digits, then */ | |
563 | /* we have a case of ASCII storage. */ | |
564 | while ( tokzer->cursor + 5 > tokzer->limit ) | |
565 | { | |
566 | error = grow( tokzer ); | |
567 | if ( error ) | |
568 | goto Fail; | |
569 | } | |
570 | ||
571 | /* skip whitespace/line feed after `eexec' */ | |
572 | base = (char*)tokzer->base + tokzer->cursor + 1; | |
573 | if ( ( hexa_value( base[0] ) | hexa_value( base[1] ) | | |
574 | hexa_value( base[2] ) | hexa_value( base[3] ) ) < 0 ) | |
575 | { | |
576 | /* binary encoding -- `simply' read the stream */ | |
577 | ||
578 | /* if it is a memory-based resource, we need to allocate a new */ | |
579 | /* storage buffer for the private dictionary, as it must be */ | |
580 | /* decrypted later */ | |
581 | if ( stream->base ) | |
582 | { | |
583 | size = stream->size - tokzer->cursor - 1; /* remaining bytes */ | |
584 | ||
585 | if ( ALLOC( private_dict, size ) ) /* alloc private dict buffer */ | |
586 | goto Fail; | |
587 | ||
588 | /* copy eexec-encrypted bytes */ | |
589 | MEM_Copy( private_dict, tokzer->base + tokzer->cursor + 1, size ); | |
590 | ||
591 | /* reset pointers - forget about file mapping */ | |
592 | tokzer->base = private_dict; | |
593 | tokzer->limit = size; | |
594 | tokzer->max = size; | |
595 | tokzer->cursor = 0; | |
596 | } | |
597 | /* On the opposite, for disk based resources, we simply grow */ | |
598 | /* the current buffer until its completion, and decrypt the */ | |
599 | /* bytes within it. In all cases, the `base' buffer will be */ | |
600 | /* discarded on DoneTokenizer if we are in the private dict. */ | |
601 | else | |
602 | { | |
603 | /* grow the read buffer to the full file */ | |
604 | while ( tokzer->limit < tokzer->max ) | |
605 | { | |
606 | error = grow( tokenizer ); | |
607 | if ( error ) | |
608 | goto Fail; | |
609 | } | |
610 | ||
611 | /* set up cursor to first encrypted byte */ | |
612 | tokzer->cursor++; | |
613 | } | |
614 | } | |
615 | else | |
616 | { | |
617 | /* ASCII hexadecimal encoding. This sucks... */ | |
618 | FT_Byte* write; | |
619 | FT_Byte* cur; | |
620 | FT_Byte* limit; | |
621 | FT_Int count; | |
622 | ||
623 | ||
624 | /* allocate a buffer, read each one byte at a time */ | |
625 | count = stream->size - tokzer->cursor; | |
626 | size = count / 2; | |
627 | ||
628 | if ( ALLOC( private_dict, size ) ) /* alloc private dict buffer */ | |
629 | goto Fail; | |
630 | ||
631 | write = private_dict; | |
632 | cur = tokzer->base + tokzer->cursor; | |
633 | limit = tokzer->base + tokzer->limit; | |
634 | ||
635 | /* read each bytes */ | |
636 | while ( count > 0 ) | |
637 | { | |
638 | /* ensure that we can read the next 2 bytes! */ | |
639 | while ( cur + 2 > limit ) | |
640 | { | |
641 | int cursor = cur - tokzer->base; | |
642 | ||
643 | ||
644 | error = grow( tokzer ); | |
645 | if ( error ) | |
646 | goto Fail_Private; | |
647 | cur = tokzer->base + cursor; | |
648 | limit = tokzer->base + tokzer->limit; | |
649 | } | |
650 | ||
651 | /* check for new line */ | |
652 | if ( cur[0] == '\r' || cur[0] == '\n' ) | |
653 | { | |
654 | cur++; | |
655 | count--; | |
656 | } | |
657 | else | |
658 | { | |
659 | int hex1 = hexa_value(cur[0]); | |
660 | ||
661 | ||
662 | /* exit if we have a non-hexadecimal digit which isn't */ | |
663 | /* a new-line character */ | |
664 | if ( hex1 < 0 ) | |
665 | break; | |
666 | ||
667 | /* otherwise, store byte */ | |
668 | *write++ = ( hex1 << 4 ) | hexa_value( cur[1] ); | |
669 | cur += 2; | |
670 | count -= 2; | |
671 | } | |
672 | } | |
673 | ||
674 | /* get rid of old buffer in the case of disk-based resources */ | |
675 | if ( !stream->base ) | |
676 | FREE( tokzer->base ); | |
677 | ||
678 | /* set up pointers */ | |
679 | tokzer->base = private_dict; | |
680 | tokzer->limit = size; | |
681 | tokzer->max = size; | |
682 | tokzer->cursor = 0; | |
683 | } | |
684 | } | |
685 | ||
686 | /* finally, decrypt the private dictionary - and skip the lenIV bytes */ | |
687 | t1_decrypt( tokzer->base, tokzer->limit, 55665 ); | |
688 | tokzer->cursor += 4; | |
689 | ||
690 | Fail: | |
691 | return error; | |
692 | ||
693 | Fail_Private: | |
694 | FREE( private_dict ); | |
695 | goto Fail; | |
696 | } | |
697 | ||
698 | ||
699 | /*************************************************************************/ | |
700 | /* */ | |
701 | /* <Function> */ | |
702 | /* Read_Token */ | |
703 | /* */ | |
704 | /* <Description> */ | |
705 | /* Reads a new token from the current input stream. This function */ | |
706 | /* extracts a token from the font program until Open_PrivateDict() */ | |
707 | /* has been called. After this, it returns tokens from the */ | |
708 | /* (eexec-encrypted) private dictionary. */ | |
709 | /* */ | |
710 | /* <Input> */ | |
711 | /* tokenizer :: The target tokenizer object. */ | |
712 | /* */ | |
713 | /* <Return> */ | |
714 | /* FreeType error code. 0 means success. */ | |
715 | /* */ | |
716 | /* <Note> */ | |
717 | /* Use the function Read_CharStrings() to read the binary charstrings */ | |
718 | /* from the private dict. */ | |
719 | /* */ | |
720 | LOCAL_FUNC | |
721 | FT_Error Read_Token( T1_Tokenizer tokenizer ) | |
722 | { | |
723 | T1_Tokenizer tok = tokenizer; | |
724 | FT_Long cur, limit; | |
725 | FT_Byte* base; | |
726 | char c, starter, ender; | |
727 | FT_Bool token_started; | |
728 | ||
729 | T1_TokenType kind; | |
730 | ||
731 | ||
732 | tok->error = T1_Err_Ok; | |
733 | tok->token.kind = tok_any; | |
734 | ||
735 | base = tok->base; | |
736 | limit = tok->limit; | |
737 | cur = tok->cursor; | |
738 | ||
739 | token_started = 0; | |
740 | ||
741 | for (;;) | |
742 | { | |
743 | if ( cur >= limit ) | |
744 | { | |
745 | if ( grow( tok ) ) | |
746 | goto Exit; | |
747 | base = tok->base; | |
748 | limit = tok->limit; | |
749 | } | |
750 | ||
751 | c = (char)base[cur++]; | |
752 | ||
753 | /* check that we have an ASCII character */ | |
754 | if ( (FT_Byte)c > 127 ) | |
755 | { | |
756 | FT_ERROR(( "Read_Token:" )); | |
757 | FT_ERROR(( " unexpected binary data in Type1 fragment!\n" )); | |
758 | tok->error = T1_Err_Invalid_File_Format; | |
759 | goto Exit; | |
760 | } | |
761 | ||
762 | switch ( c ) | |
763 | { | |
764 | case '\r': | |
765 | case '\n': | |
766 | case ' ' : | |
767 | case '\t': /* skip initial whitespace => skip to next */ | |
768 | if ( token_started ) | |
769 | { | |
770 | /* possibly a name, keyword, wathever */ | |
771 | tok->token.kind = tok_any; | |
772 | tok->token.len = cur-tok->token.start - 1; | |
773 | goto Exit; | |
774 | } | |
775 | /* otherwise, skip everything */ | |
776 | break; | |
777 | ||
778 | case '%': /* this is a comment -- skip everything */ | |
779 | for (;;) | |
780 | { | |
781 | FT_Int left = limit - cur; | |
782 | ||
783 | ||
784 | while ( left > 0 ) | |
785 | { | |
786 | c = (char)base[cur++]; | |
787 | if ( c == '\r' || c == '\n' ) | |
788 | goto Next; | |
789 | left--; | |
790 | } | |
791 | ||
792 | if ( grow( tokenizer ) ) | |
793 | goto Exit; | |
794 | base = tok->base; | |
795 | limit = tok->limit; | |
796 | } | |
797 | ||
798 | case '(': /* a Postscript string */ | |
799 | kind = tok_string; | |
800 | ender = ')'; | |
801 | ||
802 | L1: | |
803 | if ( !token_started ) | |
804 | { | |
805 | token_started = 1; | |
806 | tok->token.start = cur - 1; | |
807 | } | |
808 | ||
809 | { | |
810 | FT_Int nest_level = 1; | |
811 | ||
812 | ||
813 | starter = c; | |
814 | for (;;) | |
815 | { | |
816 | FT_Int left = limit - cur; | |
817 | ||
818 | ||
819 | while ( left > 0 ) | |
820 | { | |
821 | c = (char)base[cur++]; | |
822 | ||
823 | if ( c == starter ) | |
824 | nest_level++; | |
825 | ||
826 | else if ( c == ender ) | |
827 | { | |
828 | nest_level--; | |
829 | if ( nest_level <= 0 ) | |
830 | { | |
831 | tok->token.kind = kind; | |
832 | tok->token.len = cur - tok->token.start; | |
833 | goto Exit; | |
834 | } | |
835 | } | |
836 | left--; | |
837 | } | |
838 | ||
839 | if ( grow( tok ) ) | |
840 | goto Exit; | |
841 | base = tok->base; | |
842 | limit = tok->limit; | |
843 | } | |
844 | } | |
845 | ||
846 | case '[': /* a Postscript array */ | |
847 | if ( token_started ) | |
848 | goto Any_Token; | |
849 | ||
850 | kind = tok_array; | |
851 | ender = ']'; | |
852 | goto L1; | |
853 | break; | |
854 | ||
855 | case '{': /* a Postscript program */ | |
856 | if ( token_started ) | |
857 | goto Any_Token; | |
858 | ||
859 | kind = tok_program; | |
860 | ender = '}'; | |
861 | goto L1; | |
862 | break; | |
863 | ||
864 | case '<': /* a Postscript hex byte array? */ | |
865 | if ( token_started ) | |
866 | goto Any_Token; | |
867 | ||
868 | kind = tok_hexarray; | |
869 | ender = '>'; | |
870 | goto L1; | |
871 | break; | |
872 | ||
873 | case '0': /* any number */ | |
874 | case '1': | |
875 | case '2': | |
876 | case '3': | |
877 | case '4': | |
878 | case '5': | |
879 | case '6': | |
880 | case '7': | |
881 | case '8': | |
882 | case '9': | |
883 | if ( token_started ) | |
884 | goto Next; | |
885 | ||
886 | tok->token.kind = tok_number; | |
887 | token_started = 1; | |
888 | tok->token.start = cur - 1; | |
889 | ||
890 | L2: | |
891 | for (;;) | |
892 | { | |
893 | FT_Int left = limit-cur; | |
894 | ||
895 | ||
896 | while ( left > 0 ) | |
897 | { | |
898 | c = (char)base[cur++]; | |
899 | ||
900 | switch ( c ) | |
901 | { | |
902 | case '[': /* ] */ | |
903 | case '{': /* } */ | |
904 | case '(': /* ) */ | |
905 | case '<': | |
906 | case '/': | |
907 | goto Any_Token; | |
908 | ||
909 | case ' ': | |
910 | case '\r': | |
911 | case '\t': | |
912 | case '\n': | |
913 | tok->token.len = cur - tok->token.start - 1; | |
914 | goto Exit; | |
915 | ||
916 | default: | |
917 | ; | |
918 | } | |
919 | left--; | |
920 | } | |
921 | ||
922 | if ( grow( tok ) ) | |
923 | goto Exit; | |
924 | base = tok->base; | |
925 | limit = tok->limit; | |
926 | } | |
927 | ||
928 | case '.': /* maybe a number */ | |
929 | case '-': | |
930 | case '+': | |
931 | if ( token_started ) | |
932 | goto Next; | |
933 | ||
934 | token_started = 1; | |
935 | tok->token.start = cur - 1; | |
936 | ||
937 | for (;;) | |
938 | { | |
939 | FT_Int left = limit - cur; | |
940 | ||
941 | ||
942 | if ( left > 0 ) | |
943 | { | |
944 | /* test for any following digit, interpreted as number */ | |
945 | c = (char)base[cur]; | |
946 | tok->token.kind = ( c >= '0' && c <= '9' ? tok_number : tok_any ); | |
947 | goto L2; | |
948 | } | |
949 | ||
950 | if ( grow( tok ) ) | |
951 | goto Exit; | |
952 | base = tok->base; | |
953 | limit = tok->limit; | |
954 | } | |
955 | ||
956 | case '/': /* maybe an immediate name */ | |
957 | if ( !token_started ) | |
958 | { | |
959 | token_started = 1; | |
960 | tok->token.start = cur - 1; | |
961 | ||
962 | for (;;) | |
963 | { | |
964 | FT_Int left = limit - cur; | |
965 | ||
966 | ||
967 | if ( left > 0 ) | |
968 | { | |
969 | /* test for single '/', interpreted as garbage */ | |
970 | c = (char)base[cur]; | |
971 | tok->token.kind = ( c == ' ' || c == '\t' || | |
972 | c == '\r' || c == '\n' ) ? tok_any | |
973 | : tok_immediate; | |
974 | goto L2; | |
975 | } | |
976 | ||
977 | if ( grow( tok ) ) | |
978 | goto Exit; | |
979 | base = tok->base; | |
980 | limit = tok->limit; | |
981 | } | |
982 | } | |
983 | else | |
984 | { | |
985 | Any_Token: /* possibly a name or wathever */ | |
986 | cur--; | |
987 | tok->token.len = cur - tok->token.start; | |
988 | goto Exit; | |
989 | } | |
990 | ||
991 | default: | |
992 | if ( !token_started ) | |
993 | { | |
994 | token_started = 1; | |
995 | tok->token.start = cur - 1; | |
996 | } | |
997 | } | |
998 | ||
999 | Next: | |
1000 | ; | |
1001 | } | |
1002 | ||
1003 | Exit: | |
1004 | tok->cursor = cur; | |
1005 | ||
1006 | if ( !tok->error ) | |
1007 | { | |
1008 | /* now, tries to match keywords and immediate names */ | |
1009 | FT_Int index; | |
1010 | ||
1011 | ||
1012 | switch ( tok->token.kind ) | |
1013 | { | |
1014 | case tok_immediate: /* immediate name */ | |
1015 | index = Find_Name( (char*)( tok->base + tok->token.start + 1 ), | |
1016 | tok->token.len - 1, | |
1017 | t1_immediates, | |
1018 | imm_max - imm_first_ ); | |
1019 | tok->token.kind2 = ( index >= 0 ) | |
1020 | ? (T1_TokenType)( imm_first_ + index ) | |
1021 | : tok_error; | |
1022 | break; | |
1023 | ||
1024 | case tok_any: /* test for keyword */ | |
1025 | index = Find_Name( (char*)( tok->base + tok->token.start ), | |
1026 | tok->token.len, | |
1027 | t1_keywords, | |
1028 | key_max - key_first_ ); | |
1029 | if ( index >= 0 ) | |
1030 | { | |
1031 | tok->token.kind = tok_keyword; | |
1032 | tok->token.kind2 = (T1_TokenType)( key_first_ + index ); | |
1033 | } | |
1034 | else | |
1035 | tok->token.kind2 = tok_error; | |
1036 | break; | |
1037 | ||
1038 | default: | |
1039 | tok->token.kind2 = tok_error; | |
1040 | } | |
1041 | } | |
1042 | return tokenizer->error; | |
1043 | } | |
1044 | ||
1045 | ||
1046 | #if 0 | |
1047 | ||
1048 | /*************************************************************************/ | |
1049 | /* */ | |
1050 | /* <Function> */ | |
1051 | /* Read_CharStrings */ | |
1052 | /* */ | |
1053 | /* <Description> */ | |
1054 | /* Reads a charstrings element from the current input stream. These */ | |
1055 | /* are binary bytes that encode each individual glyph outline. */ | |
1056 | /* */ | |
1057 | /* The caller is responsible for skipping the `lenIV' bytes at the */ | |
1058 | /* start of the record. */ | |
1059 | /* */ | |
1060 | /* <Input> */ | |
1061 | /* tokenizer :: The target tokenizer object. */ | |
1062 | /* num_chars :: The number of binary bytes to read. */ | |
1063 | /* */ | |
1064 | /* <Output> */ | |
1065 | /* buffer :: The target array of bytes. These are */ | |
1066 | /* eexec-decrypted. */ | |
1067 | /* */ | |
1068 | /* <Return> */ | |
1069 | /* FreeType error code. 0 means success. */ | |
1070 | /* */ | |
1071 | /* <Note> */ | |
1072 | /* Use the function Read_CharStrings() to read binary charstrings */ | |
1073 | /* from the private dict. */ | |
1074 | /* */ | |
1075 | LOCAL_FUNC | |
1076 | FT_Error Read_CharStrings( T1_Tokenizer tokenizer, | |
1077 | FT_Int num_chars, | |
1078 | FT_Byte* buffer ) | |
1079 | { | |
1080 | for (;;) | |
1081 | { | |
1082 | FT_Int left = tokenizer->limit - tokenizer->cursor; | |
1083 | ||
1084 | ||
1085 | if ( left >= num_chars ) | |
1086 | { | |
1087 | MEM_Copy( buffer, tokenizer->base + tokenizer->cursor, num_chars ); | |
1088 | t1_decrypt( buffer, num_chars, 4330 ); | |
1089 | tokenizer->cursor += num_chars; | |
1090 | return T1_Err_Ok; | |
1091 | } | |
1092 | ||
1093 | if ( grow( tokenizer ) ) | |
1094 | return tokenizer->error; | |
1095 | } | |
1096 | } | |
1097 | ||
1098 | #endif /* 0 */ | |
1099 | ||
1100 | ||
1101 | /* END */ |