Home | History | Annotate | Download | only in autofit
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  afloader.c                                                             */
      4 /*                                                                         */
      5 /*    Auto-fitter glyph loading routines (body).                           */
      6 /*                                                                         */
      7 /*  Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 by                  */
      8 /*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
      9 /*                                                                         */
     10 /*  This file is part of the FreeType project, and may only be used,       */
     11 /*  modified, and distributed under the terms of the FreeType project      */
     12 /*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
     13 /*  this file you indicate that you have read the license and              */
     14 /*  understand and accept it fully.                                        */
     15 /*                                                                         */
     16 /***************************************************************************/
     17 
     18 
     19 #include "afloader.h"
     20 #include "afhints.h"
     21 #include "afglobal.h"
     22 #include "aferrors.h"
     23 
     24 
     25   FT_LOCAL_DEF( FT_Error )
     26   af_loader_init( AF_Loader  loader,
     27                   FT_Memory  memory )
     28   {
     29     FT_ZERO( loader );
     30 
     31     af_glyph_hints_init( &loader->hints, memory );
     32 #ifdef AF_DEBUG
     33     _af_debug_hints = &loader->hints;
     34 #endif
     35     return FT_GlyphLoader_New( memory, &loader->gloader );
     36   }
     37 
     38 
     39   FT_LOCAL_DEF( FT_Error )
     40   af_loader_reset( AF_Loader  loader,
     41                    FT_Face    face )
     42   {
     43     FT_Error  error = AF_Err_Ok;
     44 
     45 
     46     loader->face    = face;
     47     loader->globals = (AF_FaceGlobals)face->autohint.data;
     48 
     49     FT_GlyphLoader_Rewind( loader->gloader );
     50 
     51     if ( loader->globals == NULL )
     52     {
     53       error = af_face_globals_new( face, &loader->globals );
     54       if ( !error )
     55       {
     56         face->autohint.data =
     57           (FT_Pointer)loader->globals;
     58         face->autohint.finalizer =
     59           (FT_Generic_Finalizer)af_face_globals_free;
     60       }
     61     }
     62 
     63     return error;
     64   }
     65 
     66 
     67   FT_LOCAL_DEF( void )
     68   af_loader_done( AF_Loader  loader )
     69   {
     70     af_glyph_hints_done( &loader->hints );
     71 
     72     loader->face    = NULL;
     73     loader->globals = NULL;
     74 
     75 #ifdef AF_DEBUG
     76     _af_debug_hints = NULL;
     77 #endif
     78     FT_GlyphLoader_Done( loader->gloader );
     79     loader->gloader = NULL;
     80   }
     81 
     82 
     83   static FT_Error
     84   af_loader_load_g( AF_Loader  loader,
     85                     AF_Scaler  scaler,
     86                     FT_UInt    glyph_index,
     87                     FT_Int32   load_flags,
     88                     FT_UInt    depth )
     89   {
     90     FT_Error          error;
     91     FT_Face           face     = loader->face;
     92     FT_GlyphLoader    gloader  = loader->gloader;
     93     AF_ScriptMetrics  metrics  = loader->metrics;
     94     AF_GlyphHints     hints    = &loader->hints;
     95     FT_GlyphSlot      slot     = face->glyph;
     96     FT_Slot_Internal  internal = slot->internal;
     97 
     98 
     99     error = FT_Load_Glyph( face, glyph_index, load_flags );
    100     if ( error )
    101       goto Exit;
    102 
    103     loader->transformed = internal->glyph_transformed;
    104     if ( loader->transformed )
    105     {
    106       FT_Matrix  inverse;
    107 
    108 
    109       loader->trans_matrix = internal->glyph_matrix;
    110       loader->trans_delta  = internal->glyph_delta;
    111 
    112       inverse = loader->trans_matrix;
    113       FT_Matrix_Invert( &inverse );
    114       FT_Vector_Transform( &loader->trans_delta, &inverse );
    115     }
    116 
    117     /* set linear metrics */
    118     slot->linearHoriAdvance = slot->metrics.horiAdvance;
    119     slot->linearVertAdvance = slot->metrics.vertAdvance;
    120 
    121     switch ( slot->format )
    122     {
    123     case FT_GLYPH_FORMAT_OUTLINE:
    124       /* translate the loaded glyph when an internal transform is needed */
    125       if ( loader->transformed )
    126         FT_Outline_Translate( &slot->outline,
    127                               loader->trans_delta.x,
    128                               loader->trans_delta.y );
    129 
    130       /* copy the outline points in the loader's current               */
    131       /* extra points which is used to keep original glyph coordinates */
    132       error = FT_GLYPHLOADER_CHECK_POINTS( gloader,
    133                                            slot->outline.n_points + 4,
    134                                            slot->outline.n_contours );
    135       if ( error )
    136         goto Exit;
    137 
    138       FT_ARRAY_COPY( gloader->current.outline.points,
    139                      slot->outline.points,
    140                      slot->outline.n_points );
    141 
    142       FT_ARRAY_COPY( gloader->current.outline.contours,
    143                      slot->outline.contours,
    144                      slot->outline.n_contours );
    145 
    146       FT_ARRAY_COPY( gloader->current.outline.tags,
    147                      slot->outline.tags,
    148                      slot->outline.n_points );
    149 
    150       gloader->current.outline.n_points   = slot->outline.n_points;
    151       gloader->current.outline.n_contours = slot->outline.n_contours;
    152 
    153       /* compute original horizontal phantom points (and ignore */
    154       /* vertical ones)                                         */
    155       loader->pp1.x = hints->x_delta;
    156       loader->pp1.y = hints->y_delta;
    157       loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance,
    158                                  hints->x_scale ) + hints->x_delta;
    159       loader->pp2.y = hints->y_delta;
    160 
    161       /* be sure to check for spacing glyphs */
    162       if ( slot->outline.n_points == 0 )
    163         goto Hint_Metrics;
    164 
    165       /* now load the slot image into the auto-outline and run the */
    166       /* automatic hinting process                                 */
    167       if ( metrics->clazz->script_hints_apply )
    168         metrics->clazz->script_hints_apply( hints,
    169                                             &gloader->current.outline,
    170                                             metrics );
    171 
    172       /* we now need to hint the metrics according to the change in */
    173       /* width/positioning that occurred during the hinting process */
    174       if ( scaler->render_mode != FT_RENDER_MODE_LIGHT )
    175       {
    176         FT_Pos        old_rsb, old_lsb, new_lsb;
    177         FT_Pos        pp1x_uh, pp2x_uh;
    178         AF_AxisHints  axis  = &hints->axis[AF_DIMENSION_HORZ];
    179         AF_Edge       edge1 = axis->edges;         /* leftmost edge  */
    180         AF_Edge       edge2 = edge1 +
    181                               axis->num_edges - 1; /* rightmost edge */
    182 
    183 
    184         if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) )
    185         {
    186           old_rsb = loader->pp2.x - edge2->opos;
    187           old_lsb = edge1->opos;
    188           new_lsb = edge1->pos;
    189 
    190           /* remember unhinted values to later account */
    191           /* for rounding errors                       */
    192 
    193           pp1x_uh = new_lsb    - old_lsb;
    194           pp2x_uh = edge2->pos + old_rsb;
    195 
    196           /* prefer too much space over too little space */
    197           /* for very small sizes                        */
    198 
    199           if ( old_lsb < 24 )
    200             pp1x_uh -= 8;
    201 
    202           if ( old_rsb < 24 )
    203             pp2x_uh += 8;
    204 
    205           loader->pp1.x = FT_PIX_ROUND( pp1x_uh );
    206           loader->pp2.x = FT_PIX_ROUND( pp2x_uh );
    207 
    208           if ( loader->pp1.x >= new_lsb && old_lsb > 0 )
    209             loader->pp1.x -= 64;
    210 
    211           if ( loader->pp2.x <= edge2->pos && old_rsb > 0 )
    212             loader->pp2.x += 64;
    213 
    214           slot->lsb_delta = loader->pp1.x - pp1x_uh;
    215           slot->rsb_delta = loader->pp2.x - pp2x_uh;
    216         }
    217         else
    218         {
    219           FT_Pos  pp1x = loader->pp1.x;
    220           FT_Pos  pp2x = loader->pp2.x;
    221 
    222 
    223           loader->pp1.x = FT_PIX_ROUND( pp1x );
    224           loader->pp2.x = FT_PIX_ROUND( pp2x );
    225 
    226           slot->lsb_delta = loader->pp1.x - pp1x;
    227           slot->rsb_delta = loader->pp2.x - pp2x;
    228         }
    229       }
    230       else
    231       {
    232         FT_Pos  pp1x = loader->pp1.x;
    233         FT_Pos  pp2x = loader->pp2.x;
    234 
    235 
    236         loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta );
    237         loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta );
    238 
    239         slot->lsb_delta = loader->pp1.x - pp1x;
    240         slot->rsb_delta = loader->pp2.x - pp2x;
    241       }
    242 
    243       /* good, we simply add the glyph to our loader's base */
    244       FT_GlyphLoader_Add( gloader );
    245       break;
    246 
    247     case FT_GLYPH_FORMAT_COMPOSITE:
    248       {
    249         FT_UInt      nn, num_subglyphs = slot->num_subglyphs;
    250         FT_UInt      num_base_subgs, start_point;
    251         FT_SubGlyph  subglyph;
    252 
    253 
    254         start_point = gloader->base.outline.n_points;
    255 
    256         /* first of all, copy the subglyph descriptors in the glyph loader */
    257         error = FT_GlyphLoader_CheckSubGlyphs( gloader, num_subglyphs );
    258         if ( error )
    259           goto Exit;
    260 
    261         FT_ARRAY_COPY( gloader->current.subglyphs,
    262                        slot->subglyphs,
    263                        num_subglyphs );
    264 
    265         gloader->current.num_subglyphs = num_subglyphs;
    266         num_base_subgs                 = gloader->base.num_subglyphs;
    267 
    268         /* now, read each subglyph independently */
    269         for ( nn = 0; nn < num_subglyphs; nn++ )
    270         {
    271           FT_Vector  pp1, pp2;
    272           FT_Pos     x, y;
    273           FT_UInt    num_points, num_new_points, num_base_points;
    274 
    275 
    276           /* gloader.current.subglyphs can change during glyph loading due */
    277           /* to re-allocation -- we must recompute the current subglyph on */
    278           /* each iteration                                                */
    279           subglyph = gloader->base.subglyphs + num_base_subgs + nn;
    280 
    281           pp1 = loader->pp1;
    282           pp2 = loader->pp2;
    283 
    284           num_base_points = gloader->base.outline.n_points;
    285 
    286           error = af_loader_load_g( loader, scaler, subglyph->index,
    287                                     load_flags, depth + 1 );
    288           if ( error )
    289             goto Exit;
    290 
    291           /* recompute subglyph pointer */
    292           subglyph = gloader->base.subglyphs + num_base_subgs + nn;
    293 
    294           if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS )
    295           {
    296             pp1 = loader->pp1;
    297             pp2 = loader->pp2;
    298           }
    299           else
    300           {
    301             loader->pp1 = pp1;
    302             loader->pp2 = pp2;
    303           }
    304 
    305           num_points     = gloader->base.outline.n_points;
    306           num_new_points = num_points - num_base_points;
    307 
    308           /* now perform the transform required for this subglyph */
    309 
    310           if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE    |
    311                                    FT_SUBGLYPH_FLAG_XY_SCALE |
    312                                    FT_SUBGLYPH_FLAG_2X2      ) )
    313           {
    314             FT_Vector*  cur   = gloader->base.outline.points +
    315                                 num_base_points;
    316             FT_Vector*  limit = cur + num_new_points;
    317 
    318 
    319             for ( ; cur < limit; cur++ )
    320               FT_Vector_Transform( cur, &subglyph->transform );
    321           }
    322 
    323           /* apply offset */
    324 
    325           if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) )
    326           {
    327             FT_Int      k = subglyph->arg1;
    328             FT_UInt     l = subglyph->arg2;
    329             FT_Vector*  p1;
    330             FT_Vector*  p2;
    331 
    332 
    333             if ( start_point + k >= num_base_points         ||
    334                                l >= (FT_UInt)num_new_points )
    335             {
    336               error = AF_Err_Invalid_Composite;
    337               goto Exit;
    338             }
    339 
    340             l += num_base_points;
    341 
    342             /* for now, only use the current point coordinates;    */
    343             /* we may consider another approach in the near future */
    344             p1 = gloader->base.outline.points + start_point + k;
    345             p2 = gloader->base.outline.points + start_point + l;
    346 
    347             x = p1->x - p2->x;
    348             y = p1->y - p2->y;
    349           }
    350           else
    351           {
    352             x = FT_MulFix( subglyph->arg1, hints->x_scale ) + hints->x_delta;
    353             y = FT_MulFix( subglyph->arg2, hints->y_scale ) + hints->y_delta;
    354 
    355             x = FT_PIX_ROUND( x );
    356             y = FT_PIX_ROUND( y );
    357           }
    358 
    359           {
    360             FT_Outline  dummy = gloader->base.outline;
    361 
    362 
    363             dummy.points  += num_base_points;
    364             dummy.n_points = (short)num_new_points;
    365 
    366             FT_Outline_Translate( &dummy, x, y );
    367           }
    368         }
    369       }
    370       break;
    371 
    372     default:
    373       /* we don't support other formats (yet?) */
    374       error = AF_Err_Unimplemented_Feature;
    375     }
    376 
    377   Hint_Metrics:
    378     if ( depth == 0 )
    379     {
    380       FT_BBox    bbox;
    381       FT_Vector  vvector;
    382 
    383 
    384       vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX;
    385       vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY;
    386       vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale );
    387       vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale );
    388 
    389       /* transform the hinted outline if needed */
    390       if ( loader->transformed )
    391       {
    392         FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix );
    393         FT_Vector_Transform( &vvector, &loader->trans_matrix );
    394       }
    395 #if 1
    396       /* we must translate our final outline by -pp1.x and compute */
    397       /* the new metrics                                           */
    398       if ( loader->pp1.x )
    399         FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 );
    400 #endif
    401       FT_Outline_Get_CBox( &gloader->base.outline, &bbox );
    402 
    403       bbox.xMin = FT_PIX_FLOOR( bbox.xMin );
    404       bbox.yMin = FT_PIX_FLOOR( bbox.yMin );
    405       bbox.xMax = FT_PIX_CEIL(  bbox.xMax );
    406       bbox.yMax = FT_PIX_CEIL(  bbox.yMax );
    407 
    408       slot->metrics.width        = bbox.xMax - bbox.xMin;
    409       slot->metrics.height       = bbox.yMax - bbox.yMin;
    410       slot->metrics.horiBearingX = bbox.xMin;
    411       slot->metrics.horiBearingY = bbox.yMax;
    412 
    413       slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x );
    414       slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y );
    415 
    416       /* for mono-width fonts (like Andale, Courier, etc.) we need */
    417       /* to keep the original rounded advance width; ditto for     */
    418       /* digits if all have the same advance width                 */
    419 #if 0
    420       if ( !FT_IS_FIXED_WIDTH( slot->face ) )
    421         slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
    422       else
    423         slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
    424                                                x_scale );
    425 #else
    426       if ( FT_IS_FIXED_WIDTH( slot->face )                              ||
    427            ( af_face_globals_is_digit( loader->globals, glyph_index ) &&
    428              metrics->digits_have_same_width                          ) )
    429       {
    430         slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance,
    431                                                metrics->scaler.x_scale );
    432 
    433         /* Set delta values to 0.  Otherwise code that uses them is */
    434         /* going to ruin the fixed advance width.                   */
    435         slot->lsb_delta = 0;
    436         slot->rsb_delta = 0;
    437       }
    438       else
    439       {
    440         /* non-spacing glyphs must stay as-is */
    441         if ( slot->metrics.horiAdvance )
    442           slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x;
    443       }
    444 #endif
    445 
    446       slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance,
    447                                               metrics->scaler.y_scale );
    448 
    449       slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance );
    450       slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance );
    451 
    452       /* now copy outline into glyph slot */
    453       FT_GlyphLoader_Rewind( internal->loader );
    454       error = FT_GlyphLoader_CopyPoints( internal->loader, gloader );
    455       if ( error )
    456         goto Exit;
    457 
    458       slot->outline = internal->loader->base.outline;
    459       slot->format  = FT_GLYPH_FORMAT_OUTLINE;
    460     }
    461 
    462 #ifdef DEBUG_HINTER
    463     af_debug_hinter = hinter;
    464 #endif
    465 
    466   Exit:
    467     return error;
    468   }
    469 
    470 
    471   FT_LOCAL_DEF( FT_Error )
    472   af_loader_load_glyph( AF_Loader  loader,
    473                         FT_Face    face,
    474                         FT_UInt    gindex,
    475                         FT_UInt32  load_flags )
    476   {
    477     FT_Error      error;
    478     FT_Size       size = face->size;
    479     AF_ScalerRec  scaler;
    480 
    481 
    482     if ( !size )
    483       return AF_Err_Invalid_Argument;
    484 
    485     FT_ZERO( &scaler );
    486 
    487     scaler.face    = face;
    488     scaler.x_scale = size->metrics.x_scale;
    489     scaler.x_delta = 0;  /* XXX: TODO: add support for sub-pixel hinting */
    490     scaler.y_scale = size->metrics.y_scale;
    491     scaler.y_delta = 0;  /* XXX: TODO: add support for sub-pixel hinting */
    492 
    493     scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags );
    494     scaler.flags       = 0;  /* XXX: fix this */
    495 
    496     error = af_loader_reset( loader, face );
    497     if ( !error )
    498     {
    499       AF_ScriptMetrics  metrics;
    500       FT_UInt           options = 0;
    501 
    502 
    503 #ifdef FT_OPTION_AUTOFIT2
    504       /* XXX: undocumented hook to activate the latin2 hinter */
    505       if ( load_flags & ( 1UL << 20 ) )
    506         options = 2;
    507 #endif
    508 
    509       error = af_face_globals_get_metrics( loader->globals, gindex,
    510                                            options, &metrics );
    511       if ( !error )
    512       {
    513         loader->metrics = metrics;
    514 
    515         if ( metrics->clazz->script_metrics_scale )
    516           metrics->clazz->script_metrics_scale( metrics, &scaler );
    517         else
    518           metrics->scaler = scaler;
    519 
    520         load_flags |=  FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM;
    521         load_flags &= ~FT_LOAD_RENDER;
    522 
    523         if ( metrics->clazz->script_hints_init )
    524         {
    525           error = metrics->clazz->script_hints_init( &loader->hints,
    526                                                      metrics );
    527           if ( error )
    528             goto Exit;
    529         }
    530 
    531         error = af_loader_load_g( loader, &scaler, gindex, load_flags, 0 );
    532       }
    533     }
    534   Exit:
    535     return error;
    536   }
    537 
    538 
    539 /* END */
    540