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