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