text:
# FreeType 2 src/truetype Jamfile
#
# Copyright (C) 2001-2020 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
# and distributed under the terms of the FreeType project license,
# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
# indicate that you have read the license and understand and accept it
# fully.

SubDir  FT2_TOP $(FT2_SRC_DIR) truetype ;

{
  local  _sources ;

  if $(FT2_MULTI)
  {
    _sources = ttdriver
               ttgload
               ttgxvar
               ttinterp
               ttobjs
               ttpic
               ttpload
               ttsubpix
               ;
  }
  else
  {
    _sources = truetype ;
  }

  Library  $(FT2_LIB) : $(_sources).c ;
}

# end of src/truetype Jamfile

repo_language: C

mime_type: text/x-c


text:
#
# FreeType 2 TrueType module definition
#

# Copyright (C) 1996-2020 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
# and distributed under the terms of the FreeType project license,
# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
# indicate that you have read the license and understand and accept it
# fully.

FTMODULE_H_COMMANDS += TRUETYPE_DRIVER

define TRUETYPE_DRIVER
$(OPEN_DRIVER) FT_Driver_ClassRec, tt_driver_class $(CLOSE_DRIVER)
$(ECHO_DRIVER)truetype  $(ECHO_DRIVER_DESC)Windows/Mac font files with extension *.ttf or *.ttc$(ECHO_DRIVER_DONE)
endef

# EOF


text:
/****************************************************************************
 *
 * ttinterp.h
 *
 *   TrueType bytecode interpreter (specification).
 *
 * Copyright (C) 1996-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#ifndef TTINTERP_H_
#define TTINTERP_H_

#include <ft2build.h>
#include "ttobjs.h"

FT_BEGIN_HEADER

  /**************************************************************************
   *
   * Rounding mode constants.
   */
#define TT_Round_Off             5
#define TT_Round_To_Half_Grid    0
#define TT_Round_To_Grid         1
#define TT_Round_To_Double_Grid  2
#define TT_Round_Up_To_Grid      4
#define TT_Round_Down_To_Grid    3
#define TT_Round_Super           6
#define TT_Round_Super_45        7

  /**************************************************************************
   *
   * Function types used by the interpreter, depending on various modes
   * (e.g. the rounding mode, whether to render a vertical or horizontal
   * line etc).
   *
   */

  /* Rounding function */
  typedef FT_F26Dot6
  (*TT_Round_Func)( TT_ExecContext  exc,
                    FT_F26Dot6      distance,
                    FT_F26Dot6      compensation );

  /* Point displacement along the freedom vector routine */
  typedef void
  (*TT_Move_Func)( TT_ExecContext  exc,
                   TT_GlyphZone    zone,
                   FT_UShort       point,
                   FT_F26Dot6      distance );

  /* Distance projection along one of the projection vectors */
  typedef FT_F26Dot6
  (*TT_Project_Func)( TT_ExecContext  exc,
                      FT_Pos          dx,
                      FT_Pos          dy );

  /* getting current ppem.  Take care of non-square pixels if necessary */
  typedef FT_Long
  (*TT_Cur_Ppem_Func)( TT_ExecContext  exc );

  /* reading a cvt value.  Take care of non-square pixels if necessary */
  typedef FT_F26Dot6
  (*TT_Get_CVT_Func)( TT_ExecContext  exc,
                      FT_ULong        idx );

  /* setting or moving a cvt value.  Take care of non-square pixels  */
  /* if necessary                                                    */
  typedef void
  (*TT_Set_CVT_Func)( TT_ExecContext  exc,
                      FT_ULong        idx,
                      FT_F26Dot6      value );

  /**************************************************************************
   *
   * This structure defines a call record, used to manage function calls.
   */
  typedef struct  TT_CallRec_
  {
    FT_Int   Caller_Range;
    FT_Long  Caller_IP;
    FT_Long  Cur_Count;

    TT_DefRecord  *Def; /* either FDEF or IDEF */

  } TT_CallRec, *TT_CallStack;

#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY

  /**************************************************************************
   *
   * These structures define rules used to tweak subpixel hinting for
   * various fonts.  "", 0, "", NULL value indicates to match any value.
   */

#define SPH_MAX_NAME_SIZE      32
#define SPH_MAX_CLASS_MEMBERS  100

  typedef struct  SPH_TweakRule_
  {
    const char      family[SPH_MAX_NAME_SIZE];
    const FT_UInt   ppem;
    const char      style[SPH_MAX_NAME_SIZE];
    const FT_ULong  glyph;

  } SPH_TweakRule;

  typedef struct  SPH_ScaleRule_
  {
    const char      family[SPH_MAX_NAME_SIZE];
    const FT_UInt   ppem;
    const char      style[SPH_MAX_NAME_SIZE];
    const FT_ULong  glyph;
    const FT_ULong  scale;

  } SPH_ScaleRule;

  typedef struct  SPH_Font_Class_
  {
    const char  name[SPH_MAX_NAME_SIZE];
    const char  member[SPH_MAX_CLASS_MEMBERS][SPH_MAX_NAME_SIZE];

  } SPH_Font_Class;

#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */

  /**************************************************************************
   *
   * The main structure for the interpreter which collects all necessary
   * variables and states.
   */
  typedef struct  TT_ExecContextRec_
  {
    TT_Face            face;
    TT_Size            size;
    FT_Memory          memory;

    /* instructions state */

    FT_Error           error;      /* last execution error */

    FT_Long            top;        /* top of exec. stack   */

    FT_Long            stackSize;  /* size of exec. stack  */
    FT_Long*           stack;      /* current exec. stack  */

    FT_Long            args;
    FT_Long            new_top;    /* new top after exec.  */

    TT_GlyphZoneRec    zp0,        /* zone records */
                       zp1,
                       zp2,
                       pts,
                       twilight;

    FT_Long            pointSize;  /* in 26.6 format */
    FT_Size_Metrics    metrics;
    TT_Size_Metrics    tt_metrics; /* size metrics */

    TT_GraphicsState   GS;         /* current graphics state */

    FT_Int             curRange;  /* current code range number   */
    FT_Byte*           code;      /* current code range          */
    FT_Long            IP;        /* current instruction pointer */
    FT_Long            codeSize;  /* size of current range       */

    FT_Byte            opcode;    /* current opcode              */
    FT_Int             length;    /* length of current opcode    */

    FT_Bool            step_ins;  /* true if the interpreter must */
                                  /* increment IP after ins. exec */
    FT_ULong           cvtSize;
    FT_Long*           cvt;

    FT_UInt            glyphSize; /* glyph instructions buffer size */
    FT_Byte*           glyphIns;  /* glyph instructions buffer */

    FT_UInt            numFDefs;  /* number of function defs         */
    FT_UInt            maxFDefs;  /* maximum number of function defs */
    TT_DefArray        FDefs;     /* table of FDefs entries          */

    FT_UInt            numIDefs;  /* number of instruction defs */
    FT_UInt            maxIDefs;  /* maximum number of ins defs */
    TT_DefArray        IDefs;     /* table of IDefs entries     */

    FT_UInt            maxFunc;   /* maximum function index     */
    FT_UInt            maxIns;    /* maximum instruction index  */

    FT_Int             callTop,    /* top of call stack during execution */
                       callSize;   /* size of call stack */
    TT_CallStack       callStack;  /* call stack */

    FT_UShort          maxPoints;    /* capacity of this context's `pts' */
    FT_Short           maxContours;  /* record, expressed in points and  */
                                     /* contours.                        */

    TT_CodeRangeTable  codeRangeTable;  /* table of valid code ranges */
                                        /* useful for the debugger   */

    FT_UShort          storeSize;  /* size of current storage */
    FT_Long*           storage;    /* storage area            */

    FT_F26Dot6         period;     /* values used for the */
    FT_F26Dot6         phase;      /* `SuperRounding'     */
    FT_F26Dot6         threshold;

    FT_Bool            instruction_trap; /* If `True', the interpreter will */
                                         /* exit after each instruction     */

    TT_GraphicsState   default_GS;       /* graphics state resulting from   */
                                         /* the prep program                */
    FT_Bool            is_composite;     /* true if the glyph is composite  */
    FT_Bool            pedantic_hinting; /* true if pedantic interpretation */

    /* latest interpreter additions */

    FT_Long            F_dot_P;    /* dot product of freedom and projection */
                                   /* vectors                               */
    TT_Round_Func      func_round; /* current rounding function             */

    TT_Project_Func    func_project,   /* current projection function */
                       func_dualproj,  /* current dual proj. function */
                       func_freeProj;  /* current freedom proj. func  */

    TT_Move_Func       func_move;      /* current point move function */
    TT_Move_Func       func_move_orig; /* move original position function */

    TT_Cur_Ppem_Func   func_cur_ppem;  /* get current proj. ppem value  */

    TT_Get_CVT_Func    func_read_cvt;  /* read a cvt entry              */
    TT_Set_CVT_Func    func_write_cvt; /* write a cvt entry (in pixels) */
    TT_Set_CVT_Func    func_move_cvt;  /* incr a cvt entry (in pixels)  */

    FT_Bool            grayscale;      /* bi-level hinting and */
                                       /* grayscale rendering  */

#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
    /*
     * FreeType supports ClearType-like hinting of TrueType fonts through
     * the version 40 interpreter.  This is achieved through several hacks
     * in the base (v35) interpreter, as detailed below.
     *
     * ClearType is an umbrella term for several rendering techniques
     * employed by Microsoft's various GUI and rendering toolkit
     * implementations, most importantly: subpixel rendering for using the
     * RGB subpixels of LCDs to approximately triple the perceived
     * resolution on the x-axis and subpixel hinting for positioning stems
     * on subpixel borders.  TrueType programming is explicit, i.e., fonts
     * must be programmed to take advantage of ClearType's possibilities.
     *
     * When ClearType was introduced, it seemed unlikely that all fonts
     * would be reprogrammed, so Microsoft decided to implement a backward
     * compatibility mode.  It employs several simple to complicated
     * assumptions and tricks, many of them font-dependent, that modify the
     * interpretation of the bytecode contained in these fonts to retrofit
     * them into a ClearType-y look.  The quality of the results varies.
     * Most (web)fonts that were released since then have come to rely on
     * these hacks to render correctly, even some of Microsoft's flagship
     * fonts (e.g., Calibri, Cambria, Segoe UI).
     *
     * FreeType's minimal subpixel hinting code (interpreter version 40)
     * employs a small list of font-agnostic hacks loosely based on the
     * public information available on Microsoft's compatibility mode[2].
     * The focus is on modern (web)fonts rather than legacy fonts that were
     * made for monochrome rendering.  It will not match ClearType rendering
     * exactly.  Unlike the `Infinality' code (interpreter version 38) that
     * came before, it will not try to toggle hacks for specific fonts for
     * performance and complexity reasons.  It will fall back to version 35
     * behavior for tricky fonts[1] or when monochrome rendering is
     * requested.
     *
     * Major hacks
     *
     * - Any point movement on the x axis is ignored (cf. `Direct_Move' and
     *   `Direct_Move_X').  This has the smallest code footprint and single
     *   biggest effect.  The ClearType way to increase resolution is
     *   supersampling the x axis, the FreeType way is ignoring instructions
     *   on the x axis, which gives the same result in the majority of
     *   cases.
     *
     * - Points are not moved post-IUP (neither on the x nor on the y axis),
     *   except the x component of diagonal moves post-IUP (cf.
     *   `Direct_Move', `Direct_Move_Y', `Move_Zp2_Point').  Post-IUP
     *   changes are commonly used to `fix' pixel patterns which has little
     *   use outside monochrome rendering.
     *
     * - SHPIX and DELTAP don't execute unless moving a composite on the
     *   y axis or moving a previously y touched point.  SHPIX additionally
     *   denies movement on the x axis (cf. `Ins_SHPIX' and `Ins_DELTAP').
     *   Both instructions are commonly used to `fix' pixel patterns for
     *   monochrome or Windows's GDI rendering but make little sense for
     *   FreeType rendering.  Both can distort the outline.  See [2] for
     *   details.
     *
     * - The hdmx table and modifications to phantom points are ignored.
     *   Bearings and advance widths remain unchanged (except rounding them
     *   outside the interpreter!), cf. `compute_glyph_metrics' and
     *   `TT_Hint_Glyph'.  Letting non-native-ClearType fonts modify spacing
     *   might mess up spacing.
     *
     * Minor hacks
     *
     * - FLIPRGON, FLIPRGOFF, and FLIPPT don't execute post-IUP.  This
     *   prevents dents in e.g. Arial-Regular's `D' and `G' glyphs at
     *   various sizes.
     *
     * (Post-IUP is the state after both IUP[x] and IUP[y] have been
     * executed.)
     *
     * The best results are achieved for fonts that were from the outset
     * designed with ClearType in mind, meaning they leave the x axis mostly
     * alone and don't mess with the `final' outline to produce more
     * pleasing pixel patterns.  The harder the designer tried to produce
     * very specific patterns (`superhinting') for pre-ClearType-displays,
     * the worse the results.
     *
     * Microsoft defines a way to turn off backward compatibility and
     * interpret instructions as before (called `native ClearType')[2][3].
     * The font designer then regains full control and is responsible for
     * making the font work correctly with ClearType without any
     * hand-holding by the interpreter or rasterizer[4].  The v40
     * interpreter assumes backward compatibility by default, which can be
     * turned off the same way by executing the following in the control
     * program (cf. `Ins_INSTCTRL').
     *
     *   #PUSH 4,3
     *   INSTCTRL[]
     *
     * [1] Tricky fonts as FreeType defines them rely on the bytecode
     *     interpreter to display correctly.  Hacks can interfere with them,
     *     so they get treated like native ClearType fonts (v40 with
     *     backward compatibility turned off).  Cf. `TT_RunIns'.
     *
     * [2] Proposed by Microsoft's Greg Hitchcock in
     *     https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx
     *
     * [3] Beat Stamm describes it in more detail:
     *     http://rastertragedy.com/RTRCh4.htm#Sec12.
     *
     * [4] The list of `native ClearType' fonts is small at the time of this
     *     writing; I found the following on a Windows 10 Update 1511
     *     installation: Constantia, Corbel, Sitka, Malgun Gothic, Microsoft
     *     JhengHei (Bold and UI Bold), Microsoft YaHei (Bold and UI Bold),
     *     SimSun, NSimSun, and Yu Gothic.
     *
     */

    /* Using v40 implies subpixel hinting, unless FT_RENDER_MODE_MONO has been
     * requested.  Used to detect interpreter */
    /* version switches.  `_lean' to differentiate from the Infinality */
    /* `subpixel_hinting', which is managed differently.               */
    FT_Bool            subpixel_hinting_lean;

    /* Long side of a LCD subpixel is vertical (e.g., screen is rotated). */
    /* `_lean' to differentiate from the Infinality `vertical_lcd', which */
    /* is managed differently.                                            */
    FT_Bool            vertical_lcd_lean;

    /* Default to backward compatibility mode in v40 interpreter.  If   */
    /* this is false, it implies the interpreter is in v35 or in native */
    /* ClearType mode.                                                  */
    FT_Bool            backward_compatibility;

    /* Useful for detecting and denying post-IUP trickery that is usually */
    /* used to fix pixel patterns (`superhinting').                       */
    FT_Bool            iupx_called;
    FT_Bool            iupy_called;

    /* ClearType hinting and grayscale rendering, as used by Universal */
    /* Windows Platform apps (Windows 8 and above).  Like the standard */
    /* colorful ClearType mode, it utilizes a vastly increased virtual */
    /* resolution on the x axis.  Different from bi-level hinting and  */
    /* grayscale rendering, the old mode from Win9x days that roughly  */
    /* adheres to the physical pixel grid on both axes.                */
    FT_Bool            grayscale_cleartype;
#endif /* TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL */

#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
    TT_Round_Func      func_round_sphn;   /* subpixel rounding function */

    FT_Bool            subpixel_hinting;  /* Using subpixel hinting?       */
    FT_Bool            ignore_x_mode;     /* Standard rendering mode for   */
                                          /* subpixel hinting.  On if gray */
                                          /* or subpixel hinting is on.    */

    /* The following 6 aren't fully implemented but here for MS rasterizer */
    /* compatibility.                                                      */
    FT_Bool            compatible_widths;     /* compatible widths?        */
    FT_Bool            symmetrical_smoothing; /* symmetrical_smoothing?    */
    FT_Bool            bgr;                   /* bgr instead of rgb?       */
    FT_Bool            vertical_lcd;          /* long side of LCD subpixel */
                                              /* rectangles is horizontal  */
    FT_Bool            subpixel_positioned;   /* subpixel positioned       */
                                              /* (DirectWrite ClearType)?  */
    FT_Bool            gray_cleartype;        /* ClearType hinting but     */
                                              /* grayscale rendering       */

    FT_Int             rasterizer_version;    /* MS rasterizer version     */

    FT_Bool            iup_called;            /* IUP called for glyph?     */

    FT_ULong           sph_tweak_flags;       /* flags to control          */
                                              /* hint tweaks               */

    FT_ULong           sph_in_func_flags;     /* flags to indicate if in   */
                                              /* special functions         */

#endif /* TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY */

    /* We maintain two counters (in addition to the instruction counter) */
    /* that act as loop detectors for LOOPCALL and jump opcodes with     */
    /* negative arguments.                                               */
    FT_ULong           loopcall_counter;
    FT_ULong           loopcall_counter_max;
    FT_ULong           neg_jump_counter;
    FT_ULong           neg_jump_counter_max;

  } TT_ExecContextRec;

  extern const TT_GraphicsState  tt_default_graphics_state;

#ifdef TT_USE_BYTECODE_INTERPRETER
  FT_LOCAL( void )
  TT_Goto_CodeRange( TT_ExecContext  exec,
                     FT_Int          range,
                     FT_Long         IP );

  FT_LOCAL( void )
  TT_Set_CodeRange( TT_ExecContext  exec,
                    FT_Int          range,
                    void*           base,
                    FT_Long         length );

  FT_LOCAL( void )
  TT_Clear_CodeRange( TT_ExecContext  exec,
                      FT_Int          range );

  FT_LOCAL( FT_Error )
  Update_Max( FT_Memory  memory,
              FT_ULong*  size,
              FT_ULong   multiplier,
              void*      _pbuff,
              FT_ULong   new_max );
#endif /* TT_USE_BYTECODE_INTERPRETER */

  /**************************************************************************
   *
   * @Function:
   *   TT_New_Context
   *
   * @Description:
   *   Queries the face context for a given font.  Note that there is
   *   now a _single_ execution context in the TrueType driver which is
   *   shared among faces.
   *
   * @Input:
   *   face ::
   *     A handle to the source face object.
   *
   * @Return:
   *   A handle to the execution context.  Initialized for `face'.
   *
   * @Note:
   *   Only the glyph loader and debugger should call this function.
   *   (And right now only the glyph loader uses it.)
   */
  FT_EXPORT( TT_ExecContext )
  TT_New_Context( TT_Driver  driver );

#ifdef TT_USE_BYTECODE_INTERPRETER
  FT_LOCAL( void )
  TT_Done_Context( TT_ExecContext  exec );

  FT_LOCAL( FT_Error )
  TT_Load_Context( TT_ExecContext  exec,
                   TT_Face         face,
                   TT_Size         size );

  FT_LOCAL( void )
  TT_Save_Context( TT_ExecContext  exec,
                   TT_Size         ins );

  FT_LOCAL( FT_Error )
  TT_Run_Context( TT_ExecContext  exec );
#endif /* TT_USE_BYTECODE_INTERPRETER */

  /**************************************************************************
   *
   * @Function:
   *   TT_RunIns
   *
   * @Description:
   *   Executes one or more instruction in the execution context.  This
   *   is the main function of the TrueType opcode interpreter.
   *
   * @Input:
   *   exec ::
   *     A handle to the target execution context.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   *
   * @Note:
   *   Only the object manager and debugger should call this function.
   *
   *   This function is publicly exported because it is directly
   *   invoked by the TrueType debugger.
   */
  FT_EXPORT( FT_Error )
  TT_RunIns( TT_ExecContext  exec );

FT_END_HEADER

#endif /* TTINTERP_H_ */

/* END */


text:
/****************************************************************************
 *
 * tterrors.h
 *
 *   TrueType error codes (specification only).
 *
 * Copyright (C) 2001-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

  /**************************************************************************
   *
   * This file is used to define the TrueType error enumeration
   * constants.
   *
   */

#ifndef TTERRORS_H_
#define TTERRORS_H_

#include FT_MODULE_ERRORS_H

#undef FTERRORS_H_

#undef  FT_ERR_PREFIX
#define FT_ERR_PREFIX  TT_Err_
#define FT_ERR_BASE    FT_Mod_Err_TrueType

#include FT_ERRORS_H

#endif /* TTERRORS_H_ */

/* END */


text:
/****************************************************************************
 *
 * ttdriver.c
 *
 *   TrueType font driver implementation (body).
 *
 * Copyright (C) 1996-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#include <ft2build.h>
#include FT_INTERNAL_DEBUG_H
#include FT_INTERNAL_STREAM_H
#include FT_INTERNAL_SFNT_H
#include FT_SERVICE_FONT_FORMAT_H

#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
#include FT_MULTIPLE_MASTERS_H
#include FT_SERVICE_MULTIPLE_MASTERS_H
#include FT_SERVICE_METRICS_VARIATIONS_H
#endif

#include FT_SERVICE_TRUETYPE_ENGINE_H
#include FT_SERVICE_TRUETYPE_GLYF_H
#include FT_SERVICE_PROPERTIES_H
#include FT_DRIVER_H

#include "ttdriver.h"
#include "ttgload.h"
#include "ttpload.h"

#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
#include "ttgxvar.h"
#endif

#include "tterrors.h"

  /**************************************************************************
   *
   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
   * messages during execution.
   */
#undef  FT_COMPONENT
#define FT_COMPONENT  ttdriver

  /*
   * PROPERTY SERVICE
   *
   */
  static FT_Error
  tt_property_set( FT_Module    module,         /* TT_Driver */
                   const char*  property_name,
                   const void*  value,
                   FT_Bool      value_is_string )
  {
    FT_Error   error  = FT_Err_Ok;
    TT_Driver  driver = (TT_Driver)module;

#ifndef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
    FT_UNUSED( value_is_string );
#endif

    if ( !ft_strcmp( property_name, "interpreter-version" ) )
    {
      FT_UInt  interpreter_version;

#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
      if ( value_is_string )
      {
        const char*  s = (const char*)value;

        interpreter_version = (FT_UInt)ft_strtol( s, NULL, 10 );
      }
      else
#endif
      {
        FT_UInt*  iv = (FT_UInt*)value;

        interpreter_version = *iv;
      }

      if ( interpreter_version == TT_INTERPRETER_VERSION_35
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
           || interpreter_version == TT_INTERPRETER_VERSION_38
#endif
#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
           || interpreter_version == TT_INTERPRETER_VERSION_40
#endif
         )
        driver->interpreter_version = interpreter_version;
      else
        error = FT_ERR( Unimplemented_Feature );

      return error;
    }

    FT_TRACE0(( "tt_property_set: missing property `%s'\n",
                property_name ));
    return FT_THROW( Missing_Property );
  }

  static FT_Error
  tt_property_get( FT_Module    module,         /* TT_Driver */
                   const char*  property_name,
                   const void*  value )
  {
    FT_Error   error  = FT_Err_Ok;
    TT_Driver  driver = (TT_Driver)module;

    FT_UInt  interpreter_version = driver->interpreter_version;

    if ( !ft_strcmp( property_name, "interpreter-version" ) )
    {
      FT_UInt*  val = (FT_UInt*)value;

      *val = interpreter_version;

      return error;
    }

    FT_TRACE0(( "tt_property_get: missing property `%s'\n",
                property_name ));
    return FT_THROW( Missing_Property );
  }

  FT_DEFINE_SERVICE_PROPERTIESREC(
    tt_service_properties,

    (FT_Properties_SetFunc)tt_property_set,     /* set_property */
    (FT_Properties_GetFunc)tt_property_get      /* get_property */
  )

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****                          F A C E S                              ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

  /**************************************************************************
   *
   * @Function:
   *   tt_get_kerning
   *
   * @Description:
   *   A driver method used to return the kerning vector between two
   *   glyphs of the same face.
   *
   * @Input:
   *   face ::
   *     A handle to the source face object.
   *
   *   left_glyph ::
   *     The index of the left glyph in the kern pair.
   *
   *   right_glyph ::
   *     The index of the right glyph in the kern pair.
   *
   * @Output:
   *   kerning ::
   *     The kerning vector.  This is in font units for
   *     scalable formats, and in pixels for fixed-sizes
   *     formats.
   *
   * @Return:
   *   FreeType error code.  0 means success.
   *
   * @Note:
   *   Only horizontal layouts (left-to-right & right-to-left) are
   *   supported by this function.  Other layouts, or more sophisticated
   *   kernings, are out of scope of this method (the basic driver
   *   interface is meant to be simple).
   *
   *   They can be implemented by format-specific interfaces.
   */
  static FT_Error
  tt_get_kerning( FT_Face     ttface,          /* TT_Face */
                  FT_UInt     left_glyph,
                  FT_UInt     right_glyph,
                  FT_Vector*  kerning )
  {
    TT_Face       face = (TT_Face)ttface;
    SFNT_Service  sfnt = (SFNT_Service)face->sfnt;

    kerning->x = 0;
    kerning->y = 0;

    if ( sfnt )
      kerning->x = sfnt->get_kerning( face, left_glyph, right_glyph );

    return 0;
  }

  static FT_Error
  tt_get_advances( FT_Face    ttface,
                   FT_UInt    start,
                   FT_UInt    count,
                   FT_Int32   flags,
                   FT_Fixed  *advances )
  {
    FT_UInt  nn;
    TT_Face  face = (TT_Face)ttface;

    /* XXX: TODO: check for sbits */

    if ( flags & FT_LOAD_VERTICAL_LAYOUT )
    {
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
      /* no fast retrieval for blended MM fonts without VVAR table */
      if ( ( FT_IS_NAMED_INSTANCE( ttface ) || FT_IS_VARIATION( ttface ) ) &&
           !( face->variation_support & TT_FACE_FLAG_VAR_VADVANCE )        )
        return FT_THROW( Unimplemented_Feature );
#endif

      for ( nn = 0; nn < count; nn++ )
      {
        FT_Short   tsb;
        FT_UShort  ah;

        /* since we don't need `tsb', we use zero for `yMax' parameter */
        TT_Get_VMetrics( face, start + nn, 0, &tsb, &ah );
        advances[nn] = ah;
      }
    }
    else
    {
#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
      /* no fast retrieval for blended MM fonts without HVAR table */
      if ( ( FT_IS_NAMED_INSTANCE( ttface ) || FT_IS_VARIATION( ttface ) ) &&
           !( face->variation_support & TT_FACE_FLAG_VAR_HADVANCE )        )
        return FT_THROW( Unimplemented_Feature );
#endif

      for ( nn = 0; nn < count; nn++ )
      {
        FT_Short   lsb;
        FT_UShort  aw;

        TT_Get_HMetrics( face, start + nn, &lsb, &aw );
        advances[nn] = aw;
      }
    }

    return FT_Err_Ok;
  }

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****                           S I Z E S                             ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS

  static FT_Error
  tt_size_select( FT_Size   size,
                  FT_ULong  strike_index )
  {
    TT_Face   ttface = (TT_Face)size->face;
    TT_Size   ttsize = (TT_Size)size;
    FT_Error  error  = FT_Err_Ok;

    ttsize->strike_index = strike_index;

    if ( FT_IS_SCALABLE( size->face ) )
    {
      /* use the scaled metrics, even when tt_size_reset fails */
      FT_Select_Metrics( size->face, strike_index );

      tt_size_reset( ttsize, 0 ); /* ignore return value */
    }
    else
    {
      SFNT_Service      sfnt         = (SFNT_Service)ttface->sfnt;
      FT_Size_Metrics*  size_metrics = &size->metrics;

      error = sfnt->load_strike_metrics( ttface,
                                         strike_index,
                                         size_metrics );
      if ( error )
        ttsize->strike_index = 0xFFFFFFFFUL;
    }

    return error;
  }

#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */

  static FT_Error
  tt_size_request( FT_Size          size,
                   FT_Size_Request  req )
  {
    TT_Size   ttsize = (TT_Size)size;
    FT_Error  error  = FT_Err_Ok;

#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS

    if ( FT_HAS_FIXED_SIZES( size->face ) )
    {
      TT_Face       ttface = (TT_Face)size->face;
      SFNT_Service  sfnt   = (SFNT_Service)ttface->sfnt;
      FT_ULong      strike_index;

      error = sfnt->set_sbit_strike( ttface, req, &strike_index );

      if ( error )
        ttsize->strike_index = 0xFFFFFFFFUL;
      else
        return tt_size_select( size, strike_index );
    }

#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */

    FT_Request_Metrics( size->face, req );

    if ( FT_IS_SCALABLE( size->face ) )
    {
      error = tt_size_reset( ttsize, 0 );

#ifdef TT_USE_BYTECODE_INTERPRETER
      /* for the `MPS' bytecode instruction we need the point size */
      if ( !error )
      {
        FT_UInt  resolution =
                   ttsize->metrics->x_ppem > ttsize->metrics->y_ppem
                     ? req->horiResolution
                     : req->vertResolution;

        /* if we don't have a resolution value, assume 72dpi */
        if ( req->type == FT_SIZE_REQUEST_TYPE_SCALES ||
             !resolution                              )
          resolution = 72;

        ttsize->point_size = FT_MulDiv( ttsize->ttmetrics.ppem,
                                        64 * 72,
                                        resolution );
      }
#endif
    }

    return error;
  }

  /**************************************************************************
   *
   * @Function:
   *   tt_glyph_load
   *
   * @Description:
   *   A driver method used to load a glyph within a given glyph slot.
   *
   * @Input:
   *   slot ::
   *     A handle to the target slot object where the glyph
   *     will be loaded.
   *
   *   size ::
   *     A handle to the source face size at which the glyph
   *     must be scaled, loaded, etc.
   *
   *   glyph_index ::
   *     The index of the glyph in the font file.
   *
   *   load_flags ::
   *     A flag indicating what to load for this glyph.  The
   *     FT_LOAD_XXX constants can be used to control the
   *     glyph loading process (e.g., whether the outline
   *     should be scaled, whether to load bitmaps or not,
   *     whether to hint the outline, etc).
   *
   * @Return:
   *   FreeType error code.  0 means success.
   */
  static FT_Error
  tt_glyph_load( FT_GlyphSlot  ttslot,      /* TT_GlyphSlot */
                 FT_Size       ttsize,      /* TT_Size      */
                 FT_UInt       glyph_index,
                 FT_Int32      load_flags )
  {
    TT_GlyphSlot  slot = (TT_GlyphSlot)ttslot;
    TT_Size       size = (TT_Size)ttsize;
    FT_Face       face = ttslot->face;
    FT_Error      error;

    if ( !slot )
      return FT_THROW( Invalid_Slot_Handle );

    if ( !size )
      return FT_THROW( Invalid_Size_Handle );

    if ( !face )
      return FT_THROW( Invalid_Face_Handle );

#ifdef FT_CONFIG_OPTION_INCREMENTAL
    if ( glyph_index >= (FT_UInt)face->num_glyphs &&
         !face->internal->incremental_interface   )
#else
    if ( glyph_index >= (FT_UInt)face->num_glyphs )
#endif
      return FT_THROW( Invalid_Argument );

    if ( load_flags & FT_LOAD_NO_HINTING )
    {
      /* both FT_LOAD_NO_HINTING and FT_LOAD_NO_AUTOHINT   */
      /* are necessary to disable hinting for tricky fonts */

      if ( FT_IS_TRICKY( face ) )
        load_flags &= ~FT_LOAD_NO_HINTING;

      if ( load_flags & FT_LOAD_NO_AUTOHINT )
        load_flags |= FT_LOAD_NO_HINTING;
    }

    if ( load_flags & ( FT_LOAD_NO_RECURSE | FT_LOAD_NO_SCALE ) )
    {
      load_flags |= FT_LOAD_NO_BITMAP | FT_LOAD_NO_SCALE;

      if ( !FT_IS_TRICKY( face ) )
        load_flags |= FT_LOAD_NO_HINTING;
    }

    /* use hinted metrics only if we load a glyph with hinting */
    size->metrics = ( load_flags & FT_LOAD_NO_HINTING )
                      ? &ttsize->metrics
                      : &size->hinted_metrics;

    /* now fill in the glyph slot with outline/bitmap/layered */
    error = TT_Load_Glyph( size, slot, glyph_index, load_flags );

    /* force drop-out mode to 2 - irrelevant now */
    /* slot->outline.dropout_mode = 2; */

    return error;
  }

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /****                                                                 ****/
  /****                                                                 ****/
  /****                D R I V E R  I N T E R F A C E                   ****/
  /****                                                                 ****/
  /****                                                                 ****/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT

  FT_DEFINE_SERVICE_MULTIMASTERSREC(
    tt_service_gx_multi_masters,

    (FT_Get_MM_Func)             NULL,                  /* get_mm              */
    (FT_Set_MM_Design_Func)      NULL,                  /* set_mm_design       */
    (FT_Set_MM_Blend_Func)       TT_Set_MM_Blend,       /* set_mm_blend        */
    (FT_Get_MM_Blend_Func)       TT_Get_MM_Blend,       /* get_mm_blend        */
    (FT_Get_MM_Var_Func)         TT_Get_MM_Var,         /* get_mm_var          */
    (FT_Set_Var_Design_Func)     TT_Set_Var_Design,     /* set_var_design      */
    (FT_Get_Var_Design_Func)     TT_Get_Var_Design,     /* get_var_design      */
    (FT_Set_Instance_Func)       TT_Set_Named_Instance, /* set_instance        */
    (FT_Set_MM_WeightVector_Func)NULL,                  /* set_mm_weightvector */
    (FT_Get_MM_WeightVector_Func)NULL,                  /* get_mm_weightvector */

    (FT_Get_Var_Blend_Func)      tt_get_var_blend,      /* get_var_blend       */
    (FT_Done_Blend_Func)         tt_done_blend          /* done_blend          */
  )

  FT_DEFINE_SERVICE_METRICSVARIATIONSREC(
    tt_service_metrics_variations,

    (FT_HAdvance_Adjust_Func)tt_hadvance_adjust,     /* hadvance_adjust */
    (FT_LSB_Adjust_Func)     NULL,                   /* lsb_adjust      */
    (FT_RSB_Adjust_Func)     NULL,                   /* rsb_adjust      */

    (FT_VAdvance_Adjust_Func)tt_vadvance_adjust,     /* vadvance_adjust */
    (FT_TSB_Adjust_Func)     NULL,                   /* tsb_adjust      */
    (FT_BSB_Adjust_Func)     NULL,                   /* bsb_adjust      */
    (FT_VOrg_Adjust_Func)    NULL,                   /* vorg_adjust     */

    (FT_Metrics_Adjust_Func) tt_apply_mvar           /* metrics_adjust  */
  )

#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */

  static const FT_Service_TrueTypeEngineRec  tt_service_truetype_engine =
  {
#ifdef TT_USE_BYTECODE_INTERPRETER

    FT_TRUETYPE_ENGINE_TYPE_PATENTED

#else /* !TT_USE_BYTECODE_INTERPRETER */

    FT_TRUETYPE_ENGINE_TYPE_NONE

#endif /* TT_USE_BYTECODE_INTERPRETER */
  };

  FT_DEFINE_SERVICE_TTGLYFREC(
    tt_service_truetype_glyf,

    (TT_Glyf_GetLocationFunc)tt_face_get_location      /* get_location */
  )

#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
  FT_DEFINE_SERVICEDESCREC6(
    tt_services,

    FT_SERVICE_ID_FONT_FORMAT,        FT_FONT_FORMAT_TRUETYPE,
    FT_SERVICE_ID_MULTI_MASTERS,      &tt_service_gx_multi_masters,
    FT_SERVICE_ID_METRICS_VARIATIONS, &tt_service_metrics_variations,
    FT_SERVICE_ID_TRUETYPE_ENGINE,    &tt_service_truetype_engine,
    FT_SERVICE_ID_TT_GLYF,            &tt_service_truetype_glyf,
    FT_SERVICE_ID_PROPERTIES,         &tt_service_properties )
#else
  FT_DEFINE_SERVICEDESCREC4(
    tt_services,

    FT_SERVICE_ID_FONT_FORMAT,     FT_FONT_FORMAT_TRUETYPE,
    FT_SERVICE_ID_TRUETYPE_ENGINE, &tt_service_truetype_engine,
    FT_SERVICE_ID_TT_GLYF,         &tt_service_truetype_glyf,
    FT_SERVICE_ID_PROPERTIES,      &tt_service_properties )
#endif

  FT_CALLBACK_DEF( FT_Module_Interface )
  tt_get_interface( FT_Module    driver,    /* TT_Driver */
                    const char*  tt_interface )
  {
    FT_Library           library;
    FT_Module_Interface  result;
    FT_Module            sfntd;
    SFNT_Service         sfnt;

    result = ft_service_list_lookup( tt_services, tt_interface );
    if ( result )
      return result;

    if ( !driver )
      return NULL;
    library = driver->library;
    if ( !library )
      return NULL;

    /* only return the default interface from the SFNT module */
    sfntd = FT_Get_Module( library, "sfnt" );
    if ( sfntd )
    {
      sfnt = (SFNT_Service)( sfntd->clazz->module_interface );
      if ( sfnt )
        return sfnt->get_interface( driver, tt_interface );
    }

    return 0;
  }

  /* The FT_DriverInterface structure is defined in ftdriver.h. */

#ifdef TT_USE_BYTECODE_INTERPRETER
#define TT_HINTER_FLAG  FT_MODULE_DRIVER_HAS_HINTER
#else
#define TT_HINTER_FLAG  0
#endif

#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
#define TT_SIZE_SELECT  tt_size_select
#else
#define TT_SIZE_SELECT  0
#endif

  FT_DEFINE_DRIVER(
    tt_driver_class,

      FT_MODULE_FONT_DRIVER     |
      FT_MODULE_DRIVER_SCALABLE |
      TT_HINTER_FLAG,

      sizeof ( TT_DriverRec ),

      "truetype",      /* driver name                           */
      0x10000L,        /* driver version == 1.0                 */
      0x20000L,        /* driver requires FreeType 2.0 or above */

      NULL,    /* module-specific interface */

      tt_driver_init,           /* FT_Module_Constructor  module_init   */
      tt_driver_done,           /* FT_Module_Destructor   module_done   */
      tt_get_interface,         /* FT_Module_Requester    get_interface */

    sizeof ( TT_FaceRec ),
    sizeof ( TT_SizeRec ),
    sizeof ( FT_GlyphSlotRec ),

    tt_face_init,               /* FT_Face_InitFunc  init_face */
    tt_face_done,               /* FT_Face_DoneFunc  done_face */
    tt_size_init,               /* FT_Size_InitFunc  init_size */
    tt_size_done,               /* FT_Size_DoneFunc  done_size */
    tt_slot_init,               /* FT_Slot_InitFunc  init_slot */
    NULL,                       /* FT_Slot_DoneFunc  done_slot */

    tt_glyph_load,              /* FT_Slot_LoadFunc  load_glyph */

    tt_get_kerning,             /* FT_Face_GetKerningFunc   get_kerning  */
    NULL,                       /* FT_Face_AttachFunc       attach_file  */
    tt_get_advances,            /* FT_Face_GetAdvancesFunc  get_advances */

    tt_size_request,            /* FT_Size_RequestFunc  request_size */
    TT_SIZE_SELECT              /* FT_Size_SelectFunc   select_size  */
  )

/* END */


text:
/****************************************************************************
 *
 * truetype.c
 *
 *   FreeType TrueType driver component (body only).
 *
 * Copyright (C) 1996-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#define FT_MAKE_OPTION_SINGLE_OBJECT
#include <ft2build.h>

#include "ttdriver.c"   /* driver interface    */
#include "ttgload.c"    /* glyph loader        */
#include "ttgxvar.c"    /* gx distortable font */
#include "ttinterp.c"
#include "ttobjs.c"     /* object manager      */
#include "ttpload.c"    /* tables loader       */
#include "ttsubpix.c"

/* END */


text:
#
# FreeType 2 TrueType driver configuration rules
#

# Copyright (C) 1996-2020 by
# David Turner, Robert Wilhelm, and Werner Lemberg.
#
# This file is part of the FreeType project, and may only be used, modified,
# and distributed under the terms of the FreeType project license,
# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
# indicate that you have read the license and understand and accept it
# fully.

# TrueType driver directory
#
TT_DIR := $(SRC_DIR)/truetype

# compilation flags for the driver
#
TT_COMPILE := $(CC) $(ANSIFLAGS)                           \
                    $I$(subst /,$(COMPILER_SEP),$(TT_DIR)) \
                    $(INCLUDE_FLAGS)                       \
                    $(FT_CFLAGS)

# TrueType driver sources (i.e., C files)
#
TT_DRV_SRC := $(TT_DIR)/ttdriver.c \
              $(TT_DIR)/ttgload.c  \
              $(TT_DIR)/ttgxvar.c  \
              $(TT_DIR)/ttinterp.c \
              $(TT_DIR)/ttobjs.c   \
              $(TT_DIR)/ttpload.c  \
              $(TT_DIR)/ttsubpix.c

# TrueType driver headers
#
TT_DRV_H := $(TT_DRV_SRC:%.c=%.h) \
            $(TT_DIR)/tterrors.h

# TrueType driver object(s)
#
#   TT_DRV_OBJ_M is used during `multi' builds
#   TT_DRV_OBJ_S is used during `single' builds
#
TT_DRV_OBJ_M := $(TT_DRV_SRC:$(TT_DIR)/%.c=$(OBJ_DIR)/%.$O)
TT_DRV_OBJ_S := $(OBJ_DIR)/truetype.$O

# TrueType driver source file for single build
#
TT_DRV_SRC_S := $(TT_DIR)/truetype.c

# TrueType driver - single object
#
$(TT_DRV_OBJ_S): $(TT_DRV_SRC_S) $(TT_DRV_SRC) $(FREETYPE_H) $(TT_DRV_H)
	$(TT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(TT_DRV_SRC_S))

# driver - multiple objects
#
$(OBJ_DIR)/%.$O: $(TT_DIR)/%.c $(FREETYPE_H) $(TT_DRV_H)
	$(TT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)

# update main driver object lists
#
DRV_OBJS_S += $(TT_DRV_OBJ_S)
DRV_OBJS_M += $(TT_DRV_OBJ_M)

# EOF


text:
/****************************************************************************
 *
 * ttpload.h
 *
 *   TrueType-specific tables loader (specification).
 *
 * Copyright (C) 1996-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#ifndef TTPLOAD_H_
#define TTPLOAD_H_

#include <ft2build.h>
#include FT_INTERNAL_TRUETYPE_TYPES_H

FT_BEGIN_HEADER

  FT_LOCAL( FT_Error )
  tt_face_load_loca( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( FT_ULong )
  tt_face_get_location( TT_Face   face,
                        FT_UInt   gindex,
                        FT_UInt  *asize );

  FT_LOCAL( void )
  tt_face_done_loca( TT_Face  face );

  FT_LOCAL( FT_Error )
  tt_face_load_cvt( TT_Face    face,
                    FT_Stream  stream );

  FT_LOCAL( FT_Error )
  tt_face_load_fpgm( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( FT_Error )
  tt_face_load_prep( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( FT_Error )
  tt_face_load_hdmx( TT_Face    face,
                     FT_Stream  stream );

  FT_LOCAL( void )
  tt_face_free_hdmx( TT_Face  face );

  FT_LOCAL( FT_Byte* )
  tt_face_get_device_metrics( TT_Face    face,
                              FT_UInt    ppem,
                              FT_UInt    gindex );

FT_END_HEADER

#endif /* TTPLOAD_H_ */

/* END */


text:
/****************************************************************************
 *
 * ftccmap.c
 *
 *   FreeType CharMap cache (body)
 *
 * Copyright (C) 2000-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_CACHE_H
#include "ftcmanag.h"
#include FT_INTERNAL_MEMORY_H
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H

#include "ftccback.h"
#include "ftcerror.h"

#undef  FT_COMPONENT
#define FT_COMPONENT  cache

  /**************************************************************************
   *
   * Each FTC_CMapNode contains a simple array to map a range of character
   * codes to equivalent glyph indices.
   *
   * For now, the implementation is very basic: Each node maps a range of
   * 128 consecutive character codes to their corresponding glyph indices.
   *
   * We could do more complex things, but I don't think it is really very
   * useful.
   *
   */

  /* number of glyph indices / character code per node */
#define FTC_CMAP_INDICES_MAX  128

  /* compute a query/node hash */
#define FTC_CMAP_HASH( faceid, index, charcode )         \
          ( FTC_FACE_ID_HASH( faceid ) + 211 * (index) + \
            ( (charcode) / FTC_CMAP_INDICES_MAX )      )

  /* the charmap query */
  typedef struct  FTC_CMapQueryRec_
  {
    FTC_FaceID  face_id;
    FT_UInt     cmap_index;
    FT_UInt32   char_code;

  } FTC_CMapQueryRec, *FTC_CMapQuery;

#define FTC_CMAP_QUERY( x )  ((FTC_CMapQuery)(x))

  /* the cmap cache node */
  typedef struct  FTC_CMapNodeRec_
  {
    FTC_NodeRec  node;
    FTC_FaceID   face_id;
    FT_UInt      cmap_index;
    FT_UInt32    first;                         /* first character in node */
    FT_UInt16    indices[FTC_CMAP_INDICES_MAX]; /* array of glyph indices  */

  } FTC_CMapNodeRec, *FTC_CMapNode;

#define FTC_CMAP_NODE( x ) ( (FTC_CMapNode)( x ) )

  /* if (indices[n] == FTC_CMAP_UNKNOWN), we assume that the corresponding */
  /* glyph indices haven't been queried through FT_Get_Glyph_Index() yet   */
#define FTC_CMAP_UNKNOWN  (FT_UInt16)~0

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                        CHARMAP NODES                          *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  FT_CALLBACK_DEF( void )
  ftc_cmap_node_free( FTC_Node   ftcnode,
                      FTC_Cache  cache )
  {
    FTC_CMapNode  node   = (FTC_CMapNode)ftcnode;
    FT_Memory     memory = cache->memory;

    FT_FREE( node );
  }

  /* initialize a new cmap node */
  FT_CALLBACK_DEF( FT_Error )
  ftc_cmap_node_new( FTC_Node   *ftcanode,
                     FT_Pointer  ftcquery,
                     FTC_Cache   cache )
  {
    FTC_CMapNode  *anode  = (FTC_CMapNode*)ftcanode;
    FTC_CMapQuery  query  = (FTC_CMapQuery)ftcquery;
    FT_Error       error;
    FT_Memory      memory = cache->memory;
    FTC_CMapNode   node   = NULL;
    FT_UInt        nn;

    if ( !FT_NEW( node ) )
    {
      node->face_id    = query->face_id;
      node->cmap_index = query->cmap_index;
      node->first      = (query->char_code / FTC_CMAP_INDICES_MAX) *
                         FTC_CMAP_INDICES_MAX;

      for ( nn = 0; nn < FTC_CMAP_INDICES_MAX; nn++ )
        node->indices[nn] = FTC_CMAP_UNKNOWN;
    }

    *anode = node;
    return error;
  }

  /* compute the weight of a given cmap node */
  FT_CALLBACK_DEF( FT_Offset )
  ftc_cmap_node_weight( FTC_Node   cnode,
                        FTC_Cache  cache )
  {
    FT_UNUSED( cnode );
    FT_UNUSED( cache );

    return sizeof ( *cnode );
  }

  /* compare a cmap node to a given query */
  FT_CALLBACK_DEF( FT_Bool )
  ftc_cmap_node_compare( FTC_Node    ftcnode,
                         FT_Pointer  ftcquery,
                         FTC_Cache   cache,
                         FT_Bool*    list_changed )
  {
    FTC_CMapNode   node  = (FTC_CMapNode)ftcnode;
    FTC_CMapQuery  query = (FTC_CMapQuery)ftcquery;
    FT_UNUSED( cache );

    if ( list_changed )
      *list_changed = FALSE;
    if ( node->face_id    == query->face_id    &&
         node->cmap_index == query->cmap_index )
    {
      FT_UInt32  offset = (FT_UInt32)( query->char_code - node->first );

      return FT_BOOL( offset < FTC_CMAP_INDICES_MAX );
    }

    return 0;
  }

  FT_CALLBACK_DEF( FT_Bool )
  ftc_cmap_node_remove_faceid( FTC_Node    ftcnode,
                               FT_Pointer  ftcface_id,
                               FTC_Cache   cache,
                               FT_Bool*    list_changed )
  {
    FTC_CMapNode  node    = (FTC_CMapNode)ftcnode;
    FTC_FaceID    face_id = (FTC_FaceID)ftcface_id;
    FT_UNUSED( cache );

    if ( list_changed )
      *list_changed = FALSE;
    return FT_BOOL( node->face_id == face_id );
  }

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                    GLYPH IMAGE CACHE                          *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  static
  const FTC_CacheClassRec  ftc_cmap_cache_class =
  {
    ftc_cmap_node_new,           /* FTC_Node_NewFunc      node_new           */
    ftc_cmap_node_weight,        /* FTC_Node_WeightFunc   node_weight        */
    ftc_cmap_node_compare,       /* FTC_Node_CompareFunc  node_compare       */
    ftc_cmap_node_remove_faceid, /* FTC_Node_CompareFunc  node_remove_faceid */
    ftc_cmap_node_free,          /* FTC_Node_FreeFunc     node_free          */

    sizeof ( FTC_CacheRec ),
    ftc_cache_init,              /* FTC_Cache_InitFunc    cache_init         */
    ftc_cache_done,              /* FTC_Cache_DoneFunc    cache_done         */
  };

  /* documentation is in ftcache.h */

  FT_EXPORT_DEF( FT_Error )
  FTC_CMapCache_New( FTC_Manager     manager,
                     FTC_CMapCache  *acache )
  {
    return FTC_Manager_RegisterCache( manager,
                                      &ftc_cmap_cache_class,
                                      FTC_CACHE_P( acache ) );
  }

  /* documentation is in ftcache.h */

  FT_EXPORT_DEF( FT_UInt )
  FTC_CMapCache_Lookup( FTC_CMapCache  cmap_cache,
                        FTC_FaceID     face_id,
                        FT_Int         cmap_index,
                        FT_UInt32      char_code )
  {
    FTC_Cache         cache = FTC_CACHE( cmap_cache );
    FTC_CMapQueryRec  query;
    FTC_Node          node;
    FT_Error          error;
    FT_UInt           gindex = 0;
    FT_Offset         hash;
    FT_Int            no_cmap_change = 0;

    if ( cmap_index < 0 )
    {
      /* Treat a negative cmap index as a special value, meaning that you */
      /* don't want to change the FT_Face's character map through this    */
      /* call.  This can be useful if the face requester callback already */
      /* sets the face's charmap to the appropriate value.                */

      no_cmap_change = 1;
      cmap_index     = 0;
    }

    if ( !cache )
    {
      FT_TRACE0(( "FTC_CMapCache_Lookup: bad arguments, returning 0\n" ));
      return 0;
    }

    query.face_id    = face_id;
    query.cmap_index = (FT_UInt)cmap_index;
    query.char_code  = char_code;

    hash = FTC_CMAP_HASH( face_id, (FT_UInt)cmap_index, char_code );

#if 1
    FTC_CACHE_LOOKUP_CMP( cache, ftc_cmap_node_compare, hash, &query,
                          node, error );
#else
    error = FTC_Cache_Lookup( cache, hash, &query, &node );
#endif
    if ( error )
      goto Exit;

    FT_ASSERT( (FT_UInt)( char_code - FTC_CMAP_NODE( node )->first ) <
                FTC_CMAP_INDICES_MAX );

    /* something rotten can happen with rogue clients */
    if ( (FT_UInt)( char_code - FTC_CMAP_NODE( node )->first >=
                    FTC_CMAP_INDICES_MAX ) )
      return 0; /* XXX: should return appropriate error */

    gindex = FTC_CMAP_NODE( node )->indices[char_code -
                                            FTC_CMAP_NODE( node )->first];
    if ( gindex == FTC_CMAP_UNKNOWN )
    {
      FT_Face  face;

      gindex = 0;

      error = FTC_Manager_LookupFace( cache->manager,
                                      FTC_CMAP_NODE( node )->face_id,
                                      &face );
      if ( error )
        goto Exit;

      if ( (FT_UInt)cmap_index < (FT_UInt)face->num_charmaps )
      {
        FT_CharMap  old, cmap  = NULL;

        old  = face->charmap;
        cmap = face->charmaps[cmap_index];

        if ( old != cmap && !no_cmap_change )
          FT_Set_Charmap( face, cmap );

        gindex = FT_Get_Char_Index( face, char_code );

        if ( old != cmap && !no_cmap_change )
          FT_Set_Charmap( face, old );
      }

      FTC_CMAP_NODE( node )->indices[char_code -
                                     FTC_CMAP_NODE( node )->first]
        = (FT_UShort)gindex;
    }

  Exit:
    return gindex;
  }

/* END */


text:
/****************************************************************************
 *
 * ftcglyph.h
 *
 *   FreeType abstract glyph cache (specification).
 *
 * Copyright (C) 2000-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

  /*
   *
   * FTC_GCache is an _abstract_ cache object optimized to store glyph
   * data.  It works as follows:
   *
   * - It manages FTC_GNode objects. Each one of them can hold one or more
   *   glyph `items'.  Item types are not specified in the FTC_GCache but
   *   in classes that extend it.
   *
   * - Glyph attributes, like face ID, character size, render mode, etc.,
   *   can be grouped into abstract `glyph families'.  This avoids storing
   *   the attributes within the FTC_GCache, since it is likely that many
   *   FTC_GNodes will belong to the same family in typical uses.
   *
   * - Each FTC_GNode is thus an FTC_Node with two additional fields:
   *
   *   * gindex: A glyph index, or the first index in a glyph range.
   *   * family: A pointer to a glyph `family'.
   *
   * - Family types are not fully specific in the FTC_Family type, but
   *   by classes that extend it.
   *
   * Note that both FTC_ImageCache and FTC_SBitCache extend FTC_GCache.
   * They share an FTC_Family sub-class called FTC_BasicFamily which is
   * used to store the following data: face ID, pixel/point sizes, load
   * flags.  For more details see the file `src/cache/ftcbasic.c'.
   *
   * Client applications can extend FTC_GNode with their own FTC_GNode
   * and FTC_Family sub-classes to implement more complex caches (e.g.,
   * handling automatic synthesis, like obliquing & emboldening, colored
   * glyphs, etc.).
   *
   * See also the FTC_ICache & FTC_SCache classes in `ftcimage.h' and
   * `ftcsbits.h', which both extend FTC_GCache with additional
   * optimizations.
   *
   * A typical FTC_GCache implementation must provide at least the
   * following:
   *
   * - FTC_GNode sub-class, e.g. MyNode, with relevant methods:
   *     my_node_new            (must call FTC_GNode_Init)
   *     my_node_free           (must call FTC_GNode_Done)
   *     my_node_compare        (must call FTC_GNode_Compare)
   *     my_node_remove_faceid  (must call ftc_gnode_unselect in case
   *                             of match)
   *
   * - FTC_Family sub-class, e.g. MyFamily, with relevant methods:
   *     my_family_compare
   *     my_family_init
   *     my_family_reset (optional)
   *     my_family_done
   *
   * - FTC_GQuery sub-class, e.g. MyQuery, to hold cache-specific query
   *   data.
   *
   * - Constant structures for a FTC_GNodeClass.
   *
   * - MyCacheNew() can be implemented easily as a call to the convenience
   *   function FTC_GCache_New.
   *
   * - MyCacheLookup with a call to FTC_GCache_Lookup.  This function will
   *   automatically:
   *
   *   - Search for the corresponding family in the cache, or create
   *     a new one if necessary.  Put it in FTC_GQUERY(myquery).family
   *
   *   - Call FTC_Cache_Lookup.
   *
   *   If it returns NULL, you should create a new node, then call
   *   ftc_cache_add as usual.
   */

  /**************************************************************************
   *
   * Important: The functions defined in this file are only used to
   *            implement an abstract glyph cache class.  You need to
   *            provide additional logic to implement a complete cache.
   *
   */

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*********                                                       *********/
  /*********             WARNING, THIS IS BETA CODE.               *********/
  /*********                                                       *********/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

#ifndef FTCGLYPH_H_
#define FTCGLYPH_H_

#include <ft2build.h>
#include "ftcmanag.h"

FT_BEGIN_HEADER

 /*
  * We can group glyphs into `families'.  Each family correspond to a
  * given face ID, character size, transform, etc.
  *
  * Families are implemented as MRU list nodes.  They are
  * reference-counted.
  */

  typedef struct  FTC_FamilyRec_
  {
    FTC_MruNodeRec    mrunode;
    FT_UInt           num_nodes; /* current number of nodes in this family */
    FTC_Cache         cache;
    FTC_MruListClass  clazz;

  } FTC_FamilyRec, *FTC_Family;

#define  FTC_FAMILY(x)    ( (FTC_Family)(x) )
#define  FTC_FAMILY_P(x)  ( (FTC_Family*)(x) )

  typedef struct  FTC_GNodeRec_
  {
    FTC_NodeRec      node;
    FTC_Family       family;
    FT_UInt          gindex;

  } FTC_GNodeRec, *FTC_GNode;

#define FTC_GNODE( x )    ( (FTC_GNode)(x) )
#define FTC_GNODE_P( x )  ( (FTC_GNode*)(x) )

  typedef struct  FTC_GQueryRec_
  {
    FT_UInt      gindex;
    FTC_Family   family;

  } FTC_GQueryRec, *FTC_GQuery;

#define FTC_GQUERY( x )  ( (FTC_GQuery)(x) )

  /**************************************************************************
   *
   * These functions are exported so that they can be called from
   * user-provided cache classes; otherwise, they are really part of the
   * cache sub-system internals.
   */

  /* must be called by derived FTC_Node_InitFunc routines */
  FT_LOCAL( void )
  FTC_GNode_Init( FTC_GNode   node,
                  FT_UInt     gindex,  /* glyph index for node */
                  FTC_Family  family );

#ifdef FTC_INLINE

  /* returns TRUE iff the query's glyph index correspond to the node;  */
  /* this assumes that the `family' and `hash' fields of the query are */
  /* already correctly set                                             */
  FT_LOCAL( FT_Bool )
  FTC_GNode_Compare( FTC_GNode   gnode,
                     FTC_GQuery  gquery,
                     FTC_Cache   cache,
                     FT_Bool*    list_changed );

#endif

  /* call this function to clear a node's family -- this is necessary */
  /* to implement the `node_remove_faceid' cache method correctly     */
  FT_LOCAL( void )
  FTC_GNode_UnselectFamily( FTC_GNode  gnode,
                            FTC_Cache  cache );

  /* must be called by derived FTC_Node_DoneFunc routines */
  FT_LOCAL( void )
  FTC_GNode_Done( FTC_GNode  node,
                  FTC_Cache  cache );

  FT_LOCAL( void )
  FTC_Family_Init( FTC_Family  family,
                   FTC_Cache   cache );

  typedef struct FTC_GCacheRec_
  {
    FTC_CacheRec    cache;
    FTC_MruListRec  families;

  } FTC_GCacheRec, *FTC_GCache;

#define FTC_GCACHE( x )  ((FTC_GCache)(x))

#if 0
  /* can be used as @FTC_Cache_InitFunc */
  FT_LOCAL( FT_Error )
  FTC_GCache_Init( FTC_GCache  cache );
#endif

#if 0
  /* can be used as @FTC_Cache_DoneFunc */
  FT_LOCAL( void )
  FTC_GCache_Done( FTC_GCache  cache );
#endif

  /* the glyph cache class adds fields for the family implementation */
  typedef struct  FTC_GCacheClassRec_
  {
    FTC_CacheClassRec  clazz;
    FTC_MruListClass   family_class;

  } FTC_GCacheClassRec;

  typedef const FTC_GCacheClassRec*   FTC_GCacheClass;

#define FTC_GCACHE_CLASS( x )  ((FTC_GCacheClass)(x))

#define FTC_CACHE_GCACHE_CLASS( x ) \
          FTC_GCACHE_CLASS( FTC_CACHE(x)->org_class )
#define FTC_CACHE_FAMILY_CLASS( x ) \
          ( (FTC_MruListClass)FTC_CACHE_GCACHE_CLASS( x )->family_class )

  /* convenience function; use it instead of FTC_Manager_Register_Cache */
  FT_LOCAL( FT_Error )
  FTC_GCache_New( FTC_Manager       manager,
                  FTC_GCacheClass   clazz,
                  FTC_GCache       *acache );

#ifndef FTC_INLINE
  FT_LOCAL( FT_Error )
  FTC_GCache_Lookup( FTC_GCache   cache,
                     FT_Offset    hash,
                     FT_UInt      gindex,
                     FTC_GQuery   query,
                     FTC_Node    *anode );
#endif

  /* */

#define FTC_FAMILY_FREE( family, cache )                      \
          FTC_MruList_Remove( &FTC_GCACHE((cache))->families, \
                              (FTC_MruNode)(family) )

#ifdef FTC_INLINE

#define FTC_GCACHE_LOOKUP_CMP( cache, famcmp, nodecmp, hash,                \
                               gindex, query, node, error )                 \
  FT_BEGIN_STMNT                                                            \
    FTC_GCache               _gcache   = FTC_GCACHE( cache );               \
    FTC_GQuery               _gquery   = (FTC_GQuery)( query );             \
    FTC_MruNode_CompareFunc  _fcompare = (FTC_MruNode_CompareFunc)(famcmp); \
    FTC_MruNode              _mrunode;                                      \
                                                                            \
                                                                            \
    _gquery->gindex = (gindex);                                             \
                                                                            \
    FTC_MRULIST_LOOKUP_CMP( &_gcache->families, _gquery, _fcompare,         \
                            _mrunode, error );                              \
    _gquery->family = FTC_FAMILY( _mrunode );                               \
    if ( !error )                                                           \
    {                                                                       \
      FTC_Family  _gqfamily = _gquery->family;                              \
                                                                            \
                                                                            \
      _gqfamily->num_nodes++;                                               \
                                                                            \
      FTC_CACHE_LOOKUP_CMP( cache, nodecmp, hash, query, node, error );     \
                                                                            \
      if ( --_gqfamily->num_nodes == 0 )                                    \
        FTC_FAMILY_FREE( _gqfamily, _gcache );                              \
    }                                                                       \
  FT_END_STMNT
  /* */

#else /* !FTC_INLINE */

#define FTC_GCACHE_LOOKUP_CMP( cache, famcmp, nodecmp, hash,          \
                               gindex, query, node, error )           \
   FT_BEGIN_STMNT                                                     \
                                                                      \
     error = FTC_GCache_Lookup( FTC_GCACHE( cache ), hash, gindex,    \
                                FTC_GQUERY( query ), &node );         \
                                                                      \
   FT_END_STMNT

#endif /* !FTC_INLINE */

FT_END_HEADER

#endif /* FTCGLYPH_H_ */

/* END */


text:
/****************************************************************************
 *
 * ftcmanag.h
 *
 *   FreeType Cache Manager (specification).
 *
 * Copyright (C) 2000-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

  /**************************************************************************
   *
   * A cache manager is in charge of the following:
   *
   * - Maintain a mapping between generic FTC_FaceIDs and live FT_Face
   *   objects.  The mapping itself is performed through a user-provided
   *   callback.  However, the manager maintains a small cache of FT_Face
   *   and FT_Size objects in order to speed up things considerably.
   *
   * - Manage one or more cache objects.  Each cache is in charge of
   *   holding a varying number of `cache nodes'.  Each cache node
   *   represents a minimal amount of individually accessible cached
   *   data.  For example, a cache node can be an FT_Glyph image
   *   containing a vector outline, or some glyph metrics, or anything
   *   else.
   *
   *   Each cache node has a certain size in bytes that is added to the
   *   total amount of `cache memory' within the manager.
   *
   *   All cache nodes are located in a global LRU list, where the oldest
   *   node is at the tail of the list.
   *
   *   Each node belongs to a single cache, and includes a reference
   *   count to avoid destroying it (due to caching).
   *
   */

  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*********                                                       *********/
  /*********             WARNING, THIS IS BETA CODE.               *********/
  /*********                                                       *********/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/
  /*************************************************************************/

#ifndef FTCMANAG_H_
#define FTCMANAG_H_

#include <ft2build.h>
#include FT_CACHE_H
#include "ftcmru.h"
#include "ftccache.h"

FT_BEGIN_HEADER

  /**************************************************************************
   *
   * @Section:
   *   cache_subsystem
   *
   */

#define FTC_MAX_FACES_DEFAULT  2
#define FTC_MAX_SIZES_DEFAULT  4
#define FTC_MAX_BYTES_DEFAULT  200000L  /* ~200kByte by default */

  /* maximum number of caches registered in a single manager */
#define FTC_MAX_CACHES         16

  typedef struct  FTC_ManagerRec_
  {
    FT_Library          library;
    FT_Memory           memory;

    FTC_Node            nodes_list;
    FT_Offset           max_weight;
    FT_Offset           cur_weight;
    FT_UInt             num_nodes;

    FTC_Cache           caches[FTC_MAX_CACHES];
    FT_UInt             num_caches;

    FTC_MruListRec      faces;
    FTC_MruListRec      sizes;

    FT_Pointer          request_data;
    FTC_Face_Requester  request_face;

  } FTC_ManagerRec;

  /**************************************************************************
   *
   * @Function:
   *   FTC_Manager_Compress
   *
   * @Description:
   *   This function is used to check the state of the cache manager if
   *   its `num_bytes' field is greater than its `max_bytes' field.  It
   *   will flush as many old cache nodes as possible (ignoring cache
   *   nodes with a non-zero reference count).
   *
   * @InOut:
   *   manager ::
   *     A handle to the cache manager.
   *
   * @Note:
   *   Client applications should not call this function directly.  It is
   *   normally invoked by specific cache implementations.
   *
   *   The reason this function is exported is to allow client-specific
   *   cache classes.
   */
  FT_LOCAL( void )
  FTC_Manager_Compress( FTC_Manager  manager );

  /* try to flush `count' old nodes from the cache; return the number
   * of really flushed nodes
   */
  FT_LOCAL( FT_UInt )
  FTC_Manager_FlushN( FTC_Manager  manager,
                      FT_UInt      count );

  /* this must be used internally for the moment */
  FT_LOCAL( FT_Error )
  FTC_Manager_RegisterCache( FTC_Manager      manager,
                             FTC_CacheClass   clazz,
                             FTC_Cache       *acache );

 /* */

#define FTC_SCALER_COMPARE( a, b )                \
    ( (a)->face_id      == (b)->face_id      &&   \
      (a)->width        == (b)->width        &&   \
      (a)->height       == (b)->height       &&   \
      ((a)->pixel != 0) == ((b)->pixel != 0) &&   \
      ( (a)->pixel ||                             \
        ( (a)->x_res == (b)->x_res &&             \
          (a)->y_res == (b)->y_res ) ) )

#define FTC_SCALER_HASH( q )                                 \
    ( FTC_FACE_ID_HASH( (q)->face_id ) +                     \
      (q)->width + (q)->height*7 +                           \
      ( (q)->pixel ? 0 : ( (q)->x_res*33 ^ (q)->y_res*61 ) ) )

 /* */

FT_END_HEADER

#endif /* FTCMANAG_H_ */

/* END */


text:
/****************************************************************************
 *
 * ftcmanag.c
 *
 *   FreeType Cache Manager (body).
 *
 * Copyright (C) 2000-2020 by
 * David Turner, Robert Wilhelm, and Werner Lemberg.
 *
 * This file is part of the FreeType project, and may only be used,
 * modified, and distributed under the terms of the FreeType project
 * license, LICENSE.TXT.  By continuing to use, modify, or distribute
 * this file you indicate that you have read the license and
 * understand and accept it fully.
 *
 */

#include <ft2build.h>
#include FT_CACHE_H
#include "ftcmanag.h"
#include FT_INTERNAL_OBJECTS_H
#include FT_INTERNAL_DEBUG_H
#include FT_SIZES_H

#include "ftccback.h"
#include "ftcerror.h"

#undef  FT_COMPONENT
#define FT_COMPONENT  cache

  static FT_Error
  ftc_scaler_lookup_size( FTC_Manager  manager,
                          FTC_Scaler   scaler,
                          FT_Size     *asize )
  {
    FT_Face   face;
    FT_Size   size = NULL;
    FT_Error  error;

    error = FTC_Manager_LookupFace( manager, scaler->face_id, &face );
    if ( error )
      goto Exit;

    error = FT_New_Size( face, &size );
    if ( error )
      goto Exit;

    FT_Activate_Size( size );

    if ( scaler->pixel )
      error = FT_Set_Pixel_Sizes( face, scaler->width, scaler->height );
    else
      error = FT_Set_Char_Size( face,
                                (FT_F26Dot6)scaler->width,
                                (FT_F26Dot6)scaler->height,
                                scaler->x_res,
                                scaler->y_res );
    if ( error )
    {
      FT_Done_Size( size );
      size = NULL;
    }

  Exit:
    *asize = size;
    return error;
  }

  typedef struct  FTC_SizeNodeRec_
  {
    FTC_MruNodeRec  node;
    FT_Size         size;
    FTC_ScalerRec   scaler;

  } FTC_SizeNodeRec, *FTC_SizeNode;

#define FTC_SIZE_NODE( x ) ( (FTC_SizeNode)( x ) )

  FT_CALLBACK_DEF( void )
  ftc_size_node_done( FTC_MruNode  ftcnode,
                      FT_Pointer   data )
  {
    FTC_SizeNode  node = (FTC_SizeNode)ftcnode;
    FT_Size       size = node->size;
    FT_UNUSED( data );

    if ( size )
      FT_Done_Size( size );
  }

  FT_CALLBACK_DEF( FT_Bool )
  ftc_size_node_compare( FTC_MruNode  ftcnode,
                         FT_Pointer   ftcscaler )
  {
    FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
    FTC_Scaler    scaler  = (FTC_Scaler)ftcscaler;
    FTC_Scaler    scaler0 = &node->scaler;

    if ( FTC_SCALER_COMPARE( scaler0, scaler ) )
    {
      FT_Activate_Size( node->size );
      return 1;
    }
    return 0;
  }

  FT_CALLBACK_DEF( FT_Error )
  ftc_size_node_init( FTC_MruNode  ftcnode,
                      FT_Pointer   ftcscaler,
                      FT_Pointer   ftcmanager )
  {
    FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
    FTC_Scaler    scaler  = (FTC_Scaler)ftcscaler;
    FTC_Manager   manager = (FTC_Manager)ftcmanager;

    node->scaler = scaler[0];

    return ftc_scaler_lookup_size( manager, scaler, &node->size );
  }

  FT_CALLBACK_DEF( FT_Error )
  ftc_size_node_reset( FTC_MruNode  ftcnode,
                       FT_Pointer   ftcscaler,
                       FT_Pointer   ftcmanager )
  {
    FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
    FTC_Scaler    scaler  = (FTC_Scaler)ftcscaler;
    FTC_Manager   manager = (FTC_Manager)ftcmanager;

    FT_Done_Size( node->size );

    node->scaler = scaler[0];

    return ftc_scaler_lookup_size( manager, scaler, &node->size );
  }

  static
  const FTC_MruListClassRec  ftc_size_list_class =
  {
    sizeof ( FTC_SizeNodeRec ),

    ftc_size_node_compare,  /* FTC_MruNode_CompareFunc  node_compare */
    ftc_size_node_init,     /* FTC_MruNode_InitFunc     node_init    */
    ftc_size_node_reset,    /* FTC_MruNode_ResetFunc    node_reset   */
    ftc_size_node_done      /* FTC_MruNode_DoneFunc     node_done    */
  };

  /* helper function used by ftc_face_node_done */
  static FT_Bool
  ftc_size_node_compare_faceid( FTC_MruNode  ftcnode,
                                FT_Pointer   ftcface_id )
  {
    FTC_SizeNode  node    = (FTC_SizeNode)ftcnode;
    FTC_FaceID    face_id = (FTC_FaceID)ftcface_id;

    return FT_BOOL( node->scaler.face_id == face_id );
  }

  /* documentation is in ftcache.h */

  FT_EXPORT_DEF( FT_Error )
  FTC_Manager_LookupSize( FTC_Manager  manager,
                          FTC_Scaler   scaler,
                          FT_Size     *asize )
  {
    FT_Error     error;
    FTC_MruNode  mrunode;

    if ( !asize || !scaler )
      return FT_THROW( Invalid_Argument );

    *asize = NULL;

    if ( !manager )
      return FT_THROW( Invalid_Cache_Handle );

#ifdef FTC_INLINE

    FTC_MRULIST_LOOKUP_CMP( &manager->sizes, scaler, ftc_size_node_compare,
                            mrunode, error );

#else
    error = FTC_MruList_Lookup( &manager->sizes, scaler, &mrunode );
#endif

    if ( !error )
      *asize = FTC_SIZE_NODE( mrunode )->size;

    return error;
  }

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                    FACE MRU IMPLEMENTATION                    *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  typedef struct  FTC_FaceNodeRec_
  {
    FTC_MruNodeRec  node;
    FTC_FaceID      face_id;
    FT_Face         face;

  } FTC_FaceNodeRec, *FTC_FaceNode;

#define FTC_FACE_NODE( x ) ( ( FTC_FaceNode )( x ) )

  FT_CALLBACK_DEF( FT_Error )
  ftc_face_node_init( FTC_MruNode  ftcnode,
                      FT_Pointer   ftcface_id,
                      FT_Pointer   ftcmanager )
  {
    FTC_FaceNode  node    = (FTC_FaceNode)ftcnode;
    FTC_FaceID    face_id = (FTC_FaceID)ftcface_id;
    FTC_Manager   manager = (FTC_Manager)ftcmanager;
    FT_Error      error;

    node->face_id = face_id;

    error = manager->request_face( face_id,
                                   manager->library,
                                   manager->request_data,
                                   &node->face );
    if ( !error )
    {
      /* destroy initial size object; it will be re-created later */
      if ( node->face->size )
        FT_Done_Size( node->face->size );
    }

    return error;
  }

  FT_CALLBACK_DEF( void )
  ftc_face_node_done( FTC_MruNode  ftcnode,
                      FT_Pointer   ftcmanager )
  {
    FTC_FaceNode  node    = (FTC_FaceNode)ftcnode;
    FTC_Manager   manager = (FTC_Manager)ftcmanager;

    /* we must begin by removing all scalers for the target face */
    /* from the manager's list                                   */
    FTC_MruList_RemoveSelection( &manager->sizes,
                                 ftc_size_node_compare_faceid,
                                 node->face_id );

    /* all right, we can discard the face now */
    FT_Done_Face( node->face );
    node->face    = NULL;
    node->face_id = NULL;
  }

  FT_CALLBACK_DEF( FT_Bool )
  ftc_face_node_compare( FTC_MruNode  ftcnode,
                         FT_Pointer   ftcface_id )
  {
    FTC_FaceNode  node    = (FTC_FaceNode)ftcnode;
    FTC_FaceID    face_id = (FTC_FaceID)ftcface_id;

    return FT_BOOL( node->face_id == face_id );
  }

  static
  const FTC_MruListClassRec  ftc_face_list_class =
  {
    sizeof ( FTC_FaceNodeRec),

    ftc_face_node_compare,  /* FTC_MruNode_CompareFunc  node_compare */
    ftc_face_node_init,     /* FTC_MruNode_InitFunc     node_init    */
    NULL,                   /* FTC_MruNode_ResetFunc    node_reset   */
    ftc_face_node_done      /* FTC_MruNode_DoneFunc     node_done    */
  };

  /* documentation is in ftcache.h */

  FT_EXPORT_DEF( FT_Error )
  FTC_Manager_LookupFace( FTC_Manager  manager,
                          FTC_FaceID   face_id,
                          FT_Face     *aface )
  {
    FT_Error     error;
    FTC_MruNode  mrunode;

    if ( !aface )
      return FT_THROW( Invalid_Argument );

    *aface = NULL;

    if ( !manager )
      return FT_THROW( Invalid_Cache_Handle );

    /* we break encapsulation for the sake of speed */
#ifdef FTC_INLINE

    FTC_MRULIST_LOOKUP_CMP( &manager->faces, face_id, ftc_face_node_compare,
                            mrunode, error );

#else
    error = FTC_MruList_Lookup( &manager->faces, face_id, &mrunode );
#endif

    if ( !error )
      *aface = FTC_FACE_NODE( mrunode )->face;

    return error;
  }

  /*************************************************************************/
  /*************************************************************************/
  /*****                                                               *****/
  /*****                    CACHE MANAGER ROUTINES                     *****/
  /*****                                                               *****/
  /*************************************************************************/
  /*************************************************************************/

  /* documentation is in ftcache.h */

  FT_EXPORT_DEF( FT_Error )
  FTC_Manager_New( FT_Library          library,
                   FT_UInt             max_faces,
                   FT_UInt             max_sizes,
                   FT_ULong            max_bytes,
                   FTC_Face_Requester  requester,
                   FT_Pointer          req_data,
                   FTC_Manager        *amanager )
  {
    FT_Error     error;
    FT_Memory    memory;
    FTC_Manager  manager = 0;

    if ( !library )
      return FT_THROW( Invalid_Library_Handle );

    if ( !amanager || !requester )
      return FT_THROW( Invalid_Argument );

    memory = library->memory;

    if ( FT_NEW( manager ) )
      goto Exit;

    if ( max_faces == 0 )
      max_faces = FTC_MAX_FACES_DEFAULT;

    if ( max_sizes == 0 )
      max_sizes = FTC_MAX_SIZES_DEFAULT;

    if ( max_bytes == 0 )
      max_bytes = FTC_MAX_BYTES_DEFAULT;

    manager->library      = library;
    manager->memory       = memory;
    manager->max_weight   = max_bytes;

    manager->request_face = requester;
    manager->request_data = req_data;

    FTC_MruList_Init( &manager->faces,
                      &ftc_face_list_class,
                      max_faces,
                      manager,
                      memory );

    FTC_MruList_Init( &manager->sizes,
                      &ftc_size_list_class,
                      max_sizes,
                      manager,
                      memory );

    *amanager = manager;

  Exit:
    return error;
  }

  /* documentation is in ftcache.h */

  FT_EXPORT_DEF( void )
  FTC_Manager_Done( FTC_Manager  manager )
  {
    FT_Memory  memory;
    FT_UInt    idx;

    if ( !manager || !manager->library )
      return;

    memory = manager->memory;

    /* now discard all caches */
    for (idx = manager->num_caches; idx-- > 0; )
    {
      FTC_Cache  cache = manager->caches[idx];

      if ( cache )
      {
        cache->clazz.cache_done( cache );
        FT_FREE( cache );
        manager->caches[idx] = NULL;
      }
    }
    manager->num_caches = 0;

    /* discard faces and sizes */
    FTC_MruList_Done( &manager->sizes );
    FTC_MruList_Done( &manager->faces );

    manager->library = NULL;
    manager->memory  = NULL;

    FT_FREE( manager );
  }

  /* documentation is in ftcache.h */

  FT_EXPORT_DEF( void )
  FTC_Manager_Reset( FTC_Manager  manager )
  {
    if ( !manager )
      return;

    FTC_MruList_Reset( &manager->sizes );
    FTC_MruList_Reset( &manager->faces );

    FTC_Manager_FlushN( manager, manager->num_nodes );
  }

#ifdef FT_DEBUG_ERROR

  static void
  FTC_Manager_Check( FTC_Manager  manager )
  {
    FTC_Node  node, first;

    first = manager->nodes_list;

    /* check node weights */
    if ( first )
    {
      FT_Offset  weight = 0;

      node = first;

      do
      {
        FTC_Cache  cache = manager->caches[node->cache_index];

        if ( (FT_UInt)node->cache_index >= manager->num_caches )
          FT_TRACE0(( "FTC_Manager_Check: invalid node (cache index = %ld\n",
                      node->cache_index ));
        else
          weight += cache->clazz.node_weight( node, cache );

        node = FTC_NODE_NEXT( node );

      } while ( node != first );

      if ( weight != manager->cur_weight )
        FT_TRACE0(( "FTC_Manager_Check: invalid weight %ld instead of %ld\n",
                    manager->cur_weight, weight ));
    }

    /* check circular list */
    if ( first )
    {
      FT_UFast  count = 0;

      node = first;
      do
      {
        count++;
        node = FTC_NODE_NEXT( node );

      } while ( node != first );

      if ( count != manager->num_nodes )
        FT_TRACE0(( "FTC_Manager_Check:"
                    " invalid cache node count %d instead of %d\n",
                    manager->num_nodes, count ));
    }
  }

#endif /* FT_DEBUG_ERROR */

  /* `Compress' the manager's data, i.e., get rid of old cache nodes */
  /* that are not referenced anymore in order to limit the total     */
  /* memory used by the cache.                                       */

  /* documentation is in ftcmanag.h */

  FT_LOCAL_DEF( void )
  FTC_Manager_Compress( FTC_Manager  manager )
  {
    FTC_Node   node, first;

    if ( !manager )
      return;

    first = manager->nodes_list;

#ifdef FT_DEBUG_ERROR
    FTC_Manager_Check( manager );

    FT_TRACE0(( "compressing, weight = %ld, max = %ld, nodes = %d\n",
                manager->cur_weight, manager->max_weight,
                manager->num_nodes ));
#endif

    if ( manager->cur_weight < manager->max_weight || !first )
      return;

    /* go to last node -- it's a circular list */
    node = FTC_NODE_PREV( first );
    do
    {
      FTC_Node  prev;

      prev = ( node == first ) ? NULL : FTC_NODE_PREV( node );

      if ( node->ref_count <= 0 )
        ftc_node_destroy( node, manager );

      node = prev;

    } while ( node && manager->cur_weight > manager->max_weight );
  }

  /* documentation is in ftcmanag.h */

  FT_LOCAL_DEF( FT_Error )
  FTC_Manager_RegisterCache( FTC_Manager      manager,
                             FTC_CacheClass   clazz,
                             FTC_Cache       *acache )
  {
    FT_Error   error = FT_ERR( Invalid_Argument );
    FTC_Cache  cache = NULL;

    if ( manager && clazz && acache )
    {
      FT_Memory  memory = manager->memory;

      if ( manager->num_caches >= FTC_MAX_CACHES )
      {
        error = FT_THROW( Too_Many_Caches );
        FT_ERROR(( "FTC_Manager_RegisterCache:"
                   " too many registered caches\n" ));
        goto Exit;
      }

      if ( !FT_ALLOC( cache, clazz->cache_size ) )
      {
        cache->manager   = manager;
        cache->memory    = memory;
        cache->clazz     = clazz[0];
        cache->org_class = clazz;

        /* THIS IS VERY IMPORTANT!  IT WILL WRETCH THE MANAGER */
        /* IF IT IS NOT SET CORRECTLY                          */
        cache->index = manager->num_caches;

        error = clazz->cache_init( cache );
        if ( error )
        {
          clazz->cache_done( cache );
          FT_FREE( cache );
          goto Exit;
        }

        manager->caches[manager->num_caches++] = cache;
      }
    }

  Exit:
    if ( acache )
      *acache = cache;
    return error;
  }

  FT_LOCAL_DEF( FT_UInt )
  FTC_Manager_FlushN( FTC_Manager  manager,
                      FT_UInt      count )
  {
    FTC_Node  first = manager->nodes_list;
    FTC_Node  node;
    FT_UInt   result;

    /* try to remove `count' nodes from the list */
    if ( !first )  /* empty list! */
      return 0;

    /* go to last node - it's a circular list */
    node = FTC_NODE_PREV(first);
    for ( result = 0; result < count; )
    {
      FTC_Node  prev = FTC_NODE_PREV( node );

      /* don't touch locked nodes */
      if ( node->ref_count <= 0 )
      {
        ftc_node_destroy( node, manager );
        result++;
      }

      if ( node == first )
        break;

      node = prev;
    }
    return  result;
  }

  /* documentation is in ftcache.h */

  FT_EXPORT_DEF( void )
  FTC_Manager_RemoveFaceID( FTC_Manager  manager,
                            FTC_FaceID   face_id )
  {
    FT_UInt  nn;

    if ( !manager )
      return;

    /* this will remove all FTC_SizeNode that correspond to
     * the face_id as well
     */
    FTC_MruList_RemoveSelection( &manager->faces,
                                 ftc_face_node_compare,
                                 face_id );

    for ( nn = 0; nn < manager->num_caches; nn++ )
      FTC_Cache_RemoveFaceID( manager->caches[nn], face_id );
  }

  /* documentation is in ftcache.h */

  FT_EXPORT_DEF( void )
  FTC_Node_Unref( FTC_Node     node,
                  FTC_Manager  manager )
  {
    if ( node                                             &&
         manager                                          &&
         (FT_UInt)node->cache_index < manager->num_caches )
      node->ref_count--;
  }

/* END */