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