ReactOS 0.4.15-dev-7834-g00c4b3d
ttgxvar.c
Go to the documentation of this file.
1/***************************************************************************/
2/* */
3/* ttgxvar.c */
4/* */
5/* TrueType GX Font Variation loader */
6/* */
7/* Copyright 2004-2018 by */
8/* David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. */
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 /* Apple documents the `fvar', `gvar', `cvar', and `avar' tables at */
22 /* */
23 /* https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6[fgca]var.html */
24 /* */
25 /* The documentation for `gvar' is not intelligible; `cvar' refers you */
26 /* to `gvar' and is thus also incomprehensible. */
27 /* */
28 /* The documentation for `avar' appears correct, but Apple has no fonts */
29 /* with an `avar' table, so it is hard to test. */
30 /* */
31 /* Many thanks to John Jenkins (at Apple) in figuring this out. */
32 /* */
33 /* */
34 /* Apple's `kern' table has some references to tuple indices, but as */
35 /* there is no indication where these indices are defined, nor how to */
36 /* interpolate the kerning values (different tuples have different */
37 /* classes) this issue is ignored. */
38 /* */
39 /*************************************************************************/
40
41
42#include <ft2build.h>
43#include FT_INTERNAL_DEBUG_H
44#include FT_CONFIG_CONFIG_H
45#include FT_INTERNAL_STREAM_H
46#include FT_INTERNAL_SFNT_H
47#include FT_TRUETYPE_TAGS_H
48#include FT_TRUETYPE_IDS_H
49#include FT_MULTIPLE_MASTERS_H
50#include FT_LIST_H
51
52#include "ttpload.h"
53#include "ttgxvar.h"
54
55#include "tterrors.h"
56
57
58#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
59
60
61#define FT_Stream_FTell( stream ) \
62 (FT_ULong)( (stream)->cursor - (stream)->base )
63#define FT_Stream_SeekSet( stream, off ) \
64 (stream)->cursor = \
65 ( (off) < (FT_ULong)( (stream)->limit - (stream)->base ) ) \
66 ? (stream)->base + (off) \
67 : (stream)->limit
68
69
70 /*************************************************************************/
71 /* */
72 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
73 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
74 /* messages during execution. */
75 /* */
76#undef FT_COMPONENT
77#define FT_COMPONENT trace_ttgxvar
78
79
80 /*************************************************************************/
81 /*************************************************************************/
82 /***** *****/
83 /***** Internal Routines *****/
84 /***** *****/
85 /*************************************************************************/
86 /*************************************************************************/
87
88
89 /*************************************************************************/
90 /* */
91 /* The macro ALL_POINTS is used in `ft_var_readpackedpoints'. It */
92 /* indicates that there is a delta for every point without needing to */
93 /* enumerate all of them. */
94 /* */
95
96 /* ensure that value `0' has the same width as a pointer */
97#define ALL_POINTS (FT_UShort*)~(FT_PtrDist)0
98
99
100#define GX_PT_POINTS_ARE_WORDS 0x80U
101#define GX_PT_POINT_RUN_COUNT_MASK 0x7FU
102
103
104 /*************************************************************************/
105 /* */
106 /* <Function> */
107 /* ft_var_readpackedpoints */
108 /* */
109 /* <Description> */
110 /* Read a set of points to which the following deltas will apply. */
111 /* Points are packed with a run length encoding. */
112 /* */
113 /* <Input> */
114 /* stream :: The data stream. */
115 /* */
116 /* size :: The size of the table holding the data. */
117 /* */
118 /* <Output> */
119 /* point_cnt :: The number of points read. A zero value means that */
120 /* all points in the glyph will be affected, without */
121 /* enumerating them individually. */
122 /* */
123 /* <Return> */
124 /* An array of FT_UShort containing the affected points or the */
125 /* special value ALL_POINTS. */
126 /* */
127 static FT_UShort*
128 ft_var_readpackedpoints( FT_Stream stream,
130 FT_UInt *point_cnt )
131 {
133 FT_UInt n;
134 FT_UInt runcnt;
135 FT_UInt i, j;
137 FT_Memory memory = stream->memory;
139
140 FT_UNUSED( error );
141
142
143 *point_cnt = 0;
144
145 n = FT_GET_BYTE();
146 if ( n == 0 )
147 return ALL_POINTS;
148
149 if ( n & GX_PT_POINTS_ARE_WORDS )
150 {
151 n &= GX_PT_POINT_RUN_COUNT_MASK;
152 n <<= 8;
153 n |= FT_GET_BYTE();
154 }
155
156 if ( n > size )
157 {
158 FT_TRACE1(( "ft_var_readpackedpoints: number of points too large\n" ));
159 return NULL;
160 }
161
162 /* in the nested loops below we increase `i' twice; */
163 /* it is faster to simply allocate one more slot */
164 /* than to add another test within the loop */
165 if ( FT_NEW_ARRAY( points, n + 1 ) )
166 return NULL;
167
168 *point_cnt = n;
169
170 first = 0;
171 i = 0;
172 while ( i < n )
173 {
174 runcnt = FT_GET_BYTE();
175 if ( runcnt & GX_PT_POINTS_ARE_WORDS )
176 {
177 runcnt &= GX_PT_POINT_RUN_COUNT_MASK;
178 first += FT_GET_USHORT();
179 points[i++] = first;
180
181 /* first point not included in run count */
182 for ( j = 0; j < runcnt; j++ )
183 {
184 first += FT_GET_USHORT();
185 points[i++] = first;
186 if ( i >= n )
187 break;
188 }
189 }
190 else
191 {
192 first += FT_GET_BYTE();
193 points[i++] = first;
194
195 for ( j = 0; j < runcnt; j++ )
196 {
197 first += FT_GET_BYTE();
198 points[i++] = first;
199 if ( i >= n )
200 break;
201 }
202 }
203 }
204
205 return points;
206 }
207
208
209#define GX_DT_DELTAS_ARE_ZERO 0x80U
210#define GX_DT_DELTAS_ARE_WORDS 0x40U
211#define GX_DT_DELTA_RUN_COUNT_MASK 0x3FU
212
213
214 /*************************************************************************/
215 /* */
216 /* <Function> */
217 /* ft_var_readpackeddeltas */
218 /* */
219 /* <Description> */
220 /* Read a set of deltas. These are packed slightly differently than */
221 /* points. In particular there is no overall count. */
222 /* */
223 /* <Input> */
224 /* stream :: The data stream. */
225 /* */
226 /* size :: The size of the table holding the data. */
227 /* */
228 /* delta_cnt :: The number of deltas to be read. */
229 /* */
230 /* <Return> */
231 /* An array of FT_Short containing the deltas for the affected */
232 /* points. (This only gets the deltas for one dimension. It will */
233 /* generally be called twice, once for x, once for y. When used in */
234 /* cvt table, it will only be called once.) */
235 /* */
236 static FT_Short*
237 ft_var_readpackeddeltas( FT_Stream stream,
239 FT_UInt delta_cnt )
240 {
241 FT_Short *deltas = NULL;
242 FT_UInt runcnt, cnt;
243 FT_UInt i, j;
244 FT_Memory memory = stream->memory;
246
247 FT_UNUSED( error );
248
249
250 if ( delta_cnt > size )
251 {
252 FT_TRACE1(( "ft_var_readpackeddeltas: number of points too large\n" ));
253 return NULL;
254 }
255
256 if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
257 return NULL;
258
259 i = 0;
260 while ( i < delta_cnt )
261 {
262 runcnt = FT_GET_BYTE();
263 cnt = runcnt & GX_DT_DELTA_RUN_COUNT_MASK;
264
265 if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
266 {
267 /* `runcnt' zeroes get added */
268 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
269 deltas[i++] = 0;
270 }
271 else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
272 {
273 /* `runcnt' shorts from the stack */
274 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
275 deltas[i++] = FT_GET_SHORT();
276 }
277 else
278 {
279 /* `runcnt' signed bytes from the stack */
280 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
281 deltas[i++] = FT_GET_CHAR();
282 }
283
284 if ( j <= cnt )
285 {
286 /* bad format */
287 FT_FREE( deltas );
288 return NULL;
289 }
290 }
291
292 return deltas;
293 }
294
295
296 /*************************************************************************/
297 /* */
298 /* <Function> */
299 /* ft_var_load_avar */
300 /* */
301 /* <Description> */
302 /* Parse the `avar' table if present. It need not be, so we return */
303 /* nothing. */
304 /* */
305 /* <InOut> */
306 /* face :: The font face. */
307 /* */
308 static void
309 ft_var_load_avar( TT_Face face )
310 {
312 FT_Memory memory = stream->memory;
313 GX_Blend blend = face->blend;
314 GX_AVarSegment segment;
317 FT_Long axisCount;
318 FT_Int i, j;
319 FT_ULong table_len;
320
321 FT_UNUSED( error );
322
323
324 FT_TRACE2(( "AVAR " ));
325
326 blend->avar_loaded = TRUE;
327 error = face->goto_table( face, TTAG_avar, stream, &table_len );
328 if ( error )
329 {
330 FT_TRACE2(( "is missing\n" ));
331 return;
332 }
333
334 if ( FT_FRAME_ENTER( table_len ) )
335 return;
336
338 axisCount = FT_GET_LONG();
339
340 if ( version != 0x00010000L )
341 {
342 FT_TRACE2(( "bad table version\n" ));
343 goto Exit;
344 }
345
346 FT_TRACE2(( "loaded\n" ));
347
348 if ( axisCount != (FT_Long)blend->mmvar->num_axis )
349 {
350 FT_TRACE2(( "ft_var_load_avar: number of axes in `avar' and `fvar'\n"
351 " table are different\n" ));
352 goto Exit;
353 }
354
355 if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) )
356 goto Exit;
357
358 segment = &blend->avar_segment[0];
359 for ( i = 0; i < axisCount; i++, segment++ )
360 {
361 FT_TRACE5(( " axis %d:\n", i ));
362
363 segment->pairCount = FT_GET_USHORT();
364 if ( (FT_ULong)segment->pairCount * 4 > table_len ||
365 FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
366 {
367 /* Failure. Free everything we have done so far. We must do */
368 /* it right now since loading the `avar' table is optional. */
369
370 for ( j = i - 1; j >= 0; j-- )
371 FT_FREE( blend->avar_segment[j].correspondence );
372
373 FT_FREE( blend->avar_segment );
374 blend->avar_segment = NULL;
375 goto Exit;
376 }
377
378 for ( j = 0; j < segment->pairCount; j++ )
379 {
380 /* convert to Fixed */
381 segment->correspondence[j].fromCoord = FT_GET_SHORT() * 4;
382 segment->correspondence[j].toCoord = FT_GET_SHORT() * 4;
383
384 FT_TRACE5(( " mapping %.5f to %.5f\n",
385 segment->correspondence[j].fromCoord / 65536.0,
386 segment->correspondence[j].toCoord / 65536.0 ));
387 }
388
389 FT_TRACE5(( "\n" ));
390 }
391
392 Exit:
394 }
395
396
397 /* some macros we need */
398#define FT_FIXED_ONE ( (FT_Fixed)0x10000 )
399
400#define FT_fdot14ToFixed( x ) \
401 ( (FT_Fixed)( (FT_ULong)(x) << 2 ) )
402#define FT_intToFixed( i ) \
403 ( (FT_Fixed)( (FT_ULong)(i) << 16 ) )
404#define FT_fixedToInt( x ) \
405 ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
406
407
408 static FT_Error
409 ft_var_load_item_variation_store( TT_Face face,
411 GX_ItemVarStore itemStore )
412 {
414 FT_Memory memory = stream->memory;
415
418 FT_ULong region_offset;
419 FT_UInt i, j, k;
420 FT_UInt shortDeltaCount;
421
422 GX_Blend blend = face->blend;
423 GX_ItemVarData varData;
424
425 FT_ULong* dataOffsetArray = NULL;
426
427
428 if ( FT_STREAM_SEEK( offset ) ||
430 goto Exit;
431
432 if ( format != 1 )
433 {
434 FT_TRACE2(( "ft_var_load_item_variation_store: bad store format %d\n",
435 format ));
436 error = FT_THROW( Invalid_Table );
437 goto Exit;
438 }
439
440 /* read top level fields */
441 if ( FT_READ_ULONG( region_offset ) ||
442 FT_READ_USHORT( itemStore->dataCount ) )
443 goto Exit;
444
445 /* we need at least one entry in `itemStore->varData' */
446 if ( !itemStore->dataCount )
447 {
448 FT_TRACE2(( "ft_var_load_item_variation_store: missing varData\n" ));
449 error = FT_THROW( Invalid_Table );
450 goto Exit;
451 }
452
453 /* make temporary copy of item variation data offsets; */
454 /* we will parse region list first, then come back */
455 if ( FT_NEW_ARRAY( dataOffsetArray, itemStore->dataCount ) )
456 goto Exit;
457
458 for ( i = 0; i < itemStore->dataCount; i++ )
459 {
460 if ( FT_READ_ULONG( dataOffsetArray[i] ) )
461 goto Exit;
462 }
463
464 /* parse array of region records (region list) */
465 if ( FT_STREAM_SEEK( offset + region_offset ) )
466 goto Exit;
467
468 if ( FT_READ_USHORT( itemStore->axisCount ) ||
469 FT_READ_USHORT( itemStore->regionCount ) )
470 goto Exit;
471
472 if ( itemStore->axisCount != (FT_Long)blend->mmvar->num_axis )
473 {
474 FT_TRACE2(( "ft_var_load_item_variation_store:"
475 " number of axes in item variation store\n"
476 " "
477 " and `fvar' table are different\n" ));
478 error = FT_THROW( Invalid_Table );
479 goto Exit;
480 }
481
482 if ( FT_NEW_ARRAY( itemStore->varRegionList, itemStore->regionCount ) )
483 goto Exit;
484
485 for ( i = 0; i < itemStore->regionCount; i++ )
486 {
487 GX_AxisCoords axisCoords;
488
489
490 if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList,
491 itemStore->axisCount ) )
492 goto Exit;
493
494 axisCoords = itemStore->varRegionList[i].axisList;
495
496 for ( j = 0; j < itemStore->axisCount; j++ )
497 {
498 FT_Short start, peak, end;
499
500
501 if ( FT_READ_SHORT( start ) ||
502 FT_READ_SHORT( peak ) ||
503 FT_READ_SHORT( end ) )
504 goto Exit;
505
506 axisCoords[j].startCoord = FT_fdot14ToFixed( start );
507 axisCoords[j].peakCoord = FT_fdot14ToFixed( peak );
508 axisCoords[j].endCoord = FT_fdot14ToFixed( end );
509 }
510 }
511
512 /* end of region list parse */
513
514 /* use dataOffsetArray now to parse varData items */
515 if ( FT_NEW_ARRAY( itemStore->varData, itemStore->dataCount ) )
516 goto Exit;
517
518 for ( i = 0; i < itemStore->dataCount; i++ )
519 {
520 varData = &itemStore->varData[i];
521
522 if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) )
523 goto Exit;
524
525 if ( FT_READ_USHORT( varData->itemCount ) ||
526 FT_READ_USHORT( shortDeltaCount ) ||
527 FT_READ_USHORT( varData->regionIdxCount ) )
528 goto Exit;
529
530 /* check some data consistency */
531 if ( shortDeltaCount > varData->regionIdxCount )
532 {
533 FT_TRACE2(( "bad short count %d or region count %d\n",
534 shortDeltaCount,
535 varData->regionIdxCount ));
536 error = FT_THROW( Invalid_Table );
537 goto Exit;
538 }
539
540 if ( varData->regionIdxCount > itemStore->regionCount )
541 {
542 FT_TRACE2(( "inconsistent regionCount %d in varData[%d]\n",
543 varData->regionIdxCount,
544 i ));
545 error = FT_THROW( Invalid_Table );
546 goto Exit;
547 }
548
549 /* parse region indices */
550 if ( FT_NEW_ARRAY( varData->regionIndices,
551 varData->regionIdxCount ) )
552 goto Exit;
553
554 for ( j = 0; j < varData->regionIdxCount; j++ )
555 {
556 if ( FT_READ_USHORT( varData->regionIndices[j] ) )
557 goto Exit;
558
559 if ( varData->regionIndices[j] >= itemStore->regionCount )
560 {
561 FT_TRACE2(( "bad region index %d\n",
562 varData->regionIndices[j] ));
563 error = FT_THROW( Invalid_Table );
564 goto Exit;
565 }
566 }
567
568 /* Parse delta set. */
569 /* */
570 /* On input, deltas are (shortDeltaCount + regionIdxCount) bytes */
571 /* each; on output, deltas are expanded to `regionIdxCount' shorts */
572 /* each. */
573 if ( FT_NEW_ARRAY( varData->deltaSet,
574 varData->regionIdxCount * varData->itemCount ) )
575 goto Exit;
576
577 /* the delta set is stored as a 2-dimensional array of shorts; */
578 /* sign-extend signed bytes to signed shorts */
579 for ( j = 0; j < varData->itemCount * varData->regionIdxCount; )
580 {
581 for ( k = 0; k < shortDeltaCount; k++, j++ )
582 {
583 /* read the short deltas */
584 FT_Short delta;
585
586
587 if ( FT_READ_SHORT( delta ) )
588 goto Exit;
589
590 varData->deltaSet[j] = delta;
591 }
592
593 for ( ; k < varData->regionIdxCount; k++, j++ )
594 {
595 /* read the (signed) byte deltas */
596 FT_Char delta;
597
598
599 if ( FT_READ_CHAR( delta ) )
600 goto Exit;
601
602 varData->deltaSet[j] = delta;
603 }
604 }
605 }
606
607 Exit:
608 FT_FREE( dataOffsetArray );
609
610 return error;
611 }
612
613
614 static FT_Error
615 ft_var_load_delta_set_index_mapping( TT_Face face,
617 GX_DeltaSetIdxMap map,
618 GX_ItemVarStore itemStore )
619 {
621 FT_Memory memory = stream->memory;
622
624
626 FT_UInt entrySize;
627 FT_UInt innerBitCount;
628 FT_UInt innerIndexMask;
629 FT_UInt i, j;
630
631
632 if ( FT_STREAM_SEEK( offset ) ||
634 FT_READ_USHORT( map->mapCount ) )
635 goto Exit;
636
637 if ( format & 0xFFC0 )
638 {
639 FT_TRACE2(( "bad map format %d\n", format ));
640 error = FT_THROW( Invalid_Table );
641 goto Exit;
642 }
643
644 /* bytes per entry: 1, 2, 3, or 4 */
645 entrySize = ( ( format & 0x0030 ) >> 4 ) + 1;
646 innerBitCount = ( format & 0x000F ) + 1;
647 innerIndexMask = ( 1 << innerBitCount ) - 1;
648
649 if ( FT_NEW_ARRAY( map->innerIndex, map->mapCount ) )
650 goto Exit;
651
652 if ( FT_NEW_ARRAY( map->outerIndex, map->mapCount ) )
653 goto Exit;
654
655 for ( i = 0; i < map->mapCount; i++ )
656 {
657 FT_UInt mapData = 0;
658 FT_UInt outerIndex, innerIndex;
659
660
661 /* read map data one unsigned byte at a time, big endian */
662 for ( j = 0; j < entrySize; j++ )
663 {
665
666
667 if ( FT_READ_BYTE( data ) )
668 goto Exit;
669
670 mapData = ( mapData << 8 ) | data;
671 }
672
673 outerIndex = mapData >> innerBitCount;
674
675 if ( outerIndex >= itemStore->dataCount )
676 {
677 FT_TRACE2(( "outerIndex[%d] == %d out of range\n",
678 i,
679 outerIndex ));
680 error = FT_THROW( Invalid_Table );
681 goto Exit;
682 }
683
684 map->outerIndex[i] = outerIndex;
685
686 innerIndex = mapData & innerIndexMask;
687
688 if ( innerIndex >= itemStore->varData[outerIndex].itemCount )
689 {
690 FT_TRACE2(( "innerIndex[%d] == %d out of range\n",
691 i,
692 innerIndex ));
693 error = FT_THROW( Invalid_Table );
694 goto Exit;
695 }
696
697 map->innerIndex[i] = innerIndex;
698 }
699
700 Exit:
701 return error;
702 }
703
704
705 /*************************************************************************/
706 /* */
707 /* <Function> */
708 /* ft_var_load_hvvar */
709 /* */
710 /* <Description> */
711 /* If `vertical' is zero, parse the `HVAR' table and set */
712 /* `blend->hvar_loaded' to TRUE. On success, `blend->hvar_checked' */
713 /* is set to TRUE. */
714 /* */
715 /* If `vertical' is not zero, parse the `VVAR' table and set */
716 /* `blend->vvar_loaded' to TRUE. On success, `blend->vvar_checked' */
717 /* is set to TRUE. */
718 /* */
719 /* Some memory may remain allocated on error; it is always freed in */
720 /* `tt_done_blend', however. */
721 /* */
722 /* <InOut> */
723 /* face :: The font face. */
724 /* */
725 /* <Return> */
726 /* FreeType error code. 0 means success. */
727 /* */
728 static FT_Error
729 ft_var_load_hvvar( TT_Face face,
730 FT_Bool vertical )
731 {
733 FT_Memory memory = stream->memory;
734
735 GX_Blend blend = face->blend;
736
737 GX_HVVarTable table;
738
740 FT_UShort majorVersion;
741 FT_ULong table_len;
742 FT_ULong table_offset;
743 FT_ULong store_offset;
744 FT_ULong widthMap_offset;
745
746
747 if ( vertical )
748 {
749 blend->vvar_loaded = TRUE;
750
751 FT_TRACE2(( "VVAR " ));
752
753 error = face->goto_table( face, TTAG_VVAR, stream, &table_len );
754 }
755 else
756 {
757 blend->hvar_loaded = TRUE;
758
759 FT_TRACE2(( "HVAR " ));
760
761 error = face->goto_table( face, TTAG_HVAR, stream, &table_len );
762 }
763
764 if ( error )
765 {
766 FT_TRACE2(( "is missing\n" ));
767 goto Exit;
768 }
769
770 table_offset = FT_STREAM_POS();
771
772 /* skip minor version */
773 if ( FT_READ_USHORT( majorVersion ) ||
774 FT_STREAM_SKIP( 2 ) )
775 goto Exit;
776
777 if ( majorVersion != 1 )
778 {
779 FT_TRACE2(( "bad table version %d\n", majorVersion ));
780 error = FT_THROW( Invalid_Table );
781 goto Exit;
782 }
783
784 if ( FT_READ_ULONG( store_offset ) ||
785 FT_READ_ULONG( widthMap_offset ) )
786 goto Exit;
787
788 if ( vertical )
789 {
790 if ( FT_NEW( blend->vvar_table ) )
791 goto Exit;
792 table = blend->vvar_table;
793 }
794 else
795 {
796 if ( FT_NEW( blend->hvar_table ) )
797 goto Exit;
798 table = blend->hvar_table;
799 }
800
801 error = ft_var_load_item_variation_store(
802 face,
803 table_offset + store_offset,
804 &table->itemStore );
805 if ( error )
806 goto Exit;
807
808 if ( widthMap_offset )
809 {
810 error = ft_var_load_delta_set_index_mapping(
811 face,
812 table_offset + widthMap_offset,
813 &table->widthMap,
814 &table->itemStore );
815 if ( error )
816 goto Exit;
817 }
818
819 FT_TRACE2(( "loaded\n" ));
821
822 Exit:
823 if ( !error )
824 {
825 if ( vertical )
826 {
827 blend->vvar_checked = TRUE;
828
829 /* FreeType doesn't provide functions to quickly retrieve */
830 /* TSB, BSB, or VORG values; we thus don't have to implement */
831 /* support for those three item variation stores. */
832
833 face->variation_support |= TT_FACE_FLAG_VAR_VADVANCE;
834 }
835 else
836 {
837 blend->hvar_checked = TRUE;
838
839 /* FreeType doesn't provide functions to quickly retrieve */
840 /* LSB or RSB values; we thus don't have to implement */
841 /* support for those two item variation stores. */
842
843 face->variation_support |= TT_FACE_FLAG_VAR_HADVANCE;
844 }
845 }
846
847 return error;
848 }
849
850
851 static FT_Int
852 ft_var_get_item_delta( TT_Face face,
853 GX_ItemVarStore itemStore,
854 FT_UInt outerIndex,
855 FT_UInt innerIndex )
856 {
857 GX_ItemVarData varData;
858 FT_Short* deltaSet;
859
860 FT_UInt master, j;
861 FT_Fixed netAdjustment = 0; /* accumulated adjustment */
862 FT_Fixed scaledDelta;
863 FT_Fixed delta;
864
865
866 /* See pseudo code from `Font Variations Overview' */
867 /* in the OpenType specification. */
868
869 varData = &itemStore->varData[outerIndex];
870 deltaSet = &varData->deltaSet[varData->regionIdxCount * innerIndex];
871
872 /* outer loop steps through master designs to be blended */
873 for ( master = 0; master < varData->regionIdxCount; master++ )
874 {
875 FT_Fixed scalar = FT_FIXED_ONE;
876 FT_UInt regionIndex = varData->regionIndices[master];
877
878 GX_AxisCoords axis = itemStore->varRegionList[regionIndex].axisList;
879
880
881 /* inner loop steps through axes in this region */
882 for ( j = 0; j < itemStore->axisCount; j++, axis++ )
883 {
884 FT_Fixed axisScalar;
885
886
887 /* compute the scalar contribution of this axis; */
888 /* ignore invalid ranges */
889 if ( axis->startCoord > axis->peakCoord ||
890 axis->peakCoord > axis->endCoord )
891 axisScalar = FT_FIXED_ONE;
892
893 else if ( axis->startCoord < 0 &&
894 axis->endCoord > 0 &&
895 axis->peakCoord != 0 )
896 axisScalar = FT_FIXED_ONE;
897
898 /* peak of 0 means ignore this axis */
899 else if ( axis->peakCoord == 0 )
900 axisScalar = FT_FIXED_ONE;
901
902 /* ignore this region if coords are out of range */
903 else if ( face->blend->normalizedcoords[j] < axis->startCoord ||
904 face->blend->normalizedcoords[j] > axis->endCoord )
905 axisScalar = 0;
906
907 /* calculate a proportional factor */
908 else
909 {
910 if ( face->blend->normalizedcoords[j] == axis->peakCoord )
911 axisScalar = FT_FIXED_ONE;
912 else if ( face->blend->normalizedcoords[j] < axis->peakCoord )
913 axisScalar =
914 FT_DivFix( face->blend->normalizedcoords[j] - axis->startCoord,
915 axis->peakCoord - axis->startCoord );
916 else
917 axisScalar =
918 FT_DivFix( axis->endCoord - face->blend->normalizedcoords[j],
919 axis->endCoord - axis->peakCoord );
920 }
921
922 /* take product of all the axis scalars */
923 scalar = FT_MulFix( scalar, axisScalar );
924
925 } /* per-axis loop */
926
927 /* get the scaled delta for this region */
928 delta = FT_intToFixed( deltaSet[master] );
929 scaledDelta = FT_MulFix( scalar, delta );
930
931 /* accumulate the adjustments from each region */
932 netAdjustment = netAdjustment + scaledDelta;
933
934 } /* per-region loop */
935
936 return FT_fixedToInt( netAdjustment );
937 }
938
939
940 /*************************************************************************/
941 /* */
942 /* <Function> */
943 /* tt_hvadvance_adjust */
944 /* */
945 /* <Description> */
946 /* Apply `HVAR' advance width or `VVAR' advance height adjustment of */
947 /* a given glyph. */
948 /* */
949 /* <Input> */
950 /* gindex :: The glyph index. */
951 /* */
952 /* vertical :: If set, handle `VVAR' table. */
953 /* */
954 /* <InOut> */
955 /* face :: The font face. */
956 /* */
957 /* adelta :: Points to width or height value that gets modified. */
958 /* */
959 static FT_Error
960 tt_hvadvance_adjust( TT_Face face,
961 FT_UInt gindex,
962 FT_Int *avalue,
963 FT_Bool vertical )
964 {
966 FT_UInt innerIndex, outerIndex;
967 FT_Int delta;
968
969 GX_HVVarTable table;
970
971
972 if ( !face->doblend || !face->blend )
973 goto Exit;
974
975 if ( vertical )
976 {
977 if ( !face->blend->vvar_loaded )
978 {
979 /* initialize vvar table */
980 face->blend->vvar_error = ft_var_load_hvvar( face, 1 );
981 }
982
983 if ( !face->blend->vvar_checked )
984 {
985 error = face->blend->vvar_error;
986 goto Exit;
987 }
988
989 table = face->blend->vvar_table;
990 }
991 else
992 {
993 if ( !face->blend->hvar_loaded )
994 {
995 /* initialize hvar table */
996 face->blend->hvar_error = ft_var_load_hvvar( face, 0 );
997 }
998
999 if ( !face->blend->hvar_checked )
1000 {
1001 error = face->blend->hvar_error;
1002 goto Exit;
1003 }
1004
1005 table = face->blend->hvar_table;
1006 }
1007
1008 /* advance width or height adjustments are always present in an */
1009 /* `HVAR' or `VVAR' table; no need to test for this capability */
1010
1011 if ( table->widthMap.innerIndex )
1012 {
1013 FT_UInt idx = gindex;
1014
1015
1016 if ( idx >= table->widthMap.mapCount )
1017 idx = table->widthMap.mapCount - 1;
1018
1019 /* trust that HVAR parser has checked indices */
1020 outerIndex = table->widthMap.outerIndex[idx];
1021 innerIndex = table->widthMap.innerIndex[idx];
1022 }
1023 else
1024 {
1025 GX_ItemVarData varData;
1026
1027
1028 /* no widthMap data */
1029 outerIndex = 0;
1030 innerIndex = gindex;
1031
1032 varData = &table->itemStore.varData[outerIndex];
1033 if ( gindex >= varData->itemCount )
1034 {
1035 FT_TRACE2(( "gindex %d out of range\n", gindex ));
1036 error = FT_THROW( Invalid_Argument );
1037 goto Exit;
1038 }
1039 }
1040
1041 delta = ft_var_get_item_delta( face,
1042 &table->itemStore,
1043 outerIndex,
1044 innerIndex );
1045
1046 FT_TRACE5(( "%s value %d adjusted by %d unit%s (%s)\n",
1047 vertical ? "vertical height" : "horizontal width",
1048 *avalue,
1049 delta,
1050 delta == 1 ? "" : "s",
1051 vertical ? "VVAR" : "HVAR" ));
1052
1053 *avalue += delta;
1054
1055 Exit:
1056 return error;
1057 }
1058
1059
1061 tt_hadvance_adjust( TT_Face face,
1062 FT_UInt gindex,
1063 FT_Int *avalue )
1064 {
1065 return tt_hvadvance_adjust( face, gindex, avalue, 0 );
1066 }
1067
1068
1070 tt_vadvance_adjust( TT_Face face,
1071 FT_UInt gindex,
1072 FT_Int *avalue )
1073 {
1074 return tt_hvadvance_adjust( face, gindex, avalue, 1 );
1075 }
1076
1077
1078#define GX_VALUE_SIZE 8
1079
1080 /* all values are FT_Short or FT_UShort entities; */
1081 /* we treat them consistently as FT_Short */
1082#define GX_VALUE_CASE( tag, dflt ) \
1083 case MVAR_TAG_ ## tag : \
1084 p = (FT_Short*)&face->dflt; \
1085 break
1086
1087#define GX_GASP_CASE( idx ) \
1088 case MVAR_TAG_GASP_ ## idx : \
1089 if ( idx < face->gasp.numRanges - 1 ) \
1090 p = (FT_Short*)&face->gasp.gaspRanges[idx].maxPPEM; \
1091 else \
1092 p = NULL; \
1093 break
1094
1095
1096 static FT_Short*
1097 ft_var_get_value_pointer( TT_Face face,
1098 FT_ULong mvar_tag )
1099 {
1100 FT_Short* p;
1101
1102
1103 switch ( mvar_tag )
1104 {
1105 GX_GASP_CASE( 0 );
1106 GX_GASP_CASE( 1 );
1107 GX_GASP_CASE( 2 );
1108 GX_GASP_CASE( 3 );
1109 GX_GASP_CASE( 4 );
1110 GX_GASP_CASE( 5 );
1111 GX_GASP_CASE( 6 );
1112 GX_GASP_CASE( 7 );
1113 GX_GASP_CASE( 8 );
1114 GX_GASP_CASE( 9 );
1115
1116 GX_VALUE_CASE( CPHT, os2.sCapHeight );
1117 GX_VALUE_CASE( HASC, os2.sTypoAscender );
1118 GX_VALUE_CASE( HCLA, os2.usWinAscent );
1119 GX_VALUE_CASE( HCLD, os2.usWinDescent );
1120 GX_VALUE_CASE( HCOF, horizontal.caret_Offset );
1121 GX_VALUE_CASE( HCRN, horizontal.caret_Slope_Run );
1122 GX_VALUE_CASE( HCRS, horizontal.caret_Slope_Rise );
1123 GX_VALUE_CASE( HDSC, os2.sTypoDescender );
1124 GX_VALUE_CASE( HLGP, os2.sTypoLineGap );
1125 GX_VALUE_CASE( SBXO, os2.ySubscriptXOffset);
1126 GX_VALUE_CASE( SBXS, os2.ySubscriptXSize );
1127 GX_VALUE_CASE( SBYO, os2.ySubscriptYOffset );
1128 GX_VALUE_CASE( SBYS, os2.ySubscriptYSize );
1129 GX_VALUE_CASE( SPXO, os2.ySuperscriptXOffset );
1130 GX_VALUE_CASE( SPXS, os2.ySuperscriptXSize );
1131 GX_VALUE_CASE( SPYO, os2.ySuperscriptYOffset );
1132 GX_VALUE_CASE( SPYS, os2.ySuperscriptYSize );
1133 GX_VALUE_CASE( STRO, os2.yStrikeoutPosition );
1134 GX_VALUE_CASE( STRS, os2.yStrikeoutSize );
1135 GX_VALUE_CASE( UNDO, postscript.underlinePosition );
1136 GX_VALUE_CASE( UNDS, postscript.underlineThickness );
1137 GX_VALUE_CASE( VASC, vertical.Ascender );
1138 GX_VALUE_CASE( VCOF, vertical.caret_Offset );
1139 GX_VALUE_CASE( VCRN, vertical.caret_Slope_Run );
1140 GX_VALUE_CASE( VCRS, vertical.caret_Slope_Rise );
1141 GX_VALUE_CASE( VDSC, vertical.Descender );
1142 GX_VALUE_CASE( VLGP, vertical.Line_Gap );
1143 GX_VALUE_CASE( XHGT, os2.sxHeight );
1144
1145 default:
1146 /* ignore unknown tag */
1147 p = NULL;
1148 }
1149
1150 return p;
1151 }
1152
1153
1154 /*************************************************************************/
1155 /* */
1156 /* <Function> */
1157 /* ft_var_load_mvar */
1158 /* */
1159 /* <Description> */
1160 /* Parse the `MVAR' table. */
1161 /* */
1162 /* Some memory may remain allocated on error; it is always freed in */
1163 /* `tt_done_blend', however. */
1164 /* */
1165 /* <InOut> */
1166 /* face :: The font face. */
1167 /* */
1168 static void
1169 ft_var_load_mvar( TT_Face face )
1170 {
1172 FT_Memory memory = stream->memory;
1173
1174 GX_Blend blend = face->blend;
1175 GX_ItemVarStore itemStore;
1176 GX_Value value, limit;
1177
1179 FT_UShort majorVersion;
1180 FT_ULong table_len;
1181 FT_ULong table_offset;
1182 FT_UShort store_offset;
1183 FT_ULong records_offset;
1184
1185
1186 FT_TRACE2(( "MVAR " ));
1187
1188 error = face->goto_table( face, TTAG_MVAR, stream, &table_len );
1189 if ( error )
1190 {
1191 FT_TRACE2(( "is missing\n" ));
1192 return;
1193 }
1194
1195 table_offset = FT_STREAM_POS();
1196
1197 /* skip minor version */
1198 if ( FT_READ_USHORT( majorVersion ) ||
1199 FT_STREAM_SKIP( 2 ) )
1200 return;
1201
1202 if ( majorVersion != 1 )
1203 {
1204 FT_TRACE2(( "bad table version %d\n", majorVersion ));
1205 return;
1206 }
1207
1208 if ( FT_NEW( blend->mvar_table ) )
1209 return;
1210
1211 /* skip reserved entry and value record size */
1212 if ( FT_STREAM_SKIP( 4 ) ||
1213 FT_READ_USHORT( blend->mvar_table->valueCount ) ||
1214 FT_READ_USHORT( store_offset ) )
1215 return;
1216
1217 records_offset = FT_STREAM_POS();
1218
1219 error = ft_var_load_item_variation_store(
1220 face,
1221 table_offset + store_offset,
1222 &blend->mvar_table->itemStore );
1223 if ( error )
1224 return;
1225
1226 if ( FT_NEW_ARRAY( blend->mvar_table->values,
1227 blend->mvar_table->valueCount ) )
1228 return;
1229
1230 if ( FT_STREAM_SEEK( records_offset ) ||
1231 FT_FRAME_ENTER( blend->mvar_table->valueCount * GX_VALUE_SIZE ) )
1232 return;
1233
1234 value = blend->mvar_table->values;
1235 limit = value + blend->mvar_table->valueCount;
1236 itemStore = &blend->mvar_table->itemStore;
1237
1238 for ( ; value < limit; value++ )
1239 {
1240 value->tag = FT_GET_ULONG();
1241 value->outerIndex = FT_GET_USHORT();
1242 value->innerIndex = FT_GET_USHORT();
1243
1244 if ( value->outerIndex >= itemStore->dataCount ||
1245 value->innerIndex >= itemStore->varData[value->outerIndex]
1246 .itemCount )
1247 {
1248 error = FT_THROW( Invalid_Table );
1249 break;
1250 }
1251 }
1252
1253 FT_FRAME_EXIT();
1254
1255 if ( error )
1256 return;
1257
1258 FT_TRACE2(( "loaded\n" ));
1259
1260 value = blend->mvar_table->values;
1261 limit = value + blend->mvar_table->valueCount;
1262
1263 /* save original values of the data MVAR is going to modify */
1264 for ( ; value < limit; value++ )
1265 {
1266 FT_Short* p = ft_var_get_value_pointer( face, value->tag );
1267
1268
1269 if ( p )
1270 value->unmodified = *p;
1271#ifdef FT_DEBUG_LEVEL_TRACE
1272 else
1273 FT_TRACE1(( "ft_var_load_mvar: Ignoring unknown tag `%c%c%c%c'\n",
1274 (FT_Char)( value->tag >> 24 ),
1275 (FT_Char)( value->tag >> 16 ),
1276 (FT_Char)( value->tag >> 8 ),
1277 (FT_Char)( value->tag ) ));
1278#endif
1279 }
1280
1281 face->variation_support |= TT_FACE_FLAG_VAR_MVAR;
1282 }
1283
1284
1285 static FT_Error
1286 tt_size_reset_iterator( FT_ListNode node,
1287 void* user )
1288 {
1290
1291 FT_UNUSED( user );
1292
1293
1294 tt_size_reset( size, 1 );
1295
1296 return FT_Err_Ok;
1297 }
1298
1299
1300 /*************************************************************************/
1301 /* */
1302 /* <Function> */
1303 /* tt_apply_mvar */
1304 /* */
1305 /* <Description> */
1306 /* Apply `MVAR' table adjustments. */
1307 /* */
1308 /* <InOut> */
1309 /* face :: The font face. */
1310 /* */
1311 FT_LOCAL_DEF( void )
1312 tt_apply_mvar( TT_Face face )
1313 {
1314 GX_Blend blend = face->blend;
1315 GX_Value value, limit;
1316
1317
1318 if ( !( face->variation_support & TT_FACE_FLAG_VAR_MVAR ) )
1319 return;
1320
1321 value = blend->mvar_table->values;
1322 limit = value + blend->mvar_table->valueCount;
1323
1324 for ( ; value < limit; value++ )
1325 {
1326 FT_Short* p = ft_var_get_value_pointer( face, value->tag );
1327 FT_Int delta;
1328
1329
1330 delta = ft_var_get_item_delta( face,
1331 &blend->mvar_table->itemStore,
1332 value->outerIndex,
1333 value->innerIndex );
1334
1335 if ( p )
1336 {
1337 FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s (MVAR)\n",
1338 (FT_Char)( value->tag >> 24 ),
1339 (FT_Char)( value->tag >> 16 ),
1340 (FT_Char)( value->tag >> 8 ),
1341 (FT_Char)( value->tag ),
1342 value->unmodified,
1343 value->unmodified == 1 ? "" : "s",
1344 delta,
1345 delta == 1 ? "" : "s" ));
1346
1347 /* since we handle both signed and unsigned values as FT_Short, */
1348 /* ensure proper overflow arithmetic */
1349 *p = (FT_Short)( value->unmodified + (FT_Short)delta );
1350 }
1351 }
1352
1353 /* adjust all derived values */
1354 {
1355 FT_Face root = &face->root;
1356
1357
1358 if ( face->os2.version != 0xFFFFU )
1359 {
1360 if ( face->os2.sTypoAscender || face->os2.sTypoDescender )
1361 {
1362 root->ascender = face->os2.sTypoAscender;
1363 root->descender = face->os2.sTypoDescender;
1364
1365 root->height = root->ascender - root->descender +
1366 face->os2.sTypoLineGap;
1367 }
1368 else
1369 {
1370 root->ascender = (FT_Short)face->os2.usWinAscent;
1371 root->descender = -(FT_Short)face->os2.usWinDescent;
1372
1373 root->height = root->ascender - root->descender;
1374 }
1375 }
1376
1377 root->underline_position = face->postscript.underlinePosition -
1378 face->postscript.underlineThickness / 2;
1379 root->underline_thickness = face->postscript.underlineThickness;
1380
1381 /* iterate over all FT_Size objects and call `tt_size_reset' */
1382 /* to propagate the metrics changes */
1383 FT_List_Iterate( &root->sizes_list,
1384 tt_size_reset_iterator,
1385 NULL );
1386 }
1387 }
1388
1389
1390 typedef struct GX_GVar_Head_
1391 {
1393 FT_UShort axisCount;
1394 FT_UShort globalCoordCount;
1395 FT_ULong offsetToCoord;
1396 FT_UShort glyphCount;
1398 FT_ULong offsetToData;
1399
1400 } GX_GVar_Head;
1401
1402
1403 /*************************************************************************/
1404 /* */
1405 /* <Function> */
1406 /* ft_var_load_gvar */
1407 /* */
1408 /* <Description> */
1409 /* Parse the `gvar' table if present. If `fvar' is there, `gvar' had */
1410 /* better be there too. */
1411 /* */
1412 /* <InOut> */
1413 /* face :: The font face. */
1414 /* */
1415 /* <Return> */
1416 /* FreeType error code. 0 means success. */
1417 /* */
1418 static FT_Error
1419 ft_var_load_gvar( TT_Face face )
1420 {
1422 FT_Memory memory = stream->memory;
1423 GX_Blend blend = face->blend;
1425 FT_UInt i, j;
1426 FT_ULong table_len;
1427 FT_ULong gvar_start;
1428 FT_ULong offsetToData;
1429 GX_GVar_Head gvar_head;
1430
1431 static const FT_Frame_Field gvar_fields[] =
1432 {
1433
1434#undef FT_STRUCTURE
1435#define FT_STRUCTURE GX_GVar_Head
1436
1437 FT_FRAME_START( 20 ),
1439 FT_FRAME_USHORT( axisCount ),
1440 FT_FRAME_USHORT( globalCoordCount ),
1441 FT_FRAME_ULONG ( offsetToCoord ),
1442 FT_FRAME_USHORT( glyphCount ),
1444 FT_FRAME_ULONG ( offsetToData ),
1446 };
1447
1448
1449 FT_TRACE2(( "GVAR " ));
1450
1451 if ( FT_SET_ERROR( face->goto_table( face,
1452 TTAG_gvar,
1453 stream,
1454 &table_len ) ) )
1455 {
1456 FT_TRACE2(( "is missing\n" ));
1457 goto Exit;
1458 }
1459
1460 gvar_start = FT_STREAM_POS( );
1461 if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
1462 goto Exit;
1463
1464 if ( gvar_head.version != 0x00010000L )
1465 {
1466 FT_TRACE1(( "bad table version\n" ));
1467 error = FT_THROW( Invalid_Table );
1468 goto Exit;
1469 }
1470
1471 if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
1472 {
1473 FT_TRACE1(( "ft_var_load_gvar: number of axes in `gvar' and `cvar'\n"
1474 " table are different\n" ));
1475 error = FT_THROW( Invalid_Table );
1476 goto Exit;
1477 }
1478
1479 /* rough sanity check, ignoring offsets */
1480 if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount >
1481 table_len / 2 )
1482 {
1483 FT_TRACE1(( "ft_var_load_gvar:"
1484 " invalid number of global coordinates\n" ));
1485 error = FT_THROW( Invalid_Table );
1486 goto Exit;
1487 }
1488
1489 /* rough sanity check: offsets can be either 2 or 4 bytes */
1490 if ( (FT_ULong)gvar_head.glyphCount *
1491 ( ( gvar_head.flags & 1 ) ? 4 : 2 ) > table_len )
1492 {
1493 FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" ));
1494 error = FT_THROW( Invalid_Table );
1495 goto Exit;
1496 }
1497
1498 FT_TRACE2(( "loaded\n" ));
1499
1500 blend->gvar_size = table_len;
1501 blend->tuplecount = gvar_head.globalCoordCount;
1502 blend->gv_glyphcnt = gvar_head.glyphCount;
1503 offsetToData = gvar_start + gvar_head.offsetToData;
1504
1505 FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n",
1506 blend->tuplecount == 1 ? "is" : "are",
1507 blend->tuplecount,
1508 blend->tuplecount == 1 ? "" : "s" ));
1509
1510 if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
1511 goto Exit;
1512
1513 if ( gvar_head.flags & 1 )
1514 {
1515 /* long offsets (one more offset than glyphs, to mark size of last) */
1516 if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
1517 goto Exit;
1518
1519 for ( i = 0; i <= blend->gv_glyphcnt; i++ )
1520 blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG();
1521
1522 FT_FRAME_EXIT();
1523 }
1524 else
1525 {
1526 /* short offsets (one more offset than glyphs, to mark size of last) */
1527 if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
1528 goto Exit;
1529
1530 for ( i = 0; i <= blend->gv_glyphcnt; i++ )
1531 blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
1532 /* XXX: Undocumented: `*2'! */
1533
1534 FT_FRAME_EXIT();
1535 }
1536
1537 if ( blend->tuplecount != 0 )
1538 {
1539 if ( FT_NEW_ARRAY( blend->tuplecoords,
1540 gvar_head.axisCount * blend->tuplecount ) )
1541 goto Exit;
1542
1543 if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) ||
1544 FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L ) )
1545 goto Exit;
1546
1547 for ( i = 0; i < blend->tuplecount; i++ )
1548 {
1549 FT_TRACE5(( " [ " ));
1550 for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ )
1551 {
1552 blend->tuplecoords[i * gvar_head.axisCount + j] =
1553 FT_GET_SHORT() * 4; /* convert to FT_Fixed */
1554 FT_TRACE5(( "%.5f ",
1555 blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 ));
1556 }
1557 FT_TRACE5(( "]\n" ));
1558 }
1559
1560 FT_TRACE5(( "\n" ));
1561
1562 FT_FRAME_EXIT();
1563 }
1564
1565 Exit:
1566 return error;
1567 }
1568
1569
1570 /*************************************************************************/
1571 /* */
1572 /* <Function> */
1573 /* ft_var_apply_tuple */
1574 /* */
1575 /* <Description> */
1576 /* Figure out whether a given tuple (design) applies to the current */
1577 /* blend, and if so, what is the scaling factor. */
1578 /* */
1579 /* <Input> */
1580 /* blend :: The current blend of the font. */
1581 /* */
1582 /* tupleIndex :: A flag saying whether this is an intermediate */
1583 /* tuple or not. */
1584 /* */
1585 /* tuple_coords :: The coordinates of the tuple in normalized axis */
1586 /* units. */
1587 /* */
1588 /* im_start_coords :: The initial coordinates where this tuple starts */
1589 /* to apply (for intermediate coordinates). */
1590 /* */
1591 /* im_end_coords :: The final coordinates after which this tuple no */
1592 /* longer applies (for intermediate coordinates). */
1593 /* */
1594 /* <Return> */
1595 /* An FT_Fixed value containing the scaling factor. */
1596 /* */
1597 static FT_Fixed
1598 ft_var_apply_tuple( GX_Blend blend,
1599 FT_UShort tupleIndex,
1600 FT_Fixed* tuple_coords,
1601 FT_Fixed* im_start_coords,
1602 FT_Fixed* im_end_coords )
1603 {
1604 FT_UInt i;
1605 FT_Fixed apply = 0x10000L;
1606
1607
1608 for ( i = 0; i < blend->num_axis; i++ )
1609 {
1610 FT_TRACE6(( " axis coordinate %d (%.5f):\n",
1611 i, blend->normalizedcoords[i] / 65536.0 ));
1612 if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
1613 FT_TRACE6(( " intermediate coordinates %d (%.5f, %.5f):\n",
1614 i,
1615 im_start_coords[i] / 65536.0,
1616 im_end_coords[i] / 65536.0 ));
1617
1618 /* It's not clear why (for intermediate tuples) we don't need */
1619 /* to check against start/end -- the documentation says we don't. */
1620 /* Similarly, it's unclear why we don't need to scale along the */
1621 /* axis. */
1622
1623 if ( tuple_coords[i] == 0 )
1624 {
1625 FT_TRACE6(( " tuple coordinate is zero, ignored\n", i ));
1626 continue;
1627 }
1628
1629 if ( blend->normalizedcoords[i] == 0 )
1630 {
1631 FT_TRACE6(( " axis coordinate is zero, stop\n" ));
1632 apply = 0;
1633 break;
1634 }
1635
1636 if ( blend->normalizedcoords[i] == tuple_coords[i] )
1637 {
1638 FT_TRACE6(( " tuple coordinate value %.5f fits perfectly\n",
1639 tuple_coords[i] / 65536.0 ));
1640 /* `apply' does not change */
1641 continue;
1642 }
1643
1644 if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
1645 {
1646 /* not an intermediate tuple */
1647
1648 if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) ||
1649 blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) )
1650 {
1651 FT_TRACE6(( " tuple coordinate value %.5f is exceeded, stop\n",
1652 tuple_coords[i] / 65536.0 ));
1653 apply = 0;
1654 break;
1655 }
1656
1657 FT_TRACE6(( " tuple coordinate value %.5f fits\n",
1658 tuple_coords[i] / 65536.0 ));
1659 apply = FT_MulDiv( apply,
1660 blend->normalizedcoords[i],
1661 tuple_coords[i] );
1662 }
1663 else
1664 {
1665 /* intermediate tuple */
1666
1667 if ( blend->normalizedcoords[i] < im_start_coords[i] ||
1668 blend->normalizedcoords[i] > im_end_coords[i] )
1669 {
1670 FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] is exceeded,"
1671 " stop\n",
1672 im_start_coords[i] / 65536.0,
1673 im_end_coords[i] / 65536.0 ));
1674 apply = 0;
1675 break;
1676 }
1677
1678 else if ( blend->normalizedcoords[i] < tuple_coords[i] )
1679 {
1680 FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] fits\n",
1681 im_start_coords[i] / 65536.0,
1682 im_end_coords[i] / 65536.0 ));
1683 apply = FT_MulDiv( apply,
1684 blend->normalizedcoords[i] - im_start_coords[i],
1685 tuple_coords[i] - im_start_coords[i] );
1686 }
1687
1688 else
1689 {
1690 FT_TRACE6(( " intermediate tuple range [%.5f;%.5f] fits\n",
1691 im_start_coords[i] / 65536.0,
1692 im_end_coords[i] / 65536.0 ));
1693 apply = FT_MulDiv( apply,
1694 im_end_coords[i] - blend->normalizedcoords[i],
1695 im_end_coords[i] - tuple_coords[i] );
1696 }
1697 }
1698 }
1699
1700 FT_TRACE6(( " apply factor is %.5f\n", apply / 65536.0 ));
1701
1702 return apply;
1703 }
1704
1705
1706 /* convert from design coordinates to normalized coordinates */
1707
1708 static void
1709 ft_var_to_normalized( TT_Face face,
1710 FT_UInt num_coords,
1713 {
1714 GX_Blend blend;
1715 FT_MM_Var* mmvar;
1716 FT_UInt i, j;
1717 FT_Var_Axis* a;
1718 GX_AVarSegment av;
1719
1720
1721 blend = face->blend;
1722 mmvar = blend->mmvar;
1723
1724 if ( num_coords > mmvar->num_axis )
1725 {
1726 FT_TRACE2(( "ft_var_to_normalized:"
1727 " only using first %d of %d coordinates\n",
1728 mmvar->num_axis, num_coords ));
1729 num_coords = mmvar->num_axis;
1730 }
1731
1732 /* Axis normalization is a two-stage process. First we normalize */
1733 /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
1734 /* Then, if there's an `avar' table, we renormalize this range. */
1735
1736 FT_TRACE5(( "%d design coordinate%s:\n",
1737 num_coords,
1738 num_coords == 1 ? "" : "s" ));
1739
1740 a = mmvar->axis;
1741 for ( i = 0; i < num_coords; i++, a++ )
1742 {
1744
1745
1746 FT_TRACE5(( " %.5f\n", coord / 65536.0 ));
1747 if ( coord > a->maximum || coord < a->minimum )
1748 {
1749 FT_TRACE1((
1750 "ft_var_to_normalized: design coordinate %.5f\n"
1751 " is out of range [%.5f;%.5f]; clamping\n",
1752 coord / 65536.0,
1753 a->minimum / 65536.0,
1754 a->maximum / 65536.0 ));
1755
1756 if ( coord > a->maximum )
1757 coord = a->maximum;
1758 else
1759 coord = a->minimum;
1760 }
1761
1762 if ( coord < a->def )
1763 normalized[i] = -FT_DivFix( coord - a->def,
1764 a->minimum - a->def );
1765 else if ( coord > a->def )
1766 normalized[i] = FT_DivFix( coord - a->def,
1767 a->maximum - a->def );
1768 else
1769 normalized[i] = 0;
1770 }
1771
1772 FT_TRACE5(( "\n" ));
1773
1774 for ( ; i < mmvar->num_axis; i++ )
1775 normalized[i] = 0;
1776
1777 if ( blend->avar_segment )
1778 {
1779 FT_TRACE5(( "normalized design coordinates"
1780 " before applying `avar' data:\n" ));
1781
1782 av = blend->avar_segment;
1783 for ( i = 0; i < mmvar->num_axis; i++, av++ )
1784 {
1785 for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1786 {
1787 if ( normalized[i] < av->correspondence[j].fromCoord )
1788 {
1789 FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 ));
1790
1791 normalized[i] =
1792 FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
1793 av->correspondence[j].toCoord -
1794 av->correspondence[j - 1].toCoord,
1795 av->correspondence[j].fromCoord -
1796 av->correspondence[j - 1].fromCoord ) +
1797 av->correspondence[j - 1].toCoord;
1798 break;
1799 }
1800 }
1801 }
1802 }
1803 }
1804
1805
1806 /* convert from normalized coordinates to design coordinates */
1807
1808 static void
1809 ft_var_to_design( TT_Face face,
1810 FT_UInt num_coords,
1812 FT_Fixed* design )
1813 {
1814 GX_Blend blend;
1815 FT_MM_Var* mmvar;
1816 FT_Var_Axis* a;
1817
1818 FT_UInt i, j, nc;
1819
1820
1821 blend = face->blend;
1822
1823 nc = num_coords;
1824 if ( num_coords > blend->num_axis )
1825 {
1826 FT_TRACE2(( "ft_var_to_design:"
1827 " only using first %d of %d coordinates\n",
1828 blend->num_axis, num_coords ));
1829 nc = blend->num_axis;
1830 }
1831
1832 for ( i = 0; i < nc; i++ )
1833 design[i] = coords[i];
1834
1835 for ( ; i < num_coords; i++ )
1836 design[i] = 0;
1837
1838 if ( blend->avar_segment )
1839 {
1840 GX_AVarSegment av = blend->avar_segment;
1841
1842
1843 FT_TRACE5(( "design coordinates"
1844 " after removing `avar' distortion:\n" ));
1845
1846 for ( i = 0; i < nc; i++, av++ )
1847 {
1848 for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1849 {
1850 if ( design[i] < av->correspondence[j].toCoord )
1851 {
1852 design[i] =
1853 FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord,
1854 av->correspondence[j].fromCoord -
1855 av->correspondence[j - 1].fromCoord,
1856 av->correspondence[j].toCoord -
1857 av->correspondence[j - 1].toCoord ) +
1858 av->correspondence[j - 1].fromCoord;
1859
1860 FT_TRACE5(( " %.5f\n", design[i] / 65536.0 ));
1861 break;
1862 }
1863 }
1864 }
1865 }
1866
1867 mmvar = blend->mmvar;
1868 a = mmvar->axis;
1869
1870 for ( i = 0; i < nc; i++, a++ )
1871 {
1872 if ( design[i] < 0 )
1873 design[i] = a->def + FT_MulFix( design[i],
1874 a->def - a->minimum );
1875 else if ( design[i] > 0 )
1876 design[i] = a->def + FT_MulFix( design[i],
1877 a->maximum - a->def );
1878 else
1879 design[i] = a->def;
1880 }
1881 }
1882
1883
1884 /*************************************************************************/
1885 /*************************************************************************/
1886 /***** *****/
1887 /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/
1888 /***** *****/
1889 /*************************************************************************/
1890 /*************************************************************************/
1891
1892
1893 typedef struct GX_FVar_Head_
1894 {
1896 FT_UShort offsetToData;
1897 FT_UShort axisCount;
1898 FT_UShort axisSize;
1899 FT_UShort instanceCount;
1900 FT_UShort instanceSize;
1901
1902 } GX_FVar_Head;
1903
1904
1905 typedef struct fvar_axis_
1906 {
1907 FT_ULong axisTag;
1908 FT_Fixed minValue;
1909 FT_Fixed defaultValue;
1910 FT_Fixed maxValue;
1912 FT_UShort nameID;
1913
1914 } GX_FVar_Axis;
1915
1916
1917 /*************************************************************************/
1918 /* */
1919 /* <Function> */
1920 /* TT_Get_MM_Var */
1921 /* */
1922 /* <Description> */
1923 /* Check that the font's `fvar' table is valid, parse it, and return */
1924 /* those data. It also loads (and parses) the `MVAR' table, if */
1925 /* possible. */
1926 /* */
1927 /* <InOut> */
1928 /* face :: The font face. */
1929 /* TT_Get_MM_Var initializes the blend structure. */
1930 /* */
1931 /* <Output> */
1932 /* master :: The `fvar' data (must be freed by caller). Can be NULL, */
1933 /* which makes this function simply load MM support. */
1934 /* */
1935 /* <Return> */
1936 /* FreeType error code. 0 means success. */
1937 /* */
1939 TT_Get_MM_Var( TT_Face face,
1940 FT_MM_Var* *master )
1941 {
1942 FT_Stream stream = face->root.stream;
1943 FT_Memory memory = face->root.memory;
1944 FT_ULong table_len;
1946 FT_ULong fvar_start = 0;
1947 FT_UInt i, j;
1948 FT_MM_Var* mmvar = NULL;
1949 FT_Fixed* next_coords;
1950 FT_Fixed* nsc;
1951 FT_String* next_name;
1952 FT_Var_Axis* a;
1953 FT_Fixed* c;
1955 GX_FVar_Head fvar_head;
1956 FT_Bool usePsName = 0;
1957 FT_UInt num_instances;
1958 FT_UInt num_axes;
1959 FT_UShort* axis_flags;
1960
1961 FT_Offset mmvar_size;
1962 FT_Offset axis_flags_size;
1963 FT_Offset axis_size;
1964 FT_Offset namedstyle_size;
1965 FT_Offset next_coords_size;
1966 FT_Offset next_name_size;
1967
1968 FT_Bool need_init;
1969
1970 static const FT_Frame_Field fvar_fields[] =
1971 {
1972
1973#undef FT_STRUCTURE
1974#define FT_STRUCTURE GX_FVar_Head
1975
1976 FT_FRAME_START( 16 ),
1978 FT_FRAME_USHORT ( offsetToData ),
1980 FT_FRAME_USHORT ( axisCount ),
1981 FT_FRAME_USHORT ( axisSize ),
1982 FT_FRAME_USHORT ( instanceCount ),
1983 FT_FRAME_USHORT ( instanceSize ),
1985 };
1986
1987 static const FT_Frame_Field fvaraxis_fields[] =
1988 {
1989
1990#undef FT_STRUCTURE
1991#define FT_STRUCTURE GX_FVar_Axis
1992
1993 FT_FRAME_START( 20 ),
1994 FT_FRAME_ULONG ( axisTag ),
1995 FT_FRAME_LONG ( minValue ),
1996 FT_FRAME_LONG ( defaultValue ),
1997 FT_FRAME_LONG ( maxValue ),
1999 FT_FRAME_USHORT( nameID ),
2001 };
2002
2003
2004 /* read the font data and set up the internal representation */
2005 /* if not already done */
2006
2007 need_init = !face->blend;
2008
2009 if ( need_init )
2010 {
2011 FT_TRACE2(( "FVAR " ));
2012
2013 /* both `fvar' and `gvar' must be present */
2014 if ( FT_SET_ERROR( face->goto_table( face, TTAG_gvar,
2015 stream, &table_len ) ) )
2016 {
2017 /* CFF2 is an alternate to gvar here */
2018 if ( FT_SET_ERROR( face->goto_table( face, TTAG_CFF2,
2019 stream, &table_len ) ) )
2020 {
2021 FT_TRACE1(( "\n"
2022 "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" ));
2023 goto Exit;
2024 }
2025 }
2026
2027 if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar,
2028 stream, &table_len ) ) )
2029 {
2030 FT_TRACE1(( "is missing\n" ));
2031 goto Exit;
2032 }
2033
2034 fvar_start = FT_STREAM_POS( );
2035
2036 /* the validity of the `fvar' header data was already checked */
2037 /* in function `sfnt_init_face' */
2038 if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
2039 goto Exit;
2040
2041 usePsName = FT_BOOL( fvar_head.instanceSize ==
2042 6 + 4 * fvar_head.axisCount );
2043
2044 FT_TRACE2(( "loaded\n" ));
2045
2046 FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount ));
2047
2048 if ( FT_NEW( face->blend ) )
2049 goto Exit;
2050
2051 num_axes = fvar_head.axisCount;
2052 }
2053 else
2054 num_axes = face->blend->num_axis;
2055
2056 /* `num_instances' holds the number of all named instances, */
2057 /* including the default instance which might be missing */
2058 /* in fvar's table of named instances */
2059 num_instances = (FT_UInt)face->root.style_flags >> 16;
2060
2061 /* prepare storage area for MM data; this cannot overflow */
2062 /* 32-bit arithmetic because of the size limits used in the */
2063 /* `fvar' table validity check in `sfnt_init_face' */
2064
2065 /* the various `*_size' variables, which we also use as */
2066 /* offsets into the `mmlen' array, must be multiples of the */
2067 /* pointer size (except the last one); without such an */
2068 /* alignment there might be runtime errors due to */
2069 /* misaligned addresses */
2070#undef ALIGN_SIZE
2071#define ALIGN_SIZE( n ) \
2072 ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) )
2073
2074 mmvar_size = ALIGN_SIZE( sizeof ( FT_MM_Var ) );
2075 axis_flags_size = ALIGN_SIZE( num_axes *
2076 sizeof ( FT_UShort ) );
2077 axis_size = ALIGN_SIZE( num_axes *
2078 sizeof ( FT_Var_Axis ) );
2079 namedstyle_size = ALIGN_SIZE( num_instances *
2080 sizeof ( FT_Var_Named_Style ) );
2081 next_coords_size = ALIGN_SIZE( num_instances *
2082 num_axes *
2083 sizeof ( FT_Fixed ) );
2084 next_name_size = num_axes * 5;
2085
2086 if ( need_init )
2087 {
2088 face->blend->mmvar_len = mmvar_size +
2089 axis_flags_size +
2090 axis_size +
2091 namedstyle_size +
2092 next_coords_size +
2093 next_name_size;
2094
2095 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2096 goto Exit;
2097 face->blend->mmvar = mmvar;
2098
2099 /* set up pointers and offsets into the `mmvar' array; */
2100 /* the data gets filled in later on */
2101
2102 mmvar->num_axis =
2103 num_axes;
2104 mmvar->num_designs =
2105 ~0U; /* meaningless in this context; each glyph */
2106 /* may have a different number of designs */
2107 /* (or tuples, as called by Apple) */
2108 mmvar->num_namedstyles =
2109 num_instances;
2110
2111 /* alas, no public field in `FT_Var_Axis' for axis flags */
2112 axis_flags =
2113 (FT_UShort*)( (char*)mmvar + mmvar_size );
2114 mmvar->axis =
2115 (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2116 mmvar->namedstyle =
2117 (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size );
2118
2119 next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2120 namedstyle_size );
2121 for ( i = 0; i < num_instances; i++ )
2122 {
2123 mmvar->namedstyle[i].coords = next_coords;
2124 next_coords += num_axes;
2125 }
2126
2127 next_name = (FT_String*)( (char*)mmvar->namedstyle +
2128 namedstyle_size + next_coords_size );
2129 for ( i = 0; i < num_axes; i++ )
2130 {
2131 mmvar->axis[i].name = next_name;
2132 next_name += 5;
2133 }
2134
2135 /* now fill in the data */
2136
2137 if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
2138 goto Exit;
2139
2140 a = mmvar->axis;
2141 for ( i = 0; i < num_axes; i++ )
2142 {
2143 GX_FVar_Axis axis_rec;
2144
2145
2146 if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
2147 goto Exit;
2148 a->tag = axis_rec.axisTag;
2149 a->minimum = axis_rec.minValue;
2150 a->def = axis_rec.defaultValue;
2151 a->maximum = axis_rec.maxValue;
2152 a->strid = axis_rec.nameID;
2153
2154 a->name[0] = (FT_String)( a->tag >> 24 );
2155 a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
2156 a->name[2] = (FT_String)( ( a->tag >> 8 ) & 0xFF );
2157 a->name[3] = (FT_String)( ( a->tag ) & 0xFF );
2158 a->name[4] = '\0';
2159
2160 *axis_flags = axis_rec.flags;
2161
2162 if ( a->minimum > a->def ||
2163 a->def > a->maximum )
2164 {
2165 FT_TRACE2(( "TT_Get_MM_Var:"
2166 " invalid \"%s\" axis record; disabling\n",
2167 a->name ));
2168
2169 a->minimum = a->def;
2170 a->maximum = a->def;
2171 }
2172
2173 FT_TRACE5(( " \"%s\":"
2174 " minimum=%.5f, default=%.5f, maximum=%.5f,"
2175 " flags=0x%04X\n",
2176 a->name,
2177 a->minimum / 65536.0,
2178 a->def / 65536.0,
2179 a->maximum / 65536.0,
2180 *axis_flags ));
2181
2182 a++;
2183 axis_flags++;
2184 }
2185
2186 FT_TRACE5(( "\n" ));
2187
2188 /* named instance coordinates are stored as design coordinates; */
2189 /* we have to convert them to normalized coordinates also */
2190 if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords,
2191 num_axes * num_instances ) )
2192 goto Exit;
2193
2194 if ( fvar_head.instanceCount && !face->blend->avar_loaded )
2195 {
2197
2198
2199 ft_var_load_avar( face );
2200
2201 if ( FT_STREAM_SEEK( offset ) )
2202 goto Exit;
2203 }
2204
2205 ns = mmvar->namedstyle;
2206 nsc = face->blend->normalized_stylecoords;
2207 for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
2208 {
2209 /* PostScript names add 2 bytes to the instance record size */
2210 if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) +
2211 4L * num_axes ) )
2212 goto Exit;
2213
2214 ns->strid = FT_GET_USHORT();
2215 (void) /* flags = */ FT_GET_USHORT();
2216
2217 c = ns->coords;
2218 for ( j = 0; j < num_axes; j++, c++ )
2219 *c = FT_GET_LONG();
2220
2221 /* valid psid values are 6, [256;32767], and 0xFFFF */
2222 if ( usePsName )
2223 ns->psid = FT_GET_USHORT();
2224 else
2225 ns->psid = 0xFFFF;
2226
2227 ft_var_to_normalized( face, num_axes, ns->coords, nsc );
2228 nsc += num_axes;
2229
2230 FT_FRAME_EXIT();
2231 }
2232
2233 if ( num_instances != fvar_head.instanceCount )
2234 {
2236
2237 FT_Int found, dummy1, dummy2;
2238 FT_UInt strid = ~0U;
2239
2240
2241 /* the default instance is missing in array the */
2242 /* of named instances; try to synthesize an entry */
2243 found = sfnt->get_name_id( face,
2245 &dummy1,
2246 &dummy2 );
2247 if ( found )
2249 else
2250 {
2251 found = sfnt->get_name_id( face,
2253 &dummy1,
2254 &dummy2 );
2255 if ( found )
2257 }
2258
2259 if ( found )
2260 {
2261 found = sfnt->get_name_id( face,
2263 &dummy1,
2264 &dummy2 );
2265 if ( found )
2266 {
2267 FT_TRACE5(( "TT_Get_MM_Var:"
2268 " Adding default instance to named instances\n" ));
2269
2270 ns = &mmvar->namedstyle[fvar_head.instanceCount];
2271
2272 ns->strid = strid;
2273 ns->psid = TT_NAME_ID_PS_NAME;
2274
2275 a = mmvar->axis;
2276 c = ns->coords;
2277 for ( j = 0; j < num_axes; j++, a++, c++ )
2278 *c = a->def;
2279 }
2280 }
2281 }
2282
2283 ft_var_load_mvar( face );
2284 }
2285
2286 /* fill the output array if requested */
2287
2288 if ( master )
2289 {
2290 FT_UInt n;
2291
2292
2293 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2294 goto Exit;
2295 FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
2296
2297 axis_flags =
2298 (FT_UShort*)( (char*)mmvar + mmvar_size );
2299 mmvar->axis =
2300 (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2301 mmvar->namedstyle =
2302 (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size );
2303
2304 next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2305 namedstyle_size );
2306 for ( n = 0; n < mmvar->num_namedstyles; n++ )
2307 {
2308 mmvar->namedstyle[n].coords = next_coords;
2309 next_coords += num_axes;
2310 }
2311
2312 a = mmvar->axis;
2313 next_name = (FT_String*)( (char*)mmvar->namedstyle +
2314 namedstyle_size + next_coords_size );
2315 for ( n = 0; n < num_axes; n++ )
2316 {
2317 a->name = next_name;
2318
2319 /* standard PostScript names for some standard apple tags */
2320 if ( a->tag == TTAG_wght )
2321 a->name = (char*)"Weight";
2322 else if ( a->tag == TTAG_wdth )
2323 a->name = (char*)"Width";
2324 else if ( a->tag == TTAG_opsz )
2325 a->name = (char*)"OpticalSize";
2326 else if ( a->tag == TTAG_slnt )
2327 a->name = (char*)"Slant";
2328
2329 next_name += 5;
2330 a++;
2331 }
2332
2333 *master = mmvar;
2334 }
2335
2336 Exit:
2337 return error;
2338 }
2339
2340
2341 static FT_Error
2342 tt_set_mm_blend( TT_Face face,
2343 FT_UInt num_coords,
2345 FT_Bool set_design_coords )
2346 {
2348 GX_Blend blend;
2349 FT_MM_Var* mmvar;
2350 FT_UInt i;
2351
2352 FT_Bool all_design_coords = FALSE;
2353
2354 FT_Memory memory = face->root.memory;
2355
2356 enum
2357 {
2358 mcvt_retain,
2359 mcvt_modify,
2360 mcvt_load
2361
2362 } manageCvt;
2363
2364
2365 face->doblend = FALSE;
2366
2367 if ( !face->blend )
2368 {
2369 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2370 goto Exit;
2371 }
2372
2373 blend = face->blend;
2374 mmvar = blend->mmvar;
2375
2376 if ( num_coords > mmvar->num_axis )
2377 {
2378 FT_TRACE2(( "TT_Set_MM_Blend:"
2379 " only using first %d of %d coordinates\n",
2380 mmvar->num_axis, num_coords ));
2381 num_coords = mmvar->num_axis;
2382 }
2383
2384 FT_TRACE5(( "normalized design coordinates:\n" ));
2385
2386 for ( i = 0; i < num_coords; i++ )
2387 {
2388 FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 ));
2389 if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
2390 {
2391 FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n"
2392 " is out of range [-1;1]\n",
2393 coords[i] / 65536.0 ));
2394 error = FT_THROW( Invalid_Argument );
2395 goto Exit;
2396 }
2397 }
2398
2399 FT_TRACE5(( "\n" ));
2400
2401 if ( !face->is_cff2 && !blend->glyphoffsets )
2402 if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) )
2403 goto Exit;
2404
2405 if ( !blend->coords )
2406 {
2407 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2408 goto Exit;
2409
2410 /* the first time we have to compute all design coordinates */
2411 all_design_coords = TRUE;
2412 }
2413
2414 if ( !blend->normalizedcoords )
2415 {
2416 if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) )
2417 goto Exit;
2418
2419 manageCvt = mcvt_modify;
2420
2421 /* If we have not set the blend coordinates before this, then the */
2422 /* cvt table will still be what we read from the `cvt ' table and */
2423 /* we don't need to reload it. We may need to change it though... */
2424 }
2425 else
2426 {
2427 FT_Bool have_diff = 0;
2428 FT_UInt j;
2429 FT_Fixed* c;
2430 FT_Fixed* n;
2431
2432
2433 manageCvt = mcvt_retain;
2434
2435 for ( i = 0; i < num_coords; i++ )
2436 {
2437 if ( blend->normalizedcoords[i] != coords[i] )
2438 {
2439 manageCvt = mcvt_load;
2440 have_diff = 1;
2441 break;
2442 }
2443 }
2444
2445 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2446 {
2447 FT_UInt idx = (FT_UInt)face->root.face_index >> 16;
2448
2449
2450 c = blend->normalizedcoords + i;
2451 n = blend->normalized_stylecoords + idx * mmvar->num_axis + i;
2452 for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
2453 if ( *c != *n )
2454 have_diff = 1;
2455 }
2456 else
2457 {
2458 c = blend->normalizedcoords + i;
2459 for ( j = i; j < mmvar->num_axis; j++, c++ )
2460 if ( *c != 0 )
2461 have_diff = 1;
2462 }
2463
2464 /* return value -1 indicates `no change' */
2465 if ( !have_diff )
2466 return -1;
2467
2468 for ( ; i < mmvar->num_axis; i++ )
2469 {
2470 if ( blend->normalizedcoords[i] != 0 )
2471 {
2472 manageCvt = mcvt_load;
2473 break;
2474 }
2475 }
2476
2477 /* If we don't change the blend coords then we don't need to do */
2478 /* anything to the cvt table. It will be correct. Otherwise we */
2479 /* no longer have the original cvt (it was modified when we set */
2480 /* the blend last time), so we must reload and then modify it. */
2481 }
2482
2483 blend->num_axis = mmvar->num_axis;
2484 FT_MEM_COPY( blend->normalizedcoords,
2485 coords,
2486 num_coords * sizeof ( FT_Fixed ) );
2487
2488 if ( set_design_coords )
2489 ft_var_to_design( face,
2490 all_design_coords ? blend->num_axis : num_coords,
2491 blend->normalizedcoords,
2492 blend->coords );
2493
2494 face->doblend = TRUE;
2495
2496 if ( face->cvt )
2497 {
2498 switch ( manageCvt )
2499 {
2500 case mcvt_load:
2501 /* The cvt table has been loaded already; every time we change the */
2502 /* blend we may need to reload and remodify the cvt table. */
2503 FT_FREE( face->cvt );
2504 face->cvt = NULL;
2505
2506 error = tt_face_load_cvt( face, face->root.stream );
2507 break;
2508
2509 case mcvt_modify:
2510 /* The original cvt table is in memory. All we need to do is */
2511 /* apply the `cvar' table (if any). */
2512 error = tt_face_vary_cvt( face, face->root.stream );
2513 break;
2514
2515 case mcvt_retain:
2516 /* The cvt table is correct for this set of coordinates. */
2517 break;
2518 }
2519 }
2520
2521 /* enforce recomputation of the PostScript name; */
2522 FT_FREE( face->postscript_name );
2523 face->postscript_name = NULL;
2524
2525 Exit:
2526 return error;
2527 }
2528
2529
2530 /*************************************************************************/
2531 /* */
2532 /* <Function> */
2533 /* TT_Set_MM_Blend */
2534 /* */
2535 /* <Description> */
2536 /* Set the blend (normalized) coordinates for this instance of the */
2537 /* font. Check that the `gvar' table is reasonable and does some */
2538 /* initial preparation. */
2539 /* */
2540 /* <InOut> */
2541 /* face :: The font. */
2542 /* Initialize the blend structure with `gvar' data. */
2543 /* */
2544 /* <Input> */
2545 /* num_coords :: The number of available coordinates. If it is */
2546 /* larger than the number of axes, ignore the excess */
2547 /* values. If it is smaller than the number of axes, */
2548 /* use the default value (0) for the remaining axes. */
2549 /* */
2550 /* coords :: An array of `num_coords', each between [-1,1]. */
2551 /* */
2552 /* <Return> */
2553 /* FreeType error code. 0 means success. */
2554 /* */
2556 TT_Set_MM_Blend( TT_Face face,
2557 FT_UInt num_coords,
2558 FT_Fixed* coords )
2559 {
2561
2562
2563 error = tt_set_mm_blend( face, num_coords, coords, 1 );
2564 if ( error )
2565 return error;
2566
2567 if ( num_coords )
2568 face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2569 else
2570 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2571
2572 return FT_Err_Ok;
2573 }
2574
2575
2576 /*************************************************************************/
2577 /* */
2578 /* <Function> */
2579 /* TT_Get_MM_Blend */
2580 /* */
2581 /* <Description> */
2582 /* Get the blend (normalized) coordinates for this instance of the */
2583 /* font. */
2584 /* */
2585 /* <InOut> */
2586 /* face :: The font. */
2587 /* Initialize the blend structure with `gvar' data. */
2588 /* */
2589 /* <Input> */
2590 /* num_coords :: The number of available coordinates. If it is */
2591 /* larger than the number of axes, set the excess */
2592 /* values to 0. */
2593 /* */
2594 /* coords :: An array of `num_coords', each between [-1,1]. */
2595 /* */
2596 /* <Return> */
2597 /* FreeType error code. 0 means success. */
2598 /* */
2600 TT_Get_MM_Blend( TT_Face face,
2601 FT_UInt num_coords,
2602 FT_Fixed* coords )
2603 {
2605 GX_Blend blend;
2606 FT_UInt i, nc;
2607
2608
2609 if ( !face->blend )
2610 {
2611 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2612 return error;
2613 }
2614
2615 blend = face->blend;
2616
2617 if ( !blend->coords )
2618 {
2619 /* select default instance coordinates */
2620 /* if no instance is selected yet */
2621 if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2622 return error;
2623 }
2624
2625 nc = num_coords;
2626 if ( num_coords > blend->num_axis )
2627 {
2628 FT_TRACE2(( "TT_Get_MM_Blend:"
2629 " only using first %d of %d coordinates\n",
2630 blend->num_axis, num_coords ));
2631 nc = blend->num_axis;
2632 }
2633
2634 if ( face->doblend )
2635 {
2636 for ( i = 0; i < nc; i++ )
2637 coords[i] = blend->normalizedcoords[i];
2638 }
2639 else
2640 {
2641 for ( i = 0; i < nc; i++ )
2642 coords[i] = 0;
2643 }
2644
2645 for ( ; i < num_coords; i++ )
2646 coords[i] = 0;
2647
2648 return FT_Err_Ok;
2649 }
2650
2651
2652 /*************************************************************************/
2653 /* */
2654 /* <Function> */
2655 /* TT_Set_Var_Design */
2656 /* */
2657 /* <Description> */
2658 /* Set the coordinates for the instance, measured in the user */
2659 /* coordinate system. Parse the `avar' table (if present) to convert */
2660 /* from user to normalized coordinates. */
2661 /* */
2662 /* <InOut> */
2663 /* face :: The font face. */
2664 /* Initialize the blend struct with `gvar' data. */
2665 /* */
2666 /* <Input> */
2667 /* num_coords :: The number of available coordinates. If it is */
2668 /* larger than the number of axes, ignore the excess */
2669 /* values. If it is smaller than the number of axes, */
2670 /* use the default values for the remaining axes. */
2671 /* */
2672 /* coords :: A coordinate array with `num_coords' elements. */
2673 /* */
2674 /* <Return> */
2675 /* FreeType error code. 0 means success. */
2676 /* */
2678 TT_Set_Var_Design( TT_Face face,
2679 FT_UInt num_coords,
2680 FT_Fixed* coords )
2681 {
2683 GX_Blend blend;
2684 FT_MM_Var* mmvar;
2685 FT_UInt i;
2686 FT_Memory memory = face->root.memory;
2687
2688 FT_Fixed* c;
2689 FT_Fixed* n;
2691
2692 FT_Bool have_diff = 0;
2693
2694
2695 if ( !face->blend )
2696 {
2697 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2698 goto Exit;
2699 }
2700
2701 blend = face->blend;
2702 mmvar = blend->mmvar;
2703
2704 if ( num_coords > mmvar->num_axis )
2705 {
2706 FT_TRACE2(( "TT_Set_Var_Design:"
2707 " only using first %d of %d coordinates\n",
2708 mmvar->num_axis, num_coords ));
2709 num_coords = mmvar->num_axis;
2710 }
2711
2712 if ( !blend->coords )
2713 {
2714 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2715 goto Exit;
2716 }
2717
2718 c = blend->coords;
2719 n = coords;
2720 for ( i = 0; i < num_coords; i++, n++, c++ )
2721 {
2722 if ( *c != *n )
2723 {
2724 *c = *n;
2725 have_diff = 1;
2726 }
2727 }
2728
2729 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2730 {
2731 FT_UInt instance_index;
2732 FT_Var_Named_Style* named_style;
2733
2734
2735 instance_index = (FT_UInt)face->root.face_index >> 16;
2736 named_style = mmvar->namedstyle + instance_index - 1;
2737
2738 n = named_style->coords + num_coords;
2739 for ( ; i < mmvar->num_axis; i++, n++, c++ )
2740 {
2741 if ( *c != *n )
2742 {
2743 *c = *n;
2744 have_diff = 1;
2745 }
2746 }
2747 }
2748 else
2749 {
2750 FT_Var_Axis* a;
2751
2752
2753 a = mmvar->axis + num_coords;
2754 for ( ; i < mmvar->num_axis; i++, a++, c++ )
2755 {
2756 if ( *c != a->def )
2757 {
2758 *c = a->def;
2759 have_diff = 1;
2760 }
2761 }
2762 }
2763
2764 /* return value -1 indicates `no change' */
2765 if ( !have_diff )
2766 return -1;
2767
2768 if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
2769 goto Exit;
2770
2771 if ( !face->blend->avar_loaded )
2772 ft_var_load_avar( face );
2773
2774 ft_var_to_normalized( face, num_coords, blend->coords, normalized );
2775
2776 error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 );
2777 if ( error )
2778 goto Exit;
2779
2780 if ( num_coords )
2781 face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2782 else
2783 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2784
2785 Exit:
2787 return error;
2788 }
2789
2790
2791 /*************************************************************************/
2792 /* */
2793 /* <Function> */
2794 /* TT_Get_Var_Design */
2795 /* */
2796 /* <Description> */
2797 /* Get the design coordinates of the currently selected interpolated */
2798 /* font. */
2799 /* */
2800 /* <Input> */
2801 /* face :: A handle to the source face. */
2802 /* */
2803 /* num_coords :: The number of design coordinates to retrieve. If it */
2804 /* is larger than the number of axes, set the excess */
2805 /* values to~0. */
2806 /* */
2807 /* <Output> */
2808 /* coords :: The design coordinates array. */
2809 /* */
2810 /* <Return> */
2811 /* FreeType error code. 0~means success. */
2812 /* */
2814 TT_Get_Var_Design( TT_Face face,
2815 FT_UInt num_coords,
2816 FT_Fixed* coords )
2817 {
2819 GX_Blend blend;
2820 FT_UInt i, nc;
2821
2822
2823 if ( !face->blend )
2824 {
2825 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2826 return error;
2827 }
2828
2829 blend = face->blend;
2830
2831 if ( !blend->coords )
2832 {
2833 /* select default instance coordinates */
2834 /* if no instance is selected yet */
2835 if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2836 return error;
2837 }
2838
2839 nc = num_coords;
2840 if ( num_coords > blend->num_axis )
2841 {
2842 FT_TRACE2(( "TT_Get_Var_Design:"
2843 " only using first %d of %d coordinates\n",
2844 blend->num_axis, num_coords ));
2845 nc = blend->num_axis;
2846 }
2847
2848 if ( face->doblend )
2849 {
2850 for ( i = 0; i < nc; i++ )
2851 coords[i] = blend->coords[i];
2852 }
2853 else
2854 {
2855 for ( i = 0; i < nc; i++ )
2856 coords[i] = 0;
2857 }
2858
2859 for ( ; i < num_coords; i++ )
2860 coords[i] = 0;
2861
2862 return FT_Err_Ok;
2863 }
2864
2865
2866 /*************************************************************************/
2867 /* */
2868 /* <Function> */
2869 /* TT_Set_Named_Instance */
2870 /* */
2871 /* <Description> */
2872 /* Set the given named instance, also resetting any further */
2873 /* variation. */
2874 /* */
2875 /* <Input> */
2876 /* face :: A handle to the source face. */
2877 /* */
2878 /* instance_index :: The instance index, starting with value 1. */
2879 /* Value 0 indicates to not use an instance. */
2880 /* */
2881 /* <Return> */
2882 /* FreeType error code. 0~means success. */
2883 /* */
2885 TT_Set_Named_Instance( TT_Face face,
2886 FT_UInt instance_index )
2887 {
2888 FT_Error error = FT_ERR( Invalid_Argument );
2889 GX_Blend blend;
2890 FT_MM_Var* mmvar;
2891
2892 FT_UInt num_instances;
2893
2894
2895 if ( !face->blend )
2896 {
2897 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2898 goto Exit;
2899 }
2900
2901 blend = face->blend;
2902 mmvar = blend->mmvar;
2903
2904 num_instances = (FT_UInt)face->root.style_flags >> 16;
2905
2906 /* `instance_index' starts with value 1, thus `>' */
2907 if ( instance_index > num_instances )
2908 goto Exit;
2909
2910 if ( instance_index > 0 && mmvar->namedstyle )
2911 {
2912 FT_Memory memory = face->root.memory;
2914
2915 FT_Var_Named_Style* named_style;
2916 FT_String* style_name;
2917
2918
2919 named_style = mmvar->namedstyle + instance_index - 1;
2920
2921 error = sfnt->get_name( face,
2922 (FT_UShort)named_style->strid,
2923 &style_name );
2924 if ( error )
2925 goto Exit;
2926
2927 /* set (or replace) style name */
2928 FT_FREE( face->root.style_name );
2929 face->root.style_name = style_name;
2930
2931 /* finally, select the named instance */
2932 error = TT_Set_Var_Design( face,
2933 mmvar->num_axis,
2934 named_style->coords );
2935 if ( error )
2936 goto Exit;
2937 }
2938 else
2939 error = TT_Set_Var_Design( face, 0, NULL );
2940
2941 face->root.face_index = ( instance_index << 16 ) |
2942 ( face->root.face_index & 0xFFFFL );
2943 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2944
2945 Exit:
2946 return error;
2947 }
2948
2949
2950 /*************************************************************************/
2951 /*************************************************************************/
2952 /***** *****/
2953 /***** GX VAR PARSING ROUTINES *****/
2954 /***** *****/
2955 /*************************************************************************/
2956 /*************************************************************************/
2957
2958
2959 /*************************************************************************/
2960 /* */
2961 /* <Function> */
2962 /* tt_face_vary_cvt */
2963 /* */
2964 /* <Description> */
2965 /* Modify the loaded cvt table according to the `cvar' table and the */
2966 /* font's blend. */
2967 /* */
2968 /* <InOut> */
2969 /* face :: A handle to the target face object. */
2970 /* */
2971 /* <Input> */
2972 /* stream :: A handle to the input stream. */
2973 /* */
2974 /* <Return> */
2975 /* FreeType error code. 0 means success. */
2976 /* */
2977 /* Most errors are ignored. It is perfectly valid not to have a */
2978 /* `cvar' table even if there is a `gvar' and `fvar' table. */
2979 /* */
2981 tt_face_vary_cvt( TT_Face face,
2983 {
2985 FT_Memory memory = stream->memory;
2986 FT_ULong table_start;
2987 FT_ULong table_len;
2988 FT_UInt tupleCount;
2989 FT_ULong offsetToData;
2990 FT_ULong here;
2991 FT_UInt i, j;
2992 FT_Fixed* tuple_coords = NULL;
2993 FT_Fixed* im_start_coords = NULL;
2994 FT_Fixed* im_end_coords = NULL;
2995 GX_Blend blend = face->blend;
2996 FT_UInt point_count, spoint_count = 0;
2997 FT_UShort* sharedpoints = NULL;
2998 FT_UShort* localpoints = NULL;
3000 FT_Short* deltas;
3001
3002
3003 FT_TRACE2(( "CVAR " ));
3004
3005 if ( !blend )
3006 {
3007 FT_TRACE2(( "\n"
3008 "tt_face_vary_cvt: no blend specified\n" ));
3009 error = FT_Err_Ok;
3010 goto Exit;
3011 }
3012
3013 if ( !face->cvt )
3014 {
3015 FT_TRACE2(( "\n"
3016 "tt_face_vary_cvt: no `cvt ' table\n" ));
3017 error = FT_Err_Ok;
3018 goto Exit;
3019 }
3020
3021 error = face->goto_table( face, TTAG_cvar, stream, &table_len );
3022 if ( error )
3023 {
3024 FT_TRACE2(( "is missing\n" ));
3025
3026 error = FT_Err_Ok;
3027 goto Exit;
3028 }
3029
3030 if ( FT_FRAME_ENTER( table_len ) )
3031 {
3032 error = FT_Err_Ok;
3033 goto Exit;
3034 }
3035
3036 table_start = FT_Stream_FTell( stream );
3037 if ( FT_GET_LONG() != 0x00010000L )
3038 {
3039 FT_TRACE2(( "bad table version\n" ));
3040
3041 error = FT_Err_Ok;
3042 goto FExit;
3043 }
3044
3045 FT_TRACE2(( "loaded\n" ));
3046
3047 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) ||
3048 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3049 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) )
3050 goto FExit;
3051
3052 tupleCount = FT_GET_USHORT();
3053 offsetToData = FT_GET_USHORT();
3054
3055 /* rough sanity test */
3056 if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3057 table_len )
3058 {
3059 FT_TRACE2(( "tt_face_vary_cvt:"
3060 " invalid CVT variation array header\n" ));
3061
3062 error = FT_THROW( Invalid_Table );
3063 goto FExit;
3064 }
3065
3066 offsetToData += table_start;
3067
3068 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3069 {
3070 here = FT_Stream_FTell( stream );
3071
3072 FT_Stream_SeekSet( stream, offsetToData );
3073
3074 sharedpoints = ft_var_readpackedpoints( stream,
3075 table_len,
3076 &spoint_count );
3077 offsetToData = FT_Stream_FTell( stream );
3078
3079 FT_Stream_SeekSet( stream, here );
3080 }
3081
3082 FT_TRACE5(( "cvar: there %s %d tuple%s:\n",
3083 ( tupleCount & 0xFFF ) == 1 ? "is" : "are",
3084 tupleCount & 0xFFF,
3085 ( tupleCount & 0xFFF ) == 1 ? "" : "s" ));
3086
3087 for ( i = 0; i < ( tupleCount & 0xFFF ); i++ )
3088 {
3089 FT_UInt tupleDataSize;
3090 FT_UInt tupleIndex;
3091 FT_Fixed apply;
3092
3093
3094 FT_TRACE6(( " tuple %d:\n", i ));
3095
3096 tupleDataSize = FT_GET_USHORT();
3097 tupleIndex = FT_GET_USHORT();
3098
3099 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3100 {
3101 for ( j = 0; j < blend->num_axis; j++ )
3102 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */
3103 /* short frac to fixed */
3104 }
3105 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3106 {
3107 FT_TRACE2(( "tt_face_vary_cvt:"
3108 " invalid tuple index\n" ));
3109
3110 error = FT_THROW( Invalid_Table );
3111 goto Exit;
3112 }
3113 else
3115 tuple_coords,
3116 &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
3117 blend->num_axis * sizeof ( FT_Fixed ) );
3118
3119 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3120 {
3121 for ( j = 0; j < blend->num_axis; j++ )
3122 im_start_coords[j] = FT_GET_SHORT() * 4;
3123 for ( j = 0; j < blend->num_axis; j++ )
3124 im_end_coords[j] = FT_GET_SHORT() * 4;
3125 }
3126
3127 apply = ft_var_apply_tuple( blend,
3128 (FT_UShort)tupleIndex,
3129 tuple_coords,
3130 im_start_coords,
3131 im_end_coords );
3132
3133 if ( apply == 0 ) /* tuple isn't active for our blend */
3134 {
3135 offsetToData += tupleDataSize;
3136 continue;
3137 }
3138
3139 here = FT_Stream_FTell( stream );
3140
3141 FT_Stream_SeekSet( stream, offsetToData );
3142
3143 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3144 {
3145 localpoints = ft_var_readpackedpoints( stream,
3146 table_len,
3147 &point_count );
3148 points = localpoints;
3149 }
3150 else
3151 {
3152 points = sharedpoints;
3153 point_count = spoint_count;
3154 }
3155
3156 deltas = ft_var_readpackeddeltas( stream,
3157 table_len,
3158 point_count == 0 ? face->cvt_size
3159 : point_count );
3160
3161 if ( !points ||
3162 !deltas ||
3163 ( localpoints == ALL_POINTS && point_count != face->cvt_size ) )
3164 ; /* failure, ignore it */
3165
3166 else if ( localpoints == ALL_POINTS )
3167 {
3168#ifdef FT_DEBUG_LEVEL_TRACE
3169 int count = 0;
3170#endif
3171
3172
3173 FT_TRACE7(( " CVT deltas:\n" ));
3174
3175 /* this means that there are deltas for every entry in cvt */
3176 for ( j = 0; j < face->cvt_size; j++ )
3177 {
3178 FT_Long orig_cvt = face->cvt[j];
3179
3180
3181 face->cvt[j] = (FT_Short)( orig_cvt +
3182 FT_MulFix( deltas[j], apply ) );
3183
3184#ifdef FT_DEBUG_LEVEL_TRACE
3185 if ( orig_cvt != face->cvt[j] )
3186 {
3187 FT_TRACE7(( " %d: %d -> %d\n",
3188 j, orig_cvt, face->cvt[j] ));
3189 count++;
3190 }
3191#endif
3192 }
3193
3194#ifdef FT_DEBUG_LEVEL_TRACE
3195 if ( !count )
3196 FT_TRACE7(( " none\n" ));
3197#endif
3198 }
3199
3200 else
3201 {
3202#ifdef FT_DEBUG_LEVEL_TRACE
3203 int count = 0;
3204#endif
3205
3206
3207 FT_TRACE7(( " CVT deltas:\n" ));
3208
3209 for ( j = 0; j < point_count; j++ )
3210 {
3211 int pindex;
3212 FT_Long orig_cvt;
3213
3214
3215 pindex = points[j];
3216 if ( (FT_ULong)pindex >= face->cvt_size )
3217 continue;
3218
3219 orig_cvt = face->cvt[pindex];
3220 face->cvt[pindex] = (FT_Short)( orig_cvt +
3221 FT_MulFix( deltas[j], apply ) );
3222
3223#ifdef FT_DEBUG_LEVEL_TRACE
3224 if ( orig_cvt != face->cvt[pindex] )
3225 {
3226 FT_TRACE7(( " %d: %d -> %d\n",
3227 pindex, orig_cvt, face->cvt[pindex] ));
3228 count++;
3229 }
3230#endif
3231 }
3232
3233#ifdef FT_DEBUG_LEVEL_TRACE
3234 if ( !count )
3235 FT_TRACE7(( " none\n" ));
3236#endif
3237 }
3238
3239 if ( localpoints != ALL_POINTS )
3240 FT_FREE( localpoints );
3241 FT_FREE( deltas );
3242
3243 offsetToData += tupleDataSize;
3244
3245 FT_Stream_SeekSet( stream, here );
3246 }
3247
3248 FT_TRACE5(( "\n" ));
3249
3250 FExit:
3251 FT_FRAME_EXIT();
3252
3253 Exit:
3254 if ( sharedpoints != ALL_POINTS )
3255 FT_FREE( sharedpoints );
3256 FT_FREE( tuple_coords );
3257 FT_FREE( im_start_coords );
3258 FT_FREE( im_end_coords );
3259
3260 return error;
3261 }
3262
3263
3264 /* Shift the original coordinates of all points between indices `p1' */
3265 /* and `p2', using the same difference as given by index `ref'. */
3266
3267 /* modeled after `af_iup_shift' */
3268
3269 static void
3270 tt_delta_shift( int p1,
3271 int p2,
3272 int ref,
3273 FT_Vector* in_points,
3274 FT_Vector* out_points )
3275 {
3276 int p;
3277 FT_Vector delta;
3278
3279
3280 delta.x = out_points[ref].x - in_points[ref].x;
3281 delta.y = out_points[ref].y - in_points[ref].y;
3282
3283 if ( delta.x == 0 && delta.y == 0 )
3284 return;
3285
3286 for ( p = p1; p < ref; p++ )
3287 {
3288 out_points[p].x += delta.x;
3289 out_points[p].y += delta.y;
3290 }
3291
3292 for ( p = ref + 1; p <= p2; p++ )
3293 {
3294 out_points[p].x += delta.x;
3295 out_points[p].y += delta.y;
3296 }
3297 }
3298
3299
3300 /* Interpolate the original coordinates of all points with indices */
3301 /* between `p1' and `p2', using `ref1' and `ref2' as the reference */
3302 /* point indices. */
3303
3304 /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */
3305 /* `Ins_IUP' */
3306
3307 static void
3308 tt_delta_interpolate( int p1,
3309 int p2,
3310 int ref1,
3311 int ref2,
3312 FT_Vector* in_points,
3313 FT_Vector* out_points )
3314 {
3315 int p, i;
3316
3317 FT_Pos out, in1, in2, out1, out2, d1, d2;
3318
3319
3320 if ( p1 > p2 )
3321 return;
3322
3323 /* handle both horizontal and vertical coordinates */
3324 for ( i = 0; i <= 1; i++ )
3325 {
3326 /* shift array pointers so that we can access `foo.y' as `foo.x' */
3327 in_points = (FT_Vector*)( (FT_Pos*)in_points + i );
3328 out_points = (FT_Vector*)( (FT_Pos*)out_points + i );
3329
3330 if ( in_points[ref1].x > in_points[ref2].x )
3331 {
3332 p = ref1;
3333 ref1 = ref2;
3334 ref2 = p;
3335 }
3336
3337 in1 = in_points[ref1].x;
3338 in2 = in_points[ref2].x;
3339 out1 = out_points[ref1].x;
3340 out2 = out_points[ref2].x;
3341 d1 = out1 - in1;
3342 d2 = out2 - in2;
3343
3344 /* If the reference points have the same coordinate but different */
3345 /* delta, inferred delta is zero. Otherwise interpolate. */
3346 if ( in1 != in2 || out1 == out2 )
3347 {
3348 FT_Fixed scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 )
3349 : 0;
3350
3351
3352 for ( p = p1; p <= p2; p++ )
3353 {
3354 out = in_points[p].x;
3355
3356 if ( out <= in1 )
3357 out += d1;
3358 else if ( out >= in2 )
3359 out += d2;
3360 else
3361 out = out1 + FT_MulFix( out - in1, scale );
3362
3363 out_points[p].x = out;
3364 }
3365 }
3366 }
3367 }
3368
3369
3370 /* Interpolate points without delta values, similar to */
3371 /* the `IUP' hinting instruction. */
3372
3373 /* modeled after `Ins_IUP */
3374
3375 static void
3376 tt_interpolate_deltas( FT_Outline* outline,
3377 FT_Vector* out_points,
3378 FT_Vector* in_points,
3379 FT_Bool* has_delta )
3380 {
3381 FT_Int first_point;
3382 FT_Int end_point;
3383
3384 FT_Int first_delta;
3385 FT_Int cur_delta;
3386
3387 FT_Int point;
3388 FT_Short contour;
3389
3390
3391 /* ignore empty outlines */
3392 if ( !outline->n_contours )
3393 return;
3394
3395 contour = 0;
3396 point = 0;
3397
3398 do
3399 {
3400 end_point = outline->contours[contour];
3401 first_point = point;
3402
3403 /* search first point that has a delta */
3404 while ( point <= end_point && !has_delta[point] )
3405 point++;
3406
3407 if ( point <= end_point )
3408 {
3409 first_delta = point;
3410 cur_delta = point;
3411
3412 point++;
3413
3414 while ( point <= end_point )
3415 {
3416 /* search next point that has a delta */
3417 /* and interpolate intermediate points */
3418 if ( has_delta[point] )
3419 {
3420 tt_delta_interpolate( cur_delta + 1,
3421 point - 1,
3422 cur_delta,
3423 point,
3424 in_points,
3425 out_points );
3426 cur_delta = point;
3427 }
3428
3429 point++;
3430 }
3431
3432 /* shift contour if we only have a single delta */
3433 if ( cur_delta == first_delta )
3434 tt_delta_shift( first_point,
3435 end_point,
3436 cur_delta,
3437 in_points,
3438 out_points );
3439 else
3440 {
3441 /* otherwise handle remaining points */
3442 /* at the end and beginning of the contour */
3443 tt_delta_interpolate( cur_delta + 1,
3444 end_point,
3445 cur_delta,
3446 first_delta,
3447 in_points,
3448 out_points );
3449
3450 if ( first_delta > 0 )
3451 tt_delta_interpolate( first_point,
3452 first_delta - 1,
3453 cur_delta,
3454 first_delta,
3455 in_points,
3456 out_points );
3457 }
3458 }
3459 contour++;
3460
3461 } while ( contour < outline->n_contours );
3462 }
3463
3464
3465 /*************************************************************************/
3466 /* */
3467 /* <Function> */
3468 /* TT_Vary_Apply_Glyph_Deltas */
3469 /* */
3470 /* <Description> */
3471 /* Apply the appropriate deltas to the current glyph. */
3472 /* */
3473 /* <Input> */
3474 /* face :: A handle to the target face object. */
3475 /* */
3476 /* glyph_index :: The index of the glyph being modified. */
3477 /* */
3478 /* n_points :: The number of the points in the glyph, including */
3479 /* phantom points. */
3480 /* */
3481 /* <InOut> */
3482 /* outline :: The outline to change. */
3483 /* */
3484 /* <Return> */
3485 /* FreeType error code. 0 means success. */
3486 /* */
3488 TT_Vary_Apply_Glyph_Deltas( TT_Face face,
3489 FT_UInt glyph_index,
3491 FT_UInt n_points )
3492 {
3493 FT_Stream stream = face->root.stream;
3494 FT_Memory memory = stream->memory;
3495 GX_Blend blend = face->blend;
3496
3497 FT_Vector* points_org = NULL;
3498 FT_Vector* points_out = NULL;
3499 FT_Bool* has_delta = NULL;
3500
3502 FT_ULong glyph_start;
3503 FT_UInt tupleCount;
3504 FT_ULong offsetToData;
3505 FT_ULong here;
3506 FT_UInt i, j;
3507 FT_Fixed* tuple_coords = NULL;
3508 FT_Fixed* im_start_coords = NULL;
3509 FT_Fixed* im_end_coords = NULL;
3510 FT_UInt point_count, spoint_count = 0;
3511 FT_UShort* sharedpoints = NULL;
3512 FT_UShort* localpoints = NULL;
3514 FT_Short *deltas_x, *deltas_y;
3515
3516
3517 if ( !face->doblend || !blend )
3518 return FT_THROW( Invalid_Argument );
3519
3520 if ( glyph_index >= blend->gv_glyphcnt ||
3521 blend->glyphoffsets[glyph_index] ==
3522 blend->glyphoffsets[glyph_index + 1] )
3523 {
3524 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3525 " no variation data for this glyph\n" ));
3526 return FT_Err_Ok;
3527 }
3528
3529 if ( FT_NEW_ARRAY( points_org, n_points ) ||
3530 FT_NEW_ARRAY( points_out, n_points ) ||
3531 FT_NEW_ARRAY( has_delta, n_points ) )
3532 goto Fail1;
3533
3534 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) ||
3535 FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
3536 blend->glyphoffsets[glyph_index] ) )
3537 goto Fail1;
3538
3539 glyph_start = FT_Stream_FTell( stream );
3540
3541 /* each set of glyph variation data is formatted similarly to `cvar' */
3542
3543 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) ||
3544 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3545 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) )
3546 goto Fail2;
3547
3548 tupleCount = FT_GET_USHORT();
3549 offsetToData = FT_GET_USHORT();
3550
3551 /* rough sanity test */
3552 if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3553 blend->gvar_size )
3554 {
3555 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3556 " invalid glyph variation array header\n" ));
3557
3558 error = FT_THROW( Invalid_Table );
3559 goto Fail2;
3560 }
3561
3562 offsetToData += glyph_start;
3563
3564 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3565 {
3566 here = FT_Stream_FTell( stream );
3567
3568 FT_Stream_SeekSet( stream, offsetToData );
3569
3570 sharedpoints = ft_var_readpackedpoints( stream,
3571 blend->gvar_size,
3572 &spoint_count );
3573 offsetToData = FT_Stream_FTell( stream );
3574
3575 FT_Stream_SeekSet( stream, here );
3576 }
3577
3578 FT_TRACE5(( "gvar: there %s %d tuple%s:\n",
3579 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3580 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3581 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
3582
3583 for ( j = 0; j < n_points; j++ )
3584 points_org[j] = outline->points[j];
3585
3586 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
3587 {
3588 FT_UInt tupleDataSize;
3589 FT_UInt tupleIndex;
3590 FT_Fixed apply;
3591
3592
3593 FT_TRACE6(( " tuple %d:\n", i ));
3594
3595 tupleDataSize = FT_GET_USHORT();
3596 tupleIndex = FT_GET_USHORT();
3597
3598 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3599 {
3600 for ( j = 0; j < blend->num_axis; j++ )
3601 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */
3602 /* short frac to fixed */
3603 }
3604 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3605 {
3606 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3607 " invalid tuple index\n" ));
3608
3609 error = FT_THROW( Invalid_Table );
3610 goto Fail2;
3611 }
3612 else
3614 tuple_coords,
3615 &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
3616 blend->num_axis * sizeof ( FT_Fixed ) );
3617
3618 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3619 {
3620 for ( j = 0; j < blend->num_axis; j++ )
3621 im_start_coords[j] = FT_GET_SHORT() * 4;
3622 for ( j = 0; j < blend->num_axis; j++ )
3623 im_end_coords[j] = FT_GET_SHORT() * 4;
3624 }
3625
3626 apply = ft_var_apply_tuple( blend,
3627 (FT_UShort)tupleIndex,
3628 tuple_coords,
3629 im_start_coords,
3630 im_end_coords );
3631
3632 if ( apply == 0 ) /* tuple isn't active for our blend */
3633 {
3634 offsetToData += tupleDataSize;
3635 continue;
3636 }
3637
3638 here = FT_Stream_FTell( stream );
3639
3640 FT_Stream_SeekSet( stream, offsetToData );
3641
3642 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3643 {
3644 localpoints = ft_var_readpackedpoints( stream,
3645 blend->gvar_size,
3646 &point_count );
3647 points = localpoints;
3648 }
3649 else
3650 {
3651 points = sharedpoints;
3652 point_count = spoint_count;
3653 }
3654
3655 deltas_x = ft_var_readpackeddeltas( stream,
3656 blend->gvar_size,
3657 point_count == 0 ? n_points
3658 : point_count );
3659 deltas_y = ft_var_readpackeddeltas( stream,
3660 blend->gvar_size,
3661 point_count == 0 ? n_points
3662 : point_count );
3663
3664 if ( !points || !deltas_y || !deltas_x )
3665 ; /* failure, ignore it */
3666
3667 else if ( points == ALL_POINTS )
3668 {
3669#ifdef FT_DEBUG_LEVEL_TRACE
3670 int count = 0;
3671#endif
3672
3673
3674 FT_TRACE7(( " point deltas:\n" ));
3675
3676 /* this means that there are deltas for every point in the glyph */
3677 for ( j = 0; j < n_points; j++ )
3678 {
3679 FT_Pos delta_x = FT_MulFix( deltas_x[j], apply );
3680 FT_Pos delta_y = FT_MulFix( deltas_y[j], apply );
3681
3682
3683 if ( j < n_points - 4 )
3684 {
3685 outline->points[j].x += delta_x;
3686 outline->points[j].y += delta_y;
3687 }
3688 else
3689 {
3690 /* To avoid double adjustment of advance width or height, */
3691 /* adjust phantom points only if there is no HVAR or VVAR */
3692 /* support, respectively. */
3693 if ( j == ( n_points - 4 ) &&
3694 !( face->variation_support &
3696 outline->points[j].x += delta_x;
3697
3698 else if ( j == ( n_points - 3 ) &&
3699 !( face->variation_support &
3701 outline->points[j].x += delta_x;
3702
3703 else if ( j == ( n_points - 2 ) &&
3704 !( face->variation_support &
3706 outline->points[j].y += delta_y;
3707
3708 else if ( j == ( n_points - 1 ) &&
3709 !( face->variation_support &
3711 outline->points[j].y += delta_y;
3712 }
3713
3714#ifdef FT_DEBUG_LEVEL_TRACE
3715 if ( delta_x || delta_y )
3716 {
3717 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n",
3718 j,
3719 outline->points[j].x - delta_x,
3720 outline->points[j].y - delta_y,
3721 outline->points[j].x,
3722 outline->points[j].y ));
3723 count++;
3724 }
3725#endif
3726 }
3727
3728#ifdef FT_DEBUG_LEVEL_TRACE
3729 if ( !count )
3730 FT_TRACE7(( " none\n" ));
3731#endif
3732 }
3733
3734 else
3735 {
3736#ifdef FT_DEBUG_LEVEL_TRACE
3737 int count = 0;
3738#endif
3739
3740
3741 /* we have to interpolate the missing deltas similar to the */
3742 /* IUP bytecode instruction */
3743 for ( j = 0; j < n_points; j++ )
3744 {
3745 has_delta[j] = FALSE;
3746 points_out[j] = points_org[j];
3747 }
3748
3749 for ( j = 0; j < point_count; j++ )
3750 {
3751 FT_UShort idx = points[j];
3752
3753
3754 if ( idx >= n_points )
3755 continue;
3756
3757 has_delta[idx] = TRUE;
3758
3759 points_out[idx].x += FT_MulFix( deltas_x[j], apply );
3760 points_out[idx].y += FT_MulFix( deltas_y[j], apply );
3761 }
3762
3763 /* no need to handle phantom points here, */
3764 /* since solitary points can't be interpolated */
3765 tt_interpolate_deltas( outline,
3766 points_out,
3767 points_org,
3768 has_delta );
3769
3770 FT_TRACE7(( " point deltas:\n" ));
3771
3772 for ( j = 0; j < n_points; j++ )
3773 {
3774 FT_Pos delta_x = points_out[j].x - points_org[j].x;
3775 FT_Pos delta_y = points_out[j].y - points_org[j].y;
3776
3777
3778 if ( j < n_points - 4 )
3779 {
3780 outline->points[j].x += delta_x;
3781 outline->points[j].y += delta_y;
3782 }
3783 else
3784 {
3785 /* To avoid double adjustment of advance width or height, */
3786 /* adjust phantom points only if there is no HVAR or VVAR */
3787 /* support, respectively. */
3788 if ( j == ( n_points - 4 ) &&
3789 !( face->variation_support &
3791 outline->points[j].x += delta_x;
3792
3793 else if ( j == ( n_points - 3 ) &&
3794 !( face->variation_support &
3796 outline->points[j].x += delta_x;
3797
3798 else if ( j == ( n_points - 2 ) &&
3799 !( face->variation_support &
3801 outline->points[j].y += delta_y;
3802
3803 else if ( j == ( n_points - 1 ) &&
3804 !( face->variation_support &
3806 outline->points[j].y += delta_y;
3807 }
3808
3809#ifdef FT_DEBUG_LEVEL_TRACE
3810 if ( delta_x || delta_y )
3811 {
3812 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n",
3813 j,
3814 outline->points[j].x - delta_x,
3815 outline->points[j].y - delta_y,
3816 outline->points[j].x,
3817 outline->points[j].y ));
3818 count++;
3819 }
3820#endif
3821 }
3822
3823#ifdef FT_DEBUG_LEVEL_TRACE
3824 if ( !count )
3825 FT_TRACE7(( " none\n" ));
3826#endif
3827 }
3828
3829 if ( localpoints != ALL_POINTS )
3830 FT_FREE( localpoints );
3831 FT_FREE( deltas_x );
3832 FT_FREE( deltas_y );
3833
3834 offsetToData += tupleDataSize;
3835
3836 FT_Stream_SeekSet( stream, here );
3837 }
3838
3839 FT_TRACE5(( "\n" ));
3840
3841 Fail2:
3842 if ( sharedpoints != ALL_POINTS )
3843 FT_FREE( sharedpoints );
3844 FT_FREE( tuple_coords );
3845 FT_FREE( im_start_coords );
3846 FT_FREE( im_end_coords );
3847
3848 FT_FRAME_EXIT();
3849
3850 Fail1:
3851 FT_FREE( points_org );
3852 FT_FREE( points_out );
3853 FT_FREE( has_delta );
3854
3855 return error;
3856 }
3857
3858
3859 /*************************************************************************/
3860 /* */
3861 /* <Function> */
3862 /* tt_get_var_blend */
3863 /* */
3864 /* <Description> */
3865 /* An extended internal version of `TT_Get_MM_Blend' that returns */
3866 /* pointers instead of copying data, without any initialization of */
3867 /* the MM machinery in case it isn't loaded yet. */
3868 /* */
3870 tt_get_var_blend( TT_Face face,
3871 FT_UInt *num_coords,
3872 FT_Fixed* *coords,
3873 FT_Fixed* *normalizedcoords,
3874 FT_MM_Var* *mm_var )
3875 {
3876 if ( face->blend )
3877 {
3878 if ( num_coords )
3879 *num_coords = face->blend->num_axis;
3880 if ( coords )
3881 *coords = face->blend->coords;
3882 if ( normalizedcoords )
3883 *normalizedcoords = face->blend->normalizedcoords;
3884 if ( mm_var )
3885 *mm_var = face->blend->mmvar;
3886 }
3887 else
3888 {
3889 if ( num_coords )
3890 *num_coords = 0;
3891 if ( coords )
3892 *coords = NULL;
3893 if ( mm_var )
3894 *mm_var = NULL;
3895 }
3896
3897 return FT_Err_Ok;
3898 }
3899
3900
3901 static void
3902 ft_var_done_item_variation_store( TT_Face face,
3903 GX_ItemVarStore itemStore )
3904 {
3906 FT_UInt i;
3907
3908
3909 if ( itemStore->varData )
3910 {
3911 for ( i = 0; i < itemStore->dataCount; i++ )
3912 {
3913 FT_FREE( itemStore->varData[i].regionIndices );
3914 FT_FREE( itemStore->varData[i].deltaSet );
3915 }
3916
3917 FT_FREE( itemStore->varData );
3918 }
3919
3920 if ( itemStore->varRegionList )
3921 {
3922 for ( i = 0; i < itemStore->regionCount; i++ )
3923 FT_FREE( itemStore->varRegionList[i].axisList );
3924
3925 FT_FREE( itemStore->varRegionList );
3926 }
3927 }
3928
3929
3930 /*************************************************************************/
3931 /* */
3932 /* <Function> */
3933 /* tt_done_blend */
3934 /* */
3935 /* <Description> */
3936 /* Free the blend internal data structure. */
3937 /* */
3938 FT_LOCAL_DEF( void )
3939 tt_done_blend( TT_Face face )
3940 {
3942 GX_Blend blend = face->blend;
3943
3944
3945 if ( blend )
3946 {
3947 FT_UInt i, num_axes;
3948
3949
3950 /* blend->num_axis might not be set up yet */
3951 num_axes = blend->mmvar->num_axis;
3952
3953 FT_FREE( blend->coords );
3954 FT_FREE( blend->normalizedcoords );
3955 FT_FREE( blend->normalized_stylecoords );
3956 FT_FREE( blend->mmvar );
3957
3958 if ( blend->avar_segment )
3959 {
3960 for ( i = 0; i < num_axes; i++ )
3961 FT_FREE( blend->avar_segment[i].correspondence );
3962 FT_FREE( blend->avar_segment );
3963 }
3964
3965 if ( blend->hvar_table )
3966 {
3967 ft_var_done_item_variation_store( face,
3968 &blend->hvar_table->itemStore );
3969
3970 FT_FREE( blend->hvar_table->widthMap.innerIndex );
3971 FT_FREE( blend->hvar_table->widthMap.outerIndex );
3972 FT_FREE( blend->hvar_table );
3973 }
3974
3975 if ( blend->vvar_table )
3976 {
3977 ft_var_done_item_variation_store( face,
3978 &blend->vvar_table->itemStore );
3979
3980 FT_FREE( blend->vvar_table->widthMap.innerIndex );
3981 FT_FREE( blend->vvar_table->widthMap.outerIndex );
3982 FT_FREE( blend->vvar_table );
3983 }
3984
3985 if ( blend->mvar_table )
3986 {
3987 ft_var_done_item_variation_store( face,
3988 &blend->mvar_table->itemStore );
3989
3990 FT_FREE( blend->mvar_table->values );
3991 FT_FREE( blend->mvar_table );
3992 }
3993
3994 FT_FREE( blend->tuplecoords );
3995 FT_FREE( blend->glyphoffsets );
3996 FT_FREE( blend );
3997 }
3998 }
3999
4000#else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4001
4002 /* ANSI C doesn't like empty source files */
4003 typedef int _tt_gxvar_dummy;
4004
4005#endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4006
4007
4008/* END */
void user(int argc, const char *argv[])
Definition: cmds.c:1350
#define U(x)
Definition: wordpad.c:45
#define FT_fdot14ToFixed(x)
Definition: cffload.c:1117
#define FT_FIXED_ONE
Definition: cffload.c:38
Definition: _map.h:48
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
unsigned int idx
Definition: utils.c:41
static const WCHAR version[]
Definition: asmname.c:66
POINTL point
Definition: edittest.c:50
FT_DivFix(FT_Long a, FT_Long b)
Definition: ftcalc.c:608
#define FT_FACE_FLAG_VARIATION
Definition: freetype.h:1253
#define FT_IS_NAMED_INSTANCE(face)
Definition: freetype.h:1424
FT_MulDiv(FT_Long a, FT_Long b, FT_Long c)
Definition: ftcalc.c:416
FT_MulFix(FT_Long a, FT_Long b)
Definition: ftcalc.c:509
return FT_Err_Ok
Definition: ftbbox.c:511
#define FT_LOCAL_DEF(x)
Definition: ftconfig.h:388
#define FT_UNUSED(arg)
Definition: ftconfig.h:101
#define FT_TRACE5(varformat)
Definition: ftdebug.h:162
#define FT_TRACE7(varformat)
Definition: ftdebug.h:164
#define FT_TRACE6(varformat)
Definition: ftdebug.h:163
#define FT_THROW(e)
Definition: ftdebug.h:213
#define FT_TRACE2(varformat)
Definition: ftdebug.h:159
#define FT_TRACE1(varformat)
Definition: ftdebug.h:158
FT_BEGIN_HEADER typedef signed long FT_Pos
Definition: ftimage.h:58
FT_List_Iterate(FT_List list, FT_List_Iterator iterator, void *user)
Definition: ftutil.c:381
#define FT_NEW_ARRAY(ptr, count)
Definition: ftmemory.h:333
#define FT_NEW(ptr)
Definition: ftmemory.h:331
#define FT_SET_ERROR(expression)
Definition: ftmemory.h:42
#define FT_ALLOC(ptr, size)
Definition: ftmemory.h:303
#define FT_FREE(ptr)
Definition: ftmemory.h:329
#define FT_MEM_COPY(dest, source, count)
Definition: ftmemory.h:228
#define FT_FACE(x)
Definition: ftobjs.h:630
#define FT_MIN(a, b)
Definition: ftobjs.h:71
#define FT_FACE_STREAM(x)
Definition: ftobjs.h:637
#define FT_FACE_MEMORY(x)
Definition: ftobjs.h:636
#define FT_MAX(a, b)
Definition: ftobjs.h:72
#define FT_FRAME_ENTER(size)
Definition: ftstream.h:512
#define FT_READ_USHORT(var)
Definition: ftstream.h:309
#define FT_FRAME_SKIP_SHORT
Definition: ftstream.h:137
#define FT_FRAME_END
Definition: ftstream.h:118
#define FT_GET_ULONG()
Definition: ftstream.h:293
#define FT_GET_CHAR()
Definition: ftstream.h:286
#define FT_STREAM_SEEK(position)
Definition: ftstream.h:489
#define FT_READ_ULONG(var)
Definition: ftstream.h:313
#define FT_STREAM_POS()
Definition: ftstream.h:486
#define FT_GET_SHORT()
Definition: ftstream.h:288
#define FT_FRAME_ULONG(f)
Definition: ftstream.h:121
#define FT_GET_BYTE()
Definition: ftstream.h:287
#define FT_READ_BYTE(var)
Definition: ftstream.h:306
#define FT_READ_CHAR(var)
Definition: ftstream.h:307
#define FT_FRAME_EXIT()
Definition: ftstream.h:517
#define FT_STREAM_READ_FIELDS(fields, object)
Definition: ftstream.h:508
#define FT_STREAM_SKIP(distance)
Definition: ftstream.h:493
#define FT_GET_LONG()
Definition: ftstream.h:292
#define FT_FRAME_START(size)
Definition: ftstream.h:117
#define FT_FRAME_USHORT(f)
Definition: ftstream.h:123
#define FT_READ_SHORT(var)
Definition: ftstream.h:308
#define FT_FRAME_LONG(f)
Definition: ftstream.h:120
#define FT_GET_USHORT()
Definition: ftstream.h:289
typedefFT_BEGIN_HEADER struct FT_MemoryRec_ * FT_Memory
Definition: ftsystem.h:66
signed char FT_Char
Definition: fttypes.h:143
FT_BEGIN_HEADER typedef unsigned char FT_Bool
Definition: fttypes.h:108
unsigned long FT_ULong
Definition: fttypes.h:253
unsigned char FT_Byte
Definition: fttypes.h:154
signed long FT_Fixed
Definition: fttypes.h:288
int FT_Error
Definition: fttypes.h:300
signed long FT_Long
Definition: fttypes.h:242
#define FT_ERR(e)
Definition: fttypes.h:586
unsigned short FT_UShort
Definition: fttypes.h:209
char FT_String
Definition: fttypes.h:187
signed short FT_Short
Definition: fttypes.h:198
unsigned int FT_UInt
Definition: fttypes.h:231
#define FT_BOOL(x)
Definition: fttypes.h:578
size_t FT_Offset
Definition: fttypes.h:324
signed int FT_Int
Definition: fttypes.h:220
GLuint start
Definition: gl.h:1545
GLint GLint GLint GLint GLint x
Definition: gl.h:1548
GLuint GLuint end
Definition: gl.h:1545
GLuint GLuint GLsizei count
Definition: gl.h:1545
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl.h:1950
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl.h:1546
GLsizeiptr size
Definition: glext.h:5919
GLdouble n
Definition: glext.h:7729
GLenum GLenum GLenum GLenum GLenum scale
Definition: glext.h:9032
GLuint coord
Definition: glext.h:9511
const GLubyte * c
Definition: glext.h:8905
GLuint coords
Definition: glext.h:7368
GLenum GLuint GLint GLenum face
Definition: glext.h:7025
GLint limit
Definition: glext.h:10326
GLbitfield flags
Definition: glext.h:7161
const GLint * first
Definition: glext.h:5794
GLint GLenum GLboolean normalized
Definition: glext.h:6117
GLfloat GLfloat p
Definition: glext.h:8902
GLboolean GLboolean GLboolean GLboolean a
Definition: glext.h:6204
GLsizei const GLfloat * points
Definition: glext.h:8112
GLintptr offset
Definition: glext.h:5920
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint GLint GLint j
Definition: glfuncs.h:250
#define a
Definition: ke_i.h:78
#define c
Definition: ke_i.h:80
if(dx< 0)
Definition: linetemp.h:194
#define error(str)
Definition: mkdosfs.c:1605
#define for
Definition: utility.h:88
#define TT_NAME_ID_FONT_SUBFAMILY
Definition: font.c:3537
#define ALIGN_SIZE(size, alignment)
Definition: loader.c:39
static char memory[1024 *256]
Definition: process.c:116
int k
Definition: mpi.c:3369
static FILE * out
Definition: regtests2xml.c:44
SFNT_Interface * SFNT_Service
Definition: sfnt.h:628
static void Exit(void)
Definition: sock.c:1330
FT_Var_Axis * axis
Definition: ftmm.h:242
FT_UInt num_designs
Definition: ftmm.h:240
FT_UInt num_namedstyles
Definition: ftmm.h:241
FT_UInt num_axis
Definition: ftmm.h:239
FT_Var_Named_Style * namedstyle
Definition: ftmm.h:243
FT_Fixed def
Definition: ftmm.h:154
FT_String * name
Definition: ftmm.h:151
FT_Fixed * coords
Definition: ftmm.h:186
FT_UInt strid
Definition: ftmm.h:187
FT_Pos x
Definition: ftimage.h:76
FT_Pos y
Definition: ftimage.h:77
TT_Get_Name_ID_Func get_name_id
Definition: sfnt.h:622
TT_Get_Name_Func get_name
Definition: sfnt.h:621
Definition: mxnamespace.c:45
Definition: mesh.c:5330
Definition: send.c:48
Definition: parse.h:23
SFNT_Service sfnt
Definition: ttdriver.c:206
int _tt_gxvar_dummy
Definition: ttgxvar.c:4003
#define TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY
Definition: ttnameid.h:812
#define TT_NAME_ID_PS_NAME
Definition: ttnameid.h:799
tt_size_reset(TT_Size size, FT_Bool only_height)
Definition: ttobjs.c:1251
tt_face_load_cvt(TT_Face face, FT_Stream stream)
Definition: ttpload.c:318
#define TTAG_avar
Definition: tttags.h:36
#define TTAG_fvar
Definition: tttags.h:58
#define TTAG_HVAR
Definition: tttags.h:65
#define TTAG_gvar
Definition: tttags.h:64
#define TTAG_cvar
Definition: tttags.h:49
#define TTAG_CFF2
Definition: tttags.h:46
#define TTAG_VVAR
Definition: tttags.h:106
#define TTAG_MVAR
Definition: tttags.h:84
#define TT_FACE_FLAG_VAR_HADVANCE
Definition: tttypes.h:1113
#define TT_FACE_FLAG_VAR_LSB
Definition: tttypes.h:1114
#define TT_FACE_FLAG_VAR_TSB
Definition: tttypes.h:1119
struct TT_SizeRec_ * TT_Size
Definition: tttypes.h:1631
#define TT_FACE_FLAG_VAR_VADVANCE
Definition: tttypes.h:1118
#define TT_FACE_FLAG_VAR_MVAR
Definition: tttypes.h:1124
Definition: dlist.c:348
GLvoid * data
Definition: dlist.c:359
Definition: pdh_main.c:94
ActualNumberDriverObjects * sizeof(PDRIVER_OBJECT)) PDRIVER_OBJECT *DriverObjectList