• 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-2021 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_UInt     ss;
133     FT_UInt     i;
134     FT_UInt     dflt        = ~0U; /* a non-valid value */
135 
136 
137     /* the value AF_STYLE_UNASSIGNED means `uncovered glyph' */
138     for ( i = 0; i < (FT_UInt)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 ( (FT_UInt)style_class->script ==
172              globals->module->default_script )
173           dflt = ss;
174 
175         for ( range = script_class->script_uni_ranges;
176               range->first != 0;
177               range++ )
178         {
179           FT_ULong  charcode = range->first;
180           FT_UInt   gindex;
181 
182 
183           gindex = FT_Get_Char_Index( face, charcode );
184 
185           if ( gindex != 0                                                &&
186                gindex < (FT_ULong)globals->glyph_count                    &&
187                ( gstyles[gindex] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED )
188             gstyles[gindex] = (FT_UShort)ss;
189 
190           for (;;)
191           {
192             charcode = FT_Get_Next_Char( face, charcode, &gindex );
193 
194             if ( gindex == 0 || charcode > range->last )
195               break;
196 
197             if ( gindex < (FT_ULong)globals->glyph_count                    &&
198                  ( gstyles[gindex] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED )
199               gstyles[gindex] = (FT_UShort)ss;
200           }
201         }
202 
203         /* do the same for the script's non-base characters */
204         for ( range = script_class->script_uni_nonbase_ranges;
205               range->first != 0;
206               range++ )
207         {
208           FT_ULong  charcode = range->first;
209           FT_UInt   gindex;
210 
211 
212           gindex = FT_Get_Char_Index( face, charcode );
213 
214           if ( gindex != 0                                          &&
215                gindex < (FT_ULong)globals->glyph_count              &&
216                ( gstyles[gindex] & AF_STYLE_MASK ) == (FT_UShort)ss )
217             gstyles[gindex] |= AF_NONBASE;
218 
219           for (;;)
220           {
221             charcode = FT_Get_Next_Char( face, charcode, &gindex );
222 
223             if ( gindex == 0 || charcode > range->last )
224               break;
225 
226             if ( gindex < (FT_ULong)globals->glyph_count              &&
227                  ( gstyles[gindex] & AF_STYLE_MASK ) == (FT_UShort)ss )
228               gstyles[gindex] |= AF_NONBASE;
229           }
230         }
231       }
232       else
233       {
234         /* get glyphs not directly addressable by cmap */
235         af_shaper_get_coverage( globals, style_class, gstyles, 0 );
236       }
237     }
238 
239     /* handle the remaining default OpenType features ... */
240     for ( ss = 0; af_style_classes[ss]; ss++ )
241     {
242       AF_StyleClass  style_class = af_style_classes[ss];
243 
244 
245       if ( style_class->coverage == AF_COVERAGE_DEFAULT )
246         af_shaper_get_coverage( globals, style_class, gstyles, 0 );
247     }
248 
249     /* ... and finally the default OpenType features of the default script */
250     af_shaper_get_coverage( globals, af_style_classes[dflt], gstyles, 1 );
251 
252     /* mark ASCII digits */
253     for ( i = 0x30; i <= 0x39; i++ )
254     {
255       FT_UInt  gindex = FT_Get_Char_Index( face, i );
256 
257 
258       if ( gindex != 0 && gindex < (FT_ULong)globals->glyph_count )
259         gstyles[gindex] |= AF_DIGIT;
260     }
261 
262   Exit:
263     /*
264      * By default, all uncovered glyphs are set to the fallback style.
265      * XXX: Shouldn't we disable hinting or do something similar?
266      */
267     if ( globals->module->fallback_style != AF_STYLE_UNASSIGNED )
268     {
269       FT_Long  nn;
270 
271 
272       for ( nn = 0; nn < globals->glyph_count; nn++ )
273       {
274         if ( ( gstyles[nn] & AF_STYLE_MASK ) == AF_STYLE_UNASSIGNED )
275         {
276           gstyles[nn] &= ~AF_STYLE_MASK;
277           gstyles[nn] |= globals->module->fallback_style;
278         }
279       }
280     }
281 
282 #ifdef FT_DEBUG_LEVEL_TRACE
283 
284     FT_TRACE4(( "\n" ));
285     FT_TRACE4(( "style coverage\n" ));
286     FT_TRACE4(( "==============\n" ));
287     FT_TRACE4(( "\n" ));
288 
289     for ( ss = 0; af_style_classes[ss]; ss++ )
290     {
291       AF_StyleClass  style_class = af_style_classes[ss];
292       FT_UInt        count       = 0;
293       FT_Long        idx;
294 
295 
296       FT_TRACE4(( "%s:\n", af_style_names[style_class->style] ));
297 
298       for ( idx = 0; idx < globals->glyph_count; idx++ )
299       {
300         if ( ( gstyles[idx] & AF_STYLE_MASK ) == style_class->style )
301         {
302           if ( !( count % 10 ) )
303             FT_TRACE4(( " " ));
304 
305           FT_TRACE4(( " %ld", idx ));
306           count++;
307 
308           if ( !( count % 10 ) )
309             FT_TRACE4(( "\n" ));
310         }
311       }
312 
313       if ( !count )
314         FT_TRACE4(( "  (none)\n" ));
315       if ( count % 10 )
316         FT_TRACE4(( "\n" ));
317     }
318 
319 #endif /* FT_DEBUG_LEVEL_TRACE */
320 
321     FT_Set_Charmap( face, old_charmap );
322     return error;
323   }
324 
325 
326   FT_LOCAL_DEF( FT_Error )
af_face_globals_new(FT_Face face,AF_FaceGlobals * aglobals,AF_Module module)327   af_face_globals_new( FT_Face          face,
328                        AF_FaceGlobals  *aglobals,
329                        AF_Module        module )
330   {
331     FT_Error        error;
332     FT_Memory       memory;
333     AF_FaceGlobals  globals = NULL;
334 
335 
336     memory = face->memory;
337 
338     /* we allocate an AF_FaceGlobals structure together */
339     /* with the glyph_styles array                      */
340     if ( FT_ALLOC( globals,
341                    sizeof ( *globals ) +
342                      (FT_ULong)face->num_glyphs * sizeof ( FT_UShort ) ) )
343       goto Exit;
344 
345     globals->face                      = face;
346     globals->glyph_count               = face->num_glyphs;
347     /* right after the globals structure come the glyph styles */
348     globals->glyph_styles              = (FT_UShort*)( globals + 1 );
349     globals->module                    = module;
350     globals->stem_darkening_for_ppem   = 0;
351     globals->darken_x                  = 0;
352     globals->darken_y                  = 0;
353     globals->standard_vertical_width   = 0;
354     globals->standard_horizontal_width = 0;
355     globals->scale_down_factor         = 0;
356 
357 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
358     globals->hb_font = hb_ft_font_create( face, NULL );
359     globals->hb_buf  = hb_buffer_create();
360 #endif
361 
362     error = af_face_globals_compute_style_coverage( globals );
363     if ( error )
364     {
365       af_face_globals_free( globals );
366       globals = NULL;
367     }
368     else
369       globals->increase_x_height = AF_PROP_INCREASE_X_HEIGHT_MAX;
370 
371   Exit:
372     *aglobals = globals;
373     return error;
374   }
375 
376 
377   FT_LOCAL_DEF( void )
af_face_globals_free(AF_FaceGlobals globals)378   af_face_globals_free( AF_FaceGlobals  globals )
379   {
380     if ( globals )
381     {
382       FT_Memory  memory = globals->face->memory;
383       FT_UInt    nn;
384 
385 
386       for ( nn = 0; nn < AF_STYLE_MAX; nn++ )
387       {
388         if ( globals->metrics[nn] )
389         {
390           AF_StyleClass          style_class =
391             af_style_classes[nn];
392           AF_WritingSystemClass  writing_system_class =
393             af_writing_system_classes[style_class->writing_system];
394 
395 
396           if ( writing_system_class->style_metrics_done )
397             writing_system_class->style_metrics_done( globals->metrics[nn] );
398 
399           FT_FREE( globals->metrics[nn] );
400         }
401       }
402 
403 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
404       hb_font_destroy( globals->hb_font );
405       hb_buffer_destroy( globals->hb_buf );
406 #endif
407 
408       /* no need to free `globals->glyph_styles'; */
409       /* it is part of the `globals' array        */
410       FT_FREE( globals );
411     }
412   }
413 
414 
415   FT_LOCAL_DEF( FT_Error )
af_face_globals_get_metrics(AF_FaceGlobals globals,FT_UInt gindex,FT_UInt options,AF_StyleMetrics * ametrics)416   af_face_globals_get_metrics( AF_FaceGlobals    globals,
417                                FT_UInt           gindex,
418                                FT_UInt           options,
419                                AF_StyleMetrics  *ametrics )
420   {
421     AF_StyleMetrics  metrics = NULL;
422 
423     AF_Style               style = (AF_Style)options;
424     AF_WritingSystemClass  writing_system_class;
425     AF_StyleClass          style_class;
426 
427     FT_Error  error = FT_Err_Ok;
428 
429 
430     if ( gindex >= (FT_ULong)globals->glyph_count )
431     {
432       error = FT_THROW( Invalid_Argument );
433       goto Exit;
434     }
435 
436     /* if we have a forced style (via `options'), use it, */
437     /* otherwise look into `glyph_styles' array           */
438     if ( style == AF_STYLE_NONE_DFLT || style + 1 >= AF_STYLE_MAX )
439       style = (AF_Style)( globals->glyph_styles[gindex] &
440                           AF_STYLE_UNASSIGNED           );
441 
442   Again:
443     style_class          = af_style_classes[style];
444     writing_system_class = af_writing_system_classes
445                              [style_class->writing_system];
446 
447     metrics = globals->metrics[style];
448     if ( !metrics )
449     {
450       /* create the global metrics object if necessary */
451       FT_Memory  memory = globals->face->memory;
452 
453 
454       if ( FT_ALLOC( metrics, writing_system_class->style_metrics_size ) )
455         goto Exit;
456 
457       metrics->style_class = style_class;
458       metrics->globals     = globals;
459 
460       if ( writing_system_class->style_metrics_init )
461       {
462         error = writing_system_class->style_metrics_init( metrics,
463                                                           globals->face );
464         if ( error )
465         {
466           if ( writing_system_class->style_metrics_done )
467             writing_system_class->style_metrics_done( metrics );
468 
469           FT_FREE( metrics );
470 
471           /* internal error code -1 indicates   */
472           /* that no blue zones have been found */
473           if ( error == -1 )
474           {
475             style = (AF_Style)( globals->glyph_styles[gindex] &
476                                 AF_STYLE_UNASSIGNED           );
477             /* IMPORTANT: Clear the error code, see
478              * https://gitlab.freedesktop.org/freetype/freetype/-/issues/1063
479              */
480             error = FT_Err_Ok;
481             goto Again;
482           }
483 
484           goto Exit;
485         }
486       }
487 
488       globals->metrics[style] = metrics;
489     }
490 
491   Exit:
492     *ametrics = metrics;
493 
494     return error;
495   }
496 
497 
498   FT_LOCAL_DEF( FT_Bool )
af_face_globals_is_digit(AF_FaceGlobals globals,FT_UInt gindex)499   af_face_globals_is_digit( AF_FaceGlobals  globals,
500                             FT_UInt         gindex )
501   {
502     if ( gindex < (FT_ULong)globals->glyph_count )
503       return FT_BOOL( globals->glyph_styles[gindex] & AF_DIGIT );
504 
505     return FT_BOOL( 0 );
506   }
507 
508 
509 /* END */
510