/* * Class: FreetypeFont * Method: loadGlyph0 * Signature: (JI)V */ JNIEXPORT void JNICALL Java_sage_FreetypeFont_loadGlyph0 (JNIEnv *env, jobject jo, jlong fontPtr, jint glyphCode) { //FT_Face face = (FT_Face) facePtr; FTDataStruct* fontData = (FTDataStruct*)(intptr_t) fontPtr; FT_Activate_Size(fontData->sizePtr); int error = FT_Load_Glyph(fontData->facePtr, glyphCode, FT_LOAD_FORCE_AUTOHINT); if ((fontData->style & FT_STYLE_FLAG_BOLD) != 0) { // Apply bold effect if (fontData->facePtr->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { /* some reasonable strength */ FT_Pos strength = FT_MulFix(fontData->facePtr->units_per_EM, fontData->facePtr->size->metrics.y_scale ) / 42; FT_BBox bbox_before, bbox_after; // The bounding box code was what XBMC was using to do this calculation; but the // examples in the freetype library use the *4 math below which then doesn't clip // the text when we render it. // FT_Outline_Get_CBox(&fontData->facePtr->glyph->outline, &bbox_before); FT_Outline_Embolden(&fontData->facePtr->glyph->outline, strength); // ignore error // FT_Outline_Get_CBox(&fontData->facePtr->glyph->outline, &bbox_after); // FT_Pos dx = bbox_after.xMax - bbox_before.xMax; // FT_Pos dy = bbox_after.yMax - bbox_before.yMax; FT_Pos dx = strength * 4; FT_Pos dy = dx; if (fontData->facePtr->glyph->advance.x) fontData->facePtr->glyph->advance.x += dx; if (fontData->facePtr->glyph->advance.y) fontData->facePtr->glyph->advance.y += dy; fontData->facePtr->glyph->metrics.width += dx; fontData->facePtr->glyph->metrics.height += dy; fontData->facePtr->glyph->metrics.horiBearingY += dy; fontData->facePtr->glyph->metrics.horiAdvance += dx; fontData->facePtr->glyph->metrics.vertBearingX -= dx / 2; fontData->facePtr->glyph->metrics.vertBearingY += dy; fontData->facePtr->glyph->metrics.vertAdvance += dy; } } if ((fontData->style & FT_STYLE_FLAG_ITALIC) != 0) { // Apply italics effect if (fontData->facePtr->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { /* For italic, simply apply a shear transform, with an angle */ /* of about 12 degrees. */ FT_Matrix transform; transform.xx = 0x10000L; transform.yx = 0x00000L; transform.xy = 0x06000L; transform.yy = 0x10000L; FT_BBox bbox_before, bbox_after; FT_Outline_Get_CBox(&fontData->facePtr->glyph->outline, &bbox_before); FT_Outline_Transform(&fontData->facePtr->glyph->outline, &transform); FT_Outline_Get_CBox(&fontData->facePtr->glyph->outline, &bbox_after); FT_Pos dx = bbox_after.xMax - bbox_before.xMax; FT_Pos dy = bbox_after.yMax - bbox_before.yMax; fontData->facePtr->glyph->metrics.width += dx; fontData->facePtr->glyph->metrics.height += dy; } } }
// aScale is intended for a 16.16 x/y_scale of an FT_Size_Metrics static inline FT_Long ScaleRoundDesignUnits(FT_Short aDesignMetric, FT_Fixed aScale) { FT_Long fixed26dot6 = FT_MulFix(aDesignMetric, aScale); return ROUND_26_6_TO_INT(fixed26dot6); }
pfr_slot_load( FT_GlyphSlot pfrslot, /* PFR_Slot */ FT_Size pfrsize, /* PFR_Size */ FT_UInt gindex, FT_Int32 load_flags ) { PFR_Slot slot = (PFR_Slot)pfrslot; PFR_Size size = (PFR_Size)pfrsize; FT_Error error; PFR_Face face = (PFR_Face)pfrslot->face; PFR_Char gchar; FT_Outline* outline = &pfrslot->outline; FT_ULong gps_offset; if ( gindex > 0 ) gindex--; /* check that the glyph index is correct */ FT_ASSERT( gindex < face->phy_font.num_chars ); /* try to load an embedded bitmap */ if ( ( load_flags & ( FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ) ) == 0 ) { error = pfr_slot_load_bitmap( slot, size, gindex ); if ( error == 0 ) goto Exit; } if ( load_flags & FT_LOAD_SBITS_ONLY ) { error = PFR_Err_Invalid_Argument; goto Exit; } gchar = face->phy_font.chars + gindex; pfrslot->format = FT_GLYPH_FORMAT_OUTLINE; outline->n_points = 0; outline->n_contours = 0; gps_offset = face->header.gps_section_offset; /* load the glyph outline (FT_LOAD_NO_RECURSE isn't supported) */ error = pfr_glyph_load( &slot->glyph, face->root.stream, gps_offset, gchar->gps_offset, gchar->gps_size ); if ( !error ) { FT_BBox cbox; FT_Glyph_Metrics* metrics = &pfrslot->metrics; FT_Pos advance; FT_Int em_metrics, em_outline; FT_Bool scaling; scaling = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ); /* copy outline data */ *outline = slot->glyph.loader->base.outline; outline->flags &= ~FT_OUTLINE_OWNER; outline->flags |= FT_OUTLINE_REVERSE_FILL; if ( size && pfrsize->metrics.y_ppem < 24 ) outline->flags |= FT_OUTLINE_HIGH_PRECISION; /* compute the advance vector */ metrics->horiAdvance = 0; metrics->vertAdvance = 0; advance = gchar->advance; em_metrics = face->phy_font.metrics_resolution; em_outline = face->phy_font.outline_resolution; if ( em_metrics != em_outline ) advance = FT_MulDiv( advance, em_outline, em_metrics ); if ( face->phy_font.flags & PFR_PHY_VERTICAL ) metrics->vertAdvance = advance; else metrics->horiAdvance = advance; pfrslot->linearHoriAdvance = metrics->horiAdvance; pfrslot->linearVertAdvance = metrics->vertAdvance; /* make-up vertical metrics(?) */ metrics->vertBearingX = 0; metrics->vertBearingY = 0; /* Apply the font matrix, if any. */ /* TODO: Test existing fonts with unusual matrix */ /* whether we have to adjust Units per EM. */ { FT_Matrix font_matrix; font_matrix.xx = face->log_font.matrix[0] << 8; font_matrix.yx = face->log_font.matrix[1] << 8; font_matrix.xy = face->log_font.matrix[2] << 8; font_matrix.yy = face->log_font.matrix[3] << 8; FT_Outline_Transform( outline, &font_matrix ); } /* scale when needed */ if ( scaling ) { FT_Int n; FT_Fixed x_scale = pfrsize->metrics.x_scale; FT_Fixed y_scale = pfrsize->metrics.y_scale; FT_Vector* vec = outline->points; /* scale outline points */ for ( n = 0; n < outline->n_points; n++, vec++ ) { vec->x = FT_MulFix( vec->x, x_scale ); vec->y = FT_MulFix( vec->y, y_scale ); } /* scale the advance */ metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale ); metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale ); } /* compute the rest of the metrics */ FT_Outline_Get_CBox( outline, &cbox ); metrics->width = cbox.xMax - cbox.xMin; metrics->height = cbox.yMax - cbox.yMin; metrics->horiBearingX = cbox.xMin; metrics->horiBearingY = cbox.yMax - metrics->height; } Exit: return error; }
pfr_slot_load( PFR_Slot slot, PFR_Size size, FT_UInt gindex, FT_Int32 load_flags ) { FT_Error error; PFR_Face face = (PFR_Face)slot->root.face; PFR_Char gchar; FT_Outline* outline = &slot->root.outline; FT_ULong gps_offset; if (gindex > 0) gindex--; /* check that the glyph index is correct */ FT_ASSERT( gindex < face->phy_font.num_chars ); /* try to load an embedded bitmap */ if ( ( load_flags & ( FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ) ) == 0 ) { error = pfr_slot_load_bitmap( slot, size, gindex ); if ( error == 0 ) goto Exit; } gchar = face->phy_font.chars + gindex; slot->root.format = FT_GLYPH_FORMAT_OUTLINE; outline->n_points = 0; outline->n_contours = 0; gps_offset = face->header.gps_section_offset; /* load the glyph outline (FT_LOAD_NO_RECURSE isn't supported) */ error = pfr_glyph_load( &slot->glyph, face->root.stream, gps_offset, gchar->gps_offset, gchar->gps_size ); if ( !error ) { FT_BBox cbox; FT_Glyph_Metrics* metrics = &slot->root.metrics; FT_Pos advance; FT_Int em_metrics, em_outline; FT_Bool scaling; scaling = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ); /* copy outline data */ *outline = slot->glyph.loader->base.outline; outline->flags &= ~FT_OUTLINE_OWNER; outline->flags |= FT_OUTLINE_REVERSE_FILL; if ( size && size->root.metrics.y_ppem < 24 ) outline->flags |= FT_OUTLINE_HIGH_PRECISION; /* compute the advance vector */ metrics->horiAdvance = 0; metrics->vertAdvance = 0; advance = gchar->advance; em_metrics = face->phy_font.metrics_resolution; em_outline = face->phy_font.outline_resolution; if ( em_metrics != em_outline ) advance = FT_MulDiv( advance, em_outline, em_metrics ); if ( face->phy_font.flags & PFR_PHY_VERTICAL ) metrics->vertAdvance = advance; else metrics->horiAdvance = advance; slot->root.linearHoriAdvance = metrics->horiAdvance; slot->root.linearVertAdvance = metrics->vertAdvance; /* make-up vertical metrics(?) */ metrics->vertBearingX = 0; metrics->vertBearingY = 0; /* scale when needed */ if ( scaling ) { FT_Int n; FT_Fixed x_scale = size->root.metrics.x_scale; FT_Fixed y_scale = size->root.metrics.y_scale; FT_Vector* vec = outline->points; /* scale outline points */ for ( n = 0; n < outline->n_points; n++, vec++ ) { vec->x = FT_MulFix( vec->x, x_scale ); vec->y = FT_MulFix( vec->y, y_scale ); } /* scale the advance */ metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale ); metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale ); } /* compute the rest of the metrics */ FT_Outline_Get_CBox( outline, &cbox ); metrics->width = cbox.xMax - cbox.xMin; metrics->height = cbox.yMax - cbox.yMin; metrics->horiBearingX = cbox.xMin; metrics->horiBearingY = cbox.yMax - metrics->height; } Exit: return error; }
static void af_warper_compute_line_best( AF_Warper warper, FT_Fixed scale, FT_Pos delta, FT_Pos xx1, FT_Pos xx2, AF_WarpScore base_distort, AF_Segment segments, FT_UInt num_segments ) { FT_Int idx_min, idx_max, idx0; FT_UInt nn; AF_WarpScore scores[65]; for ( nn = 0; nn < 65; nn++ ) scores[nn] = 0; idx0 = xx1 - warper->t1; /* compute minimum and maximum indices */ { FT_Pos xx1min = warper->x1min; FT_Pos xx1max = warper->x1max; FT_Pos w = xx2 - xx1; if ( xx1min + w < warper->x2min ) xx1min = warper->x2min - w; xx1max = warper->x1max; if ( xx1max + w > warper->x2max ) xx1max = warper->x2max - w; idx_min = xx1min - warper->t1; idx_max = xx1max - warper->t1; if ( idx_min < 0 || idx_min > idx_max || idx_max > 64 ) { FT_TRACE5(( "invalid indices:\n" " min=%d max=%d, xx1=%ld xx2=%ld,\n" " x1min=%ld x1max=%ld, x2min=%ld x2max=%ld\n", idx_min, idx_max, xx1, xx2, warper->x1min, warper->x1max, warper->x2min, warper->x2max )); return; } } for ( nn = 0; nn < num_segments; nn++ ) { FT_Pos len = segments[nn].max_coord - segments[nn].min_coord; FT_Pos y0 = FT_MulFix( segments[nn].pos, scale ) + delta; FT_Pos y = y0 + ( idx_min - idx0 ); FT_Int idx; /* score the length of the segments for the given range */ for ( idx = idx_min; idx <= idx_max; idx++, y++ ) scores[idx] += af_warper_weights[y & 63] * len; } /* find best score */ { FT_Int idx; for ( idx = idx_min; idx <= idx_max; idx++ ) { AF_WarpScore score = scores[idx]; AF_WarpScore distort = base_distort + ( idx - idx0 ); if ( score > warper->best_score || ( score == warper->best_score && distort < warper->best_distort ) ) { warper->best_score = score; warper->best_distort = distort; warper->best_scale = scale; warper->best_delta = delta + ( idx - idx0 ); } } } }
static FT_Error af_loader_embolden_glyph_in_slot( AF_Loader loader, FT_Face face, AF_StyleMetrics style_metrics ) { FT_Error error = FT_Err_Ok; FT_GlyphSlot slot = face->glyph; AF_FaceGlobals globals = loader->globals; AF_WritingSystemClass writing_system_class; FT_Size_Metrics* size_metrics = &face->size->internal->autohint_metrics; FT_Pos stdVW = 0; FT_Pos stdHW = 0; FT_Bool size_changed = size_metrics->x_ppem != globals->stem_darkening_for_ppem; FT_Fixed em_size = af_intToFixed( face->units_per_EM ); FT_Fixed em_ratio = FT_DivFix( af_intToFixed( 1000 ), em_size ); FT_Matrix scale_down_matrix = { 0x10000L, 0, 0, 0x10000L }; /* Skip stem darkening for broken fonts. */ if ( !face->units_per_EM ) { error = FT_ERR( Corrupted_Font_Header ); goto Exit; } /* * We depend on the writing system (script analyzers) to supply * standard widths for the script of the glyph we are looking at. If * it can't deliver, stem darkening is disabled. */ writing_system_class = af_writing_system_classes[style_metrics->style_class->writing_system]; if ( writing_system_class->style_metrics_getstdw ) writing_system_class->style_metrics_getstdw( style_metrics, &stdHW, &stdVW ); else { error = FT_ERR( Unimplemented_Feature ); goto Exit; } if ( size_changed || ( stdVW > 0 && stdVW != globals->standard_vertical_width ) ) { FT_Fixed darken_by_font_units_x, darken_x; darken_by_font_units_x = af_intToFixed( af_loader_compute_darkening( loader, face, stdVW ) ); darken_x = FT_DivFix( FT_MulFix( darken_by_font_units_x, size_metrics->x_scale ), em_ratio ); globals->standard_vertical_width = stdVW; globals->stem_darkening_for_ppem = size_metrics->x_ppem; globals->darken_x = af_fixedToInt( darken_x ); } if ( size_changed || ( stdHW > 0 && stdHW != globals->standard_horizontal_width ) ) { FT_Fixed darken_by_font_units_y, darken_y; darken_by_font_units_y = af_intToFixed( af_loader_compute_darkening( loader, face, stdHW ) ); darken_y = FT_DivFix( FT_MulFix( darken_by_font_units_y, size_metrics->y_scale ), em_ratio ); globals->standard_horizontal_width = stdHW; globals->stem_darkening_for_ppem = size_metrics->x_ppem; globals->darken_y = af_fixedToInt( darken_y ); /* * Scale outlines down on the Y-axis to keep them inside their blue * zones. The stronger the emboldening, the stronger the downscaling * (plus heuristical padding to prevent outlines still falling out * their zones due to rounding). * * Reason: `FT_Outline_Embolden' works by shifting the rightmost * points of stems farther to the right, and topmost points farther * up. This positions points on the Y-axis outside their * pre-computed blue zones and leads to distortion when applying the * hints in the code further below. Code outside this emboldening * block doesn't know we are presenting it with modified outlines the * analyzer didn't see! * * An unfortunate side effect of downscaling is that the emboldening * effect is slightly decreased. The loss becomes more pronounced * versus the CFF driver at smaller sizes, e.g., at 9ppem and below. */ globals->scale_down_factor = FT_DivFix( em_size - ( darken_by_font_units_y + af_intToFixed( 8 ) ), em_size ); } FT_Outline_EmboldenXY( &slot->outline, globals->darken_x, globals->darken_y ); scale_down_matrix.yy = globals->scale_down_factor; FT_Outline_Transform( &slot->outline, &scale_down_matrix ); Exit: return error; }
tt_size_reset( TT_Size size ) { TT_Face face; FT_Error error = FT_Err_Ok; FT_Size_Metrics* metrics; size->ttmetrics.valid = FALSE; face = (TT_Face)size->root.face; metrics = &size->metrics; /* copy the result from base layer */ *metrics = size->root.metrics; if ( metrics->x_ppem < 1 || metrics->y_ppem < 1 ) return FT_THROW( Invalid_PPem ); /* This bit flag, if set, indicates that the ppems must be */ /* rounded to integers. Nearly all TrueType fonts have this bit */ /* set, as hinting won't work really well otherwise. */ /* */ if ( face->header.Flags & 8 ) { metrics->x_scale = FT_DivFix( metrics->x_ppem << 6, face->root.units_per_EM ); metrics->y_scale = FT_DivFix( metrics->y_ppem << 6, face->root.units_per_EM ); metrics->ascender = FT_PIX_ROUND( FT_MulFix( face->root.ascender, metrics->y_scale ) ); metrics->descender = FT_PIX_ROUND( FT_MulFix( face->root.descender, metrics->y_scale ) ); metrics->height = FT_PIX_ROUND( FT_MulFix( face->root.height, metrics->y_scale ) ); metrics->max_advance = FT_PIX_ROUND( FT_MulFix( face->root.max_advance_width, metrics->x_scale ) ); } /* compute new transformation */ if ( metrics->x_ppem >= metrics->y_ppem ) { size->ttmetrics.scale = metrics->x_scale; size->ttmetrics.ppem = metrics->x_ppem; size->ttmetrics.x_ratio = 0x10000L; size->ttmetrics.y_ratio = FT_DivFix( metrics->y_ppem, metrics->x_ppem ); } else { size->ttmetrics.scale = metrics->y_scale; size->ttmetrics.ppem = metrics->y_ppem; size->ttmetrics.x_ratio = FT_DivFix( metrics->x_ppem, metrics->y_ppem ); size->ttmetrics.y_ratio = 0x10000L; } #ifdef TT_USE_BYTECODE_INTERPRETER size->cvt_ready = -1; #endif /* TT_USE_BYTECODE_INTERPRETER */ if ( !error ) size->ttmetrics.valid = TRUE; return error; }
int PG_Font::GetFontHeight() { return my_internaldata->FaceCache ? FT_CEIL(FT_MulFix(my_internaldata->FaceCache->Face->height, my_internaldata->FaceCache->Face->size->metrics.y_scale)) : 0; }
tt_size_reset( TT_Size size ) { TT_Face face; FT_Error error = TT_Err_Ok; FT_Size_Metrics* metrics; size->ttmetrics.valid = FALSE; face = (TT_Face)size->root.face; metrics = &size->metrics; /* copy the result from base layer */ *metrics = size->root.metrics; if ( metrics->x_ppem < 1 || metrics->y_ppem < 1 ) return TT_Err_Invalid_PPem; /* This bit flag, if set, indicates that the ppems must be */ /* rounded to integers. Nearly all TrueType fonts have this bit */ /* set, as hinting won't work really well otherwise. */ /* */ if ( face->header.Flags & 8 ) { metrics->x_scale = FT_DivFix( metrics->x_ppem << 6, face->root.units_per_EM ); metrics->y_scale = FT_DivFix( metrics->y_ppem << 6, face->root.units_per_EM ); metrics->ascender = FT_PIX_ROUND( FT_MulFix( face->root.ascender, metrics->y_scale ) ); metrics->descender = FT_PIX_ROUND( FT_MulFix( face->root.descender, metrics->y_scale ) ); metrics->height = FT_PIX_ROUND( FT_MulFix( face->root.height, metrics->y_scale ) ); metrics->max_advance = FT_PIX_ROUND( FT_MulFix( face->root.max_advance_width, metrics->x_scale ) ); } /* compute new transformation */ if ( metrics->x_ppem >= metrics->y_ppem ) { size->ttmetrics.scale = metrics->x_scale; size->ttmetrics.ppem = metrics->x_ppem; size->ttmetrics.x_ratio = 0x10000L; size->ttmetrics.y_ratio = FT_MulDiv( metrics->y_ppem, 0x10000L, metrics->x_ppem ); } else { size->ttmetrics.scale = metrics->y_scale; size->ttmetrics.ppem = metrics->y_ppem; size->ttmetrics.x_ratio = FT_MulDiv( metrics->x_ppem, 0x10000L, metrics->y_ppem ); size->ttmetrics.y_ratio = 0x10000L; } #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER { FT_UInt i; /* Scale the cvt values to the new ppem. */ /* We use by default the y ppem to scale the CVT. */ for ( i = 0; i < size->cvt_size; i++ ) size->cvt[i] = FT_MulFix( face->cvt[i], size->ttmetrics.scale ); /* All twilight points are originally zero */ for ( i = 0; i < (FT_UInt)size->twilight.n_points; i++ ) { size->twilight.org[i].x = 0; size->twilight.org[i].y = 0; size->twilight.cur[i].x = 0; size->twilight.cur[i].y = 0; } /* clear storage area */ for ( i = 0; i < (FT_UInt)size->storage_size; i++ ) size->storage[i] = 0; size->GS = tt_default_graphics_state; error = tt_size_run_prep( size ); } #endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ if ( !error ) size->ttmetrics.valid = TRUE; return error; }
bool Font::insertGlyph(Char character) { if(!m_size) { insertPlaceholderGlyph(character); return false; } FT_UInt glyphIndex = FT_Get_Char_Index(m_size->face, character); if(!glyphIndex) { insertPlaceholderGlyph(character); return false; } FT_Int32 flags = FT_LOAD_TARGET_LIGHT; if(m_info.weight != 0) { flags |= FT_LOAD_FORCE_AUTOHINT; } if(FT_Load_Glyph(m_size->face, glyphIndex, flags)) { insertPlaceholderGlyph(character); return false; } FT_GlyphSlot ftGlyph = m_size->face->glyph; if(m_info.weight != 0 && ftGlyph->format == FT_GLYPH_FORMAT_OUTLINE) { FT_Pos strength = FT_MulFix(m_size->face->units_per_EM, m_size->metrics.y_scale) / 24 * m_info.weight / 4; FT_Outline_Embolden(&ftGlyph->outline, strength); } if(FT_Render_Glyph(ftGlyph, FT_RENDER_MODE_NORMAL)) { insertPlaceholderGlyph(character); return false; } // Fill in info for this glyph. Glyph & glyph = m_glyphs[character]; glyph.index = glyphIndex; glyph.size.x = ftGlyph->bitmap.width; glyph.size.y = ftGlyph->bitmap.rows; glyph.advance.x = float(ftGlyph->linearHoriAdvance) / 65536.f; glyph.advance.y = float(ftGlyph->linearVertAdvance) / 65536.f; glyph.lsb_delta = ftGlyph->lsb_delta; glyph.rsb_delta = ftGlyph->rsb_delta; glyph.draw_offset.x = ftGlyph->bitmap_left; glyph.draw_offset.y = ftGlyph->bitmap_top - ftGlyph->bitmap.rows; glyph.uv_start = Vec2f(0.f); glyph.uv_end = Vec2f(0.f); glyph.texture = 0; // Some glyphs like spaces have a size of 0... if(glyph.size.x != 0 && glyph.size.y != 0) { Image imgGlyph; imgGlyph.create(size_t(glyph.size.x), size_t(glyph.size.y), Image::Format_A8); FT_Bitmap & srcBitmap = ftGlyph->bitmap; arx_assert(srcBitmap.pitch >= 0); arx_assert(unsigned(srcBitmap.pitch) == unsigned(srcBitmap.width)); // Copy pixels unsigned char * src = srcBitmap.buffer; unsigned char * dst = imgGlyph.getData(); memcpy(dst, src, glyph.size.x * glyph.size.y); Vec2i offset; if(!m_textures->insertImage(imgGlyph, glyph.texture, offset)) { LogWarning << "Could not upload glyph for character U+" << std::hex << character << " (" << util::encode<util::UTF8>(character) << ") in font " << m_info.name; insertPlaceholderGlyph(character); return false; } // Compute UV mapping for each glyph. const float textureSize = float(m_textures->getTextureSize()); glyph.uv_start = Vec2f(offset) / Vec2f(textureSize); glyph.uv_end = Vec2f(offset + glyph.size) / Vec2f(textureSize); } return true; }
int PG_Font::GetFontDescender() { return my_internaldata->FaceCache ? FT_CEIL(FT_MulFix(my_internaldata->FaceCache->Face->descender, my_internaldata->FaceCache->Face->size->metrics.y_scale)) : 0; }
FT_LOCAL_DEF FT_Error CID_Load_Glyph( CID_GlyphSlot glyph, CID_Size size, FT_Int glyph_index, FT_Int load_flags ) { FT_Error error; T1_Decoder decoder; CID_Face face = (CID_Face)glyph->root.face; FT_Bool hinting; PSAux_Interface* psaux = (PSAux_Interface*)face->psaux; FT_Matrix font_matrix; FT_Vector font_offset; if ( load_flags & FT_LOAD_NO_RECURSE ) load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING; glyph->x_scale = size->root.metrics.x_scale; glyph->y_scale = size->root.metrics.y_scale; glyph->root.outline.n_points = 0; glyph->root.outline.n_contours = 0; hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 && ( load_flags & FT_LOAD_NO_HINTING ) == 0 ); glyph->root.format = ft_glyph_format_outline; { error = psaux->t1_decoder_funcs->init( &decoder, (FT_Face)face, (FT_Size)size, (FT_GlyphSlot)glyph, 0, /* glyph names -- XXX */ 0, /* blend == 0 */ hinting, cid_load_glyph ); /* set up the decoder */ decoder.builder.no_recurse = FT_BOOL( ( ( load_flags & FT_LOAD_NO_RECURSE ) != 0 ) ); error = cid_load_glyph( &decoder, glyph_index ); font_matrix = decoder.font_matrix; font_offset = decoder.font_offset; /* save new glyph tables */ psaux->t1_decoder_funcs->done( &decoder ); } /* now, set the metrics -- this is rather simple, as */ /* the left side bearing is the xMin, and the top side */ /* bearing the yMax */ if ( !error ) { glyph->root.outline.flags &= ft_outline_owner; glyph->root.outline.flags |= ft_outline_reverse_fill; /* for composite glyphs, return only left side bearing and */ /* advance width */ if ( load_flags & FT_LOAD_NO_RECURSE ) { FT_Slot_Internal internal = glyph->root.internal; glyph->root.metrics.horiBearingX = decoder.builder.left_bearing.x; glyph->root.metrics.horiAdvance = decoder.builder.advance.x; internal->glyph_matrix = font_matrix; internal->glyph_delta = font_offset; internal->glyph_transformed = 1; } else { FT_BBox cbox; FT_Glyph_Metrics* metrics = &glyph->root.metrics; /* copy the _unscaled_ advance width */ metrics->horiAdvance = decoder.builder.advance.x; glyph->root.linearHoriAdvance = decoder.builder.advance.x; glyph->root.internal->glyph_transformed = 0; /* make up vertical metrics */ metrics->vertBearingX = 0; metrics->vertBearingY = 0; metrics->vertAdvance = 0; glyph->root.linearVertAdvance = 0; glyph->root.format = ft_glyph_format_outline; if ( size && size->root.metrics.y_ppem < 24 ) glyph->root.outline.flags |= ft_outline_high_precision; /* apply the font matrix */ FT_Outline_Transform( &glyph->root.outline, &font_matrix ); FT_Outline_Translate( &glyph->root.outline, font_offset.x, font_offset.y ); if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ) { /* scale the outline and the metrics */ FT_Int n; FT_Outline* cur = decoder.builder.base; FT_Vector* vec = cur->points; FT_Fixed x_scale = glyph->x_scale; FT_Fixed y_scale = glyph->y_scale; /* First of all, scale the points */ if ( !hinting ) for ( n = cur->n_points; n > 0; n--, vec++ ) { vec->x = FT_MulFix( vec->x, x_scale ); vec->y = FT_MulFix( vec->y, y_scale ); } FT_Outline_Get_CBox( &glyph->root.outline, &cbox ); /* Then scale the metrics */ metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale ); metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale ); metrics->vertBearingX = FT_MulFix( metrics->vertBearingX, x_scale ); metrics->vertBearingY = FT_MulFix( metrics->vertBearingY, y_scale ); if ( hinting ) { metrics->horiAdvance = ( metrics->horiAdvance + 32 ) & -64; metrics->vertAdvance = ( metrics->vertAdvance + 32 ) & -64; metrics->vertBearingX = ( metrics->vertBearingX + 32 ) & -64; metrics->vertBearingY = ( metrics->vertBearingY + 32 ) & -64; } } /* compute the other metrics */ FT_Outline_Get_CBox( &glyph->root.outline, &cbox ); /* grid fit the bounding box if necessary */ if ( hinting ) { cbox.xMin &= -64; cbox.yMin &= -64; cbox.xMax = ( cbox.xMax + 63 ) & -64; cbox.yMax = ( cbox.yMax + 63 ) & -64; } metrics->width = cbox.xMax - cbox.xMin; metrics->height = cbox.yMax - cbox.yMin; metrics->horiBearingX = cbox.xMin; metrics->horiBearingY = cbox.yMax; } } return error; }
af_warper_compute( AF_Warper warper, AF_GlyphHints hints, AF_Dimension dim, FT_Fixed *a_scale, FT_Pos *a_delta ) { AF_AxisHints axis; AF_Point points; FT_Fixed org_scale; FT_Pos org_delta; FT_UInt nn, num_points, num_segments; FT_Int X1, X2; FT_Int w; AF_WarpScore base_distort; AF_Segment segments; /* get original scaling transformation */ if ( dim == AF_DIMENSION_VERT ) { org_scale = hints->y_scale; org_delta = hints->y_delta; } else { org_scale = hints->x_scale; org_delta = hints->x_delta; } warper->best_scale = org_scale; warper->best_delta = org_delta; warper->best_score = 0; warper->best_distort = 0; axis = &hints->axis[dim]; segments = axis->segments; num_segments = axis->num_segments; points = hints->points; num_points = hints->num_points; *a_scale = org_scale; *a_delta = org_delta; /* get X1 and X2, minimum and maximum in original coordinates */ if ( axis->num_segments < 1 ) return; #if 1 X1 = X2 = points[0].fx; for ( nn = 1; nn < num_points; nn++ ) { FT_Int X = points[nn].fx; if ( X < X1 ) X1 = X; if ( X > X2 ) X2 = X; } #else X1 = X2 = segments[0].pos; for ( nn = 1; nn < num_segments; nn++ ) { FT_Int X = segments[nn].pos; if ( X < X1 ) X1 = X; if ( X > X2 ) X2 = X; } #endif if ( X1 >= X2 ) return; warper->x1 = FT_MulFix( X1, org_scale ) + org_delta; warper->x2 = FT_MulFix( X2, org_scale ) + org_delta; warper->t1 = AF_WARPER_FLOOR( warper->x1 ); warper->t2 = AF_WARPER_CEIL( warper->x2 ); warper->x1min = warper->x1 & ~31; warper->x1max = warper->x1min + 32; warper->x2min = warper->x2 & ~31; warper->x2max = warper->x2min + 32; if ( warper->x1max > warper->x2 ) warper->x1max = warper->x2; if ( warper->x2min < warper->x1 ) warper->x2min = warper->x1; warper->w0 = warper->x2 - warper->x1; if ( warper->w0 <= 64 ) { warper->x1max = warper->x1; warper->x2min = warper->x2; } warper->wmin = warper->x2min - warper->x1max; warper->wmax = warper->x2max - warper->x1min; if ( warper->wmin < warper->w0 - 32 ) warper->wmin = warper->w0 - 32; if ( warper->wmax > warper->w0 + 32 ) warper->wmax = warper->w0 + 32; if ( warper->wmin < warper->w0 * 3 / 4 ) warper->wmin = warper->w0 * 3 / 4; if ( warper->wmax > warper->w0 * 5 / 4 ) warper->wmax = warper->w0 * 5 / 4; /* warper->wmin = warper->wmax = warper->w0; */ for ( w = warper->wmin; w <= warper->wmax; w++ ) { FT_Fixed new_scale; FT_Pos new_delta; FT_Pos xx1, xx2; xx1 = warper->x1; xx2 = warper->x2; if ( w >= warper->w0 ) { xx1 -= w - warper->w0; if ( xx1 < warper->x1min ) { xx2 += warper->x1min - xx1; xx1 = warper->x1min; } } else { xx1 -= w - warper->w0; if ( xx1 > warper->x1max ) { xx2 -= xx1 - warper->x1max; xx1 = warper->x1max; } } if ( xx1 < warper->x1 ) base_distort = warper->x1 - xx1; else base_distort = xx1 - warper->x1; if ( xx2 < warper->x2 ) base_distort += warper->x2 - xx2; else base_distort += xx2 - warper->x2; base_distort *= 10; new_scale = org_scale + FT_DivFix( w - warper->w0, X2 - X1 ); new_delta = xx1 - FT_MulFix( X1, new_scale ); af_warper_compute_line_best( warper, new_scale, new_delta, xx1, xx2, base_distort, segments, num_segments ); } *a_scale = warper->best_scale; *a_delta = warper->best_delta; }
t1_decoder_parse_charstrings( T1_Decoder decoder, FT_Byte* charstring_base, FT_UInt charstring_len ) { FT_Error error; T1_Decoder_Zone zone; FT_Byte* ip; FT_Byte* limit; T1_Builder builder = &decoder->builder; FT_Pos x, y, orig_x, orig_y; T1_Hints_Funcs hinter; /* we don't want to touch the source code -- use macro trick */ #define start_point t1_builder_start_point #define check_points t1_builder_check_points #define add_point t1_builder_add_point #define add_point1 t1_builder_add_point1 #define add_contour t1_builder_add_contour #define close_contour t1_builder_close_contour /* First of all, initialize the decoder */ decoder->top = decoder->stack; decoder->zone = decoder->zones; zone = decoder->zones; builder->parse_state = T1_Parse_Start; hinter = (T1_Hints_Funcs)builder->hints_funcs; zone->base = charstring_base; limit = zone->limit = charstring_base + charstring_len; ip = zone->cursor = zone->base; error = PSaux_Err_Ok; x = orig_x = builder->pos_x; y = orig_y = builder->pos_y; /* begin hints recording session, if any */ if ( hinter ) hinter->open( hinter->hints ); /* now, execute loop */ while ( ip < limit ) { FT_Long* top = decoder->top; T1_Operator op = op_none; FT_Long value = 0; /*********************************************************************/ /* */ /* Decode operator or operand */ /* */ /* */ /* first of all, decompress operator or value */ switch ( *ip++ ) { case 1: op = op_hstem; break; case 3: op = op_vstem; break; case 4: op = op_vmoveto; break; case 5: op = op_rlineto; break; case 6: op = op_hlineto; break; case 7: op = op_vlineto; break; case 8: op = op_rrcurveto; break; case 9: op = op_closepath; break; case 10: op = op_callsubr; break; case 11: op = op_return; break; case 13: op = op_hsbw; break; case 14: op = op_endchar; break; case 15: /* undocumented, obsolete operator */ op = op_none; break; case 21: op = op_rmoveto; break; case 22: op = op_hmoveto; break; case 30: op = op_vhcurveto; break; case 31: op = op_hvcurveto; break; case 12: if ( ip > limit ) { FT_ERROR(( "t1_decoder_parse_charstrings: " "invalid escape (12+EOF)\n" )); goto Syntax_Error; } switch ( *ip++ ) { case 0: op = op_dotsection; break; case 1: op = op_vstem3; break; case 2: op = op_hstem3; break; case 6: op = op_seac; break; case 7: op = op_sbw; break; case 12: op = op_div; break; case 16: op = op_callothersubr; break; case 17: op = op_pop; break; case 33: op = op_setcurrentpoint; break; default: FT_ERROR(( "t1_decoder_parse_charstrings: " "invalid escape (12+%d)\n", ip[-1] )); goto Syntax_Error; } break; case 255: /* four bytes integer */ if ( ip + 4 > limit ) { FT_ERROR(( "t1_decoder_parse_charstrings: " "unexpected EOF in integer\n" )); goto Syntax_Error; } value = (FT_Int32)( ((FT_Long)ip[0] << 24) | ((FT_Long)ip[1] << 16) | ((FT_Long)ip[2] << 8 ) | ip[3] ); ip += 4; break; default: if ( ip[-1] >= 32 ) { if ( ip[-1] < 247 ) value = (FT_Long)ip[-1] - 139; else { if ( ++ip > limit ) { FT_ERROR(( "t1_decoder_parse_charstrings: " )); FT_ERROR(( "unexpected EOF in integer\n" )); goto Syntax_Error; } if ( ip[-2] < 251 ) value = ( ( (FT_Long)ip[-2] - 247 ) << 8 ) + ip[-1] + 108; else value = -( ( ( (FT_Long)ip[-2] - 251 ) << 8 ) + ip[-1] + 108 ); } } else { FT_ERROR(( "t1_decoder_parse_charstrings: " "invalid byte (%d)\n", ip[-1] )); goto Syntax_Error; } } /*********************************************************************/ /* */ /* Push value on stack, or process operator */ /* */ /* */ if ( op == op_none ) { if ( top - decoder->stack >= T1_MAX_CHARSTRINGS_OPERANDS ) { FT_ERROR(( "t1_decoder_parse_charstrings: stack overflow!\n" )); goto Syntax_Error; } FT_TRACE4(( " %ld", value )); *top++ = value; decoder->top = top; } else if ( op == op_callothersubr ) /* callothersubr */ { FT_TRACE4(( " callothersubr" )); if ( top - decoder->stack < 2 ) goto Stack_Underflow; top -= 2; switch ( (FT_Int)top[1] ) { case 1: /* start flex feature */ if ( top[0] != 0 ) goto Unexpected_OtherSubr; decoder->flex_state = 1; decoder->num_flex_vectors = 0; if ( start_point( builder, x, y ) || check_points( builder, 6 ) ) goto Fail; break; case 2: /* add flex vectors */ { FT_Int idx; if ( top[0] != 0 ) goto Unexpected_OtherSubr; /* note that we should not add a point for index 0; */ /* this will move our current position to the flex */ /* point without adding any point to the outline */ idx = decoder->num_flex_vectors++; if ( idx > 0 && idx < 7 ) add_point( builder, x, y, (FT_Byte)( idx == 3 || idx == 6 ) ); } break; case 0: /* end flex feature */ if ( top[0] != 3 ) goto Unexpected_OtherSubr; if ( decoder->flex_state == 0 || decoder->num_flex_vectors != 7 ) { FT_ERROR(( "t1_decoder_parse_charstrings: " "unexpected flex end\n" )); goto Syntax_Error; } /* now consume the remaining `pop pop setcurpoint' */ if ( ip + 6 > limit || ip[0] != 12 || ip[1] != 17 || /* pop */ ip[2] != 12 || ip[3] != 17 || /* pop */ ip[4] != 12 || ip[5] != 33 ) /* setcurpoint */ { FT_ERROR(( "t1_decoder_parse_charstrings: " "invalid flex charstring\n" )); goto Syntax_Error; } ip += 6; decoder->flex_state = 0; break; case 3: /* change hints */ if ( top[0] != 1 ) goto Unexpected_OtherSubr; /* eat the following `pop' */ if ( ip + 2 > limit ) { FT_ERROR(( "t1_decoder_parse_charstrings: " "invalid escape (12+%d)\n", ip[-1] )); goto Syntax_Error; } if ( ip[0] != 12 || ip[1] != 17 ) { FT_ERROR(( "t1_decoder_parse_charstrings: " )); FT_ERROR(( "`pop' expected, found (%d %d)\n", ip[0], ip[1] )); goto Syntax_Error; } ip += 2; if ( hinter ) hinter->reset( hinter->hints, builder->current->n_points ); break; case 12: case 13: /* counter control hints, clear stack */ top = decoder->stack; break; case 14: case 15: case 16: case 17: case 18: /* multiple masters */ { PS_Blend blend = decoder->blend; FT_UInt num_points, nn, mm; FT_Long* delta; FT_Long* values; if ( !blend ) { FT_ERROR(( "t1_decoder_parse_charstrings: " )); FT_ERROR(( "unexpected multiple masters operator!\n" )); goto Syntax_Error; } num_points = (FT_UInt)top[1] - 13 + ( top[1] == 18 ); if ( top[0] != (FT_Int)( num_points * blend->num_designs ) ) { FT_ERROR(( "t1_decoder_parse_charstrings: " )); FT_ERROR(( "incorrect number of mm arguments\n" )); goto Syntax_Error; } top -= blend->num_designs * num_points; if ( top < decoder->stack ) goto Stack_Underflow; /* we want to compute: */ /* */ /* a0*w0 + a1*w1 + ... + ak*wk */ /* */ /* but we only have the a0, a1-a0, a2-a0, .. ak-a0 */ /* however, given that w0 + w1 + ... + wk == 1, we can */ /* rewrite it easily as: */ /* */ /* a0 + (a1-a0)*w1 + (a2-a0)*w2 + .. + (ak-a0)*wk */ /* */ /* where k == num_designs-1 */ /* */ /* I guess that's why it's written in this `compact' */ /* form. */ /* */ delta = top + num_points; values = top; for ( nn = 0; nn < num_points; nn++ ) { FT_Long tmp = values[0]; for ( mm = 1; mm < blend->num_designs; mm++ ) tmp += FT_MulFix( *delta++, blend->weight_vector[mm] ); *values++ = tmp; } /* note that `top' will be incremented later by calls to `pop' */ break; } default: Unexpected_OtherSubr: FT_ERROR(( "t1_decoder_parse_charstrings: " "invalid othersubr [%d %d]!\n", top[0], top[1] )); goto Syntax_Error; } decoder->top = top; } else /* general operator */ { FT_Int num_args = t1_args_count[op]; if ( top - decoder->stack < num_args ) goto Stack_Underflow; top -= num_args; switch ( op ) { case op_endchar: FT_TRACE4(( " endchar" )); close_contour( builder ); /* close hints recording session */ if ( hinter ) { if (hinter->close( hinter->hints, builder->current->n_points )) goto Syntax_Error; /* apply hints to the loaded glyph outline now */ hinter->apply( hinter->hints, builder->current, (PSH_Globals) builder->hints_globals, decoder->hint_mode ); } /* add current outline to the glyph slot */ FT_GlyphLoader_Add( builder->loader ); /* return now! */ FT_TRACE4(( "\n\n" )); return PSaux_Err_Ok; case op_hsbw: FT_TRACE4(( " hsbw" )); builder->parse_state = T1_Parse_Have_Width; builder->left_bearing.x += top[0]; builder->advance.x = top[1]; builder->advance.y = 0; orig_x = builder->last.x = x = builder->pos_x + top[0]; orig_y = builder->last.y = y = builder->pos_y; FT_UNUSED( orig_y ); /* the `metrics_only' indicates that we only want to compute */ /* the glyph's metrics (lsb + advance width), not load the */ /* rest of it; so exit immediately */ if ( builder->metrics_only ) return PSaux_Err_Ok; break; case op_seac: /* return immediately after the processing */ return t1operator_seac( decoder, top[0], top[1], top[2], (FT_Int)top[3], (FT_Int)top[4] ); case op_sbw: FT_TRACE4(( " sbw" )); builder->parse_state = T1_Parse_Have_Width; builder->left_bearing.x += top[0]; builder->left_bearing.y += top[1]; builder->advance.x = top[2]; builder->advance.y = top[3]; builder->last.x = x = builder->pos_x + top[0]; builder->last.y = y = builder->pos_y + top[1]; /* the `metrics_only' indicates that we only want to compute */ /* the glyph's metrics (lsb + advance width), not load the */ /* rest of it; so exit immediately */ if ( builder->metrics_only ) return PSaux_Err_Ok; break; case op_closepath: FT_TRACE4(( " closepath" )); close_contour( builder ); if ( !( builder->parse_state == T1_Parse_Have_Path || builder->parse_state == T1_Parse_Have_Moveto ) ) goto Syntax_Error; builder->parse_state = T1_Parse_Have_Width; break; case op_hlineto: FT_TRACE4(( " hlineto" )); if ( start_point( builder, x, y ) ) goto Fail; x += top[0]; goto Add_Line; case op_hmoveto: FT_TRACE4(( " hmoveto" )); x += top[0]; if ( !decoder->flex_state ) { if ( builder->parse_state == T1_Parse_Start ) goto Syntax_Error; builder->parse_state = T1_Parse_Have_Moveto; } break; case op_hvcurveto: FT_TRACE4(( " hvcurveto" )); if ( start_point( builder, x, y ) || check_points( builder, 3 ) ) goto Fail; x += top[0]; add_point( builder, x, y, 0 ); x += top[1]; y += top[2]; add_point( builder, x, y, 0 ); y += top[3]; add_point( builder, x, y, 1 ); break; case op_rlineto: FT_TRACE4(( " rlineto" )); if ( start_point( builder, x, y ) ) goto Fail; x += top[0]; y += top[1]; Add_Line: if ( add_point1( builder, x, y ) ) goto Fail; break; case op_rmoveto: FT_TRACE4(( " rmoveto" )); x += top[0]; y += top[1]; if ( !decoder->flex_state ) { if ( builder->parse_state == T1_Parse_Start ) goto Syntax_Error; builder->parse_state = T1_Parse_Have_Moveto; } break; case op_rrcurveto: FT_TRACE4(( " rcurveto" )); if ( start_point( builder, x, y ) || check_points( builder, 3 ) ) goto Fail; x += top[0]; y += top[1]; add_point( builder, x, y, 0 ); x += top[2]; y += top[3]; add_point( builder, x, y, 0 ); x += top[4]; y += top[5]; add_point( builder, x, y, 1 ); break; case op_vhcurveto: FT_TRACE4(( " vhcurveto" )); if ( start_point( builder, x, y ) || check_points( builder, 3 ) ) goto Fail; y += top[0]; add_point( builder, x, y, 0 ); x += top[1]; y += top[2]; add_point( builder, x, y, 0 ); x += top[3]; add_point( builder, x, y, 1 ); break; case op_vlineto: FT_TRACE4(( " vlineto" )); if ( start_point( builder, x, y ) ) goto Fail; y += top[0]; goto Add_Line; case op_vmoveto: FT_TRACE4(( " vmoveto" )); y += top[0]; if ( !decoder->flex_state ) { if ( builder->parse_state == T1_Parse_Start ) goto Syntax_Error; builder->parse_state = T1_Parse_Have_Moveto; } break; case op_div: FT_TRACE4(( " div" )); if ( top[1] ) { *top = top[0] / top[1]; ++top; } else { FT_ERROR(( "t1_decoder_parse_charstrings: division by 0\n" )); goto Syntax_Error; } break; case op_callsubr: { FT_Int idx; FT_TRACE4(( " callsubr" )); idx = (FT_Int)top[0]; if ( idx < 0 || idx >= (FT_Int)decoder->num_subrs ) { FT_ERROR(( "t1_decoder_parse_charstrings: " "invalid subrs index\n" )); goto Syntax_Error; } if ( zone - decoder->zones >= T1_MAX_SUBRS_CALLS ) { FT_ERROR(( "t1_decoder_parse_charstrings: " "too many nested subrs\n" )); goto Syntax_Error; } zone->cursor = ip; /* save current instruction pointer */ zone++; /* The Type 1 driver stores subroutines without the seed bytes. */ /* The CID driver stores subroutines with seed bytes. This */ /* case is taken care of when decoder->subrs_len == 0. */ zone->base = decoder->subrs[idx]; if ( decoder->subrs_len ) zone->limit = zone->base + decoder->subrs_len[idx]; else { /* We are using subroutines from a CID font. We must adjust */ /* for the seed bytes. */ zone->base += ( decoder->lenIV >= 0 ? decoder->lenIV : 0 ); zone->limit = decoder->subrs[idx + 1]; } zone->cursor = zone->base; if ( !zone->base ) { FT_ERROR(( "t1_decoder_parse_charstrings: " "invoking empty subrs!\n" )); goto Syntax_Error; } decoder->zone = zone; ip = zone->base; limit = zone->limit; break; } case op_pop: FT_TRACE4(( " pop" )); /* theoretically, the arguments are already on the stack */ top++; break; case op_return: FT_TRACE4(( " return" )); if ( zone <= decoder->zones ) { FT_ERROR(( "t1_decoder_parse_charstrings: unexpected return\n" )); goto Syntax_Error; } zone--; ip = zone->cursor; limit = zone->limit; decoder->zone = zone; break; case op_dotsection: FT_TRACE4(( " dotsection" )); break; case op_hstem: FT_TRACE4(( " hstem" )); /* record horizontal hint */ if ( hinter ) { /* top[0] += builder->left_bearing.y; */ hinter->stem( hinter->hints, 1, top ); } break; case op_hstem3: FT_TRACE4(( " hstem3" )); /* record horizontal counter-controlled hints */ if ( hinter ) hinter->stem3( hinter->hints, 1, top ); break; case op_vstem: FT_TRACE4(( " vstem" )); /* record vertical hint */ if ( hinter ) { top[0] += orig_x; hinter->stem( hinter->hints, 0, top ); } break; case op_vstem3: FT_TRACE4(( " vstem3" )); /* record vertical counter-controlled hints */ if ( hinter ) { FT_Pos dx = orig_x; top[0] += dx; top[2] += dx; top[4] += dx; hinter->stem3( hinter->hints, 0, top ); } break; case op_setcurrentpoint: FT_TRACE4(( " setcurrentpoint" )); FT_ERROR(( "t1_decoder_parse_charstrings: " )); FT_ERROR(( "unexpected `setcurrentpoint'\n" )); goto Syntax_Error; default: FT_ERROR(( "t1_decoder_parse_charstrings: " "unhandled opcode %d\n", op )); goto Syntax_Error; } decoder->top = top; } /* general operator processing */ } /* while ip < limit */ FT_TRACE4(( "..end..\n\n" )); Fail: return error; Syntax_Error: return PSaux_Err_Syntax_Error; Stack_Underflow: return PSaux_Err_Stack_Underflow; }
af_loader_load_glyph( AF_Loader loader, AF_Module module, FT_Face face, FT_UInt glyph_index, FT_Int32 load_flags ) { FT_Error error; FT_Size size = face->size; FT_Size_Internal size_internal = size->internal; FT_GlyphSlot slot = face->glyph; FT_Slot_Internal slot_internal = slot->internal; FT_GlyphLoader gloader = slot_internal->loader; AF_GlyphHints hints = loader->hints; AF_ScalerRec scaler; AF_StyleMetrics style_metrics; FT_UInt style_options = AF_STYLE_NONE_DFLT; AF_StyleClass style_class; AF_WritingSystemClass writing_system_class; if ( !size ) return FT_THROW( Invalid_Size_Handle ); FT_ZERO( &scaler ); if ( !size_internal->autohint_metrics.x_scale || size_internal->autohint_mode != FT_LOAD_TARGET_MODE( load_flags ) ) { /* switching between hinting modes usually means different scaling */ /* values; this later on enforces recomputation of everything */ /* related to the current size */ size_internal->autohint_mode = FT_LOAD_TARGET_MODE( load_flags ); size_internal->autohint_metrics = size->metrics; #ifdef AF_CONFIG_OPTION_TT_SIZE_METRICS { FT_Size_Metrics* size_metrics = &size_internal->autohint_metrics; /* set metrics to integer values and adjust scaling accordingly; */ /* this is the same setup as with TrueType fonts, cf. function */ /* `tt_size_reset' in file `ttobjs.c' */ size_metrics->ascender = FT_PIX_ROUND( FT_MulFix( face->ascender, size_metrics->y_scale ) ); size_metrics->descender = FT_PIX_ROUND( FT_MulFix( face->descender, size_metrics->y_scale ) ); size_metrics->height = FT_PIX_ROUND( FT_MulFix( face->height, size_metrics->y_scale ) ); size_metrics->x_scale = FT_DivFix( size_metrics->x_ppem << 6, face->units_per_EM ); size_metrics->y_scale = FT_DivFix( size_metrics->y_ppem << 6, face->units_per_EM ); size_metrics->max_advance = FT_PIX_ROUND( FT_MulFix( face->max_advance_width, size_metrics->x_scale ) ); } #endif /* AF_CONFIG_OPTION_TT_SIZE_METRICS */ } /* * TODO: This code currently doesn't support fractional advance widths, * i.e., placing hinted glyphs at anything other than integer * x-positions. This is only relevant for the warper code, which * scales and shifts glyphs to optimize blackness of stems (hinting on * the x-axis by nature places things on pixel integers, hinting on the * y-axis only, i.e., LIGHT mode, doesn't touch the x-axis). The delta * values of the scaler would need to be adjusted. */ scaler.face = face; scaler.x_scale = size_internal->autohint_metrics.x_scale; scaler.x_delta = 0; scaler.y_scale = size_internal->autohint_metrics.y_scale; scaler.y_delta = 0; scaler.render_mode = FT_LOAD_TARGET_MODE( load_flags ); scaler.flags = 0; /* note that the fallback style can't be changed anymore */ /* after the first call of `af_loader_load_glyph' */ error = af_loader_reset( loader, module, face ); if ( error ) goto Exit; #ifdef FT_OPTION_AUTOFIT2 /* XXX: undocumented hook to activate the latin2 writing system. */ if ( load_flags & ( 1UL << 20 ) ) style_options = AF_STYLE_LTN2_DFLT; #endif /* * Glyphs (really code points) are assigned to scripts. Script * analysis is done lazily: For each glyph that passes through here, * the corresponding script analyzer is called, but returns immediately * if it has been run already. */ error = af_face_globals_get_metrics( loader->globals, glyph_index, style_options, &style_metrics ); if ( error ) goto Exit; style_class = style_metrics->style_class; writing_system_class = af_writing_system_classes[style_class->writing_system]; loader->metrics = style_metrics; if ( writing_system_class->style_metrics_scale ) writing_system_class->style_metrics_scale( style_metrics, &scaler ); else style_metrics->scaler = scaler; if ( writing_system_class->style_hints_init ) { error = writing_system_class->style_hints_init( hints, style_metrics ); if ( error ) goto Exit; } /* * Do the main work of `af_loader_load_glyph'. Note that we never have * to deal with composite glyphs as those get loaded into * FT_GLYPH_FORMAT_OUTLINE by the recursed `FT_Load_Glyph' function. * In the rare cases where FT_LOAD_NO_RECURSE is set, it implies * FT_LOAD_NO_SCALE and as such the auto-hinter is never called. */ load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_LINEAR_DESIGN; load_flags &= ~FT_LOAD_RENDER; error = FT_Load_Glyph( face, glyph_index, load_flags ); if ( error ) goto Exit; /* * Apply stem darkening (emboldening) here before hints are applied to * the outline. Glyphs are scaled down proportionally to the * emboldening so that curve points don't fall outside their * precomputed blue zones. * * Any emboldening done by the font driver (e.g., the CFF driver) * doesn't reach here because the autohinter loads the unprocessed * glyphs in font units for analysis (functions `af_*_metrics_init_*') * and then above to prepare it for the rasterizers by itself, * independently of the font driver. So emboldening must be done here, * within the autohinter. * * All glyphs to be autohinted pass through here one by one. The * standard widths can therefore change from one glyph to the next, * depending on what script a glyph is assigned to (each script has its * own set of standard widths and other metrics). The darkening amount * must therefore be recomputed for each size and * `standard_{vertical,horizontal}_width' change. * * Ignore errors and carry on without emboldening. * */ /* stem darkening only works well in `light' mode */ if ( scaler.render_mode == FT_RENDER_MODE_LIGHT && ( !face->internal->no_stem_darkening || ( face->internal->no_stem_darkening < 0 && !module->no_stem_darkening ) ) ) af_loader_embolden_glyph_in_slot( loader, face, style_metrics ); loader->transformed = slot_internal->glyph_transformed; if ( loader->transformed ) { FT_Matrix inverse; loader->trans_matrix = slot_internal->glyph_matrix; loader->trans_delta = slot_internal->glyph_delta; inverse = loader->trans_matrix; if ( !FT_Matrix_Invert( &inverse ) ) FT_Vector_Transform( &loader->trans_delta, &inverse ); } 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 ); /* 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 */ if ( writing_system_class->style_hints_apply ) { error = writing_system_class->style_hints_apply( glyph_index, hints, &gloader->base.outline, style_metrics ); if ( error ) goto Exit; } /* we now need to adjust the metrics according to the change in */ /* width/positioning that occurred during the hinting process */ if ( scaler.render_mode != FT_RENDER_MODE_LIGHT ) { AF_AxisHints axis = &hints->axis[AF_DIMENSION_HORZ]; if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) ) { AF_Edge edge1 = axis->edges; /* leftmost edge */ AF_Edge edge2 = edge1 + axis->num_edges - 1; /* rightmost edge */ FT_Pos old_rsb = loader->pp2.x - edge2->opos; /* loader->pp1.x is always zero at this point of time */ FT_Pos old_lsb = edge1->opos; /* - loader->pp1.x */ FT_Pos new_lsb = edge1->pos; /* remember unhinted values to later account */ /* for rounding errors */ FT_Pos pp1x_uh = new_lsb - old_lsb; FT_Pos pp2x_uh = edge2->pos + old_rsb; /* prefer too much space over too little space */ /* for very small sizes */ if ( old_lsb < 24 ) pp1x_uh -= 8; if ( old_rsb < 24 ) pp2x_uh += 8; loader->pp1.x = FT_PIX_ROUND( pp1x_uh ); loader->pp2.x = FT_PIX_ROUND( pp2x_uh ); if ( loader->pp1.x >= new_lsb && old_lsb > 0 ) loader->pp1.x -= 64; if ( loader->pp2.x <= edge2->pos && old_rsb > 0 ) loader->pp2.x += 64; slot->lsb_delta = loader->pp1.x - pp1x_uh; slot->rsb_delta = loader->pp2.x - pp2x_uh; } else { FT_Pos pp1x = loader->pp1.x; FT_Pos pp2x = loader->pp2.x; loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta ); loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta ); slot->lsb_delta = loader->pp1.x - pp1x; slot->rsb_delta = loader->pp2.x - pp2x; } } /* `light' mode uses integer advance widths */ /* but sets `lsb_delta' and `rsb_delta' */ else { FT_Pos pp1x = loader->pp1.x; FT_Pos pp2x = loader->pp2.x; loader->pp1.x = FT_PIX_ROUND( pp1x ); loader->pp2.x = FT_PIX_ROUND( pp2x ); slot->lsb_delta = loader->pp1.x - pp1x; slot->rsb_delta = loader->pp2.x - pp2x; } break; default: /* we don't support other formats (yet?) */ error = FT_THROW( Unimplemented_Feature ); } Hint_Metrics: { FT_BBox bbox; FT_Vector vvector; vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX; vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY; vvector.x = FT_MulFix( vvector.x, style_metrics->scaler.x_scale ); vvector.y = FT_MulFix( vvector.y, style_metrics->scaler.y_scale ); /* transform the hinted outline if needed */ if ( loader->transformed ) { FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix ); FT_Vector_Transform( &vvector, &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; slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x ); slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y ); /* for mono-width fonts (like Andale, Courier, etc.) we need */ /* to keep the original rounded advance width; ditto for */ /* digits if all have the same advance width */ if ( scaler.render_mode != FT_RENDER_MODE_LIGHT && ( FT_IS_FIXED_WIDTH( slot->face ) || ( af_face_globals_is_digit( loader->globals, glyph_index ) && style_metrics->digits_have_same_width ) ) ) { slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, style_metrics->scaler.x_scale ); /* Set delta values to 0. Otherwise code that uses them is */ /* going to ruin the fixed advance width. */ slot->lsb_delta = 0; slot->rsb_delta = 0; } else { /* non-spacing glyphs must stay as-is */ if ( slot->metrics.horiAdvance ) slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; } slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance, style_metrics->scaler.y_scale ); slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance ); slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance ); slot->format = FT_GLYPH_FORMAT_OUTLINE; } Exit: return error; }
static void BBox_Cubic_Check( FT_Pos y1, FT_Pos y2, FT_Pos y3, FT_Pos y4, FT_Pos* min, FT_Pos* max ) { /* always compare first and last points */ if ( y1 < *min ) *min = y1; else if ( y1 > *max ) *max = y1; if ( y4 < *min ) *min = y4; else if ( y4 > *max ) *max = y4; /* now, try to see if there are split points here */ if ( y1 <= y4 ) { /* flat or ascending arc test */ if ( y1 <= y2 && y2 <= y4 && y1 <= y3 && y3 <= y4 ) return; } else /* y1 > y4 */ { /* descending arc test */ if ( y1 >= y2 && y2 >= y4 && y1 >= y3 && y3 >= y4 ) return; } /* There are some split points. Find them. */ { FT_Pos a = y4 - 3*y3 + 3*y2 - y1; FT_Pos b = y3 - 2*y2 + y1; FT_Pos c = y2 - y1; FT_Pos d; FT_Fixed t; /* We need to solve `ax^2+2bx+c' here, without floating points! */ /* The trick is to normalize to a different representation in order */ /* to use our 16.16 fixed point routines. */ /* */ /* We compute FT_MulFix(b,b) and FT_MulFix(a,c) after normalization. */ /* These values must fit into a single 16.16 value. */ /* */ /* We normalize a, b, and c to `8.16' fixed float values to ensure */ /* that its product is held in a `16.16' value. */ { FT_ULong t1, t2; int shift = 0; /* The following computation is based on the fact that for */ /* any value `y', if `n' is the position of the most */ /* significant bit of `abs(y)' (starting from 0 for the */ /* least significant bit), then `y' is in the range */ /* */ /* -2^n..2^n-1 */ /* */ /* We want to shift `a', `b', and `c' concurrently in order */ /* to ensure that they all fit in 8.16 values, which maps */ /* to the integer range `-2^23..2^23-1'. */ /* */ /* Necessarily, we need to shift `a', `b', and `c' so that */ /* the most significant bit of its absolute values is at */ /* _most_ at position 23. */ /* */ /* We begin by computing `t1' as the bitwise `OR' of the */ /* absolute values of `a', `b', `c'. */ t1 = (FT_ULong)( ( a >= 0 ) ? a : -a ); t2 = (FT_ULong)( ( b >= 0 ) ? b : -b ); t1 |= t2; t2 = (FT_ULong)( ( c >= 0 ) ? c : -c ); t1 |= t2; /* Now we can be sure that the most significant bit of `t1' */ /* is the most significant bit of either `a', `b', or `c', */ /* depending on the greatest integer range of the particular */ /* variable. */ /* */ /* Next, we compute the `shift', by shifting `t1' as many */ /* times as necessary to move its MSB to position 23. This */ /* corresponds to a value of `t1' that is in the range */ /* 0x40_0000..0x7F_FFFF. */ /* */ /* Finally, we shift `a', `b', and `c' by the same amount. */ /* This ensures that all values are now in the range */ /* -2^23..2^23, i.e., they are now expressed as 8.16 */ /* fixed-float numbers. This also means that we are using */ /* 24 bits of precision to compute the zeros, independently */ /* of the range of the original polynomial coefficients. */ /* */ /* This algorithm should ensure reasonably accurate values */ /* for the zeros. Note that they are only expressed with */ /* 16 bits when computing the extrema (the zeros need to */ /* be in 0..1 exclusive to be considered part of the arc). */ if ( t1 == 0 ) /* all coefficients are 0! */ return; if ( t1 > 0x7FFFFFUL ) { do { shift++; t1 >>= 1; } while ( t1 > 0x7FFFFFUL ); /* this loses some bits of precision, but we use 24 of them */ /* for the computation anyway */ a >>= shift; b >>= shift; c >>= shift; } else if ( t1 < 0x400000UL ) { do { shift++; t1 <<= 1; } while ( t1 < 0x400000UL ); a <<= shift; b <<= shift; c <<= shift; } } /* handle a == 0 */ if ( a == 0 ) { if ( b != 0 ) { t = - FT_DivFix( c, b ) / 2; test_cubic_extrema( y1, y2, y3, y4, t, min, max ); } } else { /* solve the equation now */ d = FT_MulFix( b, b ) - FT_MulFix( a, c ); if ( d < 0 ) return; if ( d == 0 ) { /* there is a single split point at -b/a */ t = - FT_DivFix( b, a ); test_cubic_extrema( y1, y2, y3, y4, t, min, max ); } else { /* there are two solutions; we need to filter them */ d = FT_SqrtFixed( (FT_Int32)d ); t = - FT_DivFix( b - d, a ); test_cubic_extrema( y1, y2, y3, y4, t, min, max ); t = - FT_DivFix( b + d, a ); test_cubic_extrema( y1, y2, y3, y4, t, min, max ); } } }
af_loader_compute_darkening( AF_Loader loader, FT_Face face, FT_Pos standard_width ) { AF_Module module = loader->globals->module; FT_UShort units_per_EM; FT_Fixed ppem, em_ratio; FT_Fixed stem_width, stem_width_per_1000, scaled_stem, darken_amount; FT_Int log_base_2; FT_Int x1, y1, x2, y2, x3, y3, x4, y4; ppem = FT_MAX( af_intToFixed( 4 ), af_intToFixed( face->size->metrics.x_ppem ) ); units_per_EM = face->units_per_EM; em_ratio = FT_DivFix( af_intToFixed( 1000 ), af_intToFixed ( units_per_EM ) ); if ( em_ratio < af_floatToFixed( .01 ) ) { /* If something goes wrong, don't embolden. */ return 0; } x1 = module->darken_params[0]; y1 = module->darken_params[1]; x2 = module->darken_params[2]; y2 = module->darken_params[3]; x3 = module->darken_params[4]; y3 = module->darken_params[5]; x4 = module->darken_params[6]; y4 = module->darken_params[7]; if ( standard_width <= 0 ) { stem_width = af_intToFixed( 75 ); /* taken from cf2font.c */ stem_width_per_1000 = stem_width; } else { stem_width = af_intToFixed( standard_width ); stem_width_per_1000 = FT_MulFix( stem_width, em_ratio ); } log_base_2 = FT_MSB( (FT_UInt32)stem_width_per_1000 ) + FT_MSB( (FT_UInt32)ppem ); if ( log_base_2 >= 46 ) { /* possible overflow */ scaled_stem = af_intToFixed( x4 ); } else scaled_stem = FT_MulFix( stem_width_per_1000, ppem ); /* now apply the darkening parameters */ if ( scaled_stem < af_intToFixed( x1 ) ) darken_amount = FT_DivFix( af_intToFixed( y1 ), ppem ); else if ( scaled_stem < af_intToFixed( x2 ) ) { FT_Int xdelta = x2 - x1; FT_Int ydelta = y2 - y1; FT_Int x = stem_width_per_1000 - FT_DivFix( af_intToFixed( x1 ), ppem ); if ( !xdelta ) goto Try_x3; darken_amount = FT_MulDiv( x, ydelta, xdelta ) + FT_DivFix( af_intToFixed( y1 ), ppem ); } else if ( scaled_stem < af_intToFixed( x3 ) ) { Try_x3: { FT_Int xdelta = x3 - x2; FT_Int ydelta = y3 - y2; FT_Int x = stem_width_per_1000 - FT_DivFix( af_intToFixed( x2 ), ppem ); if ( !xdelta ) goto Try_x4; darken_amount = FT_MulDiv( x, ydelta, xdelta ) + FT_DivFix( af_intToFixed( y2 ), ppem ); } } else if ( scaled_stem < af_intToFixed( x4 ) ) { Try_x4: { FT_Int xdelta = x4 - x3; FT_Int ydelta = y4 - y3; FT_Int x = stem_width_per_1000 - FT_DivFix( af_intToFixed( x3 ), ppem ); if ( !xdelta ) goto Use_y4; darken_amount = FT_MulDiv( x, ydelta, xdelta ) + FT_DivFix( af_intToFixed( y3 ), ppem ); } } else { Use_y4: darken_amount = FT_DivFix( af_intToFixed( y4 ), ppem ); } /* Convert darken_amount from per 1000 em to true character space. */ return af_fixedToInt( FT_DivFix( darken_amount, em_ratio ) ); }
FT_GlyphSlot_Embolden( FT_GlyphSlot slot ) { FT_Library library = slot->library; FT_Face face = slot->face; FT_Error error; FT_Pos xstr, ystr; if ( slot->format != FT_GLYPH_FORMAT_OUTLINE && slot->format != FT_GLYPH_FORMAT_BITMAP ) return; /* some reasonable strength */ xstr = FT_MulFix( face->units_per_EM, face->size->metrics.y_scale ) / 24; ystr = xstr; if ( slot->format == FT_GLYPH_FORMAT_OUTLINE ) { error = FT_Outline_Embolden( &slot->outline, xstr ); /* ignore error */ /* this is more than enough for most glyphs; if you need accurate */ /* values, you have to call FT_Outline_Get_CBox */ xstr = xstr * 2; ystr = xstr; } else if ( slot->format == FT_GLYPH_FORMAT_BITMAP ) { /* round to full pixels */ xstr &= ~63; if ( xstr == 0 ) xstr = 1 << 6; ystr &= ~63; error = FT_GlyphSlot_Own_Bitmap( slot ); if ( error ) return; error = FT_Bitmap_Embolden( library, &slot->bitmap, xstr, ystr ); if ( error ) return; } if ( slot->advance.x ) slot->advance.x += xstr; if ( slot->advance.y ) slot->advance.y += ystr; slot->metrics.width += xstr; slot->metrics.height += ystr; slot->metrics.horiBearingY += ystr; slot->metrics.horiAdvance += xstr; slot->metrics.vertBearingX -= xstr / 2; slot->metrics.vertBearingY += ystr; slot->metrics.vertAdvance += ystr; if ( slot->format == FT_GLYPH_FORMAT_BITMAP ) slot->bitmap_top += ystr >> 6; }
static FT_Error af_cjk_hints_compute_edges( AF_GlyphHints hints, AF_Dimension dim ) { AF_AxisHints axis = &hints->axis[dim]; FT_Error error = AF_Err_Ok; FT_Memory memory = hints->memory; AF_LatinAxis laxis = &((AF_LatinMetrics)hints->metrics)->axis[dim]; AF_Segment segments = axis->segments; AF_Segment segment_limit = segments + axis->num_segments; AF_Segment seg; FT_Fixed scale; FT_Pos edge_distance_threshold; axis->num_edges = 0; scale = ( dim == AF_DIMENSION_HORZ ) ? hints->x_scale : hints->y_scale; /*********************************************************************/ /* */ /* We begin by generating a sorted table of edges for the current */ /* direction. To do so, we simply scan each segment and try to find */ /* an edge in our table that corresponds to its position. */ /* */ /* If no edge is found, we create and insert a new edge in the */ /* sorted table. Otherwise, we simply add the segment to the edge's */ /* list which is then processed in the second step to compute the */ /* edge's properties. */ /* */ /* Note that the edges table is sorted along the segment/edge */ /* position. */ /* */ /*********************************************************************/ edge_distance_threshold = FT_MulFix( laxis->edge_distance_threshold, scale ); if ( edge_distance_threshold > 64 / 4 ) edge_distance_threshold = FT_DivFix( 64 / 4, scale ); else edge_distance_threshold = laxis->edge_distance_threshold; for ( seg = segments; seg < segment_limit; seg++ ) { AF_Edge found = 0; FT_Pos best = 0xFFFFU; FT_Int ee; /* look for an edge corresponding to the segment */ for ( ee = 0; ee < axis->num_edges; ee++ ) { AF_Edge edge = axis->edges + ee; FT_Pos dist; if ( edge->dir != seg->dir ) continue; dist = seg->pos - edge->fpos; if ( dist < 0 ) dist = -dist; if ( dist < edge_distance_threshold && dist < best ) { AF_Segment link = seg->link; /* check whether all linked segments of the candidate edge */ /* can make a single edge. */ if ( link ) { AF_Segment seg1 = edge->first; AF_Segment link1; FT_Pos dist2 = 0; do { link1 = seg1->link; if ( link1 ) { dist2 = AF_SEGMENT_DIST( link, link1 ); if ( dist2 >= edge_distance_threshold ) break; } } while ( ( seg1 = seg1->edge_next ) != edge->first ); if ( dist2 >= edge_distance_threshold ) continue; } best = dist; found = edge; } } if ( !found ) { AF_Edge edge; /* insert a new edge in the list and */ /* sort according to the position */ error = af_axis_hints_new_edge( axis, seg->pos, (AF_Direction)seg->dir, memory, &edge ); if ( error ) goto Exit; /* add the segment to the new edge's list */ FT_ZERO( edge ); edge->first = seg; edge->last = seg; edge->fpos = seg->pos; edge->opos = edge->pos = FT_MulFix( seg->pos, scale ); seg->edge_next = seg; edge->dir = seg->dir; } else { /* if an edge was found, simply add the segment to the edge's */ /* list */ seg->edge_next = found->first; found->last->edge_next = seg; found->last = seg; } } /*********************************************************************/ /* */ /* Good, we now compute each edge's properties according to segments */ /* found on its position. Basically, these are as follows. */ /* */ /* - edge's main direction */ /* - stem edge, serif edge or both (which defaults to stem then) */ /* - rounded edge, straight or both (which defaults to straight) */ /* - link for edge */ /* */ /*********************************************************************/ /* first of all, set the `edge' field in each segment -- this is */ /* required in order to compute edge links */ /* */ /* Note that removing this loop and setting the `edge' field of each */ /* segment directly in the code above slows down execution speed for */ /* some reasons on platforms like the Sun. */ { AF_Edge edges = axis->edges; AF_Edge edge_limit = edges + axis->num_edges; AF_Edge edge; for ( edge = edges; edge < edge_limit; edge++ ) { seg = edge->first; if ( seg ) do { seg->edge = edge; seg = seg->edge_next; } while ( seg != edge->first ); } /* now compute each edge properties */ for ( edge = edges; edge < edge_limit; edge++ ) { FT_Int is_round = 0; /* does it contain round segments? */ FT_Int is_straight = 0; /* does it contain straight segments? */ seg = edge->first; do { FT_Bool is_serif; /* check for roundness of segment */ if ( seg->flags & AF_EDGE_ROUND ) is_round++; else is_straight++; /* check for links -- if seg->serif is set, then seg->link must */ /* be ignored */ is_serif = (FT_Bool)( seg->serif && seg->serif->edge != edge ); if ( seg->link || is_serif ) { AF_Edge edge2; AF_Segment seg2; edge2 = edge->link; seg2 = seg->link; if ( is_serif ) { seg2 = seg->serif; edge2 = edge->serif; } if ( edge2 ) { FT_Pos edge_delta; FT_Pos seg_delta; edge_delta = edge->fpos - edge2->fpos; if ( edge_delta < 0 ) edge_delta = -edge_delta; seg_delta = AF_SEGMENT_DIST( seg, seg2 ); if ( seg_delta < edge_delta ) edge2 = seg2->edge; } else edge2 = seg2->edge; if ( is_serif ) { edge->serif = edge2; edge2->flags |= AF_EDGE_SERIF; } else edge->link = edge2; } seg = seg->edge_next; } while ( seg != edge->first ); /* set the round/straight flags */ edge->flags = AF_EDGE_NORMAL; if ( is_round > 0 && is_round >= is_straight ) edge->flags |= AF_EDGE_ROUND; /* get rid of serifs if link is set */ /* XXX: This gets rid of many unpleasant artefacts! */ /* Example: the `c' in cour.pfa at size 13 */ if ( edge->serif && edge->link ) edge->serif = 0; } } Exit: return error; }
cf2_blues_init( CF2_Blues blues, CF2_Font font ) { /* pointer to parsed font object */ CFF_Decoder* decoder = font->decoder; CF2_Fixed zoneHeight; CF2_Fixed maxZoneHeight = 0; CF2_Fixed csUnitsPerPixel; size_t numBlueValues; size_t numOtherBlues; size_t numFamilyBlues; size_t numFamilyOtherBlues; FT_Pos* blueValues; FT_Pos* otherBlues; FT_Pos* familyBlues; FT_Pos* familyOtherBlues; size_t i; CF2_Fixed emBoxBottom, emBoxTop; CF2_Int unitsPerEm = font->unitsPerEm; if ( unitsPerEm == 0 ) unitsPerEm = 1000; FT_ZERO( blues ); blues->scale = font->innerTransform.d; cf2_getBlueMetrics( decoder, &blues->blueScale, &blues->blueShift, &blues->blueFuzz ); cf2_getBlueValues( decoder, &numBlueValues, &blueValues ); cf2_getOtherBlues( decoder, &numOtherBlues, &otherBlues ); cf2_getFamilyBlues( decoder, &numFamilyBlues, &familyBlues ); cf2_getFamilyOtherBlues( decoder, &numFamilyOtherBlues, &familyOtherBlues ); /* * synthetic em box hint heuristic * * Apply this when ideographic dictionary (LanguageGroup 1) has no * real alignment zones. Adobe tools generate dummy zones at -250 and * 1100 for a 1000 unit em. Fonts with ICF-based alignment zones * should not enable the heuristic. When the heuristic is enabled, * the font's blue zones are ignored. * */ /* get em box from OS/2 typoAscender/Descender */ /* TODO: FreeType does not parse these metrics. Skip them for now. */ #if 0 FCM_getHorizontalLineMetrics( &e, font->font, &ascender, &descender, &linegap ); if ( ascender - descender == unitsPerEm ) { emBoxBottom = cf2_intToFixed( descender ); emBoxTop = cf2_intToFixed( ascender ); } else #endif { emBoxBottom = CF2_ICF_Bottom; emBoxTop = CF2_ICF_Top; } if ( cf2_getLanguageGroup( decoder ) == 1 && ( numBlueValues == 0 || ( numBlueValues == 4 && cf2_blueToFixed( blueValues[0] ) < emBoxBottom && cf2_blueToFixed( blueValues[1] ) < emBoxBottom && cf2_blueToFixed( blueValues[2] ) > emBoxTop && cf2_blueToFixed( blueValues[3] ) > emBoxTop ) ) ) { /* * Construct hint edges suitable for synthetic ghost hints at top * and bottom of em box. +-CF2_MIN_COUNTER allows for unhinted * features above or below the last hinted edge. This also gives a * net 1 pixel boost to the height of ideographic glyphs. * * Note: Adjust synthetic hints outward by epsilon (0x.0001) to * avoid interference. E.g., some fonts have real hints at * 880 and -120. */ blues->emBoxBottomEdge.csCoord = emBoxBottom - CF2_FIXED_EPSILON; blues->emBoxBottomEdge.dsCoord = cf2_fixedRound( FT_MulFix( blues->emBoxBottomEdge.csCoord, blues->scale ) ) - CF2_MIN_COUNTER; blues->emBoxBottomEdge.scale = blues->scale; blues->emBoxBottomEdge.flags = CF2_GhostBottom | CF2_Locked | CF2_Synthetic; blues->emBoxTopEdge.csCoord = emBoxTop + CF2_FIXED_EPSILON + 2 * font->darkenY; blues->emBoxTopEdge.dsCoord = cf2_fixedRound( FT_MulFix( blues->emBoxTopEdge.csCoord, blues->scale ) ) + CF2_MIN_COUNTER; blues->emBoxTopEdge.scale = blues->scale; blues->emBoxTopEdge.flags = CF2_GhostTop | CF2_Locked | CF2_Synthetic; blues->doEmBoxHints = TRUE; /* enable the heuristic */ return; } /* copy `BlueValues' and `OtherBlues' to a combined array of top and */ /* bottom zones */ for ( i = 0; i < numBlueValues; i += 2 ) { blues->zone[blues->count].csBottomEdge = cf2_blueToFixed( blueValues[i] ); blues->zone[blues->count].csTopEdge = cf2_blueToFixed( blueValues[i + 1] ); zoneHeight = blues->zone[blues->count].csTopEdge - blues->zone[blues->count].csBottomEdge; if ( zoneHeight < 0 ) { FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" )); continue; /* reject this zone */ } if ( zoneHeight > maxZoneHeight ) { /* take maximum before darkening adjustment */ /* so overshoot suppression point doesn't change */ maxZoneHeight = zoneHeight; } /* adjust both edges of top zone upward by twice darkening amount */ if ( i != 0 ) { blues->zone[blues->count].csTopEdge += 2 * font->darkenY; blues->zone[blues->count].csBottomEdge += 2 * font->darkenY; } /* first `BlueValue' is bottom zone; others are top */ if ( i == 0 ) { blues->zone[blues->count].bottomZone = TRUE; blues->zone[blues->count].csFlatEdge = blues->zone[blues->count].csTopEdge; } else { blues->zone[blues->count].bottomZone = FALSE; blues->zone[blues->count].csFlatEdge = blues->zone[blues->count].csBottomEdge; } blues->count += 1; } for ( i = 0; i < numOtherBlues; i += 2 ) { blues->zone[blues->count].csBottomEdge = cf2_blueToFixed( otherBlues[i] ); blues->zone[blues->count].csTopEdge = cf2_blueToFixed( otherBlues[i + 1] ); zoneHeight = blues->zone[blues->count].csTopEdge - blues->zone[blues->count].csBottomEdge; if ( zoneHeight < 0 ) { FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" )); continue; /* reject this zone */ } if ( zoneHeight > maxZoneHeight ) { /* take maximum before darkening adjustment */ /* so overshoot suppression point doesn't change */ maxZoneHeight = zoneHeight; } /* Note: bottom zones are not adjusted for darkening amount */ /* all OtherBlues are bottom zone */ blues->zone[blues->count].bottomZone = TRUE; blues->zone[blues->count].csFlatEdge = blues->zone[blues->count].csTopEdge; blues->count += 1; } /* Adjust for FamilyBlues */ /* Search for the nearest flat edge in `FamilyBlues' or */ /* `FamilyOtherBlues'. According to the Black Book, any matching edge */ /* must be within one device pixel */ csUnitsPerPixel = FT_DivFix( cf2_intToFixed( 1 ), blues->scale ); /* loop on all zones in this font */ for ( i = 0; i < blues->count; i++ ) { size_t j; CF2_Fixed minDiff; CF2_Fixed flatFamilyEdge, diff; /* value for this font */ CF2_Fixed flatEdge = blues->zone[i].csFlatEdge; if ( blues->zone[i].bottomZone ) { /* In a bottom zone, the top edge is the flat edge. */ /* Search `FamilyOtherBlues' for bottom zones; look for closest */ /* Family edge that is within the one pixel threshold. */ minDiff = CF2_FIXED_MAX; for ( j = 0; j < numFamilyOtherBlues; j += 2 ) { /* top edge */ flatFamilyEdge = cf2_blueToFixed( familyOtherBlues[j + 1] ); diff = cf2_fixedAbs( flatEdge - flatFamilyEdge ); if ( diff < minDiff && diff < csUnitsPerPixel ) { blues->zone[i].csFlatEdge = flatFamilyEdge; minDiff = diff; if ( diff == 0 ) break; } } /* check the first member of FamilyBlues, which is a bottom zone */ if ( numFamilyBlues >= 2 ) { /* top edge */ flatFamilyEdge = cf2_blueToFixed( familyBlues[1] ); diff = cf2_fixedAbs( flatEdge - flatFamilyEdge ); if ( diff < minDiff && diff < csUnitsPerPixel ) blues->zone[i].csFlatEdge = flatFamilyEdge; } } else { /* In a top zone, the bottom edge is the flat edge. */ /* Search `FamilyBlues' for top zones; skip first zone, which is a */ /* bottom zone; look for closest Family edge that is within the */ /* one pixel threshold */ minDiff = CF2_FIXED_MAX; for ( j = 2; j < numFamilyBlues; j += 2 ) { /* bottom edge */ flatFamilyEdge = cf2_blueToFixed( familyBlues[j] ); /* adjust edges of top zone upward by twice darkening amount */ flatFamilyEdge += 2 * font->darkenY; /* bottom edge */ diff = cf2_fixedAbs( flatEdge - flatFamilyEdge ); if ( diff < minDiff && diff < csUnitsPerPixel ) { blues->zone[i].csFlatEdge = flatFamilyEdge; minDiff = diff; if ( diff == 0 ) break; } } } } /* TODO: enforce separation of zones, including BlueFuzz */ /* Adjust BlueScale; similar to AdjustBlueScale() in coretype */ /* `bcsetup.c'. */ if ( maxZoneHeight > 0 ) { if ( blues->blueScale > FT_DivFix( cf2_intToFixed( 1 ), maxZoneHeight ) ) { /* clamp at maximum scale */ blues->blueScale = FT_DivFix( cf2_intToFixed( 1 ), maxZoneHeight ); } /* * TODO: Revisit the bug fix for 613448. The minimum scale * requirement catches a number of library fonts. For * example, with default BlueScale (.039625) and 0.4 minimum, * the test below catches any font with maxZoneHeight < 10.1. * There are library fonts ranging from 2 to 10 that get * caught, including e.g., Eurostile LT Std Medium with * maxZoneHeight of 6. * */ #if 0 if ( blueScale < .4 / maxZoneHeight ) { tetraphilia_assert( 0 ); /* clamp at minimum scale, per bug 0613448 fix */ blueScale = .4 / maxZoneHeight; } #endif } /* * Suppress overshoot and boost blue zones at small sizes. Boost * amount varies linearly from 0.5 pixel near 0 to 0 pixel at * blueScale cutoff. * Note: This boost amount is different from the coretype heuristic. * */ if ( blues->scale < blues->blueScale ) { blues->suppressOvershoot = TRUE; /* Change rounding threshold for `dsFlatEdge'. */ /* Note: constant changed from 0.5 to 0.6 to avoid a problem with */ /* 10ppem Arial */ blues->boost = FT_MulFix( cf2_floatToFixed( .6 ), ( cf2_intToFixed( 1 ) - FT_DivFix( blues->scale, blues->blueScale ) ) ); if ( blues->boost > 0x7FFF ) { /* boost must remain less than 0.5, or baseline could go negative */ blues->boost = 0x7FFF; } } /* boost and darkening have similar effects; don't do both */ if ( font->stemDarkened ) blues->boost = 0; /* set device space alignment for each zone; */ /* apply boost amount before rounding flat edge */ for ( i = 0; i < blues->count; i++ ) { if ( blues->zone[i].bottomZone ) blues->zone[i].dsFlatEdge = cf2_fixedRound( FT_MulFix( blues->zone[i].csFlatEdge, blues->scale ) - blues->boost ); else blues->zone[i].dsFlatEdge = cf2_fixedRound( FT_MulFix( blues->zone[i].csFlatEdge, blues->scale ) + blues->boost ); } }
ah_outline_load( AH_Outline outline, FT_Fixed x_scale, FT_Fixed y_scale, FT_Face face ) { FT_Memory memory = outline->memory; FT_Error error = AH_Err_Ok; FT_Outline* source = &face->glyph->outline; FT_Int num_points = source->n_points; FT_Int num_contours = source->n_contours; AH_Point points; /* check arguments */ if ( !face || !face->size || face->glyph->format != FT_GLYPH_FORMAT_OUTLINE ) return AH_Err_Invalid_Argument; /* first of all, reallocate the contours array if necessary */ if ( num_contours > outline->max_contours ) { FT_Int new_contours = ( num_contours + 3 ) & -4; if ( FT_RENEW_ARRAY( outline->contours, outline->max_contours, new_contours ) ) goto Exit; outline->max_contours = new_contours; } /* then, reallocate the points, segments & edges arrays if needed -- */ /* note that we reserved two additional point positions, used to */ /* hint metrics appropriately */ /* */ if ( num_points + 2 > outline->max_points ) { FT_Int news = ( num_points + 2 + 7 ) & -8; FT_Int max = outline->max_points; if ( FT_RENEW_ARRAY( outline->points, max, news ) || FT_RENEW_ARRAY( outline->horz_edges, max * 2, news * 2 ) || FT_RENEW_ARRAY( outline->horz_segments, max * 2, news * 2 ) ) goto Exit; /* readjust some pointers */ outline->vert_edges = outline->horz_edges + news; outline->vert_segments = outline->horz_segments + news; outline->max_points = news; } outline->num_points = num_points; outline->num_contours = num_contours; outline->num_hedges = 0; outline->num_vedges = 0; outline->num_hsegments = 0; outline->num_vsegments = 0; /* We can't rely on the value of `FT_Outline.flags' to know the fill */ /* direction used for a glyph, given that some fonts are broken (e.g. */ /* the Arphic ones). We thus recompute it each time we need to. */ /* */ outline->vert_major_dir = AH_DIR_UP; outline->horz_major_dir = AH_DIR_LEFT; if ( ah_get_orientation( source ) > 1 ) { outline->vert_major_dir = AH_DIR_DOWN; outline->horz_major_dir = AH_DIR_RIGHT; } outline->x_scale = x_scale; outline->y_scale = y_scale; points = outline->points; if ( outline->num_points == 0 ) goto Exit; { /* do one thing at a time -- it is easier to understand, and */ /* the code is clearer */ AH_Point point; AH_Point point_limit = points + outline->num_points; /* compute coordinates */ { FT_Vector* vec = source->points; for ( point = points; point < point_limit; vec++, point++ ) { point->fx = vec->x; point->fy = vec->y; point->ox = point->x = FT_MulFix( vec->x, x_scale ); point->oy = point->y = FT_MulFix( vec->y, y_scale ); point->flags = 0; } } /* compute Bezier flags */ { char* tag = source->tags; for ( point = points; point < point_limit; point++, tag++ ) { switch ( FT_CURVE_TAG( *tag ) ) { case FT_CURVE_TAG_CONIC: point->flags = AH_FLAG_CONIC; break; case FT_CURVE_TAG_CUBIC: point->flags = AH_FLAG_CUBIC; break; default: ; } } } /* compute `next' and `prev' */ { FT_Int contour_index; AH_Point prev; AH_Point first; AH_Point end; contour_index = 0; first = points; end = points + source->contours[0]; prev = end; for ( point = points; point < point_limit; point++ ) { point->prev = prev; if ( point < end ) { point->next = point + 1; prev = point; } else { point->next = first; contour_index++; if ( point + 1 < point_limit ) { end = points + source->contours[contour_index]; first = point + 1; prev = end; } } } } /* set-up the contours array */ { AH_Point* contour = outline->contours; AH_Point* contour_limit = contour + outline->num_contours; short* end = source->contours; short idx = 0; for ( ; contour < contour_limit; contour++, end++ ) { contour[0] = points + idx; idx = (short)( end[0] + 1 ); } } /* compute directions of in & out vectors */ { for ( point = points; point < point_limit; point++ ) { AH_Point prev; AH_Point next; FT_Vector ivec, ovec; prev = point->prev; ivec.x = point->fx - prev->fx; ivec.y = point->fy - prev->fy; point->in_dir = ah_compute_direction( ivec.x, ivec.y ); next = point->next; ovec.x = next->fx - point->fx; ovec.y = next->fy - point->fy; point->out_dir = ah_compute_direction( ovec.x, ovec.y ); #ifndef AH_OPTION_NO_WEAK_INTERPOLATION if ( point->flags & (AH_FLAG_CONIC | AH_FLAG_CUBIC) ) { Is_Weak_Point: point->flags |= AH_FLAG_WEAK_INTERPOLATION; } else if ( point->out_dir == point->in_dir ) { AH_Angle angle_in, angle_out, delta; if ( point->out_dir != AH_DIR_NONE ) goto Is_Weak_Point; angle_in = ah_angle( &ivec ); angle_out = ah_angle( &ovec ); delta = angle_in - angle_out; if ( delta > AH_PI ) delta = AH_2PI - delta; if ( delta < 0 ) delta = -delta; if ( delta < 2 ) goto Is_Weak_Point; } else if ( point->in_dir == -point->out_dir ) goto Is_Weak_Point; #endif } } } Exit: return error; }
bool TTFFont::load(Common::SeekableReadStream &stream, int size, bool monochrome, const uint32 *mapping) { if (!g_ttf.isInitialized()) return false; _size = stream.size(); if (!_size) return false; _ttfFile = new uint8[_size]; assert(_ttfFile); if (stream.read(_ttfFile, _size) != _size) { delete[] _ttfFile; _ttfFile = 0; return false; } if (!g_ttf.loadFont(_ttfFile, _size, _face)) { delete[] _ttfFile; _ttfFile = 0; return false; } // We only support scalable fonts. if (!FT_IS_SCALABLE(_face)) { delete[] _ttfFile; _ttfFile = 0; g_ttf.closeFont(_face); return false; } // Check whether we have kerning support _hasKerning = (FT_HAS_KERNING(_face) != 0); if (FT_Set_Char_Size(_face, 0, size * 64, 0, 0)) { delete[] _ttfFile; _ttfFile = 0; return false; } _monochrome = monochrome; FT_Fixed yScale = _face->size->metrics.y_scale; _ascent = ftCeil26_6(FT_MulFix(_face->ascender, yScale)); _descent = ftCeil26_6(FT_MulFix(_face->descender, yScale)); _width = ftCeil26_6(FT_MulFix(_face->max_advance_width, _face->size->metrics.x_scale)); _height = _ascent - _descent + 1; if (!mapping) { // Load all ISO-8859-1 characters. for (uint i = 0; i < 256; ++i) { if (!cacheGlyph(_glyphs[i], _glyphSlots[i], i)) _glyphSlots[i] = 0; } } else { for (uint i = 0; i < 256; ++i) { const uint32 unicode = mapping[i] & 0x7FFFFFFF; const bool isRequired = (mapping[i] & 0x80000000) != 0; // Check whether loading an important glyph fails and error out if // that is the case. if (!cacheGlyph(_glyphs[i], _glyphSlots[i], unicode)) { _glyphSlots[i] = 0; if (isRequired) return false; } } } _initialized = (_glyphs.size() != 0); return _initialized; }
af_warper_compute( AF_Warper warper, AF_GlyphHints hints, AF_Dimension dim, FT_Fixed *a_scale, FT_Pos *a_delta ) { AF_AxisHints axis; AF_Point points; FT_Fixed org_scale; FT_Pos org_delta; FT_UInt nn, num_points, num_segments; FT_Int X1, X2; FT_Int w; AF_WarpScore base_distort; AF_Segment segments; /* get original scaling transformation */ if ( dim == AF_DIMENSION_VERT ) { org_scale = hints->y_scale; org_delta = hints->y_delta; } else { org_scale = hints->x_scale; org_delta = hints->x_delta; } warper->best_scale = org_scale; warper->best_delta = org_delta; warper->best_score = INT_MIN; warper->best_distort = 0; axis = &hints->axis[dim]; segments = axis->segments; num_segments = axis->num_segments; points = hints->points; num_points = hints->num_points; *a_scale = org_scale; *a_delta = org_delta; /* get X1 and X2, minimum and maximum in original coordinates */ if ( num_segments < 1 ) return; #if 1 X1 = X2 = points[0].fx; for ( nn = 1; nn < num_points; nn++ ) { FT_Int X = points[nn].fx; if ( X < X1 ) X1 = X; if ( X > X2 ) X2 = X; } #else X1 = X2 = segments[0].pos; for ( nn = 1; nn < num_segments; nn++ ) { FT_Int X = segments[nn].pos; if ( X < X1 ) X1 = X; if ( X > X2 ) X2 = X; } #endif if ( X1 >= X2 ) return; warper->x1 = FT_MulFix( X1, org_scale ) + org_delta; warper->x2 = FT_MulFix( X2, org_scale ) + org_delta; warper->t1 = AF_WARPER_FLOOR( warper->x1 ); warper->t2 = AF_WARPER_CEIL( warper->x2 ); /* examine a half pixel wide range around the maximum coordinates */ warper->x1min = warper->x1 & ~31; warper->x1max = warper->x1min + 32; warper->x2min = warper->x2 & ~31; warper->x2max = warper->x2min + 32; if ( warper->x1max > warper->x2 ) warper->x1max = warper->x2; if ( warper->x2min < warper->x1 ) warper->x2min = warper->x1; warper->w0 = warper->x2 - warper->x1; if ( warper->w0 <= 64 ) { warper->x1max = warper->x1; warper->x2min = warper->x2; } /* examine (at most) a pixel wide range around the natural width */ warper->wmin = warper->x2min - warper->x1max; warper->wmax = warper->x2max - warper->x1min; #if 1 /* some heuristics to reduce the number of widths to be examined */ { int margin = 16; if ( warper->w0 <= 128 ) { margin = 8; if ( warper->w0 <= 96 ) margin = 4; } if ( warper->wmin < warper->w0 - margin ) warper->wmin = warper->w0 - margin; if ( warper->wmax > warper->w0 + margin ) warper->wmax = warper->w0 + margin; } if ( warper->wmin < warper->w0 * 3 / 4 ) warper->wmin = warper->w0 * 3 / 4; if ( warper->wmax > warper->w0 * 5 / 4 ) warper->wmax = warper->w0 * 5 / 4; #else /* no scaling, just translation */ warper->wmin = warper->wmax = warper->w0; #endif for ( w = warper->wmin; w <= warper->wmax; w++ ) { FT_Fixed new_scale; FT_Pos new_delta; FT_Pos xx1, xx2; /* compute min and max positions for given width, */ /* assuring that they stay within the coordinate ranges */ xx1 = warper->x1; xx2 = warper->x2; if ( w >= warper->w0 ) { xx1 -= w - warper->w0; if ( xx1 < warper->x1min ) { xx2 += warper->x1min - xx1; xx1 = warper->x1min; } } else { xx1 -= w - warper->w0; if ( xx1 > warper->x1max ) { xx2 -= xx1 - warper->x1max; xx1 = warper->x1max; } } if ( xx1 < warper->x1 ) base_distort = warper->x1 - xx1; else base_distort = xx1 - warper->x1; if ( xx2 < warper->x2 ) base_distort += warper->x2 - xx2; else base_distort += xx2 - warper->x2; /* give base distortion a greater weight while scoring */ base_distort *= 10; new_scale = org_scale + FT_DivFix( w - warper->w0, X2 - X1 ); new_delta = xx1 - FT_MulFix( X1, new_scale ); af_warper_compute_line_best( warper, new_scale, new_delta, xx1, xx2, base_distort, segments, num_segments ); } { FT_Fixed best_scale = warper->best_scale; FT_Pos best_delta = warper->best_delta; hints->xmin_delta = FT_MulFix( X1, best_scale - org_scale ) + best_delta; hints->xmax_delta = FT_MulFix( X2, best_scale - org_scale ) + best_delta; *a_scale = best_scale; *a_delta = best_delta; } }
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; FT_Int32 flags; flags = load_flags | FT_LOAD_LINEAR_DESIGN; error = FT_Load_Glyph( face, glyph_index, 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 ); } 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 are 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.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 */ if ( metrics->clazz->script_hints_apply ) metrics->clazz->script_hints_apply( hints, &gloader->current.outline, metrics ); /* we now need to adjust the metrics according to the change in */ /* width/positioning that occurred during the hinting process */ if ( scaler->render_mode != FT_RENDER_MODE_LIGHT ) { FT_Pos 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 */ if ( axis->num_edges > 1 && AF_HINTS_DO_ADVANCE( hints ) ) { old_rsb = loader->pp2.x - 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 -= 8; if ( old_rsb < 24 ) pp2x_uh += 8; loader->pp1.x = FT_PIX_ROUND( pp1x_uh ); loader->pp2.x = FT_PIX_ROUND( pp2x_uh ); if ( loader->pp1.x >= new_lsb && old_lsb > 0 ) loader->pp1.x -= 64; if ( loader->pp2.x <= edge2->pos && old_rsb > 0 ) loader->pp2.x += 64; slot->lsb_delta = loader->pp1.x - pp1x_uh; slot->rsb_delta = loader->pp2.x - pp2x_uh; } else { FT_Pos pp1x = loader->pp1.x; FT_Pos pp2x = loader->pp2.x; loader->pp1.x = FT_PIX_ROUND( pp1x ); loader->pp2.x = FT_PIX_ROUND( pp2x ); slot->lsb_delta = loader->pp1.x - pp1x; slot->rsb_delta = loader->pp2.x - pp2x; } } else { FT_Pos pp1x = loader->pp1.x; FT_Pos pp2x = loader->pp2.x; loader->pp1.x = FT_PIX_ROUND( pp1x + hints->xmin_delta ); loader->pp2.x = FT_PIX_ROUND( pp2x + hints->xmax_delta ); slot->lsb_delta = loader->pp1.x - pp1x; slot->rsb_delta = loader->pp2.x - pp2x; } /* 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 transformation 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* limit = cur + num_new_points; for ( ; cur < limit; cur++ ) FT_Vector_Transform( cur, &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 eventually may consider another approach */ 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; FT_Vector vvector; vvector.x = slot->metrics.vertBearingX - slot->metrics.horiBearingX; vvector.y = slot->metrics.vertBearingY - slot->metrics.horiBearingY; vvector.x = FT_MulFix( vvector.x, metrics->scaler.x_scale ); vvector.y = FT_MulFix( vvector.y, metrics->scaler.y_scale ); /* transform the hinted outline if needed */ if ( loader->transformed ) { FT_Outline_Transform( &gloader->base.outline, &loader->trans_matrix ); FT_Vector_Transform( &vvector, &loader->trans_matrix ); } #if 1 /* 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 ); #endif 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; slot->metrics.vertBearingX = FT_PIX_FLOOR( bbox.xMin + vvector.x ); slot->metrics.vertBearingY = FT_PIX_FLOOR( bbox.yMax + vvector.y ); /* for mono-width fonts (like Andale, Courier, etc.) we need */ /* to keep the original rounded advance width; ditto for */ /* digits if all have the same 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 ( scaler->render_mode != FT_RENDER_MODE_LIGHT && ( FT_IS_FIXED_WIDTH( slot->face ) || ( af_face_globals_is_digit( loader->globals, glyph_index ) && metrics->digits_have_same_width ) ) ) { slot->metrics.horiAdvance = FT_MulFix( slot->metrics.horiAdvance, metrics->scaler.x_scale ); /* Set delta values to 0. Otherwise code that uses them is */ /* going to ruin the fixed advance width. */ slot->lsb_delta = 0; slot->rsb_delta = 0; } else { /* non-spacing glyphs must stay as-is */ if ( slot->metrics.horiAdvance ) slot->metrics.horiAdvance = loader->pp2.x - loader->pp1.x; } #endif slot->metrics.vertAdvance = FT_MulFix( slot->metrics.vertAdvance, metrics->scaler.y_scale ); slot->metrics.horiAdvance = FT_PIX_ROUND( slot->metrics.horiAdvance ); slot->metrics.vertAdvance = FT_PIX_ROUND( slot->metrics.vertAdvance ); /* now copy outline into glyph slot */ FT_GlyphLoader_Rewind( internal->loader ); error = FT_GlyphLoader_CopyPoints( internal->loader, gloader ); if ( error ) goto Exit; /* reassign all outline fields except flags to protect them */ slot->outline.n_contours = internal->loader->base.outline.n_contours; slot->outline.n_points = internal->loader->base.outline.n_points; slot->outline.points = internal->loader->base.outline.points; slot->outline.tags = internal->loader->base.outline.tags; slot->outline.contours = internal->loader->base.outline.contours; slot->format = FT_GLYPH_FORMAT_OUTLINE; } Exit: return error; }
cff_slot_load( CFF_GlyphSlot glyph, CFF_Size size, FT_UInt glyph_index, FT_Int32 load_flags ) { FT_Error error; CFF_Decoder decoder; PS_Decoder psdecoder; TT_Face face = (TT_Face)glyph->root.face; FT_Bool hinting, scaled, force_scaling; CFF_Font cff = (CFF_Font)face->extra.data; PSAux_Service psaux = (PSAux_Service)face->psaux; const CFF_Decoder_Funcs decoder_funcs = psaux->cff_decoder_funcs; FT_Matrix font_matrix; FT_Vector font_offset; force_scaling = FALSE; /* in a CID-keyed font, consider `glyph_index' as a CID and map */ /* it immediately to the real glyph_index -- if it isn't a */ /* subsetted font, glyph_indices and CIDs are identical, though */ if ( cff->top_font.font_dict.cid_registry != 0xFFFFU && cff->charset.cids ) { /* don't handle CID 0 (.notdef) which is directly mapped to GID 0 */ if ( glyph_index != 0 ) { glyph_index = cff_charset_cid_to_gindex( &cff->charset, glyph_index ); if ( glyph_index == 0 ) return FT_THROW( Invalid_Argument ); } } else if ( glyph_index >= cff->num_glyphs ) return FT_THROW( Invalid_Argument ); if ( load_flags & FT_LOAD_NO_RECURSE ) load_flags |= FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING; glyph->x_scale = 0x10000L; glyph->y_scale = 0x10000L; if ( size ) { glyph->x_scale = size->root.metrics.x_scale; glyph->y_scale = size->root.metrics.y_scale; } #ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS /* try to load embedded bitmap if any */ /* */ /* XXX: The convention should be emphasized in */ /* the documents because it can be confusing. */ if ( size ) { CFF_Face cff_face = (CFF_Face)size->root.face; SFNT_Service sfnt = (SFNT_Service)cff_face->sfnt; FT_Stream stream = cff_face->root.stream; if ( size->strike_index != 0xFFFFFFFFUL && sfnt->load_eblc && ( load_flags & FT_LOAD_NO_BITMAP ) == 0 ) { TT_SBit_MetricsRec metrics; error = sfnt->load_sbit_image( face, size->strike_index, glyph_index, (FT_UInt)load_flags, stream, &glyph->root.bitmap, &metrics ); if ( !error ) { FT_Bool has_vertical_info; FT_UShort advance; FT_Short dummy; glyph->root.outline.n_points = 0; glyph->root.outline.n_contours = 0; glyph->root.metrics.width = (FT_Pos)metrics.width << 6; glyph->root.metrics.height = (FT_Pos)metrics.height << 6; glyph->root.metrics.horiBearingX = (FT_Pos)metrics.horiBearingX << 6; glyph->root.metrics.horiBearingY = (FT_Pos)metrics.horiBearingY << 6; glyph->root.metrics.horiAdvance = (FT_Pos)metrics.horiAdvance << 6; glyph->root.metrics.vertBearingX = (FT_Pos)metrics.vertBearingX << 6; glyph->root.metrics.vertBearingY = (FT_Pos)metrics.vertBearingY << 6; glyph->root.metrics.vertAdvance = (FT_Pos)metrics.vertAdvance << 6; glyph->root.format = FT_GLYPH_FORMAT_BITMAP; if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) { glyph->root.bitmap_left = metrics.vertBearingX; glyph->root.bitmap_top = metrics.vertBearingY; } else { glyph->root.bitmap_left = metrics.horiBearingX; glyph->root.bitmap_top = metrics.horiBearingY; } /* compute linear advance widths */ (void)( (SFNT_Service)face->sfnt )->get_metrics( face, 0, glyph_index, &dummy, &advance ); glyph->root.linearHoriAdvance = advance; has_vertical_info = FT_BOOL( face->vertical_info && face->vertical.number_Of_VMetrics > 0 ); /* get the vertical metrics from the vmtx table if we have one */ if ( has_vertical_info ) { (void)( (SFNT_Service)face->sfnt )->get_metrics( face, 1, glyph_index, &dummy, &advance ); glyph->root.linearVertAdvance = advance; } else { /* make up vertical ones */ if ( face->os2.version != 0xFFFFU ) glyph->root.linearVertAdvance = (FT_Pos) ( face->os2.sTypoAscender - face->os2.sTypoDescender ); else glyph->root.linearVertAdvance = (FT_Pos) ( face->horizontal.Ascender - face->horizontal.Descender ); } return error; } } } #endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */ /* return immediately if we only want the embedded bitmaps */ if ( load_flags & FT_LOAD_SBITS_ONLY ) return FT_THROW( Invalid_Argument ); /* if we have a CID subfont, use its matrix (which has already */ /* been multiplied with the root matrix) */ /* this scaling is only relevant if the PS hinter isn't active */ if ( cff->num_subfonts ) { FT_Long top_upm, sub_upm; FT_Byte fd_index = cff_fd_select_get( &cff->fd_select, glyph_index ); if ( fd_index >= cff->num_subfonts ) fd_index = (FT_Byte)( cff->num_subfonts - 1 ); top_upm = (FT_Long)cff->top_font.font_dict.units_per_em; sub_upm = (FT_Long)cff->subfonts[fd_index]->font_dict.units_per_em; font_matrix = cff->subfonts[fd_index]->font_dict.font_matrix; font_offset = cff->subfonts[fd_index]->font_dict.font_offset; if ( top_upm != sub_upm ) { glyph->x_scale = FT_MulDiv( glyph->x_scale, top_upm, sub_upm ); glyph->y_scale = FT_MulDiv( glyph->y_scale, top_upm, sub_upm ); force_scaling = TRUE; } } else { font_matrix = cff->top_font.font_dict.font_matrix; font_offset = cff->top_font.font_dict.font_offset; } glyph->root.outline.n_points = 0; glyph->root.outline.n_contours = 0; /* top-level code ensures that FT_LOAD_NO_HINTING is set */ /* if FT_LOAD_NO_SCALE is active */ hinting = FT_BOOL( ( load_flags & FT_LOAD_NO_HINTING ) == 0 ); scaled = FT_BOOL( ( load_flags & FT_LOAD_NO_SCALE ) == 0 ); glyph->hint = hinting; glyph->scaled = scaled; glyph->root.format = FT_GLYPH_FORMAT_OUTLINE; /* by default */ { #ifdef CFF_CONFIG_OPTION_OLD_ENGINE PS_Driver driver = (PS_Driver)FT_FACE_DRIVER( face ); #endif FT_Byte* charstring; FT_ULong charstring_len; decoder_funcs->init( &decoder, face, size, glyph, hinting, FT_LOAD_TARGET_MODE( load_flags ), cff_get_glyph_data, cff_free_glyph_data ); /* this is for pure CFFs */ if ( load_flags & FT_LOAD_ADVANCE_ONLY ) decoder.width_only = TRUE; decoder.builder.no_recurse = (FT_Bool)( load_flags & FT_LOAD_NO_RECURSE ); /* now load the unscaled outline */ error = cff_get_glyph_data( face, glyph_index, &charstring, &charstring_len ); if ( error ) goto Glyph_Build_Finished; error = decoder_funcs->prepare( &decoder, size, glyph_index ); if ( error ) goto Glyph_Build_Finished; #ifdef CFF_CONFIG_OPTION_OLD_ENGINE /* choose which CFF renderer to use */ if ( driver->hinting_engine == FT_HINTING_FREETYPE ) error = decoder_funcs->parse_charstrings_old( &decoder, charstring, charstring_len, 0 ); else #endif { psaux->ps_decoder_init( &psdecoder, &decoder, FALSE ); error = decoder_funcs->parse_charstrings( &psdecoder, charstring, charstring_len ); /* Adobe's engine uses 16.16 numbers everywhere; */ /* as a consequence, glyphs larger than 2000ppem get rejected */ if ( FT_ERR_EQ( error, Glyph_Too_Big ) ) { /* this time, we retry unhinted and scale up the glyph later on */ /* (the engine uses and sets the hardcoded value 0x10000 / 64 = */ /* 0x400 for both `x_scale' and `y_scale' in this case) */ hinting = FALSE; force_scaling = TRUE; glyph->hint = hinting; error = decoder_funcs->parse_charstrings( &psdecoder, charstring, charstring_len ); } } cff_free_glyph_data( face, &charstring, charstring_len ); if ( error ) goto Glyph_Build_Finished; #ifdef FT_CONFIG_OPTION_INCREMENTAL /* Control data and length may not be available for incremental */ /* fonts. */ if ( face->root.internal->incremental_interface ) { glyph->root.control_data = NULL; glyph->root.control_len = 0; } else #endif /* FT_CONFIG_OPTION_INCREMENTAL */ /* We set control_data and control_len if charstrings is loaded. */ /* See how charstring loads at cff_index_access_element() in */ /* cffload.c. */ { CFF_Index csindex = &cff->charstrings_index; if ( csindex->offsets ) { glyph->root.control_data = csindex->bytes + csindex->offsets[glyph_index] - 1; glyph->root.control_len = (FT_Long)charstring_len; } } Glyph_Build_Finished: /* save new glyph tables, if no error */ if ( !error ) decoder.builder.funcs.done( &decoder.builder ); /* XXX: anything to do for broken glyph entry? */ } #ifdef FT_CONFIG_OPTION_INCREMENTAL /* Incremental fonts can optionally override the metrics. */ if ( !error && face->root.internal->incremental_interface && face->root.internal->incremental_interface->funcs->get_glyph_metrics ) { FT_Incremental_MetricsRec metrics; metrics.bearing_x = decoder.builder.left_bearing.x; metrics.bearing_y = 0; metrics.advance = decoder.builder.advance.x; metrics.advance_v = decoder.builder.advance.y; error = face->root.internal->incremental_interface->funcs->get_glyph_metrics( face->root.internal->incremental_interface->object, glyph_index, FALSE, &metrics ); decoder.builder.left_bearing.x = metrics.bearing_x; decoder.builder.advance.x = metrics.advance; decoder.builder.advance.y = metrics.advance_v; } #endif /* FT_CONFIG_OPTION_INCREMENTAL */ if ( !error ) { /* Now, set the metrics -- this is rather simple, as */ /* the left side bearing is the xMin, and the top side */ /* bearing the yMax. */ /* For composite glyphs, return only left side bearing and */ /* advance width. */ if ( load_flags & FT_LOAD_NO_RECURSE ) { FT_Slot_Internal internal = glyph->root.internal; glyph->root.metrics.horiBearingX = decoder.builder.left_bearing.x; glyph->root.metrics.horiAdvance = decoder.glyph_width; internal->glyph_matrix = font_matrix; internal->glyph_delta = font_offset; internal->glyph_transformed = 1; } else { FT_BBox cbox; FT_Glyph_Metrics* metrics = &glyph->root.metrics; FT_Bool has_vertical_info; if ( face->horizontal.number_Of_HMetrics ) { FT_Short horiBearingX = 0; FT_UShort horiAdvance = 0; ( (SFNT_Service)face->sfnt )->get_metrics( face, 0, glyph_index, &horiBearingX, &horiAdvance ); metrics->horiAdvance = horiAdvance; metrics->horiBearingX = horiBearingX; glyph->root.linearHoriAdvance = horiAdvance; } else { /* copy the _unscaled_ advance width */ metrics->horiAdvance = decoder.glyph_width; glyph->root.linearHoriAdvance = decoder.glyph_width; } glyph->root.internal->glyph_transformed = 0; has_vertical_info = FT_BOOL( face->vertical_info && face->vertical.number_Of_VMetrics > 0 ); /* get the vertical metrics from the vmtx table if we have one */ if ( has_vertical_info ) { FT_Short vertBearingY = 0; FT_UShort vertAdvance = 0; ( (SFNT_Service)face->sfnt )->get_metrics( face, 1, glyph_index, &vertBearingY, &vertAdvance ); metrics->vertBearingY = vertBearingY; metrics->vertAdvance = vertAdvance; } else { /* make up vertical ones */ if ( face->os2.version != 0xFFFFU ) metrics->vertAdvance = (FT_Pos)( face->os2.sTypoAscender - face->os2.sTypoDescender ); else metrics->vertAdvance = (FT_Pos)( face->horizontal.Ascender - face->horizontal.Descender ); } glyph->root.linearVertAdvance = metrics->vertAdvance; glyph->root.format = FT_GLYPH_FORMAT_OUTLINE; glyph->root.outline.flags = 0; if ( size && size->root.metrics.y_ppem < 24 ) glyph->root.outline.flags |= FT_OUTLINE_HIGH_PRECISION; glyph->root.outline.flags |= FT_OUTLINE_REVERSE_FILL; /* apply the font matrix, if any */ if ( font_matrix.xx != 0x10000L || font_matrix.yy != 0x10000L || font_matrix.xy != 0 || font_matrix.yx != 0 ) { FT_Outline_Transform( &glyph->root.outline, &font_matrix ); metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, font_matrix.xx ); metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, font_matrix.yy ); } if ( font_offset.x || font_offset.y ) { FT_Outline_Translate( &glyph->root.outline, font_offset.x, font_offset.y ); metrics->horiAdvance += font_offset.x; metrics->vertAdvance += font_offset.y; } if ( ( load_flags & FT_LOAD_NO_SCALE ) == 0 || force_scaling ) { /* scale the outline and the metrics */ FT_Int n; FT_Outline* cur = &glyph->root.outline; FT_Vector* vec = cur->points; FT_Fixed x_scale = glyph->x_scale; FT_Fixed y_scale = glyph->y_scale; /* First of all, scale the points */ if ( !hinting || !decoder.builder.hints_funcs ) for ( n = cur->n_points; n > 0; n--, vec++ ) { vec->x = FT_MulFix( vec->x, x_scale ); vec->y = FT_MulFix( vec->y, y_scale ); } /* Then scale the metrics */ metrics->horiAdvance = FT_MulFix( metrics->horiAdvance, x_scale ); metrics->vertAdvance = FT_MulFix( metrics->vertAdvance, y_scale ); } /* compute the other metrics */ FT_Outline_Get_CBox( &glyph->root.outline, &cbox ); metrics->width = cbox.xMax - cbox.xMin; metrics->height = cbox.yMax - cbox.yMin; metrics->horiBearingX = cbox.xMin; metrics->horiBearingY = cbox.yMax; if ( has_vertical_info ) metrics->vertBearingX = metrics->horiBearingX - metrics->horiAdvance / 2; else { if ( load_flags & FT_LOAD_VERTICAL_LAYOUT ) ft_synthesize_vertical_metrics( metrics, metrics->vertAdvance ); } } } return error; }
TTF_Font * TTF_OpenFontIndexRW(SDL_RWops * src, int freesrc, int ptsize, long index) { TTF_Font *font; FT_Error error; FT_Face face; FT_Fixed scale; FT_Stream stream; int position; if(!TTF_initialized) { TTF_SetError("Library not initialized"); return NULL; } /* Check to make sure we can seek in this stream */ position = SDL_RWtell(src); if(position < 0) { TTF_SetError("Can't seek in stream"); return NULL; } font = (TTF_Font *) malloc(sizeof *font); if(font == NULL) { TTF_SetError("Out of memory"); return NULL; } memset(font, 0, sizeof(*font)); font->src = src; font->freesrc = freesrc; stream = (FT_Stream) malloc(sizeof(*stream)); if(stream == NULL) { TTF_SetError("Out of memory"); TTF_CloseFont(font); return NULL; } memset(stream, 0, sizeof(*stream)); /* 090303 Chase - Newer FT2 version sets this internally so we don't have to. (Can't anyway, Don't have a definition for FT_Library) */ // stream->memory = library->memory; stream->read = RWread; stream->descriptor.pointer = src; stream->pos = (unsigned long) position; SDL_RWseek(src, 0, SEEK_END); stream->size = (unsigned long) (SDL_RWtell(src) - position); SDL_RWseek(src, position, SEEK_SET); font->args.flags = FT_OPEN_STREAM; font->args.stream = stream; error = FT_Open_Face(library, &font->args, index, &font->face); if(error) { TTF_SetFTError("Couldn't load font file", error); TTF_CloseFont(font); return NULL; } face = font->face; /* Make sure that our font face is scalable (global metrics) */ if(FT_IS_SCALABLE(face)) { /* Set the character size and use default DPI (72) */ error = FT_Set_Char_Size(font->face, 0, ptsize * 64, 0, 0); if(error) { TTF_SetFTError("Couldn't set font size", error); TTF_CloseFont(font); return NULL; } /* Get the scalable font metrics for this font */ scale = face->size->metrics.y_scale; font->ascent = FT_CEIL(FT_MulFix(face->bbox.yMax, scale)); font->descent = FT_CEIL(FT_MulFix(face->bbox.yMin, scale)); font->height = font->ascent - font->descent + /* baseline */ 1; font->lineskip = FT_CEIL(FT_MulFix(face->height, scale)); font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale)); font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale)); } else { /* Non-scalable font case. ptsize determines which family * or series of fonts to grab from the non-scalable format. * It is not the point size of the font. * */ if(ptsize >= font->face->num_fixed_sizes) ptsize = font->face->num_fixed_sizes - 1; font->font_size_family = ptsize; error = FT_Set_Pixel_Sizes(face, face->available_sizes[ptsize].height, face->available_sizes[ptsize].width); /* With non-scalale fonts, Freetype2 likes to fill many of the * font metrics with the value of 0. The size of the * non-scalable fonts must be determined differently * or sometimes cannot be determined. * */ font->ascent = face->available_sizes[ptsize].height; font->descent = 0; font->height = face->available_sizes[ptsize].height; font->lineskip = FT_CEIL(font->ascent); font->underline_offset = FT_FLOOR(face->underline_position); font->underline_height = FT_FLOOR(face->underline_thickness); } if(font->underline_height < 1) { font->underline_height = 1; } #ifdef DEBUG_FONTS printf("Font metrics:\n"); printf("\tascent = %d, descent = %d\n", font->ascent, font->descent); printf("\theight = %d, lineskip = %d\n", font->height, font->lineskip); printf("\tunderline_offset = %d, underline_height = %d\n", font->underline_offset, font->underline_height); #endif /* Set the default font style */ font->style = TTF_STYLE_NORMAL; font->glyph_overhang = face->size->metrics.y_ppem / 10; /* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */ font->glyph_italics = 0.207f; font->glyph_italics *= font->height; return font; }
LOCAL_DEF FT_Error TT_Reset_Size(TT_Size size) { TT_Face face; FT_Error error = TT_Err_Ok; FT_Size_Metrics *metrics; if(size->ttmetrics.valid) { return TT_Err_Ok; } face = (TT_Face)size->root.face; metrics = &size->root.metrics; if(metrics->x_ppem < 1 || metrics->y_ppem < 1) { return TT_Err_Invalid_PPem; } /* compute new transformation */ if(metrics->x_ppem >= metrics->y_ppem) { size->ttmetrics.scale = metrics->x_scale; size->ttmetrics.ppem = metrics->x_ppem; size->ttmetrics.x_ratio = 0x10000L; size->ttmetrics.y_ratio = FT_MulDiv(metrics->y_ppem, 0x10000L, metrics->x_ppem); } else { size->ttmetrics.scale = metrics->y_scale; size->ttmetrics.ppem = metrics->y_ppem; size->ttmetrics.x_ratio = FT_MulDiv(metrics->x_ppem, 0x10000L, metrics->y_ppem); size->ttmetrics.y_ratio = 0x10000L; } /* Compute root ascender, descender, test height, and max_advance */ metrics->ascender = (FT_MulFix(face->root.ascender, metrics->y_scale) + 32) & - 64; metrics->descender = (FT_MulFix(face->root.descender, metrics->y_scale) + 32) & - 64; metrics->height = (FT_MulFix(face->root.height, metrics->y_scale) + 32) & - 64; metrics->max_advance = (FT_MulFix(face->root.max_advance_width, metrics->x_scale) + 32) & - 64; #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER { TT_ExecContext exec; FT_UInt i, j; /* Scale the cvt values to the new ppem. */ /* We use by default the y ppem to scale the CVT. */ for(i = 0; i < size->cvt_size; i++) size->cvt[i] = FT_MulFix(face->cvt[i], size->ttmetrics.scale); /* All twilight points are originally zero */ for(j = 0; j < size->twilight.n_points; j++) { size->twilight.org[j].x = 0; size->twilight.org[j].y = 0; size->twilight.cur[j].x = 0; size->twilight.cur[j].y = 0; } /* clear storage area */ for(i = 0; i < size->storage_size; i++) size->storage[i] = 0; size->GS = tt_default_graphics_state; /* get execution context and run prep program */ if(size->debug) { exec = size->context; } else { exec = TT_New_Context(face); } /* debugging instances have their own context */ if(!exec) { return TT_Err_Could_Not_Find_Context; } TT_Load_Context(exec, face, size); TT_Set_CodeRange(exec, tt_coderange_cvt, face->cvt_program, face->cvt_program_size); TT_Clear_CodeRange(exec, tt_coderange_glyph); exec->instruction_trap = FALSE; exec->top = 0; exec->callTop = 0; if(face->cvt_program_size > 0) { error = TT_Goto_CodeRange(exec, tt_coderange_cvt, 0); if(error) { goto End; } if(!size->debug) { error = face->interpreter(exec); } } else { error = TT_Err_Ok; } size->GS = exec->GS; /* save default graphics state */ End: TT_Save_Context(exec, size); if(!size->debug) { TT_Done_Context(exec); } /* debugging instances keep their context */ } #endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ if(!error) { size->ttmetrics.valid = TRUE; } return error; }
bool CGUIFontTTFBase::Load(const std::string& strFilename, float height, float aspect, float lineSpacing, bool border) { // we now know that this object is unique - only the GUIFont objects are non-unique, so no need // for reference tracking these fonts m_face = g_freeTypeLibrary.GetFont(strFilename, height, aspect, m_fontFileInMemory); if (!m_face) return false; /* the values used are described below XBMC coords Freetype coords 0 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ bbox.yMax, ascender A \ A A | A A | AAAAA pppp cellAscender A A p p | A A p p | m_cellBaseLine _ _A_ _A_ pppp_ _ _ _ _/_ _ _ _ _ 0, base line. p \ p cellDescender m_cellHeight _ _ _ _ _ p _ _ _ _ _ _/_ _ _ _ _ bbox.yMin, descender */ int cellDescender = std::min<int>(m_face->bbox.yMin, m_face->descender); int cellAscender = std::max<int>(m_face->bbox.yMax, m_face->ascender); if (border) { /* add on the strength of any border - the non-bordered font needs aligning with the bordered font by utilising GetTextBaseLine() */ FT_Pos strength = FT_MulFix( m_face->units_per_EM, m_face->size->metrics.y_scale) / 12; if (strength < 128) strength = 128; cellDescender -= strength; cellAscender += strength; m_stroker = g_freeTypeLibrary.GetStroker(); if (m_stroker) FT_Stroker_Set(m_stroker, strength, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); } // scale to pixel sizing, rounding so that maximal extent is obtained float scaler = height / m_face->units_per_EM; cellDescender = MathUtils::round_int(cellDescender * scaler - 0.5f); // round down cellAscender = MathUtils::round_int(cellAscender * scaler + 0.5f); // round up m_cellBaseLine = cellAscender; m_cellHeight = cellAscender - cellDescender; m_height = height; delete(m_texture); m_texture = NULL; delete[] m_char; m_char = NULL; m_maxChars = 0; m_numChars = 0; m_strFilename = strFilename; m_textureHeight = 0; m_textureWidth = ((m_cellHeight * CHARS_PER_TEXTURE_LINE) & ~63) + 64; m_textureWidth = CBaseTexture::PadPow2(m_textureWidth); if (m_textureWidth > g_Windowing.GetMaxTextureSize()) m_textureWidth = g_Windowing.GetMaxTextureSize(); m_textureScaleX = 1.0f / m_textureWidth; // set the posX and posY so that our texture will be created on first character write. m_posX = m_textureWidth; m_posY = -(int)GetTextureLineHeight(); // cache the ellipses width Character *ellipse = GetCharacter(L'.'); if (ellipse) m_ellipsesWidth = ellipse->advance; return true; }
/* reset the blues table when the device transform changes */ static void psh_blues_scale_zones( PSH_Blues blues, FT_Fixed scale, FT_Pos delta ) { FT_UInt count; FT_UInt num; PSH_Blue_Table table = 0; /* */ /* Determine whether we need to suppress overshoots or */ /* not. We simply need to compare the vertical scale */ /* parameter to the raw bluescale value. Here is why: */ /* */ /* We need to suppress overshoots for all pointsizes. */ /* At 300dpi that satisfies: */ /* */ /* pointsize < 240*bluescale + 0.49 */ /* */ /* This corresponds to: */ /* */ /* pixelsize < 1000*bluescale + 49/24 */ /* */ /* scale*EM_Size < 1000*bluescale + 49/24 */ /* */ /* However, for normal Type 1 fonts, EM_Size is 1000! */ /* We thus only check: */ /* */ /* scale < bluescale + 49/24000 */ /* */ /* which we shorten to */ /* */ /* "scale < bluescale" */ /* */ /* Note that `blue_scale' is stored 1000 times its real */ /* value, and that `scale' converts from font units to */ /* fractional pixels. */ /* */ /* 1000 / 64 = 125 / 8 */ if ( scale >= 0x20C49BAL ) blues->no_overshoots = FT_BOOL( scale < blues->blue_scale * 8 / 125 ); else blues->no_overshoots = FT_BOOL( scale * 125 < blues->blue_scale * 8 ); /* */ /* The blue threshold is the font units distance under */ /* which overshoots are suppressed due to the BlueShift */ /* even if the scale is greater than BlueScale. */ /* */ /* It is the smallest distance such that */ /* */ /* dist <= BlueShift && dist*scale <= 0.5 pixels */ /* */ { FT_Int threshold = blues->blue_shift; while ( threshold > 0 && FT_MulFix( threshold, scale ) > 32 ) threshold--; blues->blue_threshold = threshold; } for ( num = 0; num < 4; num++ ) { PSH_Blue_Zone zone; switch ( num ) { case 0: table = &blues->normal_top; break; case 1: table = &blues->normal_bottom; break; case 2: table = &blues->family_top; break; default: table = &blues->family_bottom; break; } zone = table->zones; count = table->count; for ( ; count > 0; count--, zone++ ) { zone->cur_top = FT_MulFix( zone->org_top, scale ) + delta; zone->cur_bottom = FT_MulFix( zone->org_bottom, scale ) + delta; zone->cur_ref = FT_MulFix( zone->org_ref, scale ) + delta; zone->cur_delta = FT_MulFix( zone->org_delta, scale ); /* round scaled reference position */ zone->cur_ref = FT_PIX_ROUND( zone->cur_ref ); #if 0 if ( zone->cur_ref > zone->cur_top ) zone->cur_ref -= 64; else if ( zone->cur_ref < zone->cur_bottom ) zone->cur_ref += 64; #endif } } /* process the families now */ for ( num = 0; num < 2; num++ ) { PSH_Blue_Zone zone1, zone2; FT_UInt count1, count2; PSH_Blue_Table normal, family; switch ( num ) { case 0: normal = &blues->normal_top; family = &blues->family_top; break; default: normal = &blues->normal_bottom; family = &blues->family_bottom; } zone1 = normal->zones; count1 = normal->count; for ( ; count1 > 0; count1--, zone1++ ) { /* try to find a family zone whose reference position is less */ /* than 1 pixel far from the current zone */ zone2 = family->zones; count2 = family->count; for ( ; count2 > 0; count2--, zone2++ ) { FT_Pos Delta; Delta = zone1->org_ref - zone2->org_ref; if ( Delta < 0 ) Delta = -Delta; if ( FT_MulFix( Delta, scale ) < 64 ) { zone1->cur_top = zone2->cur_top; zone1->cur_bottom = zone2->cur_bottom; zone1->cur_ref = zone2->cur_ref; zone1->cur_delta = zone2->cur_delta; break; } } } } }
static void af_latin_metrics_scale_dim( AF_LatinMetrics metrics, AF_Scaler scaler, AF_Dimension dim ) { FT_Fixed scale; FT_Pos delta; AF_LatinAxis axis; FT_UInt nn; if ( dim == AF_DIMENSION_HORZ ) { scale = scaler->x_scale; delta = scaler->x_delta; } else { scale = scaler->y_scale; delta = scaler->y_delta; } axis = &metrics->axis[dim]; if ( axis->org_scale == scale && axis->org_delta == delta ) return; axis->org_scale = scale; axis->org_delta = delta; /* * correct X and Y scale to optimize the alignment of the top of small * letters to the pixel grid */ { AF_LatinAxis Axis = &metrics->axis[AF_DIMENSION_VERT]; AF_LatinBlue blue = NULL; for ( nn = 0; nn < Axis->blue_count; nn++ ) { if ( Axis->blues[nn].flags & AF_LATIN_BLUE_ADJUSTMENT ) { blue = &Axis->blues[nn]; break; } } if ( blue ) { FT_Pos scaled = FT_MulFix( blue->shoot.org, scaler->y_scale ); FT_Pos fitted = ( scaled + 40 ) & ~63; if ( scaled != fitted ) { #if 0 if ( dim == AF_DIMENSION_HORZ ) { if ( fitted < scaled ) scale -= scale / 50; /* scale *= 0.98 */ } else #endif if ( dim == AF_DIMENSION_VERT ) { scale = FT_MulDiv( scale, fitted, scaled ); } } } } axis->scale = scale; axis->delta = delta; if ( dim == AF_DIMENSION_HORZ ) { metrics->root.scaler.x_scale = scale; metrics->root.scaler.x_delta = delta; } else { metrics->root.scaler.y_scale = scale; metrics->root.scaler.y_delta = delta; } /* scale the standard widths */ for ( nn = 0; nn < axis->width_count; nn++ ) { AF_Width width = axis->widths + nn; width->cur = FT_MulFix( width->org, scale ); width->fit = width->cur; } /* an extra-light axis corresponds to a standard width that is */ /* smaller than 0.75 pixels */ axis->extra_light = (FT_Bool)( FT_MulFix( axis->standard_width, scale ) < 32 + 8 ); if ( dim == AF_DIMENSION_VERT ) { /* scale the blue zones */ for ( nn = 0; nn < axis->blue_count; nn++ ) { AF_LatinBlue blue = &axis->blues[nn]; FT_Pos dist; blue->ref.cur = FT_MulFix( blue->ref.org, scale ) + delta; blue->ref.fit = blue->ref.cur; blue->shoot.cur = FT_MulFix( blue->shoot.org, scale ) + delta; blue->shoot.fit = blue->shoot.cur; blue->flags &= ~AF_LATIN_BLUE_ACTIVE; /* a blue zone is only active if it is less than 3/4 pixels tall */ dist = FT_MulFix( blue->ref.org - blue->shoot.org, scale ); if ( dist <= 48 && dist >= -48 ) { FT_Pos delta1, delta2; delta1 = blue->shoot.org - blue->ref.org; delta2 = delta1; if ( delta1 < 0 ) delta2 = -delta2; delta2 = FT_MulFix( delta2, scale ); if ( delta2 < 32 ) delta2 = 0; else if ( delta2 < 64 ) delta2 = 32 + ( ( ( delta2 - 32 ) + 16 ) & ~31 ); else delta2 = FT_PIX_ROUND( delta2 ); if ( delta1 < 0 ) delta2 = -delta2; blue->ref.fit = FT_PIX_ROUND( blue->ref.cur ); blue->shoot.fit = blue->ref.fit + delta2; blue->flags |= AF_LATIN_BLUE_ACTIVE; } } } }