blob: bbd2640483a5b32a03d7a6e92620eb0ed1b57728 [file] [log] [blame]
Werner Lemberg9ac90602018-06-03 09:01:17 +02001/****************************************************************************
2 *
3 * ttgxvar.c
4 *
5 * TrueType GX Font Variation loader
6 *
Werner Lembergb6e8a712021-01-17 07:18:48 +01007 * Copyright (C) 2004-2021 by
Werner Lemberg9ac90602018-06-03 09:01:17 +02008 * 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 */
Werner Lemberg44bb3032004-04-25 20:15:11 +000017
18
Werner Lemberg9ac90602018-06-03 09:01:17 +020019 /**************************************************************************
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 */
Werner Lemberg44bb3032004-04-25 20:15:11 +000040
41
42#include <ft2build.h>
David Turnere1339132020-06-08 13:31:55 +020043#include <freetype/internal/ftdebug.h>
Werner Lemberg44bb3032004-04-25 20:15:11 +000044#include FT_CONFIG_CONFIG_H
David Turnere1339132020-06-08 13:31:55 +020045#include <freetype/internal/ftstream.h>
46#include <freetype/internal/sfnt.h>
47#include <freetype/tttags.h>
48#include <freetype/ttnameid.h>
49#include <freetype/ftmm.h>
50#include <freetype/ftlist.h>
Werner Lemberg44bb3032004-04-25 20:15:11 +000051
Werner Lemberg44bb3032004-04-25 20:15:11 +000052#include "ttpload.h"
53#include "ttgxvar.h"
54
55#include "tterrors.h"
56
57
58#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
59
60
Werner Lemberg328b7922015-03-04 08:40:23 +010061#define FT_Stream_FTell( stream ) \
Werner Lembergbadf3172013-06-06 09:16:38 +020062 (FT_ULong)( (stream)->cursor - (stream)->base )
Werner Lemberg4261e492017-07-05 23:00:23 +020063#define FT_Stream_SeekSet( stream, off ) \
64 (stream)->cursor = \
65 ( (off) < (FT_ULong)( (stream)->limit - (stream)->base ) ) \
66 ? (stream)->base + (off) \
67 : (stream)->limit
Werner Lemberg44bb3032004-04-25 20:15:11 +000068
69
Werner Lemberga632fb52018-06-24 15:22:10 +020070 /* some macros we need */
Werner Lemberg11782272019-05-11 09:29:19 +020071#define FT_fdot14ToFixed( x ) \
72 ( (FT_Fixed)( (FT_ULong)(x) << 2 ) )
73#define FT_intToFixed( i ) \
74 ( (FT_Fixed)( (FT_ULong)(i) << 16 ) )
Werner Lemberg49079ce2019-05-29 08:08:53 +020075#define FT_fdot6ToFixed( i ) \
Werner Lemberg37580052019-05-16 12:15:54 +020076 ( (FT_Fixed)( (FT_ULong)(i) << 10 ) )
Werner Lemberg49079ce2019-05-29 08:08:53 +020077#define FT_fixedToInt( x ) \
78 ( (FT_Short)( ( (x) + 0x8000U ) >> 16 ) )
79#define FT_fixedToFdot6( x ) \
80 ( (FT_Pos)( ( (x) + 0x200 ) >> 10 ) )
Werner Lemberga632fb52018-06-24 15:22:10 +020081
82
Werner Lemberg9ac90602018-06-03 09:01:17 +020083 /**************************************************************************
84 *
85 * The macro FT_COMPONENT is used in trace mode. It is an implicit
86 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
87 * messages during execution.
88 */
Werner Lemberg44bb3032004-04-25 20:15:11 +000089#undef FT_COMPONENT
Werner Lemberga0dd16f2018-08-15 18:13:17 +020090#define FT_COMPONENT ttgxvar
Werner Lemberg44bb3032004-04-25 20:15:11 +000091
92
93 /*************************************************************************/
94 /*************************************************************************/
95 /***** *****/
96 /***** Internal Routines *****/
97 /***** *****/
98 /*************************************************************************/
99 /*************************************************************************/
100
101
Werner Lemberg9ac90602018-06-03 09:01:17 +0200102 /**************************************************************************
103 *
104 * The macro ALL_POINTS is used in `ft_var_readpackedpoints'. It
105 * indicates that there is a delta for every point without needing to
106 * enumerate all of them.
107 */
Werner Lembergbadf3172013-06-06 09:16:38 +0200108
109 /* ensure that value `0' has the same width as a pointer */
110#define ALL_POINTS (FT_UShort*)~(FT_PtrDist)0
Werner Lemberg44bb3032004-04-25 20:15:11 +0000111
112
Werner Lemberg328b7922015-03-04 08:40:23 +0100113#define GX_PT_POINTS_ARE_WORDS 0x80U
114#define GX_PT_POINT_RUN_COUNT_MASK 0x7FU
Werner Lemberg44bb3032004-04-25 20:15:11 +0000115
116
Werner Lemberg9ac90602018-06-03 09:01:17 +0200117 /**************************************************************************
118 *
119 * @Function:
120 * ft_var_readpackedpoints
121 *
122 * @Description:
123 * Read a set of points to which the following deltas will apply.
124 * Points are packed with a run length encoding.
125 *
126 * @Input:
127 * stream ::
128 * The data stream.
129 *
130 * size ::
131 * The size of the table holding the data.
132 *
133 * @Output:
134 * point_cnt ::
135 * The number of points read. A zero value means that
136 * all points in the glyph will be affected, without
137 * enumerating them individually.
138 *
139 * @Return:
140 * An array of FT_UShort containing the affected points or the
141 * special value ALL_POINTS.
142 */
Werner Lemberg44bb3032004-04-25 20:15:11 +0000143 static FT_UShort*
144 ft_var_readpackedpoints( FT_Stream stream,
Werner Lemberg43a96eb2015-10-13 11:18:55 +0200145 FT_ULong size,
Werner Lemberg44bb3032004-04-25 20:15:11 +0000146 FT_UInt *point_cnt )
147 {
suzuki toshiyae62c8762011-06-15 02:48:33 +0900148 FT_UShort *points = NULL;
Werner Lemberg328b7922015-03-04 08:40:23 +0100149 FT_UInt n;
150 FT_UInt runcnt;
151 FT_UInt i, j;
152 FT_UShort first;
Werner Lemberg44bb3032004-04-25 20:15:11 +0000153 FT_Memory memory = stream->memory;
Werner Lemberge3c93012013-03-14 11:21:17 +0100154 FT_Error error = FT_Err_Ok;
Werner Lembergf814f682005-05-22 20:33:09 +0000155
156 FT_UNUSED( error );
Werner Lemberg44bb3032004-04-25 20:15:11 +0000157
158
Werner Lemberg328b7922015-03-04 08:40:23 +0100159 *point_cnt = 0;
160
161 n = FT_GET_BYTE();
Werner Lemberg44bb3032004-04-25 20:15:11 +0000162 if ( n == 0 )
163 return ALL_POINTS;
164
165 if ( n & GX_PT_POINTS_ARE_WORDS )
Werner Lemberg328b7922015-03-04 08:40:23 +0100166 {
167 n &= GX_PT_POINT_RUN_COUNT_MASK;
168 n <<= 8;
169 n |= FT_GET_BYTE();
170 }
Werner Lemberg44bb3032004-04-25 20:15:11 +0000171
Werner Lemberg43a96eb2015-10-13 11:18:55 +0200172 if ( n > size )
Werner Lembergda346732015-10-10 10:21:27 +0200173 {
174 FT_TRACE1(( "ft_var_readpackedpoints: number of points too large\n" ));
175 return NULL;
176 }
177
Werner Lemberge69f34b2016-07-19 07:06:19 +0200178 /* in the nested loops below we increase `i' twice; */
Werner Lemberg7f631052016-07-19 21:35:58 +0200179 /* it is faster to simply allocate one more slot */
Werner Lemberge69f34b2016-07-19 07:06:19 +0200180 /* than to add another test within the loop */
Alexei Podtelezhnikovcb9e7b72021-11-06 22:59:31 -0400181 if ( FT_QNEW_ARRAY( points, n + 1 ) )
Werner Lemberg44bb3032004-04-25 20:15:11 +0000182 return NULL;
183
Werner Lemberg328b7922015-03-04 08:40:23 +0100184 *point_cnt = n;
185
Werner Lembergf147fb22016-07-16 07:06:21 +0200186 first = 0;
Werner Lemberge69f34b2016-07-19 07:06:19 +0200187 i = 0;
Werner Lemberg44bb3032004-04-25 20:15:11 +0000188 while ( i < n )
189 {
190 runcnt = FT_GET_BYTE();
191 if ( runcnt & GX_PT_POINTS_ARE_WORDS )
192 {
Werner Lemberg328b7922015-03-04 08:40:23 +0100193 runcnt &= GX_PT_POINT_RUN_COUNT_MASK;
Werner Lembergf147fb22016-07-16 07:06:21 +0200194 first += FT_GET_USHORT();
Werner Lemberg328b7922015-03-04 08:40:23 +0100195 points[i++] = first;
Werner Lemberg44bb3032004-04-25 20:15:11 +0000196
Werner Lemberg328b7922015-03-04 08:40:23 +0100197 /* first point not included in run count */
198 for ( j = 0; j < runcnt; j++ )
199 {
200 first += FT_GET_USHORT();
201 points[i++] = first;
Werner Lemberg7f631052016-07-19 21:35:58 +0200202 if ( i >= n )
Behdad Esfahbod69446dd2016-07-16 10:52:38 +0200203 break;
Werner Lemberg328b7922015-03-04 08:40:23 +0100204 }
Werner Lemberg44bb3032004-04-25 20:15:11 +0000205 }
206 else
207 {
Werner Lembergf147fb22016-07-16 07:06:21 +0200208 first += FT_GET_BYTE();
Werner Lemberg328b7922015-03-04 08:40:23 +0100209 points[i++] = first;
Werner Lemberg44bb3032004-04-25 20:15:11 +0000210
Werner Lemberg328b7922015-03-04 08:40:23 +0100211 for ( j = 0; j < runcnt; j++ )
212 {
213 first += FT_GET_BYTE();
214 points[i++] = first;
Werner Lemberg7f631052016-07-19 21:35:58 +0200215 if ( i >= n )
Behdad Esfahbod69446dd2016-07-16 10:52:38 +0200216 break;
Werner Lemberg328b7922015-03-04 08:40:23 +0100217 }
Werner Lemberg44bb3032004-04-25 20:15:11 +0000218 }
219 }
220
221 return points;
222 }
223
224
Werner Lemberg328b7922015-03-04 08:40:23 +0100225#define GX_DT_DELTAS_ARE_ZERO 0x80U
226#define GX_DT_DELTAS_ARE_WORDS 0x40U
227#define GX_DT_DELTA_RUN_COUNT_MASK 0x3FU
Werner Lemberg44bb3032004-04-25 20:15:11 +0000228
229
Werner Lemberg9ac90602018-06-03 09:01:17 +0200230 /**************************************************************************
231 *
232 * @Function:
233 * ft_var_readpackeddeltas
234 *
235 * @Description:
236 * Read a set of deltas. These are packed slightly differently than
237 * points. In particular there is no overall count.
238 *
239 * @Input:
240 * stream ::
241 * The data stream.
242 *
243 * size ::
244 * The size of the table holding the data.
245 *
246 * delta_cnt ::
247 * The number of deltas to be read.
248 *
249 * @Return:
Werner Lemberga632fb52018-06-24 15:22:10 +0200250 * An array of FT_Fixed containing the deltas for the affected
Werner Lemberg9ac90602018-06-03 09:01:17 +0200251 * points. (This only gets the deltas for one dimension. It will
252 * generally be called twice, once for x, once for y. When used in
253 * cvt table, it will only be called once.)
Werner Lemberga632fb52018-06-24 15:22:10 +0200254 *
255 * We use FT_Fixed to avoid accumulation errors while summing up all
256 * deltas (the rounding to integer values happens as the very last
257 * step).
Werner Lemberg9ac90602018-06-03 09:01:17 +0200258 */
Werner Lemberga632fb52018-06-24 15:22:10 +0200259 static FT_Fixed*
Werner Lemberg44bb3032004-04-25 20:15:11 +0000260 ft_var_readpackeddeltas( FT_Stream stream,
Werner Lemberg43a96eb2015-10-13 11:18:55 +0200261 FT_ULong size,
Werner Lemberg328b7922015-03-04 08:40:23 +0100262 FT_UInt delta_cnt )
Werner Lemberg44bb3032004-04-25 20:15:11 +0000263 {
Werner Lemberga632fb52018-06-24 15:22:10 +0200264 FT_Fixed *deltas = NULL;
Werner Lemberg328b7922015-03-04 08:40:23 +0100265 FT_UInt runcnt, cnt;
266 FT_UInt i, j;
Jany Belluzfc552912021-11-04 11:07:43 +0000267 FT_UInt bytes_used;
Werner Lemberg44bb3032004-04-25 20:15:11 +0000268 FT_Memory memory = stream->memory;
Werner Lemberge3c93012013-03-14 11:21:17 +0100269 FT_Error error = FT_Err_Ok;
Werner Lembergf814f682005-05-22 20:33:09 +0000270
271 FT_UNUSED( error );
Werner Lemberg44bb3032004-04-25 20:15:11 +0000272
273
Alexei Podtelezhnikovcb9e7b72021-11-06 22:59:31 -0400274 if ( FT_QNEW_ARRAY( deltas, delta_cnt ) )
Werner Lemberg44bb3032004-04-25 20:15:11 +0000275 return NULL;
276
Jany Belluzfc552912021-11-04 11:07:43 +0000277 i = 0;
278 bytes_used = 0;
279
280 while ( i < delta_cnt && bytes_used < size )
Werner Lemberg44bb3032004-04-25 20:15:11 +0000281 {
282 runcnt = FT_GET_BYTE();
Werner Lemberg328b7922015-03-04 08:40:23 +0100283 cnt = runcnt & GX_DT_DELTA_RUN_COUNT_MASK;
284
Jany Belluzfc552912021-11-04 11:07:43 +0000285 bytes_used++;
286
Werner Lemberg44bb3032004-04-25 20:15:11 +0000287 if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
288 {
Jany Belluzfc552912021-11-04 11:07:43 +0000289 /* `cnt` + 1 zeroes get added */
Werner Lemberg328b7922015-03-04 08:40:23 +0100290 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +0000291 deltas[i++] = 0;
292 }
293 else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
294 {
Jany Belluzfc552912021-11-04 11:07:43 +0000295 /* `cnt` + 1 shorts from the stack */
296 bytes_used += 2 * ( cnt + 1 );
297 if ( bytes_used > size )
298 {
299 FT_TRACE1(( "ft_var_readpackeddeltas:"
300 " number of short deltas too large\n" ));
301 goto Fail;
302 }
303
Werner Lemberg328b7922015-03-04 08:40:23 +0100304 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
Werner Lemberga632fb52018-06-24 15:22:10 +0200305 deltas[i++] = FT_intToFixed( FT_GET_SHORT() );
Werner Lemberg44bb3032004-04-25 20:15:11 +0000306 }
307 else
308 {
Jany Belluzfc552912021-11-04 11:07:43 +0000309 /* `cnt` + 1 signed bytes from the stack */
310 bytes_used += cnt + 1;
311 if ( bytes_used > size )
312 {
313 FT_TRACE1(( "ft_var_readpackeddeltas:"
314 " number of byte deltas too large\n" ));
315 goto Fail;
316 }
317
Werner Lemberg328b7922015-03-04 08:40:23 +0100318 for ( j = 0; j <= cnt && i < delta_cnt; j++ )
Werner Lemberga632fb52018-06-24 15:22:10 +0200319 deltas[i++] = FT_intToFixed( FT_GET_CHAR() );
Werner Lemberg44bb3032004-04-25 20:15:11 +0000320 }
321
Werner Lemberg328b7922015-03-04 08:40:23 +0100322 if ( j <= cnt )
Werner Lemberg44bb3032004-04-25 20:15:11 +0000323 {
Jany Belluzfc552912021-11-04 11:07:43 +0000324 FT_TRACE1(( "ft_var_readpackeddeltas:"
325 " number of deltas too large\n" ));
326 goto Fail;
Werner Lemberg44bb3032004-04-25 20:15:11 +0000327 }
328 }
329
Jany Belluzfc552912021-11-04 11:07:43 +0000330 if ( i < delta_cnt )
331 {
332 FT_TRACE1(( "ft_var_readpackeddeltas: not enough deltas\n" ));
333 goto Fail;
334 }
335
Werner Lemberg44bb3032004-04-25 20:15:11 +0000336 return deltas;
Jany Belluzfc552912021-11-04 11:07:43 +0000337
338 Fail:
339 FT_FREE( deltas );
340 return NULL;
Werner Lemberg44bb3032004-04-25 20:15:11 +0000341 }
342
343
Werner Lemberg9ac90602018-06-03 09:01:17 +0200344 /**************************************************************************
345 *
346 * @Function:
347 * ft_var_load_avar
348 *
349 * @Description:
350 * Parse the `avar' table if present. It need not be, so we return
351 * nothing.
352 *
353 * @InOut:
354 * face ::
355 * The font face.
356 */
Werner Lemberg44bb3032004-04-25 20:15:11 +0000357 static void
358 ft_var_load_avar( TT_Face face )
359 {
Werner Lemberg0aa36162015-03-08 22:50:37 +0100360 FT_Stream stream = FT_FACE_STREAM( face );
Werner Lemberg44bb3032004-04-25 20:15:11 +0000361 FT_Memory memory = stream->memory;
362 GX_Blend blend = face->blend;
363 GX_AVarSegment segment;
Werner Lemberge3c93012013-03-14 11:21:17 +0100364 FT_Error error = FT_Err_Ok;
Werner Lemberg1f7a4e12015-02-17 10:17:56 +0100365 FT_Long version;
Werner Lemberg44bb3032004-04-25 20:15:11 +0000366 FT_Long axisCount;
367 FT_Int i, j;
368 FT_ULong table_len;
369
Werner Lembergf814f682005-05-22 20:33:09 +0000370 FT_UNUSED( error );
371
Werner Lemberg44bb3032004-04-25 20:15:11 +0000372
Werner Lembergcdee7d12015-05-31 11:54:42 +0200373 FT_TRACE2(( "AVAR " ));
374
Werner Lemberg4a629222017-03-12 10:19:53 +0100375 blend->avar_loaded = TRUE;
Werner Lembergcdee7d12015-05-31 11:54:42 +0200376 error = face->goto_table( face, TTAG_avar, stream, &table_len );
377 if ( error )
378 {
379 FT_TRACE2(( "is missing\n" ));
Werner Lemberg44bb3032004-04-25 20:15:11 +0000380 return;
Werner Lembergcdee7d12015-05-31 11:54:42 +0200381 }
David Turnerc28b8c12004-06-24 20:18:24 +0000382
Werner Lemberg44bb3032004-04-25 20:15:11 +0000383 if ( FT_FRAME_ENTER( table_len ) )
384 return;
385
386 version = FT_GET_LONG();
387 axisCount = FT_GET_LONG();
388
Werner Lembergcdee7d12015-05-31 11:54:42 +0200389 if ( version != 0x00010000L )
390 {
391 FT_TRACE2(( "bad table version\n" ));
Werner Lemberg19b42a52004-05-10 20:54:27 +0000392 goto Exit;
Werner Lembergcdee7d12015-05-31 11:54:42 +0200393 }
394
395 FT_TRACE2(( "loaded\n" ));
396
397 if ( axisCount != (FT_Long)blend->mmvar->num_axis )
398 {
Werner Lemberga6adb252020-12-02 14:15:07 +0100399 FT_TRACE2(( "ft_var_load_avar:"
400 " number of axes in `avar' and `fvar'\n" ));
401 FT_TRACE2(( " table are different\n" ));
Werner Lembergcdee7d12015-05-31 11:54:42 +0200402 goto Exit;
403 }
Werner Lemberg44bb3032004-04-25 20:15:11 +0000404
Alexei Podtelezhnikovcb9e7b72021-11-06 22:59:31 -0400405 if ( FT_QNEW_ARRAY( blend->avar_segment, axisCount ) )
Werner Lemberg44bb3032004-04-25 20:15:11 +0000406 goto Exit;
407
408 segment = &blend->avar_segment[0];
Werner Lemberg0aa36162015-03-08 22:50:37 +0100409 for ( i = 0; i < axisCount; i++, segment++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +0000410 {
Werner Lembergcdee7d12015-05-31 11:54:42 +0200411 FT_TRACE5(( " axis %d:\n", i ));
412
Werner Lemberg44bb3032004-04-25 20:15:11 +0000413 segment->pairCount = FT_GET_USHORT();
Alexei Podtelezhnikovcb9e7b72021-11-06 22:59:31 -0400414 if ( (FT_ULong)segment->pairCount * 4 > table_len ||
415 FT_QNEW_ARRAY( segment->correspondence, segment->pairCount ) )
Werner Lembergee6e92c2004-05-11 06:45:27 +0000416 {
417 /* Failure. Free everything we have done so far. We must do */
418 /* it right now since loading the `avar' table is optional. */
419
Werner Lemberg0aa36162015-03-08 22:50:37 +0100420 for ( j = i - 1; j >= 0; j-- )
Werner Lembergee6e92c2004-05-11 06:45:27 +0000421 FT_FREE( blend->avar_segment[j].correspondence );
422
423 FT_FREE( blend->avar_segment );
424 blend->avar_segment = NULL;
Werner Lemberg44bb3032004-04-25 20:15:11 +0000425 goto Exit;
Werner Lembergee6e92c2004-05-11 06:45:27 +0000426 }
Werner Lemberg44bb3032004-04-25 20:15:11 +0000427
Werner Lemberg0aa36162015-03-08 22:50:37 +0100428 for ( j = 0; j < segment->pairCount; j++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +0000429 {
Werner Lembergdc39f762019-05-07 10:09:55 +0200430 segment->correspondence[j].fromCoord =
431 FT_fdot14ToFixed( FT_GET_SHORT() );
432 segment->correspondence[j].toCoord =
433 FT_fdot14ToFixed( FT_GET_SHORT() );
Werner Lembergcdee7d12015-05-31 11:54:42 +0200434
Werner Lemberg84b0d992016-12-29 10:38:51 +0100435 FT_TRACE5(( " mapping %.5f to %.5f\n",
Werner Lembergcdee7d12015-05-31 11:54:42 +0200436 segment->correspondence[j].fromCoord / 65536.0,
437 segment->correspondence[j].toCoord / 65536.0 ));
Werner Lemberg44bb3032004-04-25 20:15:11 +0000438 }
Werner Lembergcdee7d12015-05-31 11:54:42 +0200439
440 FT_TRACE5(( "\n" ));
Werner Lemberg44bb3032004-04-25 20:15:11 +0000441 }
442
443 Exit:
444 FT_FRAME_EXIT();
445 }
446
447
Werner Lemberg9c814702017-01-06 21:13:36 +0100448 static FT_Error
Werner Lembergfb90f002017-01-06 22:23:52 +0100449 ft_var_load_item_variation_store( TT_Face face,
450 FT_ULong offset,
451 GX_ItemVarStore itemStore )
Werner Lemberg9c814702017-01-06 21:13:36 +0100452 {
453 FT_Stream stream = FT_FACE_STREAM( face );
454 FT_Memory memory = stream->memory;
455
456 FT_Error error;
457 FT_UShort format;
458 FT_ULong region_offset;
459 FT_UInt i, j, k;
Werner Lemberge4f76732021-11-13 21:11:00 +0100460 FT_UInt wordDeltaCount;
461 FT_Bool long_words;
Werner Lemberg9c814702017-01-06 21:13:36 +0100462
Werner Lembergfb90f002017-01-06 22:23:52 +0100463 GX_Blend blend = face->blend;
464 GX_ItemVarData varData;
Werner Lemberg9c814702017-01-06 21:13:36 +0100465
466 FT_ULong* dataOffsetArray = NULL;
467
468
469 if ( FT_STREAM_SEEK( offset ) ||
470 FT_READ_USHORT( format ) )
471 goto Exit;
472
473 if ( format != 1 )
474 {
Werner Lemberg79d52b92017-01-23 07:43:56 +0100475 FT_TRACE2(( "ft_var_load_item_variation_store: bad store format %d\n",
476 format ));
Werner Lemberg9c814702017-01-06 21:13:36 +0100477 error = FT_THROW( Invalid_Table );
478 goto Exit;
479 }
480
Werner Lemberg9c814702017-01-06 21:13:36 +0100481 /* read top level fields */
482 if ( FT_READ_ULONG( region_offset ) ||
483 FT_READ_USHORT( itemStore->dataCount ) )
484 goto Exit;
485
Werner Lemberg79d52b92017-01-23 07:43:56 +0100486 /* we need at least one entry in `itemStore->varData' */
487 if ( !itemStore->dataCount )
488 {
489 FT_TRACE2(( "ft_var_load_item_variation_store: missing varData\n" ));
490 error = FT_THROW( Invalid_Table );
491 goto Exit;
492 }
493
Werner Lemberg9c814702017-01-06 21:13:36 +0100494 /* make temporary copy of item variation data offsets; */
495 /* we will parse region list first, then come back */
Alexei Podtelezhnikovcb9e7b72021-11-06 22:59:31 -0400496 if ( FT_QNEW_ARRAY( dataOffsetArray, itemStore->dataCount ) )
Werner Lemberg9c814702017-01-06 21:13:36 +0100497 goto Exit;
498
499 for ( i = 0; i < itemStore->dataCount; i++ )
500 {
501 if ( FT_READ_ULONG( dataOffsetArray[i] ) )
502 goto Exit;
503 }
504
505 /* parse array of region records (region list) */
506 if ( FT_STREAM_SEEK( offset + region_offset ) )
507 goto Exit;
508
509 if ( FT_READ_USHORT( itemStore->axisCount ) ||
510 FT_READ_USHORT( itemStore->regionCount ) )
511 goto Exit;
512
513 if ( itemStore->axisCount != (FT_Long)blend->mmvar->num_axis )
514 {
515 FT_TRACE2(( "ft_var_load_item_variation_store:"
Werner Lembergd924a662021-02-04 07:44:06 +0100516 " number of axes in item variation store\n" ));
517 FT_TRACE2(( " "
Werner Lembergfb90f002017-01-06 22:23:52 +0100518 " and `fvar' table are different\n" ));
Werner Lemberg9c814702017-01-06 21:13:36 +0100519 error = FT_THROW( Invalid_Table );
520 goto Exit;
521 }
522
Werner Lemberg804e6252020-10-28 13:34:52 +0100523 /* new constraint in OpenType 1.8.4 */
524 if ( itemStore->regionCount >= 32768U )
525 {
526 FT_TRACE2(( "ft_var_load_item_variation_store:"
527 " too many variation region tables\n" ));
528 error = FT_THROW( Invalid_Table );
529 goto Exit;
530 }
531
Werner Lemberg9c814702017-01-06 21:13:36 +0100532 if ( FT_NEW_ARRAY( itemStore->varRegionList, itemStore->regionCount ) )
533 goto Exit;
534
535 for ( i = 0; i < itemStore->regionCount; i++ )
536 {
537 GX_AxisCoords axisCoords;
538
539
540 if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList,
541 itemStore->axisCount ) )
542 goto Exit;
543
544 axisCoords = itemStore->varRegionList[i].axisList;
545
546 for ( j = 0; j < itemStore->axisCount; j++ )
547 {
548 FT_Short start, peak, end;
549
550
551 if ( FT_READ_SHORT( start ) ||
552 FT_READ_SHORT( peak ) ||
553 FT_READ_SHORT( end ) )
554 goto Exit;
555
556 axisCoords[j].startCoord = FT_fdot14ToFixed( start );
557 axisCoords[j].peakCoord = FT_fdot14ToFixed( peak );
558 axisCoords[j].endCoord = FT_fdot14ToFixed( end );
559 }
560 }
561
562 /* end of region list parse */
563
564 /* use dataOffsetArray now to parse varData items */
565 if ( FT_NEW_ARRAY( itemStore->varData, itemStore->dataCount ) )
566 goto Exit;
567
568 for ( i = 0; i < itemStore->dataCount; i++ )
569 {
Werner Lembergfb90f002017-01-06 22:23:52 +0100570 varData = &itemStore->varData[i];
Werner Lemberg9c814702017-01-06 21:13:36 +0100571
572 if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) )
573 goto Exit;
574
Werner Lembergfb90f002017-01-06 22:23:52 +0100575 if ( FT_READ_USHORT( varData->itemCount ) ||
Werner Lemberge4f76732021-11-13 21:11:00 +0100576 FT_READ_USHORT( wordDeltaCount ) ||
Werner Lembergfb90f002017-01-06 22:23:52 +0100577 FT_READ_USHORT( varData->regionIdxCount ) )
Werner Lemberg9c814702017-01-06 21:13:36 +0100578 goto Exit;
579
Werner Lemberge4f76732021-11-13 21:11:00 +0100580 long_words = !!( wordDeltaCount & 0x8000 );
581 wordDeltaCount &= 0x7FFF;
582
Werner Lemberg9c814702017-01-06 21:13:36 +0100583 /* check some data consistency */
Werner Lemberge4f76732021-11-13 21:11:00 +0100584 if ( wordDeltaCount > varData->regionIdxCount )
Werner Lemberg9c814702017-01-06 21:13:36 +0100585 {
586 FT_TRACE2(( "bad short count %d or region count %d\n",
Werner Lemberge4f76732021-11-13 21:11:00 +0100587 wordDeltaCount,
Werner Lembergfb90f002017-01-06 22:23:52 +0100588 varData->regionIdxCount ));
Werner Lemberg9c814702017-01-06 21:13:36 +0100589 error = FT_THROW( Invalid_Table );
590 goto Exit;
591 }
592
Werner Lembergfb90f002017-01-06 22:23:52 +0100593 if ( varData->regionIdxCount > itemStore->regionCount )
Werner Lemberg9c814702017-01-06 21:13:36 +0100594 {
595 FT_TRACE2(( "inconsistent regionCount %d in varData[%d]\n",
Werner Lembergfb90f002017-01-06 22:23:52 +0100596 varData->regionIdxCount,
Werner Lemberg9c814702017-01-06 21:13:36 +0100597 i ));
598 error = FT_THROW( Invalid_Table );
599 goto Exit;
600 }
601
602 /* parse region indices */
Werner Lembergfb90f002017-01-06 22:23:52 +0100603 if ( FT_NEW_ARRAY( varData->regionIndices,
604 varData->regionIdxCount ) )
Werner Lemberg9c814702017-01-06 21:13:36 +0100605 goto Exit;
606
Werner Lembergfb90f002017-01-06 22:23:52 +0100607 for ( j = 0; j < varData->regionIdxCount; j++ )
Werner Lemberg9c814702017-01-06 21:13:36 +0100608 {
Werner Lembergfb90f002017-01-06 22:23:52 +0100609 if ( FT_READ_USHORT( varData->regionIndices[j] ) )
Werner Lemberg9c814702017-01-06 21:13:36 +0100610 goto Exit;
611
Werner Lembergfb90f002017-01-06 22:23:52 +0100612 if ( varData->regionIndices[j] >= itemStore->regionCount )
Werner Lemberg9c814702017-01-06 21:13:36 +0100613 {
614 FT_TRACE2(( "bad region index %d\n",
Werner Lembergfb90f002017-01-06 22:23:52 +0100615 varData->regionIndices[j] ));
Werner Lemberg9c814702017-01-06 21:13:36 +0100616 error = FT_THROW( Invalid_Table );
617 goto Exit;
618 }
619 }
620
621 /* Parse delta set. */
622 /* */
Werner Lemberge4f76732021-11-13 21:11:00 +0100623 /* On input, deltas are (wordDeltaCount + regionIdxCount) bytes */
624 /* each if `long_words` isn't set, and twice as much otherwise. */
625 /* */
626 /* On output, deltas are expanded to `regionIdxCount` shorts each. */
Werner Lembergfb90f002017-01-06 22:23:52 +0100627 if ( FT_NEW_ARRAY( varData->deltaSet,
628 varData->regionIdxCount * varData->itemCount ) )
Werner Lemberg9c814702017-01-06 21:13:36 +0100629 goto Exit;
630
Werner Lemberge4f76732021-11-13 21:11:00 +0100631 /* the delta set is stored as a 2-dimensional array of shorts */
632 if ( long_words )
Werner Lemberg9c814702017-01-06 21:13:36 +0100633 {
Werner Lemberge4f76732021-11-13 21:11:00 +0100634 /* new in OpenType 1.9, currently for 'COLR' table only; */
635 /* the deltas are interpreted as 16.16 fixed-point scaling values */
636
637 /* not supported yet */
638
639 error = FT_THROW( Invalid_Table );
640 goto Exit;
641 }
642 else
643 {
644 for ( j = 0; j < varData->itemCount * varData->regionIdxCount; )
Werner Lemberg9c814702017-01-06 21:13:36 +0100645 {
Werner Lemberge4f76732021-11-13 21:11:00 +0100646 for ( k = 0; k < wordDeltaCount; k++, j++ )
647 {
648 /* read the short deltas */
649 FT_Short delta;
Werner Lemberg9c814702017-01-06 21:13:36 +0100650
651
Werner Lemberge4f76732021-11-13 21:11:00 +0100652 if ( FT_READ_SHORT( delta ) )
653 goto Exit;
Werner Lemberg9c814702017-01-06 21:13:36 +0100654
Werner Lemberge4f76732021-11-13 21:11:00 +0100655 varData->deltaSet[j] = delta;
656 }
Werner Lemberg9c814702017-01-06 21:13:36 +0100657
Werner Lemberge4f76732021-11-13 21:11:00 +0100658 for ( ; k < varData->regionIdxCount; k++, j++ )
659 {
660 /* read the (signed) byte deltas */
661 FT_Char delta;
Werner Lemberg9c814702017-01-06 21:13:36 +0100662
663
Werner Lemberge4f76732021-11-13 21:11:00 +0100664 if ( FT_READ_CHAR( delta ) )
665 goto Exit;
Werner Lemberg9c814702017-01-06 21:13:36 +0100666
Werner Lemberge4f76732021-11-13 21:11:00 +0100667 varData->deltaSet[j] = delta;
668 }
Werner Lemberg9c814702017-01-06 21:13:36 +0100669 }
670 }
671 }
672
673 Exit:
674 FT_FREE( dataOffsetArray );
675
676 return error;
677 }
678
679
680 static FT_Error
Werner Lembergfb90f002017-01-06 22:23:52 +0100681 ft_var_load_delta_set_index_mapping( TT_Face face,
682 FT_ULong offset,
683 GX_DeltaSetIdxMap map,
Werner Lemberg93e6b3e2021-11-13 14:41:40 +0100684 GX_ItemVarStore itemStore,
685 FT_ULong table_len )
Werner Lemberg9c814702017-01-06 21:13:36 +0100686 {
687 FT_Stream stream = FT_FACE_STREAM( face );
688 FT_Memory memory = stream->memory;
689
Werner Lemberg93e6b3e2021-11-13 14:41:40 +0100690 FT_Error error;
Werner Lemberg9c814702017-01-06 21:13:36 +0100691
Werner Lemberg93e6b3e2021-11-13 14:41:40 +0100692 FT_Byte format;
693 FT_Byte entryFormat;
694 FT_UInt entrySize;
695 FT_UInt innerBitCount;
696 FT_UInt innerIndexMask;
697 FT_ULong i;
698 FT_UInt j;
Werner Lemberg9c814702017-01-06 21:13:36 +0100699
700
Werner Lemberg93e6b3e2021-11-13 14:41:40 +0100701 if ( FT_STREAM_SEEK( offset ) ||
702 FT_READ_BYTE( format ) ||
703 FT_READ_BYTE( entryFormat ) )
Werner Lemberg9c814702017-01-06 21:13:36 +0100704 goto Exit;
705
Werner Lemberg93e6b3e2021-11-13 14:41:40 +0100706 if ( format == 0 )
707 {
708 if ( FT_READ_USHORT( map->mapCount ) )
709 goto Exit;
710 }
711 else if ( format == 1 ) /* new in OpenType 1.9 */
712 {
713 if ( FT_READ_ULONG( map->mapCount ) )
714 goto Exit;
715 }
716 else
Werner Lemberg9c814702017-01-06 21:13:36 +0100717 {
718 FT_TRACE2(( "bad map format %d\n", format ));
719 error = FT_THROW( Invalid_Table );
720 goto Exit;
721 }
722
Werner Lemberg93e6b3e2021-11-13 14:41:40 +0100723 if ( entryFormat & 0xC0 )
724 {
725 FT_TRACE2(( "bad entry format %d\n", format ));
726 error = FT_THROW( Invalid_Table );
727 goto Exit;
728 }
729
Werner Lemberg9c814702017-01-06 21:13:36 +0100730 /* bytes per entry: 1, 2, 3, or 4 */
Werner Lemberg93e6b3e2021-11-13 14:41:40 +0100731 entrySize = ( ( entryFormat & 0x30 ) >> 4 ) + 1;
732 innerBitCount = ( entryFormat & 0x0F ) + 1;
Werner Lemberg9c814702017-01-06 21:13:36 +0100733 innerIndexMask = ( 1 << innerBitCount ) - 1;
734
Werner Lemberg93e6b3e2021-11-13 14:41:40 +0100735 /* rough sanity check */
736 if ( map->mapCount * entrySize > table_len )
737 {
738 FT_TRACE1(( "ft_var_load_delta_set_index_mapping:"
739 " invalid number of delta-set index mappings\n" ));
740 error = FT_THROW( Invalid_Table );
741 goto Exit;
742 }
743
Werner Lembergfb90f002017-01-06 22:23:52 +0100744 if ( FT_NEW_ARRAY( map->innerIndex, map->mapCount ) )
Werner Lemberg9c814702017-01-06 21:13:36 +0100745 goto Exit;
746
Werner Lembergfb90f002017-01-06 22:23:52 +0100747 if ( FT_NEW_ARRAY( map->outerIndex, map->mapCount ) )
Werner Lemberg9c814702017-01-06 21:13:36 +0100748 goto Exit;
749
Werner Lembergfb90f002017-01-06 22:23:52 +0100750 for ( i = 0; i < map->mapCount; i++ )
Werner Lemberg9c814702017-01-06 21:13:36 +0100751 {
752 FT_UInt mapData = 0;
753 FT_UInt outerIndex, innerIndex;
754
755
756 /* read map data one unsigned byte at a time, big endian */
757 for ( j = 0; j < entrySize; j++ )
758 {
759 FT_Byte data;
760
761
762 if ( FT_READ_BYTE( data ) )
763 goto Exit;
764
765 mapData = ( mapData << 8 ) | data;
766 }
767
768 outerIndex = mapData >> innerBitCount;
769
Werner Lembergfb90f002017-01-06 22:23:52 +0100770 if ( outerIndex >= itemStore->dataCount )
Werner Lemberg9c814702017-01-06 21:13:36 +0100771 {
Werner Lemberg93e6b3e2021-11-13 14:41:40 +0100772 FT_TRACE2(( "outerIndex[%ld] == %d out of range\n",
Werner Lemberg9c814702017-01-06 21:13:36 +0100773 i,
774 outerIndex ));
775 error = FT_THROW( Invalid_Table );
776 goto Exit;
777 }
778
Werner Lembergfb90f002017-01-06 22:23:52 +0100779 map->outerIndex[i] = outerIndex;
Werner Lemberg9c814702017-01-06 21:13:36 +0100780
781 innerIndex = mapData & innerIndexMask;
782
Werner Lembergfb90f002017-01-06 22:23:52 +0100783 if ( innerIndex >= itemStore->varData[outerIndex].itemCount )
Werner Lemberg9c814702017-01-06 21:13:36 +0100784 {
Werner Lemberg93e6b3e2021-11-13 14:41:40 +0100785 FT_TRACE2(( "innerIndex[%ld] == %d out of range\n",
Werner Lemberg9c814702017-01-06 21:13:36 +0100786 i,
787 innerIndex ));
788 error = FT_THROW( Invalid_Table );
789 goto Exit;
790 }
791
Werner Lembergfb90f002017-01-06 22:23:52 +0100792 map->innerIndex[i] = innerIndex;
Werner Lemberg9c814702017-01-06 21:13:36 +0100793 }
794
795 Exit:
796 return error;
797 }
798
799
Werner Lemberg9ac90602018-06-03 09:01:17 +0200800 /**************************************************************************
801 *
802 * @Function:
803 * ft_var_load_hvvar
804 *
805 * @Description:
806 * If `vertical' is zero, parse the `HVAR' table and set
807 * `blend->hvar_loaded' to TRUE. On success, `blend->hvar_checked'
808 * is set to TRUE.
809 *
810 * If `vertical' is not zero, parse the `VVAR' table and set
811 * `blend->vvar_loaded' to TRUE. On success, `blend->vvar_checked'
812 * is set to TRUE.
813 *
814 * Some memory may remain allocated on error; it is always freed in
815 * `tt_done_blend', however.
816 *
817 * @InOut:
818 * face ::
819 * The font face.
820 *
821 * @Return:
822 * FreeType error code. 0 means success.
823 */
Dave Arnold097cd872016-12-15 12:58:26 +0100824 static FT_Error
Werner Lembergfaa3c882017-02-06 13:13:02 +0100825 ft_var_load_hvvar( TT_Face face,
826 FT_Bool vertical )
Dave Arnold097cd872016-12-15 12:58:26 +0100827 {
828 FT_Stream stream = FT_FACE_STREAM( face );
Werner Lembergfb90f002017-01-06 22:23:52 +0100829 FT_Memory memory = stream->memory;
Dave Arnold097cd872016-12-15 12:58:26 +0100830
831 GX_Blend blend = face->blend;
832
Werner Lembergfaa3c882017-02-06 13:13:02 +0100833 GX_HVVarTable table;
834
Dave Arnold097cd872016-12-15 12:58:26 +0100835 FT_Error error;
836 FT_UShort majorVersion;
Dave Arnold097cd872016-12-15 12:58:26 +0100837 FT_ULong table_len;
838 FT_ULong table_offset;
839 FT_ULong store_offset;
Werner Lemberg7e1cce52017-01-06 20:31:22 +0100840 FT_ULong widthMap_offset;
Dave Arnold097cd872016-12-15 12:58:26 +0100841
Dave Arnold097cd872016-12-15 12:58:26 +0100842
Werner Lembergfaa3c882017-02-06 13:13:02 +0100843 if ( vertical )
844 {
845 blend->vvar_loaded = TRUE;
Dave Arnold097cd872016-12-15 12:58:26 +0100846
Werner Lembergfaa3c882017-02-06 13:13:02 +0100847 FT_TRACE2(( "VVAR " ));
Dave Arnold097cd872016-12-15 12:58:26 +0100848
Werner Lembergfaa3c882017-02-06 13:13:02 +0100849 error = face->goto_table( face, TTAG_VVAR, stream, &table_len );
850 }
851 else
852 {
853 blend->hvar_loaded = TRUE;
854
855 FT_TRACE2(( "HVAR " ));
856
857 error = face->goto_table( face, TTAG_HVAR, stream, &table_len );
858 }
859
Dave Arnold097cd872016-12-15 12:58:26 +0100860 if ( error )
861 {
862 FT_TRACE2(( "is missing\n" ));
863 goto Exit;
864 }
865
866 table_offset = FT_STREAM_POS();
867
Werner Lemberg68620242016-12-16 11:15:17 +0100868 /* skip minor version */
Dave Arnold097cd872016-12-15 12:58:26 +0100869 if ( FT_READ_USHORT( majorVersion ) ||
Werner Lemberg68620242016-12-16 11:15:17 +0100870 FT_STREAM_SKIP( 2 ) )
Dave Arnold097cd872016-12-15 12:58:26 +0100871 goto Exit;
Werner Lemberg07ee1d22017-01-11 12:50:51 +0100872
Dave Arnold097cd872016-12-15 12:58:26 +0100873 if ( majorVersion != 1 )
874 {
875 FT_TRACE2(( "bad table version %d\n", majorVersion ));
876 error = FT_THROW( Invalid_Table );
877 goto Exit;
878 }
879
Werner Lemberg7e1cce52017-01-06 20:31:22 +0100880 if ( FT_READ_ULONG( store_offset ) ||
881 FT_READ_ULONG( widthMap_offset ) )
Dave Arnold097cd872016-12-15 12:58:26 +0100882 goto Exit;
883
Werner Lembergfaa3c882017-02-06 13:13:02 +0100884 if ( vertical )
885 {
886 if ( FT_NEW( blend->vvar_table ) )
887 goto Exit;
888 table = blend->vvar_table;
889 }
890 else
891 {
892 if ( FT_NEW( blend->hvar_table ) )
893 goto Exit;
894 table = blend->hvar_table;
895 }
Werner Lembergfb90f002017-01-06 22:23:52 +0100896
Werner Lemberg9c814702017-01-06 21:13:36 +0100897 error = ft_var_load_item_variation_store(
898 face,
Werner Lembergfb90f002017-01-06 22:23:52 +0100899 table_offset + store_offset,
Werner Lembergfaa3c882017-02-06 13:13:02 +0100900 &table->itemStore );
Werner Lemberg9c814702017-01-06 21:13:36 +0100901 if ( error )
902 goto Exit;
Dave Arnold097cd872016-12-15 12:58:26 +0100903
Werner Lemberg7e1cce52017-01-06 20:31:22 +0100904 if ( widthMap_offset )
Dave Arnold097cd872016-12-15 12:58:26 +0100905 {
Werner Lemberg9c814702017-01-06 21:13:36 +0100906 error = ft_var_load_delta_set_index_mapping(
907 face,
Werner Lembergfb90f002017-01-06 22:23:52 +0100908 table_offset + widthMap_offset,
Werner Lembergfaa3c882017-02-06 13:13:02 +0100909 &table->widthMap,
Werner Lemberg93e6b3e2021-11-13 14:41:40 +0100910 &table->itemStore,
911 table_len );
Werner Lemberg9c814702017-01-06 21:13:36 +0100912 if ( error )
Dave Arnold097cd872016-12-15 12:58:26 +0100913 goto Exit;
Dave Arnold097cd872016-12-15 12:58:26 +0100914 }
915
Dave Arnold097cd872016-12-15 12:58:26 +0100916 FT_TRACE2(( "loaded\n" ));
917 error = FT_Err_Ok;
918
919 Exit:
Dave Arnold097cd872016-12-15 12:58:26 +0100920 if ( !error )
Werner Lemberg64a91132016-12-21 19:30:33 +0100921 {
Werner Lembergfaa3c882017-02-06 13:13:02 +0100922 if ( vertical )
923 {
924 blend->vvar_checked = TRUE;
Dave Arnold097cd872016-12-15 12:58:26 +0100925
Werner Lembergfaa3c882017-02-06 13:13:02 +0100926 /* FreeType doesn't provide functions to quickly retrieve */
927 /* TSB, BSB, or VORG values; we thus don't have to implement */
928 /* support for those three item variation stores. */
Werner Lemberg348d39c2017-01-05 12:29:55 +0100929
Werner Lembergfaa3c882017-02-06 13:13:02 +0100930 face->variation_support |= TT_FACE_FLAG_VAR_VADVANCE;
931 }
932 else
933 {
934 blend->hvar_checked = TRUE;
935
936 /* FreeType doesn't provide functions to quickly retrieve */
937 /* LSB or RSB values; we thus don't have to implement */
938 /* support for those two item variation stores. */
939
940 face->variation_support |= TT_FACE_FLAG_VAR_HADVANCE;
941 }
Werner Lemberg64a91132016-12-21 19:30:33 +0100942 }
943
Dave Arnold097cd872016-12-15 12:58:26 +0100944 return error;
945 }
946
947
Werner Lemberg469ced72017-01-08 09:28:34 +0100948 static FT_Int
949 ft_var_get_item_delta( TT_Face face,
950 GX_ItemVarStore itemStore,
951 FT_UInt outerIndex,
952 FT_UInt innerIndex )
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100953 {
Werner Lemberg0bd01122017-01-06 21:32:49 +0100954 GX_ItemVarData varData;
Werner Lemberg469ced72017-01-08 09:28:34 +0100955 FT_Short* deltaSet;
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100956
Werner Lemberg469ced72017-01-08 09:28:34 +0100957 FT_UInt master, j;
958 FT_Fixed netAdjustment = 0; /* accumulated adjustment */
959 FT_Fixed scaledDelta;
960 FT_Fixed delta;
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100961
962
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100963 /* See pseudo code from `Font Variations Overview' */
964 /* in the OpenType specification. */
965
Werner Lemberg469ced72017-01-08 09:28:34 +0100966 varData = &itemStore->varData[outerIndex];
967 deltaSet = &varData->deltaSet[varData->regionIdxCount * innerIndex];
968
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100969 /* outer loop steps through master designs to be blended */
970 for ( master = 0; master < varData->regionIdxCount; master++ )
971 {
Alexei Podtelezhnikov61d50752018-11-02 20:42:25 -0400972 FT_Fixed scalar = 0x10000L;
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100973 FT_UInt regionIndex = varData->regionIndices[master];
974
Werner Lemberg469ced72017-01-08 09:28:34 +0100975 GX_AxisCoords axis = itemStore->varRegionList[regionIndex].axisList;
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100976
977
978 /* inner loop steps through axes in this region */
Werner Lemberg469ced72017-01-08 09:28:34 +0100979 for ( j = 0; j < itemStore->axisCount; j++, axis++ )
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100980 {
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100981 /* compute the scalar contribution of this axis; */
982 /* ignore invalid ranges */
983 if ( axis->startCoord > axis->peakCoord ||
984 axis->peakCoord > axis->endCoord )
Alexei Podtelezhnikov300da332018-10-31 21:55:40 -0400985 continue;
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100986
987 else if ( axis->startCoord < 0 &&
988 axis->endCoord > 0 &&
989 axis->peakCoord != 0 )
Alexei Podtelezhnikov300da332018-10-31 21:55:40 -0400990 continue;
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100991
992 /* peak of 0 means ignore this axis */
993 else if ( axis->peakCoord == 0 )
Alexei Podtelezhnikov300da332018-10-31 21:55:40 -0400994 continue;
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100995
Alexei Podtelezhnikov61d50752018-11-02 20:42:25 -0400996 else if ( face->blend->normalizedcoords[j] == axis->peakCoord )
997 continue;
998
Dave Arnoldaa0c4b42016-12-15 14:04:51 +0100999 /* ignore this region if coords are out of range */
Alexei Podtelezhnikov300da332018-10-31 21:55:40 -04001000 else if ( face->blend->normalizedcoords[j] <= axis->startCoord ||
1001 face->blend->normalizedcoords[j] >= axis->endCoord )
Dave Arnoldaa0c4b42016-12-15 14:04:51 +01001002 {
Alexei Podtelezhnikov300da332018-10-31 21:55:40 -04001003 scalar = 0;
1004 break;
Dave Arnoldaa0c4b42016-12-15 14:04:51 +01001005 }
1006
Alexei Podtelezhnikov300da332018-10-31 21:55:40 -04001007 /* cumulative product of all the axis scalars */
1008 else if ( face->blend->normalizedcoords[j] < axis->peakCoord )
1009 scalar =
1010 FT_MulDiv( scalar,
1011 face->blend->normalizedcoords[j] - axis->startCoord,
1012 axis->peakCoord - axis->startCoord );
1013 else
1014 scalar =
1015 FT_MulDiv( scalar,
1016 axis->endCoord - face->blend->normalizedcoords[j],
1017 axis->endCoord - axis->peakCoord );
Dave Arnoldaa0c4b42016-12-15 14:04:51 +01001018 } /* per-axis loop */
1019
Dave Arnoldaa0c4b42016-12-15 14:04:51 +01001020 /* get the scaled delta for this region */
1021 delta = FT_intToFixed( deltaSet[master] );
1022 scaledDelta = FT_MulFix( scalar, delta );
1023
1024 /* accumulate the adjustments from each region */
1025 netAdjustment = netAdjustment + scaledDelta;
1026
1027 } /* per-region loop */
1028
Werner Lemberg469ced72017-01-08 09:28:34 +01001029 return FT_fixedToInt( netAdjustment );
1030 }
1031
1032
Werner Lemberg9ac90602018-06-03 09:01:17 +02001033 /**************************************************************************
1034 *
1035 * @Function:
1036 * tt_hvadvance_adjust
1037 *
1038 * @Description:
1039 * Apply `HVAR' advance width or `VVAR' advance height adjustment of
1040 * a given glyph.
1041 *
1042 * @Input:
1043 * gindex ::
1044 * The glyph index.
1045 *
1046 * vertical ::
1047 * If set, handle `VVAR' table.
1048 *
1049 * @InOut:
1050 * face ::
1051 * The font face.
1052 *
1053 * adelta ::
1054 * Points to width or height value that gets modified.
1055 */
Werner Lembergfaa3c882017-02-06 13:13:02 +01001056 static FT_Error
1057 tt_hvadvance_adjust( TT_Face face,
1058 FT_UInt gindex,
1059 FT_Int *avalue,
1060 FT_Bool vertical )
Werner Lemberg469ced72017-01-08 09:28:34 +01001061 {
1062 FT_Error error = FT_Err_Ok;
1063 FT_UInt innerIndex, outerIndex;
1064 FT_Int delta;
1065
Werner Lembergfaa3c882017-02-06 13:13:02 +01001066 GX_HVVarTable table;
1067
Werner Lemberg469ced72017-01-08 09:28:34 +01001068
1069 if ( !face->doblend || !face->blend )
1070 goto Exit;
1071
Werner Lembergfaa3c882017-02-06 13:13:02 +01001072 if ( vertical )
Werner Lemberg469ced72017-01-08 09:28:34 +01001073 {
Werner Lembergfaa3c882017-02-06 13:13:02 +01001074 if ( !face->blend->vvar_loaded )
1075 {
1076 /* initialize vvar table */
1077 face->blend->vvar_error = ft_var_load_hvvar( face, 1 );
1078 }
1079
1080 if ( !face->blend->vvar_checked )
1081 {
1082 error = face->blend->vvar_error;
1083 goto Exit;
1084 }
1085
1086 table = face->blend->vvar_table;
1087 }
1088 else
1089 {
1090 if ( !face->blend->hvar_loaded )
1091 {
1092 /* initialize hvar table */
1093 face->blend->hvar_error = ft_var_load_hvvar( face, 0 );
1094 }
1095
1096 if ( !face->blend->hvar_checked )
1097 {
1098 error = face->blend->hvar_error;
1099 goto Exit;
1100 }
1101
1102 table = face->blend->hvar_table;
Werner Lemberg469ced72017-01-08 09:28:34 +01001103 }
1104
Werner Lembergfaa3c882017-02-06 13:13:02 +01001105 /* advance width or height adjustments are always present in an */
1106 /* `HVAR' or `VVAR' table; no need to test for this capability */
Werner Lemberg469ced72017-01-08 09:28:34 +01001107
Werner Lembergfaa3c882017-02-06 13:13:02 +01001108 if ( table->widthMap.innerIndex )
Werner Lemberg469ced72017-01-08 09:28:34 +01001109 {
Werner Lembergda38be82017-03-30 13:24:03 +02001110 FT_UInt idx = gindex;
1111
1112
1113 if ( idx >= table->widthMap.mapCount )
1114 idx = table->widthMap.mapCount - 1;
Werner Lemberg469ced72017-01-08 09:28:34 +01001115
1116 /* trust that HVAR parser has checked indices */
Werner Lembergda38be82017-03-30 13:24:03 +02001117 outerIndex = table->widthMap.outerIndex[idx];
1118 innerIndex = table->widthMap.innerIndex[idx];
Werner Lemberg469ced72017-01-08 09:28:34 +01001119 }
1120 else
1121 {
1122 GX_ItemVarData varData;
1123
1124
1125 /* no widthMap data */
1126 outerIndex = 0;
1127 innerIndex = gindex;
1128
Werner Lembergfaa3c882017-02-06 13:13:02 +01001129 varData = &table->itemStore.varData[outerIndex];
Werner Lemberg469ced72017-01-08 09:28:34 +01001130 if ( gindex >= varData->itemCount )
1131 {
1132 FT_TRACE2(( "gindex %d out of range\n", gindex ));
1133 error = FT_THROW( Invalid_Argument );
1134 goto Exit;
1135 }
1136 }
1137
1138 delta = ft_var_get_item_delta( face,
Werner Lembergfaa3c882017-02-06 13:13:02 +01001139 &table->itemStore,
Werner Lemberg469ced72017-01-08 09:28:34 +01001140 outerIndex,
1141 innerIndex );
1142
Werner Lemberg71fecc52017-12-05 12:06:29 +01001143 FT_TRACE5(( "%s value %d adjusted by %d unit%s (%s)\n",
Werner Lembergfaa3c882017-02-06 13:13:02 +01001144 vertical ? "vertical height" : "horizontal width",
Werner Lemberg18018512016-12-29 21:30:06 +01001145 *avalue,
Werner Lembergfaa3c882017-02-06 13:13:02 +01001146 delta,
Werner Lemberg71fecc52017-12-05 12:06:29 +01001147 delta == 1 ? "" : "s",
Werner Lembergfaa3c882017-02-06 13:13:02 +01001148 vertical ? "VVAR" : "HVAR" ));
Werner Lemberg18018512016-12-29 21:30:06 +01001149
Werner Lemberg469ced72017-01-08 09:28:34 +01001150 *avalue += delta;
Dave Arnoldaa0c4b42016-12-15 14:04:51 +01001151
1152 Exit:
1153 return error;
1154 }
1155
1156
Werner Lembergfaa3c882017-02-06 13:13:02 +01001157 FT_LOCAL_DEF( FT_Error )
1158 tt_hadvance_adjust( TT_Face face,
1159 FT_UInt gindex,
1160 FT_Int *avalue )
1161 {
1162 return tt_hvadvance_adjust( face, gindex, avalue, 0 );
1163 }
1164
1165
1166 FT_LOCAL_DEF( FT_Error )
1167 tt_vadvance_adjust( TT_Face face,
1168 FT_UInt gindex,
1169 FT_Int *avalue )
1170 {
1171 return tt_hvadvance_adjust( face, gindex, avalue, 1 );
1172 }
1173
1174
Werner Lemberg07ee1d22017-01-11 12:50:51 +01001175#define GX_VALUE_SIZE 8
1176
1177 /* all values are FT_Short or FT_UShort entities; */
1178 /* we treat them consistently as FT_Short */
1179#define GX_VALUE_CASE( tag, dflt ) \
1180 case MVAR_TAG_ ## tag : \
1181 p = (FT_Short*)&face->dflt; \
1182 break
1183
1184#define GX_GASP_CASE( idx ) \
1185 case MVAR_TAG_GASP_ ## idx : \
1186 if ( idx < face->gasp.numRanges - 1 ) \
1187 p = (FT_Short*)&face->gasp.gaspRanges[idx].maxPPEM; \
1188 else \
1189 p = NULL; \
1190 break
1191
1192
1193 static FT_Short*
1194 ft_var_get_value_pointer( TT_Face face,
1195 FT_ULong mvar_tag )
1196 {
1197 FT_Short* p;
1198
1199
1200 switch ( mvar_tag )
1201 {
1202 GX_GASP_CASE( 0 );
1203 GX_GASP_CASE( 1 );
1204 GX_GASP_CASE( 2 );
1205 GX_GASP_CASE( 3 );
1206 GX_GASP_CASE( 4 );
1207 GX_GASP_CASE( 5 );
1208 GX_GASP_CASE( 6 );
1209 GX_GASP_CASE( 7 );
1210 GX_GASP_CASE( 8 );
1211 GX_GASP_CASE( 9 );
1212
1213 GX_VALUE_CASE( CPHT, os2.sCapHeight );
1214 GX_VALUE_CASE( HASC, os2.sTypoAscender );
1215 GX_VALUE_CASE( HCLA, os2.usWinAscent );
1216 GX_VALUE_CASE( HCLD, os2.usWinDescent );
1217 GX_VALUE_CASE( HCOF, horizontal.caret_Offset );
1218 GX_VALUE_CASE( HCRN, horizontal.caret_Slope_Run );
1219 GX_VALUE_CASE( HCRS, horizontal.caret_Slope_Rise );
1220 GX_VALUE_CASE( HDSC, os2.sTypoDescender );
1221 GX_VALUE_CASE( HLGP, os2.sTypoLineGap );
1222 GX_VALUE_CASE( SBXO, os2.ySubscriptXOffset);
1223 GX_VALUE_CASE( SBXS, os2.ySubscriptXSize );
1224 GX_VALUE_CASE( SBYO, os2.ySubscriptYOffset );
1225 GX_VALUE_CASE( SBYS, os2.ySubscriptYSize );
1226 GX_VALUE_CASE( SPXO, os2.ySuperscriptXOffset );
1227 GX_VALUE_CASE( SPXS, os2.ySuperscriptXSize );
1228 GX_VALUE_CASE( SPYO, os2.ySuperscriptYOffset );
1229 GX_VALUE_CASE( SPYS, os2.ySuperscriptYSize );
1230 GX_VALUE_CASE( STRO, os2.yStrikeoutPosition );
1231 GX_VALUE_CASE( STRS, os2.yStrikeoutSize );
1232 GX_VALUE_CASE( UNDO, postscript.underlinePosition );
1233 GX_VALUE_CASE( UNDS, postscript.underlineThickness );
1234 GX_VALUE_CASE( VASC, vertical.Ascender );
1235 GX_VALUE_CASE( VCOF, vertical.caret_Offset );
1236 GX_VALUE_CASE( VCRN, vertical.caret_Slope_Run );
1237 GX_VALUE_CASE( VCRS, vertical.caret_Slope_Rise );
1238 GX_VALUE_CASE( VDSC, vertical.Descender );
1239 GX_VALUE_CASE( VLGP, vertical.Line_Gap );
1240 GX_VALUE_CASE( XHGT, os2.sxHeight );
1241
1242 default:
1243 /* ignore unknown tag */
1244 p = NULL;
1245 }
1246
1247 return p;
1248 }
1249
1250
Werner Lemberg9ac90602018-06-03 09:01:17 +02001251 /**************************************************************************
1252 *
1253 * @Function:
1254 * ft_var_load_mvar
1255 *
1256 * @Description:
1257 * Parse the `MVAR' table.
1258 *
1259 * Some memory may remain allocated on error; it is always freed in
1260 * `tt_done_blend', however.
1261 *
1262 * @InOut:
1263 * face ::
1264 * The font face.
1265 */
Werner Lemberg07ee1d22017-01-11 12:50:51 +01001266 static void
1267 ft_var_load_mvar( TT_Face face )
1268 {
1269 FT_Stream stream = FT_FACE_STREAM( face );
1270 FT_Memory memory = stream->memory;
1271
1272 GX_Blend blend = face->blend;
1273 GX_ItemVarStore itemStore;
1274 GX_Value value, limit;
1275
1276 FT_Error error;
1277 FT_UShort majorVersion;
1278 FT_ULong table_len;
1279 FT_ULong table_offset;
1280 FT_UShort store_offset;
1281 FT_ULong records_offset;
1282
1283
1284 FT_TRACE2(( "MVAR " ));
1285
1286 error = face->goto_table( face, TTAG_MVAR, stream, &table_len );
1287 if ( error )
1288 {
1289 FT_TRACE2(( "is missing\n" ));
1290 return;
1291 }
1292
1293 table_offset = FT_STREAM_POS();
1294
1295 /* skip minor version */
1296 if ( FT_READ_USHORT( majorVersion ) ||
1297 FT_STREAM_SKIP( 2 ) )
1298 return;
1299
1300 if ( majorVersion != 1 )
1301 {
1302 FT_TRACE2(( "bad table version %d\n", majorVersion ));
1303 return;
1304 }
1305
1306 if ( FT_NEW( blend->mvar_table ) )
1307 return;
1308
Werner Lembergf5020922017-01-23 11:47:40 +01001309 /* skip reserved entry and value record size */
1310 if ( FT_STREAM_SKIP( 4 ) ||
Werner Lemberg07ee1d22017-01-11 12:50:51 +01001311 FT_READ_USHORT( blend->mvar_table->valueCount ) ||
1312 FT_READ_USHORT( store_offset ) )
1313 return;
1314
1315 records_offset = FT_STREAM_POS();
1316
1317 error = ft_var_load_item_variation_store(
1318 face,
1319 table_offset + store_offset,
1320 &blend->mvar_table->itemStore );
1321 if ( error )
1322 return;
1323
1324 if ( FT_NEW_ARRAY( blend->mvar_table->values,
1325 blend->mvar_table->valueCount ) )
1326 return;
1327
1328 if ( FT_STREAM_SEEK( records_offset ) ||
1329 FT_FRAME_ENTER( blend->mvar_table->valueCount * GX_VALUE_SIZE ) )
1330 return;
1331
1332 value = blend->mvar_table->values;
1333 limit = value + blend->mvar_table->valueCount;
1334 itemStore = &blend->mvar_table->itemStore;
1335
1336 for ( ; value < limit; value++ )
1337 {
1338 value->tag = FT_GET_ULONG();
1339 value->outerIndex = FT_GET_USHORT();
1340 value->innerIndex = FT_GET_USHORT();
1341
1342 if ( value->outerIndex >= itemStore->dataCount ||
1343 value->innerIndex >= itemStore->varData[value->outerIndex]
1344 .itemCount )
1345 {
1346 error = FT_THROW( Invalid_Table );
1347 break;
1348 }
1349 }
1350
1351 FT_FRAME_EXIT();
1352
1353 if ( error )
1354 return;
1355
1356 FT_TRACE2(( "loaded\n" ));
1357
1358 value = blend->mvar_table->values;
1359 limit = value + blend->mvar_table->valueCount;
1360
1361 /* save original values of the data MVAR is going to modify */
1362 for ( ; value < limit; value++ )
1363 {
1364 FT_Short* p = ft_var_get_value_pointer( face, value->tag );
1365
1366
Werner Lemberg447a0b62017-03-13 07:42:34 +01001367 if ( p )
1368 value->unmodified = *p;
1369#ifdef FT_DEBUG_LEVEL_TRACE
1370 else
1371 FT_TRACE1(( "ft_var_load_mvar: Ignoring unknown tag `%c%c%c%c'\n",
1372 (FT_Char)( value->tag >> 24 ),
1373 (FT_Char)( value->tag >> 16 ),
1374 (FT_Char)( value->tag >> 8 ),
1375 (FT_Char)( value->tag ) ));
1376#endif
Werner Lemberg07ee1d22017-01-11 12:50:51 +01001377 }
1378
1379 face->variation_support |= TT_FACE_FLAG_VAR_MVAR;
1380 }
1381
1382
Werner Lembergd718ac42017-01-11 14:12:34 +01001383 static FT_Error
Seigo Nonakafd9b54e2023-05-02 10:01:38 +09001384 ft_size_reset_iterator( FT_ListNode node,
Werner Lembergd718ac42017-01-11 14:12:34 +01001385 void* user )
1386 {
Seigo Nonakafd9b54e2023-05-02 10:01:38 +09001387 FT_Size size = (FT_Size)node->data;
1388 FT_Service_MetricsVariations var = (FT_Service_MetricsVariations)user;
Werner Lembergd718ac42017-01-11 14:12:34 +01001389
1390
Seigo Nonakafd9b54e2023-05-02 10:01:38 +09001391 var->size_reset( size );
Werner Lembergd718ac42017-01-11 14:12:34 +01001392
1393 return FT_Err_Ok;
1394 }
1395
1396
Werner Lemberg9ac90602018-06-03 09:01:17 +02001397 /**************************************************************************
1398 *
1399 * @Function:
1400 * tt_apply_mvar
1401 *
1402 * @Description:
1403 * Apply `MVAR' table adjustments.
1404 *
1405 * @InOut:
1406 * face ::
1407 * The font face.
1408 */
Werner Lembergd718ac42017-01-11 14:12:34 +01001409 FT_LOCAL_DEF( void )
1410 tt_apply_mvar( TT_Face face )
1411 {
1412 GX_Blend blend = face->blend;
1413 GX_Value value, limit;
Nikolaus Waxweilera6feefd2019-02-02 15:50:57 +00001414 FT_Short mvar_hasc_delta = 0;
1415 FT_Short mvar_hdsc_delta = 0;
1416 FT_Short mvar_hlgp_delta = 0;
Werner Lembergd718ac42017-01-11 14:12:34 +01001417
1418
1419 if ( !( face->variation_support & TT_FACE_FLAG_VAR_MVAR ) )
1420 return;
1421
1422 value = blend->mvar_table->values;
1423 limit = value + blend->mvar_table->valueCount;
1424
1425 for ( ; value < limit; value++ )
1426 {
1427 FT_Short* p = ft_var_get_value_pointer( face, value->tag );
1428 FT_Int delta;
1429
1430
1431 delta = ft_var_get_item_delta( face,
1432 &blend->mvar_table->itemStore,
1433 value->outerIndex,
1434 value->innerIndex );
1435
Werner Lemberg447a0b62017-03-13 07:42:34 +01001436 if ( p )
1437 {
Werner Lemberg71fecc52017-12-05 12:06:29 +01001438 FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s (MVAR)\n",
Werner Lemberg447a0b62017-03-13 07:42:34 +01001439 (FT_Char)( value->tag >> 24 ),
1440 (FT_Char)( value->tag >> 16 ),
1441 (FT_Char)( value->tag >> 8 ),
1442 (FT_Char)( value->tag ),
1443 value->unmodified,
Werner Lemberg71fecc52017-12-05 12:06:29 +01001444 value->unmodified == 1 ? "" : "s",
1445 delta,
1446 delta == 1 ? "" : "s" ));
Werner Lembergd718ac42017-01-11 14:12:34 +01001447
Werner Lemberg447a0b62017-03-13 07:42:34 +01001448 /* since we handle both signed and unsigned values as FT_Short, */
1449 /* ensure proper overflow arithmetic */
1450 *p = (FT_Short)( value->unmodified + (FT_Short)delta );
Nikolaus Waxweilera6feefd2019-02-02 15:50:57 +00001451
1452 /* Treat hasc, hdsc and hlgp specially, see below. */
1453 if ( value->tag == MVAR_TAG_HASC )
1454 mvar_hasc_delta = (FT_Short)delta;
1455 else if ( value->tag == MVAR_TAG_HDSC )
1456 mvar_hdsc_delta = (FT_Short)delta;
1457 else if ( value->tag == MVAR_TAG_HLGP )
1458 mvar_hlgp_delta = (FT_Short)delta;
Werner Lemberg447a0b62017-03-13 07:42:34 +01001459 }
Werner Lembergd718ac42017-01-11 14:12:34 +01001460 }
1461
1462 /* adjust all derived values */
1463 {
Seigo Nonakafd9b54e2023-05-02 10:01:38 +09001464 FT_Service_MetricsVariations var =
1465 (FT_Service_MetricsVariations)face->face_var;
1466
Werner Lembergd718ac42017-01-11 14:12:34 +01001467 FT_Face root = &face->root;
1468
Nikolaus Waxweilera6feefd2019-02-02 15:50:57 +00001469 /*
1470 * Apply the deltas of hasc, hdsc and hlgp to the FT_Face's ascender,
1471 * descender and height attributes, no matter how they were originally
1472 * computed.
1473 *
1474 * (Code that ignores those and accesses the font's metrics values
1475 * directly is already served by the delta application code above.)
1476 *
1477 * The MVAR table supports variations for both typo and win metrics.
1478 * According to Behdad Esfahbod, the thinking of the working group was
1479 * that no one uses win metrics anymore for setting line metrics (the
1480 * specification even calls these metrics "horizontal clipping
1481 * ascent/descent", probably for their role on the Windows platform in
1482 * computing clipping boxes), and new fonts should use typo metrics, so
1483 * typo deltas should be applied to whatever sfnt_load_face decided the
1484 * line metrics should be.
1485 *
1486 * Before, the following led to different line metrics between default
1487 * outline and instances, visible when e.g. the default outlines were
1488 * used as the regular face and instances for everything else:
1489 *
1490 * 1. sfnt_load_face applied the hhea metrics by default.
1491 * 2. This code later applied the typo metrics by default, regardless of
1492 * whether they were actually changed or the font had the OS/2 table's
1493 * fsSelection's bit 7 (USE_TYPO_METRICS) set.
1494 */
1495 FT_Short current_line_gap = root->height - root->ascender +
1496 root->descender;
Werner Lembergb66d6a92019-02-06 07:38:25 +01001497
1498
Nikolaus Waxweilera6feefd2019-02-02 15:50:57 +00001499 root->ascender = root->ascender + mvar_hasc_delta;
1500 root->descender = root->descender + mvar_hdsc_delta;
1501 root->height = root->ascender - root->descender +
1502 current_line_gap + mvar_hlgp_delta;
Werner Lembergd718ac42017-01-11 14:12:34 +01001503
Werner Lemberg322b3be2017-01-15 13:57:25 +01001504 root->underline_position = face->postscript.underlinePosition -
1505 face->postscript.underlineThickness / 2;
1506 root->underline_thickness = face->postscript.underlineThickness;
1507
Seigo Nonakafd9b54e2023-05-02 10:01:38 +09001508 /* iterate over all FT_Size objects and call `var->size_reset' */
1509 /* to propagate the metrics changes */
1510 if ( var && var->size_reset )
1511 FT_List_Iterate( &root->sizes_list,
1512 ft_size_reset_iterator,
1513 (void*)var );
Werner Lembergd718ac42017-01-11 14:12:34 +01001514 }
1515 }
1516
1517
Werner Lemberg2b0b4222008-05-28 22:17:28 +00001518 typedef struct GX_GVar_Head_
1519 {
Werner Lemberg44bb3032004-04-25 20:15:11 +00001520 FT_Long version;
1521 FT_UShort axisCount;
1522 FT_UShort globalCoordCount;
1523 FT_ULong offsetToCoord;
1524 FT_UShort glyphCount;
1525 FT_UShort flags;
1526 FT_ULong offsetToData;
1527
1528 } GX_GVar_Head;
1529
1530
Werner Lemberg9ac90602018-06-03 09:01:17 +02001531 /**************************************************************************
1532 *
1533 * @Function:
1534 * ft_var_load_gvar
1535 *
1536 * @Description:
1537 * Parse the `gvar' table if present. If `fvar' is there, `gvar' had
1538 * better be there too.
1539 *
1540 * @InOut:
1541 * face ::
1542 * The font face.
1543 *
1544 * @Return:
1545 * FreeType error code. 0 means success.
1546 */
Werner Lemberg44bb3032004-04-25 20:15:11 +00001547 static FT_Error
1548 ft_var_load_gvar( TT_Face face )
1549 {
Werner Lemberg0aa36162015-03-08 22:50:37 +01001550 FT_Stream stream = FT_FACE_STREAM( face );
Werner Lemberg44bb3032004-04-25 20:15:11 +00001551 FT_Memory memory = stream->memory;
1552 GX_Blend blend = face->blend;
1553 FT_Error error;
1554 FT_UInt i, j;
1555 FT_ULong table_len;
1556 FT_ULong gvar_start;
Yamato, Masatake (大和正武)17f4b8b2004-05-07 08:59:29 +00001557 FT_ULong offsetToData;
Ben Wagner11975fe2020-02-29 20:18:00 +01001558 FT_ULong offsets_len;
Werner Lemberg44bb3032004-04-25 20:15:11 +00001559 GX_GVar_Head gvar_head;
1560
1561 static const FT_Frame_Field gvar_fields[] =
1562 {
1563
1564#undef FT_STRUCTURE
1565#define FT_STRUCTURE GX_GVar_Head
1566
1567 FT_FRAME_START( 20 ),
1568 FT_FRAME_LONG ( version ),
1569 FT_FRAME_USHORT( axisCount ),
1570 FT_FRAME_USHORT( globalCoordCount ),
1571 FT_FRAME_ULONG ( offsetToCoord ),
1572 FT_FRAME_USHORT( glyphCount ),
1573 FT_FRAME_USHORT( flags ),
1574 FT_FRAME_ULONG ( offsetToData ),
1575 FT_FRAME_END
1576 };
1577
Werner Lemberga6785602015-05-28 17:31:15 +02001578
Werner Lembergcdee7d12015-05-31 11:54:42 +02001579 FT_TRACE2(( "GVAR " ));
1580
Werner Lemberg5d664b62016-12-17 20:47:42 +01001581 if ( FT_SET_ERROR( face->goto_table( face,
1582 TTAG_gvar,
1583 stream,
1584 &table_len ) ) )
Werner Lembergcdee7d12015-05-31 11:54:42 +02001585 {
1586 FT_TRACE2(( "is missing\n" ));
Werner Lemberg44bb3032004-04-25 20:15:11 +00001587 goto Exit;
Werner Lembergcdee7d12015-05-31 11:54:42 +02001588 }
Werner Lemberg44bb3032004-04-25 20:15:11 +00001589
1590 gvar_start = FT_STREAM_POS( );
1591 if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
1592 goto Exit;
1593
Werner Lembergcdee7d12015-05-31 11:54:42 +02001594 if ( gvar_head.version != 0x00010000L )
Werner Lemberg44bb3032004-04-25 20:15:11 +00001595 {
Werner Lembergcdee7d12015-05-31 11:54:42 +02001596 FT_TRACE1(( "bad table version\n" ));
Werner Lemberg059bc332013-03-14 10:27:35 +01001597 error = FT_THROW( Invalid_Table );
Werner Lemberg44bb3032004-04-25 20:15:11 +00001598 goto Exit;
1599 }
1600
Werner Lembergcdee7d12015-05-31 11:54:42 +02001601 if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
1602 {
Werner Lemberga6adb252020-12-02 14:15:07 +01001603 FT_TRACE1(( "ft_var_load_gvar:"
1604 " number of axes in `gvar' and `cvar'\n" ));
1605 FT_TRACE1(( " table are different\n" ));
Werner Lembergcdee7d12015-05-31 11:54:42 +02001606 error = FT_THROW( Invalid_Table );
1607 goto Exit;
1608 }
1609
Werner Lembergda346732015-10-10 10:21:27 +02001610 /* rough sanity check, ignoring offsets */
1611 if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount >
1612 table_len / 2 )
1613 {
1614 FT_TRACE1(( "ft_var_load_gvar:"
1615 " invalid number of global coordinates\n" ));
1616 error = FT_THROW( Invalid_Table );
1617 goto Exit;
1618 }
1619
Ben Wagner11975fe2020-02-29 20:18:00 +01001620 /* offsets can be either 2 or 4 bytes */
1621 /* (one more offset than glyphs, to mark size of last) */
1622 offsets_len = ( gvar_head.glyphCount + 1 ) *
1623 ( ( gvar_head.flags & 1 ) ? 4L : 2L );
1624
1625 /* rough sanity check */
1626 if (offsets_len > table_len )
Werner Lembergf96094e2015-10-13 07:13:56 +02001627 {
1628 FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" ));
1629 error = FT_THROW( Invalid_Table );
1630 goto Exit;
1631 }
Werner Lembergda346732015-10-10 10:21:27 +02001632
1633 FT_TRACE2(( "loaded\n" ));
1634
Ben Wagner216e0772020-02-28 07:43:00 +01001635 blend->gvar_size = table_len;
1636 offsetToData = gvar_start + gvar_head.offsetToData;
Werner Lembergda346732015-10-10 10:21:27 +02001637
Werner Lemberg71fecc52017-12-05 12:06:29 +01001638 FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n",
Ben Wagner216e0772020-02-28 07:43:00 +01001639 gvar_head.globalCoordCount == 1 ? "is" : "are",
1640 gvar_head.globalCoordCount,
1641 gvar_head.globalCoordCount == 1 ? "" : "s" ));
Werner Lembergcdee7d12015-05-31 11:54:42 +02001642
Ben Wagner11975fe2020-02-29 20:18:00 +01001643 if ( FT_FRAME_ENTER( offsets_len ) )
Werner Lemberg44bb3032004-04-25 20:15:11 +00001644 goto Exit;
1645
Ben Wagner11975fe2020-02-29 20:18:00 +01001646 /* offsets (one more offset than glyphs, to mark size of last) */
Alexei Podtelezhnikovcb9e7b72021-11-06 22:59:31 -04001647 if ( FT_QNEW_ARRAY( blend->glyphoffsets, gvar_head.glyphCount + 1 ) )
Ben Wagner11975fe2020-02-29 20:18:00 +01001648 goto Fail2;
1649
Werner Lemberg44bb3032004-04-25 20:15:11 +00001650 if ( gvar_head.flags & 1 )
1651 {
Ben Wagner216e0772020-02-28 07:43:00 +01001652 FT_ULong limit = gvar_start + table_len;
1653 FT_ULong max_offset = 0;
Werner Lemberg53c5e4b2018-09-12 07:27:30 +02001654
1655
Ben Wagner216e0772020-02-28 07:43:00 +01001656 for ( i = 0; i <= gvar_head.glyphCount; i++ )
Werner Lemberg53c5e4b2018-09-12 07:27:30 +02001657 {
Werner Lemberg1f7a4e12015-02-17 10:17:56 +01001658 blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG();
Ben Wagner216e0772020-02-28 07:43:00 +01001659
1660 if ( max_offset <= blend->glyphoffsets[i] )
1661 max_offset = blend->glyphoffsets[i];
1662 else
Werner Lemberg53c5e4b2018-09-12 07:27:30 +02001663 {
1664 FT_TRACE2(( "ft_var_load_gvar:"
Ben Wagner216e0772020-02-28 07:43:00 +01001665 " glyph variation data offset %d not monotonic\n",
Werner Lemberg53c5e4b2018-09-12 07:27:30 +02001666 i ));
Ben Wagner216e0772020-02-28 07:43:00 +01001667 blend->glyphoffsets[i] = max_offset;
1668 }
1669
1670 /* use `<', not `<=' */
1671 if ( limit < blend->glyphoffsets[i] )
1672 {
1673 FT_TRACE2(( "ft_var_load_gvar:"
1674 " glyph variation data offset %d out of range\n",
1675 i ));
1676 blend->glyphoffsets[i] = limit;
Werner Lemberg53c5e4b2018-09-12 07:27:30 +02001677 }
1678 }
Werner Lemberg44bb3032004-04-25 20:15:11 +00001679 }
1680 else
1681 {
Ben Wagner216e0772020-02-28 07:43:00 +01001682 FT_ULong limit = gvar_start + table_len;
1683 FT_ULong max_offset = 0;
Werner Lemberg53c5e4b2018-09-12 07:27:30 +02001684
1685
Ben Wagner216e0772020-02-28 07:43:00 +01001686 for ( i = 0; i <= gvar_head.glyphCount; i++ )
Werner Lemberg53c5e4b2018-09-12 07:27:30 +02001687 {
Yamato, Masatake (大和正武)17f4b8b2004-05-07 08:59:29 +00001688 blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
Ben Wagner216e0772020-02-28 07:43:00 +01001689
1690 if ( max_offset <= blend->glyphoffsets[i] )
1691 max_offset = blend->glyphoffsets[i];
1692 else
Werner Lemberg53c5e4b2018-09-12 07:27:30 +02001693 {
1694 FT_TRACE2(( "ft_var_load_gvar:"
Ben Wagner216e0772020-02-28 07:43:00 +01001695 " glyph variation data offset %d not monotonic\n",
Werner Lemberg53c5e4b2018-09-12 07:27:30 +02001696 i ));
Ben Wagner216e0772020-02-28 07:43:00 +01001697 blend->glyphoffsets[i] = max_offset;
1698 }
1699
1700 /* use `<', not `<=' */
1701 if ( limit < blend->glyphoffsets[i] )
1702 {
1703 FT_TRACE2(( "ft_var_load_gvar:"
1704 " glyph variation data offset %d out of range\n",
1705 i ));
1706 blend->glyphoffsets[i] = limit;
Werner Lemberg53c5e4b2018-09-12 07:27:30 +02001707 }
1708 }
Werner Lemberg44bb3032004-04-25 20:15:11 +00001709 }
1710
Ben Wagner216e0772020-02-28 07:43:00 +01001711 blend->gv_glyphcnt = gvar_head.glyphCount;
1712
Ben Wagner11975fe2020-02-29 20:18:00 +01001713 FT_FRAME_EXIT();
1714
Ben Wagner216e0772020-02-28 07:43:00 +01001715 if ( gvar_head.globalCoordCount != 0 )
Werner Lemberg44bb3032004-04-25 20:15:11 +00001716 {
Ben Wagner216e0772020-02-28 07:43:00 +01001717 if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) ||
1718 FT_FRAME_ENTER( gvar_head.globalCoordCount *
1719 gvar_head.axisCount * 2L ) )
1720 {
1721 FT_TRACE2(( "ft_var_load_gvar:"
1722 " glyph variation shared tuples missing\n" ));
Ben Wagner11975fe2020-02-29 20:18:00 +01001723 goto Fail;
Ben Wagner216e0772020-02-28 07:43:00 +01001724 }
1725
Alexei Podtelezhnikovcb9e7b72021-11-06 22:59:31 -04001726 if ( FT_QNEW_ARRAY( blend->tuplecoords,
1727 gvar_head.axisCount * gvar_head.globalCoordCount ) )
Ben Wagner11975fe2020-02-29 20:18:00 +01001728 goto Fail2;
Werner Lemberg44bb3032004-04-25 20:15:11 +00001729
Ben Wagner11975fe2020-02-29 20:18:00 +01001730 for ( i = 0; i < gvar_head.globalCoordCount; i++ )
Werner Lembergcdee7d12015-05-31 11:54:42 +02001731 {
1732 FT_TRACE5(( " [ " ));
Werner Lemberg32950392016-02-15 12:54:40 +01001733 for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ )
Werner Lembergcdee7d12015-05-31 11:54:42 +02001734 {
Werner Lemberg44bb3032004-04-25 20:15:11 +00001735 blend->tuplecoords[i * gvar_head.axisCount + j] =
Werner Lembergdc39f762019-05-07 10:09:55 +02001736 FT_fdot14ToFixed( FT_GET_SHORT() );
Werner Lemberg84b0d992016-12-29 10:38:51 +01001737 FT_TRACE5(( "%.5f ",
Werner Lembergcdee7d12015-05-31 11:54:42 +02001738 blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 ));
1739 }
1740 FT_TRACE5(( "]\n" ));
1741 }
1742
Ben Wagner11975fe2020-02-29 20:18:00 +01001743 blend->tuplecount = gvar_head.globalCoordCount;
1744
Werner Lembergcdee7d12015-05-31 11:54:42 +02001745 FT_TRACE5(( "\n" ));
Werner Lemberg44bb3032004-04-25 20:15:11 +00001746
1747 FT_FRAME_EXIT();
1748 }
1749
1750 Exit:
1751 return error;
Ben Wagner11975fe2020-02-29 20:18:00 +01001752
1753 Fail2:
1754 FT_FRAME_EXIT();
1755
1756 Fail:
1757 FT_FREE( blend->glyphoffsets );
1758 blend->gv_glyphcnt = 0;
1759 goto Exit;
Werner Lemberg44bb3032004-04-25 20:15:11 +00001760 }
1761
1762
Werner Lemberg9ac90602018-06-03 09:01:17 +02001763 /**************************************************************************
1764 *
1765 * @Function:
1766 * ft_var_apply_tuple
1767 *
1768 * @Description:
1769 * Figure out whether a given tuple (design) applies to the current
1770 * blend, and if so, what is the scaling factor.
1771 *
1772 * @Input:
1773 * blend ::
1774 * The current blend of the font.
1775 *
1776 * tupleIndex ::
1777 * A flag saying whether this is an intermediate
1778 * tuple or not.
1779 *
1780 * tuple_coords ::
1781 * The coordinates of the tuple in normalized axis
1782 * units.
1783 *
1784 * im_start_coords ::
1785 * The initial coordinates where this tuple starts
1786 * to apply (for intermediate coordinates).
1787 *
1788 * im_end_coords ::
1789 * The final coordinates after which this tuple no
1790 * longer applies (for intermediate coordinates).
1791 *
1792 * @Return:
1793 * An FT_Fixed value containing the scaling factor.
1794 */
Werner Lemberg44bb3032004-04-25 20:15:11 +00001795 static FT_Fixed
1796 ft_var_apply_tuple( GX_Blend blend,
1797 FT_UShort tupleIndex,
1798 FT_Fixed* tuple_coords,
1799 FT_Fixed* im_start_coords,
1800 FT_Fixed* im_end_coords )
1801 {
1802 FT_UInt i;
Alexei Podtelezhnikov4ae9cbb2012-12-01 23:36:02 -05001803 FT_Fixed apply = 0x10000L;
Werner Lembergee6e92c2004-05-11 06:45:27 +00001804
Werner Lemberg44bb3032004-04-25 20:15:11 +00001805
Werner Lemberg0aa36162015-03-08 22:50:37 +01001806 for ( i = 0; i < blend->num_axis; i++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00001807 {
Alexei Podtelezhnikov547f82f2018-11-03 23:00:36 -04001808 FT_TRACE6(( " axis %d coordinate %.5f:\n",
Werner Lembergcdee7d12015-05-31 11:54:42 +02001809 i, blend->normalizedcoords[i] / 65536.0 ));
Werner Lemberg44bb3032004-04-25 20:15:11 +00001810
Werner Lembergcdee7d12015-05-31 11:54:42 +02001811 /* It's not clear why (for intermediate tuples) we don't need */
1812 /* to check against start/end -- the documentation says we don't. */
1813 /* Similarly, it's unclear why we don't need to scale along the */
1814 /* axis. */
1815
1816 if ( tuple_coords[i] == 0 )
1817 {
Werner Lemberg8cfc41a2020-07-25 12:23:22 +02001818 FT_TRACE6(( " tuple coordinate is zero, ignore\n" ));
Werner Lembergcdee7d12015-05-31 11:54:42 +02001819 continue;
1820 }
1821
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001822 if ( blend->normalizedcoords[i] == 0 )
Werner Lembergcdee7d12015-05-31 11:54:42 +02001823 {
1824 FT_TRACE6(( " axis coordinate is zero, stop\n" ));
1825 apply = 0;
1826 break;
1827 }
1828
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001829 if ( blend->normalizedcoords[i] == tuple_coords[i] )
Werner Lemberg44bb3032004-04-25 20:15:11 +00001830 {
Alexei Podtelezhnikov547f82f2018-11-03 23:00:36 -04001831 FT_TRACE6(( " tuple coordinate %.5f fits perfectly\n",
Werner Lembergcdee7d12015-05-31 11:54:42 +02001832 tuple_coords[i] / 65536.0 ));
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001833 /* `apply' does not change */
1834 continue;
Werner Lemberg44bb3032004-04-25 20:15:11 +00001835 }
1836
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001837 if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
Werner Lembergcdee7d12015-05-31 11:54:42 +02001838 {
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001839 /* not an intermediate tuple */
1840
1841 if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) ||
1842 blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) )
1843 {
Alexei Podtelezhnikov547f82f2018-11-03 23:00:36 -04001844 FT_TRACE6(( " tuple coordinate %.5f is exceeded, stop\n",
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001845 tuple_coords[i] / 65536.0 ));
1846 apply = 0;
1847 break;
1848 }
1849
Alexei Podtelezhnikov547f82f2018-11-03 23:00:36 -04001850 FT_TRACE6(( " tuple coordinate %.5f fits\n",
Werner Lembergcdee7d12015-05-31 11:54:42 +02001851 tuple_coords[i] / 65536.0 ));
Behdad Esfahbod74c0a722016-03-09 20:35:27 +01001852 apply = FT_MulDiv( apply,
1853 blend->normalizedcoords[i],
1854 tuple_coords[i] );
Werner Lembergcdee7d12015-05-31 11:54:42 +02001855 }
Werner Lemberg44bb3032004-04-25 20:15:11 +00001856 else
Werner Lembergcdee7d12015-05-31 11:54:42 +02001857 {
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001858 /* intermediate tuple */
1859
Alexei Podtelezhnikovd95a12b2018-11-03 23:02:58 -04001860 if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
1861 blend->normalizedcoords[i] >= im_end_coords[i] )
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001862 {
Alexei Podtelezhnikovcc288e32018-11-04 22:09:16 -05001863 FT_TRACE6(( " intermediate tuple range ]%.5f;%.5f[ is exceeded,"
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001864 " stop\n",
1865 im_start_coords[i] / 65536.0,
1866 im_end_coords[i] / 65536.0 ));
1867 apply = 0;
1868 break;
1869 }
1870
Alexei Podtelezhnikovcc288e32018-11-04 22:09:16 -05001871 FT_TRACE6(( " intermediate tuple range ]%.5f;%.5f[ fits\n",
Alexei Podtelezhnikov547f82f2018-11-03 23:00:36 -04001872 im_start_coords[i] / 65536.0,
1873 im_end_coords[i] / 65536.0 ));
1874 if ( blend->normalizedcoords[i] < tuple_coords[i] )
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001875 apply = FT_MulDiv( apply,
1876 blend->normalizedcoords[i] - im_start_coords[i],
1877 tuple_coords[i] - im_start_coords[i] );
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001878 else
Behdad Esfahbod99eff672016-04-16 07:32:23 +02001879 apply = FT_MulDiv( apply,
1880 im_end_coords[i] - blend->normalizedcoords[i],
1881 im_end_coords[i] - tuple_coords[i] );
Werner Lembergcdee7d12015-05-31 11:54:42 +02001882 }
Werner Lemberg44bb3032004-04-25 20:15:11 +00001883 }
1884
Werner Lemberg84b0d992016-12-29 10:38:51 +01001885 FT_TRACE6(( " apply factor is %.5f\n", apply / 65536.0 ));
Werner Lembergcdee7d12015-05-31 11:54:42 +02001886
Werner Lemberg44bb3032004-04-25 20:15:11 +00001887 return apply;
1888 }
1889
1890
Werner Lemberg47176962017-03-02 21:42:14 +01001891 /* convert from design coordinates to normalized coordinates */
1892
1893 static void
1894 ft_var_to_normalized( TT_Face face,
1895 FT_UInt num_coords,
1896 FT_Fixed* coords,
1897 FT_Fixed* normalized )
1898 {
1899 GX_Blend blend;
1900 FT_MM_Var* mmvar;
1901 FT_UInt i, j;
1902 FT_Var_Axis* a;
1903 GX_AVarSegment av;
1904
1905
1906 blend = face->blend;
1907 mmvar = blend->mmvar;
1908
1909 if ( num_coords > mmvar->num_axis )
1910 {
1911 FT_TRACE2(( "ft_var_to_normalized:"
1912 " only using first %d of %d coordinates\n",
1913 mmvar->num_axis, num_coords ));
1914 num_coords = mmvar->num_axis;
1915 }
1916
1917 /* Axis normalization is a two-stage process. First we normalize */
1918 /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
1919 /* Then, if there's an `avar' table, we renormalize this range. */
1920
Werner Lemberg47176962017-03-02 21:42:14 +01001921 a = mmvar->axis;
1922 for ( i = 0; i < num_coords; i++, a++ )
1923 {
1924 FT_Fixed coord = coords[i];
1925
1926
Werner Lembergef486532018-01-27 11:16:22 +01001927 FT_TRACE5(( " %d: %.5f\n", i, coord / 65536.0 ));
Werner Lemberg47176962017-03-02 21:42:14 +01001928 if ( coord > a->maximum || coord < a->minimum )
1929 {
Werner Lemberg272ae5e2020-12-07 10:29:24 +01001930 FT_TRACE1(( "ft_var_to_normalized: design coordinate %.5f\n",
1931 coord / 65536.0 ));
1932 FT_TRACE1(( " is out of range [%.5f;%.5f];"
1933 " clamping\n",
1934 a->minimum / 65536.0,
1935 a->maximum / 65536.0 ));
Werner Lemberg47176962017-03-02 21:42:14 +01001936 }
1937
Alexei Podtelezhnikovc5cd2a32021-11-10 08:46:26 -05001938 if ( coord > a->def )
1939 normalized[i] = coord >= a->maximum ? 0x10000L :
1940 FT_DivFix( SUB_LONG( coord, a->def ),
Werner Lemberg6ceeb872018-07-05 22:31:10 +02001941 SUB_LONG( a->maximum, a->def ) );
Alexei Podtelezhnikovc5cd2a32021-11-10 08:46:26 -05001942 else if ( coord < a->def )
1943 normalized[i] = coord <= a->minimum ? -0x10000L :
1944 FT_DivFix( SUB_LONG( coord, a->def ),
1945 SUB_LONG( a->def, a->minimum ) );
Werner Lemberg47176962017-03-02 21:42:14 +01001946 else
1947 normalized[i] = 0;
1948 }
1949
1950 FT_TRACE5(( "\n" ));
1951
1952 for ( ; i < mmvar->num_axis; i++ )
1953 normalized[i] = 0;
1954
1955 if ( blend->avar_segment )
1956 {
1957 FT_TRACE5(( "normalized design coordinates"
1958 " before applying `avar' data:\n" ));
1959
1960 av = blend->avar_segment;
1961 for ( i = 0; i < mmvar->num_axis; i++, av++ )
1962 {
1963 for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1964 {
1965 if ( normalized[i] < av->correspondence[j].fromCoord )
1966 {
1967 FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 ));
1968
1969 normalized[i] =
1970 FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
1971 av->correspondence[j].toCoord -
1972 av->correspondence[j - 1].toCoord,
1973 av->correspondence[j].fromCoord -
1974 av->correspondence[j - 1].fromCoord ) +
1975 av->correspondence[j - 1].toCoord;
1976 break;
1977 }
1978 }
1979 }
1980 }
1981 }
1982
1983
1984 /* convert from normalized coordinates to design coordinates */
1985
1986 static void
1987 ft_var_to_design( TT_Face face,
1988 FT_UInt num_coords,
1989 FT_Fixed* coords,
1990 FT_Fixed* design )
1991 {
1992 GX_Blend blend;
1993 FT_MM_Var* mmvar;
1994 FT_Var_Axis* a;
1995
1996 FT_UInt i, j, nc;
1997
1998
1999 blend = face->blend;
2000
2001 nc = num_coords;
2002 if ( num_coords > blend->num_axis )
2003 {
2004 FT_TRACE2(( "ft_var_to_design:"
2005 " only using first %d of %d coordinates\n",
2006 blend->num_axis, num_coords ));
2007 nc = blend->num_axis;
2008 }
2009
Werner Lemberg55d6abe2018-01-03 19:01:15 +01002010 for ( i = 0; i < nc; i++ )
2011 design[i] = coords[i];
Werner Lemberg47176962017-03-02 21:42:14 +01002012
2013 for ( ; i < num_coords; i++ )
2014 design[i] = 0;
2015
2016 if ( blend->avar_segment )
2017 {
2018 GX_AVarSegment av = blend->avar_segment;
2019
2020
2021 FT_TRACE5(( "design coordinates"
2022 " after removing `avar' distortion:\n" ));
2023
2024 for ( i = 0; i < nc; i++, av++ )
2025 {
2026 for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
2027 {
2028 if ( design[i] < av->correspondence[j].toCoord )
2029 {
2030 design[i] =
2031 FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord,
2032 av->correspondence[j].fromCoord -
2033 av->correspondence[j - 1].fromCoord,
2034 av->correspondence[j].toCoord -
2035 av->correspondence[j - 1].toCoord ) +
2036 av->correspondence[j - 1].fromCoord;
2037
2038 FT_TRACE5(( " %.5f\n", design[i] / 65536.0 ));
2039 break;
2040 }
2041 }
2042 }
2043 }
2044
2045 mmvar = blend->mmvar;
2046 a = mmvar->axis;
2047
2048 for ( i = 0; i < nc; i++, a++ )
2049 {
2050 if ( design[i] < 0 )
2051 design[i] = a->def + FT_MulFix( design[i],
2052 a->def - a->minimum );
2053 else if ( design[i] > 0 )
2054 design[i] = a->def + FT_MulFix( design[i],
2055 a->maximum - a->def );
2056 else
2057 design[i] = a->def;
2058 }
2059 }
2060
2061
Werner Lemberg44bb3032004-04-25 20:15:11 +00002062 /*************************************************************************/
2063 /*************************************************************************/
2064 /***** *****/
2065 /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/
2066 /***** *****/
2067 /*************************************************************************/
2068 /*************************************************************************/
2069
2070
Werner Lemberg2b0b4222008-05-28 22:17:28 +00002071 typedef struct GX_FVar_Head_
2072 {
Werner Lemberg44bb3032004-04-25 20:15:11 +00002073 FT_Long version;
2074 FT_UShort offsetToData;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002075 FT_UShort axisCount;
2076 FT_UShort axisSize;
2077 FT_UShort instanceCount;
2078 FT_UShort instanceSize;
2079
2080 } GX_FVar_Head;
2081
2082
Werner Lemberg2b0b4222008-05-28 22:17:28 +00002083 typedef struct fvar_axis_
2084 {
Werner Lemberg44bb3032004-04-25 20:15:11 +00002085 FT_ULong axisTag;
Werner Lemberg1f7a4e12015-02-17 10:17:56 +01002086 FT_Fixed minValue;
2087 FT_Fixed defaultValue;
2088 FT_Fixed maxValue;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002089 FT_UShort flags;
2090 FT_UShort nameID;
2091
2092 } GX_FVar_Axis;
2093
2094
Werner Lemberg9ac90602018-06-03 09:01:17 +02002095 /**************************************************************************
2096 *
2097 * @Function:
2098 * TT_Get_MM_Var
2099 *
2100 * @Description:
2101 * Check that the font's `fvar' table is valid, parse it, and return
2102 * those data. It also loads (and parses) the `MVAR' table, if
2103 * possible.
2104 *
2105 * @InOut:
2106 * face ::
2107 * The font face.
2108 * TT_Get_MM_Var initializes the blend structure.
2109 *
2110 * @Output:
2111 * master ::
2112 * The `fvar' data (must be freed by caller). Can be NULL,
2113 * which makes this function simply load MM support.
2114 *
2115 * @Return:
2116 * FreeType error code. 0 means success.
2117 */
Werner Lemberg44bb3032004-04-25 20:15:11 +00002118 FT_LOCAL_DEF( FT_Error )
2119 TT_Get_MM_Var( TT_Face face,
2120 FT_MM_Var* *master )
2121 {
Werner Lembergb19cdc92017-09-21 11:02:35 +02002122 FT_Stream stream = face->root.stream;
2123 FT_Memory memory = face->root.memory;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002124 FT_ULong table_len;
Werner Lembergb19cdc92017-09-21 11:02:35 +02002125 FT_Error error = FT_Err_Ok;
2126 FT_ULong fvar_start = 0;
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002127 FT_UInt i, j;
suzuki toshiyae62c8762011-06-15 02:48:33 +09002128 FT_MM_Var* mmvar = NULL;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002129 FT_Fixed* next_coords;
Werner Lemberg588e38e2017-03-04 11:04:24 +01002130 FT_Fixed* nsc;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002131 FT_String* next_name;
2132 FT_Var_Axis* a;
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002133 FT_Fixed* c;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002134 FT_Var_Named_Style* ns;
Werner Lemberg2149b512021-03-13 19:08:09 +01002135 GX_FVar_Head fvar_head = { 0, 0, 0, 0, 0, 0 };
Werner Lembergb19cdc92017-09-21 11:02:35 +02002136 FT_Bool usePsName = 0;
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002137 FT_UInt num_instances;
Werner Lembergb19cdc92017-09-21 11:02:35 +02002138 FT_UInt num_axes;
Werner Lembergf43b3092017-08-05 18:22:17 +02002139 FT_UShort* axis_flags;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002140
Werner Lembergb19cdc92017-09-21 11:02:35 +02002141 FT_Offset mmvar_size;
2142 FT_Offset axis_flags_size;
2143 FT_Offset axis_size;
2144 FT_Offset namedstyle_size;
2145 FT_Offset next_coords_size;
2146 FT_Offset next_name_size;
2147
2148 FT_Bool need_init;
2149
Werner Lemberg44bb3032004-04-25 20:15:11 +00002150 static const FT_Frame_Field fvar_fields[] =
2151 {
2152
2153#undef FT_STRUCTURE
2154#define FT_STRUCTURE GX_FVar_Head
2155
Werner Lemberg3bcad432004-05-06 11:48:35 +00002156 FT_FRAME_START( 16 ),
Werner Lembergf5020922017-01-23 11:47:40 +01002157 FT_FRAME_LONG ( version ),
2158 FT_FRAME_USHORT ( offsetToData ),
2159 FT_FRAME_SKIP_SHORT,
2160 FT_FRAME_USHORT ( axisCount ),
2161 FT_FRAME_USHORT ( axisSize ),
2162 FT_FRAME_USHORT ( instanceCount ),
2163 FT_FRAME_USHORT ( instanceSize ),
Werner Lemberg44bb3032004-04-25 20:15:11 +00002164 FT_FRAME_END
2165 };
2166
2167 static const FT_Frame_Field fvaraxis_fields[] =
2168 {
2169
2170#undef FT_STRUCTURE
2171#define FT_STRUCTURE GX_FVar_Axis
2172
2173 FT_FRAME_START( 20 ),
2174 FT_FRAME_ULONG ( axisTag ),
Werner Lemberg1f7a4e12015-02-17 10:17:56 +01002175 FT_FRAME_LONG ( minValue ),
2176 FT_FRAME_LONG ( defaultValue ),
2177 FT_FRAME_LONG ( maxValue ),
Werner Lemberg44bb3032004-04-25 20:15:11 +00002178 FT_FRAME_USHORT( flags ),
2179 FT_FRAME_USHORT( nameID ),
2180 FT_FRAME_END
2181 };
2182
2183
Werner Lemberg0aa36162015-03-08 22:50:37 +01002184 /* read the font data and set up the internal representation */
2185 /* if not already done */
2186
Werner Lembergb19cdc92017-09-21 11:02:35 +02002187 need_init = !face->blend;
2188
2189 if ( need_init )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002190 {
Werner Lembergcdee7d12015-05-31 11:54:42 +02002191 FT_TRACE2(( "FVAR " ));
2192
Werner Lemberg44bb3032004-04-25 20:15:11 +00002193 /* both `fvar' and `gvar' must be present */
Werner Lemberg5d664b62016-12-17 20:47:42 +01002194 if ( FT_SET_ERROR( face->goto_table( face, TTAG_gvar,
2195 stream, &table_len ) ) )
Werner Lembergcdee7d12015-05-31 11:54:42 +02002196 {
Dave Arnoldedf40142016-12-15 21:56:44 +01002197 /* CFF2 is an alternate to gvar here */
Werner Lemberg5d664b62016-12-17 20:47:42 +01002198 if ( FT_SET_ERROR( face->goto_table( face, TTAG_CFF2,
2199 stream, &table_len ) ) )
Dave Arnoldedf40142016-12-15 21:56:44 +01002200 {
Werner Lemberga6adb252020-12-02 14:15:07 +01002201 FT_TRACE1(( "\n" ));
2202 FT_TRACE1(( "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" ));
Dave Arnoldedf40142016-12-15 21:56:44 +01002203 goto Exit;
2204 }
Werner Lembergcdee7d12015-05-31 11:54:42 +02002205 }
David Turnerc28b8c12004-06-24 20:18:24 +00002206
Werner Lemberg5d664b62016-12-17 20:47:42 +01002207 if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar,
2208 stream, &table_len ) ) )
Werner Lembergcdee7d12015-05-31 11:54:42 +02002209 {
2210 FT_TRACE1(( "is missing\n" ));
Werner Lemberg44bb3032004-04-25 20:15:11 +00002211 goto Exit;
Werner Lembergcdee7d12015-05-31 11:54:42 +02002212 }
Werner Lemberg44bb3032004-04-25 20:15:11 +00002213
2214 fvar_start = FT_STREAM_POS( );
Werner Lembergee6e92c2004-05-11 06:45:27 +00002215
Werner Lemberg25f3ac22016-12-18 15:50:18 +01002216 /* the validity of the `fvar' header data was already checked */
2217 /* in function `sfnt_init_face' */
Werner Lemberg44bb3032004-04-25 20:15:11 +00002218 if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
2219 goto Exit;
2220
Werner Lemberg25f3ac22016-12-18 15:50:18 +01002221 usePsName = FT_BOOL( fvar_head.instanceSize ==
2222 6 + 4 * fvar_head.axisCount );
Dave Arnold097cd872016-12-15 12:58:26 +01002223
Werner Lembergcdee7d12015-05-31 11:54:42 +02002224 FT_TRACE2(( "loaded\n" ));
2225
Werner Lemberg50f693a2018-01-27 09:33:17 +01002226 FT_TRACE5(( "%d variation ax%s\n",
2227 fvar_head.axisCount,
2228 fvar_head.axisCount == 1 ? "is" : "es" ));
Werner Lembergcdee7d12015-05-31 11:54:42 +02002229
David Turner9ca78252006-05-02 09:00:29 +00002230 if ( FT_NEW( face->blend ) )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002231 goto Exit;
2232
Werner Lemberg994eb2b2018-01-26 23:17:43 +01002233 num_axes = fvar_head.axisCount;
2234 face->blend->num_axis = num_axes;
Werner Lembergb19cdc92017-09-21 11:02:35 +02002235 }
2236 else
2237 num_axes = face->blend->num_axis;
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002238
Werner Lembergb19cdc92017-09-21 11:02:35 +02002239 /* `num_instances' holds the number of all named instances, */
2240 /* including the default instance which might be missing */
2241 /* in fvar's table of named instances */
Werner Lembergf89c67f2017-10-07 13:10:53 +02002242 num_instances = (FT_UInt)face->root.style_flags >> 16;
Werner Lemberg3b3cb322017-09-21 09:03:20 +02002243
Werner Lembergb19cdc92017-09-21 11:02:35 +02002244 /* prepare storage area for MM data; this cannot overflow */
2245 /* 32-bit arithmetic because of the size limits used in the */
2246 /* `fvar' table validity check in `sfnt_init_face' */
2247
2248 /* the various `*_size' variables, which we also use as */
Werner Lemberg7d1d3b92019-08-26 09:08:56 +02002249 /* offsets into the `mmvar' array, must be multiples of the */
Werner Lembergb19cdc92017-09-21 11:02:35 +02002250 /* pointer size (except the last one); without such an */
2251 /* alignment there might be runtime errors due to */
2252 /* misaligned addresses */
Werner Lemberg3b3cb322017-09-21 09:03:20 +02002253#undef ALIGN_SIZE
2254#define ALIGN_SIZE( n ) \
2255 ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) )
2256
Werner Lembergb19cdc92017-09-21 11:02:35 +02002257 mmvar_size = ALIGN_SIZE( sizeof ( FT_MM_Var ) );
2258 axis_flags_size = ALIGN_SIZE( num_axes *
2259 sizeof ( FT_UShort ) );
2260 axis_size = ALIGN_SIZE( num_axes *
2261 sizeof ( FT_Var_Axis ) );
2262 namedstyle_size = ALIGN_SIZE( num_instances *
2263 sizeof ( FT_Var_Named_Style ) );
2264 next_coords_size = ALIGN_SIZE( num_instances *
2265 num_axes *
2266 sizeof ( FT_Fixed ) );
2267 next_name_size = num_axes * 5;
Werner Lemberg3b3cb322017-09-21 09:03:20 +02002268
Werner Lembergb19cdc92017-09-21 11:02:35 +02002269 if ( need_init )
2270 {
Werner Lemberg3b3cb322017-09-21 09:03:20 +02002271 face->blend->mmvar_len = mmvar_size +
2272 axis_flags_size +
2273 axis_size +
2274 namedstyle_size +
2275 next_coords_size +
2276 next_name_size;
David Turner9ca78252006-05-02 09:00:29 +00002277
Werner Lemberg44bb3032004-04-25 20:15:11 +00002278 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2279 goto Exit;
2280 face->blend->mmvar = mmvar;
2281
Werner Lemberg0aa36162015-03-08 22:50:37 +01002282 /* set up pointers and offsets into the `mmvar' array; */
2283 /* the data gets filled in later on */
2284
Werner Lemberg44bb3032004-04-25 20:15:11 +00002285 mmvar->num_axis =
Werner Lembergb19cdc92017-09-21 11:02:35 +02002286 num_axes;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002287 mmvar->num_designs =
Werner Lemberg45392b72013-06-05 13:43:20 +02002288 ~0U; /* meaningless in this context; each glyph */
Werner Lemberg44bb3032004-04-25 20:15:11 +00002289 /* may have a different number of designs */
2290 /* (or tuples, as called by Apple) */
2291 mmvar->num_namedstyles =
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002292 num_instances;
Werner Lembergf43b3092017-08-05 18:22:17 +02002293
2294 /* alas, no public field in `FT_Var_Axis' for axis flags */
2295 axis_flags =
Werner Lemberg3b3cb322017-09-21 09:03:20 +02002296 (FT_UShort*)( (char*)mmvar + mmvar_size );
Werner Lemberg44bb3032004-04-25 20:15:11 +00002297 mmvar->axis =
Werner Lemberg3b3cb322017-09-21 09:03:20 +02002298 (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
Werner Lemberg44bb3032004-04-25 20:15:11 +00002299 mmvar->namedstyle =
Werner Lemberg3b3cb322017-09-21 09:03:20 +02002300 (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size );
Werner Lemberg44bb3032004-04-25 20:15:11 +00002301
Werner Lemberg3b3cb322017-09-21 09:03:20 +02002302 next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2303 namedstyle_size );
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002304 for ( i = 0; i < num_instances; i++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002305 {
2306 mmvar->namedstyle[i].coords = next_coords;
Werner Lembergb19cdc92017-09-21 11:02:35 +02002307 next_coords += num_axes;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002308 }
2309
Werner Lemberg3b3cb322017-09-21 09:03:20 +02002310 next_name = (FT_String*)( (char*)mmvar->namedstyle +
2311 namedstyle_size + next_coords_size );
Werner Lembergb19cdc92017-09-21 11:02:35 +02002312 for ( i = 0; i < num_axes; i++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002313 {
2314 mmvar->axis[i].name = next_name;
2315 next_name += 5;
2316 }
2317
Werner Lemberg0aa36162015-03-08 22:50:37 +01002318 /* now fill in the data */
2319
Werner Lemberg44bb3032004-04-25 20:15:11 +00002320 if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
2321 goto Exit;
2322
2323 a = mmvar->axis;
Werner Lembergb19cdc92017-09-21 11:02:35 +02002324 for ( i = 0; i < num_axes; i++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002325 {
2326 GX_FVar_Axis axis_rec;
2327
Werner Lemberg50f693a2018-01-27 09:33:17 +01002328#ifdef FT_DEBUG_LEVEL_TRACE
2329 int invalid = 0;
2330#endif
2331
Werner Lemberg44bb3032004-04-25 20:15:11 +00002332
2333 if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
2334 goto Exit;
2335 a->tag = axis_rec.axisTag;
Werner Lemberg1f7a4e12015-02-17 10:17:56 +01002336 a->minimum = axis_rec.minValue;
2337 a->def = axis_rec.defaultValue;
2338 a->maximum = axis_rec.maxValue;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002339 a->strid = axis_rec.nameID;
2340
Werner Lemberg3273a992004-07-31 23:04:23 +00002341 a->name[0] = (FT_String)( a->tag >> 24 );
2342 a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
2343 a->name[2] = (FT_String)( ( a->tag >> 8 ) & 0xFF );
2344 a->name[3] = (FT_String)( ( a->tag ) & 0xFF );
Werner Lemberg0aa36162015-03-08 22:50:37 +01002345 a->name[4] = '\0';
Werner Lemberg44bb3032004-04-25 20:15:11 +00002346
Werner Lembergf43b3092017-08-05 18:22:17 +02002347 *axis_flags = axis_rec.flags;
2348
Werner Lemberg91fc3bd2016-12-29 21:34:46 +01002349 if ( a->minimum > a->def ||
2350 a->def > a->maximum )
2351 {
Werner Lemberg91fc3bd2016-12-29 21:34:46 +01002352 a->minimum = a->def;
2353 a->maximum = a->def;
Werner Lemberg50f693a2018-01-27 09:33:17 +01002354
2355#ifdef FT_DEBUG_LEVEL_TRACE
2356 invalid = 1;
2357#endif
Werner Lemberg91fc3bd2016-12-29 21:34:46 +01002358 }
2359
Werner Lemberg50f693a2018-01-27 09:33:17 +01002360#ifdef FT_DEBUG_LEVEL_TRACE
2361 if ( i == 0 )
2362 FT_TRACE5(( " idx tag "
2363 /* " XXX `XXXX'" */
2364 " minimum default maximum flags\n" ));
2365 /* " XXXX.XXXXX XXXX.XXXXX XXXX.XXXXX 0xXXXX" */
2366
2367 FT_TRACE5(( " %3d `%s'"
2368 " %10.5f %10.5f %10.5f 0x%04X%s\n",
2369 i,
Werner Lembergcdee7d12015-05-31 11:54:42 +02002370 a->name,
2371 a->minimum / 65536.0,
2372 a->def / 65536.0,
Werner Lembergf43b3092017-08-05 18:22:17 +02002373 a->maximum / 65536.0,
Werner Lemberg50f693a2018-01-27 09:33:17 +01002374 *axis_flags,
2375 invalid ? " (invalid, disabled)" : "" ));
2376#endif
Werner Lembergcdee7d12015-05-31 11:54:42 +02002377
Werner Lemberg0aa36162015-03-08 22:50:37 +01002378 a++;
Werner Lembergf43b3092017-08-05 18:22:17 +02002379 axis_flags++;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002380 }
2381
Werner Lembergcdee7d12015-05-31 11:54:42 +02002382 FT_TRACE5(( "\n" ));
2383
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002384 /* named instance coordinates are stored as design coordinates; */
2385 /* we have to convert them to normalized coordinates also */
2386 if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords,
Werner Lembergb19cdc92017-09-21 11:02:35 +02002387 num_axes * num_instances ) )
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002388 goto Exit;
Werner Lemberg588e38e2017-03-04 11:04:24 +01002389
Werner Lemberg4a629222017-03-12 10:19:53 +01002390 if ( fvar_head.instanceCount && !face->blend->avar_loaded )
Behdad Esfahbod55bbb982017-08-01 09:17:02 +02002391 {
2392 FT_ULong offset = FT_STREAM_POS();
2393
2394
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002395 ft_var_load_avar( face );
Werner Lemberg588e38e2017-03-04 11:04:24 +01002396
Behdad Esfahbod55bbb982017-08-01 09:17:02 +02002397 if ( FT_STREAM_SEEK( offset ) )
2398 goto Exit;
2399 }
2400
Werner Lembergef486532018-01-27 11:16:22 +01002401 FT_TRACE5(( "%d instance%s\n",
2402 fvar_head.instanceCount,
2403 fvar_head.instanceCount == 1 ? "" : "s" ));
2404
Werner Lemberg588e38e2017-03-04 11:04:24 +01002405 ns = mmvar->namedstyle;
2406 nsc = face->blend->normalized_stylecoords;
Werner Lemberg0aa36162015-03-08 22:50:37 +01002407 for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002408 {
Dave Arnold097cd872016-12-15 12:58:26 +01002409 /* PostScript names add 2 bytes to the instance record size */
2410 if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) +
Werner Lembergb19cdc92017-09-21 11:02:35 +02002411 4L * num_axes ) )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002412 goto Exit;
2413
2414 ns->strid = FT_GET_USHORT();
2415 (void) /* flags = */ FT_GET_USHORT();
2416
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002417 c = ns->coords;
Werner Lembergb19cdc92017-09-21 11:02:35 +02002418 for ( j = 0; j < num_axes; j++, c++ )
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002419 *c = FT_GET_LONG();
Werner Lemberg44bb3032004-04-25 20:15:11 +00002420
Werner Lemberg7e508242017-08-01 12:44:35 +02002421 /* valid psid values are 6, [256;32767], and 0xFFFF */
Dave Arnold097cd872016-12-15 12:58:26 +01002422 if ( usePsName )
2423 ns->psid = FT_GET_USHORT();
Werner Lemberg7e508242017-08-01 12:44:35 +02002424 else
2425 ns->psid = 0xFFFF;
Dave Arnold097cd872016-12-15 12:58:26 +01002426
Werner Lembergef486532018-01-27 11:16:22 +01002427#ifdef FT_DEBUG_LEVEL_TRACE
2428 {
2429 SFNT_Service sfnt = (SFNT_Service)face->sfnt;
2430
2431 FT_String* strname = NULL;
2432 FT_String* psname = NULL;
2433
2434 FT_ULong pos;
2435
2436
2437 pos = FT_STREAM_POS();
2438
2439 if ( ns->strid != 0xFFFF )
2440 {
2441 (void)sfnt->get_name( face,
2442 (FT_UShort)ns->strid,
2443 &strname );
2444 if ( strname && !ft_strcmp( strname, ".notdef" ) )
2445 strname = NULL;
2446 }
2447
2448 if ( ns->psid != 0xFFFF )
2449 {
2450 (void)sfnt->get_name( face,
2451 (FT_UShort)ns->psid,
2452 &psname );
2453 if ( psname && !ft_strcmp( psname, ".notdef" ) )
2454 psname = NULL;
2455 }
2456
2457 (void)FT_STREAM_SEEK( pos );
2458
2459 FT_TRACE5(( " instance %d (%s%s%s, %s%s%s)\n",
2460 i,
2461 strname ? "name: `" : "",
2462 strname ? strname : "unnamed",
2463 strname ? "'" : "",
2464 psname ? "PS name: `" : "",
2465 psname ? psname : "no PS name",
2466 psname ? "'" : "" ));
Werner Lemberg597cb3b2018-03-30 13:46:03 +02002467
2468 FT_FREE( strname );
2469 FT_FREE( psname );
Werner Lembergef486532018-01-27 11:16:22 +01002470 }
2471#endif /* FT_DEBUG_LEVEL_TRACE */
2472
Werner Lembergb19cdc92017-09-21 11:02:35 +02002473 ft_var_to_normalized( face, num_axes, ns->coords, nsc );
2474 nsc += num_axes;
Werner Lemberg588e38e2017-03-04 11:04:24 +01002475
Werner Lemberg44bb3032004-04-25 20:15:11 +00002476 FT_FRAME_EXIT();
2477 }
Werner Lemberg07ee1d22017-01-11 12:50:51 +01002478
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002479 if ( num_instances != fvar_head.instanceCount )
2480 {
2481 SFNT_Service sfnt = (SFNT_Service)face->sfnt;
2482
Werner Lemberg1ede3672017-03-30 00:26:31 +02002483 FT_Int found, dummy1, dummy2;
Behdad Esfahbod55bbb982017-08-01 09:17:02 +02002484 FT_UInt strid = ~0U;
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002485
2486
2487 /* the default instance is missing in array the */
2488 /* of named instances; try to synthesize an entry */
2489 found = sfnt->get_name_id( face,
2490 TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY,
Werner Lemberg1ede3672017-03-30 00:26:31 +02002491 &dummy1,
2492 &dummy2 );
2493 if ( found )
2494 strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY;
2495 else
2496 {
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002497 found = sfnt->get_name_id( face,
2498 TT_NAME_ID_FONT_SUBFAMILY,
Werner Lemberg1ede3672017-03-30 00:26:31 +02002499 &dummy1,
2500 &dummy2 );
2501 if ( found )
2502 strid = TT_NAME_ID_FONT_SUBFAMILY;
2503 }
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002504
2505 if ( found )
2506 {
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002507 found = sfnt->get_name_id( face,
2508 TT_NAME_ID_PS_NAME,
Werner Lemberg1ede3672017-03-30 00:26:31 +02002509 &dummy1,
2510 &dummy2 );
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002511 if ( found )
2512 {
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002513 FT_TRACE5(( "TT_Get_MM_Var:"
2514 " Adding default instance to named instances\n" ));
2515
2516 ns = &mmvar->namedstyle[fvar_head.instanceCount];
2517
Werner Lemberg1ede3672017-03-30 00:26:31 +02002518 ns->strid = strid;
2519 ns->psid = TT_NAME_ID_PS_NAME;
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002520
2521 a = mmvar->axis;
2522 c = ns->coords;
Werner Lembergb19cdc92017-09-21 11:02:35 +02002523 for ( j = 0; j < num_axes; j++, a++, c++ )
Werner Lemberg27fee7f2017-03-06 20:45:44 +01002524 *c = a->def;
2525 }
2526 }
2527 }
2528
Werner Lemberg07ee1d22017-01-11 12:50:51 +01002529 ft_var_load_mvar( face );
Werner Lemberg44bb3032004-04-25 20:15:11 +00002530 }
2531
Werner Lemberg0aa36162015-03-08 22:50:37 +01002532 /* fill the output array if requested */
2533
Werner Lemberg4441f7b2016-12-26 17:08:17 +01002534 if ( master )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002535 {
2536 FT_UInt n;
2537
2538
2539 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2540 goto Exit;
2541 FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
2542
Werner Lembergf43b3092017-08-05 18:22:17 +02002543 axis_flags =
Werner Lembergb19cdc92017-09-21 11:02:35 +02002544 (FT_UShort*)( (char*)mmvar + mmvar_size );
Werner Lemberg44bb3032004-04-25 20:15:11 +00002545 mmvar->axis =
Werner Lembergb19cdc92017-09-21 11:02:35 +02002546 (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
Werner Lemberg44bb3032004-04-25 20:15:11 +00002547 mmvar->namedstyle =
Werner Lembergb19cdc92017-09-21 11:02:35 +02002548 (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size );
Werner Lembergf43b3092017-08-05 18:22:17 +02002549
Werner Lembergb19cdc92017-09-21 11:02:35 +02002550 next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2551 namedstyle_size );
Werner Lemberg0aa36162015-03-08 22:50:37 +01002552 for ( n = 0; n < mmvar->num_namedstyles; n++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002553 {
2554 mmvar->namedstyle[n].coords = next_coords;
Werner Lembergb19cdc92017-09-21 11:02:35 +02002555 next_coords += num_axes;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002556 }
2557
Werner Lemberg0aa36162015-03-08 22:50:37 +01002558 a = mmvar->axis;
Werner Lembergb19cdc92017-09-21 11:02:35 +02002559 next_name = (FT_String*)( (char*)mmvar->namedstyle +
2560 namedstyle_size + next_coords_size );
2561 for ( n = 0; n < num_axes; n++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002562 {
2563 a->name = next_name;
2564
2565 /* standard PostScript names for some standard apple tags */
2566 if ( a->tag == TTAG_wght )
Werner Lemberg0aa36162015-03-08 22:50:37 +01002567 a->name = (char*)"Weight";
Werner Lemberg44bb3032004-04-25 20:15:11 +00002568 else if ( a->tag == TTAG_wdth )
Werner Lemberg0aa36162015-03-08 22:50:37 +01002569 a->name = (char*)"Width";
Werner Lemberg44bb3032004-04-25 20:15:11 +00002570 else if ( a->tag == TTAG_opsz )
Werner Lemberg0aa36162015-03-08 22:50:37 +01002571 a->name = (char*)"OpticalSize";
Werner Lemberg44bb3032004-04-25 20:15:11 +00002572 else if ( a->tag == TTAG_slnt )
Werner Lemberg0aa36162015-03-08 22:50:37 +01002573 a->name = (char*)"Slant";
Werner Lemberg44bb3032004-04-25 20:15:11 +00002574
2575 next_name += 5;
Werner Lemberg0aa36162015-03-08 22:50:37 +01002576 a++;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002577 }
2578
2579 *master = mmvar;
2580 }
2581
2582 Exit:
2583 return error;
2584 }
2585
2586
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002587 static FT_Error
2588 tt_set_mm_blend( TT_Face face,
Werner Lemberg44bb3032004-04-25 20:15:11 +00002589 FT_UInt num_coords,
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002590 FT_Fixed* coords,
2591 FT_Bool set_design_coords )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002592 {
Werner Lemberge3c93012013-03-14 11:21:17 +01002593 FT_Error error = FT_Err_Ok;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002594 GX_Blend blend;
2595 FT_MM_Var* mmvar;
Werner Lembergf89c67f2017-10-07 13:10:53 +02002596 FT_UInt i;
Werner Lemberga9331c02017-05-27 15:50:25 +02002597
Werner Lemberg8c92f762017-10-07 12:12:49 +02002598 FT_Bool all_design_coords = FALSE;
Werner Lemberga9331c02017-05-27 15:50:25 +02002599
Werner Lemberg44bb3032004-04-25 20:15:11 +00002600 FT_Memory memory = face->root.memory;
2601
2602 enum
2603 {
2604 mcvt_retain,
2605 mcvt_modify,
2606 mcvt_load
2607
2608 } manageCvt;
2609
2610
2611 face->doblend = FALSE;
2612
Werner Lemberg4441f7b2016-12-26 17:08:17 +01002613 if ( !face->blend )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002614 {
Werner Lemberg5d664b62016-12-17 20:47:42 +01002615 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002616 goto Exit;
2617 }
2618
2619 blend = face->blend;
2620 mmvar = blend->mmvar;
2621
Werner Lemberg10e2bb82015-03-29 13:32:47 +02002622 if ( num_coords > mmvar->num_axis )
Werner Lembergcdee7d12015-05-31 11:54:42 +02002623 {
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002624 FT_TRACE2(( "TT_Set_MM_Blend:"
2625 " only using first %d of %d coordinates\n",
Werner Lembergcdee7d12015-05-31 11:54:42 +02002626 mmvar->num_axis, num_coords ));
Werner Lemberg10e2bb82015-03-29 13:32:47 +02002627 num_coords = mmvar->num_axis;
Werner Lembergcdee7d12015-05-31 11:54:42 +02002628 }
2629
Werner Lemberga6adb252020-12-02 14:15:07 +01002630 FT_TRACE5(( "TT_Set_MM_Blend:\n" ));
2631 FT_TRACE5(( " normalized design coordinates:\n" ));
Werner Lemberg44bb3032004-04-25 20:15:11 +00002632
Werner Lemberg0aa36162015-03-08 22:50:37 +01002633 for ( i = 0; i < num_coords; i++ )
Werner Lembergcdee7d12015-05-31 11:54:42 +02002634 {
Werner Lembergf438e062018-01-27 14:39:15 +01002635 FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 ));
Werner Lemberg44bb3032004-04-25 20:15:11 +00002636 if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
2637 {
Werner Lemberga6adb252020-12-02 14:15:07 +01002638 FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n",
Werner Lembergcdee7d12015-05-31 11:54:42 +02002639 coords[i] / 65536.0 ));
Werner Lemberga6adb252020-12-02 14:15:07 +01002640 FT_TRACE1(( " is out of range [-1;1]\n" ));
Werner Lemberg059bc332013-03-14 10:27:35 +01002641 error = FT_THROW( Invalid_Argument );
Werner Lemberg44bb3032004-04-25 20:15:11 +00002642 goto Exit;
2643 }
Werner Lembergcdee7d12015-05-31 11:54:42 +02002644 }
2645
2646 FT_TRACE5(( "\n" ));
Werner Lemberg44bb3032004-04-25 20:15:11 +00002647
Werner Lembergf0cee1a2017-02-23 08:23:39 +01002648 if ( !face->is_cff2 && !blend->glyphoffsets )
Werner Lemberg5d664b62016-12-17 20:47:42 +01002649 if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002650 goto Exit;
2651
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002652 if ( !blend->coords )
2653 {
2654 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2655 goto Exit;
Werner Lemberga9331c02017-05-27 15:50:25 +02002656
2657 /* the first time we have to compute all design coordinates */
2658 all_design_coords = TRUE;
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002659 }
2660
Werner Lemberg4441f7b2016-12-26 17:08:17 +01002661 if ( !blend->normalizedcoords )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002662 {
Werner Lemberg10e2bb82015-03-29 13:32:47 +02002663 if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002664 goto Exit;
2665
2666 manageCvt = mcvt_modify;
2667
2668 /* If we have not set the blend coordinates before this, then the */
2669 /* cvt table will still be what we read from the `cvt ' table and */
2670 /* we don't need to reload it. We may need to change it though... */
2671 }
2672 else
2673 {
Werner Lemberg08cd62d2017-12-20 22:06:19 +01002674 FT_Bool have_diff = 0;
2675 FT_UInt j;
2676 FT_Fixed* c;
2677 FT_Fixed* n;
2678
2679
Suzuki, Toshiya (鈴木俊哉)69222662008-10-15 15:22:39 +00002680 manageCvt = mcvt_retain;
Werner Lemberg0aa36162015-03-08 22:50:37 +01002681
2682 for ( i = 0; i < num_coords; i++ )
Suzuki, Toshiya (鈴木俊哉)69222662008-10-15 15:22:39 +00002683 {
2684 if ( blend->normalizedcoords[i] != coords[i] )
2685 {
2686 manageCvt = mcvt_load;
Werner Lemberg08cd62d2017-12-20 22:06:19 +01002687 have_diff = 1;
Suzuki, Toshiya (鈴木俊哉)69222662008-10-15 15:22:39 +00002688 break;
2689 }
2690 }
Werner Lemberg44bb3032004-04-25 20:15:11 +00002691
Werner Lemberg08cd62d2017-12-20 22:06:19 +01002692 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2693 {
Werner Lemberg839cb402018-07-16 05:45:45 +02002694 FT_UInt instance_index = (FT_UInt)face->root.face_index >> 16;
Werner Lemberg08cd62d2017-12-20 22:06:19 +01002695
2696
2697 c = blend->normalizedcoords + i;
Werner Lemberg839cb402018-07-16 05:45:45 +02002698 n = blend->normalized_stylecoords +
2699 ( instance_index - 1 ) * mmvar->num_axis +
2700 i;
2701
Werner Lemberg08cd62d2017-12-20 22:06:19 +01002702 for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
2703 if ( *c != *n )
2704 have_diff = 1;
2705 }
2706 else
2707 {
2708 c = blend->normalizedcoords + i;
2709 for ( j = i; j < mmvar->num_axis; j++, c++ )
2710 if ( *c != 0 )
2711 have_diff = 1;
2712 }
2713
2714 /* return value -1 indicates `no change' */
2715 if ( !have_diff )
Werner Lembergc9edca82018-07-27 10:44:01 +02002716 {
2717 face->doblend = TRUE;
2718
Werner Lemberg08cd62d2017-12-20 22:06:19 +01002719 return -1;
Werner Lembergc9edca82018-07-27 10:44:01 +02002720 }
Werner Lemberg08cd62d2017-12-20 22:06:19 +01002721
Werner Lemberg10e2bb82015-03-29 13:32:47 +02002722 for ( ; i < mmvar->num_axis; i++ )
2723 {
2724 if ( blend->normalizedcoords[i] != 0 )
2725 {
2726 manageCvt = mcvt_load;
2727 break;
2728 }
2729 }
2730
Werner Lemberg44bb3032004-04-25 20:15:11 +00002731 /* If we don't change the blend coords then we don't need to do */
2732 /* anything to the cvt table. It will be correct. Otherwise we */
2733 /* no longer have the original cvt (it was modified when we set */
2734 /* the blend last time), so we must reload and then modify it. */
2735 }
2736
Werner Lemberg10e2bb82015-03-29 13:32:47 +02002737 blend->num_axis = mmvar->num_axis;
Ben Wagnerd3dc2da2021-06-30 18:22:29 -04002738 if ( coords )
2739 FT_MEM_COPY( blend->normalizedcoords,
2740 coords,
2741 num_coords * sizeof ( FT_Fixed ) );
Werner Lemberg44bb3032004-04-25 20:15:11 +00002742
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002743 if ( set_design_coords )
2744 ft_var_to_design( face,
Werner Lemberga9331c02017-05-27 15:50:25 +02002745 all_design_coords ? blend->num_axis : num_coords,
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002746 blend->normalizedcoords,
2747 blend->coords );
2748
Werner Lemberg55d6abe2018-01-03 19:01:15 +01002749 face->doblend = TRUE;
2750
Werner Lemberg4441f7b2016-12-26 17:08:17 +01002751 if ( face->cvt )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002752 {
2753 switch ( manageCvt )
2754 {
2755 case mcvt_load:
2756 /* The cvt table has been loaded already; every time we change the */
2757 /* blend we may need to reload and remodify the cvt table. */
2758 FT_FREE( face->cvt );
2759 face->cvt = NULL;
2760
Werner Lemberg6689a002014-11-25 08:53:09 +01002761 error = tt_face_load_cvt( face, face->root.stream );
Werner Lemberg44bb3032004-04-25 20:15:11 +00002762 break;
2763
2764 case mcvt_modify:
2765 /* The original cvt table is in memory. All we need to do is */
2766 /* apply the `cvar' table (if any). */
Werner Lemberg6689a002014-11-25 08:53:09 +01002767 error = tt_face_vary_cvt( face, face->root.stream );
Werner Lemberg44bb3032004-04-25 20:15:11 +00002768 break;
2769
2770 case mcvt_retain:
2771 /* The cvt table is correct for this set of coordinates. */
2772 break;
2773 }
2774 }
2775
Werner Lemberg34010f72017-03-14 21:50:22 +01002776 /* enforce recomputation of the PostScript name; */
2777 FT_FREE( face->postscript_name );
2778 face->postscript_name = NULL;
2779
Werner Lemberg44bb3032004-04-25 20:15:11 +00002780 Exit:
2781 return error;
2782 }
2783
2784
Werner Lemberg9ac90602018-06-03 09:01:17 +02002785 /**************************************************************************
2786 *
2787 * @Function:
2788 * TT_Set_MM_Blend
2789 *
2790 * @Description:
2791 * Set the blend (normalized) coordinates for this instance of the
2792 * font. Check that the `gvar' table is reasonable and does some
2793 * initial preparation.
2794 *
2795 * @InOut:
2796 * face ::
2797 * The font.
2798 * Initialize the blend structure with `gvar' data.
2799 *
2800 * @Input:
2801 * num_coords ::
2802 * The number of available coordinates. If it is
2803 * larger than the number of axes, ignore the excess
2804 * values. If it is smaller than the number of axes,
2805 * use the default value (0) for the remaining axes.
2806 *
2807 * coords ::
2808 * An array of `num_coords', each between [-1,1].
2809 *
2810 * @Return:
2811 * FreeType error code. 0 means success.
2812 */
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002813 FT_LOCAL_DEF( FT_Error )
2814 TT_Set_MM_Blend( TT_Face face,
2815 FT_UInt num_coords,
2816 FT_Fixed* coords )
2817 {
Werner Lemberg8c92f762017-10-07 12:12:49 +02002818 FT_Error error;
2819
2820
2821 error = tt_set_mm_blend( face, num_coords, coords, 1 );
2822 if ( error )
2823 return error;
2824
2825 if ( num_coords )
2826 face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2827 else
2828 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2829
2830 return FT_Err_Ok;
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002831 }
2832
2833
Werner Lemberg9ac90602018-06-03 09:01:17 +02002834 /**************************************************************************
2835 *
2836 * @Function:
2837 * TT_Get_MM_Blend
2838 *
2839 * @Description:
2840 * Get the blend (normalized) coordinates for this instance of the
2841 * font.
2842 *
2843 * @InOut:
2844 * face ::
2845 * The font.
2846 * Initialize the blend structure with `gvar' data.
2847 *
2848 * @Input:
2849 * num_coords ::
2850 * The number of available coordinates. If it is
2851 * larger than the number of axes, set the excess
2852 * values to 0.
2853 *
2854 * coords ::
2855 * An array of `num_coords', each between [-1,1].
2856 *
2857 * @Return:
2858 * FreeType error code. 0 means success.
2859 */
Werner Lemberg9c45ac32016-10-26 16:00:00 +02002860 FT_LOCAL_DEF( FT_Error )
2861 TT_Get_MM_Blend( TT_Face face,
2862 FT_UInt num_coords,
2863 FT_Fixed* coords )
2864 {
2865 FT_Error error = FT_Err_Ok;
2866 GX_Blend blend;
2867 FT_UInt i, nc;
2868
2869
Werner Lemberg4441f7b2016-12-26 17:08:17 +01002870 if ( !face->blend )
Werner Lemberg9c45ac32016-10-26 16:00:00 +02002871 {
Werner Lemberg5d664b62016-12-17 20:47:42 +01002872 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
Werner Lemberg9c45ac32016-10-26 16:00:00 +02002873 return error;
2874 }
2875
2876 blend = face->blend;
2877
Werner Lemberga9331c02017-05-27 15:50:25 +02002878 if ( !blend->coords )
2879 {
2880 /* select default instance coordinates */
2881 /* if no instance is selected yet */
2882 if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2883 return error;
2884 }
2885
Werner Lemberg9c45ac32016-10-26 16:00:00 +02002886 nc = num_coords;
2887 if ( num_coords > blend->num_axis )
2888 {
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002889 FT_TRACE2(( "TT_Get_MM_Blend:"
2890 " only using first %d of %d coordinates\n",
Werner Lemberg9c45ac32016-10-26 16:00:00 +02002891 blend->num_axis, num_coords ));
2892 nc = blend->num_axis;
2893 }
2894
Werner Lemberg7591bf12016-12-29 21:39:06 +01002895 if ( face->doblend )
2896 {
2897 for ( i = 0; i < nc; i++ )
2898 coords[i] = blend->normalizedcoords[i];
2899 }
2900 else
2901 {
2902 for ( i = 0; i < nc; i++ )
2903 coords[i] = 0;
2904 }
2905
Werner Lemberg9c45ac32016-10-26 16:00:00 +02002906 for ( ; i < num_coords; i++ )
2907 coords[i] = 0;
2908
2909 return FT_Err_Ok;
2910 }
2911
2912
Werner Lemberg9ac90602018-06-03 09:01:17 +02002913 /**************************************************************************
2914 *
2915 * @Function:
2916 * TT_Set_Var_Design
2917 *
2918 * @Description:
2919 * Set the coordinates for the instance, measured in the user
2920 * coordinate system. Parse the `avar' table (if present) to convert
2921 * from user to normalized coordinates.
2922 *
2923 * @InOut:
2924 * face ::
2925 * The font face.
2926 * Initialize the blend struct with `gvar' data.
2927 *
2928 * @Input:
2929 * num_coords ::
2930 * The number of available coordinates. If it is
2931 * larger than the number of axes, ignore the excess
2932 * values. If it is smaller than the number of axes,
2933 * use the default values for the remaining axes.
2934 *
2935 * coords ::
2936 * A coordinate array with `num_coords' elements.
2937 *
2938 * @Return:
2939 * FreeType error code. 0 means success.
2940 */
Werner Lemberg44bb3032004-04-25 20:15:11 +00002941 FT_LOCAL_DEF( FT_Error )
2942 TT_Set_Var_Design( TT_Face face,
2943 FT_UInt num_coords,
2944 FT_Fixed* coords )
2945 {
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002946 FT_Error error = FT_Err_Ok;
2947 GX_Blend blend;
2948 FT_MM_Var* mmvar;
2949 FT_UInt i;
2950 FT_Memory memory = face->root.memory;
2951
Werner Lembergf89c67f2017-10-07 13:10:53 +02002952 FT_Fixed* c;
Werner Lemberg08cd62d2017-12-20 22:06:19 +01002953 FT_Fixed* n;
Werner Lemberg47176962017-03-02 21:42:14 +01002954 FT_Fixed* normalized = NULL;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002955
Werner Lemberg08cd62d2017-12-20 22:06:19 +01002956 FT_Bool have_diff = 0;
2957
Werner Lemberg44bb3032004-04-25 20:15:11 +00002958
Werner Lemberg4441f7b2016-12-26 17:08:17 +01002959 if ( !face->blend )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002960 {
Werner Lemberg5d664b62016-12-17 20:47:42 +01002961 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
Werner Lemberg44bb3032004-04-25 20:15:11 +00002962 goto Exit;
2963 }
2964
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002965 blend = face->blend;
2966 mmvar = blend->mmvar;
Werner Lemberg44bb3032004-04-25 20:15:11 +00002967
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01002968 if ( num_coords > mmvar->num_axis )
2969 {
2970 FT_TRACE2(( "TT_Set_Var_Design:"
2971 " only using first %d of %d coordinates\n",
2972 mmvar->num_axis, num_coords ));
2973 num_coords = mmvar->num_axis;
2974 }
2975
2976 if ( !blend->coords )
2977 {
2978 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2979 goto Exit;
2980 }
2981
Werner Lemberg08cd62d2017-12-20 22:06:19 +01002982 c = blend->coords;
2983 n = coords;
2984 for ( i = 0; i < num_coords; i++, n++, c++ )
2985 {
2986 if ( *c != *n )
2987 {
2988 *c = *n;
2989 have_diff = 1;
2990 }
2991 }
Werner Lembergf89c67f2017-10-07 13:10:53 +02002992
2993 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2994 {
2995 FT_UInt instance_index;
2996 FT_Var_Named_Style* named_style;
Werner Lembergf89c67f2017-10-07 13:10:53 +02002997
2998
2999 instance_index = (FT_UInt)face->root.face_index >> 16;
3000 named_style = mmvar->namedstyle + instance_index - 1;
3001
3002 n = named_style->coords + num_coords;
Werner Lemberg08cd62d2017-12-20 22:06:19 +01003003 for ( ; i < mmvar->num_axis; i++, n++, c++ )
3004 {
3005 if ( *c != *n )
3006 {
3007 *c = *n;
3008 have_diff = 1;
3009 }
3010 }
Werner Lembergf89c67f2017-10-07 13:10:53 +02003011 }
3012 else
3013 {
3014 FT_Var_Axis* a;
3015
3016
3017 a = mmvar->axis + num_coords;
Werner Lemberg08cd62d2017-12-20 22:06:19 +01003018 for ( ; i < mmvar->num_axis; i++, a++, c++ )
3019 {
3020 if ( *c != a->def )
3021 {
3022 *c = a->def;
3023 have_diff = 1;
3024 }
3025 }
Werner Lembergf89c67f2017-10-07 13:10:53 +02003026 }
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01003027
Werner Lemberg68dddcd2018-01-27 23:59:30 +01003028 /* return value -1 indicates `no change'; */
3029 /* we can exit early if `normalizedcoords' is already computed */
3030 if ( blend->normalizedcoords && !have_diff )
Werner Lemberg08cd62d2017-12-20 22:06:19 +01003031 return -1;
3032
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01003033 if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
Werner Lemberg44bb3032004-04-25 20:15:11 +00003034 goto Exit;
3035
Werner Lemberg4a629222017-03-12 10:19:53 +01003036 if ( !face->blend->avar_loaded )
Werner Lemberg44bb3032004-04-25 20:15:11 +00003037 ft_var_load_avar( face );
3038
Werner Lemberga6adb252020-12-02 14:15:07 +01003039 FT_TRACE5(( "TT_Set_Var_Design:\n" ));
3040 FT_TRACE5(( " normalized design coordinates:\n" ));
Werner Lemberg5f2a72c2017-06-10 11:29:24 +02003041 ft_var_to_normalized( face, num_coords, blend->coords, normalized );
Werner Lembergcdee7d12015-05-31 11:54:42 +02003042
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01003043 error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 );
Werner Lemberg8c92f762017-10-07 12:12:49 +02003044 if ( error )
3045 goto Exit;
3046
3047 if ( num_coords )
3048 face->root.face_flags |= FT_FACE_FLAG_VARIATION;
3049 else
3050 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003051
3052 Exit:
3053 FT_FREE( normalized );
3054 return error;
3055 }
3056
3057
Werner Lemberg9ac90602018-06-03 09:01:17 +02003058 /**************************************************************************
3059 *
3060 * @Function:
3061 * TT_Get_Var_Design
3062 *
3063 * @Description:
3064 * Get the design coordinates of the currently selected interpolated
3065 * font.
3066 *
3067 * @Input:
3068 * face ::
3069 * A handle to the source face.
3070 *
3071 * num_coords ::
3072 * The number of design coordinates to retrieve. If it
3073 * is larger than the number of axes, set the excess
3074 * values to~0.
3075 *
3076 * @Output:
3077 * coords ::
3078 * The design coordinates array.
3079 *
3080 * @Return:
3081 * FreeType error code. 0~means success.
3082 */
Dave Arnoldbcae6572016-12-05 22:08:15 +01003083 FT_LOCAL_DEF( FT_Error )
3084 TT_Get_Var_Design( TT_Face face,
3085 FT_UInt num_coords,
3086 FT_Fixed* coords )
3087 {
Werner Lemberge5e35562016-12-27 06:49:37 +01003088 FT_Error error = FT_Err_Ok;
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01003089 GX_Blend blend;
3090 FT_UInt i, nc;
Dave Arnoldbcae6572016-12-05 22:08:15 +01003091
Werner Lemberge5e35562016-12-27 06:49:37 +01003092
3093 if ( !face->blend )
3094 {
3095 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
3096 return error;
3097 }
3098
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01003099 blend = face->blend;
Werner Lemberge5e35562016-12-27 06:49:37 +01003100
Werner Lemberga9331c02017-05-27 15:50:25 +02003101 if ( !blend->coords )
3102 {
3103 /* select default instance coordinates */
3104 /* if no instance is selected yet */
3105 if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
3106 return error;
3107 }
3108
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01003109 nc = num_coords;
3110 if ( num_coords > blend->num_axis )
3111 {
3112 FT_TRACE2(( "TT_Get_Var_Design:"
3113 " only using first %d of %d coordinates\n",
3114 blend->num_axis, num_coords ));
3115 nc = blend->num_axis;
3116 }
3117
3118 if ( face->doblend )
3119 {
3120 for ( i = 0; i < nc; i++ )
3121 coords[i] = blend->coords[i];
3122 }
3123 else
3124 {
3125 for ( i = 0; i < nc; i++ )
3126 coords[i] = 0;
3127 }
3128
3129 for ( ; i < num_coords; i++ )
3130 coords[i] = 0;
Werner Lemberge5e35562016-12-27 06:49:37 +01003131
3132 return FT_Err_Ok;
Dave Arnoldbcae6572016-12-05 22:08:15 +01003133 }
3134
3135
Werner Lemberg9ac90602018-06-03 09:01:17 +02003136 /**************************************************************************
3137 *
3138 * @Function:
3139 * TT_Set_Named_Instance
3140 *
3141 * @Description:
3142 * Set the given named instance, also resetting any further
3143 * variation.
3144 *
3145 * @Input:
3146 * face ::
3147 * A handle to the source face.
3148 *
3149 * instance_index ::
3150 * The instance index, starting with value 1.
3151 * Value 0 indicates to not use an instance.
3152 *
3153 * @Return:
3154 * FreeType error code. 0~means success.
3155 */
Werner Lemberge9ef5382017-10-07 12:57:11 +02003156 FT_LOCAL_DEF( FT_Error )
3157 TT_Set_Named_Instance( TT_Face face,
3158 FT_UInt instance_index )
3159 {
Werner Lemberg59415652019-08-27 14:07:14 +02003160 FT_Error error;
Werner Lemberge9ef5382017-10-07 12:57:11 +02003161 GX_Blend blend;
3162 FT_MM_Var* mmvar;
3163
3164 FT_UInt num_instances;
3165
3166
3167 if ( !face->blend )
3168 {
3169 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
3170 goto Exit;
3171 }
3172
3173 blend = face->blend;
3174 mmvar = blend->mmvar;
3175
3176 num_instances = (FT_UInt)face->root.style_flags >> 16;
3177
3178 /* `instance_index' starts with value 1, thus `>' */
3179 if ( instance_index > num_instances )
Werner Lemberg59415652019-08-27 14:07:14 +02003180 {
3181 error = FT_ERR( Invalid_Argument );
Werner Lemberge9ef5382017-10-07 12:57:11 +02003182 goto Exit;
Werner Lemberg59415652019-08-27 14:07:14 +02003183 }
Werner Lemberge9ef5382017-10-07 12:57:11 +02003184
Werner Lemberg7b841042019-05-04 08:13:22 +02003185 if ( instance_index > 0 )
Werner Lemberge9ef5382017-10-07 12:57:11 +02003186 {
3187 FT_Memory memory = face->root.memory;
3188 SFNT_Service sfnt = (SFNT_Service)face->sfnt;
3189
3190 FT_Var_Named_Style* named_style;
3191 FT_String* style_name;
3192
3193
3194 named_style = mmvar->namedstyle + instance_index - 1;
3195
3196 error = sfnt->get_name( face,
3197 (FT_UShort)named_style->strid,
3198 &style_name );
3199 if ( error )
3200 goto Exit;
3201
3202 /* set (or replace) style name */
3203 FT_FREE( face->root.style_name );
3204 face->root.style_name = style_name;
3205
3206 /* finally, select the named instance */
3207 error = TT_Set_Var_Design( face,
3208 mmvar->num_axis,
3209 named_style->coords );
3210 if ( error )
Werner Lembergaf400432019-04-22 07:41:35 +02003211 {
3212 /* internal error code -1 means `no change' */
3213 if ( error == -1 )
3214 error = FT_Err_Ok;
Werner Lemberge9ef5382017-10-07 12:57:11 +02003215 goto Exit;
Werner Lembergaf400432019-04-22 07:41:35 +02003216 }
Werner Lemberge9ef5382017-10-07 12:57:11 +02003217 }
3218 else
3219 error = TT_Set_Var_Design( face, 0, NULL );
3220
3221 face->root.face_index = ( instance_index << 16 ) |
3222 ( face->root.face_index & 0xFFFFL );
3223 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
3224
3225 Exit:
3226 return error;
3227 }
3228
3229
3230 /*************************************************************************/
Werner Lemberg44bb3032004-04-25 20:15:11 +00003231 /*************************************************************************/
3232 /***** *****/
3233 /***** GX VAR PARSING ROUTINES *****/
3234 /***** *****/
3235 /*************************************************************************/
3236 /*************************************************************************/
3237
3238
Jany Belluz23d1d8a2021-11-04 08:55:39 +00003239#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
Werner Lembergd1c20002021-08-28 07:29:05 +02003240
Werner Lembergb0522702019-05-16 12:52:57 +02003241 static FT_Error
3242 tt_cvt_ready_iterator( FT_ListNode node,
3243 void* user )
3244 {
3245 TT_Size size = (TT_Size)node->data;
3246
3247 FT_UNUSED( user );
3248
3249
3250 size->cvt_ready = -1;
3251
3252 return FT_Err_Ok;
3253 }
3254
Werner Lembergd1c20002021-08-28 07:29:05 +02003255#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
3256
3257
Werner Lembergb0522702019-05-16 12:52:57 +02003258
Werner Lemberg9ac90602018-06-03 09:01:17 +02003259 /**************************************************************************
3260 *
3261 * @Function:
3262 * tt_face_vary_cvt
3263 *
3264 * @Description:
3265 * Modify the loaded cvt table according to the `cvar' table and the
3266 * font's blend.
3267 *
3268 * @InOut:
3269 * face ::
3270 * A handle to the target face object.
3271 *
3272 * @Input:
3273 * stream ::
3274 * A handle to the input stream.
3275 *
3276 * @Return:
3277 * FreeType error code. 0 means success.
3278 *
3279 * Most errors are ignored. It is perfectly valid not to have a
3280 * `cvar' table even if there is a `gvar' and `fvar' table.
3281 */
Werner Lemberg44bb3032004-04-25 20:15:11 +00003282 FT_LOCAL_DEF( FT_Error )
3283 tt_face_vary_cvt( TT_Face face,
3284 FT_Stream stream )
3285 {
Jany Belluz23d1d8a2021-11-04 08:55:39 +00003286#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
Werner Lembergd1c20002021-08-28 07:29:05 +02003287
Werner Lemberg589d1f02018-06-25 18:38:04 +02003288 FT_Error error;
3289 FT_Memory memory = stream->memory;
3290
Werner Lembergb0522702019-05-16 12:52:57 +02003291 FT_Face root = &face->root;
3292
Werner Lemberg589d1f02018-06-25 18:38:04 +02003293 FT_ULong table_start;
3294 FT_ULong table_len;
3295
3296 FT_UInt tupleCount;
3297 FT_ULong offsetToData;
3298
3299 FT_ULong here;
3300 FT_UInt i, j;
3301
3302 FT_Fixed* tuple_coords = NULL;
3303 FT_Fixed* im_start_coords = NULL;
3304 FT_Fixed* im_end_coords = NULL;
3305
3306 GX_Blend blend = face->blend;
3307
3308 FT_UInt point_count;
3309 FT_UInt spoint_count = 0;
3310
Werner Lemberg337e49c2017-12-04 12:36:07 +01003311 FT_UShort* sharedpoints = NULL;
3312 FT_UShort* localpoints = NULL;
3313 FT_UShort* points;
Werner Lemberga632fb52018-06-24 15:22:10 +02003314
Werner Lemberg589d1f02018-06-25 18:38:04 +02003315 FT_Fixed* deltas = NULL;
3316 FT_Fixed* cvt_deltas = NULL;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003317
3318
3319 FT_TRACE2(( "CVAR " ));
3320
Werner Lemberg4441f7b2016-12-26 17:08:17 +01003321 if ( !blend )
Werner Lemberg44bb3032004-04-25 20:15:11 +00003322 {
Werner Lemberga6adb252020-12-02 14:15:07 +01003323 FT_TRACE2(( "\n" ));
3324 FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" ));
Werner Lemberge3c93012013-03-14 11:21:17 +01003325 error = FT_Err_Ok;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003326 goto Exit;
3327 }
3328
Werner Lemberg4441f7b2016-12-26 17:08:17 +01003329 if ( !face->cvt )
Werner Lemberg44bb3032004-04-25 20:15:11 +00003330 {
Werner Lemberga6adb252020-12-02 14:15:07 +01003331 FT_TRACE2(( "\n" ));
3332 FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" ));
Werner Lemberge3c93012013-03-14 11:21:17 +01003333 error = FT_Err_Ok;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003334 goto Exit;
3335 }
3336
3337 error = face->goto_table( face, TTAG_cvar, stream, &table_len );
3338 if ( error )
3339 {
Werner Lemberg858abbe2009-06-26 06:15:41 +02003340 FT_TRACE2(( "is missing\n" ));
Werner Lemberg44bb3032004-04-25 20:15:11 +00003341
Werner Lemberge3c93012013-03-14 11:21:17 +01003342 error = FT_Err_Ok;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003343 goto Exit;
3344 }
3345
3346 if ( FT_FRAME_ENTER( table_len ) )
3347 {
Werner Lemberge3c93012013-03-14 11:21:17 +01003348 error = FT_Err_Ok;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003349 goto Exit;
3350 }
3351
3352 table_start = FT_Stream_FTell( stream );
3353 if ( FT_GET_LONG() != 0x00010000L )
3354 {
Werner Lemberg858abbe2009-06-26 06:15:41 +02003355 FT_TRACE2(( "bad table version\n" ));
Werner Lemberg44bb3032004-04-25 20:15:11 +00003356
Werner Lemberge3c93012013-03-14 11:21:17 +01003357 error = FT_Err_Ok;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003358 goto FExit;
3359 }
3360
Werner Lembergcdee7d12015-05-31 11:54:42 +02003361 FT_TRACE2(( "loaded\n" ));
3362
Werner Lemberg44bb3032004-04-25 20:15:11 +00003363 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) ||
3364 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3365 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) )
3366 goto FExit;
3367
3368 tupleCount = FT_GET_USHORT();
Werner Lembergda346732015-10-10 10:21:27 +02003369 offsetToData = FT_GET_USHORT();
Werner Lemberg44bb3032004-04-25 20:15:11 +00003370
Werner Lembergda346732015-10-10 10:21:27 +02003371 /* rough sanity test */
Werner Lembergdf2cf432016-12-16 11:38:20 +01003372 if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3373 table_len )
Werner Lembergda346732015-10-10 10:21:27 +02003374 {
3375 FT_TRACE2(( "tt_face_vary_cvt:"
3376 " invalid CVT variation array header\n" ));
Werner Lemberg44bb3032004-04-25 20:15:11 +00003377
Werner Lembergda346732015-10-10 10:21:27 +02003378 error = FT_THROW( Invalid_Table );
3379 goto FExit;
3380 }
3381
3382 offsetToData += table_start;
3383
Werner Lemberg337e49c2017-12-04 12:36:07 +01003384 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3385 {
3386 here = FT_Stream_FTell( stream );
3387
3388 FT_Stream_SeekSet( stream, offsetToData );
3389
3390 sharedpoints = ft_var_readpackedpoints( stream,
3391 table_len,
3392 &spoint_count );
3393 offsetToData = FT_Stream_FTell( stream );
3394
3395 FT_Stream_SeekSet( stream, here );
3396 }
Werner Lembergda346732015-10-10 10:21:27 +02003397
Werner Lemberg71fecc52017-12-05 12:06:29 +01003398 FT_TRACE5(( "cvar: there %s %d tuple%s:\n",
Alexei Podtelezhnikov1f43aff2018-11-04 13:11:16 -05003399 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3400 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3401 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
Werner Lembergcdee7d12015-05-31 11:54:42 +02003402
Werner Lemberga632fb52018-06-24 15:22:10 +02003403 if ( FT_NEW_ARRAY( cvt_deltas, face->cvt_size ) )
3404 goto FExit;
3405
Alexei Podtelezhnikov1f43aff2018-11-04 13:11:16 -05003406 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00003407 {
3408 FT_UInt tupleDataSize;
3409 FT_UInt tupleIndex;
3410 FT_Fixed apply;
3411
3412
Werner Lembergcdee7d12015-05-31 11:54:42 +02003413 FT_TRACE6(( " tuple %d:\n", i ));
3414
Werner Lemberg44bb3032004-04-25 20:15:11 +00003415 tupleDataSize = FT_GET_USHORT();
3416 tupleIndex = FT_GET_USHORT();
3417
Werner Lemberg44bb3032004-04-25 20:15:11 +00003418 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3419 {
Werner Lemberg0aa36162015-03-08 22:50:37 +01003420 for ( j = 0; j < blend->num_axis; j++ )
Werner Lembergdc39f762019-05-07 10:09:55 +02003421 tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
Werner Lemberg44bb3032004-04-25 20:15:11 +00003422 }
Werner Lemberg337e49c2017-12-04 12:36:07 +01003423 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
Werner Lemberg44bb3032004-04-25 20:15:11 +00003424 {
Werner Lemberg337e49c2017-12-04 12:36:07 +01003425 FT_TRACE2(( "tt_face_vary_cvt:"
3426 " invalid tuple index\n" ));
Werner Lemberg44bb3032004-04-25 20:15:11 +00003427
Werner Lemberg337e49c2017-12-04 12:36:07 +01003428 error = FT_THROW( Invalid_Table );
Werner Lemberg10e54d02018-09-13 21:47:35 +02003429 goto FExit;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003430 }
Werner Lemberg337e49c2017-12-04 12:36:07 +01003431 else
Werner Lemberg10e54d02018-09-13 21:47:35 +02003432 {
3433 if ( !blend->tuplecoords )
3434 {
3435 FT_TRACE2(( "tt_face_vary_cvt:"
3436 " no valid tuple coordinates available\n" ));
3437
3438 error = FT_THROW( Invalid_Table );
3439 goto FExit;
3440 }
3441
Werner Lemberg337e49c2017-12-04 12:36:07 +01003442 FT_MEM_COPY(
3443 tuple_coords,
Alexei Podtelezhnikov1f43aff2018-11-04 13:11:16 -05003444 blend->tuplecoords +
3445 ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
Werner Lemberg337e49c2017-12-04 12:36:07 +01003446 blend->num_axis * sizeof ( FT_Fixed ) );
Werner Lemberg10e54d02018-09-13 21:47:35 +02003447 }
Werner Lemberg44bb3032004-04-25 20:15:11 +00003448
3449 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3450 {
Werner Lemberg0aa36162015-03-08 22:50:37 +01003451 for ( j = 0; j < blend->num_axis; j++ )
Werner Lembergdc39f762019-05-07 10:09:55 +02003452 im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
Werner Lemberg0aa36162015-03-08 22:50:37 +01003453 for ( j = 0; j < blend->num_axis; j++ )
Werner Lembergdc39f762019-05-07 10:09:55 +02003454 im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
Werner Lemberg44bb3032004-04-25 20:15:11 +00003455 }
3456
3457 apply = ft_var_apply_tuple( blend,
Werner Lemberg22ad9ef2005-05-09 22:11:36 +00003458 (FT_UShort)tupleIndex,
Werner Lemberg44bb3032004-04-25 20:15:11 +00003459 tuple_coords,
3460 im_start_coords,
3461 im_end_coords );
Werner Lemberg337e49c2017-12-04 12:36:07 +01003462
3463 if ( apply == 0 ) /* tuple isn't active for our blend */
Werner Lemberg44bb3032004-04-25 20:15:11 +00003464 {
Yamato, Masatake (大和正武)17f4b8b2004-05-07 08:59:29 +00003465 offsetToData += tupleDataSize;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003466 continue;
3467 }
3468
3469 here = FT_Stream_FTell( stream );
3470
Yamato, Masatake (大和正武)17f4b8b2004-05-07 08:59:29 +00003471 FT_Stream_SeekSet( stream, offsetToData );
Werner Lemberg44bb3032004-04-25 20:15:11 +00003472
Werner Lemberg337e49c2017-12-04 12:36:07 +01003473 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3474 {
3475 localpoints = ft_var_readpackedpoints( stream,
3476 table_len,
3477 &point_count );
3478 points = localpoints;
3479 }
3480 else
3481 {
3482 points = sharedpoints;
3483 point_count = spoint_count;
3484 }
3485
3486 deltas = ft_var_readpackeddeltas( stream,
3487 table_len,
3488 point_count == 0 ? face->cvt_size
3489 : point_count );
Werner Lemberg2fe272a2017-12-18 19:40:07 +01003490
Jany Belluz9ed53322021-11-04 08:56:59 +00003491 if ( !points || !deltas )
Werner Lemberg0aa36162015-03-08 22:50:37 +01003492 ; /* failure, ignore it */
Werner Lemberg44bb3032004-04-25 20:15:11 +00003493
3494 else if ( localpoints == ALL_POINTS )
3495 {
Werner Lembergcdee7d12015-05-31 11:54:42 +02003496#ifdef FT_DEBUG_LEVEL_TRACE
3497 int count = 0;
3498#endif
3499
3500
3501 FT_TRACE7(( " CVT deltas:\n" ));
3502
Werner Lemberg44bb3032004-04-25 20:15:11 +00003503 /* this means that there are deltas for every entry in cvt */
Werner Lemberg0aa36162015-03-08 22:50:37 +01003504 for ( j = 0; j < face->cvt_size; j++ )
Werner Lembergcdee7d12015-05-31 11:54:42 +02003505 {
Werner Lemberga632fb52018-06-24 15:22:10 +02003506 FT_Fixed old_cvt_delta;
Werner Lembergcdee7d12015-05-31 11:54:42 +02003507
3508
Werner Lemberga632fb52018-06-24 15:22:10 +02003509 old_cvt_delta = cvt_deltas[j];
3510 cvt_deltas[j] = old_cvt_delta + FT_MulFix( deltas[j], apply );
Werner Lembergcdee7d12015-05-31 11:54:42 +02003511
3512#ifdef FT_DEBUG_LEVEL_TRACE
Werner Lemberga632fb52018-06-24 15:22:10 +02003513 if ( old_cvt_delta != cvt_deltas[j] )
Werner Lembergcdee7d12015-05-31 11:54:42 +02003514 {
Werner Lemberga632fb52018-06-24 15:22:10 +02003515 FT_TRACE7(( " %d: %f -> %f\n",
3516 j,
Werner Lemberg37580052019-05-16 12:15:54 +02003517 ( FT_fdot6ToFixed( face->cvt[j] ) +
Werner Lemberga632fb52018-06-24 15:22:10 +02003518 old_cvt_delta ) / 65536.0,
Werner Lemberg37580052019-05-16 12:15:54 +02003519 ( FT_fdot6ToFixed( face->cvt[j] ) +
Werner Lemberga632fb52018-06-24 15:22:10 +02003520 cvt_deltas[j] ) / 65536.0 ));
Werner Lembergcdee7d12015-05-31 11:54:42 +02003521 count++;
3522 }
3523#endif
3524 }
3525
3526#ifdef FT_DEBUG_LEVEL_TRACE
3527 if ( !count )
3528 FT_TRACE7(( " none\n" ));
3529#endif
Werner Lemberg44bb3032004-04-25 20:15:11 +00003530 }
3531
3532 else
3533 {
Werner Lembergcdee7d12015-05-31 11:54:42 +02003534#ifdef FT_DEBUG_LEVEL_TRACE
3535 int count = 0;
3536#endif
3537
3538
3539 FT_TRACE7(( " CVT deltas:\n" ));
3540
Werner Lemberg0aa36162015-03-08 22:50:37 +01003541 for ( j = 0; j < point_count; j++ )
David Turner750fa962005-05-01 10:11:32 +00003542 {
Werner Lemberga632fb52018-06-24 15:22:10 +02003543 int pindex;
3544 FT_Fixed old_cvt_delta;
David Turner9ca78252006-05-02 09:00:29 +00003545
Werner Lemberg0aa36162015-03-08 22:50:37 +01003546
Werner Lemberg337e49c2017-12-04 12:36:07 +01003547 pindex = points[j];
Werner Lemberg7eeaf982016-10-14 08:58:17 +02003548 if ( (FT_ULong)pindex >= face->cvt_size )
3549 continue;
3550
Werner Lemberga632fb52018-06-24 15:22:10 +02003551 old_cvt_delta = cvt_deltas[pindex];
3552 cvt_deltas[pindex] = old_cvt_delta + FT_MulFix( deltas[j], apply );
Werner Lembergcdee7d12015-05-31 11:54:42 +02003553
3554#ifdef FT_DEBUG_LEVEL_TRACE
Werner Lemberga632fb52018-06-24 15:22:10 +02003555 if ( old_cvt_delta != cvt_deltas[pindex] )
Werner Lembergcdee7d12015-05-31 11:54:42 +02003556 {
Werner Lemberga632fb52018-06-24 15:22:10 +02003557 FT_TRACE7(( " %d: %f -> %f\n",
3558 pindex,
Werner Lemberg37580052019-05-16 12:15:54 +02003559 ( FT_fdot6ToFixed( face->cvt[pindex] ) +
Werner Lemberga632fb52018-06-24 15:22:10 +02003560 old_cvt_delta ) / 65536.0,
Werner Lemberg37580052019-05-16 12:15:54 +02003561 ( FT_fdot6ToFixed( face->cvt[pindex] ) +
Werner Lemberga632fb52018-06-24 15:22:10 +02003562 cvt_deltas[pindex] ) / 65536.0 ));
Werner Lembergcdee7d12015-05-31 11:54:42 +02003563 count++;
3564 }
3565#endif
David Turner750fa962005-05-01 10:11:32 +00003566 }
Werner Lembergcdee7d12015-05-31 11:54:42 +02003567
3568#ifdef FT_DEBUG_LEVEL_TRACE
3569 if ( !count )
3570 FT_TRACE7(( " none\n" ));
3571#endif
Werner Lemberg44bb3032004-04-25 20:15:11 +00003572 }
3573
3574 if ( localpoints != ALL_POINTS )
3575 FT_FREE( localpoints );
3576 FT_FREE( deltas );
3577
Yamato, Masatake (大和正武)17f4b8b2004-05-07 08:59:29 +00003578 offsetToData += tupleDataSize;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003579
3580 FT_Stream_SeekSet( stream, here );
3581 }
3582
Werner Lembergcdee7d12015-05-31 11:54:42 +02003583 FT_TRACE5(( "\n" ));
3584
Werner Lemberga632fb52018-06-24 15:22:10 +02003585 for ( i = 0; i < face->cvt_size; i++ )
Werner Lemberg37580052019-05-16 12:15:54 +02003586 face->cvt[i] += FT_fixedToFdot6( cvt_deltas[i] );
Werner Lemberga632fb52018-06-24 15:22:10 +02003587
Werner Lemberg44bb3032004-04-25 20:15:11 +00003588 FExit:
3589 FT_FRAME_EXIT();
3590
3591 Exit:
Werner Lemberg337e49c2017-12-04 12:36:07 +01003592 if ( sharedpoints != ALL_POINTS )
3593 FT_FREE( sharedpoints );
Werner Lemberg44bb3032004-04-25 20:15:11 +00003594 FT_FREE( tuple_coords );
3595 FT_FREE( im_start_coords );
3596 FT_FREE( im_end_coords );
Werner Lemberga632fb52018-06-24 15:22:10 +02003597 FT_FREE( cvt_deltas );
Werner Lemberg44bb3032004-04-25 20:15:11 +00003598
Werner Lembergb0522702019-05-16 12:52:57 +02003599 /* iterate over all FT_Size objects and set `cvt_ready' to -1 */
3600 /* to trigger rescaling of all CVT values */
3601 FT_List_Iterate( &root->sizes_list,
3602 tt_cvt_ready_iterator,
3603 NULL );
3604
Werner Lemberg44bb3032004-04-25 20:15:11 +00003605 return error;
Werner Lembergd1c20002021-08-28 07:29:05 +02003606
3607#else /* !TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
3608
3609 FT_UNUSED( face );
3610 FT_UNUSED( stream );
3611
3612 return FT_Err_Ok;
3613
3614#endif /* !TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
3615
Werner Lemberg44bb3032004-04-25 20:15:11 +00003616 }
3617
3618
Werner Lemberge9df4e42015-05-31 12:21:34 +02003619 /* Shift the original coordinates of all points between indices `p1' */
3620 /* and `p2', using the same difference as given by index `ref'. */
3621
3622 /* modeled after `af_iup_shift' */
3623
3624 static void
3625 tt_delta_shift( int p1,
3626 int p2,
3627 int ref,
3628 FT_Vector* in_points,
3629 FT_Vector* out_points )
3630 {
3631 int p;
3632 FT_Vector delta;
3633
3634
3635 delta.x = out_points[ref].x - in_points[ref].x;
3636 delta.y = out_points[ref].y - in_points[ref].y;
3637
3638 if ( delta.x == 0 && delta.y == 0 )
3639 return;
3640
3641 for ( p = p1; p < ref; p++ )
3642 {
3643 out_points[p].x += delta.x;
3644 out_points[p].y += delta.y;
3645 }
3646
3647 for ( p = ref + 1; p <= p2; p++ )
3648 {
3649 out_points[p].x += delta.x;
3650 out_points[p].y += delta.y;
3651 }
3652 }
3653
3654
3655 /* Interpolate the original coordinates of all points with indices */
3656 /* between `p1' and `p2', using `ref1' and `ref2' as the reference */
3657 /* point indices. */
3658
Alexei Podtelezhnikovce3feb02018-11-03 22:43:21 -04003659 /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */
Alexei Podtelezhnikov1f43aff2018-11-04 13:11:16 -05003660 /* `Ins_IUP' with spec differences in handling ill-defined cases. */
Werner Lemberge9df4e42015-05-31 12:21:34 +02003661 static void
3662 tt_delta_interpolate( int p1,
3663 int p2,
3664 int ref1,
3665 int ref2,
3666 FT_Vector* in_points,
3667 FT_Vector* out_points )
3668 {
3669 int p, i;
3670
3671 FT_Pos out, in1, in2, out1, out2, d1, d2;
3672
3673
3674 if ( p1 > p2 )
3675 return;
3676
3677 /* handle both horizontal and vertical coordinates */
3678 for ( i = 0; i <= 1; i++ )
3679 {
3680 /* shift array pointers so that we can access `foo.y' as `foo.x' */
3681 in_points = (FT_Vector*)( (FT_Pos*)in_points + i );
3682 out_points = (FT_Vector*)( (FT_Pos*)out_points + i );
3683
3684 if ( in_points[ref1].x > in_points[ref2].x )
3685 {
3686 p = ref1;
3687 ref1 = ref2;
3688 ref2 = p;
3689 }
3690
3691 in1 = in_points[ref1].x;
3692 in2 = in_points[ref2].x;
3693 out1 = out_points[ref1].x;
3694 out2 = out_points[ref2].x;
3695 d1 = out1 - in1;
3696 d2 = out2 - in2;
3697
Behdad Esfahbod60bf2642017-05-02 14:38:54 +02003698 /* If the reference points have the same coordinate but different */
3699 /* delta, inferred delta is zero. Otherwise interpolate. */
Alexei Podtelezhnikovdfa86d62018-11-03 22:36:52 -04003700 if ( in1 != in2 || out1 == out2 )
Werner Lemberge9df4e42015-05-31 12:21:34 +02003701 {
Alexei Podtelezhnikovdfa86d62018-11-03 22:36:52 -04003702 FT_Fixed scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 )
3703 : 0;
Werner Lemberge9df4e42015-05-31 12:21:34 +02003704
3705
3706 for ( p = p1; p <= p2; p++ )
3707 {
3708 out = in_points[p].x;
3709
3710 if ( out <= in1 )
3711 out += d1;
3712 else if ( out >= in2 )
3713 out += d2;
3714 else
3715 out = out1 + FT_MulFix( out - in1, scale );
3716
3717 out_points[p].x = out;
3718 }
3719 }
3720 }
3721 }
3722
3723
3724 /* Interpolate points without delta values, similar to */
3725 /* the `IUP' hinting instruction. */
3726
3727 /* modeled after `Ins_IUP */
3728
3729 static void
Werner Lembergf147fb22016-07-16 07:06:21 +02003730 tt_interpolate_deltas( FT_Outline* outline,
3731 FT_Vector* out_points,
3732 FT_Vector* in_points,
3733 FT_Bool* has_delta )
Werner Lemberge9df4e42015-05-31 12:21:34 +02003734 {
Werner Lembergeb1bba92015-06-30 09:46:39 +02003735 FT_Int first_point;
3736 FT_Int end_point;
Werner Lemberge9df4e42015-05-31 12:21:34 +02003737
Werner Lembergeb1bba92015-06-30 09:46:39 +02003738 FT_Int first_delta;
3739 FT_Int cur_delta;
Werner Lemberge9df4e42015-05-31 12:21:34 +02003740
Werner Lembergeb1bba92015-06-30 09:46:39 +02003741 FT_Int point;
Werner Lemberge9df4e42015-05-31 12:21:34 +02003742 FT_Short contour;
3743
3744
3745 /* ignore empty outlines */
3746 if ( !outline->n_contours )
3747 return;
3748
Werner Lemberge9df4e42015-05-31 12:21:34 +02003749 contour = 0;
3750 point = 0;
3751
3752 do
3753 {
3754 end_point = outline->contours[contour];
3755 first_point = point;
3756
3757 /* search first point that has a delta */
3758 while ( point <= end_point && !has_delta[point] )
3759 point++;
3760
3761 if ( point <= end_point )
3762 {
3763 first_delta = point;
3764 cur_delta = point;
3765
3766 point++;
3767
3768 while ( point <= end_point )
3769 {
3770 /* search next point that has a delta */
3771 /* and interpolate intermediate points */
3772 if ( has_delta[point] )
3773 {
3774 tt_delta_interpolate( cur_delta + 1,
3775 point - 1,
3776 cur_delta,
3777 point,
3778 in_points,
3779 out_points );
3780 cur_delta = point;
3781 }
3782
3783 point++;
3784 }
3785
3786 /* shift contour if we only have a single delta */
3787 if ( cur_delta == first_delta )
3788 tt_delta_shift( first_point,
3789 end_point,
3790 cur_delta,
3791 in_points,
3792 out_points );
3793 else
3794 {
3795 /* otherwise handle remaining points */
3796 /* at the end and beginning of the contour */
3797 tt_delta_interpolate( cur_delta + 1,
3798 end_point,
3799 cur_delta,
3800 first_delta,
3801 in_points,
3802 out_points );
3803
3804 if ( first_delta > 0 )
3805 tt_delta_interpolate( first_point,
3806 first_delta - 1,
3807 cur_delta,
3808 first_delta,
3809 in_points,
3810 out_points );
3811 }
3812 }
3813 contour++;
3814
3815 } while ( contour < outline->n_contours );
3816 }
3817
3818
Werner Lemberg9ac90602018-06-03 09:01:17 +02003819 /**************************************************************************
3820 *
3821 * @Function:
3822 * TT_Vary_Apply_Glyph_Deltas
3823 *
3824 * @Description:
3825 * Apply the appropriate deltas to the current glyph.
3826 *
3827 * @Input:
3828 * face ::
3829 * A handle to the target face object.
3830 *
3831 * glyph_index ::
3832 * The index of the glyph being modified.
3833 *
3834 * n_points ::
3835 * The number of the points in the glyph, including
3836 * phantom points.
3837 *
3838 * @InOut:
3839 * outline ::
3840 * The outline to change.
3841 *
Werner Lemberg11782272019-05-11 09:29:19 +02003842 * @Output:
3843 * unrounded ::
3844 * An array with `n_points' elements that is filled with unrounded
3845 * point coordinates (in 26.6 format).
3846 *
Werner Lemberg9ac90602018-06-03 09:01:17 +02003847 * @Return:
3848 * FreeType error code. 0 means success.
3849 */
Werner Lemberg44bb3032004-04-25 20:15:11 +00003850 FT_LOCAL_DEF( FT_Error )
Werner Lemberge9df4e42015-05-31 12:21:34 +02003851 TT_Vary_Apply_Glyph_Deltas( TT_Face face,
3852 FT_UInt glyph_index,
3853 FT_Outline* outline,
Werner Lemberg11782272019-05-11 09:29:19 +02003854 FT_Vector* unrounded,
Werner Lemberge9df4e42015-05-31 12:21:34 +02003855 FT_UInt n_points )
Werner Lemberg44bb3032004-04-25 20:15:11 +00003856 {
Werner Lemberg207ca382018-06-25 18:50:00 +02003857 FT_Error error;
3858 FT_Stream stream = face->root.stream;
3859 FT_Memory memory = stream->memory;
Werner Lemberge9df4e42015-05-31 12:21:34 +02003860
Werner Lemberga632fb52018-06-24 15:22:10 +02003861 FT_Vector* points_org = NULL; /* coordinates in 16.16 format */
3862 FT_Vector* points_out = NULL; /* coordinates in 16.16 format */
Werner Lemberge9df4e42015-05-31 12:21:34 +02003863 FT_Bool* has_delta = NULL;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003864
Werner Lemberg207ca382018-06-25 18:50:00 +02003865 FT_ULong glyph_start;
3866
3867 FT_UInt tupleCount;
3868 FT_ULong offsetToData;
Werner Lemberg65681e62018-09-12 07:40:49 +02003869 FT_ULong dataSize;
Werner Lemberg207ca382018-06-25 18:50:00 +02003870
3871 FT_ULong here;
3872 FT_UInt i, j;
3873
3874 FT_Fixed* tuple_coords = NULL;
3875 FT_Fixed* im_start_coords = NULL;
3876 FT_Fixed* im_end_coords = NULL;
3877
3878 GX_Blend blend = face->blend;
3879
3880 FT_UInt point_count;
3881 FT_UInt spoint_count = 0;
3882
Werner Lemberg44bb3032004-04-25 20:15:11 +00003883 FT_UShort* sharedpoints = NULL;
David Turner750fa962005-05-01 10:11:32 +00003884 FT_UShort* localpoints = NULL;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003885 FT_UShort* points;
Werner Lemberga632fb52018-06-24 15:22:10 +02003886
Werner Lemberg207ca382018-06-25 18:50:00 +02003887 FT_Fixed* deltas_x = NULL;
3888 FT_Fixed* deltas_y = NULL;
3889 FT_Fixed* point_deltas_x = NULL;
3890 FT_Fixed* point_deltas_y = NULL;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003891
3892
Werner Lemberg4441f7b2016-12-26 17:08:17 +01003893 if ( !face->doblend || !blend )
Werner Lemberg059bc332013-03-14 10:27:35 +01003894 return FT_THROW( Invalid_Argument );
Werner Lemberg44bb3032004-04-25 20:15:11 +00003895
Werner Lemberg306d2f62019-05-23 14:41:57 +02003896 for ( i = 0; i < n_points; i++ )
3897 {
3898 unrounded[i].x = INT_TO_F26DOT6( outline->points[i].x );
3899 unrounded[i].y = INT_TO_F26DOT6( outline->points[i].y );
3900 }
3901
Werner Lemberg44bb3032004-04-25 20:15:11 +00003902 if ( glyph_index >= blend->gv_glyphcnt ||
3903 blend->glyphoffsets[glyph_index] ==
3904 blend->glyphoffsets[glyph_index + 1] )
Werner Lemberge9df4e42015-05-31 12:21:34 +02003905 {
3906 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
Ben Wagner216e0772020-02-28 07:43:00 +01003907 " no variation data for glyph %d\n", glyph_index ));
Werner Lemberge9df4e42015-05-31 12:21:34 +02003908 return FT_Err_Ok;
3909 }
3910
3911 if ( FT_NEW_ARRAY( points_org, n_points ) ||
Werner Lembergf147fb22016-07-16 07:06:21 +02003912 FT_NEW_ARRAY( points_out, n_points ) ||
Werner Lemberge9df4e42015-05-31 12:21:34 +02003913 FT_NEW_ARRAY( has_delta, n_points ) )
3914 goto Fail1;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003915
Werner Lemberg65681e62018-09-12 07:40:49 +02003916 dataSize = blend->glyphoffsets[glyph_index + 1] -
3917 blend->glyphoffsets[glyph_index];
3918
3919 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) ||
3920 FT_FRAME_ENTER( dataSize ) )
Werner Lemberg3bcad432004-05-06 11:48:35 +00003921 goto Fail1;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003922
3923 glyph_start = FT_Stream_FTell( stream );
3924
3925 /* each set of glyph variation data is formatted similarly to `cvar' */
Werner Lemberg44bb3032004-04-25 20:15:11 +00003926
3927 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) ||
3928 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3929 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) )
Werner Lemberg3bcad432004-05-06 11:48:35 +00003930 goto Fail2;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003931
3932 tupleCount = FT_GET_USHORT();
Werner Lemberg052f6c52015-10-13 08:24:32 +02003933 offsetToData = FT_GET_USHORT();
3934
3935 /* rough sanity test */
Ben Wagnerfb0d66d2018-11-07 00:47:44 +01003936 if ( offsetToData > dataSize ||
3937 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > dataSize )
Werner Lemberg052f6c52015-10-13 08:24:32 +02003938 {
3939 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3940 " invalid glyph variation array header\n" ));
3941
3942 error = FT_THROW( Invalid_Table );
3943 goto Fail2;
3944 }
3945
3946 offsetToData += glyph_start;
Werner Lemberg44bb3032004-04-25 20:15:11 +00003947
3948 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3949 {
3950 here = FT_Stream_FTell( stream );
3951
Yamato, Masatake (大和正武)17f4b8b2004-05-07 08:59:29 +00003952 FT_Stream_SeekSet( stream, offsetToData );
Werner Lemberg44bb3032004-04-25 20:15:11 +00003953
Werner Lemberg43a96eb2015-10-13 11:18:55 +02003954 sharedpoints = ft_var_readpackedpoints( stream,
3955 blend->gvar_size,
3956 &spoint_count );
Yamato, Masatake (大和正武)17f4b8b2004-05-07 08:59:29 +00003957 offsetToData = FT_Stream_FTell( stream );
Werner Lemberg44bb3032004-04-25 20:15:11 +00003958
3959 FT_Stream_SeekSet( stream, here );
3960 }
3961
Werner Lemberg71fecc52017-12-05 12:06:29 +01003962 FT_TRACE5(( "gvar: there %s %d tuple%s:\n",
3963 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3964 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3965 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
Werner Lemberge9df4e42015-05-31 12:21:34 +02003966
Werner Lemberga632fb52018-06-24 15:22:10 +02003967 if ( FT_NEW_ARRAY( point_deltas_x, n_points ) ||
3968 FT_NEW_ARRAY( point_deltas_y, n_points ) )
3969 goto Fail3;
3970
Behdad Esfahbod0f9ddf22016-07-14 06:08:39 +02003971 for ( j = 0; j < n_points; j++ )
Werner Lemberga632fb52018-06-24 15:22:10 +02003972 {
3973 points_org[j].x = FT_intToFixed( outline->points[j].x );
3974 points_org[j].y = FT_intToFixed( outline->points[j].y );
3975 }
Behdad Esfahbod0f9ddf22016-07-14 06:08:39 +02003976
Werner Lemberg0aa36162015-03-08 22:50:37 +01003977 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00003978 {
3979 FT_UInt tupleDataSize;
3980 FT_UInt tupleIndex;
3981 FT_Fixed apply;
3982
3983
Werner Lemberge9df4e42015-05-31 12:21:34 +02003984 FT_TRACE6(( " tuple %d:\n", i ));
3985
Werner Lemberg44bb3032004-04-25 20:15:11 +00003986 tupleDataSize = FT_GET_USHORT();
3987 tupleIndex = FT_GET_USHORT();
3988
3989 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3990 {
Werner Lemberg0aa36162015-03-08 22:50:37 +01003991 for ( j = 0; j < blend->num_axis; j++ )
Werner Lembergdc39f762019-05-07 10:09:55 +02003992 tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
Werner Lemberg44bb3032004-04-25 20:15:11 +00003993 }
3994 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3995 {
Werner Lemberg052f6c52015-10-13 08:24:32 +02003996 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3997 " invalid tuple index\n" ));
3998
Werner Lemberg059bc332013-03-14 10:27:35 +01003999 error = FT_THROW( Invalid_Table );
Werner Lemberg207ca382018-06-25 18:50:00 +02004000 goto Fail3;
Werner Lemberg44bb3032004-04-25 20:15:11 +00004001 }
4002 else
Werner Lemberg44bb3032004-04-25 20:15:11 +00004003 FT_MEM_COPY(
4004 tuple_coords,
Alexei Podtelezhnikov1f43aff2018-11-04 13:11:16 -05004005 blend->tuplecoords +
4006 ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
Werner Lemberg44bb3032004-04-25 20:15:11 +00004007 blend->num_axis * sizeof ( FT_Fixed ) );
Werner Lemberg44bb3032004-04-25 20:15:11 +00004008
4009 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
4010 {
Werner Lemberg0aa36162015-03-08 22:50:37 +01004011 for ( j = 0; j < blend->num_axis; j++ )
Werner Lembergdc39f762019-05-07 10:09:55 +02004012 im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
Werner Lemberg0aa36162015-03-08 22:50:37 +01004013 for ( j = 0; j < blend->num_axis; j++ )
Werner Lembergdc39f762019-05-07 10:09:55 +02004014 im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
Werner Lemberg44bb3032004-04-25 20:15:11 +00004015 }
4016
4017 apply = ft_var_apply_tuple( blend,
Werner Lemberg22ad9ef2005-05-09 22:11:36 +00004018 (FT_UShort)tupleIndex,
Werner Lemberg44bb3032004-04-25 20:15:11 +00004019 tuple_coords,
4020 im_start_coords,
4021 im_end_coords );
4022
4023 if ( apply == 0 ) /* tuple isn't active for our blend */
4024 {
Yamato, Masatake (大和正武)17f4b8b2004-05-07 08:59:29 +00004025 offsetToData += tupleDataSize;
Werner Lemberg44bb3032004-04-25 20:15:11 +00004026 continue;
4027 }
4028
4029 here = FT_Stream_FTell( stream );
4030
Werner Lemberg4e659d72016-09-03 18:14:00 +02004031 FT_Stream_SeekSet( stream, offsetToData );
4032
Werner Lemberg44bb3032004-04-25 20:15:11 +00004033 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
4034 {
Werner Lemberg43a96eb2015-10-13 11:18:55 +02004035 localpoints = ft_var_readpackedpoints( stream,
4036 blend->gvar_size,
4037 &point_count );
Werner Lemberg44bb3032004-04-25 20:15:11 +00004038 points = localpoints;
4039 }
4040 else
4041 {
4042 points = sharedpoints;
4043 point_count = spoint_count;
4044 }
4045
4046 deltas_x = ft_var_readpackeddeltas( stream,
Werner Lemberg43a96eb2015-10-13 11:18:55 +02004047 blend->gvar_size,
Werner Lemberg44bb3032004-04-25 20:15:11 +00004048 point_count == 0 ? n_points
4049 : point_count );
4050 deltas_y = ft_var_readpackeddeltas( stream,
Werner Lemberg43a96eb2015-10-13 11:18:55 +02004051 blend->gvar_size,
Werner Lemberg44bb3032004-04-25 20:15:11 +00004052 point_count == 0 ? n_points
4053 : point_count );
4054
Werner Lemberg4441f7b2016-12-26 17:08:17 +01004055 if ( !points || !deltas_y || !deltas_x )
Werner Lemberg3bcad432004-05-06 11:48:35 +00004056 ; /* failure, ignore it */
Werner Lemberg44bb3032004-04-25 20:15:11 +00004057
4058 else if ( points == ALL_POINTS )
4059 {
Werner Lemberge9df4e42015-05-31 12:21:34 +02004060#ifdef FT_DEBUG_LEVEL_TRACE
4061 int count = 0;
4062#endif
4063
4064
4065 FT_TRACE7(( " point deltas:\n" ));
4066
Werner Lemberg44bb3032004-04-25 20:15:11 +00004067 /* this means that there are deltas for every point in the glyph */
Werner Lemberg0aa36162015-03-08 22:50:37 +01004068 for ( j = 0; j < n_points; j++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00004069 {
Werner Lemberga632fb52018-06-24 15:22:10 +02004070 FT_Fixed old_point_delta_x = point_deltas_x[j];
4071 FT_Fixed old_point_delta_y = point_deltas_y[j];
4072
4073 FT_Fixed point_delta_x = FT_MulFix( deltas_x[j], apply );
4074 FT_Fixed point_delta_y = FT_MulFix( deltas_y[j], apply );
Werner Lemberge9df4e42015-05-31 12:21:34 +02004075
4076
Jonathan Kew361af722017-12-17 15:07:02 +01004077 if ( j < n_points - 4 )
Werner Lembergd44daf92016-12-21 23:03:48 +01004078 {
Werner Lemberga632fb52018-06-24 15:22:10 +02004079 point_deltas_x[j] = old_point_delta_x + point_delta_x;
4080 point_deltas_y[j] = old_point_delta_y + point_delta_y;
Werner Lembergd44daf92016-12-21 23:03:48 +01004081 }
4082 else
4083 {
4084 /* To avoid double adjustment of advance width or height, */
4085 /* adjust phantom points only if there is no HVAR or VVAR */
4086 /* support, respectively. */
Jonathan Kew361af722017-12-17 15:07:02 +01004087 if ( j == ( n_points - 4 ) &&
4088 !( face->variation_support &
4089 TT_FACE_FLAG_VAR_LSB ) )
Werner Lemberga632fb52018-06-24 15:22:10 +02004090 point_deltas_x[j] = old_point_delta_x + point_delta_x;
Jonathan Kew361af722017-12-17 15:07:02 +01004091
4092 else if ( j == ( n_points - 3 ) &&
4093 !( face->variation_support &
4094 TT_FACE_FLAG_VAR_HADVANCE ) )
Werner Lemberga632fb52018-06-24 15:22:10 +02004095 point_deltas_x[j] = old_point_delta_x + point_delta_x;
Werner Lemberg3bd79cc2016-12-15 14:34:57 +01004096
Werner Lemberg72091102016-12-21 23:46:29 +01004097 else if ( j == ( n_points - 2 ) &&
Werner Lembergd44daf92016-12-21 23:03:48 +01004098 !( face->variation_support &
Jonathan Kew361af722017-12-17 15:07:02 +01004099 TT_FACE_FLAG_VAR_TSB ) )
Werner Lemberga632fb52018-06-24 15:22:10 +02004100 point_deltas_y[j] = old_point_delta_y + point_delta_y;
Werner Lembergd44daf92016-12-21 23:03:48 +01004101
Werner Lemberg72091102016-12-21 23:46:29 +01004102 else if ( j == ( n_points - 1 ) &&
Werner Lembergd44daf92016-12-21 23:03:48 +01004103 !( face->variation_support &
4104 TT_FACE_FLAG_VAR_VADVANCE ) )
Werner Lemberga632fb52018-06-24 15:22:10 +02004105 point_deltas_y[j] = old_point_delta_y + point_delta_y;
Werner Lembergd44daf92016-12-21 23:03:48 +01004106 }
Werner Lemberge9df4e42015-05-31 12:21:34 +02004107
4108#ifdef FT_DEBUG_LEVEL_TRACE
Werner Lemberga632fb52018-06-24 15:22:10 +02004109 if ( point_delta_x || point_delta_y )
Werner Lemberge9df4e42015-05-31 12:21:34 +02004110 {
Werner Lemberga632fb52018-06-24 15:22:10 +02004111 FT_TRACE7(( " %d: (%f, %f) -> (%f, %f)\n",
Werner Lemberge9df4e42015-05-31 12:21:34 +02004112 j,
Werner Lemberga632fb52018-06-24 15:22:10 +02004113 ( FT_intToFixed( outline->points[j].x ) +
4114 old_point_delta_x ) / 65536.0,
4115 ( FT_intToFixed( outline->points[j].y ) +
4116 old_point_delta_y ) / 65536.0,
4117 ( FT_intToFixed( outline->points[j].x ) +
4118 point_deltas_x[j] ) / 65536.0,
4119 ( FT_intToFixed( outline->points[j].y ) +
4120 point_deltas_y[j] ) / 65536.0 ));
Werner Lemberge9df4e42015-05-31 12:21:34 +02004121 count++;
4122 }
4123#endif
Werner Lemberg44bb3032004-04-25 20:15:11 +00004124 }
Werner Lemberge9df4e42015-05-31 12:21:34 +02004125
4126#ifdef FT_DEBUG_LEVEL_TRACE
4127 if ( !count )
4128 FT_TRACE7(( " none\n" ));
4129#endif
Werner Lemberg44bb3032004-04-25 20:15:11 +00004130 }
4131
4132 else
4133 {
Werner Lemberge9df4e42015-05-31 12:21:34 +02004134#ifdef FT_DEBUG_LEVEL_TRACE
4135 int count = 0;
4136#endif
4137
4138
4139 /* we have to interpolate the missing deltas similar to the */
4140 /* IUP bytecode instruction */
4141 for ( j = 0; j < n_points; j++ )
Werner Lembergf147fb22016-07-16 07:06:21 +02004142 {
Werner Lemberge9df4e42015-05-31 12:21:34 +02004143 has_delta[j] = FALSE;
Werner Lembergf147fb22016-07-16 07:06:21 +02004144 points_out[j] = points_org[j];
4145 }
Werner Lemberge9df4e42015-05-31 12:21:34 +02004146
Werner Lemberg0aa36162015-03-08 22:50:37 +01004147 for ( j = 0; j < point_count; j++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00004148 {
Werner Lemberg4e659d72016-09-03 18:14:00 +02004149 FT_UShort idx = points[j];
Werner Lemberge9df4e42015-05-31 12:21:34 +02004150
4151
4152 if ( idx >= n_points )
Werner Lemberg9c98fbf2011-10-01 09:25:55 +02004153 continue;
4154
Werner Lemberge9df4e42015-05-31 12:21:34 +02004155 has_delta[idx] = TRUE;
4156
Werner Lembergf147fb22016-07-16 07:06:21 +02004157 points_out[idx].x += FT_MulFix( deltas_x[j], apply );
4158 points_out[idx].y += FT_MulFix( deltas_y[j], apply );
Werner Lemberg44bb3032004-04-25 20:15:11 +00004159 }
Werner Lemberge9df4e42015-05-31 12:21:34 +02004160
4161 /* no need to handle phantom points here, */
4162 /* since solitary points can't be interpolated */
Werner Lembergf147fb22016-07-16 07:06:21 +02004163 tt_interpolate_deltas( outline,
4164 points_out,
4165 points_org,
4166 has_delta );
4167
Werner Lemberge9df4e42015-05-31 12:21:34 +02004168 FT_TRACE7(( " point deltas:\n" ));
4169
Werner Lemberga3b61252016-07-16 08:16:16 +02004170 for ( j = 0; j < n_points; j++ )
Werner Lemberge9df4e42015-05-31 12:21:34 +02004171 {
Werner Lemberga632fb52018-06-24 15:22:10 +02004172 FT_Fixed old_point_delta_x = point_deltas_x[j];
4173 FT_Fixed old_point_delta_y = point_deltas_y[j];
4174
4175 FT_Pos point_delta_x = points_out[j].x - points_org[j].x;
4176 FT_Pos point_delta_y = points_out[j].y - points_org[j].y;
Werner Lemberga3b61252016-07-16 08:16:16 +02004177
4178
Werner Lemberge7935f22017-12-18 07:29:57 +01004179 if ( j < n_points - 4 )
4180 {
Werner Lemberga632fb52018-06-24 15:22:10 +02004181 point_deltas_x[j] = old_point_delta_x + point_delta_x;
4182 point_deltas_y[j] = old_point_delta_y + point_delta_y;
Werner Lemberge7935f22017-12-18 07:29:57 +01004183 }
4184 else
4185 {
4186 /* To avoid double adjustment of advance width or height, */
4187 /* adjust phantom points only if there is no HVAR or VVAR */
4188 /* support, respectively. */
4189 if ( j == ( n_points - 4 ) &&
4190 !( face->variation_support &
4191 TT_FACE_FLAG_VAR_LSB ) )
Werner Lemberga632fb52018-06-24 15:22:10 +02004192 point_deltas_x[j] = old_point_delta_x + point_delta_x;
Werner Lemberge7935f22017-12-18 07:29:57 +01004193
4194 else if ( j == ( n_points - 3 ) &&
4195 !( face->variation_support &
4196 TT_FACE_FLAG_VAR_HADVANCE ) )
Werner Lemberga632fb52018-06-24 15:22:10 +02004197 point_deltas_x[j] = old_point_delta_x + point_delta_x;
Werner Lemberge7935f22017-12-18 07:29:57 +01004198
4199 else if ( j == ( n_points - 2 ) &&
4200 !( face->variation_support &
4201 TT_FACE_FLAG_VAR_TSB ) )
Werner Lemberga632fb52018-06-24 15:22:10 +02004202 point_deltas_y[j] = old_point_delta_y + point_delta_y;
Werner Lemberge7935f22017-12-18 07:29:57 +01004203
4204 else if ( j == ( n_points - 1 ) &&
4205 !( face->variation_support &
4206 TT_FACE_FLAG_VAR_VADVANCE ) )
Werner Lemberga632fb52018-06-24 15:22:10 +02004207 point_deltas_y[j] = old_point_delta_y + point_delta_y;
Werner Lemberge7935f22017-12-18 07:29:57 +01004208 }
Werner Lemberga3b61252016-07-16 08:16:16 +02004209
4210#ifdef FT_DEBUG_LEVEL_TRACE
Werner Lemberga632fb52018-06-24 15:22:10 +02004211 if ( point_delta_x || point_delta_y )
Werner Lemberge9df4e42015-05-31 12:21:34 +02004212 {
Werner Lemberga632fb52018-06-24 15:22:10 +02004213 FT_TRACE7(( " %d: (%f, %f) -> (%f, %f)\n",
Werner Lemberge9df4e42015-05-31 12:21:34 +02004214 j,
Werner Lemberga632fb52018-06-24 15:22:10 +02004215 ( FT_intToFixed( outline->points[j].x ) +
4216 old_point_delta_x ) / 65536.0,
4217 ( FT_intToFixed( outline->points[j].y ) +
4218 old_point_delta_y ) / 65536.0,
4219 ( FT_intToFixed( outline->points[j].x ) +
4220 point_deltas_x[j] ) / 65536.0,
4221 ( FT_intToFixed( outline->points[j].y ) +
4222 point_deltas_y[j] ) / 65536.0 ));
Werner Lemberge9df4e42015-05-31 12:21:34 +02004223 count++;
4224 }
Werner Lemberga3b61252016-07-16 08:16:16 +02004225#endif
Werner Lemberge9df4e42015-05-31 12:21:34 +02004226 }
4227
Werner Lemberga3b61252016-07-16 08:16:16 +02004228#ifdef FT_DEBUG_LEVEL_TRACE
Werner Lemberge9df4e42015-05-31 12:21:34 +02004229 if ( !count )
4230 FT_TRACE7(( " none\n" ));
4231#endif
Werner Lemberg44bb3032004-04-25 20:15:11 +00004232 }
4233
4234 if ( localpoints != ALL_POINTS )
4235 FT_FREE( localpoints );
4236 FT_FREE( deltas_x );
4237 FT_FREE( deltas_y );
4238
Yamato, Masatake (大和正武)17f4b8b2004-05-07 08:59:29 +00004239 offsetToData += tupleDataSize;
Werner Lemberg44bb3032004-04-25 20:15:11 +00004240
4241 FT_Stream_SeekSet( stream, here );
4242 }
4243
Werner Lemberge9df4e42015-05-31 12:21:34 +02004244 FT_TRACE5(( "\n" ));
4245
Werner Lemberga632fb52018-06-24 15:22:10 +02004246 for ( i = 0; i < n_points; i++ )
4247 {
Werner Lemberg306d2f62019-05-23 14:41:57 +02004248 unrounded[i].x += FT_fixedToFdot6( point_deltas_x[i] );
4249 unrounded[i].y += FT_fixedToFdot6( point_deltas_y[i] );
Werner Lemberg11782272019-05-11 09:29:19 +02004250
Werner Lemberga632fb52018-06-24 15:22:10 +02004251 outline->points[i].x += FT_fixedToInt( point_deltas_x[i] );
4252 outline->points[i].y += FT_fixedToInt( point_deltas_y[i] );
4253 }
4254
4255 Fail3:
4256 FT_FREE( point_deltas_x );
4257 FT_FREE( point_deltas_y );
4258
Werner Lemberge9df4e42015-05-31 12:21:34 +02004259 Fail2:
Werner Lemberg7ef0d862015-10-13 08:14:20 +02004260 if ( sharedpoints != ALL_POINTS )
4261 FT_FREE( sharedpoints );
Werner Lemberg44bb3032004-04-25 20:15:11 +00004262 FT_FREE( tuple_coords );
4263 FT_FREE( im_start_coords );
4264 FT_FREE( im_end_coords );
4265
Werner Lemberg3bcad432004-05-06 11:48:35 +00004266 FT_FRAME_EXIT();
4267
4268 Fail1:
Werner Lemberge9df4e42015-05-31 12:21:34 +02004269 FT_FREE( points_org );
Werner Lembergf147fb22016-07-16 07:06:21 +02004270 FT_FREE( points_out );
Werner Lemberge9df4e42015-05-31 12:21:34 +02004271 FT_FREE( has_delta );
Werner Lemberg3bcad432004-05-06 11:48:35 +00004272
Werner Lemberg44bb3032004-04-25 20:15:11 +00004273 return error;
4274 }
4275
4276
Werner Lemberg9ac90602018-06-03 09:01:17 +02004277 /**************************************************************************
4278 *
4279 * @Function:
4280 * tt_get_var_blend
4281 *
4282 * @Description:
4283 * An extended internal version of `TT_Get_MM_Blend' that returns
4284 * pointers instead of copying data, without any initialization of
4285 * the MM machinery in case it isn't loaded yet.
4286 */
Werner Lemberg8b174b42016-12-11 09:16:52 +01004287 FT_LOCAL_DEF( FT_Error )
Werner Lemberga8652c52016-12-18 18:12:03 +01004288 tt_get_var_blend( TT_Face face,
4289 FT_UInt *num_coords,
4290 FT_Fixed* *coords,
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01004291 FT_Fixed* *normalizedcoords,
Werner Lemberga8652c52016-12-18 18:12:03 +01004292 FT_MM_Var* *mm_var )
Werner Lemberg8b174b42016-12-11 09:16:52 +01004293 {
Werner Lemberg8b174b42016-12-11 09:16:52 +01004294 if ( face->blend )
4295 {
Werner Lemberga8652c52016-12-18 18:12:03 +01004296 if ( num_coords )
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01004297 *num_coords = face->blend->num_axis;
Werner Lemberga8652c52016-12-18 18:12:03 +01004298 if ( coords )
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01004299 *coords = face->blend->coords;
4300 if ( normalizedcoords )
4301 *normalizedcoords = face->blend->normalizedcoords;
Werner Lemberga8652c52016-12-18 18:12:03 +01004302 if ( mm_var )
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01004303 *mm_var = face->blend->mmvar;
Werner Lemberg8b174b42016-12-11 09:16:52 +01004304 }
4305 else
4306 {
Werner Lemberga8652c52016-12-18 18:12:03 +01004307 if ( num_coords )
4308 *num_coords = 0;
4309 if ( coords )
4310 *coords = NULL;
4311 if ( mm_var )
4312 *mm_var = NULL;
Werner Lemberg8b174b42016-12-11 09:16:52 +01004313 }
4314
4315 return FT_Err_Ok;
4316 }
4317
4318
Werner Lemberg02919132017-01-07 07:45:28 +01004319 static void
4320 ft_var_done_item_variation_store( TT_Face face,
4321 GX_ItemVarStore itemStore )
4322 {
4323 FT_Memory memory = FT_FACE_MEMORY( face );
4324 FT_UInt i;
4325
4326
4327 if ( itemStore->varData )
4328 {
4329 for ( i = 0; i < itemStore->dataCount; i++ )
4330 {
4331 FT_FREE( itemStore->varData[i].regionIndices );
4332 FT_FREE( itemStore->varData[i].deltaSet );
4333 }
4334
4335 FT_FREE( itemStore->varData );
4336 }
4337
4338 if ( itemStore->varRegionList )
4339 {
4340 for ( i = 0; i < itemStore->regionCount; i++ )
4341 FT_FREE( itemStore->varRegionList[i].axisList );
4342
4343 FT_FREE( itemStore->varRegionList );
4344 }
4345 }
4346
4347
Werner Lemberg9ac90602018-06-03 09:01:17 +02004348 /**************************************************************************
4349 *
4350 * @Function:
4351 * tt_done_blend
4352 *
4353 * @Description:
4354 * Free the blend internal data structure.
4355 */
Werner Lemberg44bb3032004-04-25 20:15:11 +00004356 FT_LOCAL_DEF( void )
Werner Lemberg27bdb362016-12-10 09:56:03 +01004357 tt_done_blend( TT_Face face )
Werner Lemberg44bb3032004-04-25 20:15:11 +00004358 {
Werner Lemberg27bdb362016-12-10 09:56:03 +01004359 FT_Memory memory = FT_FACE_MEMORY( face );
4360 GX_Blend blend = face->blend;
4361
4362
Werner Lemberg4441f7b2016-12-26 17:08:17 +01004363 if ( blend )
Werner Lemberg44bb3032004-04-25 20:15:11 +00004364 {
Werner Lemberg92281222016-10-11 07:12:12 +02004365 FT_UInt i, num_axes;
Werner Lemberg44bb3032004-04-25 20:15:11 +00004366
4367
Werner Lemberg92281222016-10-11 07:12:12 +02004368 /* blend->num_axis might not be set up yet */
4369 num_axes = blend->mmvar->num_axis;
4370
Werner Lemberg5eb0fd52017-03-12 20:46:56 +01004371 FT_FREE( blend->coords );
Werner Lemberg44bb3032004-04-25 20:15:11 +00004372 FT_FREE( blend->normalizedcoords );
Werner Lemberg27fee7f2017-03-06 20:45:44 +01004373 FT_FREE( blend->normalized_stylecoords );
Werner Lemberg44bb3032004-04-25 20:15:11 +00004374 FT_FREE( blend->mmvar );
4375
Werner Lemberg4441f7b2016-12-26 17:08:17 +01004376 if ( blend->avar_segment )
Werner Lemberg44bb3032004-04-25 20:15:11 +00004377 {
Werner Lemberg92281222016-10-11 07:12:12 +02004378 for ( i = 0; i < num_axes; i++ )
Werner Lemberg44bb3032004-04-25 20:15:11 +00004379 FT_FREE( blend->avar_segment[i].correspondence );
4380 FT_FREE( blend->avar_segment );
4381 }
4382
Werner Lemberg4441f7b2016-12-26 17:08:17 +01004383 if ( blend->hvar_table )
Dave Arnold097cd872016-12-15 12:58:26 +01004384 {
Werner Lemberg02919132017-01-07 07:45:28 +01004385 ft_var_done_item_variation_store( face,
4386 &blend->hvar_table->itemStore );
Dave Arnold097cd872016-12-15 12:58:26 +01004387
4388 FT_FREE( blend->hvar_table->widthMap.innerIndex );
4389 FT_FREE( blend->hvar_table->widthMap.outerIndex );
4390 FT_FREE( blend->hvar_table );
4391 }
4392
Werner Lembergd9ff6f22017-03-16 20:20:51 +01004393 if ( blend->vvar_table )
4394 {
4395 ft_var_done_item_variation_store( face,
4396 &blend->vvar_table->itemStore );
4397
4398 FT_FREE( blend->vvar_table->widthMap.innerIndex );
4399 FT_FREE( blend->vvar_table->widthMap.outerIndex );
4400 FT_FREE( blend->vvar_table );
4401 }
4402
Werner Lemberg07ee1d22017-01-11 12:50:51 +01004403 if ( blend->mvar_table )
4404 {
4405 ft_var_done_item_variation_store( face,
4406 &blend->mvar_table->itemStore );
4407
4408 FT_FREE( blend->mvar_table->values );
4409 FT_FREE( blend->mvar_table );
4410 }
4411
Werner Lemberg44bb3032004-04-25 20:15:11 +00004412 FT_FREE( blend->tuplecoords );
4413 FT_FREE( blend->glyphoffsets );
4414 FT_FREE( blend );
4415 }
4416 }
4417
Werner Lemberg99311752017-03-18 07:06:49 +01004418#else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4419
4420 /* ANSI C doesn't like empty source files */
4421 typedef int _tt_gxvar_dummy;
4422
4423#endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
Werner Lemberg44bb3032004-04-25 20:15:11 +00004424
4425
4426/* END */