void CTextRenderer::UpdateTextCache_HarfBuzz(array<CHarfbuzzGlyph>* pGlyphChain, const UChar* pTextUTF16, int Start, int Length, bool IsRTL, int FontId) { //Use harfbuzz to shape the text (arabic, tamil, ...) hb_buffer_t* m_pHBBuffer = hb_buffer_create(); hb_buffer_set_unicode_funcs(m_pHBBuffer, hb_icu_get_unicode_funcs()); hb_buffer_set_direction(m_pHBBuffer, (IsRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR)); hb_buffer_set_script(m_pHBBuffer, HB_SCRIPT_ARABIC); hb_buffer_set_language(m_pHBBuffer, hb_language_from_string("ar", 2)); hb_buffer_add_utf16(m_pHBBuffer, pTextUTF16, Length, Start, Length); hb_shape(m_Fonts[FontId]->m_pHBFont, m_pHBBuffer, 0, 0); unsigned int GlyphCount; hb_glyph_info_t* GlyphInfo = hb_buffer_get_glyph_infos(m_pHBBuffer, &GlyphCount); hb_glyph_position_t* GlyphPos = hb_buffer_get_glyph_positions(m_pHBBuffer, &GlyphCount); for(int i=0; i<GlyphCount; i++) { CHarfbuzzGlyph Glyph; Glyph.m_GlyphId = CGlyphId(FontId, GlyphInfo[i].codepoint); Glyph.m_CharPos = GlyphInfo[i].cluster; pGlyphChain->add(Glyph); } hb_buffer_destroy(m_pHBBuffer); }
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 ()); }
static hb_unicode_funcs_t* _get_unicode_funcs(void) { static hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs()); hb_unicode_funcs_set_decompose_compatibility_func(ufuncs, _decompose_compat, NULL, NULL); return ufuncs; }
bool HarfBuzzShaper::shapeHarfBuzzRuns(bool shouldSetDirection) { HarfBuzzScopedPtr<hb_buffer_t> harfBuzzBuffer(hb_buffer_create(), hb_buffer_destroy); hb_buffer_set_unicode_funcs(harfBuzzBuffer.get(), hb_icu_get_unicode_funcs()); for (unsigned i = 0; i < m_harfBuzzRuns.size(); ++i) { unsigned runIndex = m_run.rtl() ? m_harfBuzzRuns.size() - i - 1 : i; HarfBuzzRun* currentRun = m_harfBuzzRuns[runIndex].get(); const SimpleFontData* currentFontData = currentRun->fontData(); if (currentFontData->isSVGFont()) return false; hb_buffer_set_script(harfBuzzBuffer.get(), currentRun->script()); if (shouldSetDirection) hb_buffer_set_direction(harfBuzzBuffer.get(), currentRun->rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); else // Leaving direction to HarfBuzz to guess is *really* bad, but will do for now. hb_buffer_guess_segment_properties(harfBuzzBuffer.get()); // Add a space as pre-context to the buffer. This prevents showing dotted-circle // for combining marks at the beginning of runs. static const uint16_t preContext = ' '; hb_buffer_add_utf16(harfBuzzBuffer.get(), &preContext, 1, 1, 0); if (m_font->isSmallCaps() && u_islower(m_normalizedBuffer[currentRun->startIndex()])) { String upperText = String(m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters()); upperText.makeUpper(); currentFontData = m_font->glyphDataForCharacter(upperText[0], false, SmallCapsVariant).fontData; hb_buffer_add_utf16(harfBuzzBuffer.get(), upperText.characters(), currentRun->numCharacters(), 0, currentRun->numCharacters()); } else hb_buffer_add_utf16(harfBuzzBuffer.get(), m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters(), 0, currentRun->numCharacters()); FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tFontData->platformData()); HarfBuzzFace* face = platformData->harfBuzzFace(); if (!face) return false; if (m_font->fontDescription().orientation() == Vertical) face->setScriptForVerticalGlyphSubstitution(harfBuzzBuffer.get()); HarfBuzzScopedPtr<hb_font_t> harfBuzzFont(face->createFont(), hb_font_destroy); hb_shape(harfBuzzFont.get(), harfBuzzBuffer.get(), m_features.isEmpty() ? 0 : m_features.data(), m_features.size()); currentRun->applyShapeResult(harfBuzzBuffer.get()); setGlyphPositionsForHarfBuzzRun(currentRun, harfBuzzBuffer.get()); hb_buffer_reset(harfBuzzBuffer.get()); } return true; }
ilG_gui_textlayout *ilG_gui_textlayout_new(ilG_context *ctx, const char *lang, enum ilG_gui_textdir direction, const char *script, il_base *font, const ilA_file *tc, double pt, il_string *source) { il_return_null_on_fail(ctx && lang && script && font && source); struct text_globalctx *gctx = il_base_get(&ctx->base, "il.graphics.gui.text.ctx", NULL, NULL); if (!gctx) { gctx = calloc(1, sizeof(struct text_globalctx)); il_return_null_on_fail(!FT_Init_FreeType(&gctx->library)); il_base_set(&ctx->base, "il.graphics.gui.text.ctx", gctx, sizeof(struct text_globalctx), IL_VOID); } ilG_gui_textlayout *l = il_new(&ilG_gui_textlayout_type); l->context = ctx; l->lang = strdup(lang); l->script = strdup(script); l->font = il_ref(font); l->direction = direction; l->pt = pt; l->source = il_string_ref(source); size_t size; void *data = ilA_contents(tc, font, &size); if (!data) { il_error("Could not open font"); return NULL; } il_return_null_on_fail(!FT_New_Memory_Face(gctx->library, data, size, 0, &l->ft_face)); il_return_null_on_fail(!FT_Set_Char_Size(l->ft_face, 0, pt * 64, 0, 0)); l->hb_ft_font = hb_ft_font_create(l->ft_face, NULL); l->buf = hb_buffer_create(); hb_buffer_set_unicode_funcs(l->buf, hb_icu_get_unicode_funcs()); if (direction == ILG_GUI_DEFAULTDIR) { // TODO: find out how to determine this based on script direction = ILG_GUI_LTR; } hb_buffer_set_direction(l->buf, direction + 3); // Hacky solution hb_buffer_set_script(l->buf, hb_script_from_string(script, -1)); hb_buffer_set_language(l->buf, hb_language_from_string(lang, -1)); hb_buffer_add_utf8(l->buf, source->data, source->length, 0, source->length); hb_shape(l->hb_ft_font, l->buf, NULL, 0); l->glyph_info = hb_buffer_get_glyph_infos(l->buf, &l->glyph_count); l->glyph_pos = hb_buffer_get_glyph_positions(l->buf, &l->glyph_count); return l; }
bool HarfBuzzShaper::shapeHarfBuzzRuns() { HarfBuzzScopedPtr<hb_buffer_t> harfbuzzBuffer(hb_buffer_create(), hb_buffer_destroy); hb_buffer_set_unicode_funcs(harfbuzzBuffer.get(), hb_icu_get_unicode_funcs()); if (m_run.rtl() || m_run.directionalOverride()) hb_buffer_set_direction(harfbuzzBuffer.get(), m_run.rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); for (unsigned i = 0; i < m_harfbuzzRuns.size(); ++i) { unsigned runIndex = m_run.rtl() ? m_harfbuzzRuns.size() - i - 1 : i; HarfBuzzRun* currentRun = m_harfbuzzRuns[runIndex].get(); const SimpleFontData* currentFontData = currentRun->fontData(); if (m_font->isSmallCaps() && u_islower(m_normalizedBuffer[currentRun->startIndex()])) { String upperText = String(m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters()); upperText.makeUpper(); currentFontData = m_font->glyphDataForCharacter(upperText[0], false, SmallCapsVariant).fontData; hb_buffer_add_utf16(harfbuzzBuffer.get(), upperText.characters(), currentRun->numCharacters(), 0, currentRun->numCharacters()); } else hb_buffer_add_utf16(harfbuzzBuffer.get(), m_normalizedBuffer.get() + currentRun->startIndex(), currentRun->numCharacters(), 0, currentRun->numCharacters()); FontPlatformData* platformData = const_cast<FontPlatformData*>(¤tFontData->platformData()); HarfBuzzNGFace* face = platformData->harfbuzzFace(); if (!face) return false; HarfBuzzScopedPtr<hb_font_t> harfbuzzFont(face->createFont(), hb_font_destroy); hb_shape(harfbuzzFont.get(), harfbuzzBuffer.get(), m_features.isEmpty() ? 0 : m_features.data(), m_features.size()); currentRun->applyShapeResult(harfbuzzBuffer.get()); setGlyphPositionsForHarfBuzzRun(currentRun, harfbuzzBuffer.get()); hb_buffer_reset(harfbuzzBuffer.get()); if (m_run.rtl() || m_run.directionalOverride()) hb_buffer_set_direction(harfbuzzBuffer.get(), m_run.rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); } return true; }
bool sui_layout_init(sui_layout *layout, sui_font *font, const sui_layout_format *fmt, const char *text, size_t length, char **error) { static const int text_dir_table[] = { HB_DIRECTION_LTR, HB_DIRECTION_RTL, HB_DIRECTION_TTB, HB_DIRECTION_BTT }; memset(layout, 0, sizeof(sui_layout)); layout->fmt = *fmt; layout->font = font; FT_Error fterr; if ((fterr = FT_Set_Pixel_Sizes(font->face, 0, fmt->size))) { *error = sui_aprintf("FT_Set_Char_Size: %i\n", fterr); return false; } layout->buffer = hb_buffer_create(); hb_buffer_set_unicode_funcs(layout->buffer, hb_icu_get_unicode_funcs()); hb_buffer_set_direction(layout->buffer, text_dir_table[fmt->dir]); hb_script_t script = hb_script_from_string(fmt->script, -1); if (script == HB_SCRIPT_INVALID || script == HB_SCRIPT_UNKNOWN) { *error = sui_aprintf("Invalid script: %s\n", fmt->script); return false; } hb_buffer_set_script(layout->buffer, script); hb_language_t lang = hb_language_from_string(fmt->lang, -1); if (lang == HB_LANGUAGE_INVALID) { *error = sui_aprintf("Invalid language: %s\n", fmt->lang); return false; } hb_buffer_set_language(layout->buffer, lang); hb_buffer_add_utf8(layout->buffer, text, length, 0, length); hb_shape(font->hb_font, layout->buffer, NULL, 0); layout->infos = hb_buffer_get_glyph_infos(layout->buffer, &layout->count); layout->positions = hb_buffer_get_glyph_positions(layout->buffer, &layout->count); int pen_x = 0, pen_y = 0; int lx = 0, ly = 0, hx = 0, hy = 0; for (unsigned i = 0; i < layout->count; i++) { uint32_t codepoint = layout->infos[i].codepoint; if ((fterr = FT_Load_Glyph(font->face, codepoint, FT_LOAD_DEFAULT))) { *error = sui_aprintf("FT_Load_Glyph: %i\n", fterr); return false; } if (pen_x < lx) { lx = pen_x; } if (pen_y < ly) { ly = pen_y; } int w = font->face->glyph->metrics.width, h = font->face->glyph->metrics.height; if (pen_x + w > hx) { hx = pen_x + w; } if (pen_y + h > hy) { hy = pen_y + h; } pen_x += layout->positions[i].x_advance; pen_y += layout->positions[i].y_advance; } layout->origin = sui_mkpoint(-lx, -ly); layout->size = sui_mkpoint(hx-lx, hy-ly); return true; }
bool HarfBuzzShaper::setupHarfBuzzRun() { m_startIndexOfCurrentRun += m_numCharactersOfCurrentRun; // Iterate through the text to take the largest range that stays within // a single font. int endOfRunIndex = m_normalizedBufferLength - m_startIndexOfCurrentRun; SurrogatePairAwareTextIterator iterator(m_normalizedBuffer.get() + m_startIndexOfCurrentRun, 0, endOfRunIndex, endOfRunIndex); UChar32 character; unsigned clusterLength = 0; if (!iterator.consume(character, clusterLength)) return false; m_currentFontData = m_font->glyphDataForCharacter(character, false).fontData; UErrorCode errorCode = U_ZERO_ERROR; UScriptCode currentScript = uscript_getScript(character, &errorCode); if (U_FAILURE(errorCode)) return false; if (currentScript == USCRIPT_INHERITED) currentScript = USCRIPT_COMMON; for (iterator.advance(clusterLength); iterator.consume(character, clusterLength); iterator.advance(clusterLength)) { const SimpleFontData* nextFontData = m_font->glyphDataForCharacter(character, false).fontData; if (nextFontData != m_currentFontData) break; UScriptCode nextScript = uscript_getScript(character, &errorCode); if (U_FAILURE(errorCode)) return false; if (currentScript == nextScript || nextScript == USCRIPT_INHERITED || nextScript == USCRIPT_COMMON) continue; if (currentScript == USCRIPT_COMMON) currentScript = nextScript; else break; } m_numCharactersOfCurrentRun = iterator.currentCharacter(); if (!m_harfbuzzBuffer) { m_harfbuzzBuffer = hb_buffer_create(); hb_buffer_set_unicode_funcs(m_harfbuzzBuffer, hb_icu_get_unicode_funcs()); } else hb_buffer_reset(m_harfbuzzBuffer); hb_buffer_set_script(m_harfbuzzBuffer, hb_icu_script_to_script(currentScript)); // WebKit always sets direction to LTR during width calculation. // We only set direction when direction is explicitly set to RTL so that // preventng wrong width calculation. if (m_run.rtl()) hb_buffer_set_direction(m_harfbuzzBuffer, HB_DIRECTION_RTL); // Determine whether this run needs to be converted to small caps. // nextScriptRun() will always send us a run of the same case, because a // case change while in small-caps mode always results in different // FontData, so we only need to check the first character's case. if (m_font->isSmallCaps() && u_islower(m_normalizedBuffer[m_startIndexOfCurrentRun])) { String upperText = String(m_normalizedBuffer.get() + m_startIndexOfCurrentRun, m_numCharactersOfCurrentRun); upperText.makeUpper(); m_currentFontData = m_font->glyphDataForCharacter(upperText[0], false, SmallCapsVariant).fontData; hb_buffer_add_utf16(m_harfbuzzBuffer, upperText.characters(), m_numCharactersOfCurrentRun, 0, m_numCharactersOfCurrentRun); } else hb_buffer_add_utf16(m_harfbuzzBuffer, m_normalizedBuffer.get() + m_startIndexOfCurrentRun, m_numCharactersOfCurrentRun, 0, m_numCharactersOfCurrentRun); return true; }