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