[FREETYPE] Update to v2.6.3. CORE-10964
[reactos.git] / reactos / lib / 3rdparty / freetype / src / psaux / psobjs.c
1 /***************************************************************************/
2 /* */
3 /* psobjs.c */
4 /* */
5 /* Auxiliary functions for PostScript fonts (body). */
6 /* */
7 /* Copyright 1996-2016 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 #include <ft2build.h>
20 #include FT_INTERNAL_POSTSCRIPT_AUX_H
21 #include FT_INTERNAL_DEBUG_H
22 #include FT_INTERNAL_CALC_H
23
24 #include "psobjs.h"
25 #include "psconv.h"
26
27 #include "psauxerr.h"
28
29
30 /*************************************************************************/
31 /* */
32 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
33 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
34 /* messages during execution. */
35 /* */
36 #undef FT_COMPONENT
37 #define FT_COMPONENT trace_psobjs
38
39
40 /*************************************************************************/
41 /*************************************************************************/
42 /***** *****/
43 /***** PS_TABLE *****/
44 /***** *****/
45 /*************************************************************************/
46 /*************************************************************************/
47
48 /*************************************************************************/
49 /* */
50 /* <Function> */
51 /* ps_table_new */
52 /* */
53 /* <Description> */
54 /* Initializes a PS_Table. */
55 /* */
56 /* <InOut> */
57 /* table :: The address of the target table. */
58 /* */
59 /* <Input> */
60 /* count :: The table size = the maximum number of elements. */
61 /* */
62 /* memory :: The memory object to use for all subsequent */
63 /* reallocations. */
64 /* */
65 /* <Return> */
66 /* FreeType error code. 0 means success. */
67 /* */
68 FT_LOCAL_DEF( FT_Error )
69 ps_table_new( PS_Table table,
70 FT_Int count,
71 FT_Memory memory )
72 {
73 FT_Error error;
74
75
76 table->memory = memory;
77 if ( FT_NEW_ARRAY( table->elements, count ) ||
78 FT_NEW_ARRAY( table->lengths, count ) )
79 goto Exit;
80
81 table->max_elems = count;
82 table->init = 0xDEADBEEFUL;
83 table->num_elems = 0;
84 table->block = NULL;
85 table->capacity = 0;
86 table->cursor = 0;
87
88 *(PS_Table_FuncsRec*)&table->funcs = ps_table_funcs;
89
90 Exit:
91 if ( error )
92 FT_FREE( table->elements );
93
94 return error;
95 }
96
97
98 static void
99 shift_elements( PS_Table table,
100 FT_Byte* old_base )
101 {
102 FT_PtrDist delta = table->block - old_base;
103 FT_Byte** offset = table->elements;
104 FT_Byte** limit = offset + table->max_elems;
105
106
107 for ( ; offset < limit; offset++ )
108 {
109 if ( offset[0] )
110 offset[0] += delta;
111 }
112 }
113
114
115 static FT_Error
116 reallocate_t1_table( PS_Table table,
117 FT_Offset new_size )
118 {
119 FT_Memory memory = table->memory;
120 FT_Byte* old_base = table->block;
121 FT_Error error;
122
123
124 /* allocate new base block */
125 if ( FT_ALLOC( table->block, new_size ) )
126 {
127 table->block = old_base;
128 return error;
129 }
130
131 /* copy elements and shift offsets */
132 if ( old_base )
133 {
134 FT_MEM_COPY( table->block, old_base, table->capacity );
135 shift_elements( table, old_base );
136 FT_FREE( old_base );
137 }
138
139 table->capacity = new_size;
140
141 return FT_Err_Ok;
142 }
143
144
145 /*************************************************************************/
146 /* */
147 /* <Function> */
148 /* ps_table_add */
149 /* */
150 /* <Description> */
151 /* Adds an object to a PS_Table, possibly growing its memory block. */
152 /* */
153 /* <InOut> */
154 /* table :: The target table. */
155 /* */
156 /* <Input> */
157 /* idx :: The index of the object in the table. */
158 /* */
159 /* object :: The address of the object to copy in memory. */
160 /* */
161 /* length :: The length in bytes of the source object. */
162 /* */
163 /* <Return> */
164 /* FreeType error code. 0 means success. An error is returned if a */
165 /* reallocation fails. */
166 /* */
167 FT_LOCAL_DEF( FT_Error )
168 ps_table_add( PS_Table table,
169 FT_Int idx,
170 void* object,
171 FT_UInt length )
172 {
173 if ( idx < 0 || idx >= table->max_elems )
174 {
175 FT_ERROR(( "ps_table_add: invalid index\n" ));
176 return FT_THROW( Invalid_Argument );
177 }
178
179 /* grow the base block if needed */
180 if ( table->cursor + length > table->capacity )
181 {
182 FT_Error error;
183 FT_Offset new_size = table->capacity;
184 FT_PtrDist in_offset;
185
186
187 in_offset = (FT_Byte*)object - table->block;
188 if ( in_offset < 0 || (FT_Offset)in_offset >= table->capacity )
189 in_offset = -1;
190
191 while ( new_size < table->cursor + length )
192 {
193 /* increase size by 25% and round up to the nearest multiple
194 of 1024 */
195 new_size += ( new_size >> 2 ) + 1;
196 new_size = FT_PAD_CEIL( new_size, 1024 );
197 }
198
199 error = reallocate_t1_table( table, new_size );
200 if ( error )
201 return error;
202
203 if ( in_offset >= 0 )
204 object = table->block + in_offset;
205 }
206
207 /* add the object to the base block and adjust offset */
208 table->elements[idx] = table->block + table->cursor;
209 table->lengths [idx] = length;
210 FT_MEM_COPY( table->block + table->cursor, object, length );
211
212 table->cursor += length;
213 return FT_Err_Ok;
214 }
215
216
217 /*************************************************************************/
218 /* */
219 /* <Function> */
220 /* ps_table_done */
221 /* */
222 /* <Description> */
223 /* Finalizes a PS_TableRec (i.e., reallocate it to its current */
224 /* cursor). */
225 /* */
226 /* <InOut> */
227 /* table :: The target table. */
228 /* */
229 /* <Note> */
230 /* This function does NOT release the heap's memory block. It is up */
231 /* to the caller to clean it, or reference it in its own structures. */
232 /* */
233 FT_LOCAL_DEF( void )
234 ps_table_done( PS_Table table )
235 {
236 FT_Memory memory = table->memory;
237 FT_Error error;
238 FT_Byte* old_base = table->block;
239
240
241 /* should never fail, because rec.cursor <= rec.size */
242 if ( !old_base )
243 return;
244
245 if ( FT_ALLOC( table->block, table->cursor ) )
246 return;
247 FT_MEM_COPY( table->block, old_base, table->cursor );
248 shift_elements( table, old_base );
249
250 table->capacity = table->cursor;
251 FT_FREE( old_base );
252
253 FT_UNUSED( error );
254 }
255
256
257 FT_LOCAL_DEF( void )
258 ps_table_release( PS_Table table )
259 {
260 FT_Memory memory = table->memory;
261
262
263 if ( (FT_ULong)table->init == 0xDEADBEEFUL )
264 {
265 FT_FREE( table->block );
266 FT_FREE( table->elements );
267 FT_FREE( table->lengths );
268 table->init = 0;
269 }
270 }
271
272
273 /*************************************************************************/
274 /*************************************************************************/
275 /***** *****/
276 /***** T1 PARSER *****/
277 /***** *****/
278 /*************************************************************************/
279 /*************************************************************************/
280
281
282 /* first character must be already part of the comment */
283
284 static void
285 skip_comment( FT_Byte* *acur,
286 FT_Byte* limit )
287 {
288 FT_Byte* cur = *acur;
289
290
291 while ( cur < limit )
292 {
293 if ( IS_PS_NEWLINE( *cur ) )
294 break;
295 cur++;
296 }
297
298 *acur = cur;
299 }
300
301
302 static void
303 skip_spaces( FT_Byte* *acur,
304 FT_Byte* limit )
305 {
306 FT_Byte* cur = *acur;
307
308
309 while ( cur < limit )
310 {
311 if ( !IS_PS_SPACE( *cur ) )
312 {
313 if ( *cur == '%' )
314 /* According to the PLRM, a comment is equal to a space. */
315 skip_comment( &cur, limit );
316 else
317 break;
318 }
319 cur++;
320 }
321
322 *acur = cur;
323 }
324
325
326 #define IS_OCTAL_DIGIT( c ) ( '0' <= (c) && (c) <= '7' )
327
328
329 /* first character must be `('; */
330 /* *acur is positioned at the character after the closing `)' */
331
332 static FT_Error
333 skip_literal_string( FT_Byte* *acur,
334 FT_Byte* limit )
335 {
336 FT_Byte* cur = *acur;
337 FT_Int embed = 0;
338 FT_Error error = FT_ERR( Invalid_File_Format );
339 unsigned int i;
340
341
342 while ( cur < limit )
343 {
344 FT_Byte c = *cur;
345
346
347 ++cur;
348
349 if ( c == '\\' )
350 {
351 /* Red Book 3rd ed., section `Literal Text Strings', p. 29: */
352 /* A backslash can introduce three different types */
353 /* of escape sequences: */
354 /* - a special escaped char like \r, \n, etc. */
355 /* - a one-, two-, or three-digit octal number */
356 /* - none of the above in which case the backslash is ignored */
357
358 if ( cur == limit )
359 /* error (or to be ignored?) */
360 break;
361
362 switch ( *cur )
363 {
364 /* skip `special' escape */
365 case 'n':
366 case 'r':
367 case 't':
368 case 'b':
369 case 'f':
370 case '\\':
371 case '(':
372 case ')':
373 ++cur;
374 break;
375
376 default:
377 /* skip octal escape or ignore backslash */
378 for ( i = 0; i < 3 && cur < limit; ++i )
379 {
380 if ( !IS_OCTAL_DIGIT( *cur ) )
381 break;
382
383 ++cur;
384 }
385 }
386 }
387 else if ( c == '(' )
388 embed++;
389 else if ( c == ')' )
390 {
391 embed--;
392 if ( embed == 0 )
393 {
394 error = FT_Err_Ok;
395 break;
396 }
397 }
398 }
399
400 *acur = cur;
401
402 return error;
403 }
404
405
406 /* first character must be `<' */
407
408 static FT_Error
409 skip_string( FT_Byte* *acur,
410 FT_Byte* limit )
411 {
412 FT_Byte* cur = *acur;
413 FT_Error err = FT_Err_Ok;
414
415
416 while ( ++cur < limit )
417 {
418 /* All whitespace characters are ignored. */
419 skip_spaces( &cur, limit );
420 if ( cur >= limit )
421 break;
422
423 if ( !IS_PS_XDIGIT( *cur ) )
424 break;
425 }
426
427 if ( cur < limit && *cur != '>' )
428 {
429 FT_ERROR(( "skip_string: missing closing delimiter `>'\n" ));
430 err = FT_THROW( Invalid_File_Format );
431 }
432 else
433 cur++;
434
435 *acur = cur;
436 return err;
437 }
438
439
440 /* first character must be the opening brace that */
441 /* starts the procedure */
442
443 /* NB: [ and ] need not match: */
444 /* `/foo {[} def' is a valid PostScript fragment, */
445 /* even within a Type1 font */
446
447 static FT_Error
448 skip_procedure( FT_Byte* *acur,
449 FT_Byte* limit )
450 {
451 FT_Byte* cur;
452 FT_Int embed = 0;
453 FT_Error error = FT_Err_Ok;
454
455
456 FT_ASSERT( **acur == '{' );
457
458 for ( cur = *acur; cur < limit && error == FT_Err_Ok; ++cur )
459 {
460 switch ( *cur )
461 {
462 case '{':
463 ++embed;
464 break;
465
466 case '}':
467 --embed;
468 if ( embed == 0 )
469 {
470 ++cur;
471 goto end;
472 }
473 break;
474
475 case '(':
476 error = skip_literal_string( &cur, limit );
477 break;
478
479 case '<':
480 error = skip_string( &cur, limit );
481 break;
482
483 case '%':
484 skip_comment( &cur, limit );
485 break;
486 }
487 }
488
489 end:
490 if ( embed != 0 )
491 error = FT_THROW( Invalid_File_Format );
492
493 *acur = cur;
494
495 return error;
496 }
497
498
499 /***********************************************************************/
500 /* */
501 /* All exported parsing routines handle leading whitespace and stop at */
502 /* the first character which isn't part of the just handled token. */
503 /* */
504 /***********************************************************************/
505
506
507 FT_LOCAL_DEF( void )
508 ps_parser_skip_PS_token( PS_Parser parser )
509 {
510 /* Note: PostScript allows any non-delimiting, non-whitespace */
511 /* character in a name (PS Ref Manual, 3rd ed, p31). */
512 /* PostScript delimiters are (, ), <, >, [, ], {, }, /, and %. */
513
514 FT_Byte* cur = parser->cursor;
515 FT_Byte* limit = parser->limit;
516 FT_Error error = FT_Err_Ok;
517
518
519 skip_spaces( &cur, limit ); /* this also skips comments */
520 if ( cur >= limit )
521 goto Exit;
522
523 /* self-delimiting, single-character tokens */
524 if ( *cur == '[' || *cur == ']' )
525 {
526 cur++;
527 goto Exit;
528 }
529
530 /* skip balanced expressions (procedures and strings) */
531
532 if ( *cur == '{' ) /* {...} */
533 {
534 error = skip_procedure( &cur, limit );
535 goto Exit;
536 }
537
538 if ( *cur == '(' ) /* (...) */
539 {
540 error = skip_literal_string( &cur, limit );
541 goto Exit;
542 }
543
544 if ( *cur == '<' ) /* <...> */
545 {
546 if ( cur + 1 < limit && *(cur + 1) == '<' ) /* << */
547 {
548 cur++;
549 cur++;
550 }
551 else
552 error = skip_string( &cur, limit );
553
554 goto Exit;
555 }
556
557 if ( *cur == '>' )
558 {
559 cur++;
560 if ( cur >= limit || *cur != '>' ) /* >> */
561 {
562 FT_ERROR(( "ps_parser_skip_PS_token:"
563 " unexpected closing delimiter `>'\n" ));
564 error = FT_THROW( Invalid_File_Format );
565 goto Exit;
566 }
567 cur++;
568 goto Exit;
569 }
570
571 if ( *cur == '/' )
572 cur++;
573
574 /* anything else */
575 while ( cur < limit )
576 {
577 /* *cur might be invalid (e.g., ')' or '}'), but this */
578 /* is handled by the test `cur == parser->cursor' below */
579 if ( IS_PS_DELIM( *cur ) )
580 break;
581
582 cur++;
583 }
584
585 Exit:
586 if ( cur < limit && cur == parser->cursor )
587 {
588 FT_ERROR(( "ps_parser_skip_PS_token:"
589 " current token is `%c' which is self-delimiting\n"
590 " "
591 " but invalid at this point\n",
592 *cur ));
593
594 error = FT_THROW( Invalid_File_Format );
595 }
596
597 if ( cur > limit )
598 cur = limit;
599
600 parser->error = error;
601 parser->cursor = cur;
602 }
603
604
605 FT_LOCAL_DEF( void )
606 ps_parser_skip_spaces( PS_Parser parser )
607 {
608 skip_spaces( &parser->cursor, parser->limit );
609 }
610
611
612 /* `token' here means either something between balanced delimiters */
613 /* or the next token; the delimiters are not removed. */
614
615 FT_LOCAL_DEF( void )
616 ps_parser_to_token( PS_Parser parser,
617 T1_Token token )
618 {
619 FT_Byte* cur;
620 FT_Byte* limit;
621 FT_Int embed;
622
623
624 token->type = T1_TOKEN_TYPE_NONE;
625 token->start = NULL;
626 token->limit = NULL;
627
628 /* first of all, skip leading whitespace */
629 ps_parser_skip_spaces( parser );
630
631 cur = parser->cursor;
632 limit = parser->limit;
633
634 if ( cur >= limit )
635 return;
636
637 switch ( *cur )
638 {
639 /************* check for literal string *****************/
640 case '(':
641 token->type = T1_TOKEN_TYPE_STRING;
642 token->start = cur;
643
644 if ( skip_literal_string( &cur, limit ) == FT_Err_Ok )
645 token->limit = cur;
646 break;
647
648 /************* check for programs/array *****************/
649 case '{':
650 token->type = T1_TOKEN_TYPE_ARRAY;
651 token->start = cur;
652
653 if ( skip_procedure( &cur, limit ) == FT_Err_Ok )
654 token->limit = cur;
655 break;
656
657 /************* check for table/array ********************/
658 /* XXX: in theory we should also look for "<<" */
659 /* since this is semantically equivalent to "["; */
660 /* in practice it doesn't matter (?) */
661 case '[':
662 token->type = T1_TOKEN_TYPE_ARRAY;
663 embed = 1;
664 token->start = cur++;
665
666 /* we need this to catch `[ ]' */
667 parser->cursor = cur;
668 ps_parser_skip_spaces( parser );
669 cur = parser->cursor;
670
671 while ( cur < limit && !parser->error )
672 {
673 /* XXX: this is wrong because it does not */
674 /* skip comments, procedures, and strings */
675 if ( *cur == '[' )
676 embed++;
677 else if ( *cur == ']' )
678 {
679 embed--;
680 if ( embed <= 0 )
681 {
682 token->limit = ++cur;
683 break;
684 }
685 }
686
687 parser->cursor = cur;
688 ps_parser_skip_PS_token( parser );
689 /* we need this to catch `[XXX ]' */
690 ps_parser_skip_spaces ( parser );
691 cur = parser->cursor;
692 }
693 break;
694
695 /* ************ otherwise, it is any token **************/
696 default:
697 token->start = cur;
698 token->type = ( *cur == '/' ? T1_TOKEN_TYPE_KEY : T1_TOKEN_TYPE_ANY );
699 ps_parser_skip_PS_token( parser );
700 cur = parser->cursor;
701 if ( !parser->error )
702 token->limit = cur;
703 }
704
705 if ( !token->limit )
706 {
707 token->start = NULL;
708 token->type = T1_TOKEN_TYPE_NONE;
709 }
710
711 parser->cursor = cur;
712 }
713
714
715 /* NB: `tokens' can be NULL if we only want to count */
716 /* the number of array elements */
717
718 FT_LOCAL_DEF( void )
719 ps_parser_to_token_array( PS_Parser parser,
720 T1_Token tokens,
721 FT_UInt max_tokens,
722 FT_Int* pnum_tokens )
723 {
724 T1_TokenRec master;
725
726
727 *pnum_tokens = -1;
728
729 /* this also handles leading whitespace */
730 ps_parser_to_token( parser, &master );
731
732 if ( master.type == T1_TOKEN_TYPE_ARRAY )
733 {
734 FT_Byte* old_cursor = parser->cursor;
735 FT_Byte* old_limit = parser->limit;
736 T1_Token cur = tokens;
737 T1_Token limit = cur + max_tokens;
738
739
740 /* don't include outermost delimiters */
741 parser->cursor = master.start + 1;
742 parser->limit = master.limit - 1;
743
744 while ( parser->cursor < parser->limit )
745 {
746 T1_TokenRec token;
747
748
749 ps_parser_to_token( parser, &token );
750 if ( !token.type )
751 break;
752
753 if ( tokens != NULL && cur < limit )
754 *cur = token;
755
756 cur++;
757 }
758
759 *pnum_tokens = (FT_Int)( cur - tokens );
760
761 parser->cursor = old_cursor;
762 parser->limit = old_limit;
763 }
764 }
765
766
767 /* first character must be a delimiter or a part of a number */
768 /* NB: `coords' can be NULL if we just want to skip the */
769 /* array; in this case we ignore `max_coords' */
770
771 static FT_Int
772 ps_tocoordarray( FT_Byte* *acur,
773 FT_Byte* limit,
774 FT_Int max_coords,
775 FT_Short* coords )
776 {
777 FT_Byte* cur = *acur;
778 FT_Int count = 0;
779 FT_Byte c, ender;
780
781
782 if ( cur >= limit )
783 goto Exit;
784
785 /* check for the beginning of an array; otherwise, only one number */
786 /* will be read */
787 c = *cur;
788 ender = 0;
789
790 if ( c == '[' )
791 ender = ']';
792 else if ( c == '{' )
793 ender = '}';
794
795 if ( ender )
796 cur++;
797
798 /* now, read the coordinates */
799 while ( cur < limit )
800 {
801 FT_Short dummy;
802 FT_Byte* old_cur;
803
804
805 /* skip whitespace in front of data */
806 skip_spaces( &cur, limit );
807 if ( cur >= limit )
808 goto Exit;
809
810 if ( *cur == ender )
811 {
812 cur++;
813 break;
814 }
815
816 old_cur = cur;
817
818 if ( coords != NULL && count >= max_coords )
819 break;
820
821 /* call PS_Conv_ToFixed() even if coords == NULL */
822 /* to properly parse number at `cur' */
823 *( coords != NULL ? &coords[count] : &dummy ) =
824 (FT_Short)( PS_Conv_ToFixed( &cur, limit, 0 ) >> 16 );
825
826 if ( old_cur == cur )
827 {
828 count = -1;
829 goto Exit;
830 }
831 else
832 count++;
833
834 if ( !ender )
835 break;
836 }
837
838 Exit:
839 *acur = cur;
840 return count;
841 }
842
843
844 /* first character must be a delimiter or a part of a number */
845 /* NB: `values' can be NULL if we just want to skip the */
846 /* array; in this case we ignore `max_values' */
847 /* */
848 /* return number of successfully parsed values */
849
850 static FT_Int
851 ps_tofixedarray( FT_Byte* *acur,
852 FT_Byte* limit,
853 FT_Int max_values,
854 FT_Fixed* values,
855 FT_Int power_ten )
856 {
857 FT_Byte* cur = *acur;
858 FT_Int count = 0;
859 FT_Byte c, ender;
860
861
862 if ( cur >= limit )
863 goto Exit;
864
865 /* Check for the beginning of an array. Otherwise, only one number */
866 /* will be read. */
867 c = *cur;
868 ender = 0;
869
870 if ( c == '[' )
871 ender = ']';
872 else if ( c == '{' )
873 ender = '}';
874
875 if ( ender )
876 cur++;
877
878 /* now, read the values */
879 while ( cur < limit )
880 {
881 FT_Fixed dummy;
882 FT_Byte* old_cur;
883
884
885 /* skip whitespace in front of data */
886 skip_spaces( &cur, limit );
887 if ( cur >= limit )
888 goto Exit;
889
890 if ( *cur == ender )
891 {
892 cur++;
893 break;
894 }
895
896 old_cur = cur;
897
898 if ( values != NULL && count >= max_values )
899 break;
900
901 /* call PS_Conv_ToFixed() even if coords == NULL */
902 /* to properly parse number at `cur' */
903 *( values != NULL ? &values[count] : &dummy ) =
904 PS_Conv_ToFixed( &cur, limit, power_ten );
905
906 if ( old_cur == cur )
907 {
908 count = -1;
909 goto Exit;
910 }
911 else
912 count++;
913
914 if ( !ender )
915 break;
916 }
917
918 Exit:
919 *acur = cur;
920 return count;
921 }
922
923
924 #if 0
925
926 static FT_String*
927 ps_tostring( FT_Byte** cursor,
928 FT_Byte* limit,
929 FT_Memory memory )
930 {
931 FT_Byte* cur = *cursor;
932 FT_UInt len = 0;
933 FT_Int count;
934 FT_String* result;
935 FT_Error error;
936
937
938 /* XXX: some stupid fonts have a `Notice' or `Copyright' string */
939 /* that simply doesn't begin with an opening parenthesis, even */
940 /* though they have a closing one! E.g. "amuncial.pfb" */
941 /* */
942 /* We must deal with these ill-fated cases there. Note that */
943 /* these fonts didn't work with the old Type 1 driver as the */
944 /* notice/copyright was not recognized as a valid string token */
945 /* and made the old token parser commit errors. */
946
947 while ( cur < limit && ( *cur == ' ' || *cur == '\t' ) )
948 cur++;
949 if ( cur + 1 >= limit )
950 return 0;
951
952 if ( *cur == '(' )
953 cur++; /* skip the opening parenthesis, if there is one */
954
955 *cursor = cur;
956 count = 0;
957
958 /* then, count its length */
959 for ( ; cur < limit; cur++ )
960 {
961 if ( *cur == '(' )
962 count++;
963
964 else if ( *cur == ')' )
965 {
966 count--;
967 if ( count < 0 )
968 break;
969 }
970 }
971
972 len = (FT_UInt)( cur - *cursor );
973 if ( cur >= limit || FT_ALLOC( result, len + 1 ) )
974 return 0;
975
976 /* now copy the string */
977 FT_MEM_COPY( result, *cursor, len );
978 result[len] = '\0';
979 *cursor = cur;
980 return result;
981 }
982
983 #endif /* 0 */
984
985
986 static int
987 ps_tobool( FT_Byte* *acur,
988 FT_Byte* limit )
989 {
990 FT_Byte* cur = *acur;
991 FT_Bool result = 0;
992
993
994 /* return 1 if we find `true', 0 otherwise */
995 if ( cur + 3 < limit &&
996 cur[0] == 't' &&
997 cur[1] == 'r' &&
998 cur[2] == 'u' &&
999 cur[3] == 'e' )
1000 {
1001 result = 1;
1002 cur += 5;
1003 }
1004 else if ( cur + 4 < limit &&
1005 cur[0] == 'f' &&
1006 cur[1] == 'a' &&
1007 cur[2] == 'l' &&
1008 cur[3] == 's' &&
1009 cur[4] == 'e' )
1010 {
1011 result = 0;
1012 cur += 6;
1013 }
1014
1015 *acur = cur;
1016 return result;
1017 }
1018
1019
1020 /* load a simple field (i.e. non-table) into the current list of objects */
1021
1022 FT_LOCAL_DEF( FT_Error )
1023 ps_parser_load_field( PS_Parser parser,
1024 const T1_Field field,
1025 void** objects,
1026 FT_UInt max_objects,
1027 FT_ULong* pflags )
1028 {
1029 T1_TokenRec token;
1030 FT_Byte* cur;
1031 FT_Byte* limit;
1032 FT_UInt count;
1033 FT_UInt idx;
1034 FT_Error error;
1035 T1_FieldType type;
1036
1037
1038 /* this also skips leading whitespace */
1039 ps_parser_to_token( parser, &token );
1040 if ( !token.type )
1041 goto Fail;
1042
1043 count = 1;
1044 idx = 0;
1045 cur = token.start;
1046 limit = token.limit;
1047
1048 type = field->type;
1049
1050 /* we must detect arrays in /FontBBox */
1051 if ( type == T1_FIELD_TYPE_BBOX )
1052 {
1053 T1_TokenRec token2;
1054 FT_Byte* old_cur = parser->cursor;
1055 FT_Byte* old_limit = parser->limit;
1056
1057
1058 /* don't include delimiters */
1059 parser->cursor = token.start + 1;
1060 parser->limit = token.limit - 1;
1061
1062 ps_parser_to_token( parser, &token2 );
1063 parser->cursor = old_cur;
1064 parser->limit = old_limit;
1065
1066 if ( token2.type == T1_TOKEN_TYPE_ARRAY )
1067 {
1068 type = T1_FIELD_TYPE_MM_BBOX;
1069 goto FieldArray;
1070 }
1071 }
1072 else if ( token.type == T1_TOKEN_TYPE_ARRAY )
1073 {
1074 count = max_objects;
1075
1076 FieldArray:
1077 /* if this is an array and we have no blend, an error occurs */
1078 if ( max_objects == 0 )
1079 goto Fail;
1080
1081 idx = 1;
1082
1083 /* don't include delimiters */
1084 cur++;
1085 limit--;
1086 }
1087
1088 for ( ; count > 0; count--, idx++ )
1089 {
1090 FT_Byte* q = (FT_Byte*)objects[idx] + field->offset;
1091 FT_Long val;
1092 FT_String* string;
1093
1094
1095 skip_spaces( &cur, limit );
1096
1097 switch ( type )
1098 {
1099 case T1_FIELD_TYPE_BOOL:
1100 val = ps_tobool( &cur, limit );
1101 goto Store_Integer;
1102
1103 case T1_FIELD_TYPE_FIXED:
1104 val = PS_Conv_ToFixed( &cur, limit, 0 );
1105 goto Store_Integer;
1106
1107 case T1_FIELD_TYPE_FIXED_1000:
1108 val = PS_Conv_ToFixed( &cur, limit, 3 );
1109 goto Store_Integer;
1110
1111 case T1_FIELD_TYPE_INTEGER:
1112 val = PS_Conv_ToInt( &cur, limit );
1113 /* fall through */
1114
1115 Store_Integer:
1116 switch ( field->size )
1117 {
1118 case (8 / FT_CHAR_BIT):
1119 *(FT_Byte*)q = (FT_Byte)val;
1120 break;
1121
1122 case (16 / FT_CHAR_BIT):
1123 *(FT_UShort*)q = (FT_UShort)val;
1124 break;
1125
1126 case (32 / FT_CHAR_BIT):
1127 *(FT_UInt32*)q = (FT_UInt32)val;
1128 break;
1129
1130 default: /* for 64-bit systems */
1131 *(FT_Long*)q = val;
1132 }
1133 break;
1134
1135 case T1_FIELD_TYPE_STRING:
1136 case T1_FIELD_TYPE_KEY:
1137 {
1138 FT_Memory memory = parser->memory;
1139 FT_UInt len = (FT_UInt)( limit - cur );
1140
1141
1142 if ( cur >= limit )
1143 break;
1144
1145 /* we allow both a string or a name */
1146 /* for cases like /FontName (foo) def */
1147 if ( token.type == T1_TOKEN_TYPE_KEY )
1148 {
1149 /* don't include leading `/' */
1150 len--;
1151 cur++;
1152 }
1153 else if ( token.type == T1_TOKEN_TYPE_STRING )
1154 {
1155 /* don't include delimiting parentheses */
1156 /* XXX we don't handle <<...>> here */
1157 /* XXX should we convert octal escapes? */
1158 /* if so, what encoding should we use? */
1159 cur++;
1160 len -= 2;
1161 }
1162 else
1163 {
1164 FT_ERROR(( "ps_parser_load_field:"
1165 " expected a name or string\n"
1166 " "
1167 " but found token of type %d instead\n",
1168 token.type ));
1169 error = FT_THROW( Invalid_File_Format );
1170 goto Exit;
1171 }
1172
1173 /* for this to work (FT_String**)q must have been */
1174 /* initialized to NULL */
1175 if ( *(FT_String**)q != NULL )
1176 {
1177 FT_TRACE0(( "ps_parser_load_field: overwriting field %s\n",
1178 field->ident ));
1179 FT_FREE( *(FT_String**)q );
1180 *(FT_String**)q = NULL;
1181 }
1182
1183 if ( FT_ALLOC( string, len + 1 ) )
1184 goto Exit;
1185
1186 FT_MEM_COPY( string, cur, len );
1187 string[len] = 0;
1188
1189 *(FT_String**)q = string;
1190 }
1191 break;
1192
1193 case T1_FIELD_TYPE_BBOX:
1194 {
1195 FT_Fixed temp[4];
1196 FT_BBox* bbox = (FT_BBox*)q;
1197 FT_Int result;
1198
1199
1200 result = ps_tofixedarray( &cur, limit, 4, temp, 0 );
1201
1202 if ( result < 4 )
1203 {
1204 FT_ERROR(( "ps_parser_load_field:"
1205 " expected four integers in bounding box\n" ));
1206 error = FT_THROW( Invalid_File_Format );
1207 goto Exit;
1208 }
1209
1210 bbox->xMin = FT_RoundFix( temp[0] );
1211 bbox->yMin = FT_RoundFix( temp[1] );
1212 bbox->xMax = FT_RoundFix( temp[2] );
1213 bbox->yMax = FT_RoundFix( temp[3] );
1214 }
1215 break;
1216
1217 case T1_FIELD_TYPE_MM_BBOX:
1218 {
1219 FT_Memory memory = parser->memory;
1220 FT_Fixed* temp;
1221 FT_Int result;
1222 FT_UInt i;
1223
1224
1225 if ( FT_NEW_ARRAY( temp, max_objects * 4 ) )
1226 goto Exit;
1227
1228 for ( i = 0; i < 4; i++ )
1229 {
1230 result = ps_tofixedarray( &cur, limit, (FT_Int)max_objects,
1231 temp + i * max_objects, 0 );
1232 if ( result < 0 || (FT_UInt)result < max_objects )
1233 {
1234 FT_ERROR(( "ps_parser_load_field:"
1235 " expected %d integer%s in the %s subarray\n"
1236 " "
1237 " of /FontBBox in the /Blend dictionary\n",
1238 max_objects, max_objects > 1 ? "s" : "",
1239 i == 0 ? "first"
1240 : ( i == 1 ? "second"
1241 : ( i == 2 ? "third"
1242 : "fourth" ) ) ));
1243 error = FT_THROW( Invalid_File_Format );
1244
1245 FT_FREE( temp );
1246 goto Exit;
1247 }
1248
1249 skip_spaces( &cur, limit );
1250 }
1251
1252 for ( i = 0; i < max_objects; i++ )
1253 {
1254 FT_BBox* bbox = (FT_BBox*)objects[i];
1255
1256
1257 bbox->xMin = FT_RoundFix( temp[i ] );
1258 bbox->yMin = FT_RoundFix( temp[i + max_objects] );
1259 bbox->xMax = FT_RoundFix( temp[i + 2 * max_objects] );
1260 bbox->yMax = FT_RoundFix( temp[i + 3 * max_objects] );
1261 }
1262
1263 FT_FREE( temp );
1264 }
1265 break;
1266
1267 default:
1268 /* an error occurred */
1269 goto Fail;
1270 }
1271 }
1272
1273 #if 0 /* obsolete -- keep for reference */
1274 if ( pflags )
1275 *pflags |= 1L << field->flag_bit;
1276 #else
1277 FT_UNUSED( pflags );
1278 #endif
1279
1280 error = FT_Err_Ok;
1281
1282 Exit:
1283 return error;
1284
1285 Fail:
1286 error = FT_THROW( Invalid_File_Format );
1287 goto Exit;
1288 }
1289
1290
1291 #define T1_MAX_TABLE_ELEMENTS 32
1292
1293
1294 FT_LOCAL_DEF( FT_Error )
1295 ps_parser_load_field_table( PS_Parser parser,
1296 const T1_Field field,
1297 void** objects,
1298 FT_UInt max_objects,
1299 FT_ULong* pflags )
1300 {
1301 T1_TokenRec elements[T1_MAX_TABLE_ELEMENTS];
1302 T1_Token token;
1303 FT_Int num_elements;
1304 FT_Error error = FT_Err_Ok;
1305 FT_Byte* old_cursor;
1306 FT_Byte* old_limit;
1307 T1_FieldRec fieldrec = *(T1_Field)field;
1308
1309
1310 fieldrec.type = T1_FIELD_TYPE_INTEGER;
1311 if ( field->type == T1_FIELD_TYPE_FIXED_ARRAY ||
1312 field->type == T1_FIELD_TYPE_BBOX )
1313 fieldrec.type = T1_FIELD_TYPE_FIXED;
1314
1315 ps_parser_to_token_array( parser, elements,
1316 T1_MAX_TABLE_ELEMENTS, &num_elements );
1317 if ( num_elements < 0 )
1318 {
1319 error = FT_ERR( Ignore );
1320 goto Exit;
1321 }
1322 if ( (FT_UInt)num_elements > field->array_max )
1323 num_elements = (FT_Int)field->array_max;
1324
1325 old_cursor = parser->cursor;
1326 old_limit = parser->limit;
1327
1328 /* we store the elements count if necessary; */
1329 /* we further assume that `count_offset' can't be zero */
1330 if ( field->type != T1_FIELD_TYPE_BBOX && field->count_offset != 0 )
1331 *(FT_Byte*)( (FT_Byte*)objects[0] + field->count_offset ) =
1332 (FT_Byte)num_elements;
1333
1334 /* we now load each element, adjusting the field.offset on each one */
1335 token = elements;
1336 for ( ; num_elements > 0; num_elements--, token++ )
1337 {
1338 parser->cursor = token->start;
1339 parser->limit = token->limit;
1340
1341 error = ps_parser_load_field( parser,
1342 &fieldrec,
1343 objects,
1344 max_objects,
1345 0 );
1346 if ( error )
1347 break;
1348
1349 fieldrec.offset += fieldrec.size;
1350 }
1351
1352 #if 0 /* obsolete -- keep for reference */
1353 if ( pflags )
1354 *pflags |= 1L << field->flag_bit;
1355 #else
1356 FT_UNUSED( pflags );
1357 #endif
1358
1359 parser->cursor = old_cursor;
1360 parser->limit = old_limit;
1361
1362 Exit:
1363 return error;
1364 }
1365
1366
1367 FT_LOCAL_DEF( FT_Long )
1368 ps_parser_to_int( PS_Parser parser )
1369 {
1370 ps_parser_skip_spaces( parser );
1371 return PS_Conv_ToInt( &parser->cursor, parser->limit );
1372 }
1373
1374
1375 /* first character must be `<' if `delimiters' is non-zero */
1376
1377 FT_LOCAL_DEF( FT_Error )
1378 ps_parser_to_bytes( PS_Parser parser,
1379 FT_Byte* bytes,
1380 FT_Offset max_bytes,
1381 FT_ULong* pnum_bytes,
1382 FT_Bool delimiters )
1383 {
1384 FT_Error error = FT_Err_Ok;
1385 FT_Byte* cur;
1386
1387
1388 ps_parser_skip_spaces( parser );
1389 cur = parser->cursor;
1390
1391 if ( cur >= parser->limit )
1392 goto Exit;
1393
1394 if ( delimiters )
1395 {
1396 if ( *cur != '<' )
1397 {
1398 FT_ERROR(( "ps_parser_to_bytes: Missing starting delimiter `<'\n" ));
1399 error = FT_THROW( Invalid_File_Format );
1400 goto Exit;
1401 }
1402
1403 cur++;
1404 }
1405
1406 *pnum_bytes = PS_Conv_ASCIIHexDecode( &cur,
1407 parser->limit,
1408 bytes,
1409 max_bytes );
1410
1411 if ( delimiters )
1412 {
1413 if ( cur < parser->limit && *cur != '>' )
1414 {
1415 FT_ERROR(( "ps_parser_to_bytes: Missing closing delimiter `>'\n" ));
1416 error = FT_THROW( Invalid_File_Format );
1417 goto Exit;
1418 }
1419
1420 cur++;
1421 }
1422
1423 parser->cursor = cur;
1424
1425 Exit:
1426 return error;
1427 }
1428
1429
1430 FT_LOCAL_DEF( FT_Fixed )
1431 ps_parser_to_fixed( PS_Parser parser,
1432 FT_Int power_ten )
1433 {
1434 ps_parser_skip_spaces( parser );
1435 return PS_Conv_ToFixed( &parser->cursor, parser->limit, power_ten );
1436 }
1437
1438
1439 FT_LOCAL_DEF( FT_Int )
1440 ps_parser_to_coord_array( PS_Parser parser,
1441 FT_Int max_coords,
1442 FT_Short* coords )
1443 {
1444 ps_parser_skip_spaces( parser );
1445 return ps_tocoordarray( &parser->cursor, parser->limit,
1446 max_coords, coords );
1447 }
1448
1449
1450 FT_LOCAL_DEF( FT_Int )
1451 ps_parser_to_fixed_array( PS_Parser parser,
1452 FT_Int max_values,
1453 FT_Fixed* values,
1454 FT_Int power_ten )
1455 {
1456 ps_parser_skip_spaces( parser );
1457 return ps_tofixedarray( &parser->cursor, parser->limit,
1458 max_values, values, power_ten );
1459 }
1460
1461
1462 #if 0
1463
1464 FT_LOCAL_DEF( FT_String* )
1465 T1_ToString( PS_Parser parser )
1466 {
1467 return ps_tostring( &parser->cursor, parser->limit, parser->memory );
1468 }
1469
1470
1471 FT_LOCAL_DEF( FT_Bool )
1472 T1_ToBool( PS_Parser parser )
1473 {
1474 return ps_tobool( &parser->cursor, parser->limit );
1475 }
1476
1477 #endif /* 0 */
1478
1479
1480 FT_LOCAL_DEF( void )
1481 ps_parser_init( PS_Parser parser,
1482 FT_Byte* base,
1483 FT_Byte* limit,
1484 FT_Memory memory )
1485 {
1486 parser->error = FT_Err_Ok;
1487 parser->base = base;
1488 parser->limit = limit;
1489 parser->cursor = base;
1490 parser->memory = memory;
1491 parser->funcs = ps_parser_funcs;
1492 }
1493
1494
1495 FT_LOCAL_DEF( void )
1496 ps_parser_done( PS_Parser parser )
1497 {
1498 FT_UNUSED( parser );
1499 }
1500
1501
1502 /*************************************************************************/
1503 /*************************************************************************/
1504 /***** *****/
1505 /***** T1 BUILDER *****/
1506 /***** *****/
1507 /*************************************************************************/
1508 /*************************************************************************/
1509
1510 /*************************************************************************/
1511 /* */
1512 /* <Function> */
1513 /* t1_builder_init */
1514 /* */
1515 /* <Description> */
1516 /* Initializes a given glyph builder. */
1517 /* */
1518 /* <InOut> */
1519 /* builder :: A pointer to the glyph builder to initialize. */
1520 /* */
1521 /* <Input> */
1522 /* face :: The current face object. */
1523 /* */
1524 /* size :: The current size object. */
1525 /* */
1526 /* glyph :: The current glyph object. */
1527 /* */
1528 /* hinting :: Whether hinting should be applied. */
1529 /* */
1530 FT_LOCAL_DEF( void )
1531 t1_builder_init( T1_Builder builder,
1532 FT_Face face,
1533 FT_Size size,
1534 FT_GlyphSlot glyph,
1535 FT_Bool hinting )
1536 {
1537 builder->parse_state = T1_Parse_Start;
1538 builder->load_points = 1;
1539
1540 builder->face = face;
1541 builder->glyph = glyph;
1542 builder->memory = face->memory;
1543
1544 if ( glyph )
1545 {
1546 FT_GlyphLoader loader = glyph->internal->loader;
1547
1548
1549 builder->loader = loader;
1550 builder->base = &loader->base.outline;
1551 builder->current = &loader->current.outline;
1552 FT_GlyphLoader_Rewind( loader );
1553
1554 builder->hints_globals = size->internal;
1555 builder->hints_funcs = NULL;
1556
1557 if ( hinting )
1558 builder->hints_funcs = glyph->internal->glyph_hints;
1559 }
1560
1561 builder->pos_x = 0;
1562 builder->pos_y = 0;
1563
1564 builder->left_bearing.x = 0;
1565 builder->left_bearing.y = 0;
1566 builder->advance.x = 0;
1567 builder->advance.y = 0;
1568
1569 builder->funcs = t1_builder_funcs;
1570 }
1571
1572
1573 /*************************************************************************/
1574 /* */
1575 /* <Function> */
1576 /* t1_builder_done */
1577 /* */
1578 /* <Description> */
1579 /* Finalizes a given glyph builder. Its contents can still be used */
1580 /* after the call, but the function saves important information */
1581 /* within the corresponding glyph slot. */
1582 /* */
1583 /* <Input> */
1584 /* builder :: A pointer to the glyph builder to finalize. */
1585 /* */
1586 FT_LOCAL_DEF( void )
1587 t1_builder_done( T1_Builder builder )
1588 {
1589 FT_GlyphSlot glyph = builder->glyph;
1590
1591
1592 if ( glyph )
1593 glyph->outline = *builder->base;
1594 }
1595
1596
1597 /* check that there is enough space for `count' more points */
1598 FT_LOCAL_DEF( FT_Error )
1599 t1_builder_check_points( T1_Builder builder,
1600 FT_Int count )
1601 {
1602 return FT_GLYPHLOADER_CHECK_POINTS( builder->loader, count, 0 );
1603 }
1604
1605
1606 /* add a new point, do not check space */
1607 FT_LOCAL_DEF( void )
1608 t1_builder_add_point( T1_Builder builder,
1609 FT_Pos x,
1610 FT_Pos y,
1611 FT_Byte flag )
1612 {
1613 FT_Outline* outline = builder->current;
1614
1615
1616 if ( builder->load_points )
1617 {
1618 FT_Vector* point = outline->points + outline->n_points;
1619 FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points;
1620
1621
1622 point->x = FIXED_TO_INT( x );
1623 point->y = FIXED_TO_INT( y );
1624 *control = (FT_Byte)( flag ? FT_CURVE_TAG_ON : FT_CURVE_TAG_CUBIC );
1625 }
1626 outline->n_points++;
1627 }
1628
1629
1630 /* check space for a new on-curve point, then add it */
1631 FT_LOCAL_DEF( FT_Error )
1632 t1_builder_add_point1( T1_Builder builder,
1633 FT_Pos x,
1634 FT_Pos y )
1635 {
1636 FT_Error error;
1637
1638
1639 error = t1_builder_check_points( builder, 1 );
1640 if ( !error )
1641 t1_builder_add_point( builder, x, y, 1 );
1642
1643 return error;
1644 }
1645
1646
1647 /* check space for a new contour, then add it */
1648 FT_LOCAL_DEF( FT_Error )
1649 t1_builder_add_contour( T1_Builder builder )
1650 {
1651 FT_Outline* outline = builder->current;
1652 FT_Error error;
1653
1654
1655 /* this might happen in invalid fonts */
1656 if ( !outline )
1657 {
1658 FT_ERROR(( "t1_builder_add_contour: no outline to add points to\n" ));
1659 return FT_THROW( Invalid_File_Format );
1660 }
1661
1662 if ( !builder->load_points )
1663 {
1664 outline->n_contours++;
1665 return FT_Err_Ok;
1666 }
1667
1668 error = FT_GLYPHLOADER_CHECK_POINTS( builder->loader, 0, 1 );
1669 if ( !error )
1670 {
1671 if ( outline->n_contours > 0 )
1672 outline->contours[outline->n_contours - 1] =
1673 (short)( outline->n_points - 1 );
1674
1675 outline->n_contours++;
1676 }
1677
1678 return error;
1679 }
1680
1681
1682 /* if a path was begun, add its first on-curve point */
1683 FT_LOCAL_DEF( FT_Error )
1684 t1_builder_start_point( T1_Builder builder,
1685 FT_Pos x,
1686 FT_Pos y )
1687 {
1688 FT_Error error = FT_ERR( Invalid_File_Format );
1689
1690
1691 /* test whether we are building a new contour */
1692
1693 if ( builder->parse_state == T1_Parse_Have_Path )
1694 error = FT_Err_Ok;
1695 else
1696 {
1697 builder->parse_state = T1_Parse_Have_Path;
1698 error = t1_builder_add_contour( builder );
1699 if ( !error )
1700 error = t1_builder_add_point1( builder, x, y );
1701 }
1702
1703 return error;
1704 }
1705
1706
1707 /* close the current contour */
1708 FT_LOCAL_DEF( void )
1709 t1_builder_close_contour( T1_Builder builder )
1710 {
1711 FT_Outline* outline = builder->current;
1712 FT_Int first;
1713
1714
1715 if ( !outline )
1716 return;
1717
1718 first = outline->n_contours <= 1
1719 ? 0 : outline->contours[outline->n_contours - 2] + 1;
1720
1721 /* We must not include the last point in the path if it */
1722 /* is located on the first point. */
1723 if ( outline->n_points > 1 )
1724 {
1725 FT_Vector* p1 = outline->points + first;
1726 FT_Vector* p2 = outline->points + outline->n_points - 1;
1727 FT_Byte* control = (FT_Byte*)outline->tags + outline->n_points - 1;
1728
1729
1730 /* `delete' last point only if it coincides with the first */
1731 /* point and it is not a control point (which can happen). */
1732 if ( p1->x == p2->x && p1->y == p2->y )
1733 if ( *control == FT_CURVE_TAG_ON )
1734 outline->n_points--;
1735 }
1736
1737 if ( outline->n_contours > 0 )
1738 {
1739 /* Don't add contours only consisting of one point, i.e., */
1740 /* check whether the first and the last point is the same. */
1741 if ( first == outline->n_points - 1 )
1742 {
1743 outline->n_contours--;
1744 outline->n_points--;
1745 }
1746 else
1747 outline->contours[outline->n_contours - 1] =
1748 (short)( outline->n_points - 1 );
1749 }
1750 }
1751
1752
1753 /*************************************************************************/
1754 /*************************************************************************/
1755 /***** *****/
1756 /***** OTHER *****/
1757 /***** *****/
1758 /*************************************************************************/
1759 /*************************************************************************/
1760
1761 FT_LOCAL_DEF( void )
1762 t1_decrypt( FT_Byte* buffer,
1763 FT_Offset length,
1764 FT_UShort seed )
1765 {
1766 PS_Conv_EexecDecode( &buffer,
1767 buffer + length,
1768 buffer,
1769 length,
1770 &seed );
1771 }
1772
1773
1774 /* END */