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