void SVGTextLayoutAttributesBuilder::buildLayoutScopes(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter) { for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { if (child->isSVGInlineText()) { RenderSVGInlineText* text = toRenderSVGInlineText(child); if (!shouldPreserveAllWhiteSpace(text->style())) { const UChar* characters = text->characters(); unsigned textLength = text->textLength(); for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) { const UChar& currentCharacter = characters[textPosition]; if (characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) continue; lastCharacter = currentCharacter; ++atCharacter; } } else atCharacter += text->textLength(); continue; } if (!child->isSVGInline()) continue; unsigned textContentStart = atCharacter; buildLayoutScopes(child, atCharacter, lastCharacter); LayoutScope scope; buildLayoutScope(scope, child, textContentStart, atCharacter - textContentStart); m_scopes.append(scope); } }
static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textBox, int indent) { Vector<SVGTextFragment>& fragments = textBox->textFragments(); if (fragments.isEmpty()) return; RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer()); ASSERT(textRenderer); const SVGRenderStyle* svgStyle = textRenderer->style()->svgStyle(); String text = textBox->textRenderer()->text(); unsigned fragmentsSize = fragments.size(); for (unsigned i = 0; i < fragmentsSize; ++i) { SVGTextFragment& fragment = fragments.at(i); writeIndent(ts, indent + 1); unsigned startOffset = fragment.characterOffset; unsigned endOffset = fragment.characterOffset + fragment.length; // FIXME: Remove this hack, once the new text layout engine is completly landed. We want to preserve the old layout test results for now. ts << "chunk 1 "; ETextAnchor anchor = svgStyle->textAnchor(); bool isVerticalText = svgStyle->isVerticalWritingMode(); if (anchor == TA_MIDDLE) { ts << "(middle anchor"; if (isVerticalText) ts << ", vertical"; ts << ") "; } else if (anchor == TA_END) { ts << "(end anchor"; if (isVerticalText) ts << ", vertical"; ts << ") "; } else if (isVerticalText) ts << "(vertical) "; startOffset -= textBox->start(); endOffset -= textBox->start(); // </hack> ts << "text run " << i + 1 << " at (" << fragment.x << "," << fragment.y << ")"; ts << " startOffset " << startOffset << " endOffset " << endOffset; if (isVerticalText) ts << " height " << fragment.height; else ts << " width " << fragment.width; if (!textBox->isLeftToRightDirection() || textBox->dirOverride()) { ts << (textBox->isLeftToRightDirection() ? " LTR" : " RTL"); if (textBox->dirOverride()) ts << " override"; } ts << ": " << quoteAndEscapeNonPrintables(text.substring(fragment.characterOffset, fragment.length)) << "\n"; } }
void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter) const { for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { if (child->isSVGInlineText()) { RenderSVGInlineText* text = toRenderSVGInlineText(child); const UChar* characters = text->characters(); unsigned textLength = text->textLength(); bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style()); SVGTextLayoutAttributes attributes; attributes.reserveCapacity(textLength); unsigned valueListPosition = atCharacter; unsigned metricsLength = 1; for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) { const UChar& currentCharacter = characters[textPosition]; SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1); metricsLength = metrics.length(); if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) { assignEmptyLayoutAttributesForCharacter(attributes); attributes.textMetricsValues().append(SVGTextMetrics::emptyMetrics()); continue; } assignLayoutAttributesForCharacter(attributes, metrics, valueListPosition); if (metricsLength > 1) { for (unsigned i = 0; i < metricsLength - 1; ++i) assignEmptyLayoutAttributesForCharacter(attributes); } lastCharacter = currentCharacter; valueListPosition += metricsLength; } #if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0 fprintf(stderr, "\nDumping layout attributes for RenderSVGInlineText, renderer=%p, node=%p (atCharacter: %i)\n", text, text->node(), atCharacter); attributes.dump(); #endif text->storeLayoutAttributes(attributes); atCharacter = valueListPosition; continue; } if (!child->isSVGInline()) continue; propagateLayoutAttributes(child, atCharacter, lastCharacter); } }
static inline void processRenderSVGInlineText(const RenderSVGInlineText& text, unsigned& atCharacter, bool& lastCharacterWasSpace) { if (text.style().whiteSpace() == PRE) { atCharacter += text.textLength(); return; } for (unsigned textPosition = 0, textLength = text.textLength(); textPosition < textLength; ++textPosition) { const UChar currentCharacter = text[textPosition]; if (currentCharacter == ' ' && lastCharacterWasSpace) continue; lastCharacterWasSpace = currentCharacter == ' '; ++atCharacter; } }
void SVGTextMetricsCalculator::setupBidiRuns() { RenderStyle* style = m_text->style(); m_textDirection = style->direction(); if (isOverride(style->unicodeBidi())) return; BidiStatus status(LTR, false); status.last = status.lastStrong = WTF::Unicode::OtherNeutral; m_bidiResolver.setStatus(status); m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0)); const bool hardLineBreak = false; const bool reorderRuns = false; m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.length()), NoVisualOverride, hardLineBreak, reorderRuns); BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs(); m_bidiRun = bidiRuns.firstRun(); }
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 SVGTextLayoutEngine::layoutInlineTextBox(SVGInlineTextBox* textBox) { ASSERT(textBox); RenderSVGInlineText* text = toRenderSVGInlineText(textBox->textRenderer()); ASSERT(text); ASSERT(text->parent()); ASSERT(text->parent()->node()); ASSERT(text->parent()->node()->isSVGElement()); const RenderStyle* style = text->style(); ASSERT(style); textBox->clearTextFragments(); m_isVerticalText = style->svgStyle()->isVerticalWritingMode(); layoutTextOnLineOrPath(textBox, text, style); if (m_inPathLayout) { m_pathLayoutBoxes.append(textBox); return; } m_lineLayoutBoxes.append(textBox); }
void SVGTextChunkBuilder::addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxStart, unsigned boxCount) { SVGInlineTextBox* textBox = lineLayoutBoxes[boxStart]; ASSERT(textBox); RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer()); ASSERT(textRenderer); const RenderStyle* style = textRenderer->style(); ASSERT(style); const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); // Build chunk style flags. unsigned chunkStyle = SVGTextChunk::DefaultStyle; // Handle 'direction' property. if (!style->isLeftToRightDirection()) chunkStyle |= SVGTextChunk::RightToLeftText; // Handle 'writing-mode' property. if (svgStyle->isVerticalWritingMode()) chunkStyle |= SVGTextChunk::VerticalText; // Handle 'text-anchor' property. switch (svgStyle->textAnchor()) { case TA_START: break; case TA_MIDDLE: chunkStyle |= SVGTextChunk::MiddleAnchor; break; case TA_END: chunkStyle |= SVGTextChunk::EndAnchor; break; }; // Handle 'lengthAdjust' property. float desiredTextLength = 0; if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textRenderer->parent())) { desiredTextLength = textContentElement->specifiedTextLength().value(textContentElement); switch (textContentElement->lengthAdjust()) { case SVGTextContentElement::LENGTHADJUST_UNKNOWN: break; case SVGTextContentElement::LENGTHADJUST_SPACING: chunkStyle |= SVGTextChunk::LengthAdjustSpacing; break; case SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS: chunkStyle |= SVGTextChunk::LengthAdjustSpacingAndGlyphs; break; }; } SVGTextChunk chunk(chunkStyle, desiredTextLength); Vector<SVGInlineTextBox*>& boxes = chunk.boxes(); for (unsigned i = boxStart; i < boxStart + boxCount; ++i) boxes.append(lineLayoutBoxes[i]); m_textChunks.append(chunk); }
void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, Vector<SVGTextLayoutAttributes>& allAttributes, unsigned& atCharacter, UChar& lastCharacter) const { for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { if (child->isSVGInlineText()) { RenderSVGInlineText* text = toRenderSVGInlineText(child); const UChar* characters = text->characters(); unsigned textLength = text->textLength(); bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style()); SVGTextLayoutAttributes attributes(text); attributes.reserveCapacity(textLength); unsigned valueListPosition = atCharacter; unsigned metricsLength = 1; SVGTextMetrics lastMetrics(SVGTextMetrics::SkippedSpaceMetrics); for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) { const UChar& currentCharacter = characters[textPosition]; SVGTextMetrics startToCurrentMetrics; SVGTextMetrics currentMetrics; unsigned valueListAdvance = 0; if (U16_IS_LEAD(currentCharacter) && (textPosition + 1) < textLength && U16_IS_TRAIL(characters[textPosition + 1])) { // Handle surrogate pairs. startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 2); currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 2); metricsLength = currentMetrics.length(); valueListAdvance = 1; } else { // Handle BMP characters. startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 1); currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1); metricsLength = currentMetrics.length(); valueListAdvance = metricsLength; } if (!metricsLength) break; // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping. // So whenever runWidthAdvance != currentMetrics.width(), we are processing a text run whose length is // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated. float runWidthAdvance = startToCurrentMetrics.width() - lastMetrics.width(); if (runWidthAdvance != currentMetrics.width()) currentMetrics.setWidth(runWidthAdvance); lastMetrics = startToCurrentMetrics; if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) { attributes.positioningLists().appendEmptyValues(); attributes.textMetricsValues().append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics)); continue; } SVGTextLayoutAttributes::PositioningLists& positioningLists = attributes.positioningLists(); positioningLists.appendValuesFromPosition(m_positioningLists, valueListPosition); attributes.textMetricsValues().append(currentMetrics); // Pad x/y/dx/dy/rotate value lists with empty values, if the metrics span more than one character. if (metricsLength > 1) { for (unsigned i = 0; i < metricsLength - 1; ++i) positioningLists.appendEmptyValues(); } lastCharacter = currentCharacter; valueListPosition += valueListAdvance; } #if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0 fprintf(stderr, "\nDumping layout attributes for RenderSVGInlineText, renderer=%p, node=%p (atCharacter: %i)\n", text, text->node(), atCharacter); fprintf(stderr, "BiDi properties: unicode-bidi=%i, block direction=%i\n", text->style()->unicodeBidi(), text->style()->direction()); attributes.dump(); #endif text->storeLayoutAttributes(attributes); allAttributes.append(attributes); atCharacter = valueListPosition; continue; } if (!child->isSVGInline()) continue; propagateLayoutAttributes(child, allAttributes, atCharacter, lastCharacter); } }