1/****************************************************************************
2 *
3 * afmodule.c
4 *
5 * Auto-fitter module implementation (body).
6 *
7 * Copyright (C) 2003-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 "afglobal.h"
20#include "afmodule.h"
21#include "afloader.h"
22#include "aferrors.h"
23
24#ifdef FT_DEBUG_AUTOFIT
25
26#ifndef FT_MAKE_OPTION_SINGLE_OBJECT
27
28#ifdef __cplusplus
29 extern "C" {
30#endif
31 extern void
32 af_glyph_hints_dump_segments( AF_GlyphHints hints,
33 FT_Bool to_stdout );
34 extern void
35 af_glyph_hints_dump_points( AF_GlyphHints hints,
36 FT_Bool to_stdout );
37 extern void
38 af_glyph_hints_dump_edges( AF_GlyphHints hints,
39 FT_Bool to_stdout );
40#ifdef __cplusplus
41 }
42#endif
43
44#endif
45
46 int af_debug_disable_horz_hints_;
47 int af_debug_disable_vert_hints_;
48 int af_debug_disable_blue_hints_;
49
50 /* we use a global object instead of a local one for debugging */
51 static AF_GlyphHintsRec af_debug_hints_rec_[1];
52
53 void* af_debug_hints_ = af_debug_hints_rec_;
54#endif
55
56#include <freetype/internal/ftobjs.h>
57#include <freetype/internal/ftdebug.h>
58#include <freetype/ftdriver.h>
59#include <freetype/internal/services/svprop.h>
60
61
62 /**************************************************************************
63 *
64 * The macro FT_COMPONENT is used in trace mode. It is an implicit
65 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
66 * messages during execution.
67 */
68#undef FT_COMPONENT
69#define FT_COMPONENT afmodule
70
71
72 static FT_Error
73 af_property_get_face_globals( FT_Face face,
74 AF_FaceGlobals* aglobals,
75 AF_Module module )
76 {
77 FT_Error error = FT_Err_Ok;
78 AF_FaceGlobals globals;
79
80
81 if ( !face )
82 return FT_THROW( Invalid_Face_Handle );
83
84 globals = (AF_FaceGlobals)face->autohint.data;
85 if ( !globals )
86 {
87 /* trigger computation of the global style data */
88 /* in case it hasn't been done yet */
89 error = af_face_globals_new( face, &globals, module );
90 if ( !error )
91 {
92 face->autohint.data = (FT_Pointer)globals;
93 face->autohint.finalizer = af_face_globals_free;
94 }
95 }
96
97 if ( !error )
98 *aglobals = globals;
99
100 return error;
101 }
102
103
104 static FT_Error
105 af_property_set( FT_Module ft_module,
106 const char* property_name,
107 const void* value,
108 FT_Bool value_is_string )
109 {
110 FT_Error error = FT_Err_Ok;
111 AF_Module module = (AF_Module)ft_module;
112
113#ifndef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
114 FT_UNUSED( value_is_string );
115#endif
116
117
118 if ( !ft_strcmp( property_name, "fallback-script" ) )
119 {
120 AF_Script* fallback_script;
121 FT_UInt ss;
122
123
124#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
125 if ( value_is_string )
126 return FT_THROW( Invalid_Argument );
127#endif
128
129 fallback_script = (AF_Script*)value;
130
131 /* We translate the fallback script to a fallback style that uses */
132 /* `fallback-script' as its script and `AF_COVERAGE_NONE' as its */
133 /* coverage value. */
134 for ( ss = 0; af_style_classes[ss]; ss++ )
135 {
136 AF_StyleClass style_class = af_style_classes[ss];
137
138
139 if ( style_class->script == *fallback_script &&
140 style_class->coverage == AF_COVERAGE_DEFAULT )
141 {
142 module->fallback_style = ss;
143 break;
144 }
145 }
146
147 if ( !af_style_classes[ss] )
148 {
149 FT_TRACE2(( "af_property_set: Invalid value %d for property `%s'\n",
150 *fallback_script, property_name ));
151 return FT_THROW( Invalid_Argument );
152 }
153
154 return error;
155 }
156 else if ( !ft_strcmp( property_name, "default-script" ) )
157 {
158 AF_Script* default_script;
159
160
161#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
162 if ( value_is_string )
163 return FT_THROW( Invalid_Argument );
164#endif
165
166 default_script = (AF_Script*)value;
167
168 module->default_script = *default_script;
169
170 return error;
171 }
172 else if ( !ft_strcmp( property_name, "increase-x-height" ) )
173 {
174 FT_Prop_IncreaseXHeight* prop;
175 AF_FaceGlobals globals;
176
177
178#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
179 if ( value_is_string )
180 return FT_THROW( Invalid_Argument );
181#endif
182
183 prop = (FT_Prop_IncreaseXHeight*)value;
184
185 error = af_property_get_face_globals( prop->face, &globals, module );
186 if ( !error )
187 globals->increase_x_height = prop->limit;
188
189 return error;
190 }
191 else if ( !ft_strcmp( property_name, "darkening-parameters" ) )
192 {
193 FT_Int* darken_params;
194 FT_Int x1, y1, x2, y2, x3, y3, x4, y4;
195
196#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
197 FT_Int dp[8];
198
199
200 if ( value_is_string )
201 {
202 const char* s = (const char*)value;
203 char* ep;
204 int i;
205
206
207 /* eight comma-separated numbers */
208 for ( i = 0; i < 7; i++ )
209 {
210 dp[i] = (FT_Int)ft_strtol( s, &ep, 10 );
211 if ( *ep != ',' || s == ep )
212 return FT_THROW( Invalid_Argument );
213
214 s = ep + 1;
215 }
216
217 dp[7] = (FT_Int)ft_strtol( s, &ep, 10 );
218 if ( !( *ep == '\0' || *ep == ' ' ) || s == ep )
219 return FT_THROW( Invalid_Argument );
220
221 darken_params = dp;
222 }
223 else
224#endif
225 darken_params = (FT_Int*)value;
226
227 x1 = darken_params[0];
228 y1 = darken_params[1];
229 x2 = darken_params[2];
230 y2 = darken_params[3];
231 x3 = darken_params[4];
232 y3 = darken_params[5];
233 x4 = darken_params[6];
234 y4 = darken_params[7];
235
236 if ( x1 < 0 || x2 < 0 || x3 < 0 || x4 < 0 ||
237 y1 < 0 || y2 < 0 || y3 < 0 || y4 < 0 ||
238 x1 > x2 || x2 > x3 || x3 > x4 ||
239 y1 > 500 || y2 > 500 || y3 > 500 || y4 > 500 )
240 return FT_THROW( Invalid_Argument );
241
242 module->darken_params[0] = x1;
243 module->darken_params[1] = y1;
244 module->darken_params[2] = x2;
245 module->darken_params[3] = y2;
246 module->darken_params[4] = x3;
247 module->darken_params[5] = y3;
248 module->darken_params[6] = x4;
249 module->darken_params[7] = y4;
250
251 return error;
252 }
253 else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
254 {
255#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
256 if ( value_is_string )
257 {
258 const char* s = (const char*)value;
259 long nsd = ft_strtol( s, NULL, 10 );
260
261
262 if ( !nsd )
263 module->no_stem_darkening = FALSE;
264 else
265 module->no_stem_darkening = TRUE;
266 }
267 else
268#endif
269 {
270 FT_Bool* no_stem_darkening = (FT_Bool*)value;
271
272
273 module->no_stem_darkening = *no_stem_darkening;
274 }
275
276 return error;
277 }
278
279 FT_TRACE2(( "af_property_set: missing property `%s'\n",
280 property_name ));
281 return FT_THROW( Missing_Property );
282 }
283
284
285 static FT_Error
286 af_property_get( FT_Module ft_module,
287 const char* property_name,
288 void* value )
289 {
290 FT_Error error = FT_Err_Ok;
291 AF_Module module = (AF_Module)ft_module;
292
293
294 if ( !ft_strcmp( property_name, "glyph-to-script-map" ) )
295 {
296 FT_Prop_GlyphToScriptMap* prop = (FT_Prop_GlyphToScriptMap*)value;
297 AF_FaceGlobals globals;
298
299
300 error = af_property_get_face_globals( prop->face, &globals, module );
301 if ( !error )
302 prop->map = globals->glyph_styles;
303
304 return error;
305 }
306 else if ( !ft_strcmp( property_name, "fallback-script" ) )
307 {
308 AF_Script* val = (AF_Script*)value;
309
310 AF_StyleClass style_class = af_style_classes[module->fallback_style];
311
312
313 *val = style_class->script;
314
315 return error;
316 }
317 else if ( !ft_strcmp( property_name, "default-script" ) )
318 {
319 AF_Script* val = (AF_Script*)value;
320
321
322 *val = module->default_script;
323
324 return error;
325 }
326 else if ( !ft_strcmp( property_name, "increase-x-height" ) )
327 {
328 FT_Prop_IncreaseXHeight* prop = (FT_Prop_IncreaseXHeight*)value;
329 AF_FaceGlobals globals;
330
331
332 error = af_property_get_face_globals( prop->face, &globals, module );
333 if ( !error )
334 prop->limit = globals->increase_x_height;
335
336 return error;
337 }
338 else if ( !ft_strcmp( property_name, "darkening-parameters" ) )
339 {
340 FT_Int* darken_params = module->darken_params;
341 FT_Int* val = (FT_Int*)value;
342
343
344 val[0] = darken_params[0];
345 val[1] = darken_params[1];
346 val[2] = darken_params[2];
347 val[3] = darken_params[3];
348 val[4] = darken_params[4];
349 val[5] = darken_params[5];
350 val[6] = darken_params[6];
351 val[7] = darken_params[7];
352
353 return error;
354 }
355 else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
356 {
357 FT_Bool no_stem_darkening = module->no_stem_darkening;
358 FT_Bool* val = (FT_Bool*)value;
359
360
361 *val = no_stem_darkening;
362
363 return error;
364 }
365
366 FT_TRACE2(( "af_property_get: missing property `%s'\n",
367 property_name ));
368 return FT_THROW( Missing_Property );
369 }
370
371
372 FT_DEFINE_SERVICE_PROPERTIESREC(
373 af_service_properties,
374
375 af_property_set, /* FT_Properties_SetFunc set_property */
376 af_property_get /* FT_Properties_GetFunc get_property */
377 )
378
379
380 FT_DEFINE_SERVICEDESCREC1(
381 af_services,
382
383 FT_SERVICE_ID_PROPERTIES, &af_service_properties )
384
385
386 FT_CALLBACK_DEF( FT_Module_Interface )
387 af_get_interface( FT_Module module,
388 const char* module_interface )
389 {
390 FT_UNUSED( module );
391
392 return ft_service_list_lookup( af_services, module_interface );
393 }
394
395
396 FT_CALLBACK_DEF( FT_Error )
397 af_autofitter_init( FT_Module ft_module ) /* AF_Module */
398 {
399 AF_Module module = (AF_Module)ft_module;
400
401
402 module->fallback_style = AF_STYLE_FALLBACK;
403 module->default_script = AF_SCRIPT_DEFAULT;
404 module->no_stem_darkening = TRUE;
405
406 module->darken_params[0] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1;
407 module->darken_params[1] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1;
408 module->darken_params[2] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2;
409 module->darken_params[3] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2;
410 module->darken_params[4] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3;
411 module->darken_params[5] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3;
412 module->darken_params[6] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4;
413 module->darken_params[7] = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4;
414
415 return FT_Err_Ok;
416 }
417
418
419 FT_CALLBACK_DEF( void )
420 af_autofitter_done( FT_Module ft_module ) /* AF_Module */
421 {
422 FT_UNUSED( ft_module );
423
424#ifdef FT_DEBUG_AUTOFIT
425 if ( af_debug_hints_rec_->memory )
426 af_glyph_hints_done( af_debug_hints_rec_ );
427#endif
428 }
429
430
431 FT_CALLBACK_DEF( FT_Error )
432 af_autofitter_load_glyph( FT_AutoHinter module_,
433 FT_GlyphSlot slot,
434 FT_Size size,
435 FT_UInt glyph_index,
436 FT_Int32 load_flags )
437 {
438 AF_Module module = (AF_Module)module_;
439
440 FT_Error error = FT_Err_Ok;
441 FT_Memory memory = module->root.library->memory;
442
443#ifdef FT_DEBUG_AUTOFIT
444
445 /* in debug mode, we use a global object that survives this routine */
446
447 AF_GlyphHints hints = af_debug_hints_rec_;
448 AF_LoaderRec loader[1];
449
450 FT_UNUSED( size );
451
452
453 if ( hints->memory )
454 af_glyph_hints_done( hints );
455
456 af_glyph_hints_init( hints, memory );
457 af_loader_init( loader, hints );
458
459 error = af_loader_load_glyph( loader, module, slot->face,
460 glyph_index, load_flags );
461
462#ifdef FT_DEBUG_LEVEL_TRACE
463 if ( ft_trace_levels[FT_TRACE_COMP( FT_COMPONENT )] )
464 {
465#endif
466 af_glyph_hints_dump_points( hints, 0 );
467 af_glyph_hints_dump_segments( hints, 0 );
468 af_glyph_hints_dump_edges( hints, 0 );
469#ifdef FT_DEBUG_LEVEL_TRACE
470 }
471#endif
472
473 af_loader_done( loader );
474
475 return error;
476
477#else /* !FT_DEBUG_AUTOFIT */
478
479 AF_GlyphHintsRec hints[1];
480 AF_LoaderRec loader[1];
481
482 FT_UNUSED( size );
483
484
485 af_glyph_hints_init( hints, memory );
486 af_loader_init( loader, hints );
487
488 error = af_loader_load_glyph( loader, module, slot->face,
489 glyph_index, load_flags );
490
491 af_loader_done( loader );
492 af_glyph_hints_done( hints );
493
494 return error;
495
496#endif /* !FT_DEBUG_AUTOFIT */
497 }
498
499
500 FT_DEFINE_AUTOHINTER_INTERFACE(
501 af_autofitter_interface,
502
503 NULL, /* FT_AutoHinter_GlobalResetFunc reset_face */
504 NULL, /* FT_AutoHinter_GlobalGetFunc get_global_hints */
505 NULL, /* FT_AutoHinter_GlobalDoneFunc done_global_hints */
506 af_autofitter_load_glyph /* FT_AutoHinter_GlyphLoadFunc load_glyph */
507 )
508
509 FT_DEFINE_MODULE(
510 autofit_module_class,
511
512 FT_MODULE_HINTER,
513 sizeof ( AF_ModuleRec ),
514
515 "autofitter",
516 0x10000L, /* version 1.0 of the autofitter */
517 0x20000L, /* requires FreeType 2.0 or above */
518
519 (const void*)&af_autofitter_interface,
520
521 af_autofitter_init, /* FT_Module_Constructor module_init */
522 af_autofitter_done, /* FT_Module_Destructor module_done */
523 af_get_interface /* FT_Module_Requester get_interface */
524 )
525
526
527/* END */
528