1/****************************************************************************
2 *
3 * pfrgload.c
4 *
5 * FreeType PFR glyph loader (body).
6 *
7 * Copyright (C) 2002-2023 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 "pfrgload.h"
20#include "pfrsbit.h"
21#include "pfrload.h" /* for macro definitions */
22#include <freetype/internal/ftdebug.h>
23
24#include "pfrerror.h"
25
26#undef FT_COMPONENT
27#define FT_COMPONENT pfr
28
29
30 /*************************************************************************/
31 /*************************************************************************/
32 /***** *****/
33 /***** PFR GLYPH BUILDER *****/
34 /***** *****/
35 /*************************************************************************/
36 /*************************************************************************/
37
38
39 FT_LOCAL_DEF( void )
40 pfr_glyph_init( PFR_Glyph glyph,
41 FT_GlyphLoader loader )
42 {
43 FT_ZERO( glyph );
44
45 glyph->loader = loader;
46
47 FT_GlyphLoader_Rewind( loader );
48 }
49
50
51 FT_LOCAL_DEF( void )
52 pfr_glyph_done( PFR_Glyph glyph )
53 {
54 FT_Memory memory = glyph->loader->memory;
55
56
57 FT_FREE( glyph->x_control );
58 glyph->y_control = NULL;
59
60 glyph->max_xy_control = 0;
61#if 0
62 glyph->num_x_control = 0;
63 glyph->num_y_control = 0;
64#endif
65
66 FT_FREE( glyph->subs );
67
68 glyph->max_subs = 0;
69 glyph->num_subs = 0;
70
71 glyph->loader = NULL;
72 glyph->path_begun = 0;
73 }
74
75
76 /* close current contour, if any */
77 static void
78 pfr_glyph_close_contour( PFR_Glyph glyph )
79 {
80 FT_GlyphLoader loader = glyph->loader;
81 FT_Outline* outline = &loader->current.outline;
82 FT_Int last, first;
83
84
85 if ( !glyph->path_begun )
86 return;
87
88 /* compute first and last point indices in current glyph outline */
89 last = outline->n_points - 1;
90 first = 0;
91 if ( outline->n_contours > 0 )
92 first = outline->contours[outline->n_contours - 1];
93
94 /* if the last point falls on the same location as the first one */
95 /* we need to delete it */
96 if ( last > first )
97 {
98 FT_Vector* p1 = outline->points + first;
99 FT_Vector* p2 = outline->points + last;
100
101
102 if ( p1->x == p2->x && p1->y == p2->y )
103 {
104 outline->n_points--;
105 last--;
106 }
107 }
108
109 /* don't add empty contours */
110 if ( last >= first )
111 outline->contours[outline->n_contours++] = (short)last;
112
113 glyph->path_begun = 0;
114 }
115
116
117 /* reset glyph to start the loading of a new glyph */
118 static void
119 pfr_glyph_start( PFR_Glyph glyph )
120 {
121 glyph->path_begun = 0;
122 }
123
124
125 static FT_Error
126 pfr_glyph_line_to( PFR_Glyph glyph,
127 FT_Vector* to )
128 {
129 FT_GlyphLoader loader = glyph->loader;
130 FT_Outline* outline = &loader->current.outline;
131 FT_Error error;
132
133
134 /* check that we have begun a new path */
135 if ( !glyph->path_begun )
136 {
137 error = FT_THROW( Invalid_Table );
138 FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" ));
139 goto Exit;
140 }
141
142 error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 0 );
143 if ( !error )
144 {
145 FT_Int n = outline->n_points;
146
147
148 outline->points[n] = *to;
149 outline->tags [n] = FT_CURVE_TAG_ON;
150
151 outline->n_points++;
152 }
153
154 Exit:
155 return error;
156 }
157
158
159 static FT_Error
160 pfr_glyph_curve_to( PFR_Glyph glyph,
161 FT_Vector* control1,
162 FT_Vector* control2,
163 FT_Vector* to )
164 {
165 FT_GlyphLoader loader = glyph->loader;
166 FT_Outline* outline = &loader->current.outline;
167 FT_Error error;
168
169
170 /* check that we have begun a new path */
171 if ( !glyph->path_begun )
172 {
173 error = FT_THROW( Invalid_Table );
174 FT_ERROR(( "pfr_glyph_line_to: invalid glyph data\n" ));
175 goto Exit;
176 }
177
178 error = FT_GLYPHLOADER_CHECK_POINTS( loader, 3, 0 );
179 if ( !error )
180 {
181 FT_Vector* vec = outline->points + outline->n_points;
182 FT_Byte* tag = (FT_Byte*)outline->tags + outline->n_points;
183
184
185 vec[0] = *control1;
186 vec[1] = *control2;
187 vec[2] = *to;
188 tag[0] = FT_CURVE_TAG_CUBIC;
189 tag[1] = FT_CURVE_TAG_CUBIC;
190 tag[2] = FT_CURVE_TAG_ON;
191
192 outline->n_points = (FT_Short)( outline->n_points + 3 );
193 }
194
195 Exit:
196 return error;
197 }
198
199
200 static FT_Error
201 pfr_glyph_move_to( PFR_Glyph glyph,
202 FT_Vector* to )
203 {
204 FT_GlyphLoader loader = glyph->loader;
205 FT_Error error;
206
207
208 /* close current contour if any */
209 pfr_glyph_close_contour( glyph );
210
211 /* indicate that a new contour has started */
212 glyph->path_begun = 1;
213
214 /* check that there is space for a new contour and a new point */
215 error = FT_GLYPHLOADER_CHECK_POINTS( loader, 1, 1 );
216 if ( !error )
217 {
218 /* add new start point */
219 error = pfr_glyph_line_to( glyph, to );
220 }
221
222 return error;
223 }
224
225
226 static void
227 pfr_glyph_end( PFR_Glyph glyph )
228 {
229 /* close current contour if any */
230 pfr_glyph_close_contour( glyph );
231
232 /* merge the current glyph into the stack */
233 FT_GlyphLoader_Add( glyph->loader );
234 }
235
236
237 /*************************************************************************/
238 /*************************************************************************/
239 /***** *****/
240 /***** PFR GLYPH LOADER *****/
241 /***** *****/
242 /*************************************************************************/
243 /*************************************************************************/
244
245
246 /* load a simple glyph */
247 static FT_Error
248 pfr_glyph_load_simple( PFR_Glyph glyph,
249 FT_Byte* p,
250 FT_Byte* limit )
251 {
252 FT_Error error = FT_Err_Ok;
253 FT_Memory memory = glyph->loader->memory;
254 FT_UInt flags, x_count, y_count, i, count, mask;
255 FT_Int x;
256
257
258 PFR_CHECK( 1 );
259 flags = PFR_NEXT_BYTE( p );
260
261 /* test for composite glyphs */
262 if ( flags & PFR_GLYPH_IS_COMPOUND )
263 goto Failure;
264
265 x_count = 0;
266 y_count = 0;
267
268 if ( flags & PFR_GLYPH_1BYTE_XYCOUNT )
269 {
270 PFR_CHECK( 1 );
271 count = PFR_NEXT_BYTE( p );
272 x_count = count & 15;
273 y_count = count >> 4;
274 }
275 else
276 {
277 if ( flags & PFR_GLYPH_XCOUNT )
278 {
279 PFR_CHECK( 1 );
280 x_count = PFR_NEXT_BYTE( p );
281 }
282
283 if ( flags & PFR_GLYPH_YCOUNT )
284 {
285 PFR_CHECK( 1 );
286 y_count = PFR_NEXT_BYTE( p );
287 }
288 }
289
290 count = x_count + y_count;
291
292 /* re-allocate array when necessary */
293 if ( count > glyph->max_xy_control )
294 {
295 FT_UInt new_max = FT_PAD_CEIL( count, 8 );
296
297
298 if ( FT_RENEW_ARRAY( glyph->x_control,
299 glyph->max_xy_control,
300 new_max ) )
301 goto Exit;
302
303 glyph->max_xy_control = new_max;
304 }
305
306 glyph->y_control = glyph->x_control + x_count;
307
308 mask = 0;
309 x = 0;
310
311 for ( i = 0; i < count; i++ )
312 {
313 if ( ( i & 7 ) == 0 )
314 {
315 PFR_CHECK( 1 );
316 mask = PFR_NEXT_BYTE( p );
317 }
318
319 if ( mask & 1 )
320 {
321 PFR_CHECK( 2 );
322 x = PFR_NEXT_SHORT( p );
323 }
324 else
325 {
326 PFR_CHECK( 1 );
327 x += PFR_NEXT_BYTE( p );
328 }
329
330 glyph->x_control[i] = x;
331
332 mask >>= 1;
333 }
334
335 /* XXX: we ignore the secondary stroke and edge definitions */
336 /* since we don't support native PFR hinting */
337 /* */
338 if ( flags & PFR_GLYPH_SINGLE_EXTRA_ITEMS )
339 {
340 error = pfr_extra_items_skip( &p, limit );
341 if ( error )
342 goto Exit;
343 }
344
345 pfr_glyph_start( glyph );
346
347 /* now load a simple glyph */
348 {
349 FT_Vector pos[4];
350 FT_Vector* cur;
351
352
353 pos[0].x = pos[0].y = 0;
354 pos[3] = pos[0];
355
356 for (;;)
357 {
358 FT_UInt format, format_low, args_format = 0, args_count, n;
359
360
361 /****************************************************************
362 * read instruction
363 */
364 PFR_CHECK( 1 );
365 format = PFR_NEXT_BYTE( p );
366 format_low = format & 15;
367
368 switch ( format >> 4 )
369 {
370 case 0: /* end glyph */
371 FT_TRACE6(( "- end glyph" ));
372 args_count = 0;
373 break;
374
375 case 1: /* general line operation */
376 FT_TRACE6(( "- general line" ));
377 goto Line1;
378
379 case 4: /* move to inside contour */
380 FT_TRACE6(( "- move to inside" ));
381 goto Line1;
382
383 case 5: /* move to outside contour */
384 FT_TRACE6(( "- move to outside" ));
385 Line1:
386 args_format = format_low;
387 args_count = 1;
388 break;
389
390 case 2: /* horizontal line to */
391 FT_TRACE6(( "- horizontal line to cx.%d", format_low ));
392 if ( format_low >= x_count )
393 goto Failure;
394 pos[0].x = glyph->x_control[format_low];
395 pos[0].y = pos[3].y;
396 pos[3] = pos[0];
397 args_count = 0;
398 break;
399
400 case 3: /* vertical line to */
401 FT_TRACE6(( "- vertical line to cy.%d", format_low ));
402 if ( format_low >= y_count )
403 goto Failure;
404 pos[0].x = pos[3].x;
405 pos[0].y = glyph->y_control[format_low];
406 pos[3] = pos[0];
407 args_count = 0;
408 break;
409
410 case 6: /* horizontal to vertical curve */
411 FT_TRACE6(( "- hv curve" ));
412 args_format = 0xB8E;
413 args_count = 3;
414 break;
415
416 case 7: /* vertical to horizontal curve */
417 FT_TRACE6(( "- vh curve" ));
418 args_format = 0xE2B;
419 args_count = 3;
420 break;
421
422 default: /* general curve to */
423 FT_TRACE6(( "- general curve" ));
424 args_count = 4;
425 args_format = format_low;
426 }
427
428 /************************************************************
429 * now read arguments
430 */
431 cur = pos;
432 for ( n = 0; n < args_count; n++ )
433 {
434 FT_UInt idx;
435 FT_Int delta;
436
437
438 /* read the X argument */
439 switch ( args_format & 3 )
440 {
441 case 0: /* 8-bit index */
442 PFR_CHECK( 1 );
443 idx = PFR_NEXT_BYTE( p );
444 if ( idx >= x_count )
445 goto Failure;
446 cur->x = glyph->x_control[idx];
447 FT_TRACE7(( " cx#%d", idx ));
448 break;
449
450 case 1: /* 16-bit absolute value */
451 PFR_CHECK( 2 );
452 cur->x = PFR_NEXT_SHORT( p );
453 FT_TRACE7(( " x.%ld", cur->x ));
454 break;
455
456 case 2: /* 8-bit delta */
457 PFR_CHECK( 1 );
458 delta = PFR_NEXT_INT8( p );
459 cur->x = pos[3].x + delta;
460 FT_TRACE7(( " dx.%d", delta ));
461 break;
462
463 default:
464 FT_TRACE7(( " |" ));
465 cur->x = pos[3].x;
466 }
467
468 /* read the Y argument */
469 switch ( ( args_format >> 2 ) & 3 )
470 {
471 case 0: /* 8-bit index */
472 PFR_CHECK( 1 );
473 idx = PFR_NEXT_BYTE( p );
474 if ( idx >= y_count )
475 goto Failure;
476 cur->y = glyph->y_control[idx];
477 FT_TRACE7(( " cy#%d", idx ));
478 break;
479
480 case 1: /* 16-bit absolute value */
481 PFR_CHECK( 2 );
482 cur->y = PFR_NEXT_SHORT( p );
483 FT_TRACE7(( " y.%ld", cur->y ));
484 break;
485
486 case 2: /* 8-bit delta */
487 PFR_CHECK( 1 );
488 delta = PFR_NEXT_INT8( p );
489 cur->y = pos[3].y + delta;
490 FT_TRACE7(( " dy.%d", delta ));
491 break;
492
493 default:
494 FT_TRACE7(( " -" ));
495 cur->y = pos[3].y;
496 }
497
498 /* read the additional format flag for the general curve */
499 if ( n == 0 && args_count == 4 )
500 {
501 PFR_CHECK( 1 );
502 args_format = PFR_NEXT_BYTE( p );
503 args_count--;
504 }
505 else
506 args_format >>= 4;
507
508 /* save the previous point */
509 pos[3] = cur[0];
510 cur++;
511 }
512
513 FT_TRACE7(( "\n" ));
514
515 /************************************************************
516 * finally, execute instruction
517 */
518 switch ( format >> 4 )
519 {
520 case 0: /* end glyph => EXIT */
521 pfr_glyph_end( glyph );
522 goto Exit;
523
524 case 1: /* line operations */
525 case 2:
526 case 3:
527 error = pfr_glyph_line_to( glyph, pos );
528 goto Test_Error;
529
530 case 4: /* move to inside contour */
531 case 5: /* move to outside contour */
532 error = pfr_glyph_move_to( glyph, pos );
533 goto Test_Error;
534
535 default: /* curve operations */
536 error = pfr_glyph_curve_to( glyph, pos, pos + 1, pos + 2 );
537
538 Test_Error: /* test error condition */
539 if ( error )
540 goto Exit;
541 }
542 } /* for (;;) */
543 }
544
545 Exit:
546 return error;
547
548 Failure:
549 Too_Short:
550 error = FT_THROW( Invalid_Table );
551 FT_ERROR(( "pfr_glyph_load_simple: invalid glyph data\n" ));
552 goto Exit;
553 }
554
555
556 /* load a composite/compound glyph */
557 static FT_Error
558 pfr_glyph_load_compound( PFR_Glyph glyph,
559 FT_Byte* p,
560 FT_Byte* limit )
561 {
562 FT_Error error = FT_Err_Ok;
563 FT_Memory memory = glyph->loader->memory;
564 PFR_SubGlyph subglyph;
565 FT_UInt flags, i, count, org_count;
566 FT_Int x_pos, y_pos;
567
568
569 PFR_CHECK( 1 );
570 flags = PFR_NEXT_BYTE( p );
571
572 /* test for composite glyphs */
573 if ( !( flags & PFR_GLYPH_IS_COMPOUND ) )
574 goto Failure;
575
576 count = flags & 0x3F;
577
578 /* ignore extra items when present */
579 /* */
580 if ( flags & PFR_GLYPH_COMPOUND_EXTRA_ITEMS )
581 {
582 error = pfr_extra_items_skip( &p, limit );
583 if ( error )
584 goto Exit;
585 }
586
587 /* we can't rely on the FT_GlyphLoader to load sub-glyphs, because */
588 /* the PFR format is dumb, using direct file offsets to point to the */
589 /* sub-glyphs (instead of glyph indices). Sigh. */
590 /* */
591 /* For now, we load the list of sub-glyphs into a different array */
592 /* but this will prevent us from using the auto-hinter at its best */
593 /* quality. */
594 /* */
595 org_count = glyph->num_subs;
596
597 if ( org_count + count > glyph->max_subs )
598 {
599 FT_UInt new_max = ( org_count + count + 3 ) & (FT_UInt)-4;
600
601
602 /* we arbitrarily limit the number of subglyphs */
603 /* to avoid endless recursion */
604 if ( new_max > 64 )
605 {
606 error = FT_THROW( Invalid_Table );
607 FT_ERROR(( "pfr_glyph_load_compound:"
608 " too many compound glyphs components\n" ));
609 goto Exit;
610 }
611
612 if ( FT_RENEW_ARRAY( glyph->subs, glyph->max_subs, new_max ) )
613 goto Exit;
614
615 glyph->max_subs = new_max;
616 }
617
618 subglyph = glyph->subs + org_count;
619
620 for ( i = 0; i < count; i++, subglyph++ )
621 {
622 FT_UInt format;
623
624
625 x_pos = 0;
626 y_pos = 0;
627
628 PFR_CHECK( 1 );
629 format = PFR_NEXT_BYTE( p );
630
631 /* read scale when available */
632 subglyph->x_scale = 0x10000L;
633 if ( format & PFR_SUBGLYPH_XSCALE )
634 {
635 PFR_CHECK( 2 );
636 subglyph->x_scale = PFR_NEXT_SHORT( p ) * 16;
637 }
638
639 subglyph->y_scale = 0x10000L;
640 if ( format & PFR_SUBGLYPH_YSCALE )
641 {
642 PFR_CHECK( 2 );
643 subglyph->y_scale = PFR_NEXT_SHORT( p ) * 16;
644 }
645
646 /* read offset */
647 switch ( format & 3 )
648 {
649 case 1:
650 PFR_CHECK( 2 );
651 x_pos = PFR_NEXT_SHORT( p );
652 break;
653
654 case 2:
655 PFR_CHECK( 1 );
656 x_pos += PFR_NEXT_INT8( p );
657 break;
658
659 default:
660 ;
661 }
662
663 switch ( ( format >> 2 ) & 3 )
664 {
665 case 1:
666 PFR_CHECK( 2 );
667 y_pos = PFR_NEXT_SHORT( p );
668 break;
669
670 case 2:
671 PFR_CHECK( 1 );
672 y_pos += PFR_NEXT_INT8( p );
673 break;
674
675 default:
676 ;
677 }
678
679 subglyph->x_delta = x_pos;
680 subglyph->y_delta = y_pos;
681
682 /* read glyph position and size now */
683 if ( format & PFR_SUBGLYPH_2BYTE_SIZE )
684 {
685 PFR_CHECK( 2 );
686 subglyph->gps_size = PFR_NEXT_USHORT( p );
687 }
688 else
689 {
690 PFR_CHECK( 1 );
691 subglyph->gps_size = PFR_NEXT_BYTE( p );
692 }
693
694 if ( format & PFR_SUBGLYPH_3BYTE_OFFSET )
695 {
696 PFR_CHECK( 3 );
697 subglyph->gps_offset = PFR_NEXT_ULONG( p );
698 }
699 else
700 {
701 PFR_CHECK( 2 );
702 subglyph->gps_offset = PFR_NEXT_USHORT( p );
703 }
704
705 glyph->num_subs++;
706 }
707
708 Exit:
709 return error;
710
711 Failure:
712 Too_Short:
713 error = FT_THROW( Invalid_Table );
714 FT_ERROR(( "pfr_glyph_load_compound: invalid glyph data\n" ));
715 goto Exit;
716 }
717
718
719 static FT_Error
720 pfr_glyph_load_rec( PFR_Glyph glyph,
721 FT_Stream stream,
722 FT_ULong gps_offset,
723 FT_ULong offset,
724 FT_ULong size )
725 {
726 FT_Error error;
727 FT_Byte* p;
728 FT_Byte* limit;
729
730
731 if ( FT_STREAM_SEEK( gps_offset + offset ) ||
732 FT_FRAME_ENTER( size ) )
733 goto Exit;
734
735 p = (FT_Byte*)stream->cursor;
736 limit = p + size;
737
738 if ( size > 0 && *p & PFR_GLYPH_IS_COMPOUND )
739 {
740 FT_UInt n, old_count, count;
741 FT_GlyphLoader loader = glyph->loader;
742 FT_Outline* base = &loader->base.outline;
743
744
745 old_count = glyph->num_subs;
746
747 /* this is a compound glyph - load it */
748 error = pfr_glyph_load_compound( glyph, p, limit );
749
750 FT_FRAME_EXIT();
751
752 if ( error )
753 goto Exit;
754
755 count = glyph->num_subs - old_count;
756
757 FT_TRACE4(( "compound glyph with %d element%s (offset %lu):\n",
758 count,
759 count == 1 ? "" : "s",
760 offset ));
761
762 /* now, load each individual glyph */
763 for ( n = 0; n < count; n++ )
764 {
765 FT_Int i, old_points, num_points;
766 PFR_SubGlyph subglyph;
767
768
769 FT_TRACE4(( " subglyph %d:\n", n ));
770
771 subglyph = glyph->subs + old_count + n;
772 old_points = base->n_points;
773
774 error = pfr_glyph_load_rec( glyph, stream, gps_offset,
775 subglyph->gps_offset,
776 subglyph->gps_size );
777 if ( error )
778 break;
779
780 /* note that `glyph->subs' might have been re-allocated */
781 subglyph = glyph->subs + old_count + n;
782 num_points = base->n_points - old_points;
783
784 /* translate and eventually scale the new glyph points */
785 if ( subglyph->x_scale != 0x10000L || subglyph->y_scale != 0x10000L )
786 {
787 FT_Vector* vec = base->points + old_points;
788
789
790 for ( i = 0; i < num_points; i++, vec++ )
791 {
792 vec->x = FT_MulFix( vec->x, subglyph->x_scale ) +
793 subglyph->x_delta;
794 vec->y = FT_MulFix( vec->y, subglyph->y_scale ) +
795 subglyph->y_delta;
796 }
797 }
798 else
799 {
800 FT_Vector* vec = loader->base.outline.points + old_points;
801
802
803 for ( i = 0; i < num_points; i++, vec++ )
804 {
805 vec->x += subglyph->x_delta;
806 vec->y += subglyph->y_delta;
807 }
808 }
809
810 /* proceed to next sub-glyph */
811 }
812
813 FT_TRACE4(( "end compound glyph with %d element%s\n",
814 count,
815 count == 1 ? "" : "s" ));
816 }
817 else
818 {
819 FT_TRACE4(( "simple glyph (offset %lu)\n", offset ));
820
821 /* load a simple glyph */
822 error = pfr_glyph_load_simple( glyph, p, limit );
823
824 FT_FRAME_EXIT();
825 }
826
827 Exit:
828 return error;
829 }
830
831
832 FT_LOCAL_DEF( FT_Error )
833 pfr_glyph_load( PFR_Glyph glyph,
834 FT_Stream stream,
835 FT_ULong gps_offset,
836 FT_ULong offset,
837 FT_ULong size )
838 {
839 /* initialize glyph loader */
840 FT_GlyphLoader_Rewind( glyph->loader );
841
842 glyph->num_subs = 0;
843
844 /* load the glyph, recursively when needed */
845 return pfr_glyph_load_rec( glyph, stream, gps_offset, offset, size );
846 }
847
848
849/* END */
850