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