Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3918 Serge 1
/***************************************************************************/
2
/*                                                                         */
3
/*  ttgxvar.c                                                              */
4
/*                                                                         */
5
/*    TrueType GX Font Variation loader                                    */
6
/*                                                                         */
7
/*  Copyright 2004-2013 by                                                 */
8
/*  David Turner, Robert Wilhelm, Werner Lemberg, and George Williams.     */
9
/*                                                                         */
10
/*  This file is part of the FreeType project, and may only be used,       */
11
/*  modified, and distributed under the terms of the FreeType project      */
12
/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
13
/*  this file you indicate that you have read the license and              */
14
/*  understand and accept it fully.                                        */
15
/*                                                                         */
16
/***************************************************************************/
17
 
18
 
19
  /*************************************************************************/
20
  /*                                                                       */
21
  /* Apple documents the `fvar', `gvar', `cvar', and `avar' tables at      */
22
  /*                                                                       */
23
  /*   http://developer.apple.com/fonts/TTRefMan/RM06/Chap6[fgca]var.html  */
24
  /*                                                                       */
25
  /* The documentation for `fvar' is inconsistent.  At one point it says   */
26
  /* that `countSizePairs' should be 3, at another point 2.  It should     */
27
  /* be 2.                                                                 */
28
  /*                                                                       */
29
  /* The documentation for `gvar' is not intelligible; `cvar' refers you   */
30
  /* to `gvar' and is thus also incomprehensible.                          */
31
  /*                                                                       */
32
  /* The documentation for `avar' appears correct, but Apple has no fonts  */
33
  /* with an `avar' table, so it is hard to test.                          */
34
  /*                                                                       */
35
  /* Many thanks to John Jenkins (at Apple) in figuring this out.          */
36
  /*                                                                       */
37
  /*                                                                       */
38
  /* Apple's `kern' table has some references to tuple indices, but as     */
39
  /* there is no indication where these indices are defined, nor how to    */
40
  /* interpolate the kerning values (different tuples have different       */
41
  /* classes) this issue is ignored.                                       */
42
  /*                                                                       */
43
  /*************************************************************************/
44
 
45
 
46
#include 
47
#include FT_INTERNAL_DEBUG_H
48
#include FT_CONFIG_CONFIG_H
49
#include FT_INTERNAL_STREAM_H
50
#include FT_INTERNAL_SFNT_H
51
#include FT_TRUETYPE_TAGS_H
52
#include FT_MULTIPLE_MASTERS_H
53
 
54
#include "ttpload.h"
55
#include "ttgxvar.h"
56
 
57
#include "tterrors.h"
58
 
59
 
60
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
61
 
62
 
63
#define FT_Stream_FTell( stream )  \
64
          (FT_ULong)( (stream)->cursor - (stream)->base )
65
#define FT_Stream_SeekSet( stream, off ) \
66
          ( (stream)->cursor = (stream)->base + (off) )
67
 
68
 
69
  /*************************************************************************/
70
  /*                                                                       */
71
  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
72
  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
73
  /* messages during execution.                                            */
74
  /*                                                                       */
75
#undef  FT_COMPONENT
76
#define FT_COMPONENT  trace_ttgxvar
77
 
78
 
79
  /*************************************************************************/
80
  /*************************************************************************/
81
  /*****                                                               *****/
82
  /*****                       Internal Routines                       *****/
83
  /*****                                                               *****/
84
  /*************************************************************************/
85
  /*************************************************************************/
86
 
87
 
88
  /*************************************************************************/
89
  /*                                                                       */
90
  /* The macro ALL_POINTS is used in `ft_var_readpackedpoints'.  It        */
91
  /* indicates that there is a delta for every point without needing to    */
92
  /* enumerate all of them.                                                */
93
  /*                                                                       */
94
 
95
  /* ensure that value `0' has the same width as a pointer */
96
#define ALL_POINTS  (FT_UShort*)~(FT_PtrDist)0
97
 
98
 
99
#define GX_PT_POINTS_ARE_WORDS      0x80
100
#define GX_PT_POINT_RUN_COUNT_MASK  0x7F
101
 
102
 
103
  /*************************************************************************/
104
  /*                                                                       */
105
  /*                                                             */
106
  /*    ft_var_readpackedpoints                                            */
107
  /*                                                                       */
108
  /*                                                          */
109
  /*    Read a set of points to which the following deltas will apply.     */
110
  /*    Points are packed with a run length encoding.                      */
111
  /*                                                                       */
112
  /*                                                                */
113
  /*    stream    :: The data stream.                                      */
114
  /*                                                                       */
115
  /*                                                               */
116
  /*    point_cnt :: The number of points read.  A zero value means that   */
117
  /*                 all points in the glyph will be affected, without     */
118
  /*                 enumerating them individually.                        */
119
  /*                                                                       */
120
  /*                                                               */
121
  /*    An array of FT_UShort containing the affected points or the        */
122
  /*    special value ALL_POINTS.                                          */
123
  /*                                                                       */
124
  static FT_UShort*
125
  ft_var_readpackedpoints( FT_Stream  stream,
126
                           FT_UInt   *point_cnt )
127
  {
128
    FT_UShort *points = NULL;
129
    FT_Int     n;
130
    FT_Int     runcnt;
131
    FT_Int     i;
132
    FT_Int     j;
133
    FT_Int     first;
134
    FT_Memory  memory = stream->memory;
135
    FT_Error   error  = FT_Err_Ok;
136
 
137
    FT_UNUSED( error );
138
 
139
 
140
    *point_cnt = n = FT_GET_BYTE();
141
    if ( n == 0 )
142
      return ALL_POINTS;
143
 
144
    if ( n & GX_PT_POINTS_ARE_WORDS )
145
      n = FT_GET_BYTE() | ( ( n & GX_PT_POINT_RUN_COUNT_MASK ) << 8 );
146
 
147
    if ( FT_NEW_ARRAY( points, n ) )
148
      return NULL;
149
 
150
    i = 0;
151
    while ( i < n )
152
    {
153
      runcnt = FT_GET_BYTE();
154
      if ( runcnt & GX_PT_POINTS_ARE_WORDS )
155
      {
156
        runcnt = runcnt & GX_PT_POINT_RUN_COUNT_MASK;
157
        first  = points[i++] = FT_GET_USHORT();
158
 
159
        if ( runcnt < 1 || i + runcnt >= n )
160
          goto Exit;
161
 
162
        /* first point not included in runcount */
163
        for ( j = 0; j < runcnt; ++j )
164
          points[i++] = (FT_UShort)( first += FT_GET_USHORT() );
165
      }
166
      else
167
      {
168
        first = points[i++] = FT_GET_BYTE();
169
 
170
        if ( runcnt < 1 || i + runcnt >= n )
171
          goto Exit;
172
 
173
        for ( j = 0; j < runcnt; ++j )
174
          points[i++] = (FT_UShort)( first += FT_GET_BYTE() );
175
      }
176
    }
177
 
178
  Exit:
179
    return points;
180
  }
181
 
182
 
183
  enum
184
  {
185
    GX_DT_DELTAS_ARE_ZERO      = 0x80,
186
    GX_DT_DELTAS_ARE_WORDS     = 0x40,
187
    GX_DT_DELTA_RUN_COUNT_MASK = 0x3F
188
  };
189
 
190
 
191
  /*************************************************************************/
192
  /*                                                                       */
193
  /*                                                             */
194
  /*    ft_var_readpackeddeltas                                            */
195
  /*                                                                       */
196
  /*                                                          */
197
  /*    Read a set of deltas.  These are packed slightly differently than  */
198
  /*    points.  In particular there is no overall count.                  */
199
  /*                                                                       */
200
  /*                                                                */
201
  /*    stream    :: The data stream.                                      */
202
  /*                                                                       */
203
  /*    delta_cnt :: The number of to be read.                             */
204
  /*                                                                       */
205
  /*                                                               */
206
  /*    An array of FT_Short containing the deltas for the affected        */
207
  /*    points.  (This only gets the deltas for one dimension.  It will    */
208
  /*    generally be called twice, once for x, once for y.  When used in   */
209
  /*    cvt table, it will only be called once.)                           */
210
  /*                                                                       */
211
  static FT_Short*
212
  ft_var_readpackeddeltas( FT_Stream  stream,
213
                           FT_Offset  delta_cnt )
214
  {
215
    FT_Short  *deltas = NULL;
216
    FT_UInt    runcnt;
217
    FT_Offset  i;
218
    FT_UInt    j;
219
    FT_Memory  memory = stream->memory;
220
    FT_Error   error  = FT_Err_Ok;
221
 
222
    FT_UNUSED( error );
223
 
224
 
225
    if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
226
      return NULL;
227
 
228
    i = 0;
229
    while ( i < delta_cnt )
230
    {
231
      runcnt = FT_GET_BYTE();
232
      if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
233
      {
234
        /* runcnt zeroes get added */
235
        for ( j = 0;
236
              j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
237
              ++j )
238
          deltas[i++] = 0;
239
      }
240
      else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
241
      {
242
        /* runcnt shorts from the stack */
243
        for ( j = 0;
244
              j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
245
              ++j )
246
          deltas[i++] = FT_GET_SHORT();
247
      }
248
      else
249
      {
250
        /* runcnt signed bytes from the stack */
251
        for ( j = 0;
252
              j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
253
              ++j )
254
          deltas[i++] = FT_GET_CHAR();
255
      }
256
 
257
      if ( j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) )
258
      {
259
        /* Bad format */
260
        FT_FREE( deltas );
261
        return NULL;
262
      }
263
    }
264
 
265
    return deltas;
266
  }
267
 
268
 
269
  /*************************************************************************/
270
  /*                                                                       */
271
  /*                                                             */
272
  /*    ft_var_load_avar                                                   */
273
  /*                                                                       */
274
  /*                                                          */
275
  /*    Parse the `avar' table if present.  It need not be, so we return   */
276
  /*    nothing.                                                           */
277
  /*                                                                       */
278
  /*                                                                */
279
  /*    face :: The font face.                                             */
280
  /*                                                                       */
281
  static void
282
  ft_var_load_avar( TT_Face  face )
283
  {
284
    FT_Stream       stream = FT_FACE_STREAM(face);
285
    FT_Memory       memory = stream->memory;
286
    GX_Blend        blend  = face->blend;
287
    GX_AVarSegment  segment;
288
    FT_Error        error = FT_Err_Ok;
289
    FT_ULong        version;
290
    FT_Long         axisCount;
291
    FT_Int          i, j;
292
    FT_ULong        table_len;
293
 
294
    FT_UNUSED( error );
295
 
296
 
297
    blend->avar_checked = TRUE;
298
    if ( (error = face->goto_table( face, TTAG_avar, stream, &table_len )) != 0 )
299
      return;
300
 
301
    if ( FT_FRAME_ENTER( table_len ) )
302
      return;
303
 
304
    version   = FT_GET_LONG();
305
    axisCount = FT_GET_LONG();
306
 
307
    if ( version != 0x00010000L                       ||
308
         axisCount != (FT_Long)blend->mmvar->num_axis )
309
      goto Exit;
310
 
311
    if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) )
312
      goto Exit;
313
 
314
    segment = &blend->avar_segment[0];
315
    for ( i = 0; i < axisCount; ++i, ++segment )
316
    {
317
      segment->pairCount = FT_GET_USHORT();
318
      if ( FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
319
      {
320
        /* Failure.  Free everything we have done so far.  We must do */
321
        /* it right now since loading the `avar' table is optional.   */
322
 
323
        for ( j = i - 1; j >= 0; --j )
324
          FT_FREE( blend->avar_segment[j].correspondence );
325
 
326
        FT_FREE( blend->avar_segment );
327
        blend->avar_segment = NULL;
328
        goto Exit;
329
      }
330
 
331
      for ( j = 0; j < segment->pairCount; ++j )
332
      {
333
        segment->correspondence[j].fromCoord =
334
          FT_GET_SHORT() << 2;    /* convert to Fixed */
335
        segment->correspondence[j].toCoord =
336
          FT_GET_SHORT()<<2;    /* convert to Fixed */
337
      }
338
    }
339
 
340
  Exit:
341
    FT_FRAME_EXIT();
342
  }
343
 
344
 
345
  typedef struct  GX_GVar_Head_
346
  {
347
    FT_Long    version;
348
    FT_UShort  axisCount;
349
    FT_UShort  globalCoordCount;
350
    FT_ULong   offsetToCoord;
351
    FT_UShort  glyphCount;
352
    FT_UShort  flags;
353
    FT_ULong   offsetToData;
354
 
355
  } GX_GVar_Head;
356
 
357
 
358
  /*************************************************************************/
359
  /*                                                                       */
360
  /*                                                             */
361
  /*    ft_var_load_gvar                                                   */
362
  /*                                                                       */
363
  /*                                                          */
364
  /*    Parses the `gvar' table if present.  If `fvar' is there, `gvar'    */
365
  /*    had better be there too.                                           */
366
  /*                                                                       */
367
  /*                                                                */
368
  /*    face :: The font face.                                             */
369
  /*                                                                       */
370
  /*                                                               */
371
  /*    FreeType error code.  0 means success.                             */
372
  /*                                                                       */
373
  static FT_Error
374
  ft_var_load_gvar( TT_Face  face )
375
  {
376
    FT_Stream     stream = FT_FACE_STREAM(face);
377
    FT_Memory     memory = stream->memory;
378
    GX_Blend      blend  = face->blend;
379
    FT_Error      error;
380
    FT_UInt       i, j;
381
    FT_ULong      table_len;
382
    FT_ULong      gvar_start;
383
    FT_ULong      offsetToData;
384
    GX_GVar_Head  gvar_head;
385
 
386
    static const FT_Frame_Field  gvar_fields[] =
387
    {
388
 
389
#undef  FT_STRUCTURE
390
#define FT_STRUCTURE  GX_GVar_Head
391
 
392
      FT_FRAME_START( 20 ),
393
        FT_FRAME_LONG  ( version ),
394
        FT_FRAME_USHORT( axisCount ),
395
        FT_FRAME_USHORT( globalCoordCount ),
396
        FT_FRAME_ULONG ( offsetToCoord ),
397
        FT_FRAME_USHORT( glyphCount ),
398
        FT_FRAME_USHORT( flags ),
399
        FT_FRAME_ULONG ( offsetToData ),
400
      FT_FRAME_END
401
    };
402
 
403
    if ( (error = face->goto_table( face, TTAG_gvar, stream, &table_len )) != 0 )
404
      goto Exit;
405
 
406
    gvar_start = FT_STREAM_POS( );
407
    if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
408
      goto Exit;
409
 
410
    blend->tuplecount  = gvar_head.globalCoordCount;
411
    blend->gv_glyphcnt = gvar_head.glyphCount;
412
    offsetToData       = gvar_start + gvar_head.offsetToData;
413
 
414
    if ( gvar_head.version   != (FT_Long)0x00010000L              ||
415
         gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
416
    {
417
      error = FT_THROW( Invalid_Table );
418
      goto Exit;
419
    }
420
 
421
    if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
422
      goto Exit;
423
 
424
    if ( gvar_head.flags & 1 )
425
    {
426
      /* long offsets (one more offset than glyphs, to mark size of last) */
427
      if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
428
        goto Exit;
429
 
430
      for ( i = 0; i <= blend->gv_glyphcnt; ++i )
431
        blend->glyphoffsets[i] = offsetToData + FT_GET_LONG();
432
 
433
      FT_FRAME_EXIT();
434
    }
435
    else
436
    {
437
      /* short offsets (one more offset than glyphs, to mark size of last) */
438
      if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
439
        goto Exit;
440
 
441
      for ( i = 0; i <= blend->gv_glyphcnt; ++i )
442
        blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
443
                                              /* XXX: Undocumented: `*2'! */
444
 
445
      FT_FRAME_EXIT();
446
    }
447
 
448
    if ( blend->tuplecount != 0 )
449
    {
450
      if ( FT_NEW_ARRAY( blend->tuplecoords,
451
                         gvar_head.axisCount * blend->tuplecount ) )
452
        goto Exit;
453
 
454
      if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord )       ||
455
           FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L )                   )
456
        goto Exit;
457
 
458
      for ( i = 0; i < blend->tuplecount; ++i )
459
        for ( j = 0 ; j < (FT_UInt)gvar_head.axisCount; ++j )
460
          blend->tuplecoords[i * gvar_head.axisCount + j] =
461
            FT_GET_SHORT() << 2;                /* convert to FT_Fixed */
462
 
463
      FT_FRAME_EXIT();
464
    }
465
 
466
  Exit:
467
    return error;
468
  }
469
 
470
 
471
  /*************************************************************************/
472
  /*                                                                       */
473
  /*                                                             */
474
  /*    ft_var_apply_tuple                                                 */
475
  /*                                                                       */
476
  /*                                                          */
477
  /*    Figure out whether a given tuple (design) applies to the current   */
478
  /*    blend, and if so, what is the scaling factor.                      */
479
  /*                                                                       */
480
  /*                                                                */
481
  /*    blend           :: The current blend of the font.                  */
482
  /*                                                                       */
483
  /*    tupleIndex      :: A flag saying whether this is an intermediate   */
484
  /*                       tuple or not.                                   */
485
  /*                                                                       */
486
  /*    tuple_coords    :: The coordinates of the tuple in normalized axis */
487
  /*                       units.                                          */
488
  /*                                                                       */
489
  /*    im_start_coords :: The initial coordinates where this tuple starts */
490
  /*                       to apply (for intermediate coordinates).        */
491
  /*                                                                       */
492
  /*    im_end_coords   :: The final coordinates after which this tuple no */
493
  /*                       longer applies (for intermediate coordinates).  */
494
  /*                                                                       */
495
  /*                                                               */
496
  /*    An FT_Fixed value containing the scaling factor.                   */
497
  /*                                                                       */
498
  static FT_Fixed
499
  ft_var_apply_tuple( GX_Blend   blend,
500
                      FT_UShort  tupleIndex,
501
                      FT_Fixed*  tuple_coords,
502
                      FT_Fixed*  im_start_coords,
503
                      FT_Fixed*  im_end_coords )
504
  {
505
    FT_UInt   i;
506
    FT_Fixed  apply = 0x10000L;
507
 
508
 
509
    for ( i = 0; i < blend->num_axis; ++i )
510
    {
511
      if ( tuple_coords[i] == 0 )
512
        /* It's not clear why (for intermediate tuples) we don't need     */
513
        /* to check against start/end -- the documentation says we don't. */
514
        /* Similarly, it's unclear why we don't need to scale along the   */
515
        /* axis.                                                          */
516
        continue;
517
 
518
      else if ( blend->normalizedcoords[i] == 0                           ||
519
                ( blend->normalizedcoords[i] < 0 && tuple_coords[i] > 0 ) ||
520
                ( blend->normalizedcoords[i] > 0 && tuple_coords[i] < 0 ) )
521
      {
522
        apply = 0;
523
        break;
524
      }
525
 
526
      else if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
527
        /* not an intermediate tuple */
528
        apply = FT_MulFix( apply,
529
                           blend->normalizedcoords[i] > 0
530
                             ? blend->normalizedcoords[i]
531
                             : -blend->normalizedcoords[i] );
532
 
533
      else if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
534
                blend->normalizedcoords[i] >= im_end_coords[i]   )
535
      {
536
        apply = 0;
537
        break;
538
      }
539
 
540
      else if ( blend->normalizedcoords[i] < tuple_coords[i] )
541
        apply = FT_MulDiv( apply,
542
                           blend->normalizedcoords[i] - im_start_coords[i],
543
                           tuple_coords[i] - im_start_coords[i] );
544
 
545
      else
546
        apply = FT_MulDiv( apply,
547
                           im_end_coords[i] - blend->normalizedcoords[i],
548
                           im_end_coords[i] - tuple_coords[i] );
549
    }
550
 
551
    return apply;
552
  }
553
 
554
 
555
  /*************************************************************************/
556
  /*************************************************************************/
557
  /*****                                                               *****/
558
  /*****               MULTIPLE MASTERS SERVICE FUNCTIONS              *****/
559
  /*****                                                               *****/
560
  /*************************************************************************/
561
  /*************************************************************************/
562
 
563
 
564
  typedef struct  GX_FVar_Head_
565
  {
566
    FT_Long    version;
567
    FT_UShort  offsetToData;
568
    FT_UShort  countSizePairs;
569
    FT_UShort  axisCount;
570
    FT_UShort  axisSize;
571
    FT_UShort  instanceCount;
572
    FT_UShort  instanceSize;
573
 
574
  } GX_FVar_Head;
575
 
576
 
577
  typedef struct  fvar_axis_
578
  {
579
    FT_ULong   axisTag;
580
    FT_ULong   minValue;
581
    FT_ULong   defaultValue;
582
    FT_ULong   maxValue;
583
    FT_UShort  flags;
584
    FT_UShort  nameID;
585
 
586
  } GX_FVar_Axis;
587
 
588
 
589
  /*************************************************************************/
590
  /*                                                                       */
591
  /*                                                             */
592
  /*    TT_Get_MM_Var                                                      */
593
  /*                                                                       */
594
  /*                                                          */
595
  /*    Check that the font's `fvar' table is valid, parse it, and return  */
596
  /*    those data.                                                        */
597
  /*                                                                       */
598
  /*                                                                */
599
  /*    face   :: The font face.                                           */
600
  /*              TT_Get_MM_Var initializes the blend structure.           */
601
  /*                                                                       */
602
  /*                                                               */
603
  /*    master :: The `fvar' data (must be freed by caller).               */
604
  /*                                                                       */
605
  /*                                                               */
606
  /*    FreeType error code.  0 means success.                             */
607
  /*                                                                       */
608
  FT_LOCAL_DEF( FT_Error )
609
  TT_Get_MM_Var( TT_Face      face,
610
                 FT_MM_Var*  *master )
611
  {
612
    FT_Stream            stream = face->root.stream;
613
    FT_Memory            memory = face->root.memory;
614
    FT_ULong             table_len;
615
    FT_Error             error  = FT_Err_Ok;
616
    FT_ULong             fvar_start;
617
    FT_Int               i, j;
618
    FT_MM_Var*           mmvar = NULL;
619
    FT_Fixed*            next_coords;
620
    FT_String*           next_name;
621
    FT_Var_Axis*         a;
622
    FT_Var_Named_Style*  ns;
623
    GX_FVar_Head         fvar_head;
624
 
625
    static const FT_Frame_Field  fvar_fields[] =
626
    {
627
 
628
#undef  FT_STRUCTURE
629
#define FT_STRUCTURE  GX_FVar_Head
630
 
631
      FT_FRAME_START( 16 ),
632
        FT_FRAME_LONG  ( version ),
633
        FT_FRAME_USHORT( offsetToData ),
634
        FT_FRAME_USHORT( countSizePairs ),
635
        FT_FRAME_USHORT( axisCount ),
636
        FT_FRAME_USHORT( axisSize ),
637
        FT_FRAME_USHORT( instanceCount ),
638
        FT_FRAME_USHORT( instanceSize ),
639
      FT_FRAME_END
640
    };
641
 
642
    static const FT_Frame_Field  fvaraxis_fields[] =
643
    {
644
 
645
#undef  FT_STRUCTURE
646
#define FT_STRUCTURE  GX_FVar_Axis
647
 
648
      FT_FRAME_START( 20 ),
649
        FT_FRAME_ULONG ( axisTag ),
650
        FT_FRAME_ULONG ( minValue ),
651
        FT_FRAME_ULONG ( defaultValue ),
652
        FT_FRAME_ULONG ( maxValue ),
653
        FT_FRAME_USHORT( flags ),
654
        FT_FRAME_USHORT( nameID ),
655
      FT_FRAME_END
656
    };
657
 
658
 
659
    if ( face->blend == NULL )
660
    {
661
      /* both `fvar' and `gvar' must be present */
662
      if ( (error = face->goto_table( face, TTAG_gvar,
663
                                      stream, &table_len )) != 0 )
664
        goto Exit;
665
 
666
      if ( (error = face->goto_table( face, TTAG_fvar,
667
                                      stream, &table_len )) != 0 )
668
        goto Exit;
669
 
670
      fvar_start = FT_STREAM_POS( );
671
 
672
      if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
673
        goto Exit;
674
 
675
      if ( fvar_head.version != (FT_Long)0x00010000L                      ||
676
           fvar_head.countSizePairs != 2                                  ||
677
           fvar_head.axisSize != 20                                       ||
678
           /* axisCount limit implied by 16-bit instanceSize */
679
           fvar_head.axisCount > 0x3FFE                                   ||
680
           fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount          ||
681
           /* instanceCount limit implied by limited range of name IDs */
682
           fvar_head.instanceCount > 0x7EFF                               ||
683
           fvar_head.offsetToData + fvar_head.axisCount * 20U +
684
             fvar_head.instanceCount * fvar_head.instanceSize > table_len )
685
      {
686
        error = FT_THROW( Invalid_Table );
687
        goto Exit;
688
      }
689
 
690
      if ( FT_NEW( face->blend ) )
691
        goto Exit;
692
 
693
      /* cannot overflow 32-bit arithmetic because of limits above */
694
      face->blend->mmvar_len =
695
        sizeof ( FT_MM_Var ) +
696
        fvar_head.axisCount * sizeof ( FT_Var_Axis ) +
697
        fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) +
698
        fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) +
699
        5 * fvar_head.axisCount;
700
 
701
      if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
702
        goto Exit;
703
      face->blend->mmvar = mmvar;
704
 
705
      mmvar->num_axis =
706
        fvar_head.axisCount;
707
      mmvar->num_designs =
708
        ~0U;                   /* meaningless in this context; each glyph */
709
                               /* may have a different number of designs  */
710
                               /* (or tuples, as called by Apple)         */
711
      mmvar->num_namedstyles =
712
        fvar_head.instanceCount;
713
      mmvar->axis =
714
        (FT_Var_Axis*)&(mmvar[1]);
715
      mmvar->namedstyle =
716
        (FT_Var_Named_Style*)&(mmvar->axis[fvar_head.axisCount]);
717
 
718
      next_coords =
719
        (FT_Fixed*)&(mmvar->namedstyle[fvar_head.instanceCount]);
720
      for ( i = 0; i < fvar_head.instanceCount; ++i )
721
      {
722
        mmvar->namedstyle[i].coords  = next_coords;
723
        next_coords                 += fvar_head.axisCount;
724
      }
725
 
726
      next_name = (FT_String*)next_coords;
727
      for ( i = 0; i < fvar_head.axisCount; ++i )
728
      {
729
        mmvar->axis[i].name  = next_name;
730
        next_name           += 5;
731
      }
732
 
733
      if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
734
        goto Exit;
735
 
736
      a = mmvar->axis;
737
      for ( i = 0; i < fvar_head.axisCount; ++i )
738
      {
739
        GX_FVar_Axis  axis_rec;
740
 
741
 
742
        if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
743
          goto Exit;
744
        a->tag     = axis_rec.axisTag;
745
        a->minimum = axis_rec.minValue;     /* A Fixed */
746
        a->def     = axis_rec.defaultValue; /* A Fixed */
747
        a->maximum = axis_rec.maxValue;     /* A Fixed */
748
        a->strid   = axis_rec.nameID;
749
 
750
        a->name[0] = (FT_String)(   a->tag >> 24 );
751
        a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
752
        a->name[2] = (FT_String)( ( a->tag >>  8 ) & 0xFF );
753
        a->name[3] = (FT_String)( ( a->tag       ) & 0xFF );
754
        a->name[4] = 0;
755
 
756
        ++a;
757
      }
758
 
759
      ns = mmvar->namedstyle;
760
      for ( i = 0; i < fvar_head.instanceCount; ++i, ++ns )
761
      {
762
        if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) )
763
          goto Exit;
764
 
765
        ns->strid       =    FT_GET_USHORT();
766
        (void) /* flags = */ FT_GET_USHORT();
767
 
768
        for ( j = 0; j < fvar_head.axisCount; ++j )
769
          ns->coords[j] = FT_GET_ULONG();     /* A Fixed */
770
 
771
        FT_FRAME_EXIT();
772
      }
773
    }
774
 
775
    if ( master != NULL )
776
    {
777
      FT_UInt  n;
778
 
779
 
780
      if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
781
        goto Exit;
782
      FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
783
 
784
      mmvar->axis =
785
        (FT_Var_Axis*)&(mmvar[1]);
786
      mmvar->namedstyle =
787
        (FT_Var_Named_Style*)&(mmvar->axis[mmvar->num_axis]);
788
      next_coords =
789
        (FT_Fixed*)&(mmvar->namedstyle[mmvar->num_namedstyles]);
790
 
791
      for ( n = 0; n < mmvar->num_namedstyles; ++n )
792
      {
793
        mmvar->namedstyle[n].coords  = next_coords;
794
        next_coords                 += mmvar->num_axis;
795
      }
796
 
797
      a = mmvar->axis;
798
      next_name = (FT_String*)next_coords;
799
      for ( n = 0; n < mmvar->num_axis; ++n )
800
      {
801
        a->name = next_name;
802
 
803
        /* standard PostScript names for some standard apple tags */
804
        if ( a->tag == TTAG_wght )
805
          a->name = (char *)"Weight";
806
        else if ( a->tag == TTAG_wdth )
807
          a->name = (char *)"Width";
808
        else if ( a->tag == TTAG_opsz )
809
          a->name = (char *)"OpticalSize";
810
        else if ( a->tag == TTAG_slnt )
811
          a->name = (char *)"Slant";
812
 
813
        next_name += 5;
814
        ++a;
815
      }
816
 
817
      *master = mmvar;
818
    }
819
 
820
  Exit:
821
    return error;
822
  }
823
 
824
 
825
  /*************************************************************************/
826
  /*                                                                       */
827
  /*                                                             */
828
  /*    TT_Set_MM_Blend                                                    */
829
  /*                                                                       */
830
  /*                                                          */
831
  /*    Set the blend (normalized) coordinates for this instance of the    */
832
  /*    font.  Check that the `gvar' table is reasonable and does some     */
833
  /*    initial preparation.                                               */
834
  /*                                                                       */
835
  /*                                                                */
836
  /*    face       :: The font.                                            */
837
  /*                  Initialize the blend structure with `gvar' data.     */
838
  /*                                                                       */
839
  /*                                                                */
840
  /*    num_coords :: Must be the axis count of the font.                  */
841
  /*                                                                       */
842
  /*    coords     :: An array of num_coords, each between [-1,1].         */
843
  /*                                                                       */
844
  /*                                                               */
845
  /*    FreeType error code.  0 means success.                             */
846
  /*                                                                       */
847
  FT_LOCAL_DEF( FT_Error )
848
  TT_Set_MM_Blend( TT_Face    face,
849
                   FT_UInt    num_coords,
850
                   FT_Fixed*  coords )
851
  {
852
    FT_Error    error = FT_Err_Ok;
853
    GX_Blend    blend;
854
    FT_MM_Var*  mmvar;
855
    FT_UInt     i;
856
    FT_Memory   memory = face->root.memory;
857
 
858
    enum
859
    {
860
      mcvt_retain,
861
      mcvt_modify,
862
      mcvt_load
863
 
864
    } manageCvt;
865
 
866
 
867
    face->doblend = FALSE;
868
 
869
    if ( face->blend == NULL )
870
    {
871
      if ( (error = TT_Get_MM_Var( face, NULL)) != 0 )
872
        goto Exit;
873
    }
874
 
875
    blend = face->blend;
876
    mmvar = blend->mmvar;
877
 
878
    if ( num_coords != mmvar->num_axis )
879
    {
880
      error = FT_THROW( Invalid_Argument );
881
      goto Exit;
882
    }
883
 
884
    for ( i = 0; i < num_coords; ++i )
885
      if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
886
      {
887
        error = FT_THROW( Invalid_Argument );
888
        goto Exit;
889
      }
890
 
891
    if ( blend->glyphoffsets == NULL )
892
      if ( (error = ft_var_load_gvar( face )) != 0 )
893
        goto Exit;
894
 
895
    if ( blend->normalizedcoords == NULL )
896
    {
897
      if ( FT_NEW_ARRAY( blend->normalizedcoords, num_coords ) )
898
        goto Exit;
899
 
900
      manageCvt = mcvt_modify;
901
 
902
      /* If we have not set the blend coordinates before this, then the  */
903
      /* cvt table will still be what we read from the `cvt ' table and  */
904
      /* we don't need to reload it.  We may need to change it though... */
905
    }
906
    else
907
    {
908
      manageCvt = mcvt_retain;
909
      for ( i = 0; i < num_coords; ++i )
910
      {
911
        if ( blend->normalizedcoords[i] != coords[i] )
912
        {
913
          manageCvt = mcvt_load;
914
          break;
915
        }
916
      }
917
 
918
      /* If we don't change the blend coords then we don't need to do  */
919
      /* anything to the cvt table.  It will be correct.  Otherwise we */
920
      /* no longer have the original cvt (it was modified when we set  */
921
      /* the blend last time), so we must reload and then modify it.   */
922
    }
923
 
924
    blend->num_axis = num_coords;
925
    FT_MEM_COPY( blend->normalizedcoords,
926
                 coords,
927
                 num_coords * sizeof ( FT_Fixed ) );
928
 
929
    face->doblend = TRUE;
930
 
931
    if ( face->cvt != NULL )
932
    {
933
      switch ( manageCvt )
934
      {
935
      case mcvt_load:
936
        /* The cvt table has been loaded already; every time we change the */
937
        /* blend we may need to reload and remodify the cvt table.         */
938
        FT_FREE( face->cvt );
939
        face->cvt = NULL;
940
 
941
        tt_face_load_cvt( face, face->root.stream );
942
        break;
943
 
944
      case mcvt_modify:
945
        /* The original cvt table is in memory.  All we need to do is */
946
        /* apply the `cvar' table (if any).                           */
947
        tt_face_vary_cvt( face, face->root.stream );
948
        break;
949
 
950
      case mcvt_retain:
951
        /* The cvt table is correct for this set of coordinates. */
952
        break;
953
      }
954
    }
955
 
956
  Exit:
957
    return error;
958
  }
959
 
960
 
961
  /*************************************************************************/
962
  /*                                                                       */
963
  /*                                                             */
964
  /*    TT_Set_Var_Design                                                  */
965
  /*                                                                       */
966
  /*                                                          */
967
  /*    Set the coordinates for the instance, measured in the user         */
968
  /*    coordinate system.  Parse the `avar' table (if present) to convert */
969
  /*    from user to normalized coordinates.                               */
970
  /*                                                                       */
971
  /*                                                                */
972
  /*    face       :: The font face.                                       */
973
  /*                  Initialize the blend struct with `gvar' data.        */
974
  /*                                                                       */
975
  /*                                                                */
976
  /*    num_coords :: This must be the axis count of the font.             */
977
  /*                                                                       */
978
  /*    coords     :: A coordinate array with `num_coords' elements.       */
979
  /*                                                                       */
980
  /*                                                               */
981
  /*    FreeType error code.  0 means success.                             */
982
  /*                                                                       */
983
  FT_LOCAL_DEF( FT_Error )
984
  TT_Set_Var_Design( TT_Face    face,
985
                     FT_UInt    num_coords,
986
                     FT_Fixed*  coords )
987
  {
988
    FT_Error        error      = FT_Err_Ok;
989
    FT_Fixed*       normalized = NULL;
990
    GX_Blend        blend;
991
    FT_MM_Var*      mmvar;
992
    FT_UInt         i, j;
993
    FT_Var_Axis*    a;
994
    GX_AVarSegment  av;
995
    FT_Memory       memory = face->root.memory;
996
 
997
 
998
    if ( face->blend == NULL )
999
    {
1000
      if ( (error = TT_Get_MM_Var( face, NULL )) != 0 )
1001
        goto Exit;
1002
    }
1003
 
1004
    blend = face->blend;
1005
    mmvar = blend->mmvar;
1006
 
1007
    if ( num_coords != mmvar->num_axis )
1008
    {
1009
      error = FT_THROW( Invalid_Argument );
1010
      goto Exit;
1011
    }
1012
 
1013
    /* Axis normalization is a two stage process.  First we normalize */
1014
    /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
1015
    /* Then, if there's an `avar' table, we renormalize this range.   */
1016
 
1017
    if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
1018
      goto Exit;
1019
 
1020
    a = mmvar->axis;
1021
    for ( i = 0; i < mmvar->num_axis; ++i, ++a )
1022
    {
1023
      if ( coords[i] > a->maximum || coords[i] < a->minimum )
1024
      {
1025
        error = FT_THROW( Invalid_Argument );
1026
        goto Exit;
1027
      }
1028
 
1029
      if ( coords[i] < a->def )
1030
        normalized[i] = -FT_DivFix( coords[i] - a->def, a->minimum - a->def );
1031
      else if ( a->maximum == a->def )
1032
        normalized[i] = 0;
1033
      else
1034
        normalized[i] = FT_DivFix( coords[i] - a->def, a->maximum - a->def );
1035
    }
1036
 
1037
    if ( !blend->avar_checked )
1038
      ft_var_load_avar( face );
1039
 
1040
    if ( blend->avar_segment != NULL )
1041
    {
1042
      av = blend->avar_segment;
1043
      for ( i = 0; i < mmvar->num_axis; ++i, ++av )
1044
      {
1045
        for ( j = 1; j < (FT_UInt)av->pairCount; ++j )
1046
          if ( normalized[i] < av->correspondence[j].fromCoord )
1047
          {
1048
            normalized[i] =
1049
              FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
1050
                         av->correspondence[j].toCoord -
1051
                           av->correspondence[j - 1].toCoord,
1052
                         av->correspondence[j].fromCoord -
1053
                           av->correspondence[j - 1].fromCoord ) +
1054
              av->correspondence[j - 1].toCoord;
1055
            break;
1056
          }
1057
      }
1058
    }
1059
 
1060
    error = TT_Set_MM_Blend( face, num_coords, normalized );
1061
 
1062
  Exit:
1063
    FT_FREE( normalized );
1064
    return error;
1065
  }
1066
 
1067
 
1068
  /*************************************************************************/
1069
  /*************************************************************************/
1070
  /*****                                                               *****/
1071
  /*****                     GX VAR PARSING ROUTINES                   *****/
1072
  /*****                                                               *****/
1073
  /*************************************************************************/
1074
  /*************************************************************************/
1075
 
1076
 
1077
  /*************************************************************************/
1078
  /*                                                                       */
1079
  /*                                                             */
1080
  /*    tt_face_vary_cvt                                                   */
1081
  /*                                                                       */
1082
  /*                                                          */
1083
  /*    Modify the loaded cvt table according to the `cvar' table and the  */
1084
  /*    font's blend.                                                      */
1085
  /*                                                                       */
1086
  /*                                                                */
1087
  /*    face   :: A handle to the target face object.                      */
1088
  /*                                                                       */
1089
  /*                                                                */
1090
  /*    stream :: A handle to the input stream.                            */
1091
  /*                                                                       */
1092
  /*                                                               */
1093
  /*    FreeType error code.  0 means success.                             */
1094
  /*                                                                       */
1095
  /*    Most errors are ignored.  It is perfectly valid not to have a      */
1096
  /*    `cvar' table even if there is a `gvar' and `fvar' table.           */
1097
  /*                                                                       */
1098
  FT_LOCAL_DEF( FT_Error )
1099
  tt_face_vary_cvt( TT_Face    face,
1100
                    FT_Stream  stream )
1101
  {
1102
    FT_Error    error;
1103
    FT_Memory   memory = stream->memory;
1104
    FT_ULong    table_start;
1105
    FT_ULong    table_len;
1106
    FT_UInt     tupleCount;
1107
    FT_ULong    offsetToData;
1108
    FT_ULong    here;
1109
    FT_UInt     i, j;
1110
    FT_Fixed*   tuple_coords    = NULL;
1111
    FT_Fixed*   im_start_coords = NULL;
1112
    FT_Fixed*   im_end_coords   = NULL;
1113
    GX_Blend    blend           = face->blend;
1114
    FT_UInt     point_count;
1115
    FT_UShort*  localpoints;
1116
    FT_Short*   deltas;
1117
 
1118
 
1119
    FT_TRACE2(( "CVAR " ));
1120
 
1121
    if ( blend == NULL )
1122
    {
1123
      FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" ));
1124
 
1125
      error = FT_Err_Ok;
1126
      goto Exit;
1127
    }
1128
 
1129
    if ( face->cvt == NULL )
1130
    {
1131
      FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" ));
1132
 
1133
      error = FT_Err_Ok;
1134
      goto Exit;
1135
    }
1136
 
1137
    error = face->goto_table( face, TTAG_cvar, stream, &table_len );
1138
    if ( error )
1139
    {
1140
      FT_TRACE2(( "is missing\n" ));
1141
 
1142
      error = FT_Err_Ok;
1143
      goto Exit;
1144
    }
1145
 
1146
    if ( FT_FRAME_ENTER( table_len ) )
1147
    {
1148
      error = FT_Err_Ok;
1149
      goto Exit;
1150
    }
1151
 
1152
    table_start = FT_Stream_FTell( stream );
1153
    if ( FT_GET_LONG() != 0x00010000L )
1154
    {
1155
      FT_TRACE2(( "bad table version\n" ));
1156
 
1157
      error = FT_Err_Ok;
1158
      goto FExit;
1159
    }
1160
 
1161
    if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
1162
         FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
1163
         FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
1164
      goto FExit;
1165
 
1166
    tupleCount   = FT_GET_USHORT();
1167
    offsetToData = table_start + FT_GET_USHORT();
1168
 
1169
    /* The documentation implies there are flags packed into the        */
1170
    /* tuplecount, but John Jenkins says that shared points don't apply */
1171
    /* to `cvar', and no other flags are defined.                       */
1172
 
1173
    for ( i = 0; i < ( tupleCount & 0xFFF ); ++i )
1174
    {
1175
      FT_UInt   tupleDataSize;
1176
      FT_UInt   tupleIndex;
1177
      FT_Fixed  apply;
1178
 
1179
 
1180
      tupleDataSize = FT_GET_USHORT();
1181
      tupleIndex    = FT_GET_USHORT();
1182
 
1183
      /* There is no provision here for a global tuple coordinate section, */
1184
      /* so John says.  There are no tuple indices, just embedded tuples.  */
1185
 
1186
      if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
1187
      {
1188
        for ( j = 0; j < blend->num_axis; ++j )
1189
          tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from        */
1190
                                                 /* short frac to fixed */
1191
      }
1192
      else
1193
      {
1194
        /* skip this tuple; it makes no sense */
1195
 
1196
        if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1197
          for ( j = 0; j < 2 * blend->num_axis; ++j )
1198
            (void)FT_GET_SHORT();
1199
 
1200
        offsetToData += tupleDataSize;
1201
        continue;
1202
      }
1203
 
1204
      if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1205
      {
1206
        for ( j = 0; j < blend->num_axis; ++j )
1207
          im_start_coords[j] = FT_GET_SHORT() << 2;
1208
        for ( j = 0; j < blend->num_axis; ++j )
1209
          im_end_coords[j] = FT_GET_SHORT() << 2;
1210
      }
1211
 
1212
      apply = ft_var_apply_tuple( blend,
1213
                                  (FT_UShort)tupleIndex,
1214
                                  tuple_coords,
1215
                                  im_start_coords,
1216
                                  im_end_coords );
1217
      if ( /* tuple isn't active for our blend */
1218
           apply == 0                                    ||
1219
           /* global points not allowed,           */
1220
           /* if they aren't local, makes no sense */
1221
           !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) )
1222
      {
1223
        offsetToData += tupleDataSize;
1224
        continue;
1225
      }
1226
 
1227
      here = FT_Stream_FTell( stream );
1228
 
1229
      FT_Stream_SeekSet( stream, offsetToData );
1230
 
1231
      localpoints = ft_var_readpackedpoints( stream, &point_count );
1232
      deltas      = ft_var_readpackeddeltas( stream,
1233
                                             point_count == 0 ? face->cvt_size
1234
                                                              : point_count );
1235
      if ( localpoints == NULL || deltas == NULL )
1236
        /* failure, ignore it */;
1237
 
1238
      else if ( localpoints == ALL_POINTS )
1239
      {
1240
        /* this means that there are deltas for every entry in cvt */
1241
        for ( j = 0; j < face->cvt_size; ++j )
1242
          face->cvt[j] = (FT_Short)( face->cvt[j] +
1243
                                     FT_MulFix( deltas[j], apply ) );
1244
      }
1245
 
1246
      else
1247
      {
1248
        for ( j = 0; j < point_count; ++j )
1249
        {
1250
          int  pindex = localpoints[j];
1251
 
1252
          face->cvt[pindex] = (FT_Short)( face->cvt[pindex] +
1253
                                          FT_MulFix( deltas[j], apply ) );
1254
        }
1255
      }
1256
 
1257
      if ( localpoints != ALL_POINTS )
1258
        FT_FREE( localpoints );
1259
      FT_FREE( deltas );
1260
 
1261
      offsetToData += tupleDataSize;
1262
 
1263
      FT_Stream_SeekSet( stream, here );
1264
    }
1265
 
1266
  FExit:
1267
    FT_FRAME_EXIT();
1268
 
1269
  Exit:
1270
    FT_FREE( tuple_coords );
1271
    FT_FREE( im_start_coords );
1272
    FT_FREE( im_end_coords );
1273
 
1274
    return error;
1275
  }
1276
 
1277
 
1278
  /*************************************************************************/
1279
  /*                                                                       */
1280
  /*                                                             */
1281
  /*    TT_Vary_Get_Glyph_Deltas                                           */
1282
  /*                                                                       */
1283
  /*                                                          */
1284
  /*    Load the appropriate deltas for the current glyph.                 */
1285
  /*                                                                       */
1286
  /*                                                                */
1287
  /*    face        :: A handle to the target face object.                 */
1288
  /*                                                                       */
1289
  /*    glyph_index :: The index of the glyph being modified.              */
1290
  /*                                                                       */
1291
  /*    n_points    :: The number of the points in the glyph, including    */
1292
  /*                   phantom points.                                     */
1293
  /*                                                                       */
1294
  /*                                                               */
1295
  /*    deltas      :: The array of points to change.                      */
1296
  /*                                                                       */
1297
  /*                                                               */
1298
  /*    FreeType error code.  0 means success.                             */
1299
  /*                                                                       */
1300
  FT_LOCAL_DEF( FT_Error )
1301
  TT_Vary_Get_Glyph_Deltas( TT_Face      face,
1302
                            FT_UInt      glyph_index,
1303
                            FT_Vector*  *deltas,
1304
                            FT_UInt      n_points )
1305
  {
1306
    FT_Stream   stream = face->root.stream;
1307
    FT_Memory   memory = stream->memory;
1308
    GX_Blend    blend  = face->blend;
1309
    FT_Vector*  delta_xy = NULL;
1310
 
1311
    FT_Error    error;
1312
    FT_ULong    glyph_start;
1313
    FT_UInt     tupleCount;
1314
    FT_ULong    offsetToData;
1315
    FT_ULong    here;
1316
    FT_UInt     i, j;
1317
    FT_Fixed*   tuple_coords    = NULL;
1318
    FT_Fixed*   im_start_coords = NULL;
1319
    FT_Fixed*   im_end_coords   = NULL;
1320
    FT_UInt     point_count, spoint_count = 0;
1321
    FT_UShort*  sharedpoints = NULL;
1322
    FT_UShort*  localpoints  = NULL;
1323
    FT_UShort*  points;
1324
    FT_Short    *deltas_x, *deltas_y;
1325
 
1326
 
1327
    if ( !face->doblend || blend == NULL )
1328
      return FT_THROW( Invalid_Argument );
1329
 
1330
    /* to be freed by the caller */
1331
    if ( FT_NEW_ARRAY( delta_xy, n_points ) )
1332
      goto Exit;
1333
    *deltas = delta_xy;
1334
 
1335
    if ( glyph_index >= blend->gv_glyphcnt      ||
1336
         blend->glyphoffsets[glyph_index] ==
1337
           blend->glyphoffsets[glyph_index + 1] )
1338
      return FT_Err_Ok;               /* no variation data for this glyph */
1339
 
1340
    if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] )   ||
1341
         FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
1342
                           blend->glyphoffsets[glyph_index] ) )
1343
      goto Fail1;
1344
 
1345
    glyph_start = FT_Stream_FTell( stream );
1346
 
1347
    /* each set of glyph variation data is formatted similarly to `cvar' */
1348
    /* (except we get shared points and global tuples)                   */
1349
 
1350
    if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
1351
         FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
1352
         FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
1353
      goto Fail2;
1354
 
1355
    tupleCount   = FT_GET_USHORT();
1356
    offsetToData = glyph_start + FT_GET_USHORT();
1357
 
1358
    if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
1359
    {
1360
      here = FT_Stream_FTell( stream );
1361
 
1362
      FT_Stream_SeekSet( stream, offsetToData );
1363
 
1364
      sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
1365
      offsetToData = FT_Stream_FTell( stream );
1366
 
1367
      FT_Stream_SeekSet( stream, here );
1368
    }
1369
 
1370
    for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); ++i )
1371
    {
1372
      FT_UInt   tupleDataSize;
1373
      FT_UInt   tupleIndex;
1374
      FT_Fixed  apply;
1375
 
1376
 
1377
      tupleDataSize = FT_GET_USHORT();
1378
      tupleIndex    = FT_GET_USHORT();
1379
 
1380
      if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
1381
      {
1382
        for ( j = 0; j < blend->num_axis; ++j )
1383
          tuple_coords[j] = FT_GET_SHORT() << 2;  /* convert from        */
1384
                                                  /* short frac to fixed */
1385
      }
1386
      else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
1387
      {
1388
        error = FT_THROW( Invalid_Table );
1389
        goto Fail3;
1390
      }
1391
      else
1392
      {
1393
        FT_MEM_COPY(
1394
          tuple_coords,
1395
          &blend->tuplecoords[(tupleIndex & 0xFFF) * blend->num_axis],
1396
          blend->num_axis * sizeof ( FT_Fixed ) );
1397
      }
1398
 
1399
      if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1400
      {
1401
        for ( j = 0; j < blend->num_axis; ++j )
1402
          im_start_coords[j] = FT_GET_SHORT() << 2;
1403
        for ( j = 0; j < blend->num_axis; ++j )
1404
          im_end_coords[j] = FT_GET_SHORT() << 2;
1405
      }
1406
 
1407
      apply = ft_var_apply_tuple( blend,
1408
                                  (FT_UShort)tupleIndex,
1409
                                  tuple_coords,
1410
                                  im_start_coords,
1411
                                  im_end_coords );
1412
 
1413
      if ( apply == 0 )              /* tuple isn't active for our blend */
1414
      {
1415
        offsetToData += tupleDataSize;
1416
        continue;
1417
      }
1418
 
1419
      here = FT_Stream_FTell( stream );
1420
 
1421
      if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
1422
      {
1423
        FT_Stream_SeekSet( stream, offsetToData );
1424
 
1425
        localpoints = ft_var_readpackedpoints( stream, &point_count );
1426
        points      = localpoints;
1427
      }
1428
      else
1429
      {
1430
        points      = sharedpoints;
1431
        point_count = spoint_count;
1432
      }
1433
 
1434
      deltas_x = ft_var_readpackeddeltas( stream,
1435
                                          point_count == 0 ? n_points
1436
                                                           : point_count );
1437
      deltas_y = ft_var_readpackeddeltas( stream,
1438
                                          point_count == 0 ? n_points
1439
                                                           : point_count );
1440
 
1441
      if ( points == NULL || deltas_y == NULL || deltas_x == NULL )
1442
        ; /* failure, ignore it */
1443
 
1444
      else if ( points == ALL_POINTS )
1445
      {
1446
        /* this means that there are deltas for every point in the glyph */
1447
        for ( j = 0; j < n_points; ++j )
1448
        {
1449
          delta_xy[j].x += FT_MulFix( deltas_x[j], apply );
1450
          delta_xy[j].y += FT_MulFix( deltas_y[j], apply );
1451
        }
1452
      }
1453
 
1454
      else
1455
      {
1456
        for ( j = 0; j < point_count; ++j )
1457
        {
1458
          if ( localpoints[j] >= n_points )
1459
            continue;
1460
 
1461
          delta_xy[localpoints[j]].x += FT_MulFix( deltas_x[j], apply );
1462
          delta_xy[localpoints[j]].y += FT_MulFix( deltas_y[j], apply );
1463
        }
1464
      }
1465
 
1466
      if ( localpoints != ALL_POINTS )
1467
        FT_FREE( localpoints );
1468
      FT_FREE( deltas_x );
1469
      FT_FREE( deltas_y );
1470
 
1471
      offsetToData += tupleDataSize;
1472
 
1473
      FT_Stream_SeekSet( stream, here );
1474
    }
1475
 
1476
  Fail3:
1477
    FT_FREE( tuple_coords );
1478
    FT_FREE( im_start_coords );
1479
    FT_FREE( im_end_coords );
1480
 
1481
  Fail2:
1482
    FT_FRAME_EXIT();
1483
 
1484
  Fail1:
1485
    if ( error )
1486
    {
1487
      FT_FREE( delta_xy );
1488
      *deltas = NULL;
1489
    }
1490
 
1491
  Exit:
1492
    return error;
1493
  }
1494
 
1495
 
1496
  /*************************************************************************/
1497
  /*                                                                       */
1498
  /*                                                             */
1499
  /*    tt_done_blend                                                      */
1500
  /*                                                                       */
1501
  /*                                                          */
1502
  /*    Frees the blend internal data structure.                           */
1503
  /*                                                                       */
1504
  FT_LOCAL_DEF( void )
1505
  tt_done_blend( FT_Memory  memory,
1506
                 GX_Blend   blend )
1507
  {
1508
    if ( blend != NULL )
1509
    {
1510
      FT_UInt  i;
1511
 
1512
 
1513
      FT_FREE( blend->normalizedcoords );
1514
      FT_FREE( blend->mmvar );
1515
 
1516
      if ( blend->avar_segment != NULL )
1517
      {
1518
        for ( i = 0; i < blend->num_axis; ++i )
1519
          FT_FREE( blend->avar_segment[i].correspondence );
1520
        FT_FREE( blend->avar_segment );
1521
      }
1522
 
1523
      FT_FREE( blend->tuplecoords );
1524
      FT_FREE( blend->glyphoffsets );
1525
      FT_FREE( blend );
1526
    }
1527
  }
1528
 
1529
#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
1530
 
1531
 
1532
/* END */