1 /****************************************************************************
   2  *
   3  * afmodule.c
   4  *
   5  *   Auto-fitter module implementation (body).
   6  *
   7  * Copyright (C) 2003-2019 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   AF_GlyphHintsRec  _af_debug_hints_rec[1];
  52 
  53   void*  _af_debug_hints = _af_debug_hints_rec;
  54 #endif
  55 
  56 #include FT_INTERNAL_OBJECTS_H
  57 #include FT_INTERNAL_DEBUG_H
  58 #include FT_DRIVER_H
  59 #include FT_SERVICE_PROPERTIES_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 =
  93           (FT_Pointer)globals;
  94         face->autohint.finalizer =
  95           (FT_Generic_Finalizer)af_face_globals_free;
  96       }
  97     }
  98 
  99     if ( !error )
 100       *aglobals = globals;
 101 
 102     return error;
 103   }
 104 
 105 
 106   static FT_Error
 107   af_property_set( FT_Module    ft_module,
 108                    const char*  property_name,
 109                    const void*  value,
 110                    FT_Bool      value_is_string )
 111   {
 112     FT_Error   error  = FT_Err_Ok;
 113     AF_Module  module = (AF_Module)ft_module;
 114 
 115 #ifndef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
 116     FT_UNUSED( value_is_string );
 117 #endif
 118 
 119 
 120     if ( !ft_strcmp( property_name, "fallback-script" ) )
 121     {
 122       FT_UInt*  fallback_script;
 123       FT_UInt   ss;
 124 
 125 
 126 #ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
 127       if ( value_is_string )
 128         return FT_THROW( Invalid_Argument );
 129 #endif
 130 
 131       fallback_script = (FT_UInt*)value;
 132 
 133       /* We translate the fallback script to a fallback style that uses */
 134       /* `fallback-script' as its script and `AF_COVERAGE_NONE' as its  */
 135       /* coverage value.                                                */
 136       for ( ss = 0; af_style_classes[ss]; ss++ )
 137       {
 138         AF_StyleClass  style_class = af_style_classes[ss];
 139 
 140 
 141         if ( (FT_UInt)style_class->script == *fallback_script &&
 142              style_class->coverage == AF_COVERAGE_DEFAULT     )
 143         {
 144           module->fallback_style = ss;
 145           break;
 146         }
 147       }
 148 
 149       if ( !af_style_classes[ss] )
 150       {
 151         FT_TRACE0(( "af_property_set: Invalid value %d for property `%s'\n",
 152                     fallback_script, property_name ));
 153         return FT_THROW( Invalid_Argument );
 154       }
 155 
 156       return error;
 157     }
 158     else if ( !ft_strcmp( property_name, "default-script" ) )
 159     {
 160       FT_UInt*  default_script;
 161 
 162 
 163 #ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
 164       if ( value_is_string )
 165         return FT_THROW( Invalid_Argument );
 166 #endif
 167 
 168       default_script = (FT_UInt*)value;
 169 
 170       module->default_script = *default_script;
 171 
 172       return error;
 173     }
 174     else if ( !ft_strcmp( property_name, "increase-x-height" ) )
 175     {
 176       FT_Prop_IncreaseXHeight*  prop;
 177       AF_FaceGlobals            globals;
 178 
 179 
 180 #ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
 181       if ( value_is_string )
 182         return FT_THROW( Invalid_Argument );
 183 #endif
 184 
 185       prop = (FT_Prop_IncreaseXHeight*)value;
 186 
 187       error = af_property_get_face_globals( prop->face, &globals, module );
 188       if ( !error )
 189         globals->increase_x_height = prop->limit;
 190 
 191       return error;
 192     }
 193 #ifdef AF_CONFIG_OPTION_USE_WARPER
 194     else if ( !ft_strcmp( property_name, "warping" ) )
 195     {
 196 #ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
 197       if ( value_is_string )
 198       {
 199         const char*  s = (const char*)value;
 200         long         w = ft_strtol( s, NULL, 10 );
 201 
 202 
 203         if ( w == 0 )
 204           module->warping = 0;
 205         else if ( w == 1 )
 206           module->warping = 1;
 207         else
 208           return FT_THROW( Invalid_Argument );
 209       }
 210       else
 211 #endif
 212       {
 213         FT_Bool*  warping = (FT_Bool*)value;
 214 
 215 
 216         module->warping = *warping;
 217       }
 218 
 219       return error;
 220     }
 221 #endif /* AF_CONFIG_OPTION_USE_WARPER */
 222     else if ( !ft_strcmp( property_name, "darkening-parameters" ) )
 223     {
 224       FT_Int*  darken_params;
 225       FT_Int   x1, y1, x2, y2, x3, y3, x4, y4;
 226 
 227 #ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
 228       FT_Int   dp[8];
 229 
 230 
 231       if ( value_is_string )
 232       {
 233         const char*  s = (const char*)value;
 234         char*        ep;
 235         int          i;
 236 
 237 
 238         /* eight comma-separated numbers */
 239         for ( i = 0; i < 7; i++ )
 240         {
 241           dp[i] = (FT_Int)ft_strtol( s, &ep, 10 );
 242           if ( *ep != ',' || s == ep )
 243             return FT_THROW( Invalid_Argument );
 244 
 245           s = ep + 1;
 246         }
 247 
 248         dp[7] = (FT_Int)ft_strtol( s, &ep, 10 );
 249         if ( !( *ep == '\0' || *ep == ' ' ) || s == ep )
 250           return FT_THROW( Invalid_Argument );
 251 
 252         darken_params = dp;
 253       }
 254       else
 255 #endif
 256         darken_params = (FT_Int*)value;
 257 
 258       x1 = darken_params[0];
 259       y1 = darken_params[1];
 260       x2 = darken_params[2];
 261       y2 = darken_params[3];
 262       x3 = darken_params[4];
 263       y3 = darken_params[5];
 264       x4 = darken_params[6];
 265       y4 = darken_params[7];
 266 
 267       if ( x1 < 0   || x2 < 0   || x3 < 0   || x4 < 0   ||
 268            y1 < 0   || y2 < 0   || y3 < 0   || y4 < 0   ||
 269            x1 > x2  || x2 > x3  || x3 > x4              ||
 270            y1 > 500 || y2 > 500 || y3 > 500 || y4 > 500 )
 271         return FT_THROW( Invalid_Argument );
 272 
 273       module->darken_params[0] = x1;
 274       module->darken_params[1] = y1;
 275       module->darken_params[2] = x2;
 276       module->darken_params[3] = y2;
 277       module->darken_params[4] = x3;
 278       module->darken_params[5] = y3;
 279       module->darken_params[6] = x4;
 280       module->darken_params[7] = y4;
 281 
 282       return error;
 283     }
 284     else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
 285     {
 286 #ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
 287       if ( value_is_string )
 288       {
 289         const char*  s   = (const char*)value;
 290         long         nsd = ft_strtol( s, NULL, 10 );
 291 
 292 
 293         if ( !nsd )
 294           module->no_stem_darkening = FALSE;
 295         else
 296           module->no_stem_darkening = TRUE;
 297       }
 298       else
 299 #endif
 300       {
 301         FT_Bool*  no_stem_darkening = (FT_Bool*)value;
 302 
 303 
 304         module->no_stem_darkening = *no_stem_darkening;
 305       }
 306 
 307       return error;
 308     }
 309 
 310     FT_TRACE0(( "af_property_set: missing property `%s'\n",
 311                 property_name ));
 312     return FT_THROW( Missing_Property );
 313   }
 314 
 315 
 316   static FT_Error
 317   af_property_get( FT_Module    ft_module,
 318                    const char*  property_name,
 319                    void*        value )
 320   {
 321     FT_Error   error          = FT_Err_Ok;
 322     AF_Module  module         = (AF_Module)ft_module;
 323     FT_UInt    fallback_style = module->fallback_style;
 324     FT_UInt    default_script = module->default_script;
 325 #ifdef AF_CONFIG_OPTION_USE_WARPER
 326     FT_Bool    warping        = module->warping;
 327 #endif
 328 
 329 
 330     if ( !ft_strcmp( property_name, "glyph-to-script-map" ) )
 331     {
 332       FT_Prop_GlyphToScriptMap*  prop = (FT_Prop_GlyphToScriptMap*)value;
 333       AF_FaceGlobals             globals;
 334 
 335 
 336       error = af_property_get_face_globals( prop->face, &globals, module );
 337       if ( !error )
 338         prop->map = globals->glyph_styles;
 339 
 340       return error;
 341     }
 342     else if ( !ft_strcmp( property_name, "fallback-script" ) )
 343     {
 344       FT_UInt*  val = (FT_UInt*)value;
 345 
 346       AF_StyleClass  style_class = af_style_classes[fallback_style];
 347 
 348 
 349       *val = style_class->script;
 350 
 351       return error;
 352     }
 353     else if ( !ft_strcmp( property_name, "default-script" ) )
 354     {
 355       FT_UInt*  val = (FT_UInt*)value;
 356 
 357 
 358       *val = default_script;
 359 
 360       return error;
 361     }
 362     else if ( !ft_strcmp( property_name, "increase-x-height" ) )
 363     {
 364       FT_Prop_IncreaseXHeight*  prop = (FT_Prop_IncreaseXHeight*)value;
 365       AF_FaceGlobals            globals;
 366 
 367 
 368       error = af_property_get_face_globals( prop->face, &globals, module );
 369       if ( !error )
 370         prop->limit = globals->increase_x_height;
 371 
 372       return error;
 373     }
 374 #ifdef AF_CONFIG_OPTION_USE_WARPER
 375     else if ( !ft_strcmp( property_name, "warping" ) )
 376     {
 377       FT_Bool*  val = (FT_Bool*)value;
 378 
 379 
 380       *val = warping;
 381 
 382       return error;
 383     }
 384 #endif /* AF_CONFIG_OPTION_USE_WARPER */
 385     else if ( !ft_strcmp( property_name, "darkening-parameters" ) )
 386     {
 387       FT_Int*  darken_params = module->darken_params;
 388       FT_Int*  val           = (FT_Int*)value;
 389 
 390 
 391       val[0] = darken_params[0];
 392       val[1] = darken_params[1];
 393       val[2] = darken_params[2];
 394       val[3] = darken_params[3];
 395       val[4] = darken_params[4];
 396       val[5] = darken_params[5];
 397       val[6] = darken_params[6];
 398       val[7] = darken_params[7];
 399 
 400       return error;
 401     }
 402     else if ( !ft_strcmp( property_name, "no-stem-darkening" ) )
 403     {
 404       FT_Bool   no_stem_darkening = module->no_stem_darkening;
 405       FT_Bool*  val               = (FT_Bool*)value;
 406 
 407 
 408       *val = no_stem_darkening;
 409 
 410       return error;
 411     }
 412 
 413     FT_TRACE0(( "af_property_get: missing property `%s'\n",
 414                 property_name ));
 415     return FT_THROW( Missing_Property );
 416   }
 417 
 418 
 419   FT_DEFINE_SERVICE_PROPERTIESREC(
 420     af_service_properties,
 421 
 422     (FT_Properties_SetFunc)af_property_set,        /* set_property */
 423     (FT_Properties_GetFunc)af_property_get )       /* get_property */
 424 
 425 
 426   FT_DEFINE_SERVICEDESCREC1(
 427     af_services,
 428 
 429     FT_SERVICE_ID_PROPERTIES, &af_service_properties )
 430 
 431 
 432   FT_CALLBACK_DEF( FT_Module_Interface )
 433   af_get_interface( FT_Module    module,
 434                     const char*  module_interface )
 435   {
 436     FT_UNUSED( module );
 437 
 438     return ft_service_list_lookup( af_services, module_interface );
 439   }
 440 
 441 
 442   FT_CALLBACK_DEF( FT_Error )
 443   af_autofitter_init( FT_Module  ft_module )      /* AF_Module */
 444   {
 445     AF_Module  module = (AF_Module)ft_module;
 446 
 447 
 448     module->fallback_style    = AF_STYLE_FALLBACK;
 449     module->default_script    = AF_SCRIPT_DEFAULT;
 450 #ifdef AF_CONFIG_OPTION_USE_WARPER
 451     module->warping           = 0;
 452 #endif
 453     module->no_stem_darkening = TRUE;
 454 
 455     module->darken_params[0]  = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X1;
 456     module->darken_params[1]  = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y1;
 457     module->darken_params[2]  = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X2;
 458     module->darken_params[3]  = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y2;
 459     module->darken_params[4]  = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X3;
 460     module->darken_params[5]  = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y3;
 461     module->darken_params[6]  = CFF_CONFIG_OPTION_DARKENING_PARAMETER_X4;
 462     module->darken_params[7]  = CFF_CONFIG_OPTION_DARKENING_PARAMETER_Y4;
 463 
 464     return FT_Err_Ok;
 465   }
 466 
 467 
 468   FT_CALLBACK_DEF( void )
 469   af_autofitter_done( FT_Module  ft_module )      /* AF_Module */
 470   {
 471     FT_UNUSED( ft_module );
 472 
 473 #ifdef FT_DEBUG_AUTOFIT
 474     if ( _af_debug_hints_rec->memory )
 475       af_glyph_hints_done( _af_debug_hints_rec );
 476 #endif
 477   }
 478 
 479 
 480   FT_CALLBACK_DEF( FT_Error )
 481   af_autofitter_load_glyph( AF_Module     module,
 482                             FT_GlyphSlot  slot,
 483                             FT_Size       size,
 484                             FT_UInt       glyph_index,
 485                             FT_Int32      load_flags )
 486   {
 487     FT_Error   error  = FT_Err_Ok;
 488     FT_Memory  memory = module->root.library->memory;
 489 
 490 #ifdef FT_DEBUG_AUTOFIT
 491 
 492     /* in debug mode, we use a global object that survives this routine */
 493 
 494     AF_GlyphHints  hints = _af_debug_hints_rec;
 495     AF_LoaderRec   loader[1];
 496 
 497     FT_UNUSED( size );
 498 
 499 
 500     if ( hints->memory )
 501       af_glyph_hints_done( hints );
 502 
 503     af_glyph_hints_init( hints, memory );
 504     af_loader_init( loader, hints );
 505 
 506     error = af_loader_load_glyph( loader, module, slot->face,
 507                                   glyph_index, load_flags );
 508 
 509 #ifdef FT_DEBUG_LEVEL_TRACE
 510     if ( ft_trace_levels[FT_TRACE_COMP( FT_COMPONENT )] )
 511     {
 512 #endif
 513       af_glyph_hints_dump_points( hints, 0 );
 514       af_glyph_hints_dump_segments( hints, 0 );
 515       af_glyph_hints_dump_edges( hints, 0 );
 516 #ifdef FT_DEBUG_LEVEL_TRACE
 517     }
 518 #endif
 519 
 520     af_loader_done( loader );
 521 
 522     return error;
 523 
 524 #else /* !FT_DEBUG_AUTOFIT */
 525 
 526     AF_GlyphHintsRec  hints[1];
 527     AF_LoaderRec      loader[1];
 528 
 529     FT_UNUSED( size );
 530 
 531 
 532     af_glyph_hints_init( hints, memory );
 533     af_loader_init( loader, hints );
 534 
 535     error = af_loader_load_glyph( loader, module, slot->face,
 536                                   glyph_index, load_flags );
 537 
 538     af_loader_done( loader );
 539     af_glyph_hints_done( hints );
 540 
 541     return error;
 542 
 543 #endif /* !FT_DEBUG_AUTOFIT */
 544   }
 545 
 546 
 547   FT_DEFINE_AUTOHINTER_INTERFACE(
 548     af_autofitter_interface,
 549 
 550     NULL,                                                    /* reset_face */
 551     NULL,                                              /* get_global_hints */
 552     NULL,                                             /* done_global_hints */
 553     (FT_AutoHinter_GlyphLoadFunc)af_autofitter_load_glyph )  /* load_glyph */
 554 
 555 
 556   FT_DEFINE_MODULE(
 557     autofit_module_class,
 558 
 559     FT_MODULE_HINTER,
 560     sizeof ( AF_ModuleRec ),
 561 
 562     "autofitter",
 563     0x10000L,   /* version 1.0 of the autofitter  */
 564     0x20000L,   /* requires FreeType 2.0 or above */
 565 
 566     (const void*)&af_autofitter_interface,
 567 
 568     (FT_Module_Constructor)af_autofitter_init,  /* module_init   */
 569     (FT_Module_Destructor) af_autofitter_done,  /* module_done   */
 570     (FT_Module_Requester)  af_get_interface     /* get_interface */
 571   )
 572 
 573 
 574 /* END */