void SVGInlineTextBoxPainter::paintText(const PaintInfo& paintInfo, const ComputedStyle& style, const ComputedStyle& selectionStyle, const SVGTextFragment& fragment, LayoutSVGResourceMode resourceMode, bool shouldPaintSelection) { int startPosition = 0; int endPosition = 0; if (shouldPaintSelection) { m_svgInlineTextBox.selectionStartEnd(startPosition, endPosition); shouldPaintSelection = m_svgInlineTextBox.mapStartEndPositionsIntoFragmentCoordinates(fragment, startPosition, endPosition); } // Fast path if there is no selection, just draw the whole chunk part using the regular style TextRun textRun = m_svgInlineTextBox.constructTextRun(style, fragment); if (!shouldPaintSelection || startPosition >= endPosition) { paintTextWithShadows(paintInfo, style, textRun, fragment, 0, fragment.length, resourceMode); return; } // Eventually draw text using regular style until the start position of the selection bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection; if (startPosition > 0 && !paintSelectedTextOnly) paintTextWithShadows(paintInfo, style, textRun, fragment, 0, startPosition, resourceMode); // Draw text using selection style from the start to the end position of the selection if (style != selectionStyle) { StyleDifference diff; diff.setNeedsPaintInvalidationObject(); SVGResourcesCache::clientStyleChanged(&m_svgInlineTextBox.parent()->layoutObject(), diff, selectionStyle); } paintTextWithShadows(paintInfo, selectionStyle, textRun, fragment, startPosition, endPosition, resourceMode); if (style != selectionStyle) { StyleDifference diff; diff.setNeedsPaintInvalidationObject(); SVGResourcesCache::clientStyleChanged(&m_svgInlineTextBox.parent()->layoutObject(), diff, 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(paintInfo, style, textRun, fragment, endPosition, fragment.length, resourceMode); }
void TextPainter::paintText() { FloatPoint boxOrigin = m_boxRect.location(); if (!m_paintSelectedTextOnly) { // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side // effect, so only when we know we're stroking, do a save/restore. GraphicsContextStateSaver stateSaver(m_context, m_textPaintStyle.strokeWidth > 0); updateGraphicsContext(m_context, m_textPaintStyle); if (!m_paintSelectedTextSeparately || m_endPositionInTextRun <= m_startPositionInTextRun) { // FIXME: Truncate right-to-left text correctly. paintTextWithShadows(m_context, m_font, m_textRun, nullAtom, 0, 0, m_length, m_length, m_textOrigin, m_boxRect, m_textShadow, m_textPaintStyle.strokeWidth > 0, m_textBoxIsHorizontal); } else paintTextWithShadows(m_context, m_font, m_textRun, nullAtom, 0, m_endPositionInTextRun, m_startPositionInTextRun, m_length, m_textOrigin, m_boxRect, m_textShadow, m_textPaintStyle.strokeWidth > 0, m_textBoxIsHorizontal); if (!m_emphasisMark.isEmpty()) { updateGraphicsContext(m_context, m_textPaintStyle, UseEmphasisMarkColor); static NeverDestroyed<TextRun> objectReplacementCharacterTextRun(&objectReplacementCharacter, 1); TextRun& emphasisMarkTextRun = m_combinedText ? objectReplacementCharacterTextRun.get() : m_textRun; FloatPoint emphasisMarkTextOrigin = m_combinedText ? FloatPoint(boxOrigin.x() + m_boxRect.width() / 2, boxOrigin.y() + m_font.fontMetrics().ascent()) : m_textOrigin; if (m_combinedText) m_context.concatCTM(rotation(m_boxRect, Clockwise)); if (!m_paintSelectedTextSeparately || m_endPositionInTextRun <= m_startPositionInTextRun) { // FIXME: Truncate right-to-left text correctly. paintTextWithShadows(m_context, m_combinedText ? m_combinedText->originalFont() : m_font, emphasisMarkTextRun, m_emphasisMark, m_emphasisMarkOffset, 0, m_length, m_length, emphasisMarkTextOrigin, m_boxRect, m_textShadow, m_textPaintStyle.strokeWidth > 0, m_textBoxIsHorizontal); } else paintTextWithShadows(m_context, m_combinedText ? m_combinedText->originalFont() : m_font, emphasisMarkTextRun, m_emphasisMark, m_emphasisMarkOffset, m_endPositionInTextRun, m_startPositionInTextRun, m_length, emphasisMarkTextOrigin, m_boxRect, m_textShadow, m_textPaintStyle.strokeWidth > 0, m_textBoxIsHorizontal); if (m_combinedText) m_context.concatCTM(rotation(m_boxRect, Counterclockwise)); } } if ((m_paintSelectedTextOnly || m_paintSelectedTextSeparately) && m_startPositionInTextRun < m_endPositionInTextRun) { // paint only the text that is selected GraphicsContextStateSaver stateSaver(m_context, m_selectionPaintStyle.strokeWidth > 0); updateGraphicsContext(m_context, m_selectionPaintStyle); paintTextWithShadows(m_context, m_font, m_textRun, nullAtom, 0, m_startPositionInTextRun, m_endPositionInTextRun, m_length, m_textOrigin, m_boxRect, m_selectionShadow, m_selectionPaintStyle.strokeWidth > 0, m_textBoxIsHorizontal); if (!m_emphasisMark.isEmpty()) { updateGraphicsContext(m_context, m_selectionPaintStyle, UseEmphasisMarkColor); DEPRECATED_DEFINE_STATIC_LOCAL(TextRun, objectReplacementCharacterTextRun, (&objectReplacementCharacter, 1)); TextRun& emphasisMarkTextRun = m_combinedText ? objectReplacementCharacterTextRun : m_textRun; FloatPoint emphasisMarkTextOrigin = m_combinedText ? FloatPoint(boxOrigin.x() + m_boxRect.width() / 2, boxOrigin.y() + m_font.fontMetrics().ascent()) : m_textOrigin; if (m_combinedText) m_context.concatCTM(rotation(m_boxRect, Clockwise)); paintTextWithShadows(m_context, m_combinedText ? m_combinedText->originalFont() : m_font, emphasisMarkTextRun, m_emphasisMark, m_emphasisMarkOffset, m_startPositionInTextRun, m_endPositionInTextRun, m_length, emphasisMarkTextOrigin, m_boxRect, m_selectionShadow, m_selectionPaintStyle.strokeWidth > 0, m_textBoxIsHorizontal); if (m_combinedText) m_context.concatCTM(rotation(m_boxRect, Counterclockwise)); } } }
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 InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) { if (isLineBreak() || !object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE || m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline) return; ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines); int xPos = tx + m_x - parent()->maxHorizontalVisualOverflow(); int w = width() + 2 * parent()->maxHorizontalVisualOverflow(); if (xPos >= paintInfo.rect.right() || xPos + w <= paintInfo.rect.x()) return; bool isPrinting = textObject()->document()->printing(); // Determine whether or not we're selected. bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone; if (!haveSelection && paintInfo.phase == PaintPhaseSelection) // When only painting the selection, don't bother to paint if there is none. return; GraphicsContext* context = paintInfo.context; // Determine whether or not we have composition underlines to draw. bool containsComposition = object()->document()->frame()->editor()->compositionNode() == object()->node(); bool useCustomUnderlines = containsComposition && object()->document()->frame()->editor()->compositionUsesCustomUnderlines(); // Set our font. RenderStyle* styleToUse = object()->style(m_firstLine); int d = styleToUse->textDecorationsInEffect(); const Font* font = &styleToUse->font(); if (*font != context->font()) context->setFont(*font); // 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 (styleToUse->highlight() != nullAtom && !context->paintingDisabled()) paintCustomHighlight(tx, ty, styleToUse->highlight()); #endif if (containsComposition && !useCustomUnderlines) paintCompositionBackground(context, tx, ty, styleToUse, font, object()->document()->frame()->editor()->compositionStart(), object()->document()->frame()->editor()->compositionEnd()); paintDocumentMarkers(context, tx, ty, styleToUse, font, true); if (haveSelection && !useCustomUnderlines) paintSelection(context, tx, ty, styleToUse, font); } // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only). if (m_len <= 0) return; Color textFillColor; Color textStrokeColor; float textStrokeWidth = styleToUse->textStrokeWidth(); ShadowData* textShadow = paintInfo.forceBlackText ? 0 : styleToUse->textShadow(); if (paintInfo.forceBlackText) { textFillColor = Color::black; textStrokeColor = Color::black; } else { textFillColor = styleToUse->textFillColor(); if (!textFillColor.isValid()) textFillColor = styleToUse->color(); // Make the text fill color legible against a white background if (styleToUse->forceBackgroundsToWhite()) textFillColor = correctedTextColor(textFillColor, Color::white); textStrokeColor = styleToUse->textStrokeColor(); if (!textStrokeColor.isValid()) textStrokeColor = styleToUse->color(); // Make the text stroke color legible against a white background if (styleToUse->forceBackgroundsToWhite()) textStrokeColor = correctedTextColor(textStrokeColor, Color::white); } bool paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection); bool paintSelectedTextSeparately = false; Color selectionFillColor = textFillColor; Color selectionStrokeColor = textStrokeColor; float selectionStrokeWidth = textStrokeWidth; ShadowData* selectionShadow = textShadow; if (haveSelection) { // Check foreground color first. Color foreground = paintInfo.forceBlackText ? Color::black : object()->selectionForegroundColor(); if (foreground.isValid() && foreground != selectionFillColor) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionFillColor = foreground; } if (RenderStyle* pseudoStyle = object()->getCachedPseudoStyle(RenderStyle::SELECTION)) { ShadowData* shadow = paintInfo.forceBlackText ? 0 : pseudoStyle->textShadow(); if (shadow != selectionShadow) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionShadow = shadow; } float strokeWidth = pseudoStyle->textStrokeWidth(); if (strokeWidth != selectionStrokeWidth) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionStrokeWidth = strokeWidth; } Color stroke = paintInfo.forceBlackText ? Color::black : pseudoStyle->textStrokeColor(); if (!stroke.isValid()) stroke = pseudoStyle->color(); if (stroke != selectionStrokeColor) { if (!paintSelectedTextOnly) paintSelectedTextSeparately = true; selectionStrokeColor = stroke; } } } IntPoint textOrigin(m_x + tx, m_y + ty + m_baseline); TextRun textRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()); int sPos = 0; int ePos = 0; if (paintSelectedTextOnly || paintSelectedTextSeparately) selectionStartEnd(sPos, ePos); if (!paintSelectedTextOnly) { // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side // effect, so only when we know we're stroking, do a save/restore. if (textStrokeWidth > 0) context->save(); updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth); if (!paintSelectedTextSeparately || ePos <= sPos) { // FIXME: Truncate right-to-left text correctly. paintTextWithShadows(context, textRun, 0, m_truncation == cNoTruncation ? m_len : m_truncation, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); } else paintTextWithShadows(context, textRun, ePos, sPos, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); if (textStrokeWidth > 0) context->restore(); } if ((paintSelectedTextOnly || paintSelectedTextSeparately) && sPos < ePos) { // paint only the text that is selected if (selectionStrokeWidth > 0) context->save(); updateGraphicsContext(context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth); paintTextWithShadows(context, textRun, sPos, ePos, textOrigin, m_x + tx, m_y + ty, width(), height(), selectionShadow, selectionStrokeWidth > 0); if (selectionStrokeWidth > 0) context->restore(); } // Paint decorations if (d != TDNONE && paintInfo.phase != PaintPhaseSelection && styleToUse->htmlHacks()) { context->setStrokeColor(styleToUse->color()); paintDecoration(context, tx, ty, d, textShadow); } if (paintInfo.phase == PaintPhaseForeground) { paintDocumentMarkers(context, tx, ty, styleToUse, font, false); if (useCustomUnderlines) { const Vector<CompositionUnderline>& underlines = object()->document()->frame()->editor()->customCompositionUnderlines(); size_t numUnderlines = underlines.size(); for (size_t index = 0; index < numUnderlines; ++index) { const CompositionUnderline& underline = underlines[index]; if (underline.endOffset <= start()) // underline is completely before this run. This might be an underline that sits // before the first run we draw, or underlines that were within runs we skipped // due to truncation. continue; if (underline.startOffset <= end()) { // underline intersects this run. Paint it. paintCompositionUnderline(context, tx, ty, underline); if (underline.endOffset > end() + 1) // underline also runs into the next run. Bail now, no more marker advancement. break; } else // underline is completely after this run, bail. A later run will paint it. break; } } } }