void Font::drawSimpleText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { // This glyph buffer holds our glyphs+advances+font data for each glyph. GlyphBuffer glyphBuffer; float startX = point.x(); WidthIterator it(this, run); it.advance(from); float beforeWidth = it.m_runWidthSoFar; it.advance(to, &glyphBuffer); // We couldn't generate any glyphs for the run. Give up. if (glyphBuffer.isEmpty()) return; float afterWidth = it.m_runWidthSoFar; if (run.rtl()) { float finalRoundingWidth = it.m_finalRoundingWidth; it.advance(run.length()); startX += finalRoundingWidth + it.m_runWidthSoFar - afterWidth; } else startX += beforeWidth; // Swap the order of the glyphs if right-to-left. if (run.rtl()) for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end) glyphBuffer.swap(i, end); // Calculate the starting point of the glyphs to be displayed by adding // all the advances up to the first glyph. FloatPoint startPoint(startX, point.y()); drawGlyphBuffer(context, glyphBuffer, run, startPoint); }
float Font::getGlyphsAndAdvancesForSimpleText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const { float initialAdvance; WidthIterator it(this, run, 0, false, forTextEmphasis); // FIXME: Using separate glyph buffers for the prefix and the suffix is incorrect when kerning or // ligatures are enabled. GlyphBuffer localGlyphBuffer; it.advance(from, &localGlyphBuffer); float beforeWidth = it.m_runWidthSoFar; it.advance(to, &glyphBuffer); if (glyphBuffer.isEmpty()) return 0; float afterWidth = it.m_runWidthSoFar; if (run.rtl()) { float finalRoundingWidth = it.m_finalRoundingWidth; it.advance(run.length(), &localGlyphBuffer); initialAdvance = finalRoundingWidth + it.m_runWidthSoFar - afterWidth; } else { initialAdvance = beforeWidth; } if (run.rtl()) glyphBuffer.reverse(0, glyphBuffer.size()); return initialAdvance; }
TextRunWalker(const TextRun& run, unsigned startingX, const Font* font) : m_font(font) , m_run(run) , m_startingX(startingX) , m_offsetX(m_startingX) , m_iterateBackwards(run.rtl()) { memset(&m_item, 0, sizeof(m_item)); // We cannot know, ahead of time, how many glyphs a given script run // will produce. We take a guess that script runs will not produce more // than twice as many glyphs as there are code points and fallback if // we find that we are wrong. m_maxGlyphs = run.length() * 2; createGlyphArrays(); m_item.log_clusters = new unsigned short[run.length()]; m_item.face = 0; m_item.font = allocHarfbuzzFont(); m_item.string = run.characters(); m_item.stringLength = run.length(); m_item.item.bidiLevel = run.rtl(); reset(); }
float Font::getGlyphsAndAdvancesForSimpleText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const { float initialAdvance; WidthIterator it(this, run, 0, false, forTextEmphasis); it.advance(from); float beforeWidth = it.m_runWidthSoFar; it.advance(to, &glyphBuffer); if (glyphBuffer.isEmpty()) return 0; float afterWidth = it.m_runWidthSoFar; if (run.rtl()) { it.advance(run.length()); initialAdvance = it.m_runWidthSoFar - afterWidth; } else initialAdvance = beforeWidth; if (run.rtl()) { for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end) glyphBuffer.swap(i, end); } return initialAdvance; }
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { #if OS(WINDOWS) // This glyph buffer holds our glyphs + advances + font data for each glyph. GlyphBuffer glyphBuffer; float startX = point.x(); UniscribeController controller(this, run); controller.advance(from); float beforeWidth = controller.runWidthSoFar(); controller.advance(to, &glyphBuffer); // We couldn't generate any glyphs for the run. Give up. if (glyphBuffer.isEmpty()) return; float afterWidth = controller.runWidthSoFar(); if (run.rtl()) { controller.advance(run.length()); startX += controller.runWidthSoFar() - afterWidth; } else startX += beforeWidth; // Draw the glyph buffer now at the starting point returned in startX. FloatPoint startPoint(startX, point.y()); drawGlyphBuffer(context, glyphBuffer, run, startPoint); #else notImplemented(); #endif }
UniscribeHelperTextRun::UniscribeHelperTextRun(const TextRun& run, const Font& font) : UniscribeHelper(0, run.length(), run.rtl(), font.primaryFont()->platformData().hfont(), font.primaryFont()->platformData().scriptCache(), font.primaryFont()->platformData().scriptFontProperties(), font.primaryFont()->spaceGlyph()) , m_font(&font) , m_fontIndex(0) { if (run.is8Bit()) { m_stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), run.length()); setInput(m_stringFor8BitRun.characters16()); } else { setInput(run.characters16()); } setDirectionalOverride(run.directionalOverride()); setLetterSpacing(font.letterSpacing()); setSpaceWidth(font.spaceWidth()); setWordSpacing(font.wordSpacing()); setAscent(font.fontMetrics().ascent()); setRangeProperties(font.fontDescription().featureSettings()); init(); // Expansion is the amount to add to make justification happen. This // should be done after Init() so all the runs are already measured. if (run.expansion() > 0) justify(run.expansion()); }
static void adjustOffsetsForTextDrawing(const TextRun& run, int& from, int& to) { if (run.rtl()) { from = run.length() - from; to = run.length() - to; } }
PassRefPtr<ShapeResult> ShapeResult::createForTabulationCharacters(const Font* font, const TextRun& textRun, float positionOffset, unsigned count) { const SimpleFontData* fontData = font->primaryFont(); OwnPtr<ShapeResult::RunInfo> run = adoptPtr(new ShapeResult::RunInfo(fontData, // Tab characters are always LTR or RTL, not TTB, even when isVerticalAnyUpright(). textRun.rtl() ? HB_DIRECTION_RTL : HB_DIRECTION_LTR, HB_SCRIPT_COMMON, 0, count, count)); float position = textRun.xPos() + positionOffset; float startPosition = position; for (unsigned i = 0; i < count; i++) { float advance = font->tabWidth(*fontData, textRun.getTabSize(), position); run->m_glyphData[i].characterIndex = i; run->setGlyphAndPositions(i, fontData->spaceGlyph(), advance, 0, 0); position += advance; } run->m_width = position - startPosition; RefPtr<ShapeResult> result = ShapeResult::create(font, count, textRun.direction()); result->m_width = run->m_width; result->m_numGlyphs = count; ASSERT(result->m_numGlyphs == count); // no overflow result->m_hasVerticalOffsets = fontData->platformData().isVerticalAnyUpright(); result->m_runs.append(run.release()); return result.release(); }
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { if (to < 0) to = run.length(); if (from < 0) from = 0; TextRunComponents components; int w = generateComponents(&components, *this, run); int curPos = 0; for (int i = 0; i < (int)components.size(); ++i) { const TextRunComponent& comp = components.at(i); int len = comp.textLength(); int curEnd = curPos + len; if (curPos < to && from < curEnd && !comp.isSpace()) { FloatPoint pt = point; if (run.rtl()) pt.setX(point.x() + w - comp.m_offset - comp.m_width); else pt.setX(point.x() + comp.m_offset); drawSimpleText(context, comp.m_textRun, pt, from - curPos, std::min(to, curEnd) - curPos); } curPos += len; if (from < curPos) from = curPos; } }
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { // This glyph buffer holds our glyphs + advances + font data for each glyph. GlyphBuffer glyphBuffer; float startX = point.x(); CoreTextController controller(this, run); controller.advance(from); float beforeWidth = controller.runWidthSoFar(); controller.advance(to, &glyphBuffer); // We couldn't generate any glyphs for the run. Give up. if (glyphBuffer.isEmpty()) return; float afterWidth = controller.runWidthSoFar(); if (run.rtl()) { startX += controller.totalWidth() + controller.finalRoundingWidth() - afterWidth; for (int i = 0, end = glyphBuffer.size() - 1; i < glyphBuffer.size() / 2; ++i, --end) glyphBuffer.swap(i, end); } else startX += beforeWidth; // Draw the glyph buffer now at the starting point returned in startX. FloatPoint startPoint(startX, point.y()); drawGlyphBuffer(context, glyphBuffer, run, startPoint); }
float Font::getGlyphsAndAdvancesForSimpleText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const { WidthIterator it(this, run, 0, false, forTextEmphasis); GlyphBuffer localGlyphBuffer; it.advance(run.length(), &localGlyphBuffer); if (localGlyphBuffer.isEmpty()) return 0; float totalWidth = it.m_runWidthSoFar; float beforeWidth = 0; int glyphPos = 0; for (; glyphPos < localGlyphBuffer.size() && it.m_characterIndexOfGlyph[glyphPos] < from; ++glyphPos) beforeWidth += localGlyphBuffer.advanceAt(glyphPos).width(); int glyphFrom = glyphPos; float afterWidth = totalWidth; glyphPos = localGlyphBuffer.size() - 1; for (; glyphPos >= glyphFrom && it.m_characterIndexOfGlyph[glyphPos] >= to; --glyphPos) afterWidth -= localGlyphBuffer.advanceAt(glyphPos).width(); int glyphTo = glyphPos + 1; glyphBuffer.add(&localGlyphBuffer, glyphFrom, glyphTo - glyphFrom); if (run.rtl()) { glyphBuffer.reverse(0, glyphBuffer.size()); return totalWidth - afterWidth; } return beforeWidth; }
FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const { #if OS(WINDOWS) || OS(DARWIN) ComplexTextController it(this, run); it.advance(from); float beforeWidth = it.runWidthSoFar(); it.advance(to); float afterWidth = it.runWidthSoFar(); // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning if (run.rtl()) { #if OS(WINDOWS) it.advance(run.length()); float totalWidth = it.runWidthSoFar(); return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h); #else float totalWidth = it.totalWidth(); return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h); #endif } return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h); #else notImplemented(); return FloatRect(); #endif }
FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const { GlyphBuffer glyphBuffer; WidthIterator it(this, run); it.advance(run.length(), &glyphBuffer); float totalWidth = it.m_runWidthSoFar; float beforeWidth = 0; int glyphPos = 0; for (; glyphPos < glyphBuffer.size() && it.m_characterIndexOfGlyph[glyphPos] < from; ++glyphPos) beforeWidth += glyphBuffer.advanceAt(glyphPos).width(); int glyphFrom = glyphPos; float afterWidth = totalWidth; glyphPos = glyphBuffer.size() - 1; for (; glyphPos >= glyphFrom && it.m_characterIndexOfGlyph[glyphPos] >= to; --glyphPos) afterWidth -= glyphBuffer.advanceAt(glyphPos).width(); // Using roundf() rather than ceilf() for the right edge as a compromise to ensure correct caret positioning. if (run.rtl()) { return FloatRect(floorf(point.x() + totalWidth - afterWidth), point.y(), roundf(point.x() + totalWidth - beforeWidth) - floorf(point.x() + totalWidth - afterWidth), h); } return FloatRect(floorf(point.x() + beforeWidth), point.y(), roundf(point.x() + afterWidth) - floorf(point.x() + beforeWidth), h); }
int ShapeResult::offsetForPosition(Vector<RefPtr<ShapeResult>>& results, const TextRun& run, float targetX) { unsigned totalOffset; if (run.rtl()) { totalOffset = run.length(); for (unsigned i = results.size(); i; --i) { const RefPtr<ShapeResult>& wordResult = results[i - 1]; if (!wordResult) continue; totalOffset -= wordResult->numCharacters(); if (targetX >= 0 && targetX <= wordResult->width()) { int offsetForWord = wordResult->offsetForPosition(targetX); return totalOffset + offsetForWord; } targetX -= wordResult->width(); } } else { totalOffset = 0; for (auto& wordResult : results) { if (!wordResult) continue; int offsetForWord = wordResult->offsetForPosition(targetX); ASSERT(offsetForWord >= 0); totalOffset += offsetForWord; if (targetX >= 0 && targetX <= wordResult->width()) return totalOffset; targetX -= wordResult->width(); } } return totalOffset; }
float ShapeResult::fillGlyphBuffer(Vector<RefPtr<ShapeResult>>& results, GlyphBuffer* glyphBuffer, const TextRun& textRun, unsigned from, unsigned to) { float advance = 0; if (textRun.rtl()) { unsigned wordOffset = textRun.length(); for (unsigned j = 0; j < results.size(); j++) { unsigned resolvedIndex = results.size() - 1 - j; RefPtr<ShapeResult>& wordResult = results[resolvedIndex]; for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { advance += wordResult->fillGlyphBufferForRun<RTL>(glyphBuffer, wordResult->m_runs[i].get(), advance, from, to, wordOffset - wordResult->numCharacters()); } wordOffset -= wordResult->numCharacters(); } } else { unsigned wordOffset = 0; for (unsigned j = 0; j < results.size(); j++) { RefPtr<ShapeResult>& wordResult = results[j]; for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { advance += wordResult->fillGlyphBufferForRun<LTR>(glyphBuffer, wordResult->m_runs[i].get(), advance, from, to, wordOffset); } wordOffset += wordResult->numCharacters(); } } return advance; }
FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const { GlyphBuffer glyphBuffer; WidthIterator it(this, run); it.advance(from, &glyphBuffer); float beforeWidth = it.m_runWidthSoFar; it.advance(to, &glyphBuffer); float afterWidth = it.m_runWidthSoFar; // Using roundf() rather than ceilf() for the right edge as a compromise to // ensure correct caret positioning. // Use LayoutUnit::epsilon() to ensure that values that cannot be stored as // an integer are floored to n and not n-1 due to floating point imprecision. if (run.rtl()) { it.advance(run.length(), &glyphBuffer); float totalWidth = it.m_runWidthSoFar; float pixelAlignedX = floorf(point.x() + totalWidth - afterWidth + LayoutUnit::epsilon()); return FloatRect(pixelAlignedX, point.y(), roundf(point.x() + totalWidth - beforeWidth) - pixelAlignedX, h); } float pixelAlignedX = floorf(point.x() + beforeWidth + LayoutUnit::epsilon()); return FloatRect(pixelAlignedX, point.y(), roundf(point.x() + afterWidth) - pixelAlignedX, h); }
FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& point, int h, int from, int to) const { SkPaint paint; SkScalar width, left; SkPaint::FontMetrics metrics; primaryFont()->platformData().setupPaint(&paint); SkScalar spacing = paint.getFontMetrics(&metrics); float beforeWidth = SkScalarToFloat(paint.measureText(run.characters(), from << 1)); float afterWidth = SkScalarToFloat(paint.measureText(run.characters(), to << 1)); if (run.rtl()) { float totalWidth = SkScalarToFloat(paint.measureText(run.characters(), run.length() << 1)); return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), //roundf(SkScalarToFloat(spacing))); // Don't compute rect height, but use h. h); } else { return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), //roundf(SkScalarToFloat(spacing))); // Don't compute rect height, but use h. h); } }
FloatRect Font::selectionRectForTextUsingSVGFont(const TextRun& run, const IntPoint& point, int height, int from, int to) const { int charsConsumed; String glyphName; return FloatRect(point.x() + floatWidthOfSubStringUsingSVGFont(this, run, 0, run.rtl() ? to : 0, run.rtl() ? run.length() : from, charsConsumed, glyphName), point.y(), floatWidthOfSubStringUsingSVGFont(this, run, 0, from, to, charsConsumed, glyphName), height); }
// Return the rectangle for selecting the given range of code-points in the TextRun. FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int height, int from, int to) const { ComplexTextController controller(this, run, 0, 0); if (run.rtl()) controller.setupForRTL(); return controller.selectionRect(point, height, from, to); }
void ShapeResult::applySpacing(ShapeResultSpacing& spacing, const TextRun& textRun) { float offsetX, offsetY; float& offset = spacing.isVerticalOffset() ? offsetY : offsetX; float totalSpace = 0; for (auto& run : m_runs) { if (!run) continue; float totalSpaceForRun = 0; for (size_t i = 0; i < run->m_glyphData.size(); i++) { HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; // Skip if it's not a grapheme cluster boundary. if (i + 1 < run->m_glyphData.size() && glyphData.characterIndex == run->m_glyphData[i + 1].characterIndex) { // In RTL, marks need the same letter-spacing offset as the base. if (textRun.rtl() && spacing.letterSpacing()) { offsetX = offsetY = 0; offset = spacing.letterSpacing(); glyphData.offset.expand(offsetX, offsetY); } } else { offsetX = offsetY = 0; float space = spacing.computeSpacing( textRun, run->m_startIndex + glyphData.characterIndex, offset); glyphData.advance += space; totalSpaceForRun += space; if (textRun.rtl()) { // In RTL, spacing should be added to left side of glyphs. offset += space; } glyphData.offset.expand(offsetX, offsetY); } m_hasVerticalOffsets |= (glyphData.offset.height() != 0); } run->m_width += totalSpaceForRun; totalSpace += totalSpaceForRun; } m_width += totalSpace; if (spacing.isVerticalOffset()) m_glyphBoundingBox.setHeight(m_glyphBoundingBox.height() + totalSpace); else m_glyphBoundingBox.setWidth(m_glyphBoundingBox.width() + totalSpace); }
void Font::drawComplexText(GraphicsContext* gc, const TextRun& run, const FloatPoint& point, int from, int to) const { if (!run.length()) return; SkCanvas* canvas = gc->platformContext()->canvas(); TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode(); bool fill = textMode & TextModeFill; bool stroke = (textMode & TextModeStroke) && gc->platformContext()->getStrokeStyle() != NoStroke && gc->platformContext()->getStrokeThickness() > 0; if (!fill && !stroke) return; SkPaint strokePaint, fillPaint; if (fill) { gc->platformContext()->setupPaintForFilling(&fillPaint); setupForTextPainting(&fillPaint, gc->fillColor().rgb()); } if (stroke) { gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0); setupForTextPainting(&strokePaint, gc->strokeColor().rgb()); } ComplexTextController controller(run, point.x(), point.y(), this); controller.setWordSpacingAdjustment(wordSpacing()); controller.setLetterSpacingAdjustment(letterSpacing()); controller.setPadding(run.expansion()); if (run.rtl()) { // FIXME: this causes us to shape the text twice -- once to compute the width and then again // below when actually rendering. Change ComplexTextController to match platform/mac and // platform/chromium/win by having it store the shaped runs, so we can reuse the results. controller.reset(point.x() + controller.widthOfFullRun()); // We need to set the padding again because ComplexTextController layout consumed the value. // Fixing the above problem would help here too. controller.setPadding(run.expansion()); } while (controller.nextScriptRun()) { if (fill) { controller.fontPlatformDataForScriptRun()->setupPaint(&fillPaint); adjustTextRenderMode(&fillPaint, gc->platformContext()); canvas->drawPosText(controller.glyphs(), controller.length() << 1, controller.positions(), fillPaint); } if (stroke) { controller.fontPlatformDataForScriptRun()->setupPaint(&strokePaint); adjustTextRenderMode(&strokePaint, gc->platformContext()); canvas->drawPosText(controller.glyphs(), controller.length() << 1, controller.positions(), strokePaint); } } }
// Return the code point index for the given |x| offset into the text run. int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, bool includePartialGlyphs) const { // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem. int targetX = static_cast<int>(xFloat); // (Mac code ignores includePartialGlyphs, and they don't know what it's // supposed to do, so we just ignore it as well.) ComplexTextController controller(run, 0, 0, this); controller.setWordSpacingAdjustment(wordSpacing()); controller.setLetterSpacingAdjustment(letterSpacing()); controller.setPadding(run.expansion()); if (run.rtl()) { // See FIXME in drawComplexText. controller.reset(controller.widthOfFullRun()); controller.setPadding(run.expansion()); } unsigned basePosition = 0; int x = controller.offsetX(); while (controller.nextScriptRun()) { int nextX = controller.offsetX(); if (std::min(x, nextX) <= targetX && targetX <= std::max(x, nextX)) { // The x value in question is within this script run. const int glyphIndex = glyphIndexForXPositionInScriptRun(controller, targetX); // Now that we have a glyph index, we have to turn that into a // code-point index. Because of ligatures, several code-points may // have gone into a single glyph. We iterate over the clusters log // and find the first code-point which contributed to the glyph. // Some shapers (i.e. Khmer) will produce cluster logs which report // that /no/ code points contributed to certain glyphs. Because of // this, we take any code point which contributed to the glyph in // question, or any subsequent glyph. If we run off the end, then // we take the last code point. const unsigned short* log = controller.logClusters(); for (unsigned j = 0; j < controller.numCodePoints(); ++j) { if (log[j] >= glyphIndex) return basePosition + j; } return basePosition + controller.numCodePoints() - 1; } basePosition += controller.numCodePoints(); } return basePosition; }
TextRunComponent::TextRunComponent(const UChar *start, int length, const TextRun& parentTextRun, const Font &font, int o) : m_textRun(start, length, parentTextRun.allowTabs(), 0, 0 , parentTextRun.rtl() , parentTextRun.directionalOverride() , parentTextRun.applyRunRounding() , parentTextRun.applyWordRounding()) , m_offset(o) , m_spaces(0) { WidthIterator it(&font, m_textRun); it.advance(m_textRun.length(), 0); m_width = it.m_runWidthSoFar; }
float ShapeResult::fillGlyphBufferForTextEmphasis( Vector<RefPtr<ShapeResult>>& results, GlyphBuffer* glyphBuffer, const TextRun& textRun, const GlyphData* emphasisData, unsigned from, unsigned to) { float advance = 0; unsigned wordOffset = textRun.rtl() ? textRun.length() : 0; for (unsigned j = 0; j < results.size(); j++) { unsigned resolvedIndex = textRun.rtl() ? results.size() - 1 - j : j; RefPtr<ShapeResult>& wordResult = results[resolvedIndex]; for (unsigned i = 0; i < wordResult->m_runs.size(); i++) { unsigned resolvedOffset = wordOffset - (textRun.rtl() ? wordResult->numCharacters() : 0); advance += wordResult->fillGlyphBufferForTextEmphasisRun( glyphBuffer, wordResult->m_runs[i].get(), textRun, emphasisData, advance, from, to, resolvedOffset); } wordOffset += wordResult->numCharacters() * (textRun.rtl() ? -1 : 1); } return advance; }
// Return the rectangle for selecting the given range of code-points in the TextRun. FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int height, int from, int to) const { int fromX = -1, toX = -1; ComplexTextController controller(run, 0, 0, this); controller.setWordSpacingAdjustment(wordSpacing()); controller.setLetterSpacingAdjustment(letterSpacing()); controller.setPadding(run.expansion()); if (run.rtl()) { // See FIXME in drawComplexText. controller.reset(controller.widthOfFullRun()); controller.setPadding(run.expansion()); } // Iterate through the script runs in logical order, searching for the run covering the positions of interest. while (controller.nextScriptRun() && (fromX == -1 || toX == -1)) { if (fromX == -1 && from >= 0 && static_cast<unsigned>(from) < controller.numCodePoints()) { // |from| is within this script run. So we index the clusters log to // find which glyph this code-point contributed to and find its x // position. int glyph = controller.logClusters()[from]; fromX = controller.positions()[glyph].x(); if (controller.rtl()) fromX += truncateFixedPointToInteger(controller.advances()[glyph]); } else from -= controller.numCodePoints(); if (toX == -1 && to >= 0 && static_cast<unsigned>(to) < controller.numCodePoints()) { int glyph = controller.logClusters()[to]; toX = controller.positions()[glyph].x(); if (controller.rtl()) toX += truncateFixedPointToInteger(controller.advances()[glyph]); } else to -= controller.numCodePoints(); } // The position in question might be just after the text. if (fromX == -1) fromX = controller.offsetX(); if (toX == -1) toX = controller.offsetX(); ASSERT(fromX != -1 && toX != -1); if (fromX < toX) return FloatRect(point.x() + fromX, point.y(), toX - fromX, height); return FloatRect(point.x() + toX, point.y(), fromX - toX, height); }
void Font::drawComplexText(GraphicsContext* gc, const TextRun& run, const FloatPoint& point, int from, int to) const { if (!run.length()) return; SkCanvas* canvas = gc->platformContext()->canvas(); TextDrawingModeFlags textMode = gc->platformContext()->getTextDrawingMode(); bool fill = textMode & TextModeFill; bool stroke = (textMode & TextModeStroke) && gc->platformContext()->getStrokeStyle() != NoStroke && gc->platformContext()->getStrokeThickness() > 0; if (!fill && !stroke) return; SkPaint strokePaint, fillPaint; if (fill) { gc->platformContext()->setupPaintForFilling(&fillPaint); setupForTextPainting(&fillPaint, gc->fillColor().rgb()); } if (stroke) { gc->platformContext()->setupPaintForStroking(&strokePaint, 0, 0); setupForTextPainting(&strokePaint, gc->strokeColor().rgb()); } ComplexTextController controller(this, run, point.x(), point.y()); if (run.rtl()) controller.setupForRTL(); while (controller.nextScriptRun()) { // Check if there is any glyph found in the current script run. int fromGlyph, glyphLength; controller.glyphsForRange(from, to, fromGlyph, glyphLength); if (fromGlyph < 0 || glyphLength <= 0) continue; if (fill) { controller.fontPlatformDataForScriptRun()->setupPaint(&fillPaint); adjustTextRenderMode(&fillPaint, gc->platformContext()); canvas->drawPosText(controller.glyphs() + fromGlyph, glyphLength << 1, controller.positions() + fromGlyph, fillPaint); } if (stroke) { controller.fontPlatformDataForScriptRun()->setupPaint(&strokePaint); adjustTextRenderMode(&strokePaint, gc->platformContext()); canvas->drawPosText(controller.glyphs() + fromGlyph, glyphLength << 1, controller.positions() + fromGlyph, strokePaint); } } }
static QTextLine setupLayout(QTextLayout* layout, const TextRun& style) { int flags = style.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight; if (style.padding()) flags |= Qt::TextJustificationForced; layout->setFlags(flags); layout->beginLayout(); QTextLine line = layout->createLine(); line.setLineWidth(INT_MAX/256); if (style.padding()) line.setLineWidth(line.naturalTextWidth() + style.padding()); layout->endLayout(); return line; }
// Return the code point index for the given |x| offset into the text run. int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, bool includePartialGlyphs) const { // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem. int targetX = static_cast<int>(xFloat); // (Mac code ignores includePartialGlyphs, and they don't know what it's // supposed to do, so we just ignore it as well.) ComplexTextController controller(this, run, 0, 0); if (run.rtl()) controller.setupForRTL(); return controller.offsetForPosition(targetX); }
void FontCascade::adjustSelectionRectForComplexText(const TextRun& run, LayoutRect& selectionRect, int from, int to) const { UniscribeController it(this, run); it.advance(from); float beforeWidth = it.runWidthSoFar(); it.advance(to); float afterWidth = it.runWidthSoFar(); if (run.rtl()) { it.advance(run.length()); selectionRect.move(it.runWidthSoFar() - afterWidth, 0); } else selectionRect.move(beforeWidth, 0); selectionRect.setWidth(afterWidth - beforeWidth); }
FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const { TextRunComponents components; int w = generateComponents(&components, *this, run); if (!from && to == run.length()) return FloatRect(pt.x(), pt.y(), w, h); float x1 = cursorToX(this, components, w, run.rtl(), from); float x2 = cursorToX(this, components, w, run.rtl(), to); if (x2 < x1) std::swap(x1, x2); return FloatRect(pt.x() + x1, pt.y(), x2 - x1, h); }