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