FloatRect SVGInlineTextBox::selectionRectForTextFragment(const SVGTextFragment& fragment, int startPosition, int endPosition, RenderStyle* style) { ASSERT(startPosition < endPosition); ASSERT(style); FontCachePurgePreventer fontCachePurgePreventer; RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer()); ASSERT(textRenderer); float scalingFactor = textRenderer->scalingFactor(); ASSERT(scalingFactor); const Font& scaledFont = textRenderer->scaledFont(); const FontMetrics& scaledFontMetrics = scaledFont.fontMetrics(); FloatPoint textOrigin(fragment.x, fragment.y); if (scalingFactor != 1) textOrigin.scale(scalingFactor, scalingFactor); textOrigin.move(0, -scaledFontMetrics.floatAscent()); FloatRect selectionRect = scaledFont.selectionRectForText(constructTextRun(style, fragment), textOrigin, fragment.height * scalingFactor, startPosition, endPosition); if (scalingFactor == 1) return selectionRect; selectionRect.scale(1 / scalingFactor); return selectionRect; }
FloatRect SVGInlineTextBox::calculateBoundaries() const { FloatRect textRect; RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer()); ASSERT(textRenderer); float scalingFactor = textRenderer->scalingFactor(); ASSERT(scalingFactor); float baseline = textRenderer->scaledFont().fontMetrics().floatAscent() / scalingFactor; AffineTransform fragmentTransform; unsigned textFragmentsSize = m_textFragments.size(); for (unsigned i = 0; i < textFragmentsSize; ++i) { const SVGTextFragment& fragment = m_textFragments.at(i); FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height); fragment.buildFragmentTransform(fragmentTransform); if (!fragmentTransform.isIdentity()) fragmentRect = fragmentTransform.mapRect(fragmentRect); textRect.unite(fragmentRect); } return textRect; }
void SVGInlineTextBox::paintTextWithShadows(GraphicsContext* context, RenderStyle* style, TextRun& textRun, const SVGTextFragment& fragment, int startPosition, int endPosition) { RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer()); ASSERT(textRenderer); float scalingFactor = textRenderer->scalingFactor(); ASSERT(scalingFactor); const Font& scaledFont = textRenderer->scaledFont(); const ShadowData* shadow = style->textShadow(); FloatPoint textOrigin(fragment.x, fragment.y); FloatSize textSize(fragment.width, fragment.height); if (scalingFactor != 1) { textOrigin.scale(scalingFactor, scalingFactor); textSize.scale(scalingFactor); } FloatRect shadowRect(FloatPoint(textOrigin.x(), textOrigin.y() - scaledFont.fontMetrics().floatAscent()), textSize); do { if (!prepareGraphicsContextForTextPainting(context, scalingFactor, textRun, style)) break; FloatSize extraOffset; if (shadow) extraOffset = applyShadowToGraphicsContext(context, shadow, shadowRect, false /* stroked */, true /* opaque */, true /* horizontal */); AffineTransform originalTransform; if (scalingFactor != 1) { originalTransform = context->getCTM(); AffineTransform newTransform = originalTransform; newTransform.scale(1 / scalingFactor); normalizeTransform(newTransform); context->setCTM(newTransform); } scaledFont.drawText(context, textRun, textOrigin + extraOffset, startPosition, endPosition); if (scalingFactor != 1) context->setCTM(originalTransform); restoreGraphicsContextAfterTextPainting(context, textRun); if (!shadow) break; if (shadow->next()) context->restore(); else context->clearShadow(); shadow = shadow->next(); } while (shadow); }
static inline FloatRect calculateFragmentBoundaries(const RenderSVGInlineText& textRenderer, const SVGTextFragment& fragment) { float scalingFactor = textRenderer.scalingFactor(); ASSERT(scalingFactor); float baseline = textRenderer.scaledFont().fontMetrics().floatAscent() / scalingFactor; AffineTransform fragmentTransform; FloatRect fragmentRect(fragment.x, fragment.y - baseline, fragment.width, fragment.height); fragment.buildFragmentTransform(fragmentTransform); return fragmentTransform.mapRect(fragmentRect); }
void SVGInlineTextBox::paintTextWithShadows(GraphicsContext* context, RenderStyle* style, TextRun& textRun, const SVGTextFragment& fragment, int startPosition, int endPosition) { RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer()); ASSERT(textRenderer); float scalingFactor = textRenderer->scalingFactor(); ASSERT(scalingFactor); const Font& scaledFont = textRenderer->scaledFont(); const ShadowList* shadowList = style->textShadow(); // Text shadows are disabled when printing. http://crbug.com/258321 bool hasShadow = shadowList && !context->printing(); FloatPoint textOrigin(fragment.x, fragment.y); FloatSize textSize(fragment.width, fragment.height); if (scalingFactor != 1) { textOrigin.scale(scalingFactor, scalingFactor); textSize.scale(scalingFactor); context->save(); context->scale(FloatSize(1 / scalingFactor, 1 / scalingFactor)); } if (hasShadow) { DrawLooper drawLooper; for (size_t i = shadowList->shadows().size(); i--; ) { const ShadowData& shadow = shadowList->shadows()[i]; FloatSize offset(shadow.x(), shadow.y()); drawLooper.addShadow(offset, shadow.blur(), shadow.color(), DrawLooper::ShadowRespectsTransforms, DrawLooper::ShadowRespectsAlpha); } drawLooper.addUnmodifiedContent(); context->setDrawLooper(drawLooper); } if (prepareGraphicsContextForTextPainting(context, scalingFactor, textRun, style)) { TextRunPaintInfo textRunPaintInfo(textRun); textRunPaintInfo.from = startPosition; textRunPaintInfo.to = endPosition; textRunPaintInfo.bounds = FloatRect(textOrigin, textSize); scaledFont.drawText(context, textRunPaintInfo, textOrigin); restoreGraphicsContextAfterTextPainting(context, textRun); } if (scalingFactor != 1) context->restore(); else if (hasShadow) context->clearShadow(); }
int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragment, float position, bool includePartialGlyphs) const { RenderSVGInlineText* textRenderer = toRenderSVGInlineText(this->textRenderer()); ASSERT(textRenderer); float scalingFactor = textRenderer->scalingFactor(); ASSERT(scalingFactor); RenderStyle* style = textRenderer->style(); ASSERT(style); TextRun textRun = constructTextRun(style, fragment); // Eventually handle lengthAdjust="spacingAndGlyphs". // FIXME: Handle vertical text. AffineTransform fragmentTransform; fragment.buildFragmentTransform(fragmentTransform); if (!fragmentTransform.isIdentity()) textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragmentTransform.xScale())); return fragment.characterOffset - start() + textRenderer->scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs); }
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); }
static inline bool textShouldBePainted(const RenderSVGInlineText& textRenderer) { // Font::pixelSize(), returns FontDescription::computedPixelSize(), which returns "int(x + 0.5)". // If the absolute font size on screen is below x=0.5, don't render anything. return textRenderer.scaledFont().pixelSize(); }