| /***************************************************************************/ |
| /* */ |
| /* afloader.c */ |
| /* */ |
| /* Auto-fitter glyph loading routines (body). */ |
| /* */ |
| /* Copyright 2003, 2004, 2005, 2006 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 "afloader.h" |
| #include "afhints.h" |
| #include "afglobal.h" |
| #include "aflatin.h" |
| #include "aferrors.h" |
| |
| |
| FT_LOCAL_DEF( FT_Error ) |
| af_loader_init( AF_Loader loader, |
| FT_Memory memory ) |
| { |
| FT_Error error; |
| |
| |
| FT_ZERO( loader ); |
| |
| af_glyph_hints_init( &loader->hints, memory ); |
| |
| error = FT_GlyphLoader_New( memory, &loader->gloader ); |
| if ( !error ) |
| { |
| error = FT_GlyphLoader_CreateExtra( loader->gloader ); |
| if ( error ) |
| { |
| FT_GlyphLoader_Done( loader->gloader ); |
| loader->gloader = NULL; |
| } |
| } |
| return error; |
| } |
| |
| |
| FT_LOCAL_DEF( FT_Error ) |
| af_loader_reset( AF_Loader loader, |
| FT_Face face ) |
| { |
| FT_Error error = AF_Err_Ok; |
| |
| |
| loader->face = face; |
| loader->globals = (AF_FaceGlobals)face->autohint.data; |
| |
| FT_GlyphLoader_Rewind( loader->gloader ); |
| |
| if ( loader->globals == NULL ) |
| { |
| error = af_face_globals_new( face, &loader->globals ); |
| if ( !error ) |
| { |
| face->autohint.data = |
| (FT_Pointer)loader->globals; |
| face->autohint.finalizer = |
| (FT_Generic_Finalizer)af_face_globals_free; |
| } |
| } |
| |
| return error; |
| } |
| |
| |
| FT_LOCAL_DEF( void ) |
| af_loader_done( AF_Loader loader ) |
| { |
| af_glyph_hints_done( &loader->hints ); |
| |
| loader->face = NULL; |
| loader->globals = NULL; |
| |
| FT_GlyphLoader_Done( loader->gloader ); |
| loader->gloader = NULL; |
| } |
| |
| |
| static FT_Error |
| af_loader_load_g( AF_Loader loader, |
| AF_Scaler scaler, |
| FT_UInt glyph_index, |
| FT_Int32 load_flags, |
| FT_UInt depth ) |
| { |
| FT_Error error; |
| FT_Face face = loader->face; |
| FT_GlyphLoader gloader = loader->gloader; |
| AF_ScriptMetrics metrics = loader->metrics; |
| AF_GlyphHints hints = &loader->hints; |
| FT_GlyphSlot slot = face->glyph; |
| FT_Slot_Internal internal = slot->internal; |
| |
| |
| error = FT_Load_Glyph( face, glyph_index, load_flags ); |
| if ( error ) |
| goto Exit; |
| |
| loader->transformed = internal->glyph_transformed; |
| if ( loader->transformed ) |
| { |
| FT_Matrix inverse; |
| |
| |
| loader->trans_matrix = internal->glyph_matrix; |
| loader->trans_delta = internal->glyph_delta; |
| |
| inverse = loader->trans_matrix; |
| FT_Matrix_Invert( &inverse ); |
| FT_Vector_Transform( &loader->trans_delta, &inverse ); |
| } |
| |
| /* set linear metrics */ |
| slot->linearHoriAdvance = slot->metrics.horiAdvance; |
| slot->linearVertAdvance = slot->metrics.vertAdvance; |
| |
| switch ( slot->format ) |
| { |
| case FT_GLYPH_FORMAT_OUTLINE: |
| /* translate the loaded glyph when an internal transform is needed */ |
| if ( loader->transformed ) |
| FT_Outline_Translate( &slot->outline, |
| loader->trans_delta.x, |
| loader->trans_delta.y ); |
| |
| /* copy the outline points in the loader's current */ |
| /* extra points which is used to keep original glyph coordinates */ |
| error = FT_GLYPHLOADER_CHECK_POINTS( gloader, |
| slot->outline.n_points + 4, |
| slot->outline.n_contours ); |
| if ( error ) |
| goto Exit; |
| |
| FT_ARRAY_COPY( gloader->current.outline.points, |
| slot->outline.points, |
| slot->outline.n_points ); |
| |
| FT_ARRAY_COPY( gloader->current.extra_points, |
| slot->outline.points, |
| slot->outline.n_points ); |
| |
| FT_ARRAY_COPY( gloader->current.outline.contours, |
| slot->outline.contours, |
| slot->outline.n_contours ); |
| |
| FT_ARRAY_COPY( gloader->current.outline.tags, |
| slot->outline.tags, |
| slot->outline.n_points ); |
| |
| gloader->current.outline.n_points = slot->outline.n_points; |
| gloader->current.outline.n_contours = slot->outline.n_contours; |
| |
| /* compute original horizontal phantom points (and ignore */ |
| /* vertical ones) */ |
| loader->pp1.x = hints->x_delta; |
| loader->pp1.y = hints->y_delta; |
| loader->pp2.x = FT_MulFix( slot->metrics.horiAdvance, |
| hints->x_scale ) + hints->x_delta; |
| loader->pp2.y = hints->y_delta; |
| |
| /* be sure to check for spacing glyphs */ |
| if ( slot->outline.n_points == 0 ) |
| goto Hint_Metrics; |
| |
| /* now load the slot image into the auto-outline and run the */ |
| /* automatic hinting process */ |
| metrics->clazz->script_hints_apply( hints, |
| &gloader->current.outline, |
| metrics ); |
| |
| /* we now need to hint the metrics according to the change in */ |
| /* width/positioning that occured during the hinting process */ |
| { |
| FT_Pos old_advance, old_rsb, old_lsb, new_lsb; |
| FT_Pos pp1x_uh, pp2x_uh; |
| AF_AxisHints axis = &hints->axis[AF_DIMENSION_HORZ]; |
| AF_Edge edge1 = axis->edges; /* leftmost edge */ |
| AF_Edge edge2 = edge1 + |
| axis->num_edges - 1; /* rightmost edge */ |
| |
| #ifndef AF_USE_WARPER |
| if ( axis->num_edges > 1 ) |
| { |
| old_advance = loader->pp2.x; |
| old_rsb = old_advance - edge2->opos; |
| old_lsb = edge1->opos; |
| new_lsb = edge1->pos; |
| |
| /* remember unhinted values to later account */ |
| /* for rounding errors */ |
| |
| pp1x_uh = new_lsb - old_lsb; |
| pp2x_uh = edge2->pos + old_rsb; |
| |
| /* prefer too much space over too little space */ |
| /* for very small sizes */ |
| |
| if ( old_lsb < 24 ) |
| pp1x_uh -= 5; |
| |
| if ( old_rsb < 24 ) |
| pp2x_uh += 5; |
| |
| loader->pp1.x = FT_PIX_ROUND( pp1x_uh ); |
| loader->pp2.x = FT_PIX_ROUND( pp2x_uh ); |
| |
| slot->lsb_delta = loader->pp1.x - pp1x_uh; |
| slot->rsb_delta = loader->pp2.x - pp2x_uh; |
| |
| #if 0 |
| /* try to fix certain bad advance computations */ |
| if ( loader->pp2.x + loader->pp1.x == edge2->pos && old_rsb > 4 ) |
| loader->pp2.x += 64; |
| #endif |
| |
| } |
| else |
| #endif /* !AF_USE_WARPER */ |
| { |
| loader->pp1.x = FT_PIX_ROUND( loader->pp1.x ); |
| loader->pp2.x = FT_PIX_ROUND( loader->pp2.x ); |
| } |
| } |
| |
| /* good, we simply add the glyph to our loader's base */ |
| FT_GlyphLoader_Add( gloader ); |
| break; |
| |
| case FT_GLYPH_FORMAT_COMPOSITE: |
| { |
| FT_UInt nn, num_subglyphs = slot->num_subglyphs; |
| FT_UInt num_base_subgs, start_point; |
| FT_SubGlyph subglyph; |
| |
| |
| start_point = gloader->base.outline.n_points; |
| |
| /* first of all, copy the subglyph descriptors in the glyph loader */ |
| error = FT_GlyphLoader_CheckSubGlyphs( gloader, num_subglyphs ); |
| if ( error ) |
| goto Exit; |
| |
| FT_ARRAY_COPY( gloader->current.subglyphs, |
| slot->subglyphs, |
| num_subglyphs ); |
| |
| gloader->current.num_subglyphs = num_subglyphs; |
| num_base_subgs = gloader->base.num_subglyphs; |
| |
| /* now, read each subglyph independently */ |
| for ( nn = 0; nn < num_subglyphs; nn++ ) |
| { |
| FT_Vector pp1, pp2; |
| FT_Pos x, y; |
| FT_UInt num_points, num_new_points, num_base_points; |
| |
| |
| /* gloader.current.subglyphs can change during glyph loading due */ |
| /* to re-allocation -- we must recompute the current subglyph on */ |
| /* each iteration */ |
| subglyph = gloader->base.subglyphs + num_base_subgs + nn; |
| |
| pp1 = loader->pp1; |
| pp2 = loader->pp2; |
| |
| num_base_points = gloader->base.outline.n_points; |
| |
| error = af_loader_load_g( loader, scaler, subglyph->index, |
| load_flags, depth + 1 ); |
| if ( error ) |
| goto Exit; |
| |
| /* recompute subglyph pointer */ |
| subglyph = gloader->base.subglyphs + num_base_subgs + nn; |
| |
| if ( subglyph->flags & FT_SUBGLYPH_FLAG_USE_MY_METRICS ) |
| { |
| pp1 = loader->pp1; |
| pp2 = loader->pp2; |
| } |
| else |
| { |
| loader->pp1 = pp1; |
| loader->pp2 = pp2; |
| } |
| |
| num_points = gloader->base.outline.n_points; |
| num_new_points = num_points - num_base_points; |
| |
| /* now perform the transform required for this subglyph */ |
| |
| if ( subglyph->flags & ( FT_SUBGLYPH_FLAG_SCALE | |
| FT_SUBGLYPH_FLAG_XY_SCALE | |
| FT_SUBGLYPH_FLAG_2X2 ) ) |
| { |
| FT_Vector* cur = gloader->base.outline.points + |
| num_base_points; |
| FT_Vector* org = gloader->base.extra_points + |
| num_base_points; |
| FT_Vector* limit = cur + num_new_points; |
| |
| |
| for ( ; cur < limit; cur++, org++ ) |
| { |
| FT_Vector_Transform( cur, &subglyph->transform ); |
| FT_Vector_Transform( org, &subglyph->transform ); |
| } |
| } |
| |
| /* apply offset */ |
| |
| if ( !( subglyph->flags & FT_SUBGLYPH_FLAG_ARGS_ARE_XY_VALUES ) ) |
| { |
| FT_Int k = subglyph->arg1; |
| FT_UInt l = subglyph->arg2; |
| FT_Vector* p1; |
| FT_Vector* p2; |
| |
| |
| if ( start_point + k >= num_base_points || |
| l >= (FT_UInt)num_new_points ) |
| { |
| error = AF_Err_Invalid_Composite; |
| goto Exit; |
| } |
| |
| l += num_base_points; |
| |
| /* for now, only use the current point coordinates; */ |
| /* we may consider another approach in the near future */ |
| p1 = gloader->base.outline.points + start_point + k; |
| p2 = gloader->base.outline.points + start_point + l; |
| |
| x = p1->x - p2->x; |
| y = p1->y - p2->y; |
| } |
| else |
| { |
| x = FT_MulFix( subglyph->arg1, hints->x_scale ) + hints->x_delta; |
| y = FT_MulFix( subglyph->arg2, hints->y_scale ) + hints->y_delta; |
| |
| x = FT_PIX_ROUND( x ); |
| y = FT_PIX_ROUND( y ); |
| } |
| |
| { |
| FT_Outline dummy = gloader->base.outline; |
| |
| |
| dummy.points += num_base_points; |
| dummy.n_points = (short)num_new_points; |
| |
| FT_Outline_Translate( &dummy, x, y ); |
| } |
| } |
| } |
| break; |
| |
| default: |
| /* we don't support other formats (yet?) */ |
| error = AF_Err_Unimplemented_Feature; |
| } |
| |
| Hint_Metrics: |
| if ( depth == 0 ) |
| { |
| FT_BBox bbox; |
| |
| |
| /* transform the hinted outline if needed */ |
| if ( loader->transformed ) |
| FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix ); |
| |
| /* we must translate our final outline by -pp1.x and compute */ |
| /* the new metrics */ |
| if ( loader->pp1.x ) |
| FT_Outline_Translate( &gloader->base.outline, -loader->pp1.x, 0 ); |
| |
| FT_Outline_Get_CBox( &gloader->base.outline, &bbox ); |
| |
| bbox.xMin = FT_PIX_FLOOR( bbox.xMin ); |
| bbox.yMin = FT_PIX_FLOOR( bbox.yMin ); |
| bbox.xMax = FT_PIX_CEIL( bbox.xMax ); |
| bbox.yMax = FT_PIX_CEIL( bbox.yMax ); |
| |
| slot->metrics.width = bbox.xMax - bbox.xMin; |
| slot->metrics.height = bbox.yMax - bbox.yMin; |
| slot->metrics.horiBearingX = bbox.xMin; |
| slot->metrics.horiBearingY = bbox.yMax; |
| |
| /* for mono-width fonts (like Andale, Courier, etc.) we need */ |
| /* to keep the original rounded advance width */ |
| #if 0 |
| if ( !FT_IS_FIXED_WIDTH( slot->face ) ) |
| slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; |
| else |
| slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, |
| x_scale ); |
| #else |
| if ( !FT_IS_FIXED_WIDTH( slot->face ) ) |
| slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; |
| else |
| slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, |
| metrics->scaler.x_scale ); |
| #endif |
| |
| slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance ); |
| |
| /* now copy outline into glyph slot */ |
| FT_GlyphLoader_Rewind( internal->loader ); |
| error = FT_GlyphLoader_CopyPoints( internal->loader, gloader ); |
| if ( error ) |
| goto Exit; |
| |
| slot->outline = internal->loader->base.outline; |
| slot->format = FT_GLYPH_FORMAT_OUTLINE; |
| } |
| |
| #ifdef DEBUG_HINTER |
| af_debug_hinter = hinter; |
| #endif |
| |
| Exit: |
| return error; |
| } |
| |
| |
| FT_LOCAL_DEF( FT_Error ) |
| af_loader_load_glyph( AF_Loader loader, |
| FT_Face face, |
| FT_UInt gindex, |
| FT_UInt32 load_flags ) |
| { |
| FT_Error error; |
| FT_Size size = face->size; |
| AF_ScalerRec scaler; |
| |
| |
| if ( !size ) |
| return AF_Err_Invalid_Argument; |
| |
| FT_ZERO( &scaler ); |
| |
| scaler.face = face; |
| scaler.x_scale = size->metrics.x_scale; |
| scaler.x_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ |
| scaler.y_scale = size->metrics.y_scale; |
| scaler.y_delta = 0; /* XXX: TODO: add support for sub-pixel hinting */ |
| |
| scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags ); |
| scaler.flags = 0; /* XXX: fix this */ |
| |
| error = af_loader_reset( loader, face ); |
| if ( !error ) |
| { |
| AF_ScriptMetrics metrics; |
| |
| |
| error = af_face_globals_get_metrics( loader->globals, gindex, |
| &metrics ); |
| if ( !error ) |
| { |
| loader->metrics = metrics; |
| |
| if ( metrics->clazz->script_metrics_scale ) |
| metrics->clazz->script_metrics_scale( metrics, &scaler ); |
| else |
| metrics->scaler = scaler; |
| |
| load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM; |
| load_flags &= ~FT_LOAD_RENDER; |
| |
| error = metrics->clazz->script_hints_init( &loader->hints, metrics ); |
| if ( error ) |
| goto Exit; |
| |
| error = af_loader_load_g( loader, &scaler, gindex, load_flags, 0 ); |
| } |
| } |
| Exit: |
| return error; |
| } |
| |
| |
| /* END */ |