void LayoutSVGInlineText::updateMetricsList(bool& lastCharacterWasWhiteSpace) { m_metrics.clear(); if (!textLength()) return; TextRun run = constructTextRun(*this, 0, textLength(), styleRef().direction()); BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); bool bidiOverride = isOverride(styleRef().unicodeBidi()); BidiStatus status(LTR, bidiOverride); if (run.is8Bit() || bidiOverride) { WTF::Unicode::CharDirection direction = WTF::Unicode::LeftToRight; // If BiDi override is in effect, use the specified direction. if (bidiOverride && !styleRef().isLeftToRightDirection()) direction = WTF::Unicode::RightToLeft; bidiRuns.addRun(new BidiCharacterRun(0, run.charactersLength(), status.context.get(), direction)); } else { status.last = status.lastStrong = WTF::Unicode::OtherNeutral; bidiResolver.setStatus(status); bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); const bool hardLineBreak = false; const bool reorderRuns = false; bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()), NoVisualOverride, hardLineBreak, reorderRuns); } for (const BidiCharacterRun* bidiRun = bidiRuns.firstRun(); bidiRun; bidiRun = bidiRun->next()) { TextRun subRun = constructTextRun(*this, bidiRun->start(), bidiRun->stop() - bidiRun->start(), bidiRun->direction()); addMetricsFromRun(subRun, lastCharacterWasWhiteSpace); } bidiResolver.runs().deleteRuns(); }
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { GraphicsContext* context = paintInfo.context; RenderStyle* style = renderer().style(isFirstLineStyle()); const Font& font = style->font(); FloatPoint boxOrigin = locationIncludingFlipping(); boxOrigin.moveBy(FloatPoint(paintOffset)); if (!isHorizontal()) boxOrigin.move(0, -virtualLogicalHeight()); FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), virtualLogicalHeight())); GraphicsContextStateSaver stateSaver(*context); if (!isHorizontal()) context->concatCTM(InlineTextBox::rotation(boxRect, InlineTextBox::Clockwise)); FloatPoint textOrigin(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent()); bool isPrinting = renderer().document().printing(); bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone; if (haveSelection) paintSelection(context, boxOrigin, style, font); else if (paintInfo.phase == PaintPhaseSelection) return; TextPainter::Style textStyle = TextPainter::textPaintingStyle(renderer(), style, paintInfo.forceBlackText(), isPrinting); if (haveSelection) textStyle = TextPainter::selectionPaintingStyle(renderer(), true, paintInfo.forceBlackText(), isPrinting, textStyle); TextRun textRun = constructTextRun(&renderer(), font, m_str, style, TextRun::AllowTrailingExpansion); TextPainter textPainter(context, font, textRun, textOrigin, boxRect, isHorizontal()); textPainter.paint(0, m_str.length(), m_str.length(), textStyle); paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style); }
FloatRect SVGInlineTextBox::selectionRectForTextFragment(const SVGTextFragment& fragment, int startPosition, int endPosition, RenderStyle* style) { ASSERT(startPosition < endPosition); ASSERT(style); FontCachePurgePreventer fontCachePurgePreventer; RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer()); ASSERT(textRenderer); float scalingFactor = textRenderer->scalingFactor(); ASSERT(scalingFactor); const Font& scaledFont = textRenderer->scaledFont(); const FontMetrics& scaledFontMetrics = scaledFont.fontMetrics(); FloatPoint textOrigin(fragment.x, fragment.y); if (scalingFactor != 1) textOrigin.scale(scalingFactor, scalingFactor); textOrigin.move(0, -scaledFontMetrics.floatAscent()); FloatRect selectionRect = scaledFont.selectionRectForText(constructTextRun(style, fragment), textOrigin, fragment.height * scalingFactor, startPosition, endPosition); if (scalingFactor == 1) return selectionRect; selectionRect.scale(1 / scalingFactor); return selectionRect; }
int SVGInlineTextBox::offsetForPositionInFragment( const SVGTextFragment& fragment, LayoutUnit position, bool includePartialGlyphs) const { LineLayoutSVGInlineText lineLayoutItem = LineLayoutSVGInlineText(this->getLineLayoutItem()); 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( clampTo<float>(fragmentTransform.xScale())); } return fragment.characterOffset - start() + lineLayoutItem.scaledFont().offsetForPosition( textRun, position * scalingFactor, includePartialGlyphs); }
FloatRect SVGInlineTextBox::selectionRectForTextFragment( const SVGTextFragment& fragment, int startPosition, int endPosition, const ComputedStyle& style) const { ASSERT(startPosition < endPosition); LineLayoutSVGInlineText lineLayoutItem = LineLayoutSVGInlineText(this->getLineLayoutItem()); float scalingFactor = lineLayoutItem.scalingFactor(); ASSERT(scalingFactor); const Font& scaledFont = lineLayoutItem.scaledFont(); const SimpleFontData* fontData = scaledFont.primaryFont(); DCHECK(fontData); if (!fontData) return FloatRect(); const FontMetrics& scaledFontMetrics = fontData->getFontMetrics(); FloatPoint textOrigin(fragment.x, fragment.y); if (scalingFactor != 1) textOrigin.scale(scalingFactor, scalingFactor); textOrigin.move(0, -scaledFontMetrics.floatAscent()); FloatRect selectionRect = scaledFont.selectionRectForText( constructTextRun(style, fragment), textOrigin, fragment.height * scalingFactor, startPosition, endPosition); if (scalingFactor == 1) return selectionRect; selectionRect.scale(1 / scalingFactor); return selectionRect; }
FloatRect SVGInlineTextBox::selectionRectForTextFragment(const SVGTextFragment& fragment, int startPosition, int endPosition, RenderStyle* style) const { ASSERT_WITH_SECURITY_IMPLICATION(startPosition < endPosition); ASSERT(style); float scalingFactor = renderer().scalingFactor(); ASSERT(scalingFactor); const FontCascade& scaledFont = renderer().scaledFont(); const FontMetrics& scaledFontMetrics = scaledFont.fontMetrics(); FloatPoint textOrigin(fragment.x, fragment.y); if (scalingFactor != 1) textOrigin.scale(scalingFactor, scalingFactor); textOrigin.move(0, -scaledFontMetrics.floatAscent()); LayoutRect selectionRect = LayoutRect(textOrigin, LayoutSize(0, fragment.height * scalingFactor)); TextRun run = constructTextRun(style, fragment); scaledFont.adjustSelectionRectForText(run, selectionRect, startPosition, endPosition); FloatRect snappedSelectionRect = snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()); if (scalingFactor == 1) return snappedSelectionRect; snappedSelectionRect.scale(1 / scalingFactor); return snappedSelectionRect; }
LayoutRect InlineTextBox::localSelectionRect(int startPos, int endPos) const { int sPos = std::max(startPos - m_start, 0); int ePos = std::min(endPos - m_start, (int)m_len); if (sPos > ePos) return LayoutRect(); FontCachePurgePreventer fontCachePurgePreventer; LayoutUnit selTop = root().selectionTop(); LayoutUnit selHeight = root().selectionHeight(); const ComputedStyle& styleToUse = lineLayoutItem().styleRef(isFirstLineStyle()); const Font& font = styleToUse.font(); StringBuilder charactersWithHyphen; bool respectHyphen = ePos == m_len && hasHyphen(); TextRun textRun = constructTextRun(styleToUse, font, respectHyphen ? &charactersWithHyphen : 0); LayoutPoint startingPoint = LayoutPoint(logicalLeft(), selTop); LayoutRect r; if (sPos || ePos != static_cast<int>(m_len)) { r = LayoutRect(enclosingIntRect(font.selectionRectForText(textRun, FloatPoint(startingPoint), selHeight, sPos, ePos))); } else { // Avoid computing the font width when the entire line box is selected as an optimization. // FIXME: the call to rawValue() below is temporary and should be removed once the transition // to LayoutUnit-based types is complete (crbug.com/321237) r = LayoutRect(enclosingIntRect(LayoutRect(startingPoint, LayoutSize(m_logicalWidth, selHeight)))); } LayoutUnit logicalWidth = r.width(); if (r.x() > logicalRight()) logicalWidth = 0; else if (r.maxX() > logicalRight()) logicalWidth = logicalRight() - r.x(); LayoutPoint topPoint; LayoutUnit width; LayoutUnit height; if (isHorizontal()) { topPoint = LayoutPoint(r.x(), selTop); width = logicalWidth; height = selHeight; if (hasWrappedSelectionNewline()) { if (!isLeftToRightDirection()) topPoint.setX(topPoint.x() - newlineSpaceWidth()); width += newlineSpaceWidth(); } } else { topPoint = LayoutPoint(selTop, r.x()); width = selHeight; height = logicalWidth; // TODO(wkorman): RTL text embedded in top-to-bottom text can create // bottom-to-top situations. Add tests and ensure we handle correctly. if (hasWrappedSelectionNewline()) height += newlineSpaceWidth(); } return LayoutRect(topPoint, LayoutSize(width, height)); }
FloatRect SVGInlineTextBox::selectionRectForTextFragment(const SVGTextFragment& fragment, int startPosition, int endPosition, RenderStyle* style) { ASSERT(startPosition < endPosition); const Font& font = style->font(); FloatPoint textOrigin(fragment.x, fragment.y - font.fontMetrics().ascent()); return font.selectionRectForText(constructTextRun(style, fragment), textOrigin, fragment.height, startPosition, endPosition); }
void FileUploadControlPainter::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (m_renderFileUploadControl.style()->visibility() != VISIBLE) return; // Push a clip. GraphicsContextStateSaver stateSaver(*paintInfo.context, false); if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) { IntRect clipRect = enclosingIntRect(LayoutRect(paintOffset.x() + m_renderFileUploadControl.borderLeft(), paintOffset.y() + m_renderFileUploadControl.borderTop(), m_renderFileUploadControl.width() - m_renderFileUploadControl.borderLeft() - m_renderFileUploadControl.borderRight(), m_renderFileUploadControl.height() - m_renderFileUploadControl.borderBottom() - m_renderFileUploadControl.borderTop() + buttonShadowHeight)); if (clipRect.isEmpty()) return; stateSaver.save(); paintInfo.context->clip(clipRect); } if (paintInfo.phase == PaintPhaseForeground) { const String& displayedFilename = m_renderFileUploadControl.fileTextValue(); const Font& font = m_renderFileUploadControl.style()->font(); TextRun textRun = constructTextRun(&m_renderFileUploadControl, font, displayedFilename, m_renderFileUploadControl.style(), TextRun::AllowTrailingExpansion, RespectDirection | RespectDirectionOverride); // Determine where the filename should be placed LayoutUnit contentLeft = paintOffset.x() + m_renderFileUploadControl.borderLeft() + m_renderFileUploadControl.paddingLeft(); Node* button = m_renderFileUploadControl.uploadButton(); if (!button) return; int buttonWidth = (button && button->renderBox()) ? button->renderBox()->pixelSnappedWidth() : 0; LayoutUnit buttonAndSpacingWidth = buttonWidth + RenderFileUploadControl::afterButtonSpacing; float textWidth = font.width(textRun); LayoutUnit textX; if (m_renderFileUploadControl.style()->isLeftToRightDirection()) textX = contentLeft + buttonAndSpacingWidth; else textX = contentLeft + m_renderFileUploadControl.contentWidth() - buttonAndSpacingWidth - textWidth; LayoutUnit textY = 0; // We want to match the button's baseline // FIXME: Make this work with transforms. if (RenderButton* buttonRenderer = toRenderButton(button->renderer())) textY = paintOffset.y() + m_renderFileUploadControl.borderTop() + m_renderFileUploadControl.paddingTop() + buttonRenderer->baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine); else textY = m_renderFileUploadControl.baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine); TextRunPaintInfo textRunPaintInfo(textRun); // FIXME: Shouldn't these offsets be rounded? crbug.com/350474 textRunPaintInfo.bounds = FloatRect(textX.toFloat(), textY.toFloat() - m_renderFileUploadControl.style()->fontMetrics().ascent(), textWidth, m_renderFileUploadControl.style()->fontMetrics().height()); paintInfo.context->setFillColor(m_renderFileUploadControl.resolveColor(CSSPropertyColor)); // Draw the filename paintInfo.context->drawBidiText(font, textRunPaintInfo, IntPoint(roundToInt(textX), roundToInt(textY))); } // Paint the children. m_renderFileUploadControl.RenderBlockFlow::paintObject(paintInfo, paintOffset); }
void FileUploadControlPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (m_layoutFileUploadControl.style()->visibility() != VISIBLE) return; // Push a clip. Optional<ClipRecorder> clipRecorder; if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseDescendantBlockBackgroundsOnly) { IntRect clipRect = enclosingIntRect(LayoutRect( LayoutPoint(paintOffset.x() + m_layoutFileUploadControl.borderLeft(), paintOffset.y() + m_layoutFileUploadControl.borderTop()), m_layoutFileUploadControl.size() + LayoutSize(0, -m_layoutFileUploadControl.borderWidth() + buttonShadowHeight))); if (clipRect.isEmpty()) return; clipRecorder.emplace(paintInfo.context, m_layoutFileUploadControl, DisplayItem::ClipFileUploadControlRect, clipRect); } if (paintInfo.phase == PaintPhaseForeground && !LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutFileUploadControl, paintInfo.phase)) { const String& displayedFilename = m_layoutFileUploadControl.fileTextValue(); const Font& font = m_layoutFileUploadControl.style()->font(); TextRun textRun = constructTextRun(font, displayedFilename, m_layoutFileUploadControl.styleRef(), RespectDirection | RespectDirectionOverride); textRun.setExpansionBehavior(TextRun::AllowTrailingExpansion); // Determine where the filename should be placed LayoutUnit contentLeft = paintOffset.x() + m_layoutFileUploadControl.borderLeft() + m_layoutFileUploadControl.paddingLeft(); Node* button = m_layoutFileUploadControl.uploadButton(); if (!button) return; int buttonWidth = (button && button->layoutBox()) ? button->layoutBox()->pixelSnappedWidth() : 0; LayoutUnit buttonAndSpacingWidth(buttonWidth + LayoutFileUploadControl::afterButtonSpacing); float textWidth = font.width(textRun); LayoutUnit textX; if (m_layoutFileUploadControl.style()->isLeftToRightDirection()) textX = contentLeft + buttonAndSpacingWidth; else textX = LayoutUnit(contentLeft + m_layoutFileUploadControl.contentWidth() - buttonAndSpacingWidth - textWidth); LayoutUnit textY; // We want to match the button's baseline // FIXME: Make this work with transforms. if (LayoutButton* buttonLayoutObject = toLayoutButton(button->layoutObject())) textY = paintOffset.y() + m_layoutFileUploadControl.borderTop() + m_layoutFileUploadControl.paddingTop() + buttonLayoutObject->baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine); else textY = LayoutUnit(m_layoutFileUploadControl.baselinePosition(AlphabeticBaseline, true, HorizontalLine, PositionOnContainingLine)); TextRunPaintInfo textRunPaintInfo(textRun); // FIXME: Shouldn't these offsets be rounded? crbug.com/350474 textRunPaintInfo.bounds = FloatRect(textX.toFloat(), textY.toFloat() - m_layoutFileUploadControl.style()->getFontMetrics().ascent(), textWidth, m_layoutFileUploadControl.style()->getFontMetrics().height()); // Draw the filename. LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutFileUploadControl, paintInfo.phase, textRunPaintInfo.bounds); paintInfo.context.setFillColor(m_layoutFileUploadControl.resolveColor(CSSPropertyColor)); paintInfo.context.drawBidiText(font, textRunPaintInfo, FloatPoint(roundToInt(textX), roundToInt(textY))); } // Paint the children. m_layoutFileUploadControl.LayoutBlockFlow::paintObject(paintInfo, paintOffset); }
TextRun constructTextRun(const Font& font, const String& string, const ComputedStyle& style, TextRunFlags flags) { return constructTextRun(font, string, style, string.isEmpty() || string.is8Bit() ? LTR : determineDirectionality(string), flags); }
float LayoutTextControl::getAvgCharWidth(AtomicString family) { if (hasValidAvgCharWidth(family)) return roundf(style()->font().primaryFont()->avgCharWidth()); const UChar ch = '0'; const String str = String(&ch, 1); const Font& font = style()->font(); TextRun textRun = constructTextRun(this, font, str, styleRef(), TextRun::AllowTrailingExpansion); return font.width(textRun); }
float RenderTextControl::getAverageCharWidth() { float width; if (style().fontCascade().fastAverageCharWidthIfAvailable(width)) return width; const UChar ch = '0'; const String str = String(&ch, 1); const FontCascade& font = style().fontCascade(); TextRun textRun = constructTextRun(this, font, str, style(), AllowTrailingExpansion); return font.width(textRun); }
TextRun InlineTextBox::constructTextRun( const ComputedStyle& style, StringBuilder* charactersWithHyphen) const { ASSERT(getLineLayoutItem().text()); String string = getLineLayoutItem().text(); unsigned startPos = start(); unsigned length = len(); return constructTextRun(style, StringView(string, startPos, length), getLineLayoutItem().textLength() - startPos, charactersWithHyphen); }
LayoutUnit LayoutListMarker::getWidthOfTextWithSuffix() const { if (m_text.isEmpty()) return 0; const Font& font = style()->font(); LayoutUnit itemWidth = font.width(m_text); // TODO(wkorman): Look into constructing a text run for both text and suffix // and painting them together. UChar suffix[2] = { ListMarkerText::suffix(style()->listStyleType(), m_listItem->value()), ' ' }; TextRun run = constructTextRun(font, suffix, 2, styleRef(), style()->direction()); LayoutUnit suffixSpaceWidth = font.width(run); return itemWidth + suffixSpaceWidth; }
TextRun InlineTextBox::constructTextRun(const ComputedStyle& style, const Font& font, StringBuilder* charactersWithHyphen) const { ASSERT(lineLayoutItem().text()); StringView string = lineLayoutItem().text().createView(); unsigned startPos = start(); unsigned length = len(); if (string.length() != length || startPos) string.narrow(startPos, length); return constructTextRun(style, font, string, lineLayoutItem().textLength() - startPos, charactersWithHyphen); }
float LayoutTextControl::getAvgCharWidth(const AtomicString& family) const { const Font& font = style()->font(); const SimpleFontData* primaryFont = font.primaryFont(); if (primaryFont && hasValidAvgCharWidth(primaryFont, family)) return roundf(primaryFont->avgCharWidth()); const UChar ch = '0'; const String str = String(&ch, 1); TextRun textRun = constructTextRun(font, str, styleRef(), TextRun::AllowTrailingExpansion); return font.width(textRun); }
void SVGInlineTextBox::paintText(GraphicsContext* context, RenderStyle* style, RenderStyle* selectionStyle, const SVGTextFragment& fragment, bool hasSelection, bool paintSelectedTextOnly) { ASSERT(style); ASSERT(selectionStyle); int startPosition = 0; int endPosition = 0; if (hasSelection) { selectionStartEnd(startPosition, endPosition); hasSelection = mapStartEndPositionsIntoFragmentCoordinates(fragment, startPosition, endPosition); } // Fast path if there is no selection, just draw the whole chunk part using the regular style TextRun textRun = constructTextRun(style, fragment); if (!hasSelection || startPosition >= endPosition) { paintTextWithShadows(context, style, textRun, fragment, 0, fragment.length); return; } // Eventually draw text using regular style until the start position of the selection if (startPosition > 0 && !paintSelectedTextOnly) paintTextWithShadows(context, style, textRun, fragment, 0, startPosition); // Draw text using selection style from the start to the end position of the selection if (style != selectionStyle) SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, selectionStyle); TextRun selectionTextRun = constructTextRun(selectionStyle, fragment); paintTextWithShadows(context, selectionStyle, textRun, fragment, startPosition, endPosition); if (style != selectionStyle) SVGResourcesCache::clientStyleChanged(parent()->renderer(), StyleDifferenceRepaint, style); // Eventually draw text using regular style from the end position of the selection to the end of the current chunk part if (endPosition < static_cast<int>(fragment.length) && !paintSelectedTextOnly) paintTextWithShadows(context, style, textRun, fragment, endPosition, fragment.length); }
void LayoutMenuList::updateOptionsWidth() const { float maxOptionWidth = 0; for (const auto& option : selectElement()->optionList()) { String text = option->textIndentedToRespectGroupLabel(); const ComputedStyle* itemStyle = option->computedStyle() ? option->computedStyle() : style(); applyTextTransform(itemStyle, text, ' '); // We apply SELECT's style, not OPTION's style because m_optionsWidth is // used to determine intrinsic width of the menulist box. TextRun textRun = constructTextRun(style()->font(), text, *style()); maxOptionWidth = std::max(maxOptionWidth, style()->font().width(textRun)); } m_optionsWidth = static_cast<int>(ceilf(maxOptionWidth)); }
int InlineTextBox::offsetForPosition(LayoutUnit lineOffset, bool includePartialGlyphs) const { if (isLineBreak()) return 0; if (lineOffset - logicalLeft() > logicalWidth()) return isLeftToRightDirection() ? len() : 0; if (lineOffset - logicalLeft() < 0) return isLeftToRightDirection() ? 0 : len(); LineLayoutText text = lineLayoutItem(); const ComputedStyle& style = text.styleRef(isFirstLineStyle()); const Font& font = style.font(); return font.offsetForPosition(constructTextRun(style, font), (lineOffset - logicalLeft()).toFloat(), includePartialGlyphs); }
int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, float position, bool includePartialGlyphs) const { float scalingFactor = renderer().scalingFactor(); ASSERT(scalingFactor); TextRun textRun = constructTextRun(&renderer().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); }
LayoutUnit InlineTextBox::positionForOffset(int offset) const { ASSERT(offset >= m_start); ASSERT(offset <= m_start + m_len); if (isLineBreak()) return logicalLeft(); LineLayoutText text = lineLayoutItem(); const ComputedStyle& styleToUse = text.styleRef(isFirstLineStyle()); const Font& font = styleToUse.font(); int from = !isLeftToRightDirection() ? offset - m_start : 0; int to = !isLeftToRightDirection() ? m_len : offset - m_start; // FIXME: Do we need to add rightBearing here? return font.selectionRectForText(constructTextRun(styleToUse, font), IntPoint(logicalLeft(), 0), 0, from, to).maxX(); }
int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, float position, bool includePartialGlyphs) const { RenderText* textRenderer = this->textRenderer(); ASSERT(textRenderer); RenderStyle* style = textRenderer->style(); ASSERT(style); TextRun textRun(constructTextRun(style, fragment)); // Eventually handle lengthAdjust="spacingAndGlyphs". // FIXME: Handle vertical text. if (!fragment.transform.isIdentity()) textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragment.transform.xScale())); return fragment.positionListOffset - start() + style->font().offsetForPosition(textRun, position, includePartialGlyphs); }
void InlineTextBox::characterWidths(Vector<float>& widths) const { FontCachePurgePreventer fontCachePurgePreventer; const ComputedStyle& styleToUse = lineLayoutItem().styleRef(isFirstLineStyle()); const Font& font = styleToUse.font(); TextRun textRun = constructTextRun(styleToUse, font); SimpleShaper shaper(&font, textRun); float lastWidth = 0; widths.resize(m_len); for (unsigned i = 0; i < m_len; i++) { shaper.advance(i + 1); widths[i] = shaper.runWidthSoFar() - lastWidth; lastWidth = shaper.runWidthSoFar(); } }
void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) { GraphicsContext* context = paintInfo.context; RenderStyle* style = renderer().style(isFirstLineStyle()); const Font& font = style->font(); FloatPoint boxOrigin = locationIncludingFlipping(); boxOrigin.moveBy(FloatPoint(paintOffset)); FloatRect boxRect(boxOrigin, LayoutSize(logicalWidth(), virtualLogicalHeight())); GraphicsContextStateSaver stateSaver(*context); FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent()); Color styleTextColor = renderer().resolveColor(style, CSSPropertyWebkitTextFillColor); if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor); if (selectionState() != RenderObject::SelectionNone) { paintSelection(context, boxOrigin, style, font); // Select the correct color for painting the text. Color foreground = paintInfo.forceBlackText() ? Color::black : renderer().selectionForegroundColor(); if (foreground != styleTextColor) context->setFillColor(foreground); } const ShadowList* shadowList = style->textShadow(); bool hasShadow = shadowList; if (hasShadow) context->setDrawLooper(shadowList->createDrawLooper(DrawLooperBuilder::ShadowIgnoresAlpha)); TextRun textRun = constructTextRun(&renderer(), font, m_str, style, TextRun::AllowTrailingExpansion); TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.bounds = boxRect; context->drawText(font, textRunPaintInfo, textOrigin); // Restore the regular fill color. if (styleTextColor != context->fillColor()) context->setFillColor(styleTextColor); if (hasShadow) context->clearDrawLooper(); paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, style); }
void InlineTextBox::characterWidths(Vector<float>& widths) const { if (!m_len) return; FontCachePurgePreventer fontCachePurgePreventer; ASSERT(getLineLayoutItem().text()); const ComputedStyle& styleToUse = getLineLayoutItem().styleRef(isFirstLineStyle()); const Font& font = styleToUse.font(); TextRun textRun = constructTextRun(styleToUse); Vector<CharacterRange> ranges = font.individualCharacterRanges(textRun); DCHECK_EQ(ranges.size(), m_len); widths.resize(ranges.size()); for (unsigned i = 0; i < ranges.size(); i++) widths[i] = ranges[i].width(); }
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); }
void EllipsisBoxPainter::paintEllipsis(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom, const ComputedStyle& style) { bool haveSelection = !paintInfo.isPrinting() && paintInfo.phase != PaintPhaseTextClip && m_ellipsisBox.getSelectionState() != SelectionNone; LayoutRect paintRect(m_ellipsisBox.logicalFrameRect()); if (haveSelection) paintRect.unite(LayoutRect(m_ellipsisBox.selectionRect())); m_ellipsisBox.logicalRectToPhysicalRect(paintRect); paintRect.moveBy(paintOffset); GraphicsContext& context = paintInfo.context; DisplayItem::Type displayItemType = DisplayItem::paintPhaseToDrawingType(paintInfo.phase); if (DrawingRecorder::useCachedDrawingIfPossible(context, m_ellipsisBox, displayItemType)) return; DrawingRecorder recorder(context, m_ellipsisBox, displayItemType, FloatRect(paintRect)); LayoutPoint boxOrigin = m_ellipsisBox.locationIncludingFlipping(); boxOrigin.moveBy(paintOffset); LayoutRect boxRect(boxOrigin, LayoutSize(m_ellipsisBox.logicalWidth(), m_ellipsisBox.virtualLogicalHeight())); GraphicsContextStateSaver stateSaver(context); if (!m_ellipsisBox.isHorizontal()) context.concatCTM(TextPainter::rotation(boxRect, TextPainter::Clockwise)); const Font& font = style.font(); if (haveSelection) paintSelection(context, boxOrigin, style, font); else if (paintInfo.phase == PaintPhaseSelection) return; TextPainter::Style textStyle = TextPainter::textPaintingStyle(m_ellipsisBox.getLineLayoutItem(), style, paintInfo); if (haveSelection) textStyle = TextPainter::selectionPaintingStyle(m_ellipsisBox.getLineLayoutItem(), true, paintInfo, textStyle); TextRun textRun = constructTextRun(font, m_ellipsisBox.ellipsisStr(), style, TextRun::AllowTrailingExpansion); LayoutPoint textOrigin(boxOrigin.x(), boxOrigin.y() + font.getFontMetrics().ascent()); TextPainter textPainter(context, font, textRun, textOrigin, boxRect, m_ellipsisBox.isHorizontal()); textPainter.paint(0, m_ellipsisBox.ellipsisStr().length(), m_ellipsisBox.ellipsisStr().length(), textStyle); }
void InlineTextBox::characterWidths(Vector<float>& widths) const { if (!m_len) return; FontCachePurgePreventer fontCachePurgePreventer; ASSERT(getLineLayoutItem().text()); const ComputedStyle& styleToUse = getLineLayoutItem().styleRef(isFirstLineStyle()); const Font& font = styleToUse.font(); float lastWidth = 0; widths.resize(m_len); for (unsigned i = 0; i < m_len; i++) { StringView substringView = getLineLayoutItem().text().createView(); substringView.narrow(start(), 1 + i); TextRun textRun = constructTextRun(styleToUse, font, substringView, m_len); widths[i] = font.width(textRun, nullptr, nullptr) - lastWidth; lastWidth = font.width(textRun, nullptr, nullptr); } }
void EllipsisBox::paintSelection(GraphicsContext* context, const FloatPoint& boxOrigin, RenderStyle* style, const Font& font) { Color textColor = renderer().resolveColor(style, CSSPropertyColor); Color c = renderer().selectionBackgroundColor(); if (!c.alpha()) return; // If the text color ends up being the same as the selection background, invert the selection // background. if (textColor == c) c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); GraphicsContextStateSaver stateSaver(*context); LayoutUnit top = root().selectionTop(); LayoutUnit h = root().selectionHeight(); const int deltaY = roundToInt(logicalTop() - top); const FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY); FloatRect clipRect(localOrigin, FloatSize(m_logicalWidth, h.toFloat())); context->clip(clipRect); context->drawHighlightForText(font, constructTextRun(&renderer(), font, m_str, style, TextRun::AllowTrailingExpansion), localOrigin, h, c); }