SkShaper::SkShaper(sk_sp<SkTypeface> tf) : fImpl(new Impl) { fImpl->fTypeface = tf ? std::move(tf) : SkTypeface::MakeDefault(); int index; std::unique_ptr<SkStreamAsset> asset(fImpl->fTypeface->openStream(&index)); size_t size = asset->getLength(); SkAutoMalloc autoMalloc(size); // TODO(halcanary): Avoid this malloc+copy. asset->read(autoMalloc.get(), size); asset = nullptr; void* ptr = autoMalloc.get(); hb_blob_t* blob = hb_blob_create((char*)autoMalloc.release(), size, HB_MEMORY_MODE_READONLY, ptr, sk_free); SkASSERT(blob); hb_blob_make_immutable(blob); struct HBFaceDel { void operator()(hb_face_t* f) { hb_face_destroy(f); } }; std::unique_ptr<hb_face_t, HBFaceDel> face(hb_face_create(blob, (unsigned)index)); hb_blob_destroy(blob); SkASSERT(face); if (!face) { return; } hb_face_set_index(face.get(), (unsigned)index); hb_face_set_upem(face.get(), fImpl->fTypeface->getUnitsPerEm()); fImpl->fHarfBuzzFont.reset(hb_font_create(face.get())); SkASSERT(fImpl->fHarfBuzzFont); hb_font_set_scale(fImpl->fHarfBuzzFont.get(), FONT_SIZE_SCALE, FONT_SIZE_SCALE); hb_ot_font_set_funcs(fImpl->fHarfBuzzFont.get()); fImpl->fBuffer.reset(hb_buffer_create()); }
LayoutEngine::LayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, le_int32 typoFlags, LEErrorCode &success) : fHbFont(NULL), fHbBuffer(NULL), fTypoFlags(typoFlags) { if (LE_FAILURE(success)) { return; } fHbBuffer = hb_buffer_create (); if (fHbBuffer == hb_buffer_get_empty ()) { success = LE_MEMORY_ALLOCATION_ERROR; return; } hb_buffer_set_unicode_funcs (fHbBuffer, hb_icu_get_unicode_funcs ()); hb_buffer_set_script (fHbBuffer, hb_icu_script_to_script ((UScriptCode) scriptCode)); /* TODO set language */ hb_face_t *face = hb_face_create_for_tables (icu_le_hb_reference_table, (void *) fontInstance, NULL); fHbFont = hb_font_create (face); hb_face_destroy (face); if (fHbFont == hb_font_get_empty ()) { success = LE_MEMORY_ALLOCATION_ERROR; return; } hb_font_set_funcs (fHbFont, icu_le_hb_get_font_funcs (), (void *) fontInstance, NULL); hb_font_set_scale (fHbFont, +from_float (fontInstance->getXPixelsPerEm () * fontInstance->getScaleFactorX ()), -from_float (fontInstance->getYPixelsPerEm () * fontInstance->getScaleFactorY ())); hb_font_set_ppem (fHbFont, fontInstance->getXPixelsPerEm (), fontInstance->getYPixelsPerEm ()); }
hb_font_t* HarfBuzzFace::createFont() { hb_font_t* font = hb_font_create(m_face); hb_font_set_funcs(font, harfBuzzCoreTextGetFontFuncs(), m_platformData, 0); const float size = m_platformData->m_size; hb_font_set_ppem(font, size, size); const int scale = (1 << 16) * static_cast<int>(size); hb_font_set_scale(font, scale, scale); hb_font_make_immutable(font); return font; }
hb_font_t* HarfBuzzFace::createFont() { HarfBuzzFontData* hbFontData = new HarfBuzzFontData(m_glyphCacheForFaceCacheEntry); m_platformData->setupPaint(&hbFontData->m_paint); hb_font_t* font = hb_font_create(m_face); hb_font_set_funcs(font, harfBuzzSkiaGetFontFuncs(), hbFontData, destroyHarfBuzzFontData); float size = m_platformData->size(); int scale = SkiaScalarToHarfBuzzPosition(size); hb_font_set_scale(font, scale, scale); hb_font_make_immutable(font); return font; }
hb_font_t* createFont(hb_face_t* face, SkPaint* paint, float sizeX, float sizeY) { hb_font_t* font = hb_font_create(face); // Note: this needs to be reworked when we do subpixels int x_ppem = floor(sizeX + 0.5); int y_ppem = floor(sizeY + 0.5); hb_font_set_ppem(font, x_ppem, y_ppem); hb_font_set_scale(font, HBFloatToFixed(sizeX), HBFloatToFixed(sizeY)); HarfBuzzFontData* data = new HarfBuzzFontData(paint); hb_font_set_funcs(font, harfbuzzSkiaGetFontFuncs(), data, destroyHarfBuzzFontData); return font; }
hb_font_t* HarfBuzzFace::createFont() { hb_font_t* font = hb_font_create(m_face); SkPaint* paint = new SkPaint; m_platformData->setupPaint(paint); hb_font_set_funcs(font, harfbuzzSkiaGetFontFuncs(), paint, destroyPaint); float size = m_platformData->size(); if (floorf(size) == size) hb_font_set_ppem(font, size, size); int scale = SkiaScalarToHarfbuzzPosition(size); hb_font_set_scale(font, scale, scale); hb_font_make_immutable(font); return font; }
FT_Error af_get_char_index( AF_StyleMetrics metrics, FT_ULong charcode, FT_ULong *codepoint, FT_Long *y_offset ) { AF_StyleClass style_class; const hb_feature_t* feature; FT_ULong in_idx, out_idx; if ( !metrics ) return FT_THROW( Invalid_Argument ); in_idx = FT_Get_Char_Index( metrics->globals->face, charcode ); style_class = metrics->style_class; feature = features[style_class->coverage]; if ( feature ) { FT_Int upem = (FT_Int)metrics->globals->face->units_per_EM; hb_font_t* font = metrics->globals->hb_font; hb_buffer_t* buf = hb_buffer_create(); uint32_t c = (uint32_t)charcode; hb_glyph_info_t* ginfo; hb_glyph_position_t* gpos; unsigned int gcount; /* we shape at a size of units per EM; this means font units */ hb_font_set_scale( font, upem, upem ); /* XXX: is this sufficient for a single character of any script? */ hb_buffer_set_direction( buf, HB_DIRECTION_LTR ); hb_buffer_set_script( buf, scripts[style_class->script] ); /* we add one character to `buf' ... */ hb_buffer_add_utf32( buf, &c, 1, 0, 1 ); /* ... and apply one feature */ hb_shape( font, buf, feature, 1 ); ginfo = hb_buffer_get_glyph_infos( buf, &gcount ); gpos = hb_buffer_get_glyph_positions( buf, &gcount ); out_idx = ginfo[0].codepoint; /* getting the same index indicates no substitution, */ /* which means that the glyph isn't available in the feature */ if ( in_idx == out_idx ) { *codepoint = 0; *y_offset = 0; } else { *codepoint = out_idx; *y_offset = gpos[0].y_offset; } hb_buffer_destroy( buf ); #ifdef FT_DEBUG_LEVEL_TRACE if ( gcount > 1 ) FT_TRACE1(( "af_get_char_index:" " input character mapped to multiple glyphs\n" )); #endif } else { *codepoint = in_idx; *y_offset = 0; } return FT_Err_Ok; }
void XeTeXFontInst::initialize(const char* pathname, int index, int &status) { TT_Postscript *postTable; TT_OS2* os2Table; FT_Error error; hb_face_t *hbFace; if (!gFreeTypeLibrary) { error = FT_Init_FreeType(&gFreeTypeLibrary); if (error) { fprintf(stderr, "FreeType initialization failed! (%d)\n", error); exit(1); } } error = FT_New_Face(gFreeTypeLibrary, pathname, index, &m_ftFace); if (error) { status = 1; return; } if (!FT_IS_SCALABLE(m_ftFace)) { status = 1; return; } /* for non-sfnt-packaged fonts (presumably Type 1), see if there is an AFM file we can attach */ if (index == 0 && !FT_IS_SFNT(m_ftFace)) { char* afm = xstrdup (xbasename (pathname)); char* p = strrchr (afm, '.'); if (p != NULL && strlen(p) == 4 && tolower(*(p+1)) == 'p' && tolower(*(p+2)) == 'f') strcpy(p, ".afm"); char *fullafm = kpse_find_file (afm, kpse_afm_format, 0); free (afm); if (fullafm) { FT_Attach_File(m_ftFace, fullafm); free (fullafm); } } m_filename = xstrdup(pathname); m_index = index; m_unitsPerEM = m_ftFace->units_per_EM; m_ascent = unitsToPoints(m_ftFace->ascender); m_descent = unitsToPoints(m_ftFace->descender); postTable = (TT_Postscript *) getFontTable(ft_sfnt_post); if (postTable != NULL) { m_italicAngle = Fix2D(postTable->italicAngle); } os2Table = (TT_OS2*) getFontTable(ft_sfnt_os2); if (os2Table) { m_capHeight = unitsToPoints(os2Table->sCapHeight); m_xHeight = unitsToPoints(os2Table->sxHeight); } // Set up HarfBuzz font hbFace = hb_face_create_for_tables(_get_table, m_ftFace, NULL); hb_face_set_index(hbFace, index); hb_face_set_upem(hbFace, m_unitsPerEM); m_hbFont = hb_font_create(hbFace); hb_face_destroy(hbFace); if (hbFontFuncs == NULL) hbFontFuncs = _get_font_funcs(); hb_font_set_funcs(m_hbFont, hbFontFuncs, m_ftFace, NULL); hb_font_set_scale(m_hbFont, m_unitsPerEM, m_unitsPerEM); // We don’t want device tables adjustments hb_font_set_ppem(m_hbFont, 0, 0); return; }
int shape (lua_State *L) { size_t font_l; const char * text = luaL_checkstring(L, 1); const char * font_s = luaL_checklstring(L, 2, &font_l); unsigned int font_index = luaL_checknumber(L, 3); const char * script = luaL_checkstring(L, 4); const char * direction_s = luaL_checkstring(L, 5); const char * lang = luaL_checkstring(L, 6); double point_size = luaL_checknumber(L, 7); const char * featurestring = luaL_checkstring(L, 8); hb_segment_properties_t segment_props; hb_shape_plan_t *shape_plan; hb_direction_t direction; hb_feature_t* features; int nFeatures = 0; unsigned int glyph_count = 0; hb_font_t *hbFont; hb_buffer_t *buf; hb_glyph_info_t *glyph_info; hb_glyph_position_t *glyph_pos; unsigned int j; features = scan_feature_string(featurestring, &nFeatures); if (!strcasecmp(direction_s,"RTL")) direction = HB_DIRECTION_RTL; else if (!strcasecmp(direction_s,"TTB")) direction = HB_DIRECTION_TTB; else direction = HB_DIRECTION_LTR; hb_blob_t* blob = hb_blob_create (font_s, font_l, HB_MEMORY_MODE_WRITABLE, (void*)font_s, NULL); hb_face_t* hbFace = hb_face_create (blob, font_index); hbFont = hb_font_create (hbFace); unsigned int upem = hb_face_get_upem(hbFace); hb_font_set_scale(hbFont, upem, upem); /* Harfbuzz's support for OT fonts is great, but there's currently no support for CFF fonts, so downgrade to Freetype for those. */ if (strncmp(font_s, "OTTO", 4) == 0) { hb_ft_font_set_funcs(hbFont); } else { hb_ot_font_set_funcs(hbFont); } buf = hb_buffer_create(); hb_buffer_add_utf8(buf, text, strlen(text), 0, strlen(text)); hb_buffer_set_script(buf, hb_tag_from_string(script, strlen(script))); hb_buffer_set_direction(buf, direction); hb_buffer_set_language(buf, hb_language_from_string(lang,strlen(lang))); hb_buffer_guess_segment_properties(buf); hb_buffer_get_segment_properties(buf, &segment_props); shape_plan = hb_shape_plan_create_cached(hbFace, &segment_props, features, nFeatures, NULL); int res = hb_shape_plan_execute(shape_plan, hbFont, buf, features, nFeatures); if (direction == HB_DIRECTION_RTL) { hb_buffer_reverse(buf); /* URGH */ } glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); lua_checkstack(L, glyph_count); for (j = 0; j < glyph_count; ++j) { char namebuf[255]; hb_glyph_extents_t extents = {0,0,0,0}; hb_font_get_glyph_extents(hbFont, glyph_info[j].codepoint, &extents); lua_newtable(L); lua_pushstring(L, "name"); hb_font_get_glyph_name( hbFont, glyph_info[j].codepoint, namebuf, 255 ); lua_pushstring(L, namebuf); lua_settable(L, -3); /* We don't apply x-offset and y-offsets for TTB, which is arguably a bug. We should. The reason we don't is that Harfbuzz assumes that you want to shift the character from a top-center baseline to a bottom-left baseline, and gives you offsets which do that. We don't want to do that so we ignore the offsets. I'm told there is a way of configuring HB's idea of the baseline, and we should use that and take out this condition. */ if (direction != HB_DIRECTION_TTB) { if (glyph_pos[j].x_offset) { lua_pushstring(L, "x_offset"); lua_pushnumber(L, glyph_pos[j].x_offset * point_size / upem); lua_settable(L, -3); } if (glyph_pos[j].y_offset) { lua_pushstring(L, "y_offset"); lua_pushnumber(L, glyph_pos[j].y_offset * point_size / upem); lua_settable(L, -3); } } lua_pushstring(L, "codepoint"); lua_pushinteger(L, glyph_info[j].codepoint); lua_settable(L, -3); lua_pushstring(L, "index"); lua_pushinteger(L, glyph_info[j].cluster); lua_settable(L, -3); double height = extents.y_bearing * point_size / upem; double tHeight = extents.height * point_size / upem; double width = glyph_pos[j].x_advance * point_size / upem; /* The PDF model expects us to make positioning adjustments after a glyph is painted. For this we need to know the natural glyph advance. libtexpdf will use this to compute the adjustment. */ double glyphAdvance = hb_font_get_glyph_h_advance(hbFont, glyph_info[j].codepoint) * point_size / upem; if (direction == HB_DIRECTION_TTB) { height = -glyph_pos[j].y_advance * point_size / upem; tHeight = -height; /* Set depth to 0 - depth has no meaning for TTB */ width = glyphAdvance; glyphAdvance = height; } lua_pushstring(L, "glyphAdvance"); lua_pushnumber(L, glyphAdvance); lua_settable(L, -3); lua_pushstring(L, "width"); lua_pushnumber(L, width); lua_settable(L, -3); lua_pushstring(L, "height"); lua_pushnumber(L, height); lua_settable(L, -3); lua_pushstring(L, "depth"); lua_pushnumber(L, -tHeight - height); lua_settable(L, -3); } /* Cleanup */ hb_buffer_destroy(buf); hb_font_destroy(hbFont); hb_shape_plan_destroy(shape_plan); free(features); return glyph_count; }
const char* af_shaper_get_cluster( const char* p, AF_StyleMetrics metrics, void* buf_, unsigned int* count ) { AF_StyleClass style_class; const hb_feature_t* feature; FT_Int upem; const char* q; int len; hb_buffer_t* buf = (hb_buffer_t*)buf_; hb_font_t* font; hb_codepoint_t dummy; upem = (FT_Int)metrics->globals->face->units_per_EM; style_class = metrics->style_class; feature = features[style_class->coverage]; font = metrics->globals->hb_font; /* we shape at a size of units per EM; this means font units */ hb_font_set_scale( font, upem, upem ); while ( *p == ' ' ) p++; /* count bytes up to next space (or end of buffer) */ q = p; while ( !( *q == ' ' || *q == '\0' ) ) GET_UTF8_CHAR( dummy, q ); len = (int)( q - p ); /* feed character(s) to the HarfBuzz buffer */ hb_buffer_clear_contents( buf ); hb_buffer_add_utf8( buf, p, len, 0, len ); /* we let HarfBuzz guess the script and writing direction */ hb_buffer_guess_segment_properties( buf ); /* shape buffer, which means conversion from character codes to */ /* glyph indices, possibly applying a feature */ hb_shape( font, buf, feature, feature ? 1 : 0 ); if ( feature ) { hb_buffer_t* hb_buf = metrics->globals->hb_buf; unsigned int gcount; hb_glyph_info_t* ginfo; unsigned int hb_gcount; hb_glyph_info_t* hb_ginfo; /* we have to check whether applying a feature does actually change */ /* glyph indices; otherwise the affected glyph or glyphs aren't */ /* available at all in the feature */ hb_buffer_clear_contents( hb_buf ); hb_buffer_add_utf8( hb_buf, p, len, 0, len ); hb_buffer_guess_segment_properties( hb_buf ); hb_shape( font, hb_buf, NULL, 0 ); ginfo = hb_buffer_get_glyph_infos( buf, &gcount ); hb_ginfo = hb_buffer_get_glyph_infos( hb_buf, &hb_gcount ); if ( gcount == hb_gcount ) { unsigned int i; for (i = 0; i < gcount; i++ ) if ( ginfo[i].codepoint != hb_ginfo[i].codepoint ) break; if ( i == gcount ) { /* both buffers have identical glyph indices */ hb_buffer_clear_contents( buf ); } } } *count = hb_buffer_get_length( buf ); #ifdef FT_DEBUG_LEVEL_TRACE if ( feature && *count > 1 ) FT_TRACE1(( "af_shaper_get_cluster:" " input character mapped to multiple glyphs\n" )); #endif return q; }