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