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