void SVGInlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit, LayoutUnit) { ASSERT(paintInfo.shouldPaintWithinRoot(renderer())); ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); ASSERT(truncation() == cNoTruncation); if (renderer()->style()->visibility() != VISIBLE) return; // Note: We're explicitely not supporting composition & custom underlines and custom highlighters - unlike InlineTextBox. // If we ever need that for SVG, it's very easy to refactor and reuse the code. RenderObject* parentRenderer = parent()->renderer(); ASSERT(parentRenderer); bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection; bool hasSelection = !parentRenderer->document().printing() && selectionState() != RenderObject::SelectionNone; if (!hasSelection && paintSelectedTextOnly) return; RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer()); ASSERT(textRenderer); if (!textShouldBePainted(textRenderer)) return; RenderStyle* style = parentRenderer->style(); ASSERT(style); paintDocumentMarkers(paintInfo.context, paintOffset, style, textRenderer->scaledFont(), true); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); bool hasFill = svgStyle->hasFill(); bool hasVisibleStroke = svgStyle->hasVisibleStroke(); RenderStyle* selectionStyle = style; if (hasSelection) { selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION); if (selectionStyle) { const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle(); ASSERT(svgSelectionStyle); if (!hasFill) hasFill = svgSelectionStyle->hasFill(); if (!hasVisibleStroke) hasVisibleStroke = svgSelectionStyle->hasVisibleStroke(); } else selectionStyle = style; } if (textRenderer->frame() && textRenderer->frame()->view() && textRenderer->frame()->view()->paintBehavior() & PaintBehaviorRenderingSVGMask) { hasFill = true; hasVisibleStroke = false; } AffineTransform fragmentTransform; unsigned textFragmentsSize = m_textFragments.size(); for (unsigned i = 0; i < textFragmentsSize; ++i) { SVGTextFragment& fragment = m_textFragments.at(i); ASSERT(!m_paintingResource); GraphicsContextStateSaver stateSaver(*paintInfo.context, false); fragment.buildFragmentTransform(fragmentTransform); if (!fragmentTransform.isIdentity()) { stateSaver.save(); paintInfo.context->concatCTM(fragmentTransform); } // Spec: All text decorations except line-through should be drawn before the text is filled and stroked; thus, the text is rendered on top of these decorations. unsigned decorations = style->textDecorationsInEffect(); if (decorations & TextDecorationUnderline) paintDecoration(paintInfo.context, TextDecorationUnderline, fragment); if (decorations & TextDecorationOverline) paintDecoration(paintInfo.context, TextDecorationOverline, fragment); for (int i = 0; i < 3; i++) { switch (svgStyle->paintOrderType(i)) { case PT_FILL: // Fill text if (hasFill) { m_paintingResourceMode = ApplyToFillMode | ApplyToTextMode; paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly); } break; case PT_STROKE: // Stroke text if (hasVisibleStroke) { m_paintingResourceMode = ApplyToStrokeMode | ApplyToTextMode; paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly); } break; case PT_MARKERS: // Markers don't apply to text break; default: ASSERT_NOT_REACHED(); break; } } // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text. if (decorations & TextDecorationLineThrough) paintDecoration(paintInfo.context, TextDecorationLineThrough, fragment); m_paintingResourceMode = ApplyToDefaultMode; } ASSERT(!m_paintingResource); }
void SVGInlineTextBox::paint(PaintInfo& paintInfo, int, int) { ASSERT(paintInfo.shouldPaintWithinRoot(renderer())); ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); ASSERT(truncation() == cNoTruncation); if (renderer()->style()->visibility() != VISIBLE) return; // Note: We're explicitely not supporting composition & custom underlines and custom highlighters - unlike InlineTextBox. // If we ever need that for SVG, it's very easy to refactor and reuse the code. RenderObject* parentRenderer = parent()->renderer(); ASSERT(parentRenderer); bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection; bool hasSelection = !parentRenderer->document()->printing() && selectionState() != RenderObject::SelectionNone; if (!hasSelection && paintSelectedTextOnly) return; RenderStyle* style = parentRenderer->style(); ASSERT(style); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); bool hasFill = svgStyle->hasFill(); bool hasStroke = svgStyle->hasStroke(); RenderStyle* selectionStyle = style; if (hasSelection) { selectionStyle = parentRenderer->getCachedPseudoStyle(SELECTION); if (selectionStyle) { const SVGRenderStyle* svgSelectionStyle = selectionStyle->svgStyle(); ASSERT(svgSelectionStyle); if (!hasFill) hasFill = svgSelectionStyle->hasFill(); if (!hasStroke) hasStroke = svgSelectionStyle->hasStroke(); } else selectionStyle = style; } unsigned textFragmentsSize = m_textFragments.size(); for (unsigned i = 0; i < textFragmentsSize; ++i) { SVGTextFragment& fragment = m_textFragments.at(i); ASSERT(!m_paintingResource); paintInfo.context->save(); if (!fragment.transform.isIdentity()) paintInfo.context->concatCTM(fragment.transform); // Spec: All text decorations except line-through should be drawn before the text is filled and stroked; thus, the text is rendered on top of these decorations. int decorations = style->textDecorationsInEffect(); if (decorations & UNDERLINE) paintDecoration(paintInfo.context, UNDERLINE, fragment); if (decorations & OVERLINE) paintDecoration(paintInfo.context, OVERLINE, fragment); // Fill text if (hasFill) { m_paintingResourceMode = ApplyToFillMode | ApplyToTextMode; paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly); } // Stroke text if (hasStroke) { m_paintingResourceMode = ApplyToStrokeMode | ApplyToTextMode; paintText(paintInfo.context, style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly); } // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text. if (decorations & LINE_THROUGH) paintDecoration(paintInfo.context, LINE_THROUGH, fragment); m_paintingResourceMode = ApplyToDefaultMode; paintInfo.context->restore(); } ASSERT(!m_paintingResource); }
void SVGInlineTextBoxPainter::paintTextFragments(const PaintInfo& paintInfo, LayoutObject& parentLayoutObject) { const ComputedStyle& style = parentLayoutObject.styleRef(); const SVGComputedStyle& svgStyle = style.svgStyle(); bool hasFill = svgStyle.hasFill(); bool hasVisibleStroke = svgStyle.hasVisibleStroke(); const ComputedStyle* selectionStyle = &style; bool shouldPaintSelection = this->shouldPaintSelection(); if (shouldPaintSelection) { selectionStyle = parentLayoutObject.getCachedPseudoStyle(SELECTION); if (selectionStyle) { const SVGComputedStyle& svgSelectionStyle = selectionStyle->svgStyle(); if (!hasFill) hasFill = svgSelectionStyle.hasFill(); if (!hasVisibleStroke) hasVisibleStroke = svgSelectionStyle.hasVisibleStroke(); } else { selectionStyle = &style; } } if (paintInfo.isRenderingClipPathAsMaskImage()) { hasFill = true; hasVisibleStroke = false; } AffineTransform fragmentTransform; unsigned textFragmentsSize = m_svgInlineTextBox.textFragments().size(); for (unsigned i = 0; i < textFragmentsSize; ++i) { SVGTextFragment& fragment = m_svgInlineTextBox.textFragments().at(i); GraphicsContextStateSaver stateSaver(*paintInfo.context, false); fragment.buildFragmentTransform(fragmentTransform); if (!fragmentTransform.isIdentity()) { stateSaver.save(); paintInfo.context->concatCTM(fragmentTransform); } // Spec: All text decorations except line-through should be drawn before the text is filled and stroked; thus, the text is rendered on top of these decorations. unsigned decorations = style.textDecorationsInEffect(); if (decorations & TextDecorationUnderline) paintDecoration(paintInfo, TextDecorationUnderline, fragment); if (decorations & TextDecorationOverline) paintDecoration(paintInfo, TextDecorationOverline, fragment); for (int i = 0; i < 3; i++) { switch (svgStyle.paintOrderType(i)) { case PT_FILL: if (hasFill) paintText(paintInfo, style, *selectionStyle, fragment, ApplyToFillMode, shouldPaintSelection); break; case PT_STROKE: if (hasVisibleStroke) paintText(paintInfo, style, *selectionStyle, fragment, ApplyToStrokeMode, shouldPaintSelection); break; case PT_MARKERS: // Markers don't apply to text break; default: ASSERT_NOT_REACHED(); break; } } // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text. if (decorations & TextDecorationLineThrough) paintDecoration(paintInfo, TextDecorationLineThrough, fragment); } }
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; } } } }
void SVGInlineTextBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit, LayoutUnit) { ASSERT(paintInfo.shouldPaintWithinRoot(renderer())); ASSERT(paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection); ASSERT(truncation() == cNoTruncation); if (renderer().style().visibility() != VISIBLE) return; // Note: We're explicitely not supporting composition & custom underlines and custom highlighters - unlike InlineTextBox. // If we ever need that for SVG, it's very easy to refactor and reuse the code. auto& parentRenderer = parent()->renderer(); bool paintSelectedTextOnly = paintInfo.phase == PaintPhaseSelection; bool hasSelection = !parentRenderer.document().printing() && selectionState() != RenderObject::SelectionNone; if (!hasSelection && paintSelectedTextOnly) return; if (!textShouldBePainted(renderer())) return; RenderStyle& style = parentRenderer.style(); const SVGRenderStyle& svgStyle = style.svgStyle(); bool hasFill = svgStyle.hasFill(); bool hasVisibleStroke = svgStyle.hasVisibleStroke(); RenderStyle* selectionStyle = &style; if (hasSelection) { selectionStyle = parentRenderer.getCachedPseudoStyle(SELECTION); if (selectionStyle) { const SVGRenderStyle& svgSelectionStyle = selectionStyle->svgStyle(); if (!hasFill) hasFill = svgSelectionStyle.hasFill(); if (!hasVisibleStroke) hasVisibleStroke = svgSelectionStyle.hasVisibleStroke(); } else selectionStyle = &style; } if (renderer().view().frameView().paintBehavior() & PaintBehaviorRenderingSVGMask) { hasFill = true; hasVisibleStroke = false; } AffineTransform fragmentTransform; unsigned textFragmentsSize = m_textFragments.size(); for (unsigned i = 0; i < textFragmentsSize; ++i) { SVGTextFragment& fragment = m_textFragments.at(i); ASSERT(!m_paintingResource); GraphicsContextStateSaver stateSaver(paintInfo.context()); fragment.buildFragmentTransform(fragmentTransform); if (!fragmentTransform.isIdentity()) paintInfo.context().concatCTM(fragmentTransform); // Spec: All text decorations except line-through should be drawn before the text is filled and stroked; thus, the text is rendered on top of these decorations. int decorations = style.textDecorationsInEffect(); if (decorations & TextDecorationUnderline) paintDecoration(paintInfo.context(), TextDecorationUnderline, fragment); if (decorations & TextDecorationOverline) paintDecoration(paintInfo.context(), TextDecorationOverline, fragment); auto paintOrder = style.svgStyle().paintTypesForPaintOrder(); for (unsigned i = 0; i < paintOrder.size(); ++i) { switch (paintOrder.at(i)) { case PaintTypeFill: if (!hasFill) continue; m_paintingResourceMode = ApplyToFillMode | ApplyToTextMode; paintText(paintInfo.context(), &style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly); break; case PaintTypeStroke: if (!hasVisibleStroke) continue; m_paintingResourceMode = ApplyToStrokeMode | ApplyToTextMode; paintText(paintInfo.context(), &style, selectionStyle, fragment, hasSelection, paintSelectedTextOnly); break; case PaintTypeMarkers: continue; } } // Spec: Line-through should be drawn after the text is filled and stroked; thus, the line-through is rendered on top of the text. if (decorations & TextDecorationLineThrough) paintDecoration(paintInfo.context(), TextDecorationLineThrough, fragment); m_paintingResourceMode = ApplyToDefaultMode; } // Finally, paint the outline if any. if (renderer().style().hasOutline() && is<RenderInline>(parentRenderer)) downcast<RenderInline>(parentRenderer).paintOutline(paintInfo, paintOffset); ASSERT(!m_paintingResource); }