ReactOS  0.4.15-dev-499-g1f31905
ttgxvar.c
Go to the documentation of this file.
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*
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;
137  FT_Memory memory = stream->memory;
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*
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;
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
309  ft_var_load_avar( TT_Face face )
310  {
312  FT_Memory memory = stream->memory;
313  GX_Blend blend = face->blend;
314  GX_AVarSegment segment;
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
409  ft_var_load_item_variation_store( TT_Face face,
411  GX_ItemVarStore itemStore )
412  {
414  FT_Memory memory = stream->memory;
415 
416  FT_Error error;
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 ) ||
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
615  ft_var_load_delta_set_index_mapping( TT_Face face,
617  GX_DeltaSetIdxMap map,
618  GX_ItemVarStore itemStore )
619  {
621  FT_Memory memory = stream->memory;
622 
623  FT_Error error;
624 
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
729  ft_var_load_hvvar( TT_Face face,
730  FT_Bool vertical )
731  {
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
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
960  tt_hvadvance_adjust( TT_Face face,
961  FT_UInt gindex,
962  FT_Int *avalue,
963  FT_Bool vertical )
964  {
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 
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 
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*
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
1169  ft_var_load_mvar( TT_Face face )
1170  {
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
1286  tt_size_reset_iterator( FT_ListNode node,
1287  void* user )
1288  {
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 )
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
1419  ft_var_load_gvar( TT_Face face )
1420  {
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 ),
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
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
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  FT_TRACE5(( "%d design coordinate%s:\n",
1737  num_coords,
1738  num_coords == 1 ? "" : "s" ));
1739 
1740  a = mmvar->axis;
1741  for ( i = 0; i < num_coords; i++, a++ )
1742  {
1743  FT_Fixed coord = coords[i];
1744 
1745 
1746  FT_TRACE5(( " %.5f\n", coord / 65536.0 ));
1747  if ( coord > a->maximum || coord < a->minimum )
1748  {
1749  FT_TRACE1((
1750  "ft_var_to_normalized: design coordinate %.5f\n"
1751  " is out of range [%.5f;%.5f]; clamping\n",
1752  coord / 65536.0,
1753  a->minimum / 65536.0,
1754  a->maximum / 65536.0 ));
1755 
1756  if ( coord > a->maximum )
1757  coord = a->maximum;
1758  else
1759  coord = a->minimum;
1760  }
1761 
1762  if ( coord < a->def )
1763  normalized[i] = -FT_DivFix( coord - a->def,
1764  a->minimum - a->def );
1765  else if ( coord > a->def )
1766  normalized[i] = FT_DivFix( coord - a->def,
1767  a->maximum - a->def );
1768  else
1769  normalized[i] = 0;
1770  }
1771 
1772  FT_TRACE5(( "\n" ));
1773 
1774  for ( ; i < mmvar->num_axis; i++ )
1775  normalized[i] = 0;
1776 
1777  if ( blend->avar_segment )
1778  {
1779  FT_TRACE5(( "normalized design coordinates"
1780  " before applying `avar' data:\n" ));
1781 
1782  av = blend->avar_segment;
1783  for ( i = 0; i < mmvar->num_axis; i++, av++ )
1784  {
1785  for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1786  {
1787  if ( normalized[i] < av->correspondence[j].fromCoord )
1788  {
1789  FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 ));
1790 
1791  normalized[i] =
1792  FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
1793  av->correspondence[j].toCoord -
1794  av->correspondence[j - 1].toCoord,
1795  av->correspondence[j].fromCoord -
1796  av->correspondence[j - 1].fromCoord ) +
1797  av->correspondence[j - 1].toCoord;
1798  break;
1799  }
1800  }
1801  }
1802  }
1803  }
1804 
1805 
1806  /* convert from normalized coordinates to design coordinates */
1807 
1808  static void
1809  ft_var_to_design( TT_Face face,
1810  FT_UInt num_coords,
1811  FT_Fixed* coords,
1812  FT_Fixed* design )
1813  {
1814  GX_Blend blend;
1815  FT_MM_Var* mmvar;
1816  FT_Var_Axis* a;
1817 
1818  FT_UInt i, j, nc;
1819 
1820 
1821  blend = face->blend;
1822 
1823  nc = num_coords;
1824  if ( num_coords > blend->num_axis )
1825  {
1826  FT_TRACE2(( "ft_var_to_design:"
1827  " only using first %d of %d coordinates\n",
1828  blend->num_axis, num_coords ));
1829  nc = blend->num_axis;
1830  }
1831 
1832  for ( i = 0; i < nc; i++ )
1833  design[i] = coords[i];
1834 
1835  for ( ; i < num_coords; i++ )
1836  design[i] = 0;
1837 
1838  if ( blend->avar_segment )
1839  {
1840  GX_AVarSegment av = blend->avar_segment;
1841 
1842 
1843  FT_TRACE5(( "design coordinates"
1844  " after removing `avar' distortion:\n" ));
1845 
1846  for ( i = 0; i < nc; i++, av++ )
1847  {
1848  for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1849  {
1850  if ( design[i] < av->correspondence[j].toCoord )
1851  {
1852  design[i] =
1853  FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord,
1854  av->correspondence[j].fromCoord -
1855  av->correspondence[j - 1].fromCoord,
1856  av->correspondence[j].toCoord -
1857  av->correspondence[j - 1].toCoord ) +
1858  av->correspondence[j - 1].fromCoord;
1859 
1860  FT_TRACE5(( " %.5f\n", design[i] / 65536.0 ));
1861  break;
1862  }
1863  }
1864  }
1865  }
1866 
1867  mmvar = blend->mmvar;
1868  a = mmvar->axis;
1869 
1870  for ( i = 0; i < nc; i++, a++ )
1871  {
1872  if ( design[i] < 0 )
1873  design[i] = a->def + FT_MulFix( design[i],
1874  a->def - a->minimum );
1875  else if ( design[i] > 0 )
1876  design[i] = a->def + FT_MulFix( design[i],
1877  a->maximum - a->def );
1878  else
1879  design[i] = a->def;
1880  }
1881  }
1882 
1883 
1884  /*************************************************************************/
1885  /*************************************************************************/
1886  /***** *****/
1887  /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/
1888  /***** *****/
1889  /*************************************************************************/
1890  /*************************************************************************/
1891 
1892 
1893  typedef struct GX_FVar_Head_
1894  {
1895  FT_Long version;
1896  FT_UShort offsetToData;
1897  FT_UShort axisCount;
1898  FT_UShort axisSize;
1899  FT_UShort instanceCount;
1900  FT_UShort instanceSize;
1901 
1902  } GX_FVar_Head;
1903 
1904 
1905  typedef struct fvar_axis_
1906  {
1907  FT_ULong axisTag;
1908  FT_Fixed minValue;
1909  FT_Fixed defaultValue;
1910  FT_Fixed maxValue;
1911  FT_UShort flags;
1912  FT_UShort nameID;
1913 
1914  } GX_FVar_Axis;
1915 
1916 
1917  /*************************************************************************/
1918  /* */
1919  /* <Function> */
1920  /* TT_Get_MM_Var */
1921  /* */
1922  /* <Description> */
1923  /* Check that the font's `fvar' table is valid, parse it, and return */
1924  /* those data. It also loads (and parses) the `MVAR' table, if */
1925  /* possible. */
1926  /* */
1927  /* <InOut> */
1928  /* face :: The font face. */
1929  /* TT_Get_MM_Var initializes the blend structure. */
1930  /* */
1931  /* <Output> */
1932  /* master :: The `fvar' data (must be freed by caller). Can be NULL, */
1933  /* which makes this function simply load MM support. */
1934  /* */
1935  /* <Return> */
1936  /* FreeType error code. 0 means success. */
1937  /* */
1939  TT_Get_MM_Var( TT_Face face,
1940  FT_MM_Var* *master )
1941  {
1942  FT_Stream stream = face->root.stream;
1943  FT_Memory memory = face->root.memory;
1944  FT_ULong table_len;
1946  FT_ULong fvar_start = 0;
1947  FT_UInt i, j;
1948  FT_MM_Var* mmvar = NULL;
1949  FT_Fixed* next_coords;
1950  FT_Fixed* nsc;
1951  FT_String* next_name;
1952  FT_Var_Axis* a;
1953  FT_Fixed* c;
1955  GX_FVar_Head fvar_head;
1956  FT_Bool usePsName = 0;
1957  FT_UInt num_instances;
1958  FT_UInt num_axes;
1959  FT_UShort* axis_flags;
1960 
1961  FT_Offset mmvar_size;
1962  FT_Offset axis_flags_size;
1963  FT_Offset axis_size;
1964  FT_Offset namedstyle_size;
1965  FT_Offset next_coords_size;
1966  FT_Offset next_name_size;
1967 
1968  FT_Bool need_init;
1969 
1970  static const FT_Frame_Field fvar_fields[] =
1971  {
1972 
1973 #undef FT_STRUCTURE
1974 #define FT_STRUCTURE GX_FVar_Head
1975 
1976  FT_FRAME_START( 16 ),
1977  FT_FRAME_LONG ( version ),
1978  FT_FRAME_USHORT ( offsetToData ),
1980  FT_FRAME_USHORT ( axisCount ),
1981  FT_FRAME_USHORT ( axisSize ),
1982  FT_FRAME_USHORT ( instanceCount ),
1983  FT_FRAME_USHORT ( instanceSize ),
1984  FT_FRAME_END
1985  };
1986 
1987  static const FT_Frame_Field fvaraxis_fields[] =
1988  {
1989 
1990 #undef FT_STRUCTURE
1991 #define FT_STRUCTURE GX_FVar_Axis
1992 
1993  FT_FRAME_START( 20 ),
1994  FT_FRAME_ULONG ( axisTag ),
1995  FT_FRAME_LONG ( minValue ),
1996  FT_FRAME_LONG ( defaultValue ),
1997  FT_FRAME_LONG ( maxValue ),
1999  FT_FRAME_USHORT( nameID ),
2000  FT_FRAME_END
2001  };
2002 
2003 
2004  /* read the font data and set up the internal representation */
2005  /* if not already done */
2006 
2007  need_init = !face->blend;
2008 
2009  if ( need_init )
2010  {
2011  FT_TRACE2(( "FVAR " ));
2012 
2013  /* both `fvar' and `gvar' must be present */
2014  if ( FT_SET_ERROR( face->goto_table( face, TTAG_gvar,
2015  stream, &table_len ) ) )
2016  {
2017  /* CFF2 is an alternate to gvar here */
2018  if ( FT_SET_ERROR( face->goto_table( face, TTAG_CFF2,
2019  stream, &table_len ) ) )
2020  {
2021  FT_TRACE1(( "\n"
2022  "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" ));
2023  goto Exit;
2024  }
2025  }
2026 
2027  if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar,
2028  stream, &table_len ) ) )
2029  {
2030  FT_TRACE1(( "is missing\n" ));
2031  goto Exit;
2032  }
2033 
2034  fvar_start = FT_STREAM_POS( );
2035 
2036  /* the validity of the `fvar' header data was already checked */
2037  /* in function `sfnt_init_face' */
2038  if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
2039  goto Exit;
2040 
2041  usePsName = FT_BOOL( fvar_head.instanceSize ==
2042  6 + 4 * fvar_head.axisCount );
2043 
2044  FT_TRACE2(( "loaded\n" ));
2045 
2046  FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount ));
2047 
2048  if ( FT_NEW( face->blend ) )
2049  goto Exit;
2050 
2051  num_axes = fvar_head.axisCount;
2052  }
2053  else
2054  num_axes = face->blend->num_axis;
2055 
2056  /* `num_instances' holds the number of all named instances, */
2057  /* including the default instance which might be missing */
2058  /* in fvar's table of named instances */
2059  num_instances = (FT_UInt)face->root.style_flags >> 16;
2060 
2061  /* prepare storage area for MM data; this cannot overflow */
2062  /* 32-bit arithmetic because of the size limits used in the */
2063  /* `fvar' table validity check in `sfnt_init_face' */
2064 
2065  /* the various `*_size' variables, which we also use as */
2066  /* offsets into the `mmlen' array, must be multiples of the */
2067  /* pointer size (except the last one); without such an */
2068  /* alignment there might be runtime errors due to */
2069  /* misaligned addresses */
2070 #undef ALIGN_SIZE
2071 #define ALIGN_SIZE( n ) \
2072  ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) )
2073 
2074  mmvar_size = ALIGN_SIZE( sizeof ( FT_MM_Var ) );
2075  axis_flags_size = ALIGN_SIZE( num_axes *
2076  sizeof ( FT_UShort ) );
2077  axis_size = ALIGN_SIZE( num_axes *
2078  sizeof ( FT_Var_Axis ) );
2079  namedstyle_size = ALIGN_SIZE( num_instances *
2080  sizeof ( FT_Var_Named_Style ) );
2081  next_coords_size = ALIGN_SIZE( num_instances *
2082  num_axes *
2083  sizeof ( FT_Fixed ) );
2084  next_name_size = num_axes * 5;
2085 
2086  if ( need_init )
2087  {
2088  face->blend->mmvar_len = mmvar_size +
2089  axis_flags_size +
2090  axis_size +
2091  namedstyle_size +
2092  next_coords_size +
2093  next_name_size;
2094 
2095  if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2096  goto Exit;
2097  face->blend->mmvar = mmvar;
2098 
2099  /* set up pointers and offsets into the `mmvar' array; */
2100  /* the data gets filled in later on */
2101 
2102  mmvar->num_axis =
2103  num_axes;
2104  mmvar->num_designs =
2105  ~0U; /* meaningless in this context; each glyph */
2106  /* may have a different number of designs */
2107  /* (or tuples, as called by Apple) */
2108  mmvar->num_namedstyles =
2109  num_instances;
2110 
2111  /* alas, no public field in `FT_Var_Axis' for axis flags */
2112  axis_flags =
2113  (FT_UShort*)( (char*)mmvar + mmvar_size );
2114  mmvar->axis =
2115  (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2116  mmvar->namedstyle =
2117  (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size );
2118 
2119  next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2120  namedstyle_size );
2121  for ( i = 0; i < num_instances; i++ )
2122  {
2123  mmvar->namedstyle[i].coords = next_coords;
2124  next_coords += num_axes;
2125  }
2126 
2127  next_name = (FT_String*)( (char*)mmvar->namedstyle +
2128  namedstyle_size + next_coords_size );
2129  for ( i = 0; i < num_axes; i++ )
2130  {
2131  mmvar->axis[i].name = next_name;
2132  next_name += 5;
2133  }
2134 
2135  /* now fill in the data */
2136 
2137  if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
2138  goto Exit;
2139 
2140  a = mmvar->axis;
2141  for ( i = 0; i < num_axes; i++ )
2142  {
2143  GX_FVar_Axis axis_rec;
2144 
2145 
2146  if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
2147  goto Exit;
2148  a->tag = axis_rec.axisTag;
2149  a->minimum = axis_rec.minValue;
2150  a->def = axis_rec.defaultValue;
2151  a->maximum = axis_rec.maxValue;
2152  a->strid = axis_rec.nameID;
2153 
2154  a->name[0] = (FT_String)( a->tag >> 24 );
2155  a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
2156  a->name[2] = (FT_String)( ( a->tag >> 8 ) & 0xFF );
2157  a->name[3] = (FT_String)( ( a->tag ) & 0xFF );
2158  a->name[4] = '\0';
2159 
2160  *axis_flags = axis_rec.flags;
2161 
2162  if ( a->minimum > a->def ||
2163  a->def > a->maximum )
2164  {
2165  FT_TRACE2(( "TT_Get_MM_Var:"
2166  " invalid \"%s\" axis record; disabling\n",
2167  a->name ));
2168 
2169  a->minimum = a->def;
2170  a->maximum = a->def;
2171  }
2172 
2173  FT_TRACE5(( " \"%s\":"
2174  " minimum=%.5f, default=%.5f, maximum=%.5f,"
2175  " flags=0x%04X\n",
2176  a->name,
2177  a->minimum / 65536.0,
2178  a->def / 65536.0,
2179  a->maximum / 65536.0,
2180  *axis_flags ));
2181 
2182  a++;
2183  axis_flags++;
2184  }
2185 
2186  FT_TRACE5(( "\n" ));
2187 
2188  /* named instance coordinates are stored as design coordinates; */
2189  /* we have to convert them to normalized coordinates also */
2190  if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords,
2191  num_axes * num_instances ) )
2192  goto Exit;
2193 
2194  if ( fvar_head.instanceCount && !face->blend->avar_loaded )
2195  {
2197 
2198 
2199  ft_var_load_avar( face );
2200 
2201  if ( FT_STREAM_SEEK( offset ) )
2202  goto Exit;
2203  }
2204 
2205  ns = mmvar->namedstyle;
2206  nsc = face->blend->normalized_stylecoords;
2207  for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
2208  {
2209  /* PostScript names add 2 bytes to the instance record size */
2210  if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) +
2211  4L * num_axes ) )
2212  goto Exit;
2213 
2214  ns->strid = FT_GET_USHORT();
2215  (void) /* flags = */ FT_GET_USHORT();
2216 
2217  c = ns->coords;
2218  for ( j = 0; j < num_axes; j++, c++ )
2219  *c = FT_GET_LONG();
2220 
2221  /* valid psid values are 6, [256;32767], and 0xFFFF */
2222  if ( usePsName )
2223  ns->psid = FT_GET_USHORT();
2224  else
2225  ns->psid = 0xFFFF;
2226 
2227  ft_var_to_normalized( face, num_axes, ns->coords, nsc );
2228  nsc += num_axes;
2229 
2230  FT_FRAME_EXIT();
2231  }
2232 
2233  if ( num_instances != fvar_head.instanceCount )
2234  {
2236 
2237  FT_Int found, dummy1, dummy2;
2238  FT_UInt strid = ~0U;
2239 
2240 
2241  /* the default instance is missing in array the */
2242  /* of named instances; try to synthesize an entry */
2243  found = sfnt->get_name_id( face,
2245  &dummy1,
2246  &dummy2 );
2247  if ( found )
2249  else
2250  {
2251  found = sfnt->get_name_id( face,
2253  &dummy1,
2254  &dummy2 );
2255  if ( found )
2256  strid = TT_NAME_ID_FONT_SUBFAMILY;
2257  }
2258 
2259  if ( found )
2260  {
2261  found = sfnt->get_name_id( face,
2263  &dummy1,
2264  &dummy2 );
2265  if ( found )
2266  {
2267  FT_TRACE5(( "TT_Get_MM_Var:"
2268  " Adding default instance to named instances\n" ));
2269 
2270  ns = &mmvar->namedstyle[fvar_head.instanceCount];
2271 
2272  ns->strid = strid;
2273  ns->psid = TT_NAME_ID_PS_NAME;
2274 
2275  a = mmvar->axis;
2276  c = ns->coords;
2277  for ( j = 0; j < num_axes; j++, a++, c++ )
2278  *c = a->def;
2279  }
2280  }
2281  }
2282 
2283  ft_var_load_mvar( face );
2284  }
2285 
2286  /* fill the output array if requested */
2287 
2288  if ( master )
2289  {
2290  FT_UInt n;
2291 
2292 
2293  if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2294  goto Exit;
2295  FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
2296 
2297  axis_flags =
2298  (FT_UShort*)( (char*)mmvar + mmvar_size );
2299  mmvar->axis =
2300  (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2301  mmvar->namedstyle =
2302  (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size );
2303 
2304  next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2305  namedstyle_size );
2306  for ( n = 0; n < mmvar->num_namedstyles; n++ )
2307  {
2308  mmvar->namedstyle[n].coords = next_coords;
2309  next_coords += num_axes;
2310  }
2311 
2312  a = mmvar->axis;
2313  next_name = (FT_String*)( (char*)mmvar->namedstyle +
2314  namedstyle_size + next_coords_size );
2315  for ( n = 0; n < num_axes; n++ )
2316  {
2317  a->name = next_name;
2318 
2319  /* standard PostScript names for some standard apple tags */
2320  if ( a->tag == TTAG_wght )
2321  a->name = (char*)"Weight";
2322  else if ( a->tag == TTAG_wdth )
2323  a->name = (char*)"Width";
2324  else if ( a->tag == TTAG_opsz )
2325  a->name = (char*)"OpticalSize";
2326  else if ( a->tag == TTAG_slnt )
2327  a->name = (char*)"Slant";
2328 
2329  next_name += 5;
2330  a++;
2331  }
2332 
2333  *master = mmvar;
2334  }
2335 
2336  Exit:
2337  return error;
2338  }
2339 
2340 
2341  static FT_Error
2342  tt_set_mm_blend( TT_Face face,
2343  FT_UInt num_coords,
2344  FT_Fixed* coords,
2345  FT_Bool set_design_coords )
2346  {
2348  GX_Blend blend;
2349  FT_MM_Var* mmvar;
2350  FT_UInt i;
2351 
2352  FT_Bool all_design_coords = FALSE;
2353 
2354  FT_Memory memory = face->root.memory;
2355 
2356  enum
2357  {
2358  mcvt_retain,
2359  mcvt_modify,
2360  mcvt_load
2361 
2362  } manageCvt;
2363 
2364 
2365  face->doblend = FALSE;
2366 
2367  if ( !face->blend )
2368  {
2369  if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2370  goto Exit;
2371  }
2372 
2373  blend = face->blend;
2374  mmvar = blend->mmvar;
2375 
2376  if ( num_coords > mmvar->num_axis )
2377  {
2378  FT_TRACE2(( "TT_Set_MM_Blend:"
2379  " only using first %d of %d coordinates\n",
2380  mmvar->num_axis, num_coords ));
2381  num_coords = mmvar->num_axis;
2382  }
2383 
2384  FT_TRACE5(( "normalized design coordinates:\n" ));
2385 
2386  for ( i = 0; i < num_coords; i++ )
2387  {
2388  FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 ));
2389  if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
2390  {
2391  FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n"
2392  " is out of range [-1;1]\n",
2393  coords[i] / 65536.0 ));
2394  error = FT_THROW( Invalid_Argument );
2395  goto Exit;
2396  }
2397  }
2398 
2399  FT_TRACE5(( "\n" ));
2400 
2401  if ( !face->is_cff2 && !blend->glyphoffsets )
2402  if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) )
2403  goto Exit;
2404 
2405  if ( !blend->coords )
2406  {
2407  if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2408  goto Exit;
2409 
2410  /* the first time we have to compute all design coordinates */
2411  all_design_coords = TRUE;
2412  }
2413 
2414  if ( !blend->normalizedcoords )
2415  {
2416  if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) )
2417  goto Exit;
2418 
2419  manageCvt = mcvt_modify;
2420 
2421  /* If we have not set the blend coordinates before this, then the */
2422  /* cvt table will still be what we read from the `cvt ' table and */
2423  /* we don't need to reload it. We may need to change it though... */
2424  }
2425  else
2426  {
2427  FT_Bool have_diff = 0;
2428  FT_UInt j;
2429  FT_Fixed* c;
2430  FT_Fixed* n;
2431 
2432 
2433  manageCvt = mcvt_retain;
2434 
2435  for ( i = 0; i < num_coords; i++ )
2436  {
2437  if ( blend->normalizedcoords[i] != coords[i] )
2438  {
2439  manageCvt = mcvt_load;
2440  have_diff = 1;
2441  break;
2442  }
2443  }
2444 
2445  if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2446  {
2447  FT_UInt idx = (FT_UInt)face->root.face_index >> 16;
2448 
2449 
2450  c = blend->normalizedcoords + i;
2451  n = blend->normalized_stylecoords + idx * mmvar->num_axis + i;
2452  for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
2453  if ( *c != *n )
2454  have_diff = 1;
2455  }
2456  else
2457  {
2458  c = blend->normalizedcoords + i;
2459  for ( j = i; j < mmvar->num_axis; j++, c++ )
2460  if ( *c != 0 )
2461  have_diff = 1;
2462  }
2463 
2464  /* return value -1 indicates `no change' */
2465  if ( !have_diff )
2466  return -1;
2467 
2468  for ( ; i < mmvar->num_axis; i++ )
2469  {
2470  if ( blend->normalizedcoords[i] != 0 )
2471  {
2472  manageCvt = mcvt_load;
2473  break;
2474  }
2475  }
2476 
2477  /* If we don't change the blend coords then we don't need to do */
2478  /* anything to the cvt table. It will be correct. Otherwise we */
2479  /* no longer have the original cvt (it was modified when we set */
2480  /* the blend last time), so we must reload and then modify it. */
2481  }
2482 
2483  blend->num_axis = mmvar->num_axis;
2484  FT_MEM_COPY( blend->normalizedcoords,
2485  coords,
2486  num_coords * sizeof ( FT_Fixed ) );
2487 
2488  if ( set_design_coords )
2489  ft_var_to_design( face,
2490  all_design_coords ? blend->num_axis : num_coords,
2491  blend->normalizedcoords,
2492  blend->coords );
2493 
2494  face->doblend = TRUE;
2495 
2496  if ( face->cvt )
2497  {
2498  switch ( manageCvt )
2499  {
2500  case mcvt_load:
2501  /* The cvt table has been loaded already; every time we change the */
2502  /* blend we may need to reload and remodify the cvt table. */
2503  FT_FREE( face->cvt );
2504  face->cvt = NULL;
2505 
2506  error = tt_face_load_cvt( face, face->root.stream );
2507  break;
2508 
2509  case mcvt_modify:
2510  /* The original cvt table is in memory. All we need to do is */
2511  /* apply the `cvar' table (if any). */
2512  error = tt_face_vary_cvt( face, face->root.stream );
2513  break;
2514 
2515  case mcvt_retain:
2516  /* The cvt table is correct for this set of coordinates. */
2517  break;
2518  }
2519  }
2520 
2521  /* enforce recomputation of the PostScript name; */
2522  FT_FREE( face->postscript_name );
2523  face->postscript_name = NULL;
2524 
2525  Exit:
2526  return error;
2527  }
2528 
2529 
2530  /*************************************************************************/
2531  /* */
2532  /* <Function> */
2533  /* TT_Set_MM_Blend */
2534  /* */
2535  /* <Description> */
2536  /* Set the blend (normalized) coordinates for this instance of the */
2537  /* font. Check that the `gvar' table is reasonable and does some */
2538  /* initial preparation. */
2539  /* */
2540  /* <InOut> */
2541  /* face :: The font. */
2542  /* Initialize the blend structure with `gvar' data. */
2543  /* */
2544  /* <Input> */
2545  /* num_coords :: The number of available coordinates. If it is */
2546  /* larger than the number of axes, ignore the excess */
2547  /* values. If it is smaller than the number of axes, */
2548  /* use the default value (0) for the remaining axes. */
2549  /* */
2550  /* coords :: An array of `num_coords', each between [-1,1]. */
2551  /* */
2552  /* <Return> */
2553  /* FreeType error code. 0 means success. */
2554  /* */
2556  TT_Set_MM_Blend( TT_Face face,
2557  FT_UInt num_coords,
2558  FT_Fixed* coords )
2559  {
2560  FT_Error error;
2561 
2562 
2563  error = tt_set_mm_blend( face, num_coords, coords, 1 );
2564  if ( error )
2565  return error;
2566 
2567  if ( num_coords )
2568  face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2569  else
2570  face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2571 
2572  return FT_Err_Ok;
2573  }
2574 
2575 
2576  /*************************************************************************/
2577  /* */
2578  /* <Function> */
2579  /* TT_Get_MM_Blend */
2580  /* */
2581  /* <Description> */
2582  /* Get the blend (normalized) coordinates for this instance of the */
2583  /* font. */
2584  /* */
2585  /* <InOut> */
2586  /* face :: The font. */
2587  /* Initialize the blend structure with `gvar' data. */
2588  /* */
2589  /* <Input> */
2590  /* num_coords :: The number of available coordinates. If it is */
2591  /* larger than the number of axes, set the excess */
2592  /* values to 0. */
2593  /* */
2594  /* coords :: An array of `num_coords', each between [-1,1]. */
2595  /* */
2596  /* <Return> */
2597  /* FreeType error code. 0 means success. */
2598  /* */
2600  TT_Get_MM_Blend( TT_Face face,
2601  FT_UInt num_coords,
2602  FT_Fixed* coords )
2603  {
2605  GX_Blend blend;
2606  FT_UInt i, nc;
2607 
2608 
2609  if ( !face->blend )
2610  {
2611  if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2612  return error;
2613  }
2614 
2615  blend = face->blend;
2616 
2617  if ( !blend->coords )
2618  {
2619  /* select default instance coordinates */
2620  /* if no instance is selected yet */
2621  if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2622  return error;
2623  }
2624 
2625  nc = num_coords;
2626  if ( num_coords > blend->num_axis )
2627  {
2628  FT_TRACE2(( "TT_Get_MM_Blend:"
2629  " only using first %d of %d coordinates\n",
2630  blend->num_axis, num_coords ));
2631  nc = blend->num_axis;
2632  }
2633 
2634  if ( face->doblend )
2635  {
2636  for ( i = 0; i < nc; i++ )
2637  coords[i] = blend->normalizedcoords[i];
2638  }
2639  else
2640  {
2641  for ( i = 0; i < nc; i++ )
2642  coords[i] = 0;
2643  }
2644 
2645  for ( ; i < num_coords; i++ )
2646  coords[i] = 0;
2647 
2648  return FT_Err_Ok;
2649  }
2650 
2651 
2652  /*************************************************************************/
2653  /* */
2654  /* <Function> */
2655  /* TT_Set_Var_Design */
2656  /* */
2657  /* <Description> */
2658  /* Set the coordinates for the instance, measured in the user */
2659  /* coordinate system. Parse the `avar' table (if present) to convert */
2660  /* from user to normalized coordinates. */
2661  /* */
2662  /* <InOut> */
2663  /* face :: The font face. */
2664  /* Initialize the blend struct with `gvar' data. */
2665  /* */
2666  /* <Input> */
2667  /* num_coords :: The number of available coordinates. If it is */
2668  /* larger than the number of axes, ignore the excess */
2669  /* values. If it is smaller than the number of axes, */
2670  /* use the default values for the remaining axes. */
2671  /* */
2672  /* coords :: A coordinate array with `num_coords' elements. */
2673  /* */
2674  /* <Return> */
2675  /* FreeType error code. 0 means success. */
2676  /* */
2678  TT_Set_Var_Design( TT_Face face,
2679  FT_UInt num_coords,
2680  FT_Fixed* coords )
2681  {
2683  GX_Blend blend;
2684  FT_MM_Var* mmvar;
2685  FT_UInt i;
2686  FT_Memory memory = face->root.memory;
2687 
2688  FT_Fixed* c;
2689  FT_Fixed* n;
2691 
2692  FT_Bool have_diff = 0;
2693 
2694 
2695  if ( !face->blend )
2696  {
2697  if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2698  goto Exit;
2699  }
2700 
2701  blend = face->blend;
2702  mmvar = blend->mmvar;
2703 
2704  if ( num_coords > mmvar->num_axis )
2705  {
2706  FT_TRACE2(( "TT_Set_Var_Design:"
2707  " only using first %d of %d coordinates\n",
2708  mmvar->num_axis, num_coords ));
2709  num_coords = mmvar->num_axis;
2710  }
2711 
2712  if ( !blend->coords )
2713  {
2714  if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2715  goto Exit;
2716  }
2717 
2718  c = blend->coords;
2719  n = coords;
2720  for ( i = 0; i < num_coords; i++, n++, c++ )
2721  {
2722  if ( *c != *n )
2723  {
2724  *c = *n;
2725  have_diff = 1;
2726  }
2727  }
2728 
2729  if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2730  {
2731  FT_UInt instance_index;
2732  FT_Var_Named_Style* named_style;
2733 
2734 
2735  instance_index = (FT_UInt)face->root.face_index >> 16;
2736  named_style = mmvar->namedstyle + instance_index - 1;
2737 
2738  n = named_style->coords + num_coords;
2739  for ( ; i < mmvar->num_axis; i++, n++, c++ )
2740  {
2741  if ( *c != *n )
2742  {
2743  *c = *n;
2744  have_diff = 1;
2745  }
2746  }
2747  }
2748  else
2749  {
2750  FT_Var_Axis* a;
2751 
2752 
2753  a = mmvar->axis + num_coords;
2754  for ( ; i < mmvar->num_axis; i++, a++, c++ )
2755  {
2756  if ( *c != a->def )
2757  {
2758  *c = a->def;
2759  have_diff = 1;
2760  }
2761  }
2762  }
2763 
2764  /* return value -1 indicates `no change' */
2765  if ( !have_diff )
2766  return -1;
2767 
2768  if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
2769  goto Exit;
2770 
2771  if ( !face->blend->avar_loaded )
2772  ft_var_load_avar( face );
2773 
2774  ft_var_to_normalized( face, num_coords, blend->coords, normalized );
2775 
2776  error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 );
2777  if ( error )
2778  goto Exit;
2779 
2780  if ( num_coords )
2781  face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2782  else
2783  face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2784 
2785  Exit:
2786  FT_FREE( normalized );
2787  return error;
2788  }
2789 
2790 
2791  /*************************************************************************/
2792  /* */
2793  /* <Function> */
2794  /* TT_Get_Var_Design */
2795  /* */
2796  /* <Description> */
2797  /* Get the design coordinates of the currently selected interpolated */
2798  /* font. */
2799  /* */
2800  /* <Input> */
2801  /* face :: A handle to the source face. */
2802  /* */
2803  /* num_coords :: The number of design coordinates to retrieve. If it */
2804  /* is larger than the number of axes, set the excess */
2805  /* values to~0. */
2806  /* */
2807  /* <Output> */
2808  /* coords :: The design coordinates array. */
2809  /* */
2810  /* <Return> */
2811  /* FreeType error code. 0~means success. */
2812  /* */
2814  TT_Get_Var_Design( TT_Face face,
2815  FT_UInt num_coords,
2816  FT_Fixed* coords )
2817  {
2819  GX_Blend blend;
2820  FT_UInt i, nc;
2821 
2822 
2823  if ( !face->blend )
2824  {
2825  if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2826  return error;
2827  }
2828 
2829  blend = face->blend;
2830 
2831  if ( !blend->coords )
2832  {
2833  /* select default instance coordinates */
2834  /* if no instance is selected yet */
2835  if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2836  return error;
2837  }
2838 
2839  nc = num_coords;
2840  if ( num_coords > blend->num_axis )
2841  {
2842  FT_TRACE2(( "TT_Get_Var_Design:"
2843  " only using first %d of %d coordinates\n",
2844  blend->num_axis, num_coords ));
2845  nc = blend->num_axis;
2846  }
2847 
2848  if ( face->doblend )
2849  {
2850  for ( i = 0; i < nc; i++ )
2851  coords[i] = blend->coords[i];
2852  }
2853  else
2854  {
2855  for ( i = 0; i < nc; i++ )
2856  coords[i] = 0;
2857  }
2858 
2859  for ( ; i < num_coords; i++ )
2860  coords[i] = 0;
2861 
2862  return FT_Err_Ok;
2863  }
2864 
2865 
2866  /*************************************************************************/
2867  /* */
2868  /* <Function> */
2869  /* TT_Set_Named_Instance */
2870  /* */
2871  /* <Description> */
2872  /* Set the given named instance, also resetting any further */
2873  /* variation. */
2874  /* */
2875  /* <Input> */
2876  /* face :: A handle to the source face. */
2877  /* */
2878  /* instance_index :: The instance index, starting with value 1. */
2879  /* Value 0 indicates to not use an instance. */
2880  /* */
2881  /* <Return> */
2882  /* FreeType error code. 0~means success. */
2883  /* */
2885  TT_Set_Named_Instance( TT_Face face,
2886  FT_UInt instance_index )
2887  {
2888  FT_Error error = FT_ERR( Invalid_Argument );
2889  GX_Blend blend;
2890  FT_MM_Var* mmvar;
2891 
2892  FT_UInt num_instances;
2893 
2894 
2895  if ( !face->blend )
2896  {
2897  if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2898  goto Exit;
2899  }
2900 
2901  blend = face->blend;
2902  mmvar = blend->mmvar;
2903 
2904  num_instances = (FT_UInt)face->root.style_flags >> 16;
2905 
2906  /* `instance_index' starts with value 1, thus `>' */
2907  if ( instance_index > num_instances )
2908  goto Exit;
2909 
2910  if ( instance_index > 0 && mmvar->namedstyle )
2911  {
2912  FT_Memory memory = face->root.memory;
2914 
2915  FT_Var_Named_Style* named_style;
2916  FT_String* style_name;
2917 
2918 
2919  named_style = mmvar->namedstyle + instance_index - 1;
2920 
2921  error = sfnt->get_name( face,
2922  (FT_UShort)named_style->strid,
2923  &style_name );
2924  if ( error )
2925  goto Exit;
2926 
2927  /* set (or replace) style name */
2928  FT_FREE( face->root.style_name );
2929  face->root.style_name = style_name;
2930 
2931  /* finally, select the named instance */
2932  error = TT_Set_Var_Design( face,
2933  mmvar->num_axis,
2934  named_style->coords );
2935  if ( error )
2936  goto Exit;
2937  }
2938  else
2939  error = TT_Set_Var_Design( face, 0, NULL );
2940 
2941  face->root.face_index = ( instance_index << 16 ) |
2942  ( face->root.face_index & 0xFFFFL );
2943  face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2944 
2945  Exit:
2946  return error;
2947  }
2948 
2949 
2950  /*************************************************************************/
2951  /*************************************************************************/
2952  /***** *****/
2953  /***** GX VAR PARSING ROUTINES *****/
2954  /***** *****/
2955  /*************************************************************************/
2956  /*************************************************************************/
2957 
2958 
2959  /*************************************************************************/
2960  /* */
2961  /* <Function> */
2962  /* tt_face_vary_cvt */
2963  /* */
2964  /* <Description> */
2965  /* Modify the loaded cvt table according to the `cvar' table and the */
2966  /* font's blend. */
2967  /* */
2968  /* <InOut> */
2969  /* face :: A handle to the target face object. */
2970  /* */
2971  /* <Input> */
2972  /* stream :: A handle to the input stream. */
2973  /* */
2974  /* <Return> */
2975  /* FreeType error code. 0 means success. */
2976  /* */
2977  /* Most errors are ignored. It is perfectly valid not to have a */
2978  /* `cvar' table even if there is a `gvar' and `fvar' table. */
2979  /* */
2981  tt_face_vary_cvt( TT_Face face,
2982  FT_Stream stream )
2983  {
2984  FT_Error error;
2985  FT_Memory memory = stream->memory;
2986  FT_ULong table_start;
2987  FT_ULong table_len;
2988  FT_UInt tupleCount;
2989  FT_ULong offsetToData;
2990  FT_ULong here;
2991  FT_UInt i, j;
2992  FT_Fixed* tuple_coords = NULL;
2993  FT_Fixed* im_start_coords = NULL;
2994  FT_Fixed* im_end_coords = NULL;
2995  GX_Blend blend = face->blend;
2996  FT_UInt point_count, spoint_count = 0;
2997  FT_UShort* sharedpoints = NULL;
2998  FT_UShort* localpoints = NULL;
2999  FT_UShort* points;
3000  FT_Short* deltas;
3001 
3002 
3003  FT_TRACE2(( "CVAR " ));
3004 
3005  if ( !blend )
3006  {
3007  FT_TRACE2(( "\n"
3008  "tt_face_vary_cvt: no blend specified\n" ));
3009  error = FT_Err_Ok;
3010  goto Exit;
3011  }
3012 
3013  if ( !face->cvt )
3014  {
3015  FT_TRACE2(( "\n"
3016  "tt_face_vary_cvt: no `cvt ' table\n" ));
3017  error = FT_Err_Ok;
3018  goto Exit;
3019  }
3020 
3021  error = face->goto_table( face, TTAG_cvar, stream, &table_len );
3022  if ( error )
3023  {
3024  FT_TRACE2(( "is missing\n" ));
3025 
3026  error = FT_Err_Ok;
3027  goto Exit;
3028  }
3029 
3030  if ( FT_FRAME_ENTER( table_len ) )
3031  {
3032  error = FT_Err_Ok;
3033  goto Exit;
3034  }
3035 
3036  table_start = FT_Stream_FTell( stream );
3037  if ( FT_GET_LONG() != 0x00010000L )
3038  {
3039  FT_TRACE2(( "bad table version\n" ));
3040 
3041  error = FT_Err_Ok;
3042  goto FExit;
3043  }
3044 
3045  FT_TRACE2(( "loaded\n" ));
3046 
3047  if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) ||
3048  FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3049  FT_NEW_ARRAY( im_end_coords, blend->num_axis ) )
3050  goto FExit;
3051 
3052  tupleCount = FT_GET_USHORT();
3053  offsetToData = FT_GET_USHORT();
3054 
3055  /* rough sanity test */
3056  if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3057  table_len )
3058  {
3059  FT_TRACE2(( "tt_face_vary_cvt:"
3060  " invalid CVT variation array header\n" ));
3061 
3062  error = FT_THROW( Invalid_Table );
3063  goto FExit;
3064  }
3065 
3066  offsetToData += table_start;
3067 
3068  if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3069  {
3070  here = FT_Stream_FTell( stream );
3071 
3072  FT_Stream_SeekSet( stream, offsetToData );
3073 
3074  sharedpoints = ft_var_readpackedpoints( stream,
3075  table_len,
3076  &spoint_count );
3077  offsetToData = FT_Stream_FTell( stream );
3078 
3079  FT_Stream_SeekSet( stream, here );
3080  }
3081 
3082  FT_TRACE5(( "cvar: there %s %d tuple%s:\n",
3083  ( tupleCount & 0xFFF ) == 1 ? "is" : "are",
3084  tupleCount & 0xFFF,
3085  ( tupleCount & 0xFFF ) == 1 ? "" : "s" ));
3086 
3087  for ( i = 0; i < ( tupleCount & 0xFFF ); i++ )
3088  {
3089  FT_UInt tupleDataSize;
3090  FT_UInt tupleIndex;
3091  FT_Fixed apply;
3092 
3093 
3094  FT_TRACE6(( " tuple %d:\n", i ));
3095 
3096  tupleDataSize = FT_GET_USHORT();
3097  tupleIndex = FT_GET_USHORT();
3098 
3099  if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3100  {
3101  for ( j = 0; j < blend->num_axis; j++ )
3102  tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */
3103  /* short frac to fixed */
3104  }
3105  else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3106  {
3107  FT_TRACE2(( "tt_face_vary_cvt:"
3108  " invalid tuple index\n" ));
3109 
3110  error = FT_THROW( Invalid_Table );
3111  goto Exit;
3112  }
3113  else
3114  FT_MEM_COPY(
3115  tuple_coords,
3116  &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
3117  blend->num_axis * sizeof ( FT_Fixed ) );
3118 
3119  if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3120  {
3121  for ( j = 0; j < blend->num_axis; j++ )
3122  im_start_coords[j] = FT_GET_SHORT() * 4;
3123  for ( j = 0; j < blend->num_axis; j++ )
3124  im_end_coords[j] = FT_GET_SHORT() * 4;
3125  }
3126 
3127  apply = ft_var_apply_tuple( blend,
3128  (FT_UShort)tupleIndex,
3129  tuple_coords,
3130  im_start_coords,
3131  im_end_coords );
3132 
3133  if ( apply == 0 ) /* tuple isn't active for our blend */
3134  {
3135  offsetToData += tupleDataSize;
3136  continue;
3137  }
3138 
3139  here = FT_Stream_FTell( stream );
3140 
3141  FT_Stream_SeekSet( stream, offsetToData );
3142 
3143  if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3144  {
3145  localpoints = ft_var_readpackedpoints( stream,
3146  table_len,
3147  &point_count );
3148  points = localpoints;
3149  }
3150  else
3151  {
3152  points = sharedpoints;
3153  point_count = spoint_count;
3154  }
3155 
3156  deltas = ft_var_readpackeddeltas( stream,
3157  table_len,
3158  point_count == 0 ? face->cvt_size
3159  : point_count );
3160 
3161  if ( !points ||
3162  !deltas ||
3163  ( localpoints == ALL_POINTS && point_count != face->cvt_size ) )
3164  ; /* failure, ignore it */
3165 
3166  else if ( localpoints == ALL_POINTS )
3167  {
3168 #ifdef FT_DEBUG_LEVEL_TRACE
3169  int count = 0;
3170 #endif
3171 
3172 
3173  FT_TRACE7(( " CVT deltas:\n" ));
3174 
3175  /* this means that there are deltas for every entry in cvt */
3176  for ( j = 0; j < face->cvt_size; j++ )
3177  {
3178  FT_Long orig_cvt = face->cvt[j];
3179 
3180 
3181  face->cvt[j] = (FT_Short)( orig_cvt +
3182  FT_MulFix( deltas[j], apply ) );
3183 
3184 #ifdef FT_DEBUG_LEVEL_TRACE
3185  if ( orig_cvt != face->cvt[j] )
3186  {
3187  FT_TRACE7(( " %d: %d -> %d\n",
3188  j, orig_cvt, face->cvt[j] ));
3189  count++;
3190  }
3191 #endif
3192  }
3193 
3194 #ifdef FT_DEBUG_LEVEL_TRACE
3195  if ( !count )
3196  FT_TRACE7(( " none\n" ));
3197 #endif
3198  }
3199 
3200  else
3201  {
3202 #ifdef FT_DEBUG_LEVEL_TRACE
3203  int count = 0;
3204 #endif
3205 
3206 
3207  FT_TRACE7(( " CVT deltas:\n" ));
3208 
3209  for ( j = 0; j < point_count; j++ )
3210  {
3211  int pindex;
3212  FT_Long orig_cvt;
3213 
3214 
3215  pindex = points[j];
3216  if ( (FT_ULong)pindex >= face->cvt_size )
3217  continue;
3218 
3219  orig_cvt = face->cvt[pindex];
3220  face->cvt[pindex] = (FT_Short)( orig_cvt +
3221  FT_MulFix( deltas[j], apply ) );
3222 
3223 #ifdef FT_DEBUG_LEVEL_TRACE
3224  if ( orig_cvt != face->cvt[pindex] )
3225  {
3226  FT_TRACE7(( " %d: %d -> %d\n",
3227  pindex, orig_cvt, face->cvt[pindex] ));
3228  count++;
3229  }
3230 #endif
3231  }
3232 
3233 #ifdef FT_DEBUG_LEVEL_TRACE
3234  if ( !count )
3235  FT_TRACE7(( " none\n" ));
3236 #endif
3237  }
3238 
3239  if ( localpoints != ALL_POINTS )
3240  FT_FREE( localpoints );
3241  FT_FREE( deltas );
3242 
3243  offsetToData += tupleDataSize;
3244 
3245  FT_Stream_SeekSet( stream, here );
3246  }
3247 
3248  FT_TRACE5(( "\n" ));
3249 
3250  FExit:
3251  FT_FRAME_EXIT();
3252 
3253  Exit:
3254  if ( sharedpoints != ALL_POINTS )
3255  FT_FREE( sharedpoints );
3256  FT_FREE( tuple_coords );
3257  FT_FREE( im_start_coords );
3258  FT_FREE( im_end_coords );
3259 
3260  return error;
3261  }
3262 
3263 
3264  /* Shift the original coordinates of all points between indices `p1' */
3265  /* and `p2', using the same difference as given by index `ref'. */
3266 
3267  /* modeled after `af_iup_shift' */
3268 
3269  static void
3270  tt_delta_shift( int p1,
3271  int p2,
3272  int ref,
3273  FT_Vector* in_points,
3274  FT_Vector* out_points )
3275  {
3276  int p;
3277  FT_Vector delta;
3278 
3279 
3280  delta.x = out_points[ref].x - in_points[ref].x;
3281  delta.y = out_points[ref].y - in_points[ref].y;
3282 
3283  if ( delta.x == 0 && delta.y == 0 )
3284  return;
3285 
3286  for ( p = p1; p < ref; p++ )
3287  {
3288  out_points[p].x += delta.x;
3289  out_points[p].y += delta.y;
3290  }
3291 
3292  for ( p = ref + 1; p <= p2; p++ )
3293  {
3294  out_points[p].x += delta.x;
3295  out_points[p].y += delta.y;
3296  }
3297  }
3298 
3299 
3300  /* Interpolate the original coordinates of all points with indices */
3301  /* between `p1' and `p2', using `ref1' and `ref2' as the reference */
3302  /* point indices. */
3303 
3304  /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */
3305  /* `Ins_IUP' */
3306 
3307  static void
3308  tt_delta_interpolate( int p1,
3309  int p2,
3310  int ref1,
3311  int ref2,
3312  FT_Vector* in_points,
3313  FT_Vector* out_points )
3314  {
3315  int p, i;
3316 
3317  FT_Pos out, in1, in2, out1, out2, d1, d2;
3318 
3319 
3320  if ( p1 > p2 )
3321  return;
3322 
3323  /* handle both horizontal and vertical coordinates */
3324  for ( i = 0; i <= 1; i++ )
3325  {
3326  /* shift array pointers so that we can access `foo.y' as `foo.x' */
3327  in_points = (FT_Vector*)( (FT_Pos*)in_points + i );
3328  out_points = (FT_Vector*)( (FT_Pos*)out_points + i );
3329 
3330  if ( in_points[ref1].x > in_points[ref2].x )
3331  {
3332  p = ref1;
3333  ref1 = ref2;
3334  ref2 = p;
3335  }
3336 
3337  in1 = in_points[ref1].x;
3338  in2 = in_points[ref2].x;
3339  out1 = out_points[ref1].x;
3340  out2 = out_points[ref2].x;
3341  d1 = out1 - in1;
3342  d2 = out2 - in2;
3343 
3344  /* If the reference points have the same coordinate but different */
3345  /* delta, inferred delta is zero. Otherwise interpolate. */
3346  if ( in1 != in2 || out1 == out2 )
3347  {
3348  FT_Fixed scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 )
3349  : 0;
3350 
3351 
3352  for ( p = p1; p <= p2; p++ )
3353  {
3354  out = in_points[p].x;
3355 
3356  if ( out <= in1 )
3357  out += d1;
3358  else if ( out >= in2 )
3359  out += d2;
3360  else
3361  out = out1 + FT_MulFix( out - in1, scale );
3362 
3363  out_points[p].x = out;
3364  }
3365  }
3366  }
3367  }
3368 
3369 
3370  /* Interpolate points without delta values, similar to */
3371  /* the `IUP' hinting instruction. */
3372 
3373  /* modeled after `Ins_IUP */
3374 
3375  static void
3376  tt_interpolate_deltas( FT_Outline* outline,
3377  FT_Vector* out_points,
3378  FT_Vector* in_points,
3379  FT_Bool* has_delta )
3380  {
3381  FT_Int first_point;
3382  FT_Int end_point;
3383 
3384  FT_Int first_delta;
3385  FT_Int cur_delta;
3386 
3387  FT_Int point;
3388  FT_Short contour;
3389 
3390 
3391  /* ignore empty outlines */
3392  if ( !outline->n_contours )
3393  return;
3394 
3395  contour = 0;
3396  point = 0;
3397 
3398  do
3399  {
3400  end_point = outline->contours[contour];
3401  first_point = point;
3402 
3403  /* search first point that has a delta */
3404  while ( point <= end_point && !has_delta[point] )
3405  point++;
3406 
3407  if ( point <= end_point )
3408  {
3409  first_delta = point;
3410  cur_delta = point;
3411 
3412  point++;
3413 
3414  while ( point <= end_point )
3415  {
3416  /* search next point that has a delta */
3417  /* and interpolate intermediate points */
3418  if ( has_delta[point] )
3419  {
3420  tt_delta_interpolate( cur_delta + 1,
3421  point - 1,
3422  cur_delta,
3423  point,
3424  in_points,
3425  out_points );
3426  cur_delta = point;
3427  }
3428 
3429  point++;
3430  }
3431 
3432  /* shift contour if we only have a single delta */
3433  if ( cur_delta == first_delta )
3434  tt_delta_shift( first_point,
3435  end_point,
3436  cur_delta,
3437  in_points,
3438  out_points );
3439  else
3440  {
3441  /* otherwise handle remaining points */
3442  /* at the end and beginning of the contour */
3443  tt_delta_interpolate( cur_delta + 1,
3444  end_point,
3445  cur_delta,
3446  first_delta,
3447  in_points,
3448  out_points );
3449 
3450  if ( first_delta > 0 )
3451  tt_delta_interpolate( first_point,
3452  first_delta - 1,
3453  cur_delta,
3454  first_delta,
3455  in_points,
3456  out_points );
3457  }
3458  }
3459  contour++;
3460 
3461  } while ( contour < outline->n_contours );
3462  }
3463 
3464 
3465  /*************************************************************************/
3466  /* */
3467  /* <Function> */
3468  /* TT_Vary_Apply_Glyph_Deltas */
3469  /* */
3470  /* <Description> */
3471  /* Apply the appropriate deltas to the current glyph. */
3472  /* */
3473  /* <Input> */
3474  /* face :: A handle to the target face object. */
3475  /* */
3476  /* glyph_index :: The index of the glyph being modified. */
3477  /* */
3478  /* n_points :: The number of the points in the glyph, including */
3479  /* phantom points. */
3480  /* */
3481  /* <InOut> */
3482  /* outline :: The outline to change. */
3483  /* */
3484  /* <Return> */
3485  /* FreeType error code. 0 means success. */
3486  /* */
3488  TT_Vary_Apply_Glyph_Deltas( TT_Face face,
3489  FT_UInt glyph_index,
3491  FT_UInt n_points )
3492  {
3493  FT_Stream stream = face->root.stream;
3494  FT_Memory memory = stream->memory;
3495  GX_Blend blend = face->blend;
3496 
3497  FT_Vector* points_org = NULL;
3498  FT_Vector* points_out = NULL;
3499  FT_Bool* has_delta = NULL;
3500 
3501  FT_Error error;
3502  FT_ULong glyph_start;
3503  FT_UInt tupleCount;
3504  FT_ULong offsetToData;
3505  FT_ULong here;
3506  FT_UInt i, j;
3507  FT_Fixed* tuple_coords = NULL;
3508  FT_Fixed* im_start_coords = NULL;
3509  FT_Fixed* im_end_coords = NULL;
3510  FT_UInt point_count, spoint_count = 0;
3511  FT_UShort* sharedpoints = NULL;
3512  FT_UShort* localpoints = NULL;
3513  FT_UShort* points;
3514  FT_Short *deltas_x, *deltas_y;
3515 
3516 
3517  if ( !face->doblend || !blend )
3518  return FT_THROW( Invalid_Argument );
3519 
3520  if ( glyph_index >= blend->gv_glyphcnt ||
3521  blend->glyphoffsets[glyph_index] ==
3522  blend->glyphoffsets[glyph_index + 1] )
3523  {
3524  FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3525  " no variation data for this glyph\n" ));
3526  return FT_Err_Ok;
3527  }
3528 
3529  if ( FT_NEW_ARRAY( points_org, n_points ) ||
3530  FT_NEW_ARRAY( points_out, n_points ) ||
3531  FT_NEW_ARRAY( has_delta, n_points ) )
3532  goto Fail1;
3533 
3534  if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) ||
3535  FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
3536  blend->glyphoffsets[glyph_index] ) )
3537  goto Fail1;
3538 
3539  glyph_start = FT_Stream_FTell( stream );
3540 
3541  /* each set of glyph variation data is formatted similarly to `cvar' */
3542 
3543  if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) ||
3544  FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3545  FT_NEW_ARRAY( im_end_coords, blend->num_axis ) )
3546  goto Fail2;
3547 
3548  tupleCount = FT_GET_USHORT();
3549  offsetToData = FT_GET_USHORT();
3550 
3551  /* rough sanity test */
3552  if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3553  blend->gvar_size )
3554  {
3555  FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3556  " invalid glyph variation array header\n" ));
3557 
3558  error = FT_THROW( Invalid_Table );
3559  goto Fail2;
3560  }
3561 
3562  offsetToData += glyph_start;
3563 
3564  if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3565  {
3566  here = FT_Stream_FTell( stream );
3567 
3568  FT_Stream_SeekSet( stream, offsetToData );
3569 
3570  sharedpoints = ft_var_readpackedpoints( stream,
3571  blend->gvar_size,
3572  &spoint_count );
3573  offsetToData = FT_Stream_FTell( stream );
3574 
3575  FT_Stream_SeekSet( stream, here );
3576  }
3577 
3578  FT_TRACE5(( "gvar: there %s %d tuple%s:\n",
3579  ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3580  tupleCount & GX_TC_TUPLE_COUNT_MASK,
3581  ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
3582 
3583  for ( j = 0; j < n_points; j++ )
3584  points_org[j] = outline->points[j];
3585 
3586  for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
3587  {
3588  FT_UInt tupleDataSize;
3589  FT_UInt tupleIndex;
3590  FT_Fixed apply;
3591 
3592 
3593  FT_TRACE6(( " tuple %d:\n", i ));
3594 
3595  tupleDataSize = FT_GET_USHORT();
3596  tupleIndex = FT_GET_USHORT();
3597 
3598  if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3599  {
3600  for ( j = 0; j < blend->num_axis; j++ )
3601  tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */
3602  /* short frac to fixed */
3603  }
3604  else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3605  {
3606  FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3607  " invalid tuple index\n" ));
3608 
3609  error = FT_THROW( Invalid_Table );
3610  goto Fail2;
3611  }
3612  else
3613  FT_MEM_COPY(
3614  tuple_coords,
3615  &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
3616  blend->num_axis * sizeof ( FT_Fixed ) );
3617 
3618  if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3619  {
3620  for ( j = 0; j < blend->num_axis; j++ )
3621  im_start_coords[j] = FT_GET_SHORT() * 4;
3622  for ( j = 0; j < blend->num_axis; j++ )
3623  im_end_coords[j] = FT_GET_SHORT() * 4;
3624  }
3625 
3626  apply = ft_var_apply_tuple( blend,
3627  (FT_UShort)tupleIndex,
3628  tuple_coords,
3629  im_start_coords,
3630  im_end_coords );
3631 
3632  if ( apply == 0 ) /* tuple isn't active for our blend */
3633  {
3634  offsetToData += tupleDataSize;
3635  continue;
3636  }
3637 
3638  here = FT_Stream_FTell( stream );
3639 
3640  FT_Stream_SeekSet( stream, offsetToData );
3641 
3642  if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3643  {
3644  localpoints = ft_var_readpackedpoints( stream,
3645  blend->gvar_size,
3646  &point_count );
3647  points = localpoints;
3648  }
3649  else
3650  {
3651  points = sharedpoints;
3652  point_count = spoint_count;
3653  }
3654 
3655  deltas_x = ft_var_readpackeddeltas( stream,
3656  blend->gvar_size,
3657  point_count == 0 ? n_points
3658  : point_count );
3659  deltas_y = ft_var_readpackeddeltas( stream,
3660  blend->gvar_size,
3661  point_count == 0 ? n_points
3662  : point_count );
3663 
3664  if ( !points || !deltas_y || !deltas_x )
3665  ; /* failure, ignore it */
3666 
3667  else if ( points == ALL_POINTS )
3668  {
3669 #ifdef FT_DEBUG_LEVEL_TRACE
3670  int count = 0;
3671 #endif
3672 
3673 
3674  FT_TRACE7(( " point deltas:\n" ));
3675 
3676  /* this means that there are deltas for every point in the glyph */
3677  for ( j = 0; j < n_points; j++ )
3678  {
3679  FT_Pos delta_x = FT_MulFix( deltas_x[j], apply );
3680  FT_Pos delta_y = FT_MulFix( deltas_y[j], apply );
3681 
3682 
3683  if ( j < n_points - 4 )
3684  {
3685  outline->points[j].x += delta_x;
3686  outline->points[j].y += delta_y;
3687  }
3688  else
3689  {
3690  /* To avoid double adjustment of advance width or height, */
3691  /* adjust phantom points only if there is no HVAR or VVAR */
3692  /* support, respectively. */
3693  if ( j == ( n_points - 4 ) &&
3694  !( face->variation_support &
3696  outline->points[j].x += delta_x;
3697 
3698  else if ( j == ( n_points - 3 ) &&
3699  !( face->variation_support &
3701  outline->points[j].x += delta_x;
3702 
3703  else if ( j == ( n_points - 2 ) &&
3704  !( face->variation_support &
3706  outline->points[j].y += delta_y;
3707 
3708  else if ( j == ( n_points - 1 ) &&
3709  !( face->variation_support &
3711  outline->points[j].y += delta_y;
3712  }
3713 
3714 #ifdef FT_DEBUG_LEVEL_TRACE
3715  if ( delta_x || delta_y )
3716  {
3717  FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n",
3718  j,
3719  outline->points[j].x - delta_x,
3720  outline->points[j].y - delta_y,
3721  outline->points[j].x,
3722  outline->points[j].y ));
3723  count++;
3724  }
3725 #endif
3726  }
3727 
3728 #ifdef FT_DEBUG_LEVEL_TRACE
3729  if ( !count )
3730  FT_TRACE7(( " none\n" ));
3731 #endif
3732  }
3733 
3734  else
3735  {
3736 #ifdef FT_DEBUG_LEVEL_TRACE
3737  int count = 0;
3738 #endif
3739 
3740 
3741  /* we have to interpolate the missing deltas similar to the */
3742  /* IUP bytecode instruction */
3743  for ( j = 0; j < n_points; j++ )
3744  {
3745  has_delta[j] = FALSE;
3746  points_out[j] = points_org[j];
3747  }
3748 
3749  for ( j = 0; j < point_count; j++ )
3750  {
3751  FT_UShort idx = points[j];
3752 
3753 
3754  if ( idx >= n_points )
3755  continue;
3756 
3757  has_delta[idx] = TRUE;
3758 
3759  points_out[idx].x += FT_MulFix( deltas_x[j], apply );
3760  points_out[idx].y += FT_MulFix( deltas_y[j], apply );
3761  }
3762 
3763  /* no need to handle phantom points here, */
3764  /* since solitary points can't be interpolated */
3765  tt_interpolate_deltas( outline,
3766  points_out,
3767  points_org,
3768  has_delta );
3769 
3770  FT_TRACE7(( " point deltas:\n" ));
3771 
3772  for ( j = 0; j < n_points; j++ )
3773  {
3774  FT_Pos delta_x = points_out[j].x - points_org[j].x;
3775  FT_Pos delta_y = points_out[j].y - points_org[j].y;
3776 
3777 
3778  if ( j < n_points - 4 )
3779  {
3780  outline->points[j].x += delta_x;
3781  outline->points[j].y += delta_y;
3782  }
3783  else
3784  {
3785  /* To avoid double adjustment of advance width or height, */
3786  /* adjust phantom points only if there is no HVAR or VVAR */
3787  /* support, respectively. */
3788  if ( j == ( n_points - 4 ) &&
3789  !( face->variation_support &
3791  outline->points[j].x += delta_x;
3792 
3793  else if ( j == ( n_points - 3 ) &&
3794  !( face->variation_support &
3796  outline->points[j].x += delta_x;
3797 
3798  else if ( j == ( n_points - 2 ) &&
3799  !( face->variation_support &
3801  outline->points[j].y += delta_y;
3802 
3803  else if ( j == ( n_points - 1 ) &&
3804  !( face->variation_support &
3806  outline->points[j].y += delta_y;
3807  }
3808 
3809 #ifdef FT_DEBUG_LEVEL_TRACE
3810  if ( delta_x || delta_y )
3811  {
3812  FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n",
3813  j,
3814  outline->points[j].x - delta_x,
3815  outline->points[j].y - delta_y,
3816  outline->points[j].x,
3817  outline->points[j].y ));
3818  count++;
3819  }
3820 #endif
3821  }
3822 
3823 #ifdef FT_DEBUG_LEVEL_TRACE
3824  if ( !count )
3825  FT_TRACE7(( " none\n" ));
3826 #endif
3827  }
3828 
3829  if ( localpoints != ALL_POINTS )
3830  FT_FREE( localpoints );
3831  FT_FREE( deltas_x );
3832  FT_FREE( deltas_y );
3833 
3834  offsetToData += tupleDataSize;
3835 
3836  FT_Stream_SeekSet( stream, here );
3837  }
3838 
3839  FT_TRACE5(( "\n" ));
3840 
3841  Fail2:
3842  if ( sharedpoints != ALL_POINTS )
3843  FT_FREE( sharedpoints );
3844  FT_FREE( tuple_coords );
3845  FT_FREE( im_start_coords );
3846  FT_FREE( im_end_coords );
3847 
3848  FT_FRAME_EXIT();
3849 
3850  Fail1:
3851  FT_FREE( points_org );
3852  FT_FREE( points_out );
3853  FT_FREE( has_delta );
3854 
3855  return error;
3856  }
3857 
3858 
3859  /*************************************************************************/
3860  /* */
3861  /* <Function> */
3862  /* tt_get_var_blend */
3863  /* */
3864  /* <Description> */
3865  /* An extended internal version of `TT_Get_MM_Blend' that returns */
3866  /* pointers instead of copying data, without any initialization of */
3867  /* the MM machinery in case it isn't loaded yet. */
3868  /* */
3870  tt_get_var_blend( TT_Face face,
3871  FT_UInt *num_coords,
3872  FT_Fixed* *coords,
3873  FT_Fixed* *normalizedcoords,
3874  FT_MM_Var* *mm_var )
3875  {
3876  if ( face->blend )
3877  {
3878  if ( num_coords )
3879  *num_coords = face->blend->num_axis;
3880  if ( coords )
3881  *coords = face->blend->coords;
3882  if ( normalizedcoords )
3883  *normalizedcoords = face->blend->normalizedcoords;
3884  if ( mm_var )
3885  *mm_var = face->blend->mmvar;
3886  }
3887  else
3888  {
3889  if ( num_coords )
3890  *num_coords = 0;
3891  if ( coords )
3892  *coords = NULL;
3893  if ( mm_var )
3894  *mm_var = NULL;
3895  }
3896 
3897  return FT_Err_Ok;
3898  }
3899 
3900 
3901  static void
3902  ft_var_done_item_variation_store( TT_Face face,
3903  GX_ItemVarStore itemStore )
3904  {
3906  FT_UInt i;
3907 
3908 
3909  if ( itemStore->varData )
3910  {
3911  for ( i = 0; i < itemStore->dataCount; i++ )
3912  {
3913  FT_FREE( itemStore->varData[i].regionIndices );
3914  FT_FREE( itemStore->varData[i].deltaSet );
3915  }
3916 
3917  FT_FREE( itemStore->varData );
3918  }
3919 
3920  if ( itemStore->varRegionList )
3921  {
3922  for ( i = 0; i < itemStore->regionCount; i++ )
3923  FT_FREE( itemStore->varRegionList[i].axisList );
3924 
3925  FT_FREE( itemStore->varRegionList );
3926  }
3927  }
3928 
3929 
3930  /*************************************************************************/
3931  /* */
3932  /* <Function> */
3933  /* tt_done_blend */
3934  /* */
3935  /* <Description> */
3936  /* Free the blend internal data structure. */
3937  /* */
3938  FT_LOCAL_DEF( void )
3939  tt_done_blend( TT_Face face )
3940  {
3942  GX_Blend blend = face->blend;
3943 
3944 
3945  if ( blend )
3946  {
3947  FT_UInt i, num_axes;
3948 
3949 
3950  /* blend->num_axis might not be set up yet */
3951  num_axes = blend->mmvar->num_axis;
3952 
3953  FT_FREE( blend->coords );
3954  FT_FREE( blend->normalizedcoords );
3955  FT_FREE( blend->normalized_stylecoords );
3956  FT_FREE( blend->mmvar );
3957 
3958  if ( blend->avar_segment )
3959  {
3960  for ( i = 0; i < num_axes; i++ )
3961  FT_FREE( blend->avar_segment[i].correspondence );
3962  FT_FREE( blend->avar_segment );
3963  }
3964 
3965  if ( blend->hvar_table )
3966  {
3967  ft_var_done_item_variation_store( face,
3968  &blend->hvar_table->itemStore );
3969 
3970  FT_FREE( blend->hvar_table->widthMap.innerIndex );
3971  FT_FREE( blend->hvar_table->widthMap.outerIndex );
3972  FT_FREE( blend->hvar_table );
3973  }
3974 
3975  if ( blend->vvar_table )
3976  {
3977  ft_var_done_item_variation_store( face,
3978  &blend->vvar_table->itemStore );
3979 
3980  FT_FREE( blend->vvar_table->widthMap.innerIndex );
3981  FT_FREE( blend->vvar_table->widthMap.outerIndex );
3982  FT_FREE( blend->vvar_table );
3983  }
3984 
3985  if ( blend->mvar_table )
3986  {
3987  ft_var_done_item_variation_store( face,
3988  &blend->mvar_table->itemStore );
3989 
3990  FT_FREE( blend->mvar_table->values );
3991  FT_FREE( blend->mvar_table );
3992  }
3993 
3994  FT_FREE( blend->tuplecoords );
3995  FT_FREE( blend->glyphoffsets );
3996  FT_FREE( blend );
3997  }
3998  }
3999 
4000 #else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4001 
4002  /* ANSI C doesn't like empty source files */
4003  typedef int _tt_gxvar_dummy;
4004 
4005 #endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4006 
4007 
4008 /* END */
struct TT_SizeRec_ * TT_Size
Definition: tttypes.h:1631
#define FT_ALLOC(ptr, size)
Definition: ftmemory.h:303
int FT_Error
Definition: fttypes.h:300
#define FT_GET_BYTE()
Definition: ftstream.h:287
FT_DivFix(FT_Long a, FT_Long b)
Definition: ftcalc.c:608
#define FT_FRAME_LONG(f)
Definition: ftstream.h:120
#define TT_FACE_FLAG_VAR_HADVANCE
Definition: tttypes.h:1113
FT_Pos y
Definition: ftimage.h:77
signed long FT_Long
Definition: fttypes.h:242
#define TRUE
Definition: types.h:120
SFNT_Interface * SFNT_Service
Definition: sfnt.h:628
unsigned long FT_ULong
Definition: fttypes.h:253
FT_BEGIN_HEADER typedef signed long FT_Pos
Definition: ftimage.h:58
struct png_info_def **typedef void(__cdecl typeof(png_destroy_read_struct))(struct png_struct_def **
Definition: typeof.h:49
#define error(str)
Definition: mkdosfs.c:1605
ActualNumberDriverObjects * sizeof(PDRIVER_OBJECT)) PDRIVER_OBJECT *DriverObjectList
#define ALIGN_SIZE
Definition: recv.cpp:83
#define FT_FACE_FLAG_VARIATION
Definition: freetype.h:1253
FT_Pos x
Definition: ftimage.h:76
signed int FT_Int
Definition: fttypes.h:220
SFNT_Service sfnt
Definition: ttdriver.c:206
GLuint GLuint GLsizei count
Definition: gl.h:1545
#define TTAG_HVAR
Definition: tttags.h:65
GLenum GLenum GLenum GLenum GLenum scale
Definition: glext.h:9032
const GLint * first
Definition: glext.h:5794
#define U(x)
Definition: wordpad.c:45
FT_UInt strid
Definition: ftmm.h:187
GLintptr offset
Definition: glext.h:5920
GLdouble n
Definition: glext.h:7729
#define FT_GET_CHAR()
Definition: ftstream.h:286
#define TTAG_gvar
Definition: tttags.h:64
#define FT_MIN(a, b)
Definition: ftobjs.h:71
GLenum GLsizei GLenum GLenum const GLvoid * table
Definition: glext.h:5644
GLint GLint GLint GLint GLint x
Definition: gl.h:1548
signed char FT_Char
Definition: fttypes.h:143
#define FT_READ_BYTE(var)
Definition: ftstream.h:306
GLuint GLuint end
Definition: gl.h:1545
GLuint coords
Definition: glext.h:7368
FT_String * name
Definition: ftmm.h:151
int _tt_gxvar_dummy
Definition: ttgxvar.c:4003
return FT_Err_Ok
Definition: ftbbox.c:511
FT_UInt num_namedstyles
Definition: ftmm.h:241
static char memory[1024 *256]
Definition: process.c:116
#define FT_READ_USHORT(var)
Definition: ftstream.h:309
Definition: send.c:48
POINTL point
Definition: edittest.c:50
GLvoid * data
Definition: dlist.c:359
GLint GLint GLsizei GLsizei GLsizei GLint GLenum format
Definition: gl.h:1546
GLint limit
Definition: glext.h:10326
GLuint coord
Definition: glext.h:9511
FT_BEGIN_HEADER typedef unsigned char FT_Bool
Definition: fttypes.h:108
tt_face_load_cvt(TT_Face face, FT_Stream stream)
Definition: ttpload.c:318
#define TT_FACE_FLAG_VAR_TSB
Definition: tttypes.h:1119
#define FT_TRACE1(varformat)
Definition: ftdebug.h:158
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint i
Definition: glfuncs.h:248
unsigned char FT_Byte
Definition: fttypes.h:154
#define FT_THROW(e)
Definition: ftdebug.h:213
#define a
Definition: ke_i.h:78
GLenum GLint ref
Definition: glext.h:6028
FT_Fixed * coords
Definition: ftmm.h:186
#define TT_FACE_FLAG_VAR_VADVANCE
Definition: tttypes.h:1118
FT_List_Iterate(FT_List list, FT_List_Iterator iterator, void *user)
Definition: ftutil.c:381
unsigned int idx
Definition: utils.c:41
smooth NULL
Definition: ftsmooth.c:416
#define FT_STREAM_READ_FIELDS(fields, object)
Definition: ftstream.h:508
static const WCHAR version[]
Definition: asmname.c:66
#define TT_NAME_ID_PS_NAME
Definition: ttnameid.h:799
#define FT_GET_USHORT()
Definition: ftstream.h:289
#define FT_FREE(ptr)
Definition: ftmemory.h:329
#define TTAG_VVAR
Definition: tttags.h:106
#define FT_FRAME_END
Definition: ftstream.h:118
#define FT_STREAM_SKIP(distance)
Definition: ftstream.h:493
TT_Get_Name_Func get_name
Definition: sfnt.h:621
#define FT_FRAME_USHORT(f)
Definition: ftstream.h:123
#define FT_LOCAL_DEF(x)
Definition: ftconfig.h:388
#define TTAG_CFF2
Definition: tttags.h:46
GLsizei GLenum const GLvoid GLsizei GLenum GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLint GLint GLint GLshort GLshort GLshort GLubyte GLubyte GLubyte GLuint GLuint GLuint GLushort GLushort GLushort GLbyte GLbyte GLbyte GLbyte GLdouble GLdouble GLdouble GLdouble GLfloat GLfloat GLfloat GLfloat GLint GLint GLint GLint GLshort GLshort GLshort GLshort GLubyte GLubyte GLubyte GLubyte GLuint GLuint GLuint GLuint GLushort GLushort GLushort GLushort GLboolean const GLdouble const GLfloat const GLint const GLshort const GLbyte const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLdouble const GLfloat const GLfloat const GLint const GLint const GLshort const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort const GLdouble const GLfloat const GLint const GLshort GLenum GLenum GLenum GLfloat GLenum GLint GLenum GLenum GLenum GLfloat GLenum GLenum GLint GLenum GLfloat GLenum GLint GLint GLushort GLenum GLenum GLfloat GLenum GLenum GLint GLfloat const GLubyte GLenum GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLint GLint GLsizei GLsizei GLint GLenum GLenum const GLvoid GLenum GLenum const GLfloat GLenum GLenum const GLint GLenum GLenum const GLdouble GLenum GLenum const GLfloat GLenum GLenum const GLint GLsizei GLuint GLfloat GLuint GLbitfield GLfloat GLint GLuint GLboolean GLenum GLfloat GLenum GLbitfield GLenum GLfloat GLfloat GLint GLint const GLfloat GLenum GLfloat GLfloat GLint GLint GLfloat GLfloat GLint GLint const GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat GLint GLfloat GLfloat const GLdouble const GLfloat const GLdouble const GLfloat GLint GLint GLint j
Definition: glfuncs.h:250
#define TTAG_avar
Definition: tttags.h:36
FT_MulDiv(FT_Long a, FT_Long b, FT_Long c)
Definition: ftcalc.c:416
#define FT_ERR(e)
Definition: fttypes.h:586
#define FT_IS_NAMED_INSTANCE(face)
Definition: freetype.h:1424
#define FT_READ_ULONG(var)
Definition: ftstream.h:313
#define TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY
Definition: ttnameid.h:812
FT_Var_Named_Style * namedstyle
Definition: ftmm.h:243
char FT_String
Definition: fttypes.h:187
GLsizeiptr size
Definition: glext.h:5919
if(!(yy_init))
Definition: macro.lex.yy.c:714
static void Exit(void)
Definition: sock.c:1331
#define FT_MAX(a, b)
Definition: ftobjs.h:72
#define FT_TRACE2(varformat)
Definition: ftdebug.h:159
#define TTAG_cvar
Definition: tttags.h:49
#define TTAG_fvar
Definition: tttags.h:58
const GLubyte * c
Definition: glext.h:8905
static FILE * out
Definition: regtests2xml.c:44
#define for
Definition: utility.h:88
#define FT_TRACE7(varformat)
Definition: ftdebug.h:164
#define FT_fdot14ToFixed(x)
Definition: cffload.c:1117
Definition: _map.h:44
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: gl.h:1950
GLbitfield flags
Definition: glext.h:7161
#define TTAG_MVAR
Definition: tttags.h:84
GLsizei const GLfloat * points
Definition: glext.h:8112
#define TT_NAME_ID_FONT_SUBFAMILY
Definition: font.c:3537
Definition: mxnamespace.c:44
static const WCHAR L[]
Definition: oid.c:1250
Definition: parse.h:22
signed short FT_Short
Definition: fttypes.h:198
#define FT_READ_CHAR(var)
Definition: ftstream.h:307
typedefFT_BEGIN_HEADER struct FT_MemoryRec_ * FT_Memory
Definition: ftsystem.h:66
#define FT_BOOL(x)
Definition: fttypes.h:578
#define FT_FRAME_EXIT()
Definition: ftstream.h:517
#define FT_NEW_ARRAY(ptr, count)
Definition: ftmemory.h:333
GLsizei const GLfloat * value
Definition: glext.h:6069
Definition: mesh.c:5329
#define FT_STREAM_SEEK(position)
Definition: ftstream.h:489
FT_MulFix(FT_Long a, FT_Long b)
Definition: ftcalc.c:509
FT_Var_Axis * axis
Definition: ftmm.h:242
#define FT_FACE_MEMORY(x)
Definition: ftobjs.h:636
#define FT_TRACE6(varformat)
Definition: ftdebug.h:163
#define FT_STREAM_POS()
Definition: ftstream.h:486
TT_Get_Name_ID_Func get_name_id
Definition: sfnt.h:622
signed long FT_Fixed
Definition: fttypes.h:288
#define FT_FRAME_ULONG(f)
Definition: ftstream.h:121
#define FT_FACE(x)
Definition: ftobjs.h:630
#define FT_SET_ERROR(expression)
Definition: ftmemory.h:42
#define FT_FIXED_ONE
Definition: cffload.c:38
unsigned int FT_UInt
Definition: fttypes.h:231
GLuint start
Definition: gl.h:1545
#define FT_TRACE5(varformat)
Definition: ftdebug.h:162
#define FT_GET_SHORT()
Definition: ftstream.h:288
FT_UInt num_axis
Definition: ftmm.h:239
tt_size_reset(TT_Size size, FT_Bool only_height)
Definition: ttobjs.c:1251
#define TT_FACE_FLAG_VAR_LSB
Definition: tttypes.h:1114
#define FT_GET_ULONG()
Definition: ftstream.h:293
#define FT_FRAME_ENTER(size)
Definition: ftstream.h:512
#define c
Definition: ke_i.h:80
FT_UInt num_designs
Definition: ftmm.h:240
#define FT_NEW(ptr)
Definition: ftmemory.h:331
#define FT_MEM_COPY(dest, source, count)
Definition: ftmemory.h:228
#define FT_FRAME_SKIP_SHORT
Definition: ftstream.h:137
#define FT_READ_SHORT(var)
Definition: ftstream.h:308
#define FT_GET_LONG()
Definition: ftstream.h:292
GLboolean GLboolean GLboolean GLboolean a
Definition: glext.h:6204
GLfloat GLfloat p
Definition: glext.h:8902
unsigned short FT_UShort
Definition: fttypes.h:209
#define FT_FACE_STREAM(x)
Definition: ftobjs.h:637
#define TT_FACE_FLAG_VAR_MVAR
Definition: tttypes.h:1124
#define FT_UNUSED(arg)
Definition: ftconfig.h:101
int k
Definition: mpi.c:3369
void user(int argc, const char *argv[])
Definition: cmds.c:1350
#define FT_FRAME_START(size)
Definition: ftstream.h:117
GLenum GLuint GLint GLenum face
Definition: glext.h:7025
size_t FT_Offset
Definition: fttypes.h:324
GLint GLenum GLboolean normalized
Definition: glext.h:6117
FT_Fixed def
Definition: ftmm.h:154
Definition: dlist.c:348