summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/InlineTextBox.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/InlineTextBox.cpp')
-rw-r--r--Source/WebCore/rendering/InlineTextBox.cpp1125
1 files changed, 293 insertions, 832 deletions
diff --git a/Source/WebCore/rendering/InlineTextBox.cpp b/Source/WebCore/rendering/InlineTextBox.cpp
index 7b8efc2af..f6f248af3 100644
--- a/Source/WebCore/rendering/InlineTextBox.cpp
+++ b/Source/WebCore/rendering/InlineTextBox.cpp
@@ -1,7 +1,7 @@
/*
* (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 2000 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2004-2014 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -23,18 +23,17 @@
#include "config.h"
#include "InlineTextBox.h"
-#include "Chrome.h"
-#include "ChromeClient.h"
+#include "BreakLines.h"
#include "DashArray.h"
#include "Document.h"
#include "DocumentMarkerController.h"
#include "Editor.h"
#include "EllipsisBox.h"
-#include "FontCache.h"
#include "Frame.h"
#include "GraphicsContext.h"
#include "HitTestResult.h"
#include "ImageBuffer.h"
+#include "InlineTextBoxStyle.h"
#include "Page.h"
#include "PaintInfo.h"
#include "RenderedDocumentMarker.h"
@@ -45,12 +44,11 @@
#include "RenderRubyText.h"
#include "RenderTheme.h"
#include "RenderView.h"
-#include "Settings.h"
-#include "SVGTextRunRenderingContext.h"
#include "Text.h"
+#include "TextDecorationPainter.h"
#include "TextPaintStyle.h"
#include "TextPainter.h"
-#include "break_lines.h"
+#include <stdio.h>
#include <wtf/text/CString.h>
namespace WebCore {
@@ -66,71 +64,6 @@ COMPILE_ASSERT(sizeof(InlineTextBox) == sizeof(SameSizeAsInlineTextBox), InlineT
typedef WTF::HashMap<const InlineTextBox*, LayoutRect> InlineTextBoxOverflowMap;
static InlineTextBoxOverflowMap* gTextBoxesWithOverflow;
-#if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
-static bool compareTuples(std::pair<float, float> l, std::pair<float, float> r)
-{
- return l.first < r.first;
-}
-
-static DashArray translateIntersectionPointsToSkipInkBoundaries(const DashArray& intersections, float dilationAmount, float totalWidth)
-{
- ASSERT(!(intersections.size() % 2));
-
- // Step 1: Make pairs so we can sort based on range starting-point. We dilate the ranges in this step as well.
- Vector<std::pair<float, float>> tuples;
- for (auto i = intersections.begin(); i != intersections.end(); i++, i++)
- tuples.append(std::make_pair(*i - dilationAmount, *(i + 1) + dilationAmount));
- std::sort(tuples.begin(), tuples.end(), &compareTuples);
-
- // Step 2: Deal with intersecting ranges.
- Vector<std::pair<float, float>> intermediateTuples;
- if (tuples.size() >= 2) {
- intermediateTuples.append(*tuples.begin());
- for (auto i = tuples.begin() + 1; i != tuples.end(); i++) {
- float& firstEnd = intermediateTuples.last().second;
- float secondStart = i->first;
- float secondEnd = i->second;
- if (secondStart <= firstEnd && secondEnd <= firstEnd) {
- // Ignore this range completely
- } else if (secondStart <= firstEnd)
- firstEnd = secondEnd;
- else
- intermediateTuples.append(*i);
- }
- } else
- intermediateTuples = tuples;
-
- // Step 3: Output the space between the ranges, but only if the space warrants an underline.
- float previous = 0;
- DashArray result;
- for (auto i = intermediateTuples.begin(); i != intermediateTuples.end(); i++) {
- if (i->first - previous > dilationAmount) {
- result.append(previous);
- result.append(i->first);
- }
- previous = i->second;
- }
- if (totalWidth - previous > dilationAmount) {
- result.append(previous);
- result.append(totalWidth);
- }
-
- return result;
-}
-
-static void drawSkipInkUnderline(TextPainter& textPainter, GraphicsContext& context, FloatPoint localOrigin, float underlineOffset, float width, bool isPrinting)
-{
- FloatPoint adjustedLocalOrigin = localOrigin;
- adjustedLocalOrigin.move(0, underlineOffset);
- FloatRect underlineBoundingBox = context.computeLineBoundsForText(adjustedLocalOrigin, width, isPrinting);
- DashArray intersections = textPainter.dashesForIntersectionsWithRect(underlineBoundingBox);
- DashArray a = translateIntersectionPointsToSkipInkBoundaries(intersections, underlineBoundingBox.height(), width);
-
- ASSERT(!(a.size() % 2));
- context.drawLinesForText(adjustedLocalOrigin, a, isPrinting);
-}
-#endif
-
InlineTextBox::~InlineTextBox()
{
if (!knownToHaveNoOverflow() && gTextBoxesWithOverflow)
@@ -167,7 +100,7 @@ int InlineTextBox::baselinePosition(FontBaseline baselineType) const
return 0;
if (&parent()->renderer() == renderer().parent())
return parent()->baselinePosition(baselineType);
- return toRenderBoxModelObject(renderer().parent())->baselinePosition(baselineType, isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
+ return downcast<RenderBoxModelObject>(*renderer().parent()).baselinePosition(baselineType, isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
}
LayoutUnit InlineTextBox::lineHeight() const
@@ -176,7 +109,7 @@ LayoutUnit InlineTextBox::lineHeight() const
return 0;
if (&parent()->renderer() == renderer().parent())
return parent()->lineHeight();
- return toRenderBoxModelObject(renderer().parent())->lineHeight(isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
+ return downcast<RenderBoxModelObject>(*renderer().parent()).lineHeight(isFirstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
}
LayoutUnit InlineTextBox::selectionTop() const
@@ -194,21 +127,26 @@ LayoutUnit InlineTextBox::selectionHeight() const
return root().selectionHeight();
}
-bool InlineTextBox::isSelected(int startPos, int endPos) const
+bool InlineTextBox::isSelected(unsigned startPos, unsigned endPos) const
{
- LayoutUnit sPos = std::max<LayoutUnit>(startPos - m_start, 0);
- LayoutUnit ePos = std::min<LayoutUnit>(endPos - m_start, m_len);
- return (sPos < ePos);
+ int sPos = clampedOffset(startPos);
+ int ePos = clampedOffset(endPos);
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=160786
+ // We should only be checking if sPos >= ePos here, because those are the
+ // indices used to actually generate the selection rect. Allowing us past this guard
+ // on any other condition creates zero-width selection rects.
+ return sPos < ePos || (startPos == endPos && startPos >= start() && startPos <= (start() + len()));
}
RenderObject::SelectionState InlineTextBox::selectionState()
{
RenderObject::SelectionState state = renderer().selectionState();
if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) {
- int startPos, endPos;
+ unsigned startPos, endPos;
renderer().selectionStartEnd(startPos, endPos);
// The position after a hard line break is considered to be past its end.
- int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0);
+ ASSERT(start() + len() >= (isLineBreak() ? 1 : 0));
+ unsigned lastSelectable = start() + len() - (isLineBreak() ? 1 : 0);
bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable);
@@ -229,13 +167,14 @@ RenderObject::SelectionState InlineTextBox::selectionState()
if (m_truncation != cNoTruncation && root().ellipsisBox()) {
EllipsisBox* ellipsis = root().ellipsisBox();
if (state != RenderObject::SelectionNone) {
- int start, end;
- selectionStartEnd(start, end);
+ unsigned selectionStart;
+ unsigned selectionEnd;
+ std::tie(selectionStart, selectionEnd) = selectionStartEnd();
// The ellipsis should be considered to be selected if the end of
// the selection is past the beginning of the truncation and the
// beginning of the selection is before or at the beginning of the
// truncation.
- ellipsis->setSelectionState(end >= m_truncation && start <= m_truncation ?
+ ellipsis->setSelectionState(selectionEnd >= m_truncation && selectionStart <= m_truncation ?
RenderObject::SelectionInside : RenderObject::SelectionNone);
} else
ellipsis->setSelectionState(RenderObject::SelectionNone);
@@ -244,63 +183,53 @@ RenderObject::SelectionState InlineTextBox::selectionState()
return state;
}
-static void adjustCharactersAndLengthForHyphen(BufferForAppendingHyphen& charactersWithHyphen, const RenderStyle& style, String& string, int& length)
+static const FontCascade& fontToUse(const RenderStyle& style, const RenderText& renderer)
{
- const AtomicString& hyphenString = style.hyphenString();
- charactersWithHyphen.reserveCapacity(length + hyphenString.length());
- charactersWithHyphen.append(string);
- charactersWithHyphen.append(hyphenString);
- string = charactersWithHyphen.toString();
- length += hyphenString.length();
-}
-
-static const Font& fontToUse(const RenderStyle& style, const RenderText& renderer)
-{
- if (style.hasTextCombine() && renderer.isCombineText()) {
- const RenderCombineText& textCombineRenderer = toRenderCombineText(renderer);
+ if (style.hasTextCombine() && is<RenderCombineText>(renderer)) {
+ const auto& textCombineRenderer = downcast<RenderCombineText>(renderer);
if (textCombineRenderer.isCombined())
return textCombineRenderer.textCombineFont();
}
- return style.font();
+ return style.fontCascade();
}
-LayoutRect InlineTextBox::localSelectionRect(int startPos, int endPos) const
+LayoutRect InlineTextBox::localSelectionRect(unsigned startPos, unsigned endPos) const
{
- int sPos = std::max(startPos - m_start, 0);
- int ePos = std::min(endPos - m_start, (int)m_len);
-
- if (sPos > ePos)
+ unsigned sPos = clampedOffset(startPos);
+ unsigned ePos = clampedOffset(endPos);
+
+ // FIXME: https://bugs.webkit.org/show_bug.cgi?id=160786
+ // We should only be checking if sPos >= ePos here, because those are the
+ // indices used to actually generate the selection rect. Allowing us past this guard
+ // on any other condition creates zero-width selection rects.
+ if (sPos >= ePos && !(startPos == endPos && startPos >= start() && startPos <= (start() + len())))
return LayoutRect();
- FontCachePurgePreventer fontCachePurgePreventer;
-
- LayoutUnit selTop = selectionTop();
- LayoutUnit selHeight = selectionHeight();
+ LayoutUnit selectionTop = this->selectionTop();
+ LayoutUnit selectionHeight = this->selectionHeight();
const RenderStyle& lineStyle = this->lineStyle();
- const Font& font = fontToUse(lineStyle, renderer());
+ const FontCascade& font = fontToUse(lineStyle, renderer());
- BufferForAppendingHyphen charactersWithHyphen;
+ String hyphenatedString;
bool respectHyphen = ePos == m_len && hasHyphen();
- TextRun textRun = constructTextRun(lineStyle, font, respectHyphen ? &charactersWithHyphen : 0);
if (respectHyphen)
- endPos = textRun.length();
-
- FloatPoint startingPoint = FloatPoint(logicalLeft(), selTop);
- LayoutRect r;
- if (sPos || ePos != static_cast<int>(m_len))
- r = enclosingIntRect(font.selectionRectForText(textRun, startingPoint, selHeight, sPos, ePos));
- else // Avoid computing the font width when the entire line box is selected as an optimization.
- r = enclosingIntRect(FloatRect(startingPoint, FloatSize(m_logicalWidth, selHeight)));
-
- LayoutUnit logicalWidth = r.width();
- if (r.x() > logicalRight())
+ hyphenatedString = hyphenatedStringForTextRun(lineStyle);
+ TextRun textRun = constructTextRun(lineStyle, hyphenatedString);
+
+ LayoutRect selectionRect = LayoutRect(LayoutPoint(logicalLeft(), selectionTop), LayoutSize(m_logicalWidth, selectionHeight));
+ // Avoid computing the font width when the entire line box is selected as an optimization.
+ if (sPos || ePos != m_len)
+ font.adjustSelectionRectForText(textRun, selectionRect, sPos, ePos);
+ IntRect snappedSelectionRect = enclosingIntRect(selectionRect);
+ LayoutUnit logicalWidth = snappedSelectionRect.width();
+ if (snappedSelectionRect.x() > logicalRight())
logicalWidth = 0;
- else if (r.maxX() > logicalRight())
- logicalWidth = logicalRight() - r.x();
+ else if (snappedSelectionRect.maxX() > logicalRight())
+ logicalWidth = logicalRight() - snappedSelectionRect.x();
- LayoutPoint topPoint = isHorizontal() ? LayoutPoint(r.x(), selTop) : LayoutPoint(selTop, r.x());
- LayoutUnit width = isHorizontal() ? logicalWidth : selHeight;
- LayoutUnit height = isHorizontal() ? selHeight : logicalWidth;
+ LayoutPoint topPoint = isHorizontal() ? LayoutPoint(snappedSelectionRect.x(), selectionTop) : LayoutPoint(selectionTop, snappedSelectionRect.x());
+ LayoutUnit width = isHorizontal() ? logicalWidth : selectionHeight;
+ LayoutUnit height = isHorizontal() ? selectionHeight : logicalWidth;
return LayoutRect(topPoint, LayoutSize(width, height));
}
@@ -402,7 +331,8 @@ bool InlineTextBox::isLineBreak() const
return renderer().style().preserveNewline() && len() == 1 && (*renderer().text())[start()] == '\n';
}
-bool InlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/)
+bool InlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/,
+ HitTestAction /*hitTestAction*/)
{
if (!visibleToHitTesting())
return false;
@@ -434,33 +364,6 @@ bool InlineTextBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& re
return false;
}
-FloatSize InlineTextBox::applyShadowToGraphicsContext(GraphicsContext* context, const ShadowData* shadow, const FloatRect& textRect, bool stroked, bool opaque, bool horizontal)
-{
- if (!shadow)
- return FloatSize();
-
- FloatSize extraOffset;
- int shadowX = horizontal ? shadow->x() : shadow->y();
- int shadowY = horizontal ? shadow->y() : -shadow->x();
- FloatSize shadowOffset(shadowX, shadowY);
- int shadowRadius = shadow->radius();
- const Color& shadowColor = shadow->color();
-
- if (shadow->next() || stroked || !opaque) {
- FloatRect shadowRect(textRect);
- shadowRect.inflate(shadow->paintingExtent());
- shadowRect.move(shadowOffset);
- context->save();
- context->clip(shadowRect);
-
- extraOffset = FloatSize(0, 2 * textRect.height() + std::max(0.0f, shadowOffset.height()) + shadowRadius);
- shadowOffset -= extraOffset;
- }
-
- context->setShadow(shadowOffset, shadowRadius, shadowColor, context->fillColorSpace());
- return extraOffset;
-}
-
static inline bool emphasisPositionHasNeitherLeftNorRight(TextEmphasisPosition emphasisPosition)
{
return !(emphasisPosition & TextEmphasisPositionLeft) && !(emphasisPosition & TextEmphasisPositionRight);
@@ -491,10 +394,10 @@ bool InlineTextBox::emphasisMarkExistsAndIsAbove(const RenderStyle& style, bool&
if (!containingBlock->isRubyBase())
return true; // This text is not inside a ruby base, so it does not have ruby text over it.
- if (!containingBlock->parent()->isRubyRun())
+ if (!is<RenderRubyRun>(*containingBlock->parent()))
return true; // Cannot get the ruby text.
- RenderRubyText* rubyText = toRenderRubyRun(containingBlock->parent())->rubyText();
+ RenderRubyText* rubyText = downcast<RenderRubyRun>(*containingBlock->parent()).rubyText();
// The emphasis marks over are suppressed only if there is a ruby text box and it not empty.
return !rubyText || !rubyText->hasLines();
@@ -516,7 +419,7 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset,
LayoutUnit paintEnd = isHorizontal() ? paintInfo.rect.maxX() : paintInfo.rect.maxY();
LayoutUnit paintStart = isHorizontal() ? paintInfo.rect.x() : paintInfo.rect.y();
- LayoutPoint adjustedPaintOffset = roundedIntPoint(paintOffset);
+ FloatPoint localPaintOffset(paintOffset);
if (logicalStart >= paintEnd || logicalStart + logicalExtent <= paintStart)
return;
@@ -542,59 +445,45 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset,
LayoutUnit widthOfVisibleText = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
LayoutUnit widthOfHiddenText = m_logicalWidth - widthOfVisibleText;
LayoutSize truncationOffset(isLeftToRightDirection() ? widthOfHiddenText : -widthOfHiddenText, 0);
- adjustedPaintOffset.move(isHorizontal() ? truncationOffset : truncationOffset.transposedSize());
+ localPaintOffset.move(isHorizontal() ? truncationOffset : truncationOffset.transposedSize());
}
}
- GraphicsContext* context = paintInfo.context;
+ GraphicsContext& context = paintInfo.context();
const RenderStyle& lineStyle = this->lineStyle();
- adjustedPaintOffset.move(0, lineStyle.isHorizontalWritingMode() ? 0 : -logicalHeight());
+ localPaintOffset.move(0, lineStyle.isHorizontalWritingMode() ? 0 : -logicalHeight());
FloatPoint boxOrigin = locationIncludingFlipping();
- boxOrigin.move(adjustedPaintOffset.x(), adjustedPaintOffset.y());
+ boxOrigin.moveBy(localPaintOffset);
FloatRect boxRect(boxOrigin, FloatSize(logicalWidth(), logicalHeight()));
- RenderCombineText* combinedText = lineStyle.hasTextCombine() && renderer().isCombineText() && toRenderCombineText(renderer()).isCombined() ? &toRenderCombineText(renderer()) : 0;
+ RenderCombineText* combinedText = lineStyle.hasTextCombine() && is<RenderCombineText>(renderer()) && downcast<RenderCombineText>(renderer()).isCombined() ? &downcast<RenderCombineText>(renderer()) : nullptr;
bool shouldRotate = !isHorizontal() && !combinedText;
if (shouldRotate)
- context->concatCTM(rotation(boxRect, Clockwise));
+ context.concatCTM(rotation(boxRect, Clockwise));
// Determine whether or not we have composition underlines to draw.
bool containsComposition = renderer().textNode() && renderer().frame().editor().compositionNode() == renderer().textNode();
bool useCustomUnderlines = containsComposition && renderer().frame().editor().compositionUsesCustomUnderlines();
// Determine the text colors and selection colors.
- TextPaintStyle textPaintStyle = computeTextPaintStyle(renderer(), lineStyle, paintInfo);
-
- bool paintSelectedTextOnly;
- bool paintSelectedTextSeparately;
- const ShadowData* selectionShadow;
- TextPaintStyle selectionPaintStyle = computeTextSelectionPaintStyle(textPaintStyle, renderer(), lineStyle, paintInfo, paintSelectedTextOnly, paintSelectedTextSeparately, selectionShadow);
+ TextPaintStyle textPaintStyle = computeTextPaintStyle(renderer().frame(), lineStyle, paintInfo);
+ bool paintSelectedTextOnly = false;
+ bool paintSelectedTextSeparately = false;
+ const ShadowData* selectionShadow = nullptr;
+
// Text with custom underlines does not have selection background painted, so selection paint style is not appropriate for it.
- if (useCustomUnderlines)
- selectionPaintStyle = textPaintStyle;
+ TextPaintStyle selectionPaintStyle = haveSelection && !useCustomUnderlines ? computeTextSelectionPaintStyle(textPaintStyle, renderer(), lineStyle, paintInfo, paintSelectedTextOnly, paintSelectedTextSeparately, selectionShadow) : textPaintStyle;
// Set our font.
- const Font& font = fontToUse(lineStyle, renderer());
-
- FloatPoint textOrigin = FloatPoint(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent());
-
- if (combinedText)
- combinedText->adjustTextOrigin(textOrigin, boxRect);
-
+ const FontCascade& font = fontToUse(lineStyle, renderer());
// 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection
// and composition underlines.
if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) {
-#if PLATFORM(MAC)
- // Custom highlighters go behind everything else.
- if (lineStyle.highlight() != nullAtom && !context->paintingDisabled())
- paintCustomHighlight(adjustedPaintOffset, lineStyle.highlight());
-#endif
-
if (containsComposition && !useCustomUnderlines)
paintCompositionBackground(context, boxOrigin, lineStyle, font,
renderer().frame().editor().compositionStart(),
@@ -606,69 +495,68 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset,
paintSelection(context, boxOrigin, lineStyle, font, selectionPaintStyle.fillColor);
}
- if (Page* page = renderer().frame().page()) {
- // FIXME: Right now, InlineTextBoxes never call addRelevantUnpaintedObject() even though they might
- // legitimately be unpainted if they are waiting on a slow-loading web font. We should fix that, and
- // when we do, we will have to account for the fact the InlineTextBoxes do not always have unique
- // renderers and Page currently relies on each unpainted object having a unique renderer.
- if (paintInfo.phase == PaintPhaseForeground)
- page->addRelevantRepaintedObject(&renderer(), IntRect(boxOrigin.x(), boxOrigin.y(), logicalWidth(), logicalHeight()));
- }
+ // FIXME: Right now, InlineTextBoxes never call addRelevantUnpaintedObject() even though they might
+ // legitimately be unpainted if they are waiting on a slow-loading web font. We should fix that, and
+ // when we do, we will have to account for the fact the InlineTextBoxes do not always have unique
+ // renderers and Page currently relies on each unpainted object having a unique renderer.
+ if (paintInfo.phase == PaintPhaseForeground)
+ renderer().page().addRelevantRepaintedObject(&renderer(), IntRect(boxOrigin.x(), boxOrigin.y(), logicalWidth(), logicalHeight()));
// 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
- int length = m_len;
- int maximumLength;
- String string;
- if (!combinedText) {
- string = renderer().text();
- if (static_cast<unsigned>(length) != string.length() || m_start) {
- ASSERT_WITH_SECURITY_IMPLICATION(static_cast<unsigned>(m_start + length) <= string.length());
- string = string.substringSharingImpl(m_start, length);
- }
- maximumLength = renderer().textLength() - m_start;
- } else {
- combinedText->getStringToRender(m_start, string, length);
- maximumLength = length;
- }
+ String alternateStringToRender;
+ if (combinedText)
+ alternateStringToRender = combinedText->combinedStringForRendering();
+ else if (hasHyphen())
+ alternateStringToRender = hyphenatedStringForTextRun(lineStyle);
- BufferForAppendingHyphen charactersWithHyphen;
- TextRun textRun = constructTextRun(lineStyle, font, string, maximumLength, hasHyphen() ? &charactersWithHyphen : 0);
- if (hasHyphen())
- length = textRun.length();
+ TextRun textRun = constructTextRun(lineStyle, alternateStringToRender);
+ unsigned length = textRun.length();
- int sPos = 0;
- int ePos = 0;
+ unsigned selectionStart = 0;
+ unsigned selectionEnd = 0;
if (haveSelection && (paintSelectedTextOnly || paintSelectedTextSeparately))
- selectionStartEnd(sPos, ePos);
+ std::tie(selectionStart, selectionEnd) = selectionStartEnd();
if (m_truncation != cNoTruncation) {
- sPos = std::min<int>(sPos, m_truncation);
- ePos = std::min<int>(ePos, m_truncation);
+ selectionStart = std::min(selectionStart, static_cast<unsigned>(m_truncation));
+ selectionEnd = std::min(selectionEnd, static_cast<unsigned>(m_truncation));
length = m_truncation;
}
- int emphasisMarkOffset = 0;
+ float emphasisMarkOffset = 0;
bool emphasisMarkAbove;
bool hasTextEmphasis = emphasisMarkExistsAndIsAbove(lineStyle, emphasisMarkAbove);
const AtomicString& emphasisMark = hasTextEmphasis ? lineStyle.textEmphasisMarkString() : nullAtom;
if (!emphasisMark.isEmpty())
emphasisMarkOffset = emphasisMarkAbove ? -font.fontMetrics().ascent() - font.emphasisMarkDescent(emphasisMark) : font.fontMetrics().descent() + font.emphasisMarkAscent(emphasisMark);
- const ShadowData* textShadow = paintInfo.forceBlackText() ? 0 : lineStyle.textShadow();
+ const ShadowData* textShadow = (paintInfo.forceTextColor()) ? nullptr : lineStyle.textShadow();
- TextPainter textPainter(*context, paintSelectedTextOnly, paintSelectedTextSeparately, font, sPos, ePos, length, emphasisMark, combinedText, textRun, boxRect, textOrigin, emphasisMarkOffset, textShadow, selectionShadow, isHorizontal(), textPaintStyle, selectionPaintStyle);
- textPainter.paintText();
+ FloatPoint textOrigin(boxOrigin.x(), boxOrigin.y() + font.fontMetrics().ascent());
+ if (combinedText) {
+ if (auto newOrigin = combinedText->computeTextOrigin(boxRect))
+ textOrigin = newOrigin.value();
+ }
+
+ if (isHorizontal())
+ textOrigin.setY(roundToDevicePixel(LayoutUnit(textOrigin.y()), renderer().document().deviceScaleFactor()));
+ else
+ textOrigin.setX(roundToDevicePixel(LayoutUnit(textOrigin.x()), renderer().document().deviceScaleFactor()));
+
+ TextPainter textPainter(context);
+ textPainter.setFont(font);
+ textPainter.setTextPaintStyle(textPaintStyle);
+ textPainter.setSelectionPaintStyle(selectionPaintStyle);
+ textPainter.setIsHorizontal(isHorizontal());
+ textPainter.addTextShadow(textShadow, selectionShadow);
+ textPainter.addEmphasis(emphasisMark, emphasisMarkOffset, combinedText);
+
+ textPainter.paintText(textRun, length, boxRect, textOrigin, selectionStart, selectionEnd, paintSelectedTextOnly, paintSelectedTextSeparately);
// Paint decorations
TextDecoration textDecorations = lineStyle.textDecorationsInEffect();
- if (textDecorations != TextDecorationNone && paintInfo.phase != PaintPhaseSelection) {
- updateGraphicsContext(*context, textPaintStyle);
- if (combinedText)
- context->concatCTM(rotation(boxRect, Clockwise));
- paintDecoration(*context, boxOrigin, textDecorations, lineStyle.textDecorationStyle(), textShadow, textPainter);
- if (combinedText)
- context->concatCTM(rotation(boxRect, Counterclockwise));
- }
+ if (textDecorations != TextDecorationNone && paintInfo.phase != PaintPhaseSelection)
+ paintDecoration(context, font, combinedText, textRun, textOrigin, boxRect, textDecorations, textPaintStyle, textShadow);
if (paintInfo.phase == PaintPhaseForeground) {
paintDocumentMarkers(context, boxOrigin, lineStyle, font, false);
@@ -700,44 +588,41 @@ void InlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset,
}
if (shouldRotate)
- context->concatCTM(rotation(boxRect, Counterclockwise));
+ context.concatCTM(rotation(boxRect, Counterclockwise));
}
-void InlineTextBox::selectionStartEnd(int& sPos, int& ePos)
+unsigned InlineTextBox::clampedOffset(unsigned x) const
{
- int startPos, endPos;
- if (renderer().selectionState() == RenderObject::SelectionInside) {
- startPos = 0;
- endPos = renderer().textLength();
- } else {
- renderer().selectionStartEnd(startPos, endPos);
- if (renderer().selectionState() == RenderObject::SelectionStart)
- endPos = renderer().textLength();
- else if (renderer().selectionState() == RenderObject::SelectionEnd)
- startPos = 0;
- }
-
- sPos = std::max(startPos - m_start, 0);
- ePos = std::min(endPos - m_start, (int)m_len);
+ return std::max(std::min(x, start() + len()), start()) - start();
}
-void alignSelectionRectToDevicePixels(FloatRect& rect)
+std::pair<unsigned, unsigned> InlineTextBox::selectionStartEnd() const
{
- float maxX = floorf(rect.maxX());
- rect.setX(floorf(rect.x()));
- rect.setWidth(roundf(maxX - rect.x()));
+ auto selectionState = renderer().selectionState();
+ if (selectionState == RenderObject::SelectionInside)
+ return { 0, m_len };
+
+ unsigned start;
+ unsigned end;
+ renderer().selectionStartEnd(start, end);
+ if (selectionState == RenderObject::SelectionStart)
+ end = renderer().textLength();
+ else if (selectionState == RenderObject::SelectionEnd)
+ start = 0;
+ return { clampedOffset(start), clampedOffset(end) };
}
-void InlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& boxOrigin, const RenderStyle& style, const Font& font, Color textColor)
+void InlineTextBox::paintSelection(GraphicsContext& context, const FloatPoint& boxOrigin, const RenderStyle& style, const FontCascade& font, const Color& textColor)
{
#if ENABLE(TEXT_SELECTION)
- if (context->paintingDisabled())
+ if (context.paintingDisabled())
return;
// See if we have a selection to paint at all.
- int sPos, ePos;
- selectionStartEnd(sPos, ePos);
- if (sPos >= ePos)
+ unsigned selectionStart;
+ unsigned selectionEnd;
+ std::tie(selectionStart, selectionEnd) = selectionStartEnd();
+ if (selectionStart >= selectionEnd)
return;
Color c = renderer().selectionBackgroundColor();
@@ -749,39 +634,32 @@ void InlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& b
if (textColor == c)
c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
- GraphicsContextStateSaver stateSaver(*context);
- updateGraphicsContext(*context, TextPaintStyle(c, style.colorSpace())); // Don't draw text at all!
-
+ GraphicsContextStateSaver stateSaver(context);
+ updateGraphicsContext(context, TextPaintStyle(c)); // Don't draw text at all!
+
// If the text is truncated, let the thing being painted in the truncation
// draw its own highlight.
- int length = m_truncation != cNoTruncation ? m_truncation : m_len;
- String string = renderer().text();
- if (string.length() != static_cast<unsigned>(length) || m_start) {
- ASSERT_WITH_SECURITY_IMPLICATION(static_cast<unsigned>(m_start + length) <= string.length());
- string = string.substringSharingImpl(m_start, length);
- }
+ unsigned length = m_truncation != cNoTruncation ? m_truncation : len();
- BufferForAppendingHyphen charactersWithHyphen;
- bool respectHyphen = ePos == length && hasHyphen();
- TextRun textRun = constructTextRun(style, font, string, renderer().textLength() - m_start, respectHyphen ? &charactersWithHyphen : 0);
+ String hyphenatedString;
+ bool respectHyphen = selectionEnd == length && hasHyphen();
+ if (respectHyphen)
+ hyphenatedString = hyphenatedStringForTextRun(style, length);
+ TextRun textRun = constructTextRun(style, hyphenatedString, std::optional<unsigned>(length));
if (respectHyphen)
- ePos = textRun.length();
+ selectionEnd = textRun.length();
const RootInlineBox& rootBox = root();
LayoutUnit selectionBottom = rootBox.selectionBottom();
LayoutUnit selectionTop = rootBox.selectionTopAdjustedForPrecedingBlock();
- int deltaY = roundToInt(renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop);
- int selHeight = std::max(0, roundToInt(selectionBottom - selectionTop));
-
- FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
- FloatRect clipRect(localOrigin, FloatSize(m_logicalWidth, selHeight));
- alignSelectionRectToDevicePixels(clipRect);
-
- context->clip(clipRect);
+ LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom - logicalBottom() : logicalTop() - selectionTop;
+ LayoutUnit selectionHeight = std::max<LayoutUnit>(0, selectionBottom - selectionTop);
- context->drawHighlightForText(font, textRun, localOrigin, selHeight, c, style.colorSpace(), sPos, ePos);
+ LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selectionHeight);
+ font.adjustSelectionRectForText(textRun, selectionRect, selectionStart, selectionEnd);
+ context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), c);
#else
UNUSED_PARAM(context);
UNUSED_PARAM(boxOrigin);
@@ -791,476 +669,62 @@ void InlineTextBox::paintSelection(GraphicsContext* context, const FloatPoint& b
#endif
}
-void InlineTextBox::paintCompositionBackground(GraphicsContext* context, const FloatPoint& boxOrigin, const RenderStyle& style, const Font& font, int startPos, int endPos)
+void InlineTextBox::paintCompositionBackground(GraphicsContext& context, const FloatPoint& boxOrigin, const RenderStyle& style, const FontCascade& font, unsigned startPos, unsigned endPos)
{
- int offset = m_start;
- int sPos = std::max(startPos - offset, 0);
- int ePos = std::min(endPos - offset, (int)m_len);
-
- if (sPos >= ePos)
+ unsigned selectionStart = clampedOffset(startPos);
+ unsigned selectionEnd = clampedOffset(endPos);
+ if (selectionStart >= selectionEnd)
return;
- GraphicsContextStateSaver stateSaver(*context);
-
-#if !PLATFORM(IOS)
- // FIXME: Is this color still applicable as of Mavericks? for non-Mac ports? We should
- // be able to move this color information to RenderStyle.
- Color c = Color(225, 221, 85);
-#else
- Color c = style.compositionFillColor();
-#endif
-
- updateGraphicsContext(*context, TextPaintStyle(c, style.colorSpace())); // Don't draw text at all!
-
- int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
- int selHeight = selectionHeight();
- FloatPoint localOrigin(boxOrigin.x(), boxOrigin.y() - deltaY);
- context->drawHighlightForText(font, constructTextRun(style, font), localOrigin, selHeight, c, style.colorSpace(), sPos, ePos);
-}
-
-#if PLATFORM(MAC)
-
-void InlineTextBox::paintCustomHighlight(const LayoutPoint& paintOffset, const AtomicString& type)
-{
- Page* page = renderer().frame().page();
- if (!page)
- return;
-
- const RootInlineBox& rootBox = root();
- FloatRect rootRect(paintOffset.x() + rootBox.x(), paintOffset.y() + selectionTop(), rootBox.logicalWidth(), selectionHeight());
- FloatRect textRect(paintOffset.x() + x(), rootRect.y(), logicalWidth(), rootRect.height());
-
- page->chrome().client().paintCustomHighlight(renderer().textNode(), type, textRect, rootRect, true, false);
-}
-
-#endif
-
-static StrokeStyle textDecorationStyleToStrokeStyle(TextDecorationStyle decorationStyle)
-{
- StrokeStyle strokeStyle = SolidStroke;
- switch (decorationStyle) {
- case TextDecorationStyleSolid:
- strokeStyle = SolidStroke;
- break;
- case TextDecorationStyleDouble:
- strokeStyle = DoubleStroke;
- break;
- case TextDecorationStyleDotted:
- strokeStyle = DottedStroke;
- break;
- case TextDecorationStyleDashed:
- strokeStyle = DashedStroke;
- break;
- case TextDecorationStyleWavy:
- strokeStyle = WavyStroke;
- break;
- }
-
- return strokeStyle;
-}
-
-static int computeUnderlineOffset(const TextUnderlinePosition underlinePosition, const FontMetrics& fontMetrics, const InlineTextBox* inlineTextBox, const int textDecorationThickness)
-{
- // Compute the gap between the font and the underline. Use at least one
- // pixel gap, if underline is thick then use a bigger gap.
- const int gap = std::max<int>(1, ceilf(textDecorationThickness / 2.0));
-
- // According to the specification TextUnderlinePositionAuto should default to 'alphabetic' for horizontal text
- // and to 'under Left' for vertical text (e.g. japanese). We support only horizontal text for now.
- switch (underlinePosition) {
- case TextUnderlinePositionAlphabetic:
- case TextUnderlinePositionAuto:
- return fontMetrics.ascent() + gap; // Position underline near the alphabetic baseline.
- case TextUnderlinePositionUnder: {
- // Position underline relative to the under edge of the lowest element's content box.
- const float offset = inlineTextBox->root().maxLogicalTop() - inlineTextBox->logicalTop();
- return inlineTextBox->logicalHeight() + gap + std::max<float>(offset, 0);
- }
- }
+ GraphicsContextStateSaver stateSaver(context);
+ Color compositionColor = Color::compositionFill;
+ updateGraphicsContext(context, TextPaintStyle(compositionColor)); // Don't draw text at all!
- ASSERT_NOT_REACHED();
- return fontMetrics.ascent() + gap;
+ LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
+ LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, 0, selectionHeight());
+ TextRun textRun = constructTextRun(style);
+ font.adjustSelectionRectForText(textRun, selectionRect, selectionStart, selectionEnd);
+ context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), textRun.ltr()), compositionColor);
}
-static void adjustStepToDecorationLength(float& step, float& controlPointDistance, float length)
+static inline void mirrorRTLSegment(float logicalWidth, TextDirection direction, float& start, float width)
{
- ASSERT(step > 0);
-
- if (length <= 0)
+ if (direction == LTR)
return;
-
- unsigned stepCount = static_cast<unsigned>(length / step);
-
- // Each Bezier curve starts at the same pixel that the previous one
- // ended. We need to subtract (stepCount - 1) pixels when calculating the
- // length covered to account for that.
- float uncoveredLength = length - (stepCount * step - (stepCount - 1));
- float adjustment = uncoveredLength / stepCount;
- step += adjustment;
- controlPointDistance += adjustment;
+ start = logicalWidth - width - start;
}
-static void getWavyStrokeParameters(float strokeThickness, float& controlPointDistance, float& step)
+void InlineTextBox::paintDecoration(GraphicsContext& context, const FontCascade& font, RenderCombineText* combinedText, const TextRun& textRun, const FloatPoint& textOrigin,
+ const FloatRect& boxRect, TextDecoration decoration, TextPaintStyle textPaintStyle, const ShadowData* shadow)
{
- // Distance between decoration's axis and Bezier curve's control points.
- // The height of the curve is based on this distance. Use a minimum of 6 pixels distance since
- // the actual curve passes approximately at half of that distance, that is 3 pixels.
- // The minimum height of the curve is also approximately 3 pixels. Increases the curve's height
- // as strockThickness increases to make the curve looks better.
- controlPointDistance = 3 * std::max<float>(2, strokeThickness);
-
- // Increment used to form the diamond shape between start point (p1), control
- // points and end point (p2) along the axis of the decoration. Makes the
- // curve wider as strockThickness increases to make the curve looks better.
- step = 2 * std::max<float>(2, strokeThickness);
-}
-
-/*
- * Draw one cubic Bezier curve and repeat the same pattern long the the decoration's axis.
- * The start point (p1), controlPoint1, controlPoint2 and end point (p2) of the Bezier curve
- * form a diamond shape:
- *
- * step
- * |-----------|
- *
- * controlPoint1
- * +
- *
- *
- * . .
- * . .
- * . .
- * (x1, y1) p1 + . + p2 (x2, y2) - <--- Decoration's axis
- * . . |
- * . . |
- * . . | controlPointDistance
- * |
- * |
- * + -
- * controlPoint2
- *
- * |-----------|
- * step
- */
-static void strokeWavyTextDecoration(GraphicsContext& context, FloatPoint& p1, FloatPoint& p2, float strokeThickness)
-{
- context.adjustLineToPixelBoundaries(p1, p2, strokeThickness, context.strokeStyle());
-
- Path path;
- path.moveTo(p1);
-
- float controlPointDistance;
- float step;
- getWavyStrokeParameters(strokeThickness, controlPointDistance, step);
-
- bool isVerticalLine = (p1.x() == p2.x());
-
- if (isVerticalLine) {
- ASSERT(p1.x() == p2.x());
-
- float xAxis = p1.x();
- float y1;
- float y2;
-
- if (p1.y() < p2.y()) {
- y1 = p1.y();
- y2 = p2.y();
- } else {
- y1 = p2.y();
- y2 = p1.y();
- }
-
- adjustStepToDecorationLength(step, controlPointDistance, y2 - y1);
- FloatPoint controlPoint1(xAxis + controlPointDistance, 0);
- FloatPoint controlPoint2(xAxis - controlPointDistance, 0);
-
- for (float y = y1; y + 2 * step <= y2;) {
- controlPoint1.setY(y + step);
- controlPoint2.setY(y + step);
- y += 2 * step;
- path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(xAxis, y));
- }
- } else {
- ASSERT(p1.y() == p2.y());
-
- float yAxis = p1.y();
- float x1;
- float x2;
-
- if (p1.x() < p2.x()) {
- x1 = p1.x();
- x2 = p2.x();
- } else {
- x1 = p2.x();
- x2 = p1.x();
- }
-
- adjustStepToDecorationLength(step, controlPointDistance, x2 - x1);
- FloatPoint controlPoint1(0, yAxis + controlPointDistance);
- FloatPoint controlPoint2(0, yAxis - controlPointDistance);
-
- for (float x = x1; x + 2 * step <= x2;) {
- controlPoint1.setX(x + step);
- controlPoint2.setX(x + step);
- x += 2 * step;
- path.addBezierCurveTo(controlPoint1, controlPoint2, FloatPoint(x, yAxis));
- }
- }
-
- context.setShouldAntialias(true);
- context.strokePath(path);
-}
-
-// Because finding the bounding box of an underline is structurally similar to finding
-// the bounding box of a strikethrough, we can pull out the computation and parameterize
-// by the location of the decoration (yOffset, underlineOffset, and doubleOffset).
-static FloatRect boundingBoxForSingleDecoration(GraphicsContext& context, float textDecorationThickness,
- float width, FloatPoint localOrigin, TextDecorationStyle decorationStyle,
- bool isPrinting, float yOffset, float underlineOffset, float doubleOffset)
-{
- FloatRect boundingBox;
-
- switch (decorationStyle) {
- case TextDecorationStyleWavy: {
- FloatPoint start(localOrigin.x(), localOrigin.y() + yOffset);
- FloatPoint end = start + FloatSize(width, 0);
- context.adjustLineToPixelBoundaries(start, end, textDecorationThickness, context.strokeStyle());
-
- float controlPointDistance;
- float step;
- getWavyStrokeParameters(textDecorationThickness, controlPointDistance, step);
-
- adjustStepToDecorationLength(step, controlPointDistance, width);
-
- controlPointDistance += textDecorationThickness;
- FloatPoint boundingBoxOrigin = start - FloatSize(0, controlPointDistance);
- FloatSize boundingBoxSize = FloatSize(width, 2 * controlPointDistance);
- boundingBox = FloatRect(boundingBoxOrigin, boundingBoxSize);
- break;
- }
- default:
- boundingBox = context.computeLineBoundsForText(localOrigin + FloatSize(0, underlineOffset), width, isPrinting);
- if (decorationStyle == TextDecorationStyleDouble)
- boundingBox.unite(context.computeLineBoundsForText(localOrigin + FloatSize(0, doubleOffset), width, isPrinting));
- }
- return boundingBox;
-}
-
-static FloatRect boundingBoxForAllActiveDecorations(InlineTextBox& inlineTextBox, GraphicsContext& context, TextDecoration decoration, float textDecorationThickness, float width, float doubleOffset, TextDecorationStyle decorationStyle, const FloatPoint localOrigin, const RenderStyle& lineStyle, bool isPrinting, int baseline)
-{
- FloatRect boundingBox;
- if (decoration & TextDecorationUnderline) {
- int underlineOffset = computeUnderlineOffset(lineStyle.textUnderlinePosition(), lineStyle.fontMetrics(), &inlineTextBox, textDecorationThickness);
-
- boundingBox.unite(boundingBoxForSingleDecoration(context, textDecorationThickness, width, localOrigin, decorationStyle, isPrinting, underlineOffset + doubleOffset, underlineOffset, baseline + 1));
- }
- if (decoration & TextDecorationOverline)
- boundingBox.unite(boundingBoxForSingleDecoration(context, textDecorationThickness, width, localOrigin, decorationStyle, isPrinting, -doubleOffset, 0, -doubleOffset));
- if (decoration & TextDecorationLineThrough)
- boundingBox.unite(boundingBoxForSingleDecoration(context, textDecorationThickness, width, localOrigin, decorationStyle, isPrinting, 2 * baseline / 3, 2 * baseline / 3, doubleOffset + 2 * baseline / 3));
- return boundingBox;
-}
-
-void InlineTextBox::paintDecoration(GraphicsContext& context, const FloatPoint& boxOrigin, TextDecoration decoration, TextDecorationStyle decorationStyle, const ShadowData* shadow, TextPainter& textPainter)
-{
- // FIXME: We should improve this rule and not always just assume 1.
- const float textDecorationThickness = 1.f;
-
if (m_truncation == cFullTruncation)
return;
- FloatPoint localOrigin = boxOrigin;
+ updateGraphicsContext(context, textPaintStyle);
+ if (combinedText)
+ context.concatCTM(rotation(boxRect, Clockwise));
+ float start = 0;
float width = m_logicalWidth;
if (m_truncation != cNoTruncation) {
width = renderer().width(m_start, m_truncation, textPos(), isFirstLine());
- if (!isLeftToRightDirection())
- localOrigin.move(m_logicalWidth - width, 0);
- }
-
- // Get the text decoration colors.
- Color underline, overline, linethrough;
- renderer().getTextDecorationColors(decoration, underline, overline, linethrough, true);
- if (isFirstLine())
- renderer().getTextDecorationColors(decoration, underline, overline, linethrough, true, true);
-
- // Use a special function for underlines to get the positioning exactly right.
- bool isPrinting = renderer().document().printing();
-#if !PLATFORM(IOS)
- context.setStrokeThickness(textDecorationThickness);
-#else
- // On iOS we want to draw crisp decorations. The function drawLineForText takes the context's
- // strokeThickness and renders that at device pixel scale (i.e. a strokeThickness of 1 will
- // produce a 1 device pixel line, so thinner on retina than non-retina). We will also scale
- // our thickness based on the size of the font. Since our default size is 16px we'll use that
- // as a scale reference.
- float pageScale = 1;
- if (Page* page = renderer().frame().page())
- pageScale = page->pageScaleFactor();
-
- const float textDecorationBaseFontSize = 16;
- float fontSizeScaling = renderer().style().fontSize() / textDecorationBaseFontSize;
- float strokeThickness = roundf(textDecorationThickness * fontSizeScaling * pageScale);
- context.setStrokeThickness(strokeThickness);
-#endif
-
- bool linesAreOpaque = !isPrinting && (!(decoration & TextDecorationUnderline) || underline.alpha() == 255) && (!(decoration & TextDecorationOverline) || overline.alpha() == 255) && (!(decoration & TextDecorationLineThrough) || linethrough.alpha() == 255);
-
- const RenderStyle& lineStyle = this->lineStyle();
- int baseline = lineStyle.fontMetrics().ascent();
-
- bool setClip = false;
- int extraOffset = 0;
- if (!linesAreOpaque && shadow && shadow->next()) {
- FloatRect clipRect(localOrigin, FloatSize(width, baseline + 2));
- for (const ShadowData* s = shadow; s; s = s->next()) {
- int shadowExtent = s->paintingExtent();
- FloatRect shadowRect(localOrigin, FloatSize(width, baseline + 2));
- shadowRect.inflate(shadowExtent);
- int shadowX = isHorizontal() ? s->x() : s->y();
- int shadowY = isHorizontal() ? s->y() : -s->x();
- shadowRect.move(shadowX, shadowY);
- clipRect.unite(shadowRect);
- extraOffset = std::max(extraOffset, std::max(0, shadowY) + shadowExtent);
- }
- context.save();
- context.clip(clipRect);
- extraOffset += baseline + 2;
- localOrigin.move(0, extraOffset);
- setClip = true;
+ mirrorRTLSegment(m_logicalWidth, direction(), start, width);
}
- ColorSpace colorSpace = renderer().style().colorSpace();
- bool setShadow = false;
+ TextDecorationPainter decorationPainter(context, decoration, renderer(), isFirstLine());
+ decorationPainter.setInlineTextBox(this);
+ decorationPainter.setFont(font);
+ decorationPainter.setWidth(width);
+ decorationPainter.setBaseline(lineStyle().fontMetrics().ascent());
+ decorationPainter.setIsHorizontal(isHorizontal());
+ decorationPainter.addTextShadow(shadow);
- do {
- if (shadow) {
- if (!shadow->next()) {
- // The last set of lines paints normally inside the clip.
- localOrigin.move(0, -extraOffset);
- extraOffset = 0;
- }
- int shadowX = isHorizontal() ? shadow->x() : shadow->y();
- int shadowY = isHorizontal() ? shadow->y() : -shadow->x();
- context.setShadow(FloatSize(shadowX, shadowY - extraOffset), shadow->radius(), shadow->color(), colorSpace);
- setShadow = true;
- shadow = shadow->next();
- }
+ FloatPoint localOrigin = boxRect.location();
+ localOrigin.move(start, 0);
+ decorationPainter.paintTextDecoration(textRun, textOrigin, localOrigin);
- // Offset between lines - always non-zero, so lines never cross each other.
- float doubleOffset = textDecorationThickness + 1;
-
- bool clipDecorationToMask = false;
-
- GraphicsContextStateSaver stateSaver(context, false);
-
- if (clipDecorationToMask) {
- const float skipInkGapWidth = 1;
-
- stateSaver.save();
-
- FloatRect underlineRect = boundingBoxForAllActiveDecorations(*this, context, decoration, textDecorationThickness, width, doubleOffset, decorationStyle, localOrigin, lineStyle, isPrinting, baseline);
- IntRect enclosingDeviceRect = enclosingIntRect(underlineRect);
- std::unique_ptr<ImageBuffer> imageBuffer = context.createCompatibleBuffer(enclosingDeviceRect.size());
-
- if (imageBuffer.get()) {
- GraphicsContext& maskContext = *imageBuffer->context();
- maskContext.setFillColor(Color::black, ColorSpaceDeviceRGB);
- maskContext.setLineJoin(RoundJoin);
- maskContext.translate(FloatPoint() - enclosingDeviceRect.location());
-
- maskContext.fillRect(enclosingDeviceRect);
- maskContext.setCompositeOperation(CompositeClear);
-
- textPainter.paintTextInContext(maskContext, skipInkGapWidth);
-
- context.clipToImageBuffer(imageBuffer.get(), enclosingDeviceRect);
- }
- }
- context.setStrokeStyle(textDecorationStyleToStrokeStyle(decorationStyle));
- if (decoration & TextDecorationUnderline) {
- context.setStrokeColor(underline, colorSpace);
- TextUnderlinePosition underlinePosition = lineStyle.textUnderlinePosition();
- const int underlineOffset = computeUnderlineOffset(underlinePosition, lineStyle.fontMetrics(), this, textDecorationThickness);
-
- switch (decorationStyle) {
- case TextDecorationStyleWavy: {
- FloatPoint start(localOrigin.x(), localOrigin.y() + underlineOffset + doubleOffset);
- FloatPoint end(localOrigin.x() + width, localOrigin.y() + underlineOffset + doubleOffset);
- strokeWavyTextDecoration(context, start, end, textDecorationThickness);
- break;
- }
- default:
-#if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
- if (lineStyle.textDecorationSkip() == TextDecorationSkipInk) {
- if (!context.paintingDisabled()) {
- drawSkipInkUnderline(textPainter, context, localOrigin, underlineOffset, width, isPrinting);
-
- if (decorationStyle == TextDecorationStyleDouble)
- drawSkipInkUnderline(textPainter, context, localOrigin, underlineOffset + doubleOffset, width, isPrinting);
- }
- } else {
-#endif // CSS3_TEXT_DECORATION_SKIP_INK
- context.drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + underlineOffset), width, isPrinting);
-
- if (decorationStyle == TextDecorationStyleDouble)
- context.drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + underlineOffset + doubleOffset), width, isPrinting);
-#if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
- }
-#endif
- }
- }
- if (decoration & TextDecorationOverline) {
- context.setStrokeColor(overline, colorSpace);
- switch (decorationStyle) {
- case TextDecorationStyleWavy: {
- FloatPoint start(localOrigin.x(), localOrigin.y() - doubleOffset);
- FloatPoint end(localOrigin.x() + width, localOrigin.y() - doubleOffset);
- strokeWavyTextDecoration(context, start, end, textDecorationThickness);
- break;
- }
- default:
-#if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
- if (lineStyle.textDecorationSkip() == TextDecorationSkipInk) {
- if (!context.paintingDisabled()) {
- drawSkipInkUnderline(textPainter, context, localOrigin, 0, width, isPrinting);
-
- if (decorationStyle == TextDecorationStyleDouble)
- drawSkipInkUnderline(textPainter, context, localOrigin, -doubleOffset, width, isPrinting);
- }
- } else {
-#endif // CSS3_TEXT_DECORATION_SKIP_INK
- context.drawLineForText(localOrigin, width, isPrinting);
- if (decorationStyle == TextDecorationStyleDouble)
- context.drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() - doubleOffset), width, isPrinting);
-#if ENABLE(CSS3_TEXT_DECORATION_SKIP_INK)
- }
-#endif
- }
- }
- if (clipDecorationToMask)
- stateSaver.restore();
- if (decoration & TextDecorationLineThrough) {
- context.setStrokeColor(linethrough, colorSpace);
- switch (decorationStyle) {
- case TextDecorationStyleWavy: {
- FloatPoint start(localOrigin.x(), localOrigin.y() + 2 * baseline / 3);
- FloatPoint end(localOrigin.x() + width, localOrigin.y() + 2 * baseline / 3);
- strokeWavyTextDecoration(context, start, end, textDecorationThickness);
- break;
- }
- default:
- context.drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + 2 * baseline / 3), width, isPrinting);
- if (decorationStyle == TextDecorationStyleDouble)
- context.drawLineForText(FloatPoint(localOrigin.x(), localOrigin.y() + doubleOffset + 2 * baseline / 3), width, isPrinting);
- }
- }
- } while (shadow);
-
- if (setClip)
- context.restore();
- else if (setShadow)
- context.clearShadow();
+ if (combinedText)
+ context.concatCTM(rotation(boxRect, Counterclockwise));
}
static GraphicsContext::DocumentMarkerLineStyle lineStyleForMarkerType(DocumentMarker::MarkerType markerType)
@@ -1285,7 +749,7 @@ static GraphicsContext::DocumentMarkerLineStyle lineStyleForMarkerType(DocumentM
}
}
-void InlineTextBox::paintDocumentMarker(GraphicsContext* pt, const FloatPoint& boxOrigin, DocumentMarker* marker, const RenderStyle& style, const Font& font, bool grammar)
+void InlineTextBox::paintDocumentMarker(GraphicsContext& context, const FloatPoint& boxOrigin, RenderedDocumentMarker& marker, const RenderStyle& style, const FontCascade& font, bool grammar)
{
// Never print spelling/grammar markers (5327887)
if (renderer().document().printing())
@@ -1299,39 +763,32 @@ void InlineTextBox::paintDocumentMarker(GraphicsContext* pt, const FloatPoint& b
// Determine whether we need to measure text
bool markerSpansWholeBox = true;
- if (m_start <= (int)marker->startOffset())
+ if (m_start <= marker.startOffset())
markerSpansWholeBox = false;
- if ((end() + 1) != marker->endOffset()) // end points at the last char, not past it
+ if ((end() + 1) != marker.endOffset()) // end points at the last char, not past it
markerSpansWholeBox = false;
if (m_truncation != cNoTruncation)
markerSpansWholeBox = false;
- bool isDictationMarker = marker->type() == DocumentMarker::DictationAlternatives;
+ bool isDictationMarker = marker.type() == DocumentMarker::DictationAlternatives;
if (!markerSpansWholeBox || grammar || isDictationMarker) {
- int startPosition = std::max<int>(marker->startOffset() - m_start, 0);
- int endPosition = std::min<int>(marker->endOffset() - m_start, m_len);
+ unsigned startPosition = clampedOffset(marker.startOffset());
+ unsigned endPosition = clampedOffset(marker.endOffset());
if (m_truncation != cNoTruncation)
- endPosition = std::min<int>(endPosition, m_truncation);
+ endPosition = std::min(endPosition, static_cast<unsigned>(m_truncation));
// Calculate start & width
int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
int selHeight = selectionHeight();
FloatPoint startPoint(boxOrigin.x(), boxOrigin.y() - deltaY);
- TextRun run = constructTextRun(style, font);
+ TextRun run = constructTextRun(style);
- // FIXME: Convert the document markers to float rects.
- IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, selHeight, startPosition, endPosition));
+ LayoutRect selectionRect = LayoutRect(startPoint, FloatSize(0, selHeight));
+ font.adjustSelectionRectForText(run, selectionRect, startPosition, endPosition);
+ IntRect markerRect = enclosingIntRect(selectionRect);
start = markerRect.x() - startPoint.x();
width = markerRect.width();
-
- // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to
- // display a toolTip. We don't do this for misspelling markers.
- if (grammar || isDictationMarker) {
- markerRect.move(-boxOrigin.x(), -boxOrigin.y());
- markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
- toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
- }
}
// IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
@@ -1351,65 +808,44 @@ void InlineTextBox::paintDocumentMarker(GraphicsContext* pt, const FloatPoint& b
// In larger fonts, though, place the underline up near the baseline to prevent a big gap.
underlineOffset = baseline + 2;
}
- pt->drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForMarkerType(marker->type()));
+ context.drawLineForDocumentMarker(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + underlineOffset), width, lineStyleForMarkerType(marker.type()));
}
-void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, const FloatPoint& boxOrigin, DocumentMarker* marker, const RenderStyle& style, const Font& font)
+void InlineTextBox::paintTextMatchMarker(GraphicsContext& context, const FloatPoint& boxOrigin, RenderedDocumentMarker& marker, const RenderStyle& style, const FontCascade& font)
{
+ if (!renderer().frame().editor().markedTextMatchesAreHighlighted())
+ return;
+
+ Color color = marker.isActiveMatch() ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
+ GraphicsContextStateSaver stateSaver(context);
+ updateGraphicsContext(context, TextPaintStyle(color)); // Don't draw text at all!
+
// Use same y positioning and height as for selection, so that when the selection and this highlight are on
// the same word there are no pieces sticking out.
- int deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
- int selHeight = selectionHeight();
+ LayoutUnit deltaY = renderer().style().isFlippedLinesWritingMode() ? selectionBottom() - logicalBottom() : logicalTop() - selectionTop();
+ LayoutRect selectionRect = LayoutRect(boxOrigin.x(), boxOrigin.y() - deltaY, 0, this->selectionHeight());
- int sPos = std::max(marker->startOffset() - m_start, (unsigned)0);
- int ePos = std::min(marker->endOffset() - m_start, (unsigned)m_len);
- TextRun run = constructTextRun(style, font);
+ unsigned sPos = clampedOffset(marker.startOffset());
+ unsigned ePos = clampedOffset(marker.endOffset());
+ TextRun run = constructTextRun(style);
+ font.adjustSelectionRectForText(run, selectionRect, sPos, ePos);
- // Always compute and store the rect associated with this marker. The computed rect is in absolute coordinates.
- IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, IntPoint(x(), selectionTop()), selHeight, sPos, ePos));
- markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
- toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
-
- // Optionally highlight the text
- if (renderer().frame().editor().markedTextMatchesAreHighlighted()) {
- Color color = marker->activeMatch() ? renderer().theme().platformActiveTextSearchHighlightColor() : renderer().theme().platformInactiveTextSearchHighlightColor();
- GraphicsContextStateSaver stateSaver(*pt);
- updateGraphicsContext(*pt, TextPaintStyle(color, style.colorSpace())); // Don't draw text at all!
- pt->clip(FloatRect(boxOrigin.x(), boxOrigin.y() - deltaY, m_logicalWidth, selHeight));
- pt->drawHighlightForText(font, run, FloatPoint(boxOrigin.x(), boxOrigin.y() - deltaY), selHeight, color, style.colorSpace(), sPos, ePos);
- }
-}
+ if (selectionRect.isEmpty())
+ return;
-void InlineTextBox::computeRectForReplacementMarker(DocumentMarker* marker, const RenderStyle& style, const Font& font)
-{
- // Replacement markers are not actually drawn, but their rects need to be computed for hit testing.
- int top = selectionTop();
- int h = selectionHeight();
-
- int sPos = std::max(marker->startOffset() - m_start, (unsigned)0);
- int ePos = std::min(marker->endOffset() - m_start, (unsigned)m_len);
- TextRun run = constructTextRun(style, font);
- IntPoint startPoint = IntPoint(x(), top);
-
- // Compute and store the rect associated with this marker.
- IntRect markerRect = enclosingIntRect(font.selectionRectForText(run, startPoint, h, sPos, ePos));
- markerRect = renderer().localToAbsoluteQuad(FloatRect(markerRect)).enclosingBoundingBox();
- toRenderedDocumentMarker(marker)->setRenderedRect(markerRect);
+ context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()), color);
}
-void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, const FloatPoint& boxOrigin, const RenderStyle& style, const Font& font, bool background)
+void InlineTextBox::paintDocumentMarkers(GraphicsContext& context, const FloatPoint& boxOrigin, const RenderStyle& style, const FontCascade& font, bool background)
{
if (!renderer().textNode())
return;
- Vector<DocumentMarker*> markers = renderer().document().markers().markersFor(renderer().textNode());
- Vector<DocumentMarker*>::const_iterator markerIt = markers.begin();
+ Vector<RenderedDocumentMarker*> markers = renderer().document().markers().markersFor(renderer().textNode());
// Give any document markers that touch this run a chance to draw before the text has been drawn.
// Note end() points at the last char, not one past it like endOffset and ranges do.
- for ( ; markerIt != markers.end(); ++markerIt) {
- DocumentMarker* marker = *markerIt;
-
+ for (auto* marker : markers) {
// Paint either the background markers or the foreground markers, but not both
switch (marker->type()) {
case DocumentMarker::Grammar:
@@ -1425,6 +861,9 @@ void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, const FloatPoint&
continue;
break;
case DocumentMarker::TextMatch:
+#if ENABLE(TELEPHONE_NUMBER_DETECTION)
+ case DocumentMarker::TelephoneNumber:
+#endif
if (!background)
continue;
break;
@@ -1446,23 +885,26 @@ void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, const FloatPoint&
case DocumentMarker::Spelling:
case DocumentMarker::CorrectionIndicator:
case DocumentMarker::DictationAlternatives:
- paintDocumentMarker(pt, boxOrigin, marker, style, font, false);
+ paintDocumentMarker(context, boxOrigin, *marker, style, font, false);
break;
case DocumentMarker::Grammar:
- paintDocumentMarker(pt, boxOrigin, marker, style, font, true);
+ paintDocumentMarker(context, boxOrigin, *marker, style, font, true);
break;
#if PLATFORM(IOS)
// FIXME: See <rdar://problem/8933352>. Also, remove the PLATFORM(IOS)-guard.
case DocumentMarker::DictationPhraseWithAlternatives:
- paintDocumentMarker(pt, boxOrigin, marker, style, font, true);
+ paintDocumentMarker(context, boxOrigin, *marker, style, font, true);
break;
#endif
case DocumentMarker::TextMatch:
- paintTextMatchMarker(pt, boxOrigin, marker, style, font);
+ paintTextMatchMarker(context, boxOrigin, *marker, style, font);
break;
case DocumentMarker::Replacement:
- computeRectForReplacementMarker(marker, style, font);
break;
+#if ENABLE(TELEPHONE_NUMBER_DETECTION)
+ case DocumentMarker::TelephoneNumber:
+ break;
+#endif
default:
ASSERT_NOT_REACHED();
}
@@ -1470,7 +912,7 @@ void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, const FloatPoint&
}
}
-void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, const FloatPoint& boxOrigin, const CompositionUnderline& underline)
+void InlineTextBox::paintCompositionUnderline(GraphicsContext& context, const FloatPoint& boxOrigin, const CompositionUnderline& underline)
{
if (m_truncation == cFullTruncation)
return;
@@ -1495,6 +937,7 @@ void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, const FloatP
}
if (!useWholeWidth) {
width = renderer().width(paintStart, paintEnd - paintStart, textPos() + start, isFirstLine());
+ mirrorRTLSegment(m_logicalWidth, direction(), start, width);
}
// Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline.
@@ -1510,9 +953,9 @@ void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, const FloatP
start += 1;
width -= 2;
- ctx->setStrokeColor(underline.color, renderer().style().colorSpace());
- ctx->setStrokeThickness(lineThickness);
- ctx->drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, renderer().document().printing());
+ context.setStrokeColor(underline.color);
+ context.setStrokeThickness(lineThickness);
+ context.drawLineForText(FloatPoint(boxOrigin.x() + start, boxOrigin.y() + logicalHeight() - lineThickness), width, renderer().document().printing());
}
int InlineTextBox::caretMinOffset() const
@@ -1544,60 +987,54 @@ int InlineTextBox::offsetForPosition(float lineOffset, bool includePartialGlyphs
if (lineOffset - logicalLeft() < 0)
return isLeftToRightDirection() ? 0 : len();
- FontCachePurgePreventer fontCachePurgePreventer;
-
const RenderStyle& lineStyle = this->lineStyle();
- const Font& font = fontToUse(lineStyle, renderer());
- return font.offsetForPosition(constructTextRun(lineStyle, font), lineOffset - logicalLeft(), includePartialGlyphs);
+ const FontCascade& font = fontToUse(lineStyle, renderer());
+ return font.offsetForPosition(constructTextRun(lineStyle), lineOffset - logicalLeft(), includePartialGlyphs);
}
-float InlineTextBox::positionForOffset(int offset) const
+float InlineTextBox::positionForOffset(unsigned offset) const
{
ASSERT(offset >= m_start);
- ASSERT(offset <= m_start + m_len);
+ ASSERT(offset <= m_start + len());
if (isLineBreak())
return logicalLeft();
- FontCachePurgePreventer fontCachePurgePreventer;
-
const RenderStyle& lineStyle = this->lineStyle();
- const Font& font = fontToUse(lineStyle, renderer());
- int from = !isLeftToRightDirection() ? offset - m_start : 0;
- int to = !isLeftToRightDirection() ? m_len : offset - m_start;
+ const FontCascade& font = fontToUse(lineStyle, renderer());
+ unsigned from = !isLeftToRightDirection() ? clampedOffset(offset) : 0;
+ unsigned to = !isLeftToRightDirection() ? m_len : clampedOffset(offset);
// FIXME: Do we need to add rightBearing here?
- return font.selectionRectForText(constructTextRun(lineStyle, font), IntPoint(logicalLeft(), 0), 0, from, to).maxX();
+ LayoutRect selectionRect = LayoutRect(logicalLeft(), 0, 0, 0);
+ TextRun run = constructTextRun(lineStyle);
+ font.adjustSelectionRectForText(run, selectionRect, from, to);
+ return snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()).maxX();
}
-TextRun InlineTextBox::constructTextRun(const RenderStyle& style, const Font& font, BufferForAppendingHyphen* charactersWithHyphen) const
+StringView InlineTextBox::substringToRender(std::optional<unsigned> overridingLength) const
{
- ASSERT(renderer().text());
-
- String string = renderer().text();
- unsigned startPos = start();
- unsigned length = len();
-
- if (string.length() != length || startPos)
- string = string.substringSharingImpl(startPos, length);
-
- return constructTextRun(style, font, string, renderer().textLength() - startPos, charactersWithHyphen);
+ return StringView(renderer().text()).substring(start(), overridingLength.value_or(len()));
}
-TextRun InlineTextBox::constructTextRun(const RenderStyle& style, const Font& font, String string, int maximumLength, BufferForAppendingHyphen* charactersWithHyphen) const
+String InlineTextBox::hyphenatedStringForTextRun(const RenderStyle& style, std::optional<unsigned> alternateLength) const
{
- int length = string.length();
+ ASSERT(hasHyphen());
+ return makeString(substringToRender(alternateLength), style.hyphenString());
+}
- if (charactersWithHyphen) {
- adjustCharactersAndLengthForHyphen(*charactersWithHyphen, style, string, length);
- maximumLength = length;
- }
+TextRun InlineTextBox::constructTextRun(const RenderStyle& style, StringView alternateStringToRender, std::optional<unsigned> alternateLength) const
+{
+ if (alternateStringToRender.isNull())
+ return constructTextRun(style, substringToRender(alternateLength), renderer().textLength() - start());
+ return constructTextRun(style, alternateStringToRender, alternateStringToRender.length());
+}
- ASSERT(maximumLength >= length);
+TextRun InlineTextBox::constructTextRun(const RenderStyle& style, StringView string, unsigned maximumLength) const
+{
+ ASSERT(maximumLength >= string.length());
TextRun run(string, textPos(), expansion(), expansionBehavior(), direction(), dirOverride() || style.rtlOrdering() == VisualOrder, !renderer().canUseSimpleFontCodePath());
run.setTabSize(!style.collapseWhiteSpace(), style.tabSize());
- if (font.isSVGFont())
- run.setRenderingContext(SVGTextRunRenderingContext::create(renderer()));
// Propagate the maximum length of the characters buffer to the TextRun, even when we're only processing a substring.
run.setCharactersLength(maximumLength);
@@ -1605,27 +1042,51 @@ TextRun InlineTextBox::constructTextRun(const RenderStyle& style, const Font& fo
return run;
}
-#ifndef NDEBUG
+ExpansionBehavior InlineTextBox::expansionBehavior() const
+{
+ ExpansionBehavior leadingBehavior;
+ if (forceLeadingExpansion())
+ leadingBehavior = ForceLeadingExpansion;
+ else if (canHaveLeadingExpansion())
+ leadingBehavior = AllowLeadingExpansion;
+ else
+ leadingBehavior = ForbidLeadingExpansion;
+
+ ExpansionBehavior trailingBehavior;
+ if (forceTrailingExpansion())
+ trailingBehavior = ForceTrailingExpansion;
+ else if (expansion() && nextLeafChild() && !nextLeafChild()->isLineBreak())
+ trailingBehavior = AllowTrailingExpansion;
+ else
+ trailingBehavior = ForbidTrailingExpansion;
+
+ return leadingBehavior | trailingBehavior;
+}
+
+#if ENABLE(TREE_DEBUGGING)
const char* InlineTextBox::boxName() const
{
return "InlineTextBox";
}
-void InlineTextBox::showBox(int printedCharacters) const
+void InlineTextBox::showLineBox(bool mark, int depth) const
{
+ fprintf(stderr, "-------- %c-", isDirty() ? 'D' : '-');
+
+ int printedCharacters = 0;
+ if (mark) {
+ fprintf(stderr, "*");
+ ++printedCharacters;
+ }
+ while (++printedCharacters <= depth * 2)
+ fputc(' ', stderr);
+
String value = renderer().text();
value = value.substring(start(), len());
value.replaceWithLiteral('\\', "\\\\");
value.replaceWithLiteral('\n', "\\n");
- printedCharacters += fprintf(stderr, "%s\t%p", boxName(), this);
- for (; printedCharacters < showTreeCharacterOffset; printedCharacters++)
- fputc(' ', stderr);
- printedCharacters = fprintf(stderr, "\t%s %p", renderer().renderName(), &renderer());
- const int rendererCharacterOffset = 24;
- for (; printedCharacters < rendererCharacterOffset; printedCharacters++)
- fputc(' ', stderr);
- fprintf(stderr, "(%d,%d) \"%s\"\n", start(), start() + len(), value.utf8().data());
+ fprintf(stderr, "%s (%.2f, %.2f) (%.2f, %.2f) (%p) renderer->(%p) run(%d, %d) \"%s\"\n", boxName(), x(), y(), width(), height(), this, &renderer(), start(), start() + len(), value.utf8().data());
}
#endif