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