1/****************************************************************************
2 *
3 * ftmm.c
4 *
5 * Multiple Master font support (body).
6 *
7 * Copyright (C) 1996-2023 by
8 * David Turner, Robert Wilhelm, and Werner Lemberg.
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#include <freetype/internal/ftdebug.h>
20
21#include <freetype/ftmm.h>
22#include <freetype/internal/ftobjs.h>
23#include <freetype/internal/services/svmm.h>
24#include <freetype/internal/services/svmetric.h>
25
26
27 /**************************************************************************
28 *
29 * The macro FT_COMPONENT is used in trace mode. It is an implicit
30 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
31 * messages during execution.
32 */
33#undef FT_COMPONENT
34#define FT_COMPONENT mm
35
36
37 static FT_Error
38 ft_face_get_mm_service( FT_Face face,
39 FT_Service_MultiMasters *aservice )
40 {
41 FT_Error error;
42
43
44 *aservice = NULL;
45
46 if ( !face )
47 return FT_THROW( Invalid_Face_Handle );
48
49 error = FT_ERR( Invalid_Argument );
50
51 if ( FT_HAS_MULTIPLE_MASTERS( face ) )
52 {
53 FT_FACE_LOOKUP_SERVICE( face,
54 *aservice,
55 MULTI_MASTERS );
56
57 if ( *aservice )
58 error = FT_Err_Ok;
59 }
60
61 return error;
62 }
63
64
65 static FT_Error
66 ft_face_get_mvar_service( FT_Face face,
67 FT_Service_MetricsVariations *aservice )
68 {
69 FT_Error error;
70
71
72 *aservice = NULL;
73
74 if ( !face )
75 return FT_THROW( Invalid_Face_Handle );
76
77 error = FT_ERR( Invalid_Argument );
78
79 if ( FT_HAS_MULTIPLE_MASTERS( face ) )
80 {
81 FT_FACE_LOOKUP_SERVICE( face,
82 *aservice,
83 METRICS_VARIATIONS );
84
85 if ( *aservice )
86 error = FT_Err_Ok;
87 }
88
89 return error;
90 }
91
92
93 /* documentation is in ftmm.h */
94
95 FT_EXPORT_DEF( FT_Error )
96 FT_Get_Multi_Master( FT_Face face,
97 FT_Multi_Master *amaster )
98 {
99 FT_Error error;
100 FT_Service_MultiMasters service;
101
102
103 /* check of `face' delayed to `ft_face_get_mm_service' */
104
105 if ( !amaster )
106 return FT_THROW( Invalid_Argument );
107
108 error = ft_face_get_mm_service( face, &service );
109 if ( !error )
110 {
111 error = FT_ERR( Invalid_Argument );
112 if ( service->get_mm )
113 error = service->get_mm( face, amaster );
114 }
115
116 return error;
117 }
118
119
120 /* documentation is in ftmm.h */
121
122 FT_EXPORT_DEF( FT_Error )
123 FT_Get_MM_Var( FT_Face face,
124 FT_MM_Var* *amaster )
125 {
126 FT_Error error;
127 FT_Service_MultiMasters service;
128
129
130 /* check of `face' delayed to `ft_face_get_mm_service' */
131
132 if ( !amaster )
133 return FT_THROW( Invalid_Argument );
134
135 error = ft_face_get_mm_service( face, &service );
136 if ( !error )
137 {
138 error = FT_ERR( Invalid_Argument );
139 if ( service->get_mm_var )
140 error = service->get_mm_var( face, amaster );
141 }
142
143 return error;
144 }
145
146
147 /* documentation is in ftmm.h */
148
149 FT_EXPORT_DEF( FT_Error )
150 FT_Done_MM_Var( FT_Library library,
151 FT_MM_Var* amaster )
152 {
153 FT_Memory memory;
154
155
156 if ( !library )
157 return FT_THROW( Invalid_Library_Handle );
158
159 memory = library->memory;
160 FT_FREE( amaster );
161
162 return FT_Err_Ok;
163 }
164
165
166 /* documentation is in ftmm.h */
167
168 FT_EXPORT_DEF( FT_Error )
169 FT_Set_MM_Design_Coordinates( FT_Face face,
170 FT_UInt num_coords,
171 FT_Long* coords )
172 {
173 FT_Error error;
174 FT_Service_MultiMasters service;
175
176
177 /* check of `face' delayed to `ft_face_get_mm_service' */
178
179 if ( num_coords && !coords )
180 return FT_THROW( Invalid_Argument );
181
182 error = ft_face_get_mm_service( face, &service );
183 if ( !error )
184 {
185 error = FT_ERR( Invalid_Argument );
186 if ( service->set_mm_design )
187 error = service->set_mm_design( face, num_coords, coords );
188
189 if ( !error )
190 {
191 if ( num_coords )
192 face->face_flags |= FT_FACE_FLAG_VARIATION;
193 else
194 face->face_flags &= ~FT_FACE_FLAG_VARIATION;
195 }
196 }
197
198 /* enforce recomputation of auto-hinting data */
199 if ( !error && face->autohint.finalizer )
200 {
201 face->autohint.finalizer( face->autohint.data );
202 face->autohint.data = NULL;
203 }
204
205 return error;
206 }
207
208
209 /* documentation is in ftmm.h */
210
211 FT_EXPORT_DEF( FT_Error )
212 FT_Set_MM_WeightVector( FT_Face face,
213 FT_UInt len,
214 FT_Fixed* weightvector )
215 {
216 FT_Error error;
217 FT_Service_MultiMasters service;
218
219
220 /* check of `face' delayed to `ft_face_get_mm_service' */
221
222 if ( len && !weightvector )
223 return FT_THROW( Invalid_Argument );
224
225 error = ft_face_get_mm_service( face, &service );
226 if ( !error )
227 {
228 error = FT_ERR( Invalid_Argument );
229 if ( service->set_mm_weightvector )
230 error = service->set_mm_weightvector( face, len, weightvector );
231
232 if ( !error )
233 {
234 if ( len )
235 face->face_flags |= FT_FACE_FLAG_VARIATION;
236 else
237 face->face_flags &= ~FT_FACE_FLAG_VARIATION;
238 }
239 }
240
241 /* enforce recomputation of auto-hinting data */
242 if ( !error && face->autohint.finalizer )
243 {
244 face->autohint.finalizer( face->autohint.data );
245 face->autohint.data = NULL;
246 }
247
248 return error;
249 }
250
251
252 FT_EXPORT_DEF( FT_Error )
253 FT_Get_MM_WeightVector( FT_Face face,
254 FT_UInt* len,
255 FT_Fixed* weightvector )
256 {
257 FT_Error error;
258 FT_Service_MultiMasters service;
259
260
261 /* check of `face' delayed to `ft_face_get_mm_service' */
262
263 if ( len && !weightvector )
264 return FT_THROW( Invalid_Argument );
265
266 error = ft_face_get_mm_service( face, &service );
267 if ( !error )
268 {
269 error = FT_ERR( Invalid_Argument );
270 if ( service->get_mm_weightvector )
271 error = service->get_mm_weightvector( face, len, weightvector );
272 }
273
274 return error;
275 }
276
277
278 /* documentation is in ftmm.h */
279
280 FT_EXPORT_DEF( FT_Error )
281 FT_Set_Var_Design_Coordinates( FT_Face face,
282 FT_UInt num_coords,
283 FT_Fixed* coords )
284 {
285 FT_Error error;
286 FT_Service_MultiMasters service_mm = NULL;
287 FT_Service_MetricsVariations service_mvar = NULL;
288
289
290 /* check of `face' delayed to `ft_face_get_mm_service' */
291
292 if ( num_coords && !coords )
293 return FT_THROW( Invalid_Argument );
294
295 error = ft_face_get_mm_service( face, &service_mm );
296 if ( !error )
297 {
298 error = FT_ERR( Invalid_Argument );
299 if ( service_mm->set_var_design )
300 error = service_mm->set_var_design( face, num_coords, coords );
301
302 if ( !error || error == -1 )
303 {
304 FT_Bool is_variation_old = FT_IS_VARIATION( face );
305
306
307 if ( num_coords )
308 face->face_flags |= FT_FACE_FLAG_VARIATION;
309 else
310 face->face_flags &= ~FT_FACE_FLAG_VARIATION;
311
312 if ( service_mm->construct_ps_name )
313 {
314 if ( error == -1 )
315 {
316 /* The PS name of a named instance and a non-named instance */
317 /* usually differs, even if the axis values are identical. */
318 if ( is_variation_old != FT_IS_VARIATION( face ) )
319 service_mm->construct_ps_name( face );
320 }
321 else
322 service_mm->construct_ps_name( face );
323 }
324 }
325
326 /* internal error code -1 means `no change'; we can exit immediately */
327 if ( error == -1 )
328 return FT_Err_Ok;
329 }
330
331 if ( !error )
332 {
333 (void)ft_face_get_mvar_service( face, &service_mvar );
334
335 if ( service_mvar && service_mvar->metrics_adjust )
336 service_mvar->metrics_adjust( face );
337 }
338
339 /* enforce recomputation of auto-hinting data */
340 if ( !error && face->autohint.finalizer )
341 {
342 face->autohint.finalizer( face->autohint.data );
343 face->autohint.data = NULL;
344 }
345
346 return error;
347 }
348
349
350 /* documentation is in ftmm.h */
351
352 FT_EXPORT_DEF( FT_Error )
353 FT_Get_Var_Design_Coordinates( FT_Face face,
354 FT_UInt num_coords,
355 FT_Fixed* coords )
356 {
357 FT_Error error;
358 FT_Service_MultiMasters service;
359
360
361 /* check of `face' delayed to `ft_face_get_mm_service' */
362
363 if ( !coords )
364 return FT_THROW( Invalid_Argument );
365
366 error = ft_face_get_mm_service( face, &service );
367 if ( !error )
368 {
369 error = FT_ERR( Invalid_Argument );
370 if ( service->get_var_design )
371 error = service->get_var_design( face, num_coords, coords );
372 }
373
374 return error;
375 }
376
377
378 /* documentation is in ftmm.h */
379
380 FT_EXPORT_DEF( FT_Error )
381 FT_Set_MM_Blend_Coordinates( FT_Face face,
382 FT_UInt num_coords,
383 FT_Fixed* coords )
384 {
385 FT_Error error;
386 FT_Service_MultiMasters service_mm = NULL;
387 FT_Service_MetricsVariations service_mvar = NULL;
388
389
390 /* check of `face' delayed to `ft_face_get_mm_service' */
391
392 if ( num_coords && !coords )
393 return FT_THROW( Invalid_Argument );
394
395 error = ft_face_get_mm_service( face, &service_mm );
396 if ( !error )
397 {
398 error = FT_ERR( Invalid_Argument );
399 if ( service_mm->set_mm_blend )
400 error = service_mm->set_mm_blend( face, num_coords, coords );
401
402 if ( !error || error == -1 )
403 {
404 FT_Bool is_variation_old = FT_IS_VARIATION( face );
405
406
407 if ( num_coords )
408 face->face_flags |= FT_FACE_FLAG_VARIATION;
409 else
410 face->face_flags &= ~FT_FACE_FLAG_VARIATION;
411
412 if ( service_mm->construct_ps_name )
413 {
414 if ( error == -1 )
415 {
416 /* The PS name of a named instance and a non-named instance */
417 /* usually differs, even if the axis values are identical. */
418 if ( is_variation_old != FT_IS_VARIATION( face ) )
419 service_mm->construct_ps_name( face );
420 }
421 else
422 service_mm->construct_ps_name( face );
423 }
424 }
425
426 /* internal error code -1 means `no change'; we can exit immediately */
427 if ( error == -1 )
428 return FT_Err_Ok;
429 }
430
431 if ( !error )
432 {
433 (void)ft_face_get_mvar_service( face, &service_mvar );
434
435 if ( service_mvar && service_mvar->metrics_adjust )
436 service_mvar->metrics_adjust( face );
437 }
438
439 /* enforce recomputation of auto-hinting data */
440 if ( !error && face->autohint.finalizer )
441 {
442 face->autohint.finalizer( face->autohint.data );
443 face->autohint.data = NULL;
444 }
445
446 return error;
447 }
448
449
450 /* documentation is in ftmm.h */
451
452 /* This is exactly the same as the previous function. It exists for */
453 /* orthogonality. */
454
455 FT_EXPORT_DEF( FT_Error )
456 FT_Set_Var_Blend_Coordinates( FT_Face face,
457 FT_UInt num_coords,
458 FT_Fixed* coords )
459 {
460 FT_Error error;
461 FT_Service_MultiMasters service_mm = NULL;
462 FT_Service_MetricsVariations service_mvar = NULL;
463
464
465 /* check of `face' delayed to `ft_face_get_mm_service' */
466
467 if ( num_coords && !coords )
468 return FT_THROW( Invalid_Argument );
469
470 error = ft_face_get_mm_service( face, &service_mm );
471 if ( !error )
472 {
473 error = FT_ERR( Invalid_Argument );
474 if ( service_mm->set_mm_blend )
475 error = service_mm->set_mm_blend( face, num_coords, coords );
476
477 if ( !error || error == -1 )
478 {
479 FT_Bool is_variation_old = FT_IS_VARIATION( face );
480
481
482 if ( num_coords )
483 face->face_flags |= FT_FACE_FLAG_VARIATION;
484 else
485 face->face_flags &= ~FT_FACE_FLAG_VARIATION;
486
487 if ( service_mm->construct_ps_name )
488 {
489 if ( error == -1 )
490 {
491 /* The PS name of a named instance and a non-named instance */
492 /* usually differs, even if the axis values are identical. */
493 if ( is_variation_old != FT_IS_VARIATION( face ) )
494 service_mm->construct_ps_name( face );
495 }
496 else
497 service_mm->construct_ps_name( face );
498 }
499 }
500
501 /* internal error code -1 means `no change'; we can exit immediately */
502 if ( error == -1 )
503 return FT_Err_Ok;
504 }
505
506 if ( !error )
507 {
508 (void)ft_face_get_mvar_service( face, &service_mvar );
509
510 if ( service_mvar && service_mvar->metrics_adjust )
511 service_mvar->metrics_adjust( face );
512 }
513
514 /* enforce recomputation of auto-hinting data */
515 if ( !error && face->autohint.finalizer )
516 {
517 face->autohint.finalizer( face->autohint.data );
518 face->autohint.data = NULL;
519 }
520
521 return error;
522 }
523
524
525 /* documentation is in ftmm.h */
526
527 FT_EXPORT_DEF( FT_Error )
528 FT_Get_MM_Blend_Coordinates( FT_Face face,
529 FT_UInt num_coords,
530 FT_Fixed* coords )
531 {
532 FT_Error error;
533 FT_Service_MultiMasters service;
534
535
536 /* check of `face' delayed to `ft_face_get_mm_service' */
537
538 if ( !coords )
539 return FT_THROW( Invalid_Argument );
540
541 error = ft_face_get_mm_service( face, &service );
542 if ( !error )
543 {
544 error = FT_ERR( Invalid_Argument );
545 if ( service->get_mm_blend )
546 error = service->get_mm_blend( face, num_coords, coords );
547 }
548
549 return error;
550 }
551
552
553 /* documentation is in ftmm.h */
554
555 /* This is exactly the same as the previous function. It exists for */
556 /* orthogonality. */
557
558 FT_EXPORT_DEF( FT_Error )
559 FT_Get_Var_Blend_Coordinates( FT_Face face,
560 FT_UInt num_coords,
561 FT_Fixed* coords )
562 {
563 FT_Error error;
564 FT_Service_MultiMasters service;
565
566
567 /* check of `face' delayed to `ft_face_get_mm_service' */
568
569 if ( !coords )
570 return FT_THROW( Invalid_Argument );
571
572 error = ft_face_get_mm_service( face, &service );
573 if ( !error )
574 {
575 error = FT_ERR( Invalid_Argument );
576 if ( service->get_mm_blend )
577 error = service->get_mm_blend( face, num_coords, coords );
578 }
579
580 return error;
581 }
582
583
584 /* documentation is in ftmm.h */
585
586 FT_EXPORT_DEF( FT_Error )
587 FT_Get_Var_Axis_Flags( FT_MM_Var* master,
588 FT_UInt axis_index,
589 FT_UInt* flags )
590 {
591 FT_UShort* axis_flags;
592
593
594 if ( !master || !flags )
595 return FT_THROW( Invalid_Argument );
596
597 if ( axis_index >= master->num_axis )
598 return FT_THROW( Invalid_Argument );
599
600 /* the axis flags array immediately follows the data of `master' */
601 axis_flags = (FT_UShort*)&( master[1] );
602 *flags = axis_flags[axis_index];
603
604 return FT_Err_Ok;
605 }
606
607
608 /* documentation is in ftmm.h */
609
610 FT_EXPORT_DEF( FT_Error )
611 FT_Set_Named_Instance( FT_Face face,
612 FT_UInt instance_index )
613 {
614 FT_Error error;
615
616 FT_Service_MultiMasters service_mm = NULL;
617 FT_Service_MetricsVariations service_mvar = NULL;
618
619
620 /* check of `face' delayed to `ft_face_get_mm_service' */
621
622 error = ft_face_get_mm_service( face, &service_mm );
623 if ( !error )
624 {
625 error = FT_ERR( Invalid_Argument );
626 if ( service_mm->set_named_instance )
627 error = service_mm->set_named_instance( face, instance_index );
628
629 if ( !error || error == -1 )
630 {
631 FT_Bool is_variation_old = FT_IS_VARIATION( face );
632
633
634 face->face_flags &= ~FT_FACE_FLAG_VARIATION;
635 face->face_index = ( instance_index << 16 ) |
636 ( face->face_index & 0xFFFFL );
637
638 if ( service_mm->construct_ps_name )
639 {
640 if ( error == -1 )
641 {
642 /* The PS name of a named instance and a non-named instance */
643 /* usually differs, even if the axis values are identical. */
644 if ( is_variation_old != FT_IS_VARIATION( face ) )
645 service_mm->construct_ps_name( face );
646 }
647 else
648 service_mm->construct_ps_name( face );
649 }
650 }
651
652 /* internal error code -1 means `no change'; we can exit immediately */
653 if ( error == -1 )
654 return FT_Err_Ok;
655 }
656
657 if ( !error )
658 {
659 (void)ft_face_get_mvar_service( face, &service_mvar );
660
661 if ( service_mvar && service_mvar->metrics_adjust )
662 service_mvar->metrics_adjust( face );
663 }
664
665 /* enforce recomputation of auto-hinting data */
666 if ( !error && face->autohint.finalizer )
667 {
668 face->autohint.finalizer( face->autohint.data );
669 face->autohint.data = NULL;
670 }
671
672 return error;
673 }
674
675
676 /* documentation is in ftmm.h */
677
678 FT_EXPORT_DEF( FT_Error )
679 FT_Get_Default_Named_Instance( FT_Face face,
680 FT_UInt *instance_index )
681 {
682 FT_Error error;
683
684 FT_Service_MultiMasters service_mm = NULL;
685
686
687 /* check of `face' delayed to `ft_face_get_mm_service' */
688
689 error = ft_face_get_mm_service( face, &service_mm );
690 if ( !error )
691 {
692 /* no error if `get_default_named_instance` is not available */
693 if ( service_mm->get_default_named_instance )
694 error = service_mm->get_default_named_instance( face,
695 instance_index );
696 else
697 error = FT_Err_Ok;
698 }
699
700 return error;
701 }
702
703
704/* END */
705