void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction) { if (paintingDisabled()) return; BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); // FIXME: This ownership should be reversed. We should pass BidiRunList // to BidiResolver in createBidiRunsForLine. BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); if (!bidiRuns.runCount()) return; FloatPoint currPoint = point; BidiCharacterRun* bidiRun = bidiRuns.firstRun(); while (bidiRun) { TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start()); bool isRTL = bidiRun->level() % 2; subrun.setDirection(isRTL ? RTL : LTR); subrun.setDirectionalOverride(bidiRun->dirOverride(false)); font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction); bidiRun = bidiRun->next(); // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here. if (bidiRun) currPoint.move(font.width(subrun), 0); } bidiRuns.deleteRuns(); }
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(); int textMode = gc->platformContext()->getTextDrawingMode(); bool fill = textMode & cTextFill; bool stroke = (textMode & cTextStroke) && 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()); } TextRunWalker walker(run, point.x(), this); walker.setWordSpacingAdjustment(wordSpacing()); walker.setLetterSpacingAdjustment(letterSpacing()); walker.setPadding(run.padding()); while (walker.nextScriptRun()) { if (fill) { walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint); adjustTextRenderMode(&fillPaint, gc->platformContext()); canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), fillPaint); } if (stroke) { walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint); adjustTextRenderMode(&strokePaint, gc->platformContext()); canvas->drawPosTextH(walker.glyphs(), walker.length() << 1, walker.xPositions(), point.y(), strokePaint); } } }
FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const { UniscribeController 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()) { it.advance(run.length()); float totalWidth = it.runWidthSoFar(); return FloatRect(point.x() + floorf(totalWidth - afterWidth), point.y(), roundf(totalWidth - beforeWidth) - floorf(totalWidth - afterWidth), h); } return FloatRect(point.x() + floorf(beforeWidth), point.y(), roundf(afterWidth) - floorf(beforeWidth), h); }
static bool isOneLeftToRightRun(const TextRun& run) { for (int i = 0; i < run.length(); i++) { WTF::Unicode::Direction direction = WTF::Unicode::direction(run[i]); if (direction == WTF::Unicode::RightToLeft || direction > WTF::Unicode::OtherNeutral) return false; } return true; }
// 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); }
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. if (run.rtl()) { it.advance(run.length(), &glyphBuffer); float totalWidth = it.m_runWidthSoFar; 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); }
void Font::drawComplexText(GraphicsContext* gc, TextRun const& run, FloatPoint const& point, int, int) const { SkCanvas* canvas = gc->platformContext()->mCanvas; SkPaint paint; if (!setupForText(&paint, gc, primaryFont())) { return; } // go to chars, instead of glyphs, which was set by setupForText() paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); canvas->drawText(run.characters(), run.length() << 1, SkFloatToScalar(point.x()), SkFloatToScalar(point.y()), paint); }
FloatRect Font::selectionRectForText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const { to = (to == -1 ? run.length() : to); if (codePath(run) != Complex) return selectionRectForSimpleText(run, point, h, from, to); return selectionRectForComplexText(run, point, h, from, to); }
void Font::drawEmphasisMarks(GraphicsContext* context, const TextRun& run, const AtomicString& mark, const FloatPoint& point, int from, int to) const { if (loadingCustomFonts()) return; if (to < 0) to = run.length(); CodePath codePathToUse = codePath(run); // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 if (codePathToUse != Complex && typesettingFeatures() && (from || to != run.length())) codePathToUse = Complex; if (codePathToUse != Complex) drawEmphasisMarksForSimpleText(context, run, mark, point, from, to); else drawEmphasisMarksForComplexText(context, run, mark, point, from, to); }
CharacterRange CachingWordShaper::getCharacterRange(const Font* font, const TextRun& run, unsigned from, unsigned to) { ShapeResultBuffer buffer; float totalWidth = shapeResultsForRun(m_shapeCache, font, run, nullptr, &buffer); return buffer.getCharacterRange(run.direction(), totalWidth, from, to); }
void Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { // Don't draw anything while we are using custom fonts that are in the process of loading. if (loadingCustomFonts()) return; to = (to == -1 ? run.length() : to); CodePath codePathToUse = codePath(run); // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 if (codePathToUse != Complex && typesettingFeatures() && (from || to != run.length())) codePathToUse = Complex; if (codePathToUse != Complex) return drawSimpleText(context, run, point, from, to); return drawComplexText(context, run, point, from, to); }
void copyTextRunTo(const TextRun& run, UChar *word) { int word_size = run.length() - run.from(), j = 0; for (int i = run.from(); i < run.length(); i++) { if (Font::treatAsSpace(run[i])) { word[j] = ' '; j++; } else { word[j] = run[i]; j++; } } word[j]='\0'; }
static bool isOneLeftToRightRun(const TextRun& run) { for (int i = 0; i < run.length(); i++) { UCharDirection direction = u_charDirection(run[i]); if (direction == U_RIGHT_TO_LEFT || direction > U_OTHER_NEUTRAL) return false; } return true; }
TextRun constructTextRun(const Font& font, const LineLayoutText text, unsigned offset, unsigned length, const ComputedStyle& style) { ASSERT(offset + length <= text.textLength()); if (text.hasEmptyText()) return constructTextRunInternal(font, static_cast<const LChar*>(nullptr), 0, style, LTR); if (text.is8Bit()) return constructTextRunInternal(font, text.characters8() + offset, length, style, LTR); TextRun run = constructTextRunInternal(font, text.characters16() + offset, length, style, LTR); run.setDirection(directionForRun(run)); return run; }
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); }
int Font::offsetForPositionForComplexText(const TextRun& run, int x, bool includePartialGlyphs) const { PangoLayout* layout = getDefaultPangoLayout(run); setPangoAttributes(this, run, layout); gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); int index, trailing; pango_layout_xy_to_index(layout, x * PANGO_SCALE, 1, &index, &trailing); glong offset = g_utf8_pointer_to_offset(utf8, utf8 + index); if (includePartialGlyphs) offset += trailing; g_free(utf8); g_object_unref(layout); return offset; }
float Font::floatWidthForComplexText(const TextRun& run) const { if (run.length() == 0) return 0.0f; PangoLayout* layout = getDefaultPangoLayout(run); setPangoAttributes(this, run, layout); gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); int width; pango_layout_get_pixel_size(layout, &width, 0); g_free(utf8); g_object_unref(layout); return width; }
float Font::floatWidthForComplexText(const TextRun& run, const TextStyle& style) const { if (run.length() == 0) return 0.0f; PangoLayout* layout = getDefaultPangoLayout(run); setPangoAttributes(this, run, layout, style.rtl()); gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); g_free(utf8); int layoutWidth; pango_layout_get_size(layout, &layoutWidth, 0); float width = (float)layoutWidth / (double)PANGO_SCALE; g_object_unref(layout); return width; }
FloatRect CachingWordShaper::selectionRect(const Font* font, const TextRun& run, const FloatPoint& point, int height, unsigned from, unsigned to) { Vector<RefPtr<ShapeResult>> results; float totalWidth = shapeResultsForRun(m_shapeCache, font, run, nullptr, &results); return ShapeResult::selectionRect(results, run.direction(), totalWidth, point, height, from, to); }
void SVGInlineTextBox::restoreGraphicsContextAfterTextPainting(GraphicsContext*& context, TextRun& textRun) { releasePaintingResource(context, /* path */0); #if ENABLE(SVG_FONTS) TextRun::RenderingContext* renderingContext = textRun.renderingContext(); if (renderingContext) static_cast<SVGTextRunRenderingContext*>(renderingContext)->setActivePaintingResource(0); #endif }
float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* glyphOverflow) const { if (!primaryFont()->platformData().size()) return 0; if (!run.length()) return 0; String sanitized = Font::normalizeSpaces(run.characters(), run.length()); QString string = fromRawDataWithoutRef(sanitized); int w = QFontMetrics(font()).width(string, -1, Qt::TextBypassShaping); // WebKit expects us to ignore word spacing on the first character (as opposed to what Qt does) if (treatAsSpace(run[0])) w -= m_wordSpacing; return w + run.expansion(); }
int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, float position, bool includePartialGlyphs) const { float scalingFactor = renderer().scalingFactor(); ASSERT(scalingFactor); RenderStyle* style = renderer().style(); ASSERT(style); TextRun textRun = constructTextRun(style, fragment); // Eventually handle lengthAdjust="spacingAndGlyphs". // FIXME: Handle vertical text. AffineTransform fragmentTransform; fragment.buildFragmentTransform(fragmentTransform); if (!fragmentTransform.isIdentity()) textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragmentTransform.xScale())); return fragment.characterOffset - start() + renderer().scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs); }
FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const { #if USE(FREETYPE) if (!primaryFont()->platformData().m_pattern) return selectionRectForSimpleText(run, point, h, from, to); #endif PangoLayout* layout = getDefaultPangoLayout(run); setPangoAttributes(this, run, layout); gchar* utf8 = convertUniCharToUTF8(run.characters(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); char* start = g_utf8_offset_to_pointer(utf8, from); char* end = g_utf8_offset_to_pointer(start, to - from); if (run.ltr()) { from = start - utf8; to = end - utf8; } else { from = end - utf8; to = start - utf8; } PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0); int xPos; xPos = 0; if (from < layoutLine->length) pango_layout_line_index_to_x(layoutLine, from, FALSE, &xPos); float beforeWidth = PANGO_PIXELS_FLOOR(xPos); xPos = 0; if (run.ltr() || to < layoutLine->length) pango_layout_line_index_to_x(layoutLine, to, FALSE, &xPos); float afterWidth = PANGO_PIXELS(xPos); g_free(utf8); g_object_unref(layout); return FloatRect(point.x() + beforeWidth, point.y(), afterWidth - beforeWidth, h); }
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 position = static_cast<int>(xFloat); TextRunComponents components; int w = generateComponents(&components, *this, run); if (position >= w) return run.length(); int offset = 0; if (run.rtl()) { for (size_t i = 0; i < components.size(); ++i) { const TextRunComponent& comp = components.at(i); int xe = w - comp.m_offset; int xs = xe - comp.m_width; if (position >= xs) return offset + (comp.isSpace() ? static_cast<int>((position - xe) * comp.m_spaces / std::max(1.f, comp.m_width) + 0.5) : offsetForPositionForSimpleText(comp.m_textRun, position - xs, includePartialGlyphs)); offset += comp.textLength(); } } else { for (size_t i = 0; i < components.size(); ++i) { const TextRunComponent& comp = components.at(i); int xs = comp.m_offset; int xe = xs + comp.m_width; if (position <= xe) { if (position - xs >= xe) return offset + comp.textLength(); return offset + (comp.isSpace() ? static_cast<int>((position - xs) * comp.m_spaces / std::max(1.f, comp.m_width) + 0.5) : offsetForPositionForSimpleText(comp.m_textRun, position - xs, includePartialGlyphs)); } offset += comp.textLength(); } } return run.length(); }
void Font::drawComplexText(GraphicsContext* gc, TextRun const& run, FloatPoint const& point, int, int) const { if (!run.length()) return; int mode = gc->textDrawingMode(); bool fill = mode & TextModeFill; bool stroke = mode & TextModeStroke; if (!fill && !stroke) return; SkPaint fillPaint, strokePaint; if (fill) setupFill(&fillPaint, gc, primaryFont()); if (stroke) setupStroke(&strokePaint, gc, primaryFont()); SkCanvas* canvas = gc->platformContext()->recordingCanvas(); bool haveMultipleLayers = isCanvasMultiLayered(canvas); TextRunWalker walker(run, point.x(), point.y(), this); walker.setWordAndLetterSpacing(wordSpacing(), letterSpacing()); walker.setPadding(run.expansion()); while (walker.nextScriptRun()) { if (fill) { walker.fontPlatformDataForScriptRun()->setupPaint(&fillPaint); adjustTextRenderMode(&fillPaint, haveMultipleLayers); canvas->drawPosText(walker.glyphs(), walker.length() << 1, walker.positions(), fillPaint); } if (stroke) { walker.fontPlatformDataForScriptRun()->setupPaint(&strokePaint); adjustTextRenderMode(&strokePaint, haveMultipleLayers); canvas->drawPosText(walker.glyphs(), walker.length() << 1, walker.positions(), strokePaint); } } gc->platformContext()->endRecording(); }
SVGTextMetrics::SVGTextMetrics(RenderSVGInlineText* textRenderer, const TextRun& run) { ASSERT(textRenderer); float scalingFactor = textRenderer->scalingFactor(); ASSERT(scalingFactor); const Font& scaledFont = textRenderer->scaledFont(); int length = 0; // Calculate width/height using the scaled font, divide this result by the scalingFactor afterwards. m_width = scaledFont.width(run, length, m_glyph.name) / scalingFactor; m_height = scaledFont.fontMetrics().floatHeight() / scalingFactor; m_glyph.unicodeString = run.is8Bit() ? String(run.characters8(), length) : String(run.characters16(), length); m_glyph.isValid = true; ASSERT(length >= 0); m_length = static_cast<unsigned>(length); }
float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts) const { #if OS(WINDOWS) UniscribeController controller(this, run, fallbackFonts); controller.advance(run.length()); return controller.runWidthSoFar(); #else notImplemented(); return 0; #endif }
void Font::drawText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to, CustomFontNotReadyAction customFontNotReadyAction) const { // Don't draw anything while we are using custom fonts that are in the process of loading, // except if the 'force' argument is set to true (in which case it will use a fallback // font). if (loadingCustomFonts() && customFontNotReadyAction == DoNotPaintIfFontNotReady) return; to = (to == -1 ? run.length() : to); CodePath codePathToUse = codePath(run); // FIXME: Use the fast code path once it handles partial runs with kerning and ligatures. See http://webkit.org/b/100050 if (codePathToUse != Complex && typesettingFeatures() && (from || to != run.length())) codePathToUse = Complex; if (codePathToUse != Complex) return drawSimpleText(context, run, point, from, to); return drawComplexText(context, run, point, from, to); }
int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, FloatWillBeLayoutUnit position, bool includePartialGlyphs) const { LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText(this->layoutObject()); float scalingFactor = textLayoutObject.scalingFactor(); ASSERT(scalingFactor); const ComputedStyle& style = textLayoutObject.styleRef(); TextRun textRun = constructTextRun(style, fragment); // Eventually handle lengthAdjust="spacingAndGlyphs". // FIXME: Handle vertical text. AffineTransform fragmentTransform; fragment.buildFragmentTransform(fragmentTransform); if (!fragmentTransform.isIdentity()) textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragmentTransform.xScale())); return fragment.characterOffset - start() + textLayoutObject.scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs); }
int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, LayoutUnit position, bool includePartialGlyphs) const { LineLayoutSVGInlineText lineLayoutItem = LineLayoutSVGInlineText(this->lineLayoutItem()); float scalingFactor = lineLayoutItem.scalingFactor(); ASSERT(scalingFactor); const ComputedStyle& style = lineLayoutItem.styleRef(); TextRun textRun = constructTextRun(style, fragment); // Eventually handle lengthAdjust="spacingAndGlyphs". // FIXME: Handle vertical text. if (fragment.isTransformed()) { AffineTransform fragmentTransform = fragment.buildFragmentTransform(); textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragmentTransform.xScale())); } return fragment.characterOffset - start() + lineLayoutItem.scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs); }