• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
ft_size_reset_iterator(FT_ListNode node,void * user)1384   ft_size_reset_iterator( FT_ListNode  node,
1385                           void*        user )
1386   {
1387     FT_Size                       size = (FT_Size)node->data;
1388     FT_Service_MetricsVariations  var  = (FT_Service_MetricsVariations)user;
1389 
1390 
1391     var->size_reset( size );
1392 
1393     return FT_Err_Ok;
1394   }
1395 
1396 
1397   /**************************************************************************
1398    *
1399    * @Function:
1400    *   tt_apply_mvar
1401    *
1402    * @Description:
1403    *   Apply `MVAR' table adjustments.
1404    *
1405    * @InOut:
1406    *   face ::
1407    *     The font face.
1408    */
1409   FT_LOCAL_DEF( void )
tt_apply_mvar(TT_Face face)1410   tt_apply_mvar( TT_Face  face )
1411   {
1412     GX_Blend  blend = face->blend;
1413     GX_Value  value, limit;
1414     FT_Short  mvar_hasc_delta = 0;
1415     FT_Short  mvar_hdsc_delta = 0;
1416     FT_Short  mvar_hlgp_delta = 0;
1417 
1418 
1419     if ( !( face->variation_support & TT_FACE_FLAG_VAR_MVAR ) )
1420       return;
1421 
1422     value = blend->mvar_table->values;
1423     limit = value + blend->mvar_table->valueCount;
1424 
1425     for ( ; value < limit; value++ )
1426     {
1427       FT_Short*  p = ft_var_get_value_pointer( face, value->tag );
1428       FT_Int     delta;
1429 
1430 
1431       delta = ft_var_get_item_delta( face,
1432                                      &blend->mvar_table->itemStore,
1433                                      value->outerIndex,
1434                                      value->innerIndex );
1435 
1436       if ( p )
1437       {
1438         FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s (MVAR)\n",
1439                     (FT_Char)( value->tag >> 24 ),
1440                     (FT_Char)( value->tag >> 16 ),
1441                     (FT_Char)( value->tag >> 8 ),
1442                     (FT_Char)( value->tag ),
1443                     value->unmodified,
1444                     value->unmodified == 1 ? "" : "s",
1445                     delta,
1446                     delta == 1 ? "" : "s" ));
1447 
1448         /* since we handle both signed and unsigned values as FT_Short, */
1449         /* ensure proper overflow arithmetic                            */
1450         *p = (FT_Short)( value->unmodified + (FT_Short)delta );
1451 
1452         /* Treat hasc, hdsc and hlgp specially, see below. */
1453         if ( value->tag == MVAR_TAG_HASC )
1454           mvar_hasc_delta = (FT_Short)delta;
1455         else if ( value->tag == MVAR_TAG_HDSC )
1456           mvar_hdsc_delta = (FT_Short)delta;
1457         else if ( value->tag == MVAR_TAG_HLGP )
1458           mvar_hlgp_delta = (FT_Short)delta;
1459       }
1460     }
1461 
1462     /* adjust all derived values */
1463     {
1464       FT_Service_MetricsVariations  var =
1465         (FT_Service_MetricsVariations)face->face_var;
1466 
1467       FT_Face  root = &face->root;
1468 
1469       /*
1470        * Apply the deltas of hasc, hdsc and hlgp to the FT_Face's ascender,
1471        * descender and height attributes, no matter how they were originally
1472        * computed.
1473        *
1474        * (Code that ignores those and accesses the font's metrics values
1475        * directly is already served by the delta application code above.)
1476        *
1477        * The MVAR table supports variations for both typo and win metrics.
1478        * According to Behdad Esfahbod, the thinking of the working group was
1479        * that no one uses win metrics anymore for setting line metrics (the
1480        * specification even calls these metrics "horizontal clipping
1481        * ascent/descent", probably for their role on the Windows platform in
1482        * computing clipping boxes), and new fonts should use typo metrics, so
1483        * typo deltas should be applied to whatever sfnt_load_face decided the
1484        * line metrics should be.
1485        *
1486        * Before, the following led to different line metrics between default
1487        * outline and instances, visible when e.g. the default outlines were
1488        * used as the regular face and instances for everything else:
1489        *
1490        * 1. sfnt_load_face applied the hhea metrics by default.
1491        * 2. This code later applied the typo metrics by default, regardless of
1492        *    whether they were actually changed or the font had the OS/2 table's
1493        *    fsSelection's bit 7 (USE_TYPO_METRICS) set.
1494        */
1495       FT_Short  current_line_gap = root->height - root->ascender +
1496                                    root->descender;
1497 
1498 
1499       root->ascender  = root->ascender + mvar_hasc_delta;
1500       root->descender = root->descender + mvar_hdsc_delta;
1501       root->height    = root->ascender - root->descender +
1502                         current_line_gap + mvar_hlgp_delta;
1503 
1504       root->underline_position  = face->postscript.underlinePosition -
1505                                   face->postscript.underlineThickness / 2;
1506       root->underline_thickness = face->postscript.underlineThickness;
1507 
1508       /* iterate over all FT_Size objects and call `var->size_reset' */
1509       /* to propagate the metrics changes                            */
1510       if ( var && var->size_reset )
1511         FT_List_Iterate( &root->sizes_list,
1512                          ft_size_reset_iterator,
1513                          (void*)var );
1514     }
1515   }
1516 
1517 
1518   typedef struct  GX_GVar_Head_
1519   {
1520     FT_Long    version;
1521     FT_UShort  axisCount;
1522     FT_UShort  globalCoordCount;
1523     FT_ULong   offsetToCoord;
1524     FT_UShort  glyphCount;
1525     FT_UShort  flags;
1526     FT_ULong   offsetToData;
1527 
1528   } GX_GVar_Head;
1529 
1530 
1531   /**************************************************************************
1532    *
1533    * @Function:
1534    *   ft_var_load_gvar
1535    *
1536    * @Description:
1537    *   Parse the `gvar' table if present.  If `fvar' is there, `gvar' had
1538    *   better be there too.
1539    *
1540    * @InOut:
1541    *   face ::
1542    *     The font face.
1543    *
1544    * @Return:
1545    *   FreeType error code.  0 means success.
1546    */
1547   static FT_Error
ft_var_load_gvar(TT_Face face)1548   ft_var_load_gvar( TT_Face  face )
1549   {
1550     FT_Stream     stream = FT_FACE_STREAM( face );
1551     FT_Memory     memory = stream->memory;
1552     GX_Blend      blend  = face->blend;
1553     FT_Error      error;
1554     FT_UInt       i, j;
1555     FT_ULong      table_len;
1556     FT_ULong      gvar_start;
1557     FT_ULong      offsetToData;
1558     FT_ULong      offsets_len;
1559     GX_GVar_Head  gvar_head;
1560 
1561     static const FT_Frame_Field  gvar_fields[] =
1562     {
1563 
1564 #undef  FT_STRUCTURE
1565 #define FT_STRUCTURE  GX_GVar_Head
1566 
1567       FT_FRAME_START( 20 ),
1568         FT_FRAME_LONG  ( version ),
1569         FT_FRAME_USHORT( axisCount ),
1570         FT_FRAME_USHORT( globalCoordCount ),
1571         FT_FRAME_ULONG ( offsetToCoord ),
1572         FT_FRAME_USHORT( glyphCount ),
1573         FT_FRAME_USHORT( flags ),
1574         FT_FRAME_ULONG ( offsetToData ),
1575       FT_FRAME_END
1576     };
1577 
1578 
1579     FT_TRACE2(( "GVAR " ));
1580 
1581     if ( FT_SET_ERROR( face->goto_table( face,
1582                                          TTAG_gvar,
1583                                          stream,
1584                                          &table_len ) ) )
1585     {
1586       FT_TRACE2(( "is missing\n" ));
1587       goto Exit;
1588     }
1589 
1590     gvar_start = FT_STREAM_POS( );
1591     if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
1592       goto Exit;
1593 
1594     if ( gvar_head.version != 0x00010000L )
1595     {
1596       FT_TRACE1(( "bad table version\n" ));
1597       error = FT_THROW( Invalid_Table );
1598       goto Exit;
1599     }
1600 
1601     if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
1602     {
1603       FT_TRACE1(( "ft_var_load_gvar:"
1604                   " number of axes in `gvar' and `cvar'\n" ));
1605       FT_TRACE1(( "                  table are different\n" ));
1606       error = FT_THROW( Invalid_Table );
1607       goto Exit;
1608     }
1609 
1610     /* rough sanity check, ignoring offsets */
1611     if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount >
1612            table_len / 2 )
1613     {
1614       FT_TRACE1(( "ft_var_load_gvar:"
1615                   " invalid number of global coordinates\n" ));
1616       error = FT_THROW( Invalid_Table );
1617       goto Exit;
1618     }
1619 
1620     /* offsets can be either 2 or 4 bytes                  */
1621     /* (one more offset than glyphs, to mark size of last) */
1622     offsets_len = ( gvar_head.glyphCount + 1 ) *
1623                   ( ( gvar_head.flags & 1 ) ? 4L : 2L );
1624 
1625     /* rough sanity check */
1626     if (offsets_len > table_len )
1627     {
1628       FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" ));
1629       error = FT_THROW( Invalid_Table );
1630       goto Exit;
1631     }
1632 
1633     FT_TRACE2(( "loaded\n" ));
1634 
1635     blend->gvar_size = table_len;
1636     offsetToData     = gvar_start + gvar_head.offsetToData;
1637 
1638     FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n",
1639                 gvar_head.globalCoordCount == 1 ? "is" : "are",
1640                 gvar_head.globalCoordCount,
1641                 gvar_head.globalCoordCount == 1 ? "" : "s" ));
1642 
1643     if ( FT_FRAME_ENTER( offsets_len ) )
1644       goto Exit;
1645 
1646     /* offsets (one more offset than glyphs, to mark size of last) */
1647     if ( FT_QNEW_ARRAY( blend->glyphoffsets, gvar_head.glyphCount + 1 ) )
1648       goto Fail2;
1649 
1650     if ( gvar_head.flags & 1 )
1651     {
1652       FT_ULong  limit      = gvar_start + table_len;
1653       FT_ULong  max_offset = 0;
1654 
1655 
1656       for ( i = 0; i <= gvar_head.glyphCount; i++ )
1657       {
1658         blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG();
1659 
1660         if ( max_offset <= blend->glyphoffsets[i] )
1661           max_offset = blend->glyphoffsets[i];
1662         else
1663         {
1664           FT_TRACE2(( "ft_var_load_gvar:"
1665                       " glyph variation data offset %d not monotonic\n",
1666                       i ));
1667           blend->glyphoffsets[i] = max_offset;
1668         }
1669 
1670         /* use `<', not `<=' */
1671         if ( limit < blend->glyphoffsets[i] )
1672         {
1673           FT_TRACE2(( "ft_var_load_gvar:"
1674                       " glyph variation data offset %d out of range\n",
1675                       i ));
1676           blend->glyphoffsets[i] = limit;
1677         }
1678       }
1679     }
1680     else
1681     {
1682       FT_ULong  limit      = gvar_start + table_len;
1683       FT_ULong  max_offset = 0;
1684 
1685 
1686       for ( i = 0; i <= gvar_head.glyphCount; i++ )
1687       {
1688         blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
1689 
1690         if ( max_offset <= blend->glyphoffsets[i] )
1691           max_offset = blend->glyphoffsets[i];
1692         else
1693         {
1694           FT_TRACE2(( "ft_var_load_gvar:"
1695                       " glyph variation data offset %d not monotonic\n",
1696                       i ));
1697           blend->glyphoffsets[i] = max_offset;
1698         }
1699 
1700         /* use `<', not `<=' */
1701         if ( limit < blend->glyphoffsets[i] )
1702         {
1703           FT_TRACE2(( "ft_var_load_gvar:"
1704                       " glyph variation data offset %d out of range\n",
1705                       i ));
1706           blend->glyphoffsets[i] = limit;
1707         }
1708       }
1709     }
1710 
1711     blend->gv_glyphcnt = gvar_head.glyphCount;
1712 
1713     FT_FRAME_EXIT();
1714 
1715     if ( gvar_head.globalCoordCount != 0 )
1716     {
1717       if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) ||
1718            FT_FRAME_ENTER( gvar_head.globalCoordCount *
1719                            gvar_head.axisCount * 2L )             )
1720       {
1721         FT_TRACE2(( "ft_var_load_gvar:"
1722                     " glyph variation shared tuples missing\n" ));
1723         goto Fail;
1724       }
1725 
1726       if ( FT_QNEW_ARRAY( blend->tuplecoords,
1727                           gvar_head.axisCount * gvar_head.globalCoordCount ) )
1728         goto Fail2;
1729 
1730       for ( i = 0; i < gvar_head.globalCoordCount; i++ )
1731       {
1732         FT_TRACE5(( "  [ " ));
1733         for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ )
1734         {
1735           blend->tuplecoords[i * gvar_head.axisCount + j] =
1736             FT_fdot14ToFixed( FT_GET_SHORT() );
1737           FT_TRACE5(( "%.5f ",
1738             blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 ));
1739         }
1740         FT_TRACE5(( "]\n" ));
1741       }
1742 
1743       blend->tuplecount = gvar_head.globalCoordCount;
1744 
1745       FT_TRACE5(( "\n" ));
1746 
1747       FT_FRAME_EXIT();
1748     }
1749 
1750   Exit:
1751     return error;
1752 
1753   Fail2:
1754     FT_FRAME_EXIT();
1755 
1756   Fail:
1757     FT_FREE( blend->glyphoffsets );
1758     blend->gv_glyphcnt = 0;
1759     goto Exit;
1760   }
1761 
1762 
1763   /**************************************************************************
1764    *
1765    * @Function:
1766    *   ft_var_apply_tuple
1767    *
1768    * @Description:
1769    *   Figure out whether a given tuple (design) applies to the current
1770    *   blend, and if so, what is the scaling factor.
1771    *
1772    * @Input:
1773    *   blend ::
1774    *     The current blend of the font.
1775    *
1776    *   tupleIndex ::
1777    *     A flag saying whether this is an intermediate
1778    *     tuple or not.
1779    *
1780    *   tuple_coords ::
1781    *     The coordinates of the tuple in normalized axis
1782    *     units.
1783    *
1784    *   im_start_coords ::
1785    *     The initial coordinates where this tuple starts
1786    *     to apply (for intermediate coordinates).
1787    *
1788    *   im_end_coords ::
1789    *     The final coordinates after which this tuple no
1790    *     longer applies (for intermediate coordinates).
1791    *
1792    * @Return:
1793    *   An FT_Fixed value containing the scaling factor.
1794    */
1795   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)1796   ft_var_apply_tuple( GX_Blend   blend,
1797                       FT_UShort  tupleIndex,
1798                       FT_Fixed*  tuple_coords,
1799                       FT_Fixed*  im_start_coords,
1800                       FT_Fixed*  im_end_coords )
1801   {
1802     FT_UInt   i;
1803     FT_Fixed  apply = 0x10000L;
1804 
1805 
1806     for ( i = 0; i < blend->num_axis; i++ )
1807     {
1808       FT_TRACE6(( "    axis %d coordinate %.5f:\n",
1809                   i, blend->normalizedcoords[i] / 65536.0 ));
1810 
1811       /* It's not clear why (for intermediate tuples) we don't need     */
1812       /* to check against start/end -- the documentation says we don't. */
1813       /* Similarly, it's unclear why we don't need to scale along the   */
1814       /* axis.                                                          */
1815 
1816       if ( tuple_coords[i] == 0 )
1817       {
1818         FT_TRACE6(( "      tuple coordinate is zero, ignore\n" ));
1819         continue;
1820       }
1821 
1822       if ( blend->normalizedcoords[i] == 0 )
1823       {
1824         FT_TRACE6(( "      axis coordinate is zero, stop\n" ));
1825         apply = 0;
1826         break;
1827       }
1828 
1829       if ( blend->normalizedcoords[i] == tuple_coords[i] )
1830       {
1831         FT_TRACE6(( "      tuple coordinate %.5f fits perfectly\n",
1832                     tuple_coords[i] / 65536.0 ));
1833         /* `apply' does not change */
1834         continue;
1835       }
1836 
1837       if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
1838       {
1839         /* not an intermediate tuple */
1840 
1841         if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) ||
1842              blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) )
1843         {
1844           FT_TRACE6(( "      tuple coordinate %.5f is exceeded, stop\n",
1845                       tuple_coords[i] / 65536.0 ));
1846           apply = 0;
1847           break;
1848         }
1849 
1850         FT_TRACE6(( "      tuple coordinate %.5f fits\n",
1851                     tuple_coords[i] / 65536.0 ));
1852         apply = FT_MulDiv( apply,
1853                            blend->normalizedcoords[i],
1854                            tuple_coords[i] );
1855       }
1856       else
1857       {
1858         /* intermediate tuple */
1859 
1860         if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
1861              blend->normalizedcoords[i] >= im_end_coords[i]   )
1862         {
1863           FT_TRACE6(( "      intermediate tuple range ]%.5f;%.5f[ is exceeded,"
1864                       " stop\n",
1865                       im_start_coords[i] / 65536.0,
1866                       im_end_coords[i] / 65536.0 ));
1867           apply = 0;
1868           break;
1869         }
1870 
1871         FT_TRACE6(( "      intermediate tuple range ]%.5f;%.5f[ fits\n",
1872                     im_start_coords[i] / 65536.0,
1873                     im_end_coords[i] / 65536.0 ));
1874         if ( blend->normalizedcoords[i] < tuple_coords[i] )
1875           apply = FT_MulDiv( apply,
1876                              blend->normalizedcoords[i] - im_start_coords[i],
1877                              tuple_coords[i] - im_start_coords[i] );
1878         else
1879           apply = FT_MulDiv( apply,
1880                              im_end_coords[i] - blend->normalizedcoords[i],
1881                              im_end_coords[i] - tuple_coords[i] );
1882       }
1883     }
1884 
1885     FT_TRACE6(( "    apply factor is %.5f\n", apply / 65536.0 ));
1886 
1887     return apply;
1888   }
1889 
1890 
1891   /* convert from design coordinates to normalized coordinates */
1892 
1893   static void
ft_var_to_normalized(TT_Face face,FT_UInt num_coords,FT_Fixed * coords,FT_Fixed * normalized)1894   ft_var_to_normalized( TT_Face    face,
1895                         FT_UInt    num_coords,
1896                         FT_Fixed*  coords,
1897                         FT_Fixed*  normalized )
1898   {
1899     GX_Blend        blend;
1900     FT_MM_Var*      mmvar;
1901     FT_UInt         i, j;
1902     FT_Var_Axis*    a;
1903     GX_AVarSegment  av;
1904 
1905 
1906     blend = face->blend;
1907     mmvar = blend->mmvar;
1908 
1909     if ( num_coords > mmvar->num_axis )
1910     {
1911       FT_TRACE2(( "ft_var_to_normalized:"
1912                   " only using first %d of %d coordinates\n",
1913                   mmvar->num_axis, num_coords ));
1914       num_coords = mmvar->num_axis;
1915     }
1916 
1917     /* Axis normalization is a two-stage process.  First we normalize */
1918     /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
1919     /* Then, if there's an `avar' table, we renormalize this range.   */
1920 
1921     a = mmvar->axis;
1922     for ( i = 0; i < num_coords; i++, a++ )
1923     {
1924       FT_Fixed  coord = coords[i];
1925 
1926 
1927       FT_TRACE5(( "    %d: %.5f\n", i, coord / 65536.0 ));
1928       if ( coord > a->maximum || coord < a->minimum )
1929       {
1930         FT_TRACE1(( "ft_var_to_normalized: design coordinate %.5f\n",
1931                     coord / 65536.0 ));
1932         FT_TRACE1(( "                      is out of range [%.5f;%.5f];"
1933                     " clamping\n",
1934                     a->minimum / 65536.0,
1935                     a->maximum / 65536.0 ));
1936       }
1937 
1938       if ( coord > a->def )
1939         normalized[i] = coord >= a->maximum ?  0x10000L :
1940                         FT_DivFix( SUB_LONG( coord, a->def ),
1941                                    SUB_LONG( a->maximum, a->def ) );
1942       else if ( coord < a->def )
1943         normalized[i] = coord <= a->minimum ? -0x10000L :
1944                         FT_DivFix( SUB_LONG( coord, a->def ),
1945                                    SUB_LONG( a->def, a->minimum ) );
1946       else
1947         normalized[i] = 0;
1948     }
1949 
1950     FT_TRACE5(( "\n" ));
1951 
1952     for ( ; i < mmvar->num_axis; i++ )
1953       normalized[i] = 0;
1954 
1955     if ( blend->avar_segment )
1956     {
1957       FT_TRACE5(( "normalized design coordinates"
1958                   " before applying `avar' data:\n" ));
1959 
1960       av = blend->avar_segment;
1961       for ( i = 0; i < mmvar->num_axis; i++, av++ )
1962       {
1963         for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1964         {
1965           if ( normalized[i] < av->correspondence[j].fromCoord )
1966           {
1967             FT_TRACE5(( "  %.5f\n", normalized[i] / 65536.0 ));
1968 
1969             normalized[i] =
1970               FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
1971                          av->correspondence[j].toCoord -
1972                            av->correspondence[j - 1].toCoord,
1973                          av->correspondence[j].fromCoord -
1974                            av->correspondence[j - 1].fromCoord ) +
1975               av->correspondence[j - 1].toCoord;
1976             break;
1977           }
1978         }
1979       }
1980     }
1981   }
1982 
1983 
1984   /* convert from normalized coordinates to design coordinates */
1985 
1986   static void
ft_var_to_design(TT_Face face,FT_UInt num_coords,FT_Fixed * coords,FT_Fixed * design)1987   ft_var_to_design( TT_Face    face,
1988                     FT_UInt    num_coords,
1989                     FT_Fixed*  coords,
1990                     FT_Fixed*  design )
1991   {
1992     GX_Blend      blend;
1993     FT_MM_Var*    mmvar;
1994     FT_Var_Axis*  a;
1995 
1996     FT_UInt  i, j, nc;
1997 
1998 
1999     blend = face->blend;
2000 
2001     nc = num_coords;
2002     if ( num_coords > blend->num_axis )
2003     {
2004       FT_TRACE2(( "ft_var_to_design:"
2005                   " only using first %d of %d coordinates\n",
2006                   blend->num_axis, num_coords ));
2007       nc = blend->num_axis;
2008     }
2009 
2010     for ( i = 0; i < nc; i++ )
2011       design[i] = coords[i];
2012 
2013     for ( ; i < num_coords; i++ )
2014       design[i] = 0;
2015 
2016     if ( blend->avar_segment )
2017     {
2018       GX_AVarSegment  av = blend->avar_segment;
2019 
2020 
2021       FT_TRACE5(( "design coordinates"
2022                   " after removing `avar' distortion:\n" ));
2023 
2024       for ( i = 0; i < nc; i++, av++ )
2025       {
2026         for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
2027         {
2028           if ( design[i] < av->correspondence[j].toCoord )
2029           {
2030             design[i] =
2031               FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord,
2032                          av->correspondence[j].fromCoord -
2033                            av->correspondence[j - 1].fromCoord,
2034                          av->correspondence[j].toCoord -
2035                            av->correspondence[j - 1].toCoord ) +
2036               av->correspondence[j - 1].fromCoord;
2037 
2038             FT_TRACE5(( "  %.5f\n", design[i] / 65536.0 ));
2039             break;
2040           }
2041         }
2042       }
2043     }
2044 
2045     mmvar = blend->mmvar;
2046     a     = mmvar->axis;
2047 
2048     for ( i = 0; i < nc; i++, a++ )
2049     {
2050       if ( design[i] < 0 )
2051         design[i] = a->def + FT_MulFix( design[i],
2052                                         a->def - a->minimum );
2053       else if ( design[i] > 0 )
2054         design[i] = a->def + FT_MulFix( design[i],
2055                                         a->maximum - a->def );
2056       else
2057         design[i] = a->def;
2058     }
2059   }
2060 
2061 
2062   /*************************************************************************/
2063   /*************************************************************************/
2064   /*****                                                               *****/
2065   /*****               MULTIPLE MASTERS SERVICE FUNCTIONS              *****/
2066   /*****                                                               *****/
2067   /*************************************************************************/
2068   /*************************************************************************/
2069 
2070 
2071   typedef struct  GX_FVar_Head_
2072   {
2073     FT_Long    version;
2074     FT_UShort  offsetToData;
2075     FT_UShort  axisCount;
2076     FT_UShort  axisSize;
2077     FT_UShort  instanceCount;
2078     FT_UShort  instanceSize;
2079 
2080   } GX_FVar_Head;
2081 
2082 
2083   typedef struct  fvar_axis_
2084   {
2085     FT_ULong   axisTag;
2086     FT_Fixed   minValue;
2087     FT_Fixed   defaultValue;
2088     FT_Fixed   maxValue;
2089     FT_UShort  flags;
2090     FT_UShort  nameID;
2091 
2092   } GX_FVar_Axis;
2093 
2094 
2095   /**************************************************************************
2096    *
2097    * @Function:
2098    *   TT_Get_MM_Var
2099    *
2100    * @Description:
2101    *   Check that the font's `fvar' table is valid, parse it, and return
2102    *   those data.  It also loads (and parses) the `MVAR' table, if
2103    *   possible.
2104    *
2105    * @InOut:
2106    *   face ::
2107    *     The font face.
2108    *     TT_Get_MM_Var initializes the blend structure.
2109    *
2110    * @Output:
2111    *   master ::
2112    *     The `fvar' data (must be freed by caller).  Can be NULL,
2113    *     which makes this function simply load MM support.
2114    *
2115    * @Return:
2116    *   FreeType error code.  0 means success.
2117    */
2118   FT_LOCAL_DEF( FT_Error )
TT_Get_MM_Var(TT_Face face,FT_MM_Var ** master)2119   TT_Get_MM_Var( TT_Face      face,
2120                  FT_MM_Var*  *master )
2121   {
2122     FT_Stream            stream     = face->root.stream;
2123     FT_Memory            memory     = face->root.memory;
2124     FT_ULong             table_len;
2125     FT_Error             error      = FT_Err_Ok;
2126     FT_ULong             fvar_start = 0;
2127     FT_UInt              i, j;
2128     FT_MM_Var*           mmvar = NULL;
2129     FT_Fixed*            next_coords;
2130     FT_Fixed*            nsc;
2131     FT_String*           next_name;
2132     FT_Var_Axis*         a;
2133     FT_Fixed*            c;
2134     FT_Var_Named_Style*  ns;
2135     GX_FVar_Head         fvar_head  = { 0, 0, 0, 0, 0, 0 };
2136     FT_Bool              usePsName  = 0;
2137     FT_UInt              num_instances;
2138     FT_UInt              num_axes;
2139     FT_UShort*           axis_flags;
2140 
2141     FT_Offset  mmvar_size;
2142     FT_Offset  axis_flags_size;
2143     FT_Offset  axis_size;
2144     FT_Offset  namedstyle_size;
2145     FT_Offset  next_coords_size;
2146     FT_Offset  next_name_size;
2147 
2148     FT_Bool  need_init;
2149 
2150     static const FT_Frame_Field  fvar_fields[] =
2151     {
2152 
2153 #undef  FT_STRUCTURE
2154 #define FT_STRUCTURE  GX_FVar_Head
2155 
2156       FT_FRAME_START( 16 ),
2157         FT_FRAME_LONG      ( version ),
2158         FT_FRAME_USHORT    ( offsetToData ),
2159         FT_FRAME_SKIP_SHORT,
2160         FT_FRAME_USHORT    ( axisCount ),
2161         FT_FRAME_USHORT    ( axisSize ),
2162         FT_FRAME_USHORT    ( instanceCount ),
2163         FT_FRAME_USHORT    ( instanceSize ),
2164       FT_FRAME_END
2165     };
2166 
2167     static const FT_Frame_Field  fvaraxis_fields[] =
2168     {
2169 
2170 #undef  FT_STRUCTURE
2171 #define FT_STRUCTURE  GX_FVar_Axis
2172 
2173       FT_FRAME_START( 20 ),
2174         FT_FRAME_ULONG ( axisTag ),
2175         FT_FRAME_LONG  ( minValue ),
2176         FT_FRAME_LONG  ( defaultValue ),
2177         FT_FRAME_LONG  ( maxValue ),
2178         FT_FRAME_USHORT( flags ),
2179         FT_FRAME_USHORT( nameID ),
2180       FT_FRAME_END
2181     };
2182 
2183 
2184     /* read the font data and set up the internal representation */
2185     /* if not already done                                       */
2186 
2187     need_init = !face->blend;
2188 
2189     if ( need_init )
2190     {
2191       FT_TRACE2(( "FVAR " ));
2192 
2193       /* both `fvar' and `gvar' must be present */
2194       if ( FT_SET_ERROR( face->goto_table( face, TTAG_gvar,
2195                                            stream, &table_len ) ) )
2196       {
2197         /* CFF2 is an alternate to gvar here */
2198         if ( FT_SET_ERROR( face->goto_table( face, TTAG_CFF2,
2199                                              stream, &table_len ) ) )
2200         {
2201           FT_TRACE1(( "\n" ));
2202           FT_TRACE1(( "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" ));
2203           goto Exit;
2204         }
2205       }
2206 
2207       if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar,
2208                                            stream, &table_len ) ) )
2209       {
2210         FT_TRACE1(( "is missing\n" ));
2211         goto Exit;
2212       }
2213 
2214       fvar_start = FT_STREAM_POS( );
2215 
2216       /* the validity of the `fvar' header data was already checked */
2217       /* in function `sfnt_init_face'                               */
2218       if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
2219         goto Exit;
2220 
2221       usePsName = FT_BOOL( fvar_head.instanceSize ==
2222                            6 + 4 * fvar_head.axisCount );
2223 
2224       FT_TRACE2(( "loaded\n" ));
2225 
2226       FT_TRACE5(( "%d variation ax%s\n",
2227                   fvar_head.axisCount,
2228                   fvar_head.axisCount == 1 ? "is" : "es" ));
2229 
2230       if ( FT_NEW( face->blend ) )
2231         goto Exit;
2232 
2233       num_axes              = fvar_head.axisCount;
2234       face->blend->num_axis = num_axes;
2235     }
2236     else
2237       num_axes = face->blend->num_axis;
2238 
2239     /* `num_instances' holds the number of all named instances, */
2240     /* including the default instance which might be missing    */
2241     /* in fvar's table of named instances                       */
2242     num_instances = (FT_UInt)face->root.style_flags >> 16;
2243 
2244     /* prepare storage area for MM data; this cannot overflow   */
2245     /* 32-bit arithmetic because of the size limits used in the */
2246     /* `fvar' table validity check in `sfnt_init_face'          */
2247 
2248     /* the various `*_size' variables, which we also use as     */
2249     /* offsets into the `mmvar' array, must be multiples of the */
2250     /* pointer size (except the last one); without such an      */
2251     /* alignment there might be runtime errors due to           */
2252     /* misaligned addresses                                     */
2253 #undef  ALIGN_SIZE
2254 #define ALIGN_SIZE( n ) \
2255           ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) )
2256 
2257     mmvar_size       = ALIGN_SIZE( sizeof ( FT_MM_Var ) );
2258     axis_flags_size  = ALIGN_SIZE( num_axes *
2259                                    sizeof ( FT_UShort ) );
2260     axis_size        = ALIGN_SIZE( num_axes *
2261                                    sizeof ( FT_Var_Axis ) );
2262     namedstyle_size  = ALIGN_SIZE( num_instances *
2263                                    sizeof ( FT_Var_Named_Style ) );
2264     next_coords_size = ALIGN_SIZE( num_instances *
2265                                    num_axes *
2266                                    sizeof ( FT_Fixed ) );
2267     next_name_size   = num_axes * 5;
2268 
2269     if ( need_init )
2270     {
2271       face->blend->mmvar_len = mmvar_size       +
2272                                axis_flags_size  +
2273                                axis_size        +
2274                                namedstyle_size  +
2275                                next_coords_size +
2276                                next_name_size;
2277 
2278       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2279         goto Exit;
2280       face->blend->mmvar = mmvar;
2281 
2282       /* set up pointers and offsets into the `mmvar' array; */
2283       /* the data gets filled in later on                    */
2284 
2285       mmvar->num_axis =
2286         num_axes;
2287       mmvar->num_designs =
2288         ~0U;                   /* meaningless in this context; each glyph */
2289                                /* may have a different number of designs  */
2290                                /* (or tuples, as called by Apple)         */
2291       mmvar->num_namedstyles =
2292         num_instances;
2293 
2294       /* alas, no public field in `FT_Var_Axis' for axis flags */
2295       axis_flags =
2296         (FT_UShort*)( (char*)mmvar + mmvar_size );
2297       mmvar->axis =
2298         (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2299       mmvar->namedstyle =
2300         (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size );
2301 
2302       next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2303                                  namedstyle_size );
2304       for ( i = 0; i < num_instances; i++ )
2305       {
2306         mmvar->namedstyle[i].coords  = next_coords;
2307         next_coords                 += num_axes;
2308       }
2309 
2310       next_name = (FT_String*)( (char*)mmvar->namedstyle +
2311                                 namedstyle_size + next_coords_size );
2312       for ( i = 0; i < num_axes; i++ )
2313       {
2314         mmvar->axis[i].name  = next_name;
2315         next_name           += 5;
2316       }
2317 
2318       /* now fill in the data */
2319 
2320       if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
2321         goto Exit;
2322 
2323       a = mmvar->axis;
2324       for ( i = 0; i < num_axes; i++ )
2325       {
2326         GX_FVar_Axis  axis_rec;
2327 
2328 #ifdef FT_DEBUG_LEVEL_TRACE
2329         int  invalid = 0;
2330 #endif
2331 
2332 
2333         if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
2334           goto Exit;
2335         a->tag     = axis_rec.axisTag;
2336         a->minimum = axis_rec.minValue;
2337         a->def     = axis_rec.defaultValue;
2338         a->maximum = axis_rec.maxValue;
2339         a->strid   = axis_rec.nameID;
2340 
2341         a->name[0] = (FT_String)(   a->tag >> 24 );
2342         a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
2343         a->name[2] = (FT_String)( ( a->tag >>  8 ) & 0xFF );
2344         a->name[3] = (FT_String)( ( a->tag       ) & 0xFF );
2345         a->name[4] = '\0';
2346 
2347         *axis_flags = axis_rec.flags;
2348 
2349         if ( a->minimum > a->def ||
2350              a->def > a->maximum )
2351         {
2352           a->minimum = a->def;
2353           a->maximum = a->def;
2354 
2355 #ifdef FT_DEBUG_LEVEL_TRACE
2356           invalid = 1;
2357 #endif
2358         }
2359 
2360 #ifdef FT_DEBUG_LEVEL_TRACE
2361         if ( i == 0 )
2362           FT_TRACE5(( "  idx   tag  "
2363                    /* "  XXX  `XXXX'" */
2364                       "    minimum     default     maximum   flags\n" ));
2365                    /* "  XXXX.XXXXX  XXXX.XXXXX  XXXX.XXXXX  0xXXXX" */
2366 
2367         FT_TRACE5(( "  %3d  `%s'"
2368                     "  %10.5f  %10.5f  %10.5f  0x%04X%s\n",
2369                     i,
2370                     a->name,
2371                     a->minimum / 65536.0,
2372                     a->def / 65536.0,
2373                     a->maximum / 65536.0,
2374                     *axis_flags,
2375                     invalid ? " (invalid, disabled)" : "" ));
2376 #endif
2377 
2378         a++;
2379         axis_flags++;
2380       }
2381 
2382       FT_TRACE5(( "\n" ));
2383 
2384       /* named instance coordinates are stored as design coordinates; */
2385       /* we have to convert them to normalized coordinates also       */
2386       if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords,
2387                          num_axes * num_instances ) )
2388         goto Exit;
2389 
2390       if ( fvar_head.instanceCount && !face->blend->avar_loaded )
2391       {
2392         FT_ULong  offset = FT_STREAM_POS();
2393 
2394 
2395         ft_var_load_avar( face );
2396 
2397         if ( FT_STREAM_SEEK( offset ) )
2398           goto Exit;
2399       }
2400 
2401       FT_TRACE5(( "%d instance%s\n",
2402                   fvar_head.instanceCount,
2403                   fvar_head.instanceCount == 1 ? "" : "s" ));
2404 
2405       ns  = mmvar->namedstyle;
2406       nsc = face->blend->normalized_stylecoords;
2407       for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
2408       {
2409         /* PostScript names add 2 bytes to the instance record size */
2410         if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) +
2411                              4L * num_axes ) )
2412           goto Exit;
2413 
2414         ns->strid       =    FT_GET_USHORT();
2415         (void) /* flags = */ FT_GET_USHORT();
2416 
2417         c = ns->coords;
2418         for ( j = 0; j < num_axes; j++, c++ )
2419           *c = FT_GET_LONG();
2420 
2421         /* valid psid values are 6, [256;32767], and 0xFFFF */
2422         if ( usePsName )
2423           ns->psid = FT_GET_USHORT();
2424         else
2425           ns->psid = 0xFFFF;
2426 
2427 #ifdef FT_DEBUG_LEVEL_TRACE
2428         {
2429           SFNT_Service  sfnt = (SFNT_Service)face->sfnt;
2430 
2431           FT_String*  strname = NULL;
2432           FT_String*  psname  = NULL;
2433 
2434           FT_ULong  pos;
2435 
2436 
2437           pos = FT_STREAM_POS();
2438 
2439           if ( ns->strid != 0xFFFF )
2440           {
2441             (void)sfnt->get_name( face,
2442                                   (FT_UShort)ns->strid,
2443                                   &strname );
2444             if ( strname && !ft_strcmp( strname, ".notdef" ) )
2445               strname = NULL;
2446           }
2447 
2448           if ( ns->psid != 0xFFFF )
2449           {
2450             (void)sfnt->get_name( face,
2451                                   (FT_UShort)ns->psid,
2452                                   &psname );
2453             if ( psname && !ft_strcmp( psname, ".notdef" ) )
2454               psname = NULL;
2455           }
2456 
2457           (void)FT_STREAM_SEEK( pos );
2458 
2459           FT_TRACE5(( "  instance %d (%s%s%s, %s%s%s)\n",
2460                       i,
2461                       strname ? "name: `" : "",
2462                       strname ? strname : "unnamed",
2463                       strname ? "'" : "",
2464                       psname ? "PS name: `" : "",
2465                       psname ? psname : "no PS name",
2466                       psname ? "'" : "" ));
2467 
2468           FT_FREE( strname );
2469           FT_FREE( psname );
2470         }
2471 #endif /* FT_DEBUG_LEVEL_TRACE */
2472 
2473         ft_var_to_normalized( face, num_axes, ns->coords, nsc );
2474         nsc += num_axes;
2475 
2476         FT_FRAME_EXIT();
2477       }
2478 
2479       if ( num_instances != fvar_head.instanceCount )
2480       {
2481         SFNT_Service  sfnt = (SFNT_Service)face->sfnt;
2482 
2483         FT_Int   found, dummy1, dummy2;
2484         FT_UInt  strid = ~0U;
2485 
2486 
2487         /* the default instance is missing in array the   */
2488         /* of named instances; try to synthesize an entry */
2489         found = sfnt->get_name_id( face,
2490                                    TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY,
2491                                    &dummy1,
2492                                    &dummy2 );
2493         if ( found )
2494           strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY;
2495         else
2496         {
2497           found = sfnt->get_name_id( face,
2498                                      TT_NAME_ID_FONT_SUBFAMILY,
2499                                      &dummy1,
2500                                      &dummy2 );
2501           if ( found )
2502             strid = TT_NAME_ID_FONT_SUBFAMILY;
2503         }
2504 
2505         if ( found )
2506         {
2507           found = sfnt->get_name_id( face,
2508                                      TT_NAME_ID_PS_NAME,
2509                                      &dummy1,
2510                                      &dummy2 );
2511           if ( found )
2512           {
2513             FT_TRACE5(( "TT_Get_MM_Var:"
2514                         " Adding default instance to named instances\n" ));
2515 
2516             ns = &mmvar->namedstyle[fvar_head.instanceCount];
2517 
2518             ns->strid = strid;
2519             ns->psid  = TT_NAME_ID_PS_NAME;
2520 
2521             a = mmvar->axis;
2522             c = ns->coords;
2523             for ( j = 0; j < num_axes; j++, a++, c++ )
2524               *c = a->def;
2525           }
2526         }
2527       }
2528 
2529       ft_var_load_mvar( face );
2530     }
2531 
2532     /* fill the output array if requested */
2533 
2534     if ( master )
2535     {
2536       FT_UInt  n;
2537 
2538 
2539       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2540         goto Exit;
2541       FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
2542 
2543       axis_flags =
2544         (FT_UShort*)( (char*)mmvar + mmvar_size );
2545       mmvar->axis =
2546         (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2547       mmvar->namedstyle =
2548         (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size );
2549 
2550       next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2551                                  namedstyle_size );
2552       for ( n = 0; n < mmvar->num_namedstyles; n++ )
2553       {
2554         mmvar->namedstyle[n].coords  = next_coords;
2555         next_coords                 += num_axes;
2556       }
2557 
2558       a         = mmvar->axis;
2559       next_name = (FT_String*)( (char*)mmvar->namedstyle +
2560                                 namedstyle_size + next_coords_size );
2561       for ( n = 0; n < num_axes; n++ )
2562       {
2563         a->name = next_name;
2564 
2565         /* standard PostScript names for some standard apple tags */
2566         if ( a->tag == TTAG_wght )
2567           a->name = (char*)"Weight";
2568         else if ( a->tag == TTAG_wdth )
2569           a->name = (char*)"Width";
2570         else if ( a->tag == TTAG_opsz )
2571           a->name = (char*)"OpticalSize";
2572         else if ( a->tag == TTAG_slnt )
2573           a->name = (char*)"Slant";
2574 
2575         next_name += 5;
2576         a++;
2577       }
2578 
2579       *master = mmvar;
2580     }
2581 
2582   Exit:
2583     return error;
2584   }
2585 
2586 
2587   static FT_Error
tt_set_mm_blend(TT_Face face,FT_UInt num_coords,FT_Fixed * coords,FT_Bool set_design_coords)2588   tt_set_mm_blend( TT_Face    face,
2589                    FT_UInt    num_coords,
2590                    FT_Fixed*  coords,
2591                    FT_Bool    set_design_coords )
2592   {
2593     FT_Error    error = FT_Err_Ok;
2594     GX_Blend    blend;
2595     FT_MM_Var*  mmvar;
2596     FT_UInt     i;
2597 
2598     FT_Bool     all_design_coords = FALSE;
2599 
2600     FT_Memory   memory = face->root.memory;
2601 
2602     enum
2603     {
2604       mcvt_retain,
2605       mcvt_modify,
2606       mcvt_load
2607 
2608     } manageCvt;
2609 
2610 
2611     face->doblend = FALSE;
2612 
2613     if ( !face->blend )
2614     {
2615       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2616         goto Exit;
2617     }
2618 
2619     blend = face->blend;
2620     mmvar = blend->mmvar;
2621 
2622     if ( num_coords > mmvar->num_axis )
2623     {
2624       FT_TRACE2(( "TT_Set_MM_Blend:"
2625                   " only using first %d of %d coordinates\n",
2626                   mmvar->num_axis, num_coords ));
2627       num_coords = mmvar->num_axis;
2628     }
2629 
2630     FT_TRACE5(( "TT_Set_MM_Blend:\n" ));
2631     FT_TRACE5(( "  normalized design coordinates:\n" ));
2632 
2633     for ( i = 0; i < num_coords; i++ )
2634     {
2635       FT_TRACE5(( "    %.5f\n", coords[i] / 65536.0 ));
2636       if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
2637       {
2638         FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n",
2639                     coords[i] / 65536.0 ));
2640         FT_TRACE1(( "                 is out of range [-1;1]\n" ));
2641         error = FT_THROW( Invalid_Argument );
2642         goto Exit;
2643       }
2644     }
2645 
2646     FT_TRACE5(( "\n" ));
2647 
2648     if ( !face->is_cff2 && !blend->glyphoffsets )
2649       if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) )
2650         goto Exit;
2651 
2652     if ( !blend->coords )
2653     {
2654       if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2655         goto Exit;
2656 
2657       /* the first time we have to compute all design coordinates */
2658       all_design_coords = TRUE;
2659     }
2660 
2661     if ( !blend->normalizedcoords )
2662     {
2663       if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) )
2664         goto Exit;
2665 
2666       manageCvt = mcvt_modify;
2667 
2668       /* If we have not set the blend coordinates before this, then the  */
2669       /* cvt table will still be what we read from the `cvt ' table and  */
2670       /* we don't need to reload it.  We may need to change it though... */
2671     }
2672     else
2673     {
2674       FT_Bool    have_diff = 0;
2675       FT_UInt    j;
2676       FT_Fixed*  c;
2677       FT_Fixed*  n;
2678 
2679 
2680       manageCvt = mcvt_retain;
2681 
2682       for ( i = 0; i < num_coords; i++ )
2683       {
2684         if ( blend->normalizedcoords[i] != coords[i] )
2685         {
2686           manageCvt = mcvt_load;
2687           have_diff = 1;
2688           break;
2689         }
2690       }
2691 
2692       if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2693       {
2694         FT_UInt  instance_index = (FT_UInt)face->root.face_index >> 16;
2695 
2696 
2697         c = blend->normalizedcoords + i;
2698         n = blend->normalized_stylecoords            +
2699             ( instance_index - 1 ) * mmvar->num_axis +
2700             i;
2701 
2702         for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
2703           if ( *c != *n )
2704             have_diff = 1;
2705       }
2706       else
2707       {
2708         c = blend->normalizedcoords + i;
2709         for ( j = i; j < mmvar->num_axis; j++, c++ )
2710           if ( *c != 0 )
2711             have_diff = 1;
2712       }
2713 
2714       /* return value -1 indicates `no change' */
2715       if ( !have_diff )
2716       {
2717         face->doblend = TRUE;
2718 
2719         return -1;
2720       }
2721 
2722       for ( ; i < mmvar->num_axis; i++ )
2723       {
2724         if ( blend->normalizedcoords[i] != 0 )
2725         {
2726           manageCvt = mcvt_load;
2727           break;
2728         }
2729       }
2730 
2731       /* If we don't change the blend coords then we don't need to do  */
2732       /* anything to the cvt table.  It will be correct.  Otherwise we */
2733       /* no longer have the original cvt (it was modified when we set  */
2734       /* the blend last time), so we must reload and then modify it.   */
2735     }
2736 
2737     blend->num_axis = mmvar->num_axis;
2738     if ( coords )
2739       FT_MEM_COPY( blend->normalizedcoords,
2740                    coords,
2741                    num_coords * sizeof ( FT_Fixed ) );
2742 
2743     if ( set_design_coords )
2744       ft_var_to_design( face,
2745                         all_design_coords ? blend->num_axis : num_coords,
2746                         blend->normalizedcoords,
2747                         blend->coords );
2748 
2749     face->doblend = TRUE;
2750 
2751     if ( face->cvt )
2752     {
2753       switch ( manageCvt )
2754       {
2755       case mcvt_load:
2756         /* The cvt table has been loaded already; every time we change the */
2757         /* blend we may need to reload and remodify the cvt table.         */
2758         FT_FREE( face->cvt );
2759         face->cvt = NULL;
2760 
2761         error = tt_face_load_cvt( face, face->root.stream );
2762         break;
2763 
2764       case mcvt_modify:
2765         /* The original cvt table is in memory.  All we need to do is */
2766         /* apply the `cvar' table (if any).                           */
2767         error = tt_face_vary_cvt( face, face->root.stream );
2768         break;
2769 
2770       case mcvt_retain:
2771         /* The cvt table is correct for this set of coordinates. */
2772         break;
2773       }
2774     }
2775 
2776     /* enforce recomputation of the PostScript name; */
2777     FT_FREE( face->postscript_name );
2778     face->postscript_name = NULL;
2779 
2780   Exit:
2781     return error;
2782   }
2783 
2784 
2785   /**************************************************************************
2786    *
2787    * @Function:
2788    *   TT_Set_MM_Blend
2789    *
2790    * @Description:
2791    *   Set the blend (normalized) coordinates for this instance of the
2792    *   font.  Check that the `gvar' table is reasonable and does some
2793    *   initial preparation.
2794    *
2795    * @InOut:
2796    *   face ::
2797    *     The font.
2798    *     Initialize the blend structure with `gvar' data.
2799    *
2800    * @Input:
2801    *   num_coords ::
2802    *     The number of available coordinates.  If it is
2803    *     larger than the number of axes, ignore the excess
2804    *     values.  If it is smaller than the number of axes,
2805    *     use the default value (0) for the remaining axes.
2806    *
2807    *   coords ::
2808    *     An array of `num_coords', each between [-1,1].
2809    *
2810    * @Return:
2811    *   FreeType error code.  0 means success.
2812    */
2813   FT_LOCAL_DEF( FT_Error )
TT_Set_MM_Blend(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)2814   TT_Set_MM_Blend( TT_Face    face,
2815                    FT_UInt    num_coords,
2816                    FT_Fixed*  coords )
2817   {
2818     FT_Error  error;
2819 
2820 
2821     error = tt_set_mm_blend( face, num_coords, coords, 1 );
2822     if ( error )
2823       return error;
2824 
2825     if ( num_coords )
2826       face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2827     else
2828       face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2829 
2830     return FT_Err_Ok;
2831   }
2832 
2833 
2834   /**************************************************************************
2835    *
2836    * @Function:
2837    *   TT_Get_MM_Blend
2838    *
2839    * @Description:
2840    *   Get the blend (normalized) coordinates for this instance of the
2841    *   font.
2842    *
2843    * @InOut:
2844    *   face ::
2845    *     The font.
2846    *     Initialize the blend structure with `gvar' data.
2847    *
2848    * @Input:
2849    *   num_coords ::
2850    *     The number of available coordinates.  If it is
2851    *     larger than the number of axes, set the excess
2852    *     values to 0.
2853    *
2854    *   coords ::
2855    *     An array of `num_coords', each between [-1,1].
2856    *
2857    * @Return:
2858    *   FreeType error code.  0 means success.
2859    */
2860   FT_LOCAL_DEF( FT_Error )
TT_Get_MM_Blend(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)2861   TT_Get_MM_Blend( TT_Face    face,
2862                    FT_UInt    num_coords,
2863                    FT_Fixed*  coords )
2864   {
2865     FT_Error  error = FT_Err_Ok;
2866     GX_Blend  blend;
2867     FT_UInt   i, nc;
2868 
2869 
2870     if ( !face->blend )
2871     {
2872       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2873         return error;
2874     }
2875 
2876     blend = face->blend;
2877 
2878     if ( !blend->coords )
2879     {
2880       /* select default instance coordinates */
2881       /* if no instance is selected yet      */
2882       if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2883         return error;
2884     }
2885 
2886     nc = num_coords;
2887     if ( num_coords > blend->num_axis )
2888     {
2889       FT_TRACE2(( "TT_Get_MM_Blend:"
2890                   " only using first %d of %d coordinates\n",
2891                   blend->num_axis, num_coords ));
2892       nc = blend->num_axis;
2893     }
2894 
2895     if ( face->doblend )
2896     {
2897       for ( i = 0; i < nc; i++ )
2898         coords[i] = blend->normalizedcoords[i];
2899     }
2900     else
2901     {
2902       for ( i = 0; i < nc; i++ )
2903         coords[i] = 0;
2904     }
2905 
2906     for ( ; i < num_coords; i++ )
2907       coords[i] = 0;
2908 
2909     return FT_Err_Ok;
2910   }
2911 
2912 
2913   /**************************************************************************
2914    *
2915    * @Function:
2916    *   TT_Set_Var_Design
2917    *
2918    * @Description:
2919    *   Set the coordinates for the instance, measured in the user
2920    *   coordinate system.  Parse the `avar' table (if present) to convert
2921    *   from user to normalized coordinates.
2922    *
2923    * @InOut:
2924    *   face ::
2925    *     The font face.
2926    *     Initialize the blend struct with `gvar' data.
2927    *
2928    * @Input:
2929    *   num_coords ::
2930    *     The number of available coordinates.  If it is
2931    *     larger than the number of axes, ignore the excess
2932    *     values.  If it is smaller than the number of axes,
2933    *     use the default values for the remaining axes.
2934    *
2935    *   coords ::
2936    *     A coordinate array with `num_coords' elements.
2937    *
2938    * @Return:
2939    *   FreeType error code.  0 means success.
2940    */
2941   FT_LOCAL_DEF( FT_Error )
TT_Set_Var_Design(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)2942   TT_Set_Var_Design( TT_Face    face,
2943                      FT_UInt    num_coords,
2944                      FT_Fixed*  coords )
2945   {
2946     FT_Error    error  = FT_Err_Ok;
2947     GX_Blend    blend;
2948     FT_MM_Var*  mmvar;
2949     FT_UInt     i;
2950     FT_Memory   memory = face->root.memory;
2951 
2952     FT_Fixed*  c;
2953     FT_Fixed*  n;
2954     FT_Fixed*  normalized = NULL;
2955 
2956     FT_Bool  have_diff = 0;
2957 
2958 
2959     if ( !face->blend )
2960     {
2961       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2962         goto Exit;
2963     }
2964 
2965     blend = face->blend;
2966     mmvar = blend->mmvar;
2967 
2968     if ( num_coords > mmvar->num_axis )
2969     {
2970       FT_TRACE2(( "TT_Set_Var_Design:"
2971                   " only using first %d of %d coordinates\n",
2972                   mmvar->num_axis, num_coords ));
2973       num_coords = mmvar->num_axis;
2974     }
2975 
2976     if ( !blend->coords )
2977     {
2978       if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2979         goto Exit;
2980     }
2981 
2982     c = blend->coords;
2983     n = coords;
2984     for ( i = 0; i < num_coords; i++, n++, c++ )
2985     {
2986       if ( *c != *n )
2987       {
2988         *c        = *n;
2989         have_diff = 1;
2990       }
2991     }
2992 
2993     if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2994     {
2995       FT_UInt              instance_index;
2996       FT_Var_Named_Style*  named_style;
2997 
2998 
2999       instance_index = (FT_UInt)face->root.face_index >> 16;
3000       named_style    = mmvar->namedstyle + instance_index - 1;
3001 
3002       n = named_style->coords + num_coords;
3003       for ( ; i < mmvar->num_axis; i++, n++, c++ )
3004       {
3005         if ( *c != *n )
3006         {
3007           *c        = *n;
3008           have_diff = 1;
3009         }
3010       }
3011     }
3012     else
3013     {
3014       FT_Var_Axis*  a;
3015 
3016 
3017       a = mmvar->axis + num_coords;
3018       for ( ; i < mmvar->num_axis; i++, a++, c++ )
3019       {
3020         if ( *c != a->def )
3021         {
3022           *c        = a->def;
3023           have_diff = 1;
3024         }
3025       }
3026     }
3027 
3028     /* return value -1 indicates `no change';                      */
3029     /* we can exit early if `normalizedcoords' is already computed */
3030     if ( blend->normalizedcoords && !have_diff )
3031       return -1;
3032 
3033     if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
3034       goto Exit;
3035 
3036     if ( !face->blend->avar_loaded )
3037       ft_var_load_avar( face );
3038 
3039     FT_TRACE5(( "TT_Set_Var_Design:\n" ));
3040     FT_TRACE5(( "  normalized design coordinates:\n" ));
3041     ft_var_to_normalized( face, num_coords, blend->coords, normalized );
3042 
3043     error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 );
3044     if ( error )
3045       goto Exit;
3046 
3047     if ( num_coords )
3048       face->root.face_flags |= FT_FACE_FLAG_VARIATION;
3049     else
3050       face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
3051 
3052   Exit:
3053     FT_FREE( normalized );
3054     return error;
3055   }
3056 
3057 
3058   /**************************************************************************
3059    *
3060    * @Function:
3061    *   TT_Get_Var_Design
3062    *
3063    * @Description:
3064    *   Get the design coordinates of the currently selected interpolated
3065    *   font.
3066    *
3067    * @Input:
3068    *   face ::
3069    *     A handle to the source face.
3070    *
3071    *   num_coords ::
3072    *     The number of design coordinates to retrieve.  If it
3073    *     is larger than the number of axes, set the excess
3074    *     values to~0.
3075    *
3076    * @Output:
3077    *   coords ::
3078    *     The design coordinates array.
3079    *
3080    * @Return:
3081    *   FreeType error code.  0~means success.
3082    */
3083   FT_LOCAL_DEF( FT_Error )
TT_Get_Var_Design(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)3084   TT_Get_Var_Design( TT_Face    face,
3085                      FT_UInt    num_coords,
3086                      FT_Fixed*  coords )
3087   {
3088     FT_Error  error = FT_Err_Ok;
3089     GX_Blend  blend;
3090     FT_UInt   i, nc;
3091 
3092 
3093     if ( !face->blend )
3094     {
3095       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
3096         return error;
3097     }
3098 
3099     blend = face->blend;
3100 
3101     if ( !blend->coords )
3102     {
3103       /* select default instance coordinates */
3104       /* if no instance is selected yet      */
3105       if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
3106         return error;
3107     }
3108 
3109     nc = num_coords;
3110     if ( num_coords > blend->num_axis )
3111     {
3112       FT_TRACE2(( "TT_Get_Var_Design:"
3113                   " only using first %d of %d coordinates\n",
3114                   blend->num_axis, num_coords ));
3115       nc = blend->num_axis;
3116     }
3117 
3118     if ( face->doblend )
3119     {
3120       for ( i = 0; i < nc; i++ )
3121         coords[i] = blend->coords[i];
3122     }
3123     else
3124     {
3125       for ( i = 0; i < nc; i++ )
3126         coords[i] = 0;
3127     }
3128 
3129     for ( ; i < num_coords; i++ )
3130       coords[i] = 0;
3131 
3132     return FT_Err_Ok;
3133   }
3134 
3135 
3136   /**************************************************************************
3137    *
3138    * @Function:
3139    *   TT_Set_Named_Instance
3140    *
3141    * @Description:
3142    *   Set the given named instance, also resetting any further
3143    *   variation.
3144    *
3145    * @Input:
3146    *   face ::
3147    *     A handle to the source face.
3148    *
3149    *   instance_index ::
3150    *     The instance index, starting with value 1.
3151    *     Value 0 indicates to not use an instance.
3152    *
3153    * @Return:
3154    *   FreeType error code.  0~means success.
3155    */
3156   FT_LOCAL_DEF( FT_Error )
TT_Set_Named_Instance(TT_Face face,FT_UInt instance_index)3157   TT_Set_Named_Instance( TT_Face  face,
3158                          FT_UInt  instance_index )
3159   {
3160     FT_Error    error;
3161     GX_Blend    blend;
3162     FT_MM_Var*  mmvar;
3163 
3164     FT_UInt  num_instances;
3165 
3166 
3167     if ( !face->blend )
3168     {
3169       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
3170         goto Exit;
3171     }
3172 
3173     blend = face->blend;
3174     mmvar = blend->mmvar;
3175 
3176     num_instances = (FT_UInt)face->root.style_flags >> 16;
3177 
3178     /* `instance_index' starts with value 1, thus `>' */
3179     if ( instance_index > num_instances )
3180     {
3181       error = FT_ERR( Invalid_Argument );
3182       goto Exit;
3183     }
3184 
3185     if ( instance_index > 0 )
3186     {
3187       FT_Memory     memory = face->root.memory;
3188       SFNT_Service  sfnt   = (SFNT_Service)face->sfnt;
3189 
3190       FT_Var_Named_Style*  named_style;
3191       FT_String*           style_name;
3192 
3193 
3194       named_style = mmvar->namedstyle + instance_index - 1;
3195 
3196       error = sfnt->get_name( face,
3197                               (FT_UShort)named_style->strid,
3198                               &style_name );
3199       if ( error )
3200         goto Exit;
3201 
3202       /* set (or replace) style name */
3203       FT_FREE( face->root.style_name );
3204       face->root.style_name = style_name;
3205 
3206       /* finally, select the named instance */
3207       error = TT_Set_Var_Design( face,
3208                                  mmvar->num_axis,
3209                                  named_style->coords );
3210       if ( error )
3211       {
3212         /* internal error code -1 means `no change' */
3213         if ( error == -1 )
3214           error = FT_Err_Ok;
3215         goto Exit;
3216       }
3217     }
3218     else
3219       error = TT_Set_Var_Design( face, 0, NULL );
3220 
3221     face->root.face_index  = ( instance_index << 16 )             |
3222                              ( face->root.face_index & 0xFFFFL );
3223     face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
3224 
3225   Exit:
3226     return error;
3227   }
3228 
3229 
3230   /*************************************************************************/
3231   /*************************************************************************/
3232   /*****                                                               *****/
3233   /*****                     GX VAR PARSING ROUTINES                   *****/
3234   /*****                                                               *****/
3235   /*************************************************************************/
3236   /*************************************************************************/
3237 
3238 
3239 #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
3240 
3241   static FT_Error
tt_cvt_ready_iterator(FT_ListNode node,void * user)3242   tt_cvt_ready_iterator( FT_ListNode  node,
3243                          void*        user )
3244   {
3245     TT_Size  size = (TT_Size)node->data;
3246 
3247     FT_UNUSED( user );
3248 
3249 
3250     size->cvt_ready = -1;
3251 
3252     return FT_Err_Ok;
3253   }
3254 
3255 #endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
3256 
3257 
3258 
3259   /**************************************************************************
3260    *
3261    * @Function:
3262    *   tt_face_vary_cvt
3263    *
3264    * @Description:
3265    *   Modify the loaded cvt table according to the `cvar' table and the
3266    *   font's blend.
3267    *
3268    * @InOut:
3269    *   face ::
3270    *     A handle to the target face object.
3271    *
3272    * @Input:
3273    *   stream ::
3274    *     A handle to the input stream.
3275    *
3276    * @Return:
3277    *   FreeType error code.  0 means success.
3278    *
3279    *   Most errors are ignored.  It is perfectly valid not to have a
3280    *   `cvar' table even if there is a `gvar' and `fvar' table.
3281    */
3282   FT_LOCAL_DEF( FT_Error )
tt_face_vary_cvt(TT_Face face,FT_Stream stream)3283   tt_face_vary_cvt( TT_Face    face,
3284                     FT_Stream  stream )
3285   {
3286 #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
3287 
3288     FT_Error   error;
3289     FT_Memory  memory = stream->memory;
3290 
3291     FT_Face  root = &face->root;
3292 
3293     FT_ULong  table_start;
3294     FT_ULong  table_len;
3295 
3296     FT_UInt   tupleCount;
3297     FT_ULong  offsetToData;
3298 
3299     FT_ULong  here;
3300     FT_UInt   i, j;
3301 
3302     FT_Fixed*  tuple_coords    = NULL;
3303     FT_Fixed*  im_start_coords = NULL;
3304     FT_Fixed*  im_end_coords   = NULL;
3305 
3306     GX_Blend  blend = face->blend;
3307 
3308     FT_UInt  point_count;
3309     FT_UInt  spoint_count = 0;
3310 
3311     FT_UShort*  sharedpoints = NULL;
3312     FT_UShort*  localpoints  = NULL;
3313     FT_UShort*  points;
3314 
3315     FT_Fixed*  deltas     = NULL;
3316     FT_Fixed*  cvt_deltas = NULL;
3317 
3318 
3319     FT_TRACE2(( "CVAR " ));
3320 
3321     if ( !blend )
3322     {
3323       FT_TRACE2(( "\n" ));
3324       FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" ));
3325       error = FT_Err_Ok;
3326       goto Exit;
3327     }
3328 
3329     if ( !face->cvt )
3330     {
3331       FT_TRACE2(( "\n" ));
3332       FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" ));
3333       error = FT_Err_Ok;
3334       goto Exit;
3335     }
3336 
3337     error = face->goto_table( face, TTAG_cvar, stream, &table_len );
3338     if ( error )
3339     {
3340       FT_TRACE2(( "is missing\n" ));
3341 
3342       error = FT_Err_Ok;
3343       goto Exit;
3344     }
3345 
3346     if ( FT_FRAME_ENTER( table_len ) )
3347     {
3348       error = FT_Err_Ok;
3349       goto Exit;
3350     }
3351 
3352     table_start = FT_Stream_FTell( stream );
3353     if ( FT_GET_LONG() != 0x00010000L )
3354     {
3355       FT_TRACE2(( "bad table version\n" ));
3356 
3357       error = FT_Err_Ok;
3358       goto FExit;
3359     }
3360 
3361     FT_TRACE2(( "loaded\n" ));
3362 
3363     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
3364          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3365          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
3366       goto FExit;
3367 
3368     tupleCount   = FT_GET_USHORT();
3369     offsetToData = FT_GET_USHORT();
3370 
3371     /* rough sanity test */
3372     if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3373            table_len )
3374     {
3375       FT_TRACE2(( "tt_face_vary_cvt:"
3376                   " invalid CVT variation array header\n" ));
3377 
3378       error = FT_THROW( Invalid_Table );
3379       goto FExit;
3380     }
3381 
3382     offsetToData += table_start;
3383 
3384     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3385     {
3386       here = FT_Stream_FTell( stream );
3387 
3388       FT_Stream_SeekSet( stream, offsetToData );
3389 
3390       sharedpoints = ft_var_readpackedpoints( stream,
3391                                               table_len,
3392                                               &spoint_count );
3393       offsetToData = FT_Stream_FTell( stream );
3394 
3395       FT_Stream_SeekSet( stream, here );
3396     }
3397 
3398     FT_TRACE5(( "cvar: there %s %d tuple%s:\n",
3399                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3400                 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3401                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
3402 
3403     if ( FT_NEW_ARRAY( cvt_deltas, face->cvt_size ) )
3404       goto FExit;
3405 
3406     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
3407     {
3408       FT_UInt   tupleDataSize;
3409       FT_UInt   tupleIndex;
3410       FT_Fixed  apply;
3411 
3412 
3413       FT_TRACE6(( "  tuple %d:\n", i ));
3414 
3415       tupleDataSize = FT_GET_USHORT();
3416       tupleIndex    = FT_GET_USHORT();
3417 
3418       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3419       {
3420         for ( j = 0; j < blend->num_axis; j++ )
3421           tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
3422       }
3423       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3424       {
3425         FT_TRACE2(( "tt_face_vary_cvt:"
3426                     " invalid tuple index\n" ));
3427 
3428         error = FT_THROW( Invalid_Table );
3429         goto FExit;
3430       }
3431       else
3432       {
3433         if ( !blend->tuplecoords )
3434         {
3435           FT_TRACE2(( "tt_face_vary_cvt:"
3436                       " no valid tuple coordinates available\n" ));
3437 
3438           error = FT_THROW( Invalid_Table );
3439           goto FExit;
3440         }
3441 
3442         FT_MEM_COPY(
3443           tuple_coords,
3444           blend->tuplecoords +
3445             ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
3446           blend->num_axis * sizeof ( FT_Fixed ) );
3447       }
3448 
3449       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3450       {
3451         for ( j = 0; j < blend->num_axis; j++ )
3452           im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
3453         for ( j = 0; j < blend->num_axis; j++ )
3454           im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
3455       }
3456 
3457       apply = ft_var_apply_tuple( blend,
3458                                   (FT_UShort)tupleIndex,
3459                                   tuple_coords,
3460                                   im_start_coords,
3461                                   im_end_coords );
3462 
3463       if ( apply == 0 )              /* tuple isn't active for our blend */
3464       {
3465         offsetToData += tupleDataSize;
3466         continue;
3467       }
3468 
3469       here = FT_Stream_FTell( stream );
3470 
3471       FT_Stream_SeekSet( stream, offsetToData );
3472 
3473       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3474       {
3475         localpoints = ft_var_readpackedpoints( stream,
3476                                                table_len,
3477                                                &point_count );
3478         points      = localpoints;
3479       }
3480       else
3481       {
3482         points      = sharedpoints;
3483         point_count = spoint_count;
3484       }
3485 
3486       deltas = ft_var_readpackeddeltas( stream,
3487                                         table_len,
3488                                         point_count == 0 ? face->cvt_size
3489                                                          : point_count );
3490 
3491       if ( !points || !deltas )
3492         ; /* failure, ignore it */
3493 
3494       else if ( localpoints == ALL_POINTS )
3495       {
3496 #ifdef FT_DEBUG_LEVEL_TRACE
3497         int  count = 0;
3498 #endif
3499 
3500 
3501         FT_TRACE7(( "    CVT deltas:\n" ));
3502 
3503         /* this means that there are deltas for every entry in cvt */
3504         for ( j = 0; j < face->cvt_size; j++ )
3505         {
3506           FT_Fixed  old_cvt_delta;
3507 
3508 
3509           old_cvt_delta = cvt_deltas[j];
3510           cvt_deltas[j] = old_cvt_delta + FT_MulFix( deltas[j], apply );
3511 
3512 #ifdef FT_DEBUG_LEVEL_TRACE
3513           if ( old_cvt_delta != cvt_deltas[j] )
3514           {
3515             FT_TRACE7(( "      %d: %f -> %f\n",
3516                         j,
3517                         ( FT_fdot6ToFixed( face->cvt[j] ) +
3518                           old_cvt_delta ) / 65536.0,
3519                         ( FT_fdot6ToFixed( face->cvt[j] ) +
3520                           cvt_deltas[j] ) / 65536.0 ));
3521             count++;
3522           }
3523 #endif
3524         }
3525 
3526 #ifdef FT_DEBUG_LEVEL_TRACE
3527         if ( !count )
3528           FT_TRACE7(( "      none\n" ));
3529 #endif
3530       }
3531 
3532       else
3533       {
3534 #ifdef FT_DEBUG_LEVEL_TRACE
3535         int  count = 0;
3536 #endif
3537 
3538 
3539         FT_TRACE7(( "    CVT deltas:\n" ));
3540 
3541         for ( j = 0; j < point_count; j++ )
3542         {
3543           int       pindex;
3544           FT_Fixed  old_cvt_delta;
3545 
3546 
3547           pindex = points[j];
3548           if ( (FT_ULong)pindex >= face->cvt_size )
3549             continue;
3550 
3551           old_cvt_delta      = cvt_deltas[pindex];
3552           cvt_deltas[pindex] = old_cvt_delta + FT_MulFix( deltas[j], apply );
3553 
3554 #ifdef FT_DEBUG_LEVEL_TRACE
3555           if ( old_cvt_delta != cvt_deltas[pindex] )
3556           {
3557             FT_TRACE7(( "      %d: %f -> %f\n",
3558                         pindex,
3559                         ( FT_fdot6ToFixed( face->cvt[pindex] ) +
3560                           old_cvt_delta ) / 65536.0,
3561                         ( FT_fdot6ToFixed( face->cvt[pindex] ) +
3562                           cvt_deltas[pindex] ) / 65536.0 ));
3563             count++;
3564           }
3565 #endif
3566         }
3567 
3568 #ifdef FT_DEBUG_LEVEL_TRACE
3569         if ( !count )
3570           FT_TRACE7(( "      none\n" ));
3571 #endif
3572       }
3573 
3574       if ( localpoints != ALL_POINTS )
3575         FT_FREE( localpoints );
3576       FT_FREE( deltas );
3577 
3578       offsetToData += tupleDataSize;
3579 
3580       FT_Stream_SeekSet( stream, here );
3581     }
3582 
3583     FT_TRACE5(( "\n" ));
3584 
3585     for ( i = 0; i < face->cvt_size; i++ )
3586       face->cvt[i] += FT_fixedToFdot6( cvt_deltas[i] );
3587 
3588   FExit:
3589     FT_FRAME_EXIT();
3590 
3591   Exit:
3592     if ( sharedpoints != ALL_POINTS )
3593       FT_FREE( sharedpoints );
3594     FT_FREE( tuple_coords );
3595     FT_FREE( im_start_coords );
3596     FT_FREE( im_end_coords );
3597     FT_FREE( cvt_deltas );
3598 
3599     /* iterate over all FT_Size objects and set `cvt_ready' to -1 */
3600     /* to trigger rescaling of all CVT values                     */
3601     FT_List_Iterate( &root->sizes_list,
3602                      tt_cvt_ready_iterator,
3603                      NULL );
3604 
3605     return error;
3606 
3607 #else /* !TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
3608 
3609     FT_UNUSED( face );
3610     FT_UNUSED( stream );
3611 
3612     return FT_Err_Ok;
3613 
3614 #endif /* !TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
3615 
3616   }
3617 
3618 
3619   /* Shift the original coordinates of all points between indices `p1' */
3620   /* and `p2', using the same difference as given by index `ref'.      */
3621 
3622   /* modeled after `af_iup_shift' */
3623 
3624   static void
tt_delta_shift(int p1,int p2,int ref,FT_Vector * in_points,FT_Vector * out_points)3625   tt_delta_shift( int         p1,
3626                   int         p2,
3627                   int         ref,
3628                   FT_Vector*  in_points,
3629                   FT_Vector*  out_points )
3630   {
3631     int        p;
3632     FT_Vector  delta;
3633 
3634 
3635     delta.x = out_points[ref].x - in_points[ref].x;
3636     delta.y = out_points[ref].y - in_points[ref].y;
3637 
3638     if ( delta.x == 0 && delta.y == 0 )
3639       return;
3640 
3641     for ( p = p1; p < ref; p++ )
3642     {
3643       out_points[p].x += delta.x;
3644       out_points[p].y += delta.y;
3645     }
3646 
3647     for ( p = ref + 1; p <= p2; p++ )
3648     {
3649       out_points[p].x += delta.x;
3650       out_points[p].y += delta.y;
3651     }
3652   }
3653 
3654 
3655   /* Interpolate the original coordinates of all points with indices */
3656   /* between `p1' and `p2', using `ref1' and `ref2' as the reference */
3657   /* point indices.                                                  */
3658 
3659   /* modeled after `af_iup_interp', `_iup_worker_interpolate', and   */
3660   /* `Ins_IUP' with spec differences in handling ill-defined cases.  */
3661   static void
tt_delta_interpolate(int p1,int p2,int ref1,int ref2,FT_Vector * in_points,FT_Vector * out_points)3662   tt_delta_interpolate( int         p1,
3663                         int         p2,
3664                         int         ref1,
3665                         int         ref2,
3666                         FT_Vector*  in_points,
3667                         FT_Vector*  out_points )
3668   {
3669     int  p, i;
3670 
3671     FT_Pos  out, in1, in2, out1, out2, d1, d2;
3672 
3673 
3674     if ( p1 > p2 )
3675       return;
3676 
3677     /* handle both horizontal and vertical coordinates */
3678     for ( i = 0; i <= 1; i++ )
3679     {
3680       /* shift array pointers so that we can access `foo.y' as `foo.x' */
3681       in_points  = (FT_Vector*)( (FT_Pos*)in_points + i );
3682       out_points = (FT_Vector*)( (FT_Pos*)out_points + i );
3683 
3684       if ( in_points[ref1].x > in_points[ref2].x )
3685       {
3686         p    = ref1;
3687         ref1 = ref2;
3688         ref2 = p;
3689       }
3690 
3691       in1  = in_points[ref1].x;
3692       in2  = in_points[ref2].x;
3693       out1 = out_points[ref1].x;
3694       out2 = out_points[ref2].x;
3695       d1   = out1 - in1;
3696       d2   = out2 - in2;
3697 
3698       /* If the reference points have the same coordinate but different */
3699       /* delta, inferred delta is zero.  Otherwise interpolate.         */
3700       if ( in1 != in2 || out1 == out2 )
3701       {
3702         FT_Fixed  scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 )
3703                                      : 0;
3704 
3705 
3706         for ( p = p1; p <= p2; p++ )
3707         {
3708           out = in_points[p].x;
3709 
3710           if ( out <= in1 )
3711             out += d1;
3712           else if ( out >= in2 )
3713             out += d2;
3714           else
3715             out = out1 + FT_MulFix( out - in1, scale );
3716 
3717           out_points[p].x = out;
3718         }
3719       }
3720     }
3721   }
3722 
3723 
3724   /* Interpolate points without delta values, similar to */
3725   /* the `IUP' hinting instruction.                      */
3726 
3727   /* modeled after `Ins_IUP */
3728 
3729   static void
tt_interpolate_deltas(FT_Outline * outline,FT_Vector * out_points,FT_Vector * in_points,FT_Bool * has_delta)3730   tt_interpolate_deltas( FT_Outline*  outline,
3731                          FT_Vector*   out_points,
3732                          FT_Vector*   in_points,
3733                          FT_Bool*     has_delta )
3734   {
3735     FT_Int  first_point;
3736     FT_Int  end_point;
3737 
3738     FT_Int  first_delta;
3739     FT_Int  cur_delta;
3740 
3741     FT_Int    point;
3742     FT_Short  contour;
3743 
3744 
3745     /* ignore empty outlines */
3746     if ( !outline->n_contours )
3747       return;
3748 
3749     contour = 0;
3750     point   = 0;
3751 
3752     do
3753     {
3754       end_point   = outline->contours[contour];
3755       first_point = point;
3756 
3757       /* search first point that has a delta */
3758       while ( point <= end_point && !has_delta[point] )
3759         point++;
3760 
3761       if ( point <= end_point )
3762       {
3763         first_delta = point;
3764         cur_delta   = point;
3765 
3766         point++;
3767 
3768         while ( point <= end_point )
3769         {
3770           /* search next point that has a delta  */
3771           /* and interpolate intermediate points */
3772           if ( has_delta[point] )
3773           {
3774             tt_delta_interpolate( cur_delta + 1,
3775                                   point - 1,
3776                                   cur_delta,
3777                                   point,
3778                                   in_points,
3779                                   out_points );
3780             cur_delta = point;
3781           }
3782 
3783           point++;
3784         }
3785 
3786         /* shift contour if we only have a single delta */
3787         if ( cur_delta == first_delta )
3788           tt_delta_shift( first_point,
3789                           end_point,
3790                           cur_delta,
3791                           in_points,
3792                           out_points );
3793         else
3794         {
3795           /* otherwise handle remaining points       */
3796           /* at the end and beginning of the contour */
3797           tt_delta_interpolate( cur_delta + 1,
3798                                 end_point,
3799                                 cur_delta,
3800                                 first_delta,
3801                                 in_points,
3802                                 out_points );
3803 
3804           if ( first_delta > 0 )
3805             tt_delta_interpolate( first_point,
3806                                   first_delta - 1,
3807                                   cur_delta,
3808                                   first_delta,
3809                                   in_points,
3810                                   out_points );
3811         }
3812       }
3813       contour++;
3814 
3815     } while ( contour < outline->n_contours );
3816   }
3817 
3818 
3819   /**************************************************************************
3820    *
3821    * @Function:
3822    *   TT_Vary_Apply_Glyph_Deltas
3823    *
3824    * @Description:
3825    *   Apply the appropriate deltas to the current glyph.
3826    *
3827    * @Input:
3828    *   face ::
3829    *     A handle to the target face object.
3830    *
3831    *   glyph_index ::
3832    *     The index of the glyph being modified.
3833    *
3834    *   n_points ::
3835    *     The number of the points in the glyph, including
3836    *     phantom points.
3837    *
3838    * @InOut:
3839    *   outline ::
3840    *     The outline to change.
3841    *
3842    * @Output:
3843    *   unrounded ::
3844    *     An array with `n_points' elements that is filled with unrounded
3845    *     point coordinates (in 26.6 format).
3846    *
3847    * @Return:
3848    *   FreeType error code.  0 means success.
3849    */
3850   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)3851   TT_Vary_Apply_Glyph_Deltas( TT_Face      face,
3852                               FT_UInt      glyph_index,
3853                               FT_Outline*  outline,
3854                               FT_Vector*   unrounded,
3855                               FT_UInt      n_points )
3856   {
3857     FT_Error   error;
3858     FT_Stream  stream = face->root.stream;
3859     FT_Memory  memory = stream->memory;
3860 
3861     FT_Vector*  points_org = NULL;  /* coordinates in 16.16 format */
3862     FT_Vector*  points_out = NULL;  /* coordinates in 16.16 format */
3863     FT_Bool*    has_delta  = NULL;
3864 
3865     FT_ULong  glyph_start;
3866 
3867     FT_UInt   tupleCount;
3868     FT_ULong  offsetToData;
3869     FT_ULong  dataSize;
3870 
3871     FT_ULong  here;
3872     FT_UInt   i, j;
3873 
3874     FT_Fixed*  tuple_coords    = NULL;
3875     FT_Fixed*  im_start_coords = NULL;
3876     FT_Fixed*  im_end_coords   = NULL;
3877 
3878     GX_Blend  blend = face->blend;
3879 
3880     FT_UInt  point_count;
3881     FT_UInt  spoint_count = 0;
3882 
3883     FT_UShort*  sharedpoints = NULL;
3884     FT_UShort*  localpoints  = NULL;
3885     FT_UShort*  points;
3886 
3887     FT_Fixed*  deltas_x       = NULL;
3888     FT_Fixed*  deltas_y       = NULL;
3889     FT_Fixed*  point_deltas_x = NULL;
3890     FT_Fixed*  point_deltas_y = NULL;
3891 
3892 
3893     if ( !face->doblend || !blend )
3894       return FT_THROW( Invalid_Argument );
3895 
3896     for ( i = 0; i < n_points; i++ )
3897     {
3898       unrounded[i].x = INT_TO_F26DOT6( outline->points[i].x );
3899       unrounded[i].y = INT_TO_F26DOT6( outline->points[i].y );
3900     }
3901 
3902     if ( glyph_index >= blend->gv_glyphcnt      ||
3903          blend->glyphoffsets[glyph_index] ==
3904            blend->glyphoffsets[glyph_index + 1] )
3905     {
3906       FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3907                   " no variation data for glyph %d\n", glyph_index ));
3908       return FT_Err_Ok;
3909     }
3910 
3911     if ( FT_NEW_ARRAY( points_org, n_points ) ||
3912          FT_NEW_ARRAY( points_out, n_points ) ||
3913          FT_NEW_ARRAY( has_delta, n_points )  )
3914       goto Fail1;
3915 
3916     dataSize = blend->glyphoffsets[glyph_index + 1] -
3917                  blend->glyphoffsets[glyph_index];
3918 
3919     if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) ||
3920          FT_FRAME_ENTER( dataSize )                         )
3921       goto Fail1;
3922 
3923     glyph_start = FT_Stream_FTell( stream );
3924 
3925     /* each set of glyph variation data is formatted similarly to `cvar' */
3926 
3927     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
3928          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3929          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
3930       goto Fail2;
3931 
3932     tupleCount   = FT_GET_USHORT();
3933     offsetToData = FT_GET_USHORT();
3934 
3935     /* rough sanity test */
3936     if ( offsetToData > dataSize                                ||
3937          ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > dataSize )
3938     {
3939       FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3940                   " invalid glyph variation array header\n" ));
3941 
3942       error = FT_THROW( Invalid_Table );
3943       goto Fail2;
3944     }
3945 
3946     offsetToData += glyph_start;
3947 
3948     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3949     {
3950       here = FT_Stream_FTell( stream );
3951 
3952       FT_Stream_SeekSet( stream, offsetToData );
3953 
3954       sharedpoints = ft_var_readpackedpoints( stream,
3955                                               blend->gvar_size,
3956                                               &spoint_count );
3957       offsetToData = FT_Stream_FTell( stream );
3958 
3959       FT_Stream_SeekSet( stream, here );
3960     }
3961 
3962     FT_TRACE5(( "gvar: there %s %d tuple%s:\n",
3963                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3964                 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3965                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
3966 
3967     if ( FT_NEW_ARRAY( point_deltas_x, n_points ) ||
3968          FT_NEW_ARRAY( point_deltas_y, n_points ) )
3969       goto Fail3;
3970 
3971     for ( j = 0; j < n_points; j++ )
3972     {
3973       points_org[j].x = FT_intToFixed( outline->points[j].x );
3974       points_org[j].y = FT_intToFixed( outline->points[j].y );
3975     }
3976 
3977     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
3978     {
3979       FT_UInt   tupleDataSize;
3980       FT_UInt   tupleIndex;
3981       FT_Fixed  apply;
3982 
3983 
3984       FT_TRACE6(( "  tuple %d:\n", i ));
3985 
3986       tupleDataSize = FT_GET_USHORT();
3987       tupleIndex    = FT_GET_USHORT();
3988 
3989       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3990       {
3991         for ( j = 0; j < blend->num_axis; j++ )
3992           tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
3993       }
3994       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3995       {
3996         FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3997                     " invalid tuple index\n" ));
3998 
3999         error = FT_THROW( Invalid_Table );
4000         goto Fail3;
4001       }
4002       else
4003         FT_MEM_COPY(
4004           tuple_coords,
4005           blend->tuplecoords +
4006             ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
4007           blend->num_axis * sizeof ( FT_Fixed ) );
4008 
4009       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
4010       {
4011         for ( j = 0; j < blend->num_axis; j++ )
4012           im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
4013         for ( j = 0; j < blend->num_axis; j++ )
4014           im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
4015       }
4016 
4017       apply = ft_var_apply_tuple( blend,
4018                                   (FT_UShort)tupleIndex,
4019                                   tuple_coords,
4020                                   im_start_coords,
4021                                   im_end_coords );
4022 
4023       if ( apply == 0 )              /* tuple isn't active for our blend */
4024       {
4025         offsetToData += tupleDataSize;
4026         continue;
4027       }
4028 
4029       here = FT_Stream_FTell( stream );
4030 
4031       FT_Stream_SeekSet( stream, offsetToData );
4032 
4033       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
4034       {
4035         localpoints = ft_var_readpackedpoints( stream,
4036                                                blend->gvar_size,
4037                                                &point_count );
4038         points      = localpoints;
4039       }
4040       else
4041       {
4042         points      = sharedpoints;
4043         point_count = spoint_count;
4044       }
4045 
4046       deltas_x = ft_var_readpackeddeltas( stream,
4047                                           blend->gvar_size,
4048                                           point_count == 0 ? n_points
4049                                                            : point_count );
4050       deltas_y = ft_var_readpackeddeltas( stream,
4051                                           blend->gvar_size,
4052                                           point_count == 0 ? n_points
4053                                                            : point_count );
4054 
4055       if ( !points || !deltas_y || !deltas_x )
4056         ; /* failure, ignore it */
4057 
4058       else if ( points == ALL_POINTS )
4059       {
4060 #ifdef FT_DEBUG_LEVEL_TRACE
4061         int  count = 0;
4062 #endif
4063 
4064 
4065         FT_TRACE7(( "    point deltas:\n" ));
4066 
4067         /* this means that there are deltas for every point in the glyph */
4068         for ( j = 0; j < n_points; j++ )
4069         {
4070           FT_Fixed  old_point_delta_x = point_deltas_x[j];
4071           FT_Fixed  old_point_delta_y = point_deltas_y[j];
4072 
4073           FT_Fixed  point_delta_x = FT_MulFix( deltas_x[j], apply );
4074           FT_Fixed  point_delta_y = FT_MulFix( deltas_y[j], apply );
4075 
4076 
4077           if ( j < n_points - 4 )
4078           {
4079             point_deltas_x[j] = old_point_delta_x + point_delta_x;
4080             point_deltas_y[j] = old_point_delta_y + point_delta_y;
4081           }
4082           else
4083           {
4084             /* To avoid double adjustment of advance width or height, */
4085             /* adjust phantom points only if there is no HVAR or VVAR */
4086             /* support, respectively.                                 */
4087             if ( j == ( n_points - 4 )        &&
4088                  !( face->variation_support &
4089                     TT_FACE_FLAG_VAR_LSB    ) )
4090               point_deltas_x[j] = old_point_delta_x + point_delta_x;
4091 
4092             else if ( j == ( n_points - 3 )          &&
4093                       !( face->variation_support   &
4094                          TT_FACE_FLAG_VAR_HADVANCE ) )
4095               point_deltas_x[j] = old_point_delta_x + point_delta_x;
4096 
4097             else if ( j == ( n_points - 2 )        &&
4098                       !( face->variation_support &
4099                          TT_FACE_FLAG_VAR_TSB    ) )
4100               point_deltas_y[j] = old_point_delta_y + point_delta_y;
4101 
4102             else if ( j == ( n_points - 1 )          &&
4103                       !( face->variation_support   &
4104                          TT_FACE_FLAG_VAR_VADVANCE ) )
4105               point_deltas_y[j] = old_point_delta_y + point_delta_y;
4106           }
4107 
4108 #ifdef FT_DEBUG_LEVEL_TRACE
4109           if ( point_delta_x || point_delta_y )
4110           {
4111             FT_TRACE7(( "      %d: (%f, %f) -> (%f, %f)\n",
4112                         j,
4113                         ( FT_intToFixed( outline->points[j].x ) +
4114                           old_point_delta_x ) / 65536.0,
4115                         ( FT_intToFixed( outline->points[j].y ) +
4116                           old_point_delta_y ) / 65536.0,
4117                         ( FT_intToFixed( outline->points[j].x ) +
4118                           point_deltas_x[j] ) / 65536.0,
4119                         ( FT_intToFixed( outline->points[j].y ) +
4120                           point_deltas_y[j] ) / 65536.0 ));
4121             count++;
4122           }
4123 #endif
4124         }
4125 
4126 #ifdef FT_DEBUG_LEVEL_TRACE
4127         if ( !count )
4128           FT_TRACE7(( "      none\n" ));
4129 #endif
4130       }
4131 
4132       else
4133       {
4134 #ifdef FT_DEBUG_LEVEL_TRACE
4135         int  count = 0;
4136 #endif
4137 
4138 
4139         /* we have to interpolate the missing deltas similar to the */
4140         /* IUP bytecode instruction                                 */
4141         for ( j = 0; j < n_points; j++ )
4142         {
4143           has_delta[j]  = FALSE;
4144           points_out[j] = points_org[j];
4145         }
4146 
4147         for ( j = 0; j < point_count; j++ )
4148         {
4149           FT_UShort  idx = points[j];
4150 
4151 
4152           if ( idx >= n_points )
4153             continue;
4154 
4155           has_delta[idx] = TRUE;
4156 
4157           points_out[idx].x += FT_MulFix( deltas_x[j], apply );
4158           points_out[idx].y += FT_MulFix( deltas_y[j], apply );
4159         }
4160 
4161         /* no need to handle phantom points here,      */
4162         /* since solitary points can't be interpolated */
4163         tt_interpolate_deltas( outline,
4164                                points_out,
4165                                points_org,
4166                                has_delta );
4167 
4168         FT_TRACE7(( "    point deltas:\n" ));
4169 
4170         for ( j = 0; j < n_points; j++ )
4171         {
4172           FT_Fixed  old_point_delta_x = point_deltas_x[j];
4173           FT_Fixed  old_point_delta_y = point_deltas_y[j];
4174 
4175           FT_Pos  point_delta_x = points_out[j].x - points_org[j].x;
4176           FT_Pos  point_delta_y = points_out[j].y - points_org[j].y;
4177 
4178 
4179           if ( j < n_points - 4 )
4180           {
4181             point_deltas_x[j] = old_point_delta_x + point_delta_x;
4182             point_deltas_y[j] = old_point_delta_y + point_delta_y;
4183           }
4184           else
4185           {
4186             /* To avoid double adjustment of advance width or height, */
4187             /* adjust phantom points only if there is no HVAR or VVAR */
4188             /* support, respectively.                                 */
4189             if ( j == ( n_points - 4 )        &&
4190                  !( face->variation_support &
4191                     TT_FACE_FLAG_VAR_LSB    ) )
4192               point_deltas_x[j] = old_point_delta_x + point_delta_x;
4193 
4194             else if ( j == ( n_points - 3 )          &&
4195                       !( face->variation_support   &
4196                          TT_FACE_FLAG_VAR_HADVANCE ) )
4197               point_deltas_x[j] = old_point_delta_x + point_delta_x;
4198 
4199             else if ( j == ( n_points - 2 )        &&
4200                       !( face->variation_support &
4201                          TT_FACE_FLAG_VAR_TSB    ) )
4202               point_deltas_y[j] = old_point_delta_y + point_delta_y;
4203 
4204             else if ( j == ( n_points - 1 )          &&
4205                       !( face->variation_support   &
4206                          TT_FACE_FLAG_VAR_VADVANCE ) )
4207               point_deltas_y[j] = old_point_delta_y + point_delta_y;
4208           }
4209 
4210 #ifdef FT_DEBUG_LEVEL_TRACE
4211           if ( point_delta_x || point_delta_y )
4212           {
4213             FT_TRACE7(( "      %d: (%f, %f) -> (%f, %f)\n",
4214                         j,
4215                         ( FT_intToFixed( outline->points[j].x ) +
4216                           old_point_delta_x ) / 65536.0,
4217                         ( FT_intToFixed( outline->points[j].y ) +
4218                           old_point_delta_y ) / 65536.0,
4219                         ( FT_intToFixed( outline->points[j].x ) +
4220                           point_deltas_x[j] ) / 65536.0,
4221                         ( FT_intToFixed( outline->points[j].y ) +
4222                           point_deltas_y[j] ) / 65536.0 ));
4223             count++;
4224           }
4225 #endif
4226         }
4227 
4228 #ifdef FT_DEBUG_LEVEL_TRACE
4229         if ( !count )
4230           FT_TRACE7(( "      none\n" ));
4231 #endif
4232       }
4233 
4234       if ( localpoints != ALL_POINTS )
4235         FT_FREE( localpoints );
4236       FT_FREE( deltas_x );
4237       FT_FREE( deltas_y );
4238 
4239       offsetToData += tupleDataSize;
4240 
4241       FT_Stream_SeekSet( stream, here );
4242     }
4243 
4244     FT_TRACE5(( "\n" ));
4245 
4246     for ( i = 0; i < n_points; i++ )
4247     {
4248       unrounded[i].x += FT_fixedToFdot6( point_deltas_x[i] );
4249       unrounded[i].y += FT_fixedToFdot6( point_deltas_y[i] );
4250 
4251       outline->points[i].x += FT_fixedToInt( point_deltas_x[i] );
4252       outline->points[i].y += FT_fixedToInt( point_deltas_y[i] );
4253     }
4254 
4255   Fail3:
4256     FT_FREE( point_deltas_x );
4257     FT_FREE( point_deltas_y );
4258 
4259   Fail2:
4260     if ( sharedpoints != ALL_POINTS )
4261       FT_FREE( sharedpoints );
4262     FT_FREE( tuple_coords );
4263     FT_FREE( im_start_coords );
4264     FT_FREE( im_end_coords );
4265 
4266     FT_FRAME_EXIT();
4267 
4268   Fail1:
4269     FT_FREE( points_org );
4270     FT_FREE( points_out );
4271     FT_FREE( has_delta );
4272 
4273     return error;
4274   }
4275 
4276 
4277   /**************************************************************************
4278    *
4279    * @Function:
4280    *   tt_get_var_blend
4281    *
4282    * @Description:
4283    *   An extended internal version of `TT_Get_MM_Blend' that returns
4284    *   pointers instead of copying data, without any initialization of
4285    *   the MM machinery in case it isn't loaded yet.
4286    */
4287   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)4288   tt_get_var_blend( TT_Face      face,
4289                     FT_UInt     *num_coords,
4290                     FT_Fixed*   *coords,
4291                     FT_Fixed*   *normalizedcoords,
4292                     FT_MM_Var*  *mm_var )
4293   {
4294     if ( face->blend )
4295     {
4296       if ( num_coords )
4297         *num_coords       = face->blend->num_axis;
4298       if ( coords )
4299         *coords           = face->blend->coords;
4300       if ( normalizedcoords )
4301         *normalizedcoords = face->blend->normalizedcoords;
4302       if ( mm_var )
4303         *mm_var           = face->blend->mmvar;
4304     }
4305     else
4306     {
4307       if ( num_coords )
4308         *num_coords = 0;
4309       if ( coords )
4310         *coords     = NULL;
4311       if ( mm_var )
4312         *mm_var     = NULL;
4313     }
4314 
4315     return FT_Err_Ok;
4316   }
4317 
4318 
4319   static void
ft_var_done_item_variation_store(TT_Face face,GX_ItemVarStore itemStore)4320   ft_var_done_item_variation_store( TT_Face          face,
4321                                     GX_ItemVarStore  itemStore )
4322   {
4323     FT_Memory  memory = FT_FACE_MEMORY( face );
4324     FT_UInt    i;
4325 
4326 
4327     if ( itemStore->varData )
4328     {
4329       for ( i = 0; i < itemStore->dataCount; i++ )
4330       {
4331         FT_FREE( itemStore->varData[i].regionIndices );
4332         FT_FREE( itemStore->varData[i].deltaSet );
4333       }
4334 
4335       FT_FREE( itemStore->varData );
4336     }
4337 
4338     if ( itemStore->varRegionList )
4339     {
4340       for ( i = 0; i < itemStore->regionCount; i++ )
4341         FT_FREE( itemStore->varRegionList[i].axisList );
4342 
4343       FT_FREE( itemStore->varRegionList );
4344     }
4345   }
4346 
4347 
4348   /**************************************************************************
4349    *
4350    * @Function:
4351    *   tt_done_blend
4352    *
4353    * @Description:
4354    *   Free the blend internal data structure.
4355    */
4356   FT_LOCAL_DEF( void )
tt_done_blend(TT_Face face)4357   tt_done_blend( TT_Face  face )
4358   {
4359     FT_Memory  memory = FT_FACE_MEMORY( face );
4360     GX_Blend   blend  = face->blend;
4361 
4362 
4363     if ( blend )
4364     {
4365       FT_UInt  i, num_axes;
4366 
4367 
4368       /* blend->num_axis might not be set up yet */
4369       num_axes = blend->mmvar->num_axis;
4370 
4371       FT_FREE( blend->coords );
4372       FT_FREE( blend->normalizedcoords );
4373       FT_FREE( blend->normalized_stylecoords );
4374       FT_FREE( blend->mmvar );
4375 
4376       if ( blend->avar_segment )
4377       {
4378         for ( i = 0; i < num_axes; i++ )
4379           FT_FREE( blend->avar_segment[i].correspondence );
4380         FT_FREE( blend->avar_segment );
4381       }
4382 
4383       if ( blend->hvar_table )
4384       {
4385         ft_var_done_item_variation_store( face,
4386                                           &blend->hvar_table->itemStore );
4387 
4388         FT_FREE( blend->hvar_table->widthMap.innerIndex );
4389         FT_FREE( blend->hvar_table->widthMap.outerIndex );
4390         FT_FREE( blend->hvar_table );
4391       }
4392 
4393       if ( blend->vvar_table )
4394       {
4395         ft_var_done_item_variation_store( face,
4396                                           &blend->vvar_table->itemStore );
4397 
4398         FT_FREE( blend->vvar_table->widthMap.innerIndex );
4399         FT_FREE( blend->vvar_table->widthMap.outerIndex );
4400         FT_FREE( blend->vvar_table );
4401       }
4402 
4403       if ( blend->mvar_table )
4404       {
4405         ft_var_done_item_variation_store( face,
4406                                           &blend->mvar_table->itemStore );
4407 
4408         FT_FREE( blend->mvar_table->values );
4409         FT_FREE( blend->mvar_table );
4410       }
4411 
4412       FT_FREE( blend->tuplecoords );
4413       FT_FREE( blend->glyphoffsets );
4414       FT_FREE( blend );
4415     }
4416   }
4417 
4418 #else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4419 
4420   /* ANSI C doesn't like empty source files */
4421   typedef int  _tt_gxvar_dummy;
4422 
4423 #endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4424 
4425 
4426 /* END */
4427