void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount, float x, float y, int bidiFlags, const Paint& origPaint, Typeface* typeface) { // minikin may modify the original paint Paint paint(origPaint); Layout layout; MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount); size_t nGlyphs = layout.nGlyphs(); std::unique_ptr<uint16_t[]> glyphs(new uint16_t[nGlyphs]); std::unique_ptr<float[]> pos(new float[nGlyphs * 2]); x += MinikinUtils::xOffsetForTextAlign(&paint, layout); MinikinRect bounds; layout.getBounds(&bounds); if (!drawTextAbsolutePos()) { bounds.offset(x, y); } // Set align to left for drawing, as we don't want individual // glyphs centered or right-aligned; the offset above takes // care of all alignment. paint.setTextAlign(Paint::kLeft_Align); DrawTextFunctor f(layout, this, glyphs.get(), pos.get(), paint, x, y, bounds, layout.getAdvance()); MinikinUtils::forFontRun(layout, &paint, f); }
void MinikinRect::join(const MinikinRect& r) { if (isEmpty()) { set(r); } else if (!r.isEmpty()) { mLeft = std::min(mLeft, r.mLeft); mTop = std::min(mTop, r.mTop); mRight = std::max(mRight, r.mRight); mBottom = std::max(mBottom, r.mBottom); } }
void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t bufSize, bool isRtl, LayoutContext* ctx) { hb_buffer_t* buffer = LayoutEngine::getInstance().hbBuffer; vector<FontCollection::Run> items; mCollection->itemize(buf + start, count, ctx->style, &items); if (isRtl) { std::reverse(items.begin(), items.end()); } vector<hb_feature_t> features; // Disable default-on non-required ligature features if letter-spacing // See http://dev.w3.org/csswg/css-text-3/#letter-spacing-property // "When the effective spacing between two characters is not zero (due to // either justification or a non-zero value of letter-spacing), user agents // should not apply optional ligatures." if (fabs(ctx->paint.letterSpacing) > 0.03) { static const hb_feature_t no_liga = { HB_TAG('l', 'i', 'g', 'a'), 0, 0, ~0u }; static const hb_feature_t no_clig = { HB_TAG('c', 'l', 'i', 'g'), 0, 0, ~0u }; features.push_back(no_liga); features.push_back(no_clig); } addFeatures(ctx->paint.fontFeatureSettings, &features); double size = ctx->paint.size; double scaleX = ctx->paint.scaleX; double letterSpace = ctx->paint.letterSpacing * size * scaleX; double letterSpaceHalfLeft; if ((ctx->paint.paintFlags & LinearTextFlag) == 0) { letterSpace = round(letterSpace); letterSpaceHalfLeft = floor(letterSpace * 0.5); } else { letterSpaceHalfLeft = letterSpace * 0.5; } double letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft; float x = mAdvance; float y = 0; for (size_t run_ix = 0; run_ix < items.size(); run_ix++) { FontCollection::Run &run = items[run_ix]; if (run.fakedFont.font == NULL) { ALOGE("no font for run starting u+%04x length %d", buf[run.start], run.end - run.start); continue; } int font_ix = findFace(run.fakedFont, ctx); ctx->paint.font = mFaces[font_ix].font; ctx->paint.fakery = mFaces[font_ix].fakery; hb_font_t* hbFont = ctx->hbFonts[font_ix]; #ifdef VERBOSE std::cout << "Run " << run_ix << ", font " << font_ix << " [" << run.start << ":" << run.end << "]" << std::endl; #endif hb_font_set_ppem(hbFont, size * scaleX, size); hb_font_set_scale(hbFont, HBFloatToFixed(size * scaleX), HBFloatToFixed(size)); // TODO: if there are multiple scripts within a font in an RTL run, // we need to reorder those runs. This is unlikely with our current // font stack, but should be done for correctness. ssize_t srunend; for (ssize_t srunstart = run.start; srunstart < run.end; srunstart = srunend) { srunend = srunstart; hb_script_t script = getScriptRun(buf + start, run.end, &srunend); hb_buffer_reset(buffer); hb_buffer_set_script(buffer, script); hb_buffer_set_direction(buffer, isRtl? HB_DIRECTION_RTL : HB_DIRECTION_LTR); FontLanguage language = ctx->style.getLanguage(); if (language) { string lang = language.getString(); hb_buffer_set_language(buffer, hb_language_from_string(lang.c_str(), -1)); } hb_buffer_add_utf16(buffer, buf, bufSize, srunstart + start, srunend - srunstart); hb_shape(hbFont, buffer, features.empty() ? NULL : &features[0], features.size()); unsigned int numGlyphs; hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs); hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL); if (numGlyphs) { mAdvances[info[0].cluster - start] += letterSpaceHalfLeft; x += letterSpaceHalfLeft; } for (unsigned int i = 0; i < numGlyphs; i++) { #ifdef VERBOSE std::cout << positions[i].x_advance << " " << positions[i].y_advance << " " << positions[i].x_offset << " " << positions[i].y_offset << std::endl; std::cout << "DoLayout " << info[i].codepoint << ": " << HBFixedToFloat(positions[i].x_advance) << "; " << positions[i].x_offset << ", " << positions[i].y_offset << std::endl; #endif if (i > 0 && info[i - 1].cluster != info[i].cluster) { mAdvances[info[i - 1].cluster - start] += letterSpaceHalfRight; mAdvances[info[i].cluster - start] += letterSpaceHalfLeft; x += letterSpace; } hb_codepoint_t glyph_ix = info[i].codepoint; float xoff = HBFixedToFloat(positions[i].x_offset); float yoff = -HBFixedToFloat(positions[i].y_offset); xoff += yoff * ctx->paint.skewX; LayoutGlyph glyph = {font_ix, glyph_ix, x + xoff, y + yoff}; mGlyphs.push_back(glyph); float xAdvance = HBFixedToFloat(positions[i].x_advance); if ((ctx->paint.paintFlags & LinearTextFlag) == 0) { xAdvance = roundf(xAdvance); } MinikinRect glyphBounds; ctx->paint.font->GetBounds(&glyphBounds, glyph_ix, ctx->paint); glyphBounds.offset(x + xoff, y + yoff); mBounds.join(glyphBounds); mAdvances[info[i].cluster - start] += xAdvance; x += xAdvance; } if (numGlyphs) { mAdvances[info[numGlyphs - 1].cluster - start] += letterSpaceHalfRight; x += letterSpaceHalfRight; } } } mAdvance = x; }