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