• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /****************************************************************************
2  *
3  * afglobal.c
4  *
5  *   Auto-fitter routines to compute global hinting values (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 "afranges.h"
21 #include "afshaper.h"
22 #include "afws-decl.h"
23 #include <freetype/internal/ftdebug.h>
24 
25 
26   /**************************************************************************
27    *
28    * The macro FT_COMPONENT is used in trace mode.  It is an implicit
29    * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
30    * messages during execution.
31    */
32 #undef  FT_COMPONENT
33 #define FT_COMPONENT  afglobal
34 
35 
36 #include "aferrors.h"
37 
38 
39 #undef  SCRIPT
40 #define SCRIPT( s, S, d, h, H, ss )         \
41           AF_DEFINE_SCRIPT_CLASS(           \
42             af_ ## s ## _script_class,      \
43             AF_SCRIPT_ ## S,                \
44             af_ ## s ## _uniranges,         \
45             af_ ## s ## _nonbase_uniranges, \
46             AF_ ## H,                       \
47             ss )
48 
49 #include "afscript.h"
50 
51 
52 #undef  STYLE
53 #define STYLE( s, S, d, ws, sc, ss, c )  \
54           AF_DEFINE_STYLE_CLASS(         \
55             af_ ## s ## _style_class,    \
56             AF_STYLE_ ## S,              \
57             ws,                          \
58             sc,                          \
59             ss,                          \
60             c )
61 
62 #include "afstyles.h"
63 
64 
65 #undef  WRITING_SYSTEM
66 #define WRITING_SYSTEM( ws, WS )               \
67           &af_ ## ws ## _writing_system_class,
68 
69   FT_LOCAL_ARRAY_DEF( AF_WritingSystemClass )
70   af_writing_system_classes[] =
71   {
72 
73 #include "afws-iter.h"
74 
75     NULL  /* do not remove */
76   };
77 
78 
79 #undef  SCRIPT
80 #define SCRIPT( s, S, d, h, H, ss )   \
81           &af_ ## s ## _script_class,
82 
83   FT_LOCAL_ARRAY_DEF( AF_ScriptClass )
84   af_script_classes[] =
85   {
86 
87 #include "afscript.h"
88 
89     NULL  /* do not remove */
90   };
91 
92 
93 #undef  STYLE
94 #define STYLE( s, S, d, ws, sc, ss, c ) \
95           &af_ ## s ## _style_class,
96 
97   FT_LOCAL_ARRAY_DEF( AF_StyleClass )
98   af_style_classes[] =
99   {
100 
101 #include "afstyles.h"
102 
103     NULL  /* do not remove */
104   };
105 
106 
107 #ifdef FT_DEBUG_LEVEL_TRACE
108 
109 #undef  STYLE
110 #define STYLE( s, S, d, ws, sc, ss, c )  #s,
111 
112   FT_LOCAL_ARRAY_DEF( char* )
113   af_style_names[] =
114   {
115 
116 #include "afstyles.h"
117 
118   };
119 
120 #endif /* FT_DEBUG_LEVEL_TRACE */
121 
122 
123   /* Compute the style index of each glyph within a given face. */
124 
125   static FT_Error
af_face_globals_compute_style_coverage(AF_FaceGlobals globals)126   af_face_globals_compute_style_coverage( AF_FaceGlobals  globals )
127   {
128     FT_Error    error;
129     FT_Face     face        = globals->face;
130     FT_CharMap  old_charmap = face->charmap;
131     FT_UShort*  gstyles     = globals->glyph_styles;
132     FT_UShort   ss;
133     FT_UShort   dflt        = 0xFFFFU; /* a non-valid value */
134     FT_UInt     i;
135 
136 
137     /* the value AF_STYLE_UNASSIGNED means `uncovered glyph' */
138     for ( i = 0; i < globals->glyph_count; i++ )
139       gstyles[i] = AF_STYLE_UNASSIGNED;
140 
141     error = FT_Select_Charmap( face, FT_ENCODING_UNICODE );
142     if ( error )
143     {
144       /*
145        * Ignore this error; we simply use the fallback style.
146        * XXX: Shouldn't we rather disable hinting?
147        */
148       error = FT_Err_Ok;
149       goto Exit;
150     }
151 
152     /* scan each style in a Unicode charmap */
153     for ( ss = 0; af_style_classes[ss]; ss++ )
154     {
155       AF_StyleClass       style_class =
156                             af_style_classes[ss];
157       AF_ScriptClass      script_class =
158                             af_script_classes[style_class->script];
159       AF_Script_UniRange  range;
160 
161 
162       if ( !script_class->script_uni_ranges )
163         continue;
164 
165       /*
166        * Scan all Unicode points in the range and set the corresponding
167        * glyph style index.
168        */
169       if ( style_class->coverage == AF_COVERAGE_DEFAULT )
170       {
171         if ( style_class->script == globals->module->default_script )
172           dflt = ss;
173 
174         for ( range = script_class->script_uni_ranges;
175               range->first != 0;
176               range++ )
177         {
178           FT_ULong  charcode = range->first;
179           FT_UInt   gindex;
180 
181 
182           gindex = FT_Get_Char_Index( face, charcode );
183 
184           if ( gindex != 0                                                &&
185                gindex < globals->glyph_count                              &&
186                ( gstyles[gindex] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED )
187             gstyles[gindex] = ss;
188 
189           for (;;)
190           {
191             charcode = FT_Get_Next_Char( face, charcode, &gindex );
192 
193             if ( gindex == 0 || charcode > range->last )
194               break;
195 
196             if ( gindex < globals->glyph_count                              &&
197                  ( gstyles[gindex] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED )
198               gstyles[gindex] = ss;
199           }
200         }
201 
202         /* do the same for the script's non-base characters */
203         for ( range = script_class->script_uni_nonbase_ranges;
204               range->first != 0;
205               range++ )
206         {
207           FT_ULong  charcode = range->first;
208           FT_UInt   gindex;
209 
210 
211           gindex = FT_Get_Char_Index( face, charcode );
212 
213           if ( gindex != 0                               &&
214                gindex < globals->glyph_count             &&
215                ( gstyles[gindex] & AF_STYLE_MASK ) == ss )
216             gstyles[gindex] |= AF_NONBASE;
217 
218           for (;;)
219           {
220             charcode = FT_Get_Next_Char( face, charcode, &gindex );
221 
222             if ( gindex == 0 || charcode > range->last )
223               break;
224 
225             if ( gindex < globals->glyph_count             &&
226                  ( gstyles[gindex] & AF_STYLE_MASK ) == ss )
227               gstyles[gindex] |= AF_NONBASE;
228           }
229         }
230       }
231       else
232       {
233         /* get glyphs not directly addressable by cmap */
234         af_shaper_get_coverage( globals, style_class, gstyles, 0 );
235       }
236     }
237 
238     /* handle the remaining default OpenType features ... */
239     for ( ss = 0; af_style_classes[ss]; ss++ )
240     {
241       AF_StyleClass  style_class = af_style_classes[ss];
242 
243 
244       if ( style_class->coverage == AF_COVERAGE_DEFAULT )
245         af_shaper_get_coverage( globals, style_class, gstyles, 0 );
246     }
247 
248     /* ... and finally the default OpenType features of the default script */
249     af_shaper_get_coverage( globals, af_style_classes[dflt], gstyles, 1 );
250 
251     /* mark ASCII digits */
252     for ( i = 0x30; i <= 0x39; i++ )
253     {
254       FT_UInt  gindex = FT_Get_Char_Index( face, i );
255 
256 
257       if ( gindex != 0 && gindex < globals->glyph_count )
258         gstyles[gindex] |= AF_DIGIT;
259     }
260 
261   Exit:
262     /*
263      * By default, all uncovered glyphs are set to the fallback style.
264      * XXX: Shouldn't we disable hinting or do something similar?
265      */
266     if ( globals->module->fallback_style != AF_STYLE_UNASSIGNED )
267     {
268       FT_UInt  nn;
269 
270 
271       for ( nn = 0; nn < globals->glyph_count; nn++ )
272       {
273         if ( ( gstyles[nn] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED )
274         {
275           gstyles[nn] &= ~AF_STYLE_MASK;
276           gstyles[nn] |= globals->module->fallback_style;
277         }
278       }
279     }
280 
281 #ifdef FT_DEBUG_LEVEL_TRACE
282 
283     FT_TRACE4(( "\n" ));
284     FT_TRACE4(( "style coverage\n" ));
285     FT_TRACE4(( "==============\n" ));
286     FT_TRACE4(( "\n" ));
287 
288     for ( ss = 0; af_style_classes[ss]; ss++ )
289     {
290       AF_StyleClass  style_class = af_style_classes[ss];
291       FT_UInt        count       = 0;
292       FT_UInt        idx;
293 
294 
295       FT_TRACE4(( "%s:\n", af_style_names[style_class->style] ));
296 
297       for ( idx = 0; idx < globals->glyph_count; idx++ )
298       {
299         if ( ( gstyles[idx] & AF_STYLE_MASK ) == style_class->style )
300         {
301           if ( !( count % 10 ) )
302             FT_TRACE4(( " " ));
303 
304           FT_TRACE4(( " %d", idx ));
305           count++;
306 
307           if ( !( count % 10 ) )
308             FT_TRACE4(( "\n" ));
309         }
310       }
311 
312       if ( !count )
313         FT_TRACE4(( "  (none)\n" ));
314       if ( count % 10 )
315         FT_TRACE4(( "\n" ));
316     }
317 
318 #endif /* FT_DEBUG_LEVEL_TRACE */
319 
320     face->charmap = old_charmap;
321     return error;
322   }
323 
324 
325   FT_LOCAL_DEF( FT_Error )
af_face_globals_new(FT_Face face,AF_FaceGlobals * aglobals,AF_Module module)326   af_face_globals_new( FT_Face          face,
327                        AF_FaceGlobals  *aglobals,
328                        AF_Module        module )
329   {
330     FT_Error        error;
331     FT_Memory       memory;
332     AF_FaceGlobals  globals = NULL;
333 
334 
335     memory = face->memory;
336 
337     /* we allocate an AF_FaceGlobals structure together */
338     /* with the glyph_styles array                      */
339     if ( FT_QALLOC( globals,
340                     sizeof ( *globals ) +
341                       (FT_ULong)face->num_glyphs * sizeof ( FT_UShort ) ) )
342       goto Exit;
343 
344     FT_ZERO( &globals->metrics );
345 
346     globals->face                      = face;
347     globals->glyph_count               = (FT_UInt)face->num_glyphs;
348     /* right after the globals structure come the glyph styles */
349     globals->glyph_styles              = (FT_UShort*)( globals + 1 );
350     globals->module                    = module;
351     globals->stem_darkening_for_ppem   = 0;
352     globals->darken_x                  = 0;
353     globals->darken_y                  = 0;
354     globals->standard_vertical_width   = 0;
355     globals->standard_horizontal_width = 0;
356     globals->scale_down_factor         = 0;
357 
358 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
359     globals->hb_font = hb_ft_font_create_( face, NULL );
360     globals->hb_buf  = hb_buffer_create();
361 #endif
362 
363     error = af_face_globals_compute_style_coverage( globals );
364     if ( error )
365     {
366       af_face_globals_free( globals );
367       globals = NULL;
368     }
369     else
370       globals->increase_x_height = AF_PROP_INCREASE_X_HEIGHT_MAX;
371 
372   Exit:
373     *aglobals = globals;
374     return error;
375   }
376 
377 
378   FT_LOCAL_DEF( void )
af_face_globals_free(AF_FaceGlobals globals)379   af_face_globals_free( AF_FaceGlobals  globals )
380   {
381     if ( globals )
382     {
383       FT_Memory  memory = globals->face->memory;
384       FT_UInt    nn;
385 
386 
387       for ( nn = 0; nn < AF_STYLE_MAX; nn++ )
388       {
389         if ( globals->metrics[nn] )
390         {
391           AF_StyleClass          style_class =
392             af_style_classes[nn];
393           AF_WritingSystemClass  writing_system_class =
394             af_writing_system_classes[style_class->writing_system];
395 
396 
397           if ( writing_system_class->style_metrics_done )
398             writing_system_class->style_metrics_done( globals->metrics[nn] );
399 
400           FT_FREE( globals->metrics[nn] );
401         }
402       }
403 
404 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
405       hb_font_destroy( globals->hb_font );
406       hb_buffer_destroy( globals->hb_buf );
407 #endif
408 
409       /* no need to free `globals->glyph_styles'; */
410       /* it is part of the `globals' array        */
411       FT_FREE( globals );
412     }
413   }
414 
415 
416   FT_LOCAL_DEF( FT_Error )
af_face_globals_get_metrics(AF_FaceGlobals globals,FT_UInt gindex,FT_UInt options,AF_StyleMetrics * ametrics)417   af_face_globals_get_metrics( AF_FaceGlobals    globals,
418                                FT_UInt           gindex,
419                                FT_UInt           options,
420                                AF_StyleMetrics  *ametrics )
421   {
422     AF_StyleMetrics  metrics = NULL;
423 
424     AF_Style               style = (AF_Style)options;
425     AF_WritingSystemClass  writing_system_class;
426     AF_StyleClass          style_class;
427 
428     FT_Error  error = FT_Err_Ok;
429 
430 
431     if ( gindex >= globals->glyph_count )
432     {
433       error = FT_THROW( Invalid_Argument );
434       goto Exit;
435     }
436 
437     /* if we have a forced style (via `options'), use it, */
438     /* otherwise look into `glyph_styles' array           */
439     if ( style == AF_STYLE_NONE_DFLT || style + 1 >= AF_STYLE_MAX )
440       style = (AF_Style)( globals->glyph_styles[gindex] &
441                           AF_STYLE_UNASSIGNED           );
442 
443   Again:
444     style_class          = af_style_classes[style];
445     writing_system_class = af_writing_system_classes
446                              [style_class->writing_system];
447 
448     metrics = globals->metrics[style];
449     if ( !metrics )
450     {
451       /* create the global metrics object if necessary */
452       FT_Memory  memory = globals->face->memory;
453 
454 
455       if ( FT_ALLOC( metrics, writing_system_class->style_metrics_size ) )
456         goto Exit;
457 
458       metrics->style_class = style_class;
459       metrics->globals     = globals;
460 
461       if ( writing_system_class->style_metrics_init )
462       {
463         error = writing_system_class->style_metrics_init( metrics,
464                                                           globals->face );
465         if ( error )
466         {
467           if ( writing_system_class->style_metrics_done )
468             writing_system_class->style_metrics_done( metrics );
469 
470           FT_FREE( metrics );
471 
472           /* internal error code -1 indicates   */
473           /* that no blue zones have been found */
474           if ( error == -1 )
475           {
476             style = (AF_Style)( globals->glyph_styles[gindex] &
477                                 AF_STYLE_UNASSIGNED           );
478             /* IMPORTANT: Clear the error code, see
479              * https://gitlab.freedesktop.org/freetype/freetype/-/issues/1063
480              */
481             error = FT_Err_Ok;
482             goto Again;
483           }
484 
485           goto Exit;
486         }
487       }
488 
489       globals->metrics[style] = metrics;
490     }
491 
492   Exit:
493     *ametrics = metrics;
494 
495     return error;
496   }
497 
498 
499   FT_LOCAL_DEF( FT_Bool )
af_face_globals_is_digit(AF_FaceGlobals globals,FT_UInt gindex)500   af_face_globals_is_digit( AF_FaceGlobals  globals,
501                             FT_UInt         gindex )
502   {
503     if ( gindex < globals->glyph_count )
504       return FT_BOOL( globals->glyph_styles[gindex] & AF_DIGIT );
505 
506     return FT_BOOL( 0 );
507   }
508 
509 
510 /* END */
511