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