diff options
Diffstat (limited to 'Source/WebCore/rendering/svg')
22 files changed, 433 insertions, 156 deletions
diff --git a/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp b/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp index 835bce8..9f3d8a5 100644 --- a/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp @@ -34,6 +34,7 @@ #include "RenderSVGInline.cpp" #include "RenderSVGInlineText.cpp" #include "RenderSVGModelObject.cpp" +#include "RenderSVGPath.cpp" #include "RenderSVGResource.cpp" #include "RenderSVGResourceClipper.cpp" #include "RenderSVGResourceContainer.cpp" diff --git a/Source/WebCore/rendering/svg/RenderSVGText.cpp b/Source/WebCore/rendering/svg/RenderSVGText.cpp index 56d9306..8ca3d58 100644 --- a/Source/WebCore/rendering/svg/RenderSVGText.cpp +++ b/Source/WebCore/rendering/svg/RenderSVGText.cpp @@ -51,6 +51,7 @@ namespace WebCore { RenderSVGText::RenderSVGText(SVGTextElement* node) : RenderSVGBlock(node) + , m_needsReordering(false) , m_needsPositioningValuesUpdate(true) , m_needsTransformUpdate(true) { @@ -127,6 +128,7 @@ void RenderSVGText::layout() // Perform SVG text layout phase one (see SVGTextLayoutAttributesBuilder for details). SVGTextLayoutAttributesBuilder layoutAttributesBuilder; layoutAttributesBuilder.buildLayoutAttributesForTextSubtree(this); + m_needsReordering = true; m_needsPositioningValuesUpdate = false; updateCachedBoundariesInParents = true; } @@ -134,7 +136,7 @@ void RenderSVGText::layout() // Reduced version of RenderBlock::layoutBlock(), which only takes care of SVG text. // All if branches that could cause early exit in RenderBlocks layoutBlock() method are turned into assertions. ASSERT(!isInline()); - ASSERT(!layoutOnlyPositionedObjects()); + ASSERT(!simplifiedLayout()); ASSERT(!scrollsOverflow()); ASSERT(!hasControlClip()); ASSERT(!hasColumns()); @@ -150,6 +152,9 @@ void RenderSVGText::layout() ASSERT(childrenInline()); forceLayoutInlineChildren(); + if (m_needsReordering) + m_needsReordering = false; + if (!updateCachedBoundariesInParents) updateCachedBoundariesInParents = oldBoundaries != objectBoundingBox(); diff --git a/Source/WebCore/rendering/svg/RenderSVGText.h b/Source/WebCore/rendering/svg/RenderSVGText.h index 188d5fc..93fc5f8 100644 --- a/Source/WebCore/rendering/svg/RenderSVGText.h +++ b/Source/WebCore/rendering/svg/RenderSVGText.h @@ -26,6 +26,7 @@ #include "AffineTransform.h" #include "RenderSVGBlock.h" +#include "SVGTextLayoutAttributes.h" namespace WebCore { @@ -33,7 +34,7 @@ class SVGTextElement; class RenderSVGText : public RenderSVGBlock { public: - RenderSVGText(SVGTextElement* node); + RenderSVGText(SVGTextElement*); void setNeedsPositioningValuesUpdate() { m_needsPositioningValuesUpdate = true; } virtual void setNeedsTransformUpdate() { m_needsTransformUpdate = true; } @@ -42,6 +43,9 @@ public: static RenderSVGText* locateRenderSVGTextAncestor(RenderObject*); static const RenderSVGText* locateRenderSVGTextAncestor(const RenderObject*); + Vector<SVGTextLayoutAttributes>& layoutAttributes() { return m_layoutAttributes; } + bool needsReordering() const { return m_needsReordering; } + private: virtual const char* renderName() const { return "RenderSVGText"; } virtual bool isSVGText() const { return true; } @@ -71,9 +75,11 @@ private: virtual RenderBlock* firstLineBlock() const; virtual void updateFirstLetter(); + bool m_needsReordering : 1; bool m_needsPositioningValuesUpdate : 1; bool m_needsTransformUpdate : 1; AffineTransform m_localTransform; + Vector<SVGTextLayoutAttributes> m_layoutAttributes; }; inline RenderSVGText* toRenderSVGText(RenderObject* object) diff --git a/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp b/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp index 05e1357..17bbfaa 100644 --- a/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp +++ b/Source/WebCore/rendering/svg/SVGInlineTextBox.cpp @@ -73,7 +73,7 @@ int SVGInlineTextBox::offsetForPositionInFragment(const SVGTextFragment& fragmen if (!fragment.transform.isIdentity()) textRun.setHorizontalGlyphStretch(narrowPrecisionToFloat(fragment.transform.xScale())); - return fragment.positionListOffset - start() + textRenderer->scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs); + return fragment.characterOffset - start() + textRenderer->scaledFont().offsetForPosition(textRun, position * scalingFactor, includePartialGlyphs); } float SVGInlineTextBox::positionForOffset(int) const @@ -402,7 +402,7 @@ TextRun SVGInlineTextBox::constructTextRun(RenderStyle* style, const SVGTextFrag RenderText* text = textRenderer(); ASSERT(text); - TextRun run(text->characters() + fragment.positionListOffset + TextRun run(text->characters() + fragment.characterOffset , fragment.length , false /* allowTabs */ , 0 /* xPos, only relevant with allowTabs=true */ @@ -425,7 +425,7 @@ bool SVGInlineTextBox::mapStartEndPositionsIntoFragmentCoordinates(const SVGText if (startPosition >= endPosition) return false; - int offset = static_cast<int>(fragment.positionListOffset) - start(); + int offset = static_cast<int>(fragment.characterOffset) - start(); int length = static_cast<int>(fragment.length); if (startPosition >= offset + length || endPosition <= offset) diff --git a/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp b/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp index 0f94fdd..a099f87 100644 --- a/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp +++ b/Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp @@ -464,8 +464,8 @@ static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textB SVGTextFragment& fragment = fragments.at(i); writeIndent(ts, indent + 1); - unsigned startOffset = fragment.positionListOffset; - unsigned endOffset = fragment.positionListOffset + fragment.length; + 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 "; @@ -500,7 +500,7 @@ static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textB ts << " override"; } - ts << ": " << quoteAndEscapeNonPrintables(text.substring(fragment.positionListOffset, fragment.length)) << "\n"; + ts << ": " << quoteAndEscapeNonPrintables(text.substring(fragment.characterOffset, fragment.length)) << "\n"; } } diff --git a/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp b/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp index c2289d6..ddbd3ea 100644 --- a/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp +++ b/Source/WebCore/rendering/svg/SVGRootInlineBox.cpp @@ -25,8 +25,8 @@ #if ENABLE(SVG) #include "GraphicsContext.h" -#include "RenderBlock.h" #include "RenderSVGInlineText.h" +#include "RenderSVGText.h" #include "SVGInlineFlowBox.h" #include "SVGInlineTextBox.h" #include "SVGNames.h" @@ -73,8 +73,15 @@ void SVGRootInlineBox::paint(PaintInfo& paintInfo, int, int) void SVGRootInlineBox::computePerCharacterLayoutInformation() { + RenderSVGText* parentBlock = toRenderSVGText(block()); + ASSERT(parentBlock); + + Vector<SVGTextLayoutAttributes>& attributes = parentBlock->layoutAttributes(); + if (parentBlock->needsReordering()) + reorderValueLists(attributes); + // Perform SVG text layout phase two (see SVGTextLayoutEngine for details). - SVGTextLayoutEngine characterLayout; + SVGTextLayoutEngine characterLayout(attributes); layoutCharactersInTextBoxes(this, characterLayout); // Perform SVG text layout phase three (see SVGTextChunkBuilder for details). @@ -108,8 +115,12 @@ void SVGRootInlineBox::layoutCharactersInTextBoxes(InlineFlowBox* start, SVGText if (isTextPath) { // Build text chunks for all <textPath> children, using the line layout algorithm. // This is needeed as text-anchor is just an additional startOffset for text paths. - SVGTextLayoutEngine lineLayout; + RenderSVGText* parentBlock = toRenderSVGText(block()); + ASSERT(parentBlock); + + SVGTextLayoutEngine lineLayout(parentBlock->layoutAttributes()); layoutCharactersInTextBoxes(flowBox, lineLayout); + characterLayout.beginTextPathLayout(child->renderer(), lineLayout); } @@ -215,6 +226,91 @@ InlineBox* SVGRootInlineBox::closestLeafChildForPosition(const IntPoint& point) return closestLeaf ? closestLeaf : lastLeaf; } + +static inline void swapItemsInVector(Vector<float>& firstVector, Vector<float>& lastVector, unsigned first, unsigned last) +{ + float temp = firstVector.at(first); + firstVector.at(first) = lastVector.at(last); + lastVector.at(last) = temp; +} + +static inline void swapItemsInLayoutAttributes(SVGTextLayoutAttributes& firstAttributes, SVGTextLayoutAttributes& lastAttributes, unsigned firstPosition, unsigned lastPosition) +{ + swapItemsInVector(firstAttributes.xValues(), lastAttributes.xValues(), firstPosition, lastPosition); + swapItemsInVector(firstAttributes.yValues(), lastAttributes.yValues(), firstPosition, lastPosition); + swapItemsInVector(firstAttributes.dxValues(), lastAttributes.dxValues(), firstPosition, lastPosition); + swapItemsInVector(firstAttributes.dyValues(), lastAttributes.dyValues(), firstPosition, lastPosition); + swapItemsInVector(firstAttributes.rotateValues(), lastAttributes.rotateValues(), firstPosition, lastPosition); +} + +static inline void findFirstAndLastAttributesInVector(Vector<SVGTextLayoutAttributes>& attributes, RenderSVGInlineText* firstContext, RenderSVGInlineText* lastContext, + SVGTextLayoutAttributes*& first, SVGTextLayoutAttributes*& last) +{ + first = 0; + last = 0; + + unsigned attributesSize = attributes.size(); + for (unsigned i = 0; i < attributesSize; ++i) { + SVGTextLayoutAttributes& current = attributes.at(i); + if (!first && firstContext == current.context()) + first = ¤t; + if (!last && lastContext == current.context()) + last = ¤t; + if (first && last) + break; + } + + ASSERT(first); + ASSERT(last); +} + +static inline void reverseInlineBoxRangeAndValueListsIfNeeded(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last) +{ + ASSERT(userData); + Vector<SVGTextLayoutAttributes>& attributes = *reinterpret_cast<Vector<SVGTextLayoutAttributes>*>(userData); + + // This is a copy of std::reverse(first, last). It additionally assure that the value lists within the InlineBoxes are reordered as well. + while (true) { + if (first == last || first == --last) + return; + + ASSERT((*first)->isSVGInlineTextBox()); + ASSERT((*last)->isSVGInlineTextBox()); + + SVGInlineTextBox* firstTextBox = static_cast<SVGInlineTextBox*>(*first); + SVGInlineTextBox* lastTextBox = static_cast<SVGInlineTextBox*>(*last); + + // Reordering is only necessary for BiDi text that is _absolutely_ positioned. + if (firstTextBox->len() == 1 && firstTextBox->len() == lastTextBox->len()) { + RenderSVGInlineText* firstContext = toRenderSVGInlineText(firstTextBox->textRenderer()); + RenderSVGInlineText* lastContext = toRenderSVGInlineText(lastTextBox->textRenderer()); + + SVGTextLayoutAttributes* firstAttributes = 0; + SVGTextLayoutAttributes* lastAttributes = 0; + findFirstAndLastAttributesInVector(attributes, firstContext, lastContext, firstAttributes, lastAttributes); + + unsigned firstBoxPosition = firstTextBox->start(); + unsigned firstBoxEnd = firstTextBox->end(); + + unsigned lastBoxPosition = lastTextBox->start(); + unsigned lastBoxEnd = lastTextBox->end(); + for (; firstBoxPosition <= firstBoxEnd && lastBoxPosition <= lastBoxEnd; ++lastBoxPosition, ++firstBoxPosition) + swapItemsInLayoutAttributes(*firstAttributes, *lastAttributes, firstBoxPosition, lastBoxPosition); + } + + InlineBox* temp = *first; + *first = *last; + *last = temp; + + ++first; + } +} + +void SVGRootInlineBox::reorderValueLists(Vector<SVGTextLayoutAttributes>& attributes) +{ + Vector<InlineBox*> leafBoxesInLogicalOrder; + collectLeafBoxesInLogicalOrder(leafBoxesInLogicalOrder, reverseInlineBoxRangeAndValueListsIfNeeded, &attributes); +} } // namespace WebCore diff --git a/Source/WebCore/rendering/svg/SVGRootInlineBox.h b/Source/WebCore/rendering/svg/SVGRootInlineBox.h index 2e073c9..39612e7 100644 --- a/Source/WebCore/rendering/svg/SVGRootInlineBox.h +++ b/Source/WebCore/rendering/svg/SVGRootInlineBox.h @@ -55,6 +55,7 @@ public: InlineBox* closestLeafChildForPosition(const IntPoint&); private: + void reorderValueLists(Vector<SVGTextLayoutAttributes>&); void layoutCharactersInTextBoxes(InlineFlowBox*, SVGTextLayoutEngine&); void layoutChildBoxes(InlineFlowBox*); void layoutRootBox(); diff --git a/Source/WebCore/rendering/svg/SVGShadowTreeElements.cpp b/Source/WebCore/rendering/svg/SVGShadowTreeElements.cpp index 2d84c48..9ccdef0 100644 --- a/Source/WebCore/rendering/svg/SVGShadowTreeElements.cpp +++ b/Source/WebCore/rendering/svg/SVGShadowTreeElements.cpp @@ -47,6 +47,10 @@ FloatSize SVGShadowTreeContainerElement::containerTranslation() const return FloatSize(m_xOffset.value(this), m_yOffset.value(this)); } +PassRefPtr<Element> SVGShadowTreeContainerElement::cloneElementWithoutAttributesAndChildren() const +{ + return adoptRef(new SVGShadowTreeContainerElement(document())); +} // SVGShadowTreeRootElement inline SVGShadowTreeRootElement::SVGShadowTreeRootElement(Document* document, SVGUseElement* shadowParent) diff --git a/Source/WebCore/rendering/svg/SVGShadowTreeElements.h b/Source/WebCore/rendering/svg/SVGShadowTreeElements.h index 3406f95..2952e35 100644 --- a/Source/WebCore/rendering/svg/SVGShadowTreeElements.h +++ b/Source/WebCore/rendering/svg/SVGShadowTreeElements.h @@ -44,6 +44,7 @@ protected: SVGShadowTreeContainerElement(Document*); private: + virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() const; virtual bool isShadowTreeContainerElement() const { return true; } SVGLength m_xOffset; diff --git a/Source/WebCore/rendering/svg/SVGTextChunk.cpp b/Source/WebCore/rendering/svg/SVGTextChunk.cpp index 5dea6ad..49407e5 100644 --- a/Source/WebCore/rendering/svg/SVGTextChunk.cpp +++ b/Source/WebCore/rendering/svg/SVGTextChunk.cpp @@ -27,10 +27,8 @@ namespace WebCore { -SVGTextChunk::SVGTextChunk(bool isVerticalText, ETextAnchor textAnchor, SVGTextContentElement::SVGLengthAdjustType lengthAdjust, float desiredTextLength) - : m_isVerticalText(isVerticalText) - , m_textAnchor(textAnchor) - , m_lengthAdjust(lengthAdjust) +SVGTextChunk::SVGTextChunk(unsigned chunkStyle, float desiredTextLength) + : m_chunkStyle(chunkStyle) , m_desiredTextLength(desiredTextLength) { } @@ -52,7 +50,7 @@ void SVGTextChunk::calculateLength(float& length, unsigned& characters) const SVGTextFragment& fragment = fragments.at(i); characters += fragment.length; - if (m_isVerticalText) + if (m_chunkStyle & VerticalText) length += fragment.height; else length += fragment.width; @@ -63,7 +61,7 @@ void SVGTextChunk::calculateLength(float& length, unsigned& characters) const } // Resepect gap between chunks. - if (m_isVerticalText) + if (m_chunkStyle & VerticalText) length += fragment.y - (lastFragment->y + lastFragment->height); else length += fragment.x - (lastFragment->x + lastFragment->width); @@ -75,17 +73,11 @@ void SVGTextChunk::calculateLength(float& length, unsigned& characters) const float SVGTextChunk::calculateTextAnchorShift(float length) const { - switch (m_textAnchor) { - case TA_START: - return 0; - case TA_MIDDLE: + if (m_chunkStyle & MiddleAnchor) return -length / 2; - case TA_END: - return -length; - }; - - ASSERT_NOT_REACHED(); - return 0; + if (m_chunkStyle & EndAnchor) + return m_chunkStyle & RightToLeftText ? 0 : -length; + return m_chunkStyle & RightToLeftText ? -length : 0; } } // namespace WebCore diff --git a/Source/WebCore/rendering/svg/SVGTextChunk.h b/Source/WebCore/rendering/svg/SVGTextChunk.h index ebe6d81..9618d9f 100644 --- a/Source/WebCore/rendering/svg/SVGTextChunk.h +++ b/Source/WebCore/rendering/svg/SVGTextChunk.h @@ -31,34 +31,37 @@ class SVGInlineTextBox; // A SVGTextChunk describes a range of SVGTextFragments, see the SVG spec definition of a "text chunk". class SVGTextChunk { public: - SVGTextChunk(bool isVerticalText, ETextAnchor, SVGTextContentElement::SVGLengthAdjustType, float desiredTextLength); + enum ChunkStyle { + DefaultStyle = 1 << 0, + MiddleAnchor = 1 << 1, + EndAnchor = 1 << 2, + RightToLeftText = 1 << 3, + VerticalText = 1 << 4, + LengthAdjustSpacing = 1 << 5, + LengthAdjustSpacingAndGlyphs = 1 << 6 + }; + + SVGTextChunk(unsigned chunkStyle, float desiredTextLength); void calculateLength(float& length, unsigned& characters) const; float calculateTextAnchorShift(float length) const; - bool isVerticalText() const { return m_isVerticalText; } - ETextAnchor textAnchor() const { return m_textAnchor; } - SVGTextContentElement::SVGLengthAdjustType lengthAdjust() const { return m_lengthAdjust; } + bool isVerticalText() const { return m_chunkStyle & VerticalText; } float desiredTextLength() const { return m_desiredTextLength; } Vector<SVGInlineTextBox*>& boxes() { return m_boxes; } const Vector<SVGInlineTextBox*>& boxes() const { return m_boxes; } - bool hasDesiredTextLength() const { return m_lengthAdjust != SVGTextContentElement::LENGTHADJUST_UNKNOWN && m_desiredTextLength > 0; } - bool hasTextAnchor() const { return m_textAnchor != TA_START; } + bool hasDesiredTextLength() const { return m_desiredTextLength > 0 && ((m_chunkStyle & LengthAdjustSpacing) || (m_chunkStyle & LengthAdjustSpacingAndGlyphs)); } + bool hasTextAnchor() const { return m_chunkStyle & RightToLeftText ? !(m_chunkStyle & EndAnchor) : (m_chunkStyle & MiddleAnchor) || (m_chunkStyle & EndAnchor); } + bool hasLengthAdjustSpacing() const { return m_chunkStyle & LengthAdjustSpacing; } + bool hasLengthAdjustSpacingAndGlyphs() const { return m_chunkStyle & LengthAdjustSpacingAndGlyphs; } private: // Contains all SVGInlineTextBoxes this chunk spans. Vector<SVGInlineTextBox*> m_boxes; - // writing-mode specific property. - bool m_isVerticalText; - - // text-anchor specific properties. - ETextAnchor m_textAnchor; - - // textLength/lengthAdjust specific properties. - SVGTextContentElement::SVGLengthAdjustType m_lengthAdjust; + unsigned m_chunkStyle; float m_desiredTextLength; }; diff --git a/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp b/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp index bbbae6c..47311bd 100644 --- a/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp +++ b/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp @@ -101,15 +101,47 @@ void SVGTextChunkBuilder::addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxe const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); - SVGTextContentElement::SVGLengthAdjustType lengthAdjust = SVGTextContentElement::LENGTHADJUST_UNKNOWN; + // 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())) { - lengthAdjust = static_cast<SVGTextContentElement::SVGLengthAdjustType>(textContentElement->lengthAdjust()); desiredTextLength = textContentElement->textLength().value(textContentElement); + + switch (static_cast<SVGTextContentElement::SVGLengthAdjustType>(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(svgStyle->isVerticalWritingMode(), svgStyle->textAnchor(), lengthAdjust, desiredTextLength); + SVGTextChunk chunk(chunkStyle, desiredTextLength); Vector<SVGInlineTextBox*>& boxes = chunk.boxes(); for (unsigned i = boxStart; i < boxStart + boxCount; ++i) @@ -137,7 +169,7 @@ void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk) bool isVerticalText = chunk.isVerticalText(); if (processTextLength) { - if (chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACING) { + if (chunk.hasLengthAdjustSpacing()) { float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters; unsigned atCharacter = 0; for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { @@ -147,7 +179,7 @@ void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk) processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter); } } else { - ASSERT(chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS); + ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs()); float scale = chunk.desiredTextLength() / chunkLength; AffineTransform spacingAndGlyphsTransform; @@ -172,7 +204,7 @@ void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk) return; // If we previously applied a lengthAdjust="spacing" correction, we have to recalculate the chunk length, to be able to apply the text-anchor shift. - if (processTextLength && chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACING) { + if (processTextLength && chunk.hasLengthAdjustSpacing()) { chunkLength = 0; chunkCharacters = 0; chunk.calculateLength(chunkLength, chunkCharacters); diff --git a/Source/WebCore/rendering/svg/SVGTextFragment.h b/Source/WebCore/rendering/svg/SVGTextFragment.h index 2e520da..b5b3c57 100644 --- a/Source/WebCore/rendering/svg/SVGTextFragment.h +++ b/Source/WebCore/rendering/svg/SVGTextFragment.h @@ -28,7 +28,8 @@ namespace WebCore { // A SVGTextFragment describes a text fragment of a RenderSVGInlineText which can be rendered at once. struct SVGTextFragment { SVGTextFragment() - : positionListOffset(0) + : characterOffset(0) + , metricsListOffset(0) , length(0) , x(0) , y(0) @@ -37,8 +38,9 @@ struct SVGTextFragment { { } - // The first rendered character starts at RenderSVGInlineText::characters() + positionListOffset. - unsigned positionListOffset; + // The first rendered character starts at RenderSVGInlineText::characters() + characterOffset. + unsigned characterOffset; + unsigned metricsListOffset; unsigned length; float x; diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp index 3037b77..4c227b4 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp +++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp @@ -27,7 +27,8 @@ namespace WebCore { -SVGTextLayoutAttributes::SVGTextLayoutAttributes() +SVGTextLayoutAttributes::SVGTextLayoutAttributes(RenderSVGInlineText* context) + : m_context(context) { } @@ -65,6 +66,8 @@ static inline void dumpLayoutVector(const Vector<float>& values) void SVGTextLayoutAttributes::dump() const { + fprintf(stderr, "context: %p\n", m_context); + fprintf(stderr, "x values: "); dumpLayoutVector(m_xValues); fprintf(stderr, "\n"); diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h index d08d5b7..fc6bd10 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h +++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h @@ -27,15 +27,19 @@ namespace WebCore { +class RenderSVGInlineText; + class SVGTextLayoutAttributes { public: - SVGTextLayoutAttributes(); + SVGTextLayoutAttributes(RenderSVGInlineText* context = 0); void reserveCapacity(unsigned length); void dump() const; static float emptyValue(); + RenderSVGInlineText* context() const { return m_context; } + Vector<float>& xValues() { return m_xValues; } const Vector<float>& xValues() const { return m_xValues; } @@ -55,6 +59,7 @@ public: const Vector<SVGTextMetrics>& textMetricsValues() const { return m_textMetricsValues; } private: + RenderSVGInlineText* m_context; Vector<float> m_xValues; Vector<float> m_yValues; Vector<float> m_dxValues; diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp index 3122912..86be424 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp +++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp @@ -52,9 +52,11 @@ void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextSubtree(RenderS buildOutermostLayoutScope(textRoot, atCharacter); // Propagate layout attributes to each RenderSVGInlineText object. + Vector<SVGTextLayoutAttributes>& allAttributes = textRoot->layoutAttributes(); + allAttributes.clear(); atCharacter = 0; lastCharacter = '\0'; - propagateLayoutAttributes(textRoot, atCharacter, lastCharacter); + propagateLayoutAttributes(textRoot, allAttributes, atCharacter, lastCharacter); } static inline void extractFloatValuesFromSVGLengthList(SVGElement* lengthContext, const SVGLengthList& list, Vector<float>& floatValues, unsigned textContentLength) @@ -186,7 +188,7 @@ void SVGTextLayoutAttributesBuilder::buildOutermostLayoutScope(RenderSVGText* te m_scopes.prepend(scope); } -void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter) const +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()) { @@ -195,16 +197,29 @@ void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* sta unsigned textLength = text->textLength(); bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style()); - SVGTextLayoutAttributes attributes; + SVGTextLayoutAttributes attributes(text); attributes.reserveCapacity(textLength); unsigned valueListPosition = atCharacter; unsigned metricsLength = 1; + SVGTextMetrics lastMetrics = SVGTextMetrics::emptyMetrics(); + for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) { const UChar& currentCharacter = characters[textPosition]; - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1); - metricsLength = metrics.length(); + SVGTextMetrics startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 1); + SVGTextMetrics currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1); + + // 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; + metricsLength = currentMetrics.length(); if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) { assignEmptyLayoutAttributesForCharacter(attributes); @@ -212,7 +227,7 @@ void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* sta continue; } - assignLayoutAttributesForCharacter(attributes, metrics, valueListPosition); + assignLayoutAttributesForCharacter(attributes, currentMetrics, valueListPosition); if (metricsLength > 1) { for (unsigned i = 0; i < metricsLength - 1; ++i) @@ -225,10 +240,12 @@ void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* sta #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; } @@ -236,7 +253,7 @@ void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* sta if (!child->isSVGInline()) continue; - propagateLayoutAttributes(child, atCharacter, lastCharacter); + propagateLayoutAttributes(child, allAttributes, atCharacter, lastCharacter); } } diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h index c68185b..b368c51 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h +++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h @@ -60,7 +60,7 @@ private: void buildLayoutScope(LayoutScope&, RenderObject*, unsigned textContentStart, unsigned textContentLength) const; void buildLayoutScopes(RenderObject*, unsigned& atCharacter, UChar& lastCharacter); void buildOutermostLayoutScope(RenderSVGText*, unsigned textLength); - void propagateLayoutAttributes(RenderObject*, unsigned& atCharacter, UChar& lastCharacter) const; + void propagateLayoutAttributes(RenderObject*, Vector<SVGTextLayoutAttributes>& allAttributes, unsigned& atCharacter, UChar& lastCharacter) const; enum LayoutValueType { XValueAttribute, diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp index c25ed79..71db2ea 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp +++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp @@ -34,8 +34,13 @@ namespace WebCore { -SVGTextLayoutEngine::SVGTextLayoutEngine() - : m_x(0) +SVGTextLayoutEngine::SVGTextLayoutEngine(Vector<SVGTextLayoutAttributes>& layoutAttributes) + : m_layoutAttributes(layoutAttributes) + , m_logicalCharacterOffset(0) + , m_logicalMetricsListOffset(0) + , m_visualCharacterOffset(0) + , m_visualMetricsListOffset(0) + , m_x(0) , m_y(0) , m_dx(0) , m_dy(0) @@ -46,6 +51,7 @@ SVGTextLayoutEngine::SVGTextLayoutEngine() , m_textPathSpacing(0) , m_textPathScaling(1) { + ASSERT(!m_layoutAttributes.isEmpty()); } void SVGTextLayoutEngine::updateCharacerPositionIfNeeded(float& x, float& y) @@ -77,7 +83,7 @@ void SVGTextLayoutEngine::updateCurrentTextPosition(float x, float y, float glyp } } -void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues, unsigned positionListOffset) +void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues) { // Update relative positioning information. if (dxValues.isEmpty() && dyValues.isEmpty()) @@ -85,14 +91,14 @@ void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(Vector<float float dx = 0; if (!dxValues.isEmpty()) { - float& dxCurrent = dxValues.at(positionListOffset); + float& dxCurrent = dxValues.at(m_logicalCharacterOffset); if (dxCurrent != SVGTextLayoutAttributes::emptyValue()) dx = dxCurrent; } float dy = 0; if (!dyValues.isEmpty()) { - float& dyCurrent = dyValues.at(positionListOffset); + float& dyCurrent = dyValues.at(m_logicalCharacterOffset); if (dyCurrent != SVGTextLayoutAttributes::emptyValue()) dy = dyCurrent; } @@ -113,23 +119,31 @@ void SVGTextLayoutEngine::updateRelativePositionAdjustmentsIfNeeded(Vector<float m_dy = dy; } -void SVGTextLayoutEngine::recordTextFragment(SVGInlineTextBox* textBox, RenderSVGInlineText* text, unsigned positionListOffset, const SVGTextMetrics& lastCharacterMetrics) +void SVGTextLayoutEngine::recordTextFragment(SVGInlineTextBox* textBox, Vector<SVGTextMetrics>& textMetricsValues) { ASSERT(!m_currentTextFragment.length); + ASSERT(m_visualMetricsListOffset > 0); // Figure out length of fragment. - m_currentTextFragment.length = positionListOffset - m_currentTextFragment.positionListOffset; + m_currentTextFragment.length = m_visualCharacterOffset - m_currentTextFragment.characterOffset; // Figure out fragment metrics. - if (m_currentTextFragment.length == 1) { - // Fast path, can rely on already computed per-character metrics. - m_currentTextFragment.width = lastCharacterMetrics.width(); - m_currentTextFragment.height = lastCharacterMetrics.height(); - } else { - // Need to measure the whole range (range metrics != sum of character metrics) - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(text, m_currentTextFragment.positionListOffset, m_currentTextFragment.length); - m_currentTextFragment.width = metrics.width(); - m_currentTextFragment.height = metrics.height(); + SVGTextMetrics& lastCharacterMetrics = textMetricsValues.at(m_visualMetricsListOffset - 1); + m_currentTextFragment.width = lastCharacterMetrics.width(); + m_currentTextFragment.height = lastCharacterMetrics.height(); + + if (m_currentTextFragment.length > 1) { + // SVGTextLayoutAttributesBuilder assures that the length of the range is equal to the sum of the individual lengths of the glyphs. + float length = 0; + if (m_isVerticalText) { + for (unsigned i = m_currentTextFragment.metricsListOffset; i < m_visualMetricsListOffset; ++i) + length += textMetricsValues.at(i).height(); + m_currentTextFragment.height = length; + } else { + for (unsigned i = m_currentTextFragment.metricsListOffset; i < m_visualMetricsListOffset; ++i) + length += textMetricsValues.at(i).width(); + m_currentTextFragment.width = length; + } } textBox->textFragments().append(m_currentTextFragment); @@ -281,7 +295,7 @@ void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& b SVGInlineTextBox* textBox = boxes.at(boxPosition); Vector<SVGTextFragment>& fragments = textBox->textFragments(); fprintf(stderr, "-> Box %i: Dumping text fragments for SVGInlineTextBox, textBox=%p, textRenderer=%p\n", boxPosition, textBox, textBox->textRenderer()); - fprintf(stderr, " textBox properties, start=%i, len=%i\n", textBox->start(), textBox->len()); + fprintf(stderr, " textBox properties, start=%i, len=%i, box direction=%i\n", textBox->start(), textBox->len(), textBox->direction()); fprintf(stderr, " textRenderer properties, textLength=%i\n", textBox->textRenderer()->textLength()); const UChar* characters = textBox->textRenderer()->characters(); @@ -289,9 +303,9 @@ void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& b unsigned fragmentCount = fragments.size(); for (unsigned i = 0; i < fragmentCount; ++i) { SVGTextFragment& fragment = fragments.at(i); - String fragmentString(characters + fragment.positionListOffset, fragment.length); - fprintf(stderr, " -> Fragment %i, x=%lf, y=%lf, width=%lf, height=%lf, positionListOffset=%i, length=%i, characters='%s'\n" - , i, fragment.x, fragment.y, fragment.width, fragment.height, fragment.positionListOffset, fragment.length, fragmentString.utf8().data()); + String fragmentString(characters + fragment.characterOffset, fragment.length); + fprintf(stderr, " -> Fragment %i, x=%lf, y=%lf, width=%lf, height=%lf, characterOffset=%i, length=%i, characters='%s'\n" + , i, fragment.x, fragment.y, fragment.width, fragment.height, fragment.characterOffset, fragment.length, fragmentString.utf8().data()); } } #endif @@ -328,6 +342,97 @@ void SVGTextLayoutEngine::finalizeTransformMatrices(Vector<SVGInlineTextBox*>& b boxes.clear(); } +bool SVGTextLayoutEngine::currentLogicalCharacterAttributes(SVGTextLayoutAttributes& logicalAttributes) +{ + logicalAttributes = m_layoutAttributes.first(); + if (m_logicalCharacterOffset != logicalAttributes.xValues().size()) + return true; + + m_layoutAttributes.remove(0); + if (m_layoutAttributes.isEmpty()) + return false; + + logicalAttributes = m_layoutAttributes.first(); + m_logicalMetricsListOffset = 0; + m_logicalCharacterOffset = 0; + return true; +} + +bool SVGTextLayoutEngine::currentLogicalCharacterMetrics(SVGTextLayoutAttributes& logicalAttributes, SVGTextMetrics& logicalMetrics) +{ + logicalMetrics = SVGTextMetrics::emptyMetrics(); + + Vector<SVGTextMetrics>& textMetricsValues = logicalAttributes.textMetricsValues(); + unsigned textMetricsSize = textMetricsValues.size(); + while (true) { + if (m_logicalMetricsListOffset == textMetricsSize) { + if (!currentLogicalCharacterAttributes(logicalAttributes)) + return false; + + textMetricsValues = logicalAttributes.textMetricsValues(); + textMetricsSize = textMetricsValues.size(); + continue; + } + + logicalMetrics = textMetricsValues.at(m_logicalMetricsListOffset); + if (logicalMetrics == SVGTextMetrics::emptyMetrics() || (!logicalMetrics.width() && !logicalMetrics.height())) { + advanceToNextLogicalCharacter(logicalMetrics); + continue; + } + + // Stop if we found the next valid logical text metrics object. + return true; + } + + ASSERT_NOT_REACHED(); + return true; +} + +bool SVGTextLayoutEngine::currentVisualCharacterMetrics(SVGInlineTextBox* textBox, RenderSVGInlineText* text, SVGTextMetrics& metrics) +{ + SVGTextLayoutAttributes& attributes = text->layoutAttributes(); + Vector<SVGTextMetrics>& textMetricsValues = attributes.textMetricsValues(); + ASSERT(!textMetricsValues.isEmpty()); + + unsigned textMetricsSize = textMetricsValues.size(); + unsigned boxStart = textBox->start(); + unsigned boxLength = textBox->len(); + + if (m_visualMetricsListOffset == textMetricsSize) + return false; + + while (m_visualMetricsListOffset < textMetricsSize) { + SVGTextMetrics& visualMetrics = textMetricsValues.at(m_visualMetricsListOffset); + + // Advance to text box start location. + if (m_visualCharacterOffset < boxStart) { + advanceToNextVisualCharacter(visualMetrics); + continue; + } + + // Stop if we've finished processing this text box. + if (m_visualCharacterOffset >= boxStart + boxLength) + return false; + + metrics = visualMetrics; + return true; + } + + return false; +} + +void SVGTextLayoutEngine::advanceToNextLogicalCharacter(const SVGTextMetrics& logicalMetrics) +{ + ++m_logicalMetricsListOffset; + m_logicalCharacterOffset += logicalMetrics.length(); +} + +void SVGTextLayoutEngine::advanceToNextVisualCharacter(const SVGTextMetrics& visualMetrics) +{ + ++m_visualMetricsListOffset; + m_visualCharacterOffset += visualMetrics.length(); +} + void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, RenderSVGInlineText* text, const RenderStyle* style) { SVGElement* lengthContext = static_cast<SVGElement*>(text->parent()->node()); @@ -338,26 +443,10 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend const SVGRenderStyle* svgStyle = style->svgStyle(); ASSERT(svgStyle); - SVGTextLayoutAttributes& attributes = text->layoutAttributes(); - Vector<float>& xValues = attributes.xValues(); - Vector<float>& yValues = attributes.yValues(); - Vector<float>& dxValues = attributes.dxValues(); - Vector<float>& dyValues = attributes.dyValues(); - Vector<float>& rotateValues = attributes.rotateValues(); - Vector<SVGTextMetrics>& textMetricsValues = attributes.textMetricsValues(); - - unsigned boxStart = textBox->start(); - unsigned boxLength = textBox->len(); - unsigned textMetricsSize = textMetricsValues.size(); - ASSERT(textMetricsSize <= xValues.size()); - ASSERT(textMetricsSize <= yValues.size()); - ASSERT(xValues.size() == yValues.size()); + m_visualMetricsListOffset = 0; + m_visualCharacterOffset = 0; - if (boxLength > textMetricsSize) - textMetricsSize = boxLength; - - unsigned positionListOffset = 0; - unsigned metricsListOffset = 0; + Vector<SVGTextMetrics>& textMetricsValues = text->layoutAttributes().textMetricsValues(); const UChar* characters = text->characters(); const Font& font = style->font(); @@ -372,56 +461,63 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend baselineShift -= baselineLayout.calculateAlignmentBaselineShift(m_isVerticalText, text); // Main layout algorithm. - unsigned positionListSize = xValues.size(); - for (; metricsListOffset < textMetricsSize && positionListOffset < positionListSize; ++metricsListOffset) { - SVGTextMetrics& metrics = textMetricsValues.at(metricsListOffset); - // Advance to text box start location. - if (positionListOffset < boxStart) { - positionListOffset += metrics.length(); + while (true) { + // Find the start of the current text box in this list, respecting ligatures. + SVGTextMetrics visualMetrics = SVGTextMetrics::emptyMetrics(); + if (!currentVisualCharacterMetrics(textBox, text, visualMetrics)) + break; + + if (visualMetrics == SVGTextMetrics::emptyMetrics()) { + advanceToNextVisualCharacter(visualMetrics); continue; } - // Stop if we've finished processing this text box. - if (positionListOffset >= boxStart + boxLength) + SVGTextLayoutAttributes logicalAttributes; + if (!currentLogicalCharacterAttributes(logicalAttributes)) break; - - float x = xValues.at(positionListOffset); - float y = yValues.at(positionListOffset); - // When we've advanced to the box start offset, determine using the original x/y values, - // wheter this character starts a new text chunk, before doing any further processing. - if (positionListOffset == boxStart) - textBox->setStartsNewTextChunk(text->characterStartsNewTextChunk(boxStart)); + SVGTextMetrics logicalMetrics = SVGTextMetrics::emptyMetrics(); + if (!currentLogicalCharacterMetrics(logicalAttributes, logicalMetrics)) + break; - if (metrics == SVGTextMetrics::emptyMetrics()) { - positionListOffset += metrics.length(); - continue; - } + Vector<float>& xValues = logicalAttributes.xValues(); + Vector<float>& yValues = logicalAttributes.yValues(); + Vector<float>& dxValues = logicalAttributes.dxValues(); + Vector<float>& dyValues = logicalAttributes.dyValues(); + Vector<float>& rotateValues = logicalAttributes.rotateValues(); + + float x = xValues.at(m_logicalCharacterOffset); + float y = yValues.at(m_logicalCharacterOffset); + + // When we've advanced to the box start offset, determine using the original x/y values, + // whether this character starts a new text chunk, before doing any further processing. + if (m_visualCharacterOffset == textBox->start()) + textBox->setStartsNewTextChunk(logicalAttributes.context()->characterStartsNewTextChunk(m_logicalCharacterOffset)); - const UChar* currentCharacter = characters + positionListOffset; float angle = 0; if (!rotateValues.isEmpty()) { - float newAngle = rotateValues.at(positionListOffset); + float newAngle = rotateValues.at(m_logicalCharacterOffset); if (newAngle != SVGTextLayoutAttributes::emptyValue()) angle = newAngle; } // Calculate glyph orientation angle. + const UChar* currentCharacter = characters + m_visualCharacterOffset; float orientationAngle = baselineLayout.calculateGlyphOrientationAngle(m_isVerticalText, svgStyle, *currentCharacter); // Calculate glyph advance & x/y orientation shifts. float xOrientationShift = 0; float yOrientationShift = 0; - float glyphAdvance = baselineLayout.calculateGlyphAdvanceAndOrientation(m_isVerticalText, metrics, orientationAngle, xOrientationShift, yOrientationShift); + float glyphAdvance = baselineLayout.calculateGlyphAdvanceAndOrientation(m_isVerticalText, visualMetrics, orientationAngle, xOrientationShift, yOrientationShift); // Assign current text position to x/y values, if needed. updateCharacerPositionIfNeeded(x, y); // Apply dx/dy value adjustments to current text position, if needed. - updateRelativePositionAdjustmentsIfNeeded(dxValues, dyValues, positionListOffset); + updateRelativePositionAdjustmentsIfNeeded(dxValues, dyValues); // Calculate SVG Fonts kerning, if needed. - float kerning = spacingLayout.calculateSVGKerning(m_isVerticalText, metrics.glyph()); + float kerning = spacingLayout.calculateSVGKerning(m_isVerticalText, visualMetrics.glyph()); // Calculate CSS 'kerning', 'letter-spacing' and 'word-spacing' for next character, if needed. float spacing = spacingLayout.calculateCSSKerningAndSpacing(svgStyle, lengthContext, currentCharacter); @@ -461,7 +557,8 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend // Skip character, if we're before the path. if (textPathOffset < 0) { - positionListOffset += metrics.length(); + advanceToNextLogicalCharacter(logicalMetrics); + advanceToNextVisualCharacter(visualMetrics); continue; } @@ -513,16 +610,17 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend // If we already started a fragment, close it now. if (didStartTextFragment && shouldStartNewFragment) { applySpacingToNextCharacter = false; - recordTextFragment(textBox, text, positionListOffset, textMetricsValues.at(metricsListOffset - 1)); + recordTextFragment(textBox, textMetricsValues); } // Eventually start a new fragment, if not yet done. if (!didStartTextFragment || shouldStartNewFragment) { - ASSERT(!m_currentTextFragment.positionListOffset); + ASSERT(!m_currentTextFragment.characterOffset); ASSERT(!m_currentTextFragment.length); didStartTextFragment = true; - m_currentTextFragment.positionListOffset = positionListOffset; + m_currentTextFragment.characterOffset = m_visualCharacterOffset; + m_currentTextFragment.metricsListOffset = m_visualMetricsListOffset; m_currentTextFragment.x = x; m_currentTextFragment.y = y; @@ -563,7 +661,8 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend updateCurrentTextPosition(xNew, yNew, glyphAdvance + spacing); } - positionListOffset += metrics.length(); + advanceToNextLogicalCharacter(logicalMetrics); + advanceToNextVisualCharacter(visualMetrics); lastAngle = angle; } @@ -571,7 +670,7 @@ void SVGTextLayoutEngine::layoutTextOnLineOrPath(SVGInlineTextBox* textBox, Rend return; // Close last open fragment, if needed. - recordTextFragment(textBox, text, positionListOffset, textMetricsValues.at(metricsListOffset - 1)); + recordTextFragment(textBox, textMetricsValues); } } diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.h b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.h index 631e4cd..22dd59b 100644 --- a/Source/WebCore/rendering/svg/SVGTextLayoutEngine.h +++ b/Source/WebCore/rendering/svg/SVGTextLayoutEngine.h @@ -24,6 +24,7 @@ #include "Path.h" #include "SVGTextChunkBuilder.h" #include "SVGTextFragment.h" +#include "SVGTextLayoutAttributes.h" #include "SVGTextMetrics.h" #include <wtf/Vector.h> @@ -47,7 +48,7 @@ class SVGRenderStyle; class SVGTextLayoutEngine { WTF_MAKE_NONCOPYABLE(SVGTextLayoutEngine); public: - SVGTextLayoutEngine(); + SVGTextLayoutEngine(Vector<SVGTextLayoutAttributes>&); SVGTextChunkBuilder& chunkLayoutBuilder() { return m_chunkLayoutBuilder; } void beginTextPathLayout(RenderObject*, SVGTextLayoutEngine& lineLayout); @@ -59,20 +60,32 @@ public: private: void updateCharacerPositionIfNeeded(float& x, float& y); void updateCurrentTextPosition(float x, float y, float glyphAdvance); - void updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues, unsigned valueListPosition); + void updateRelativePositionAdjustmentsIfNeeded(Vector<float>& dxValues, Vector<float>& dyValues); - void recordTextFragment(SVGInlineTextBox*, RenderSVGInlineText*, unsigned positionListOffset, const SVGTextMetrics& lastCharacterMetrics); + void recordTextFragment(SVGInlineTextBox*, Vector<SVGTextMetrics>& textMetricValues); bool parentDefinesTextLength(RenderObject*) const; void layoutTextOnLineOrPath(SVGInlineTextBox*, RenderSVGInlineText*, const RenderStyle*); void finalizeTransformMatrices(Vector<SVGInlineTextBox*>&); + bool currentLogicalCharacterAttributes(SVGTextLayoutAttributes&); + bool currentLogicalCharacterMetrics(SVGTextLayoutAttributes&, SVGTextMetrics&); + bool currentVisualCharacterMetrics(SVGInlineTextBox*, RenderSVGInlineText*, SVGTextMetrics&); + + void advanceToNextLogicalCharacter(const SVGTextMetrics&); + void advanceToNextVisualCharacter(const SVGTextMetrics&); + private: + Vector<SVGTextLayoutAttributes> m_layoutAttributes; Vector<SVGInlineTextBox*> m_lineLayoutBoxes; Vector<SVGInlineTextBox*> m_pathLayoutBoxes; SVGTextChunkBuilder m_chunkLayoutBuilder; SVGTextFragment m_currentTextFragment; + unsigned m_logicalCharacterOffset; + unsigned m_logicalMetricsListOffset; + unsigned m_visualCharacterOffset; + unsigned m_visualMetricsListOffset; float m_x; float m_y; float m_dx; diff --git a/Source/WebCore/rendering/svg/SVGTextMetrics.cpp b/Source/WebCore/rendering/svg/SVGTextMetrics.cpp index d75bdb3..042bc22 100644 --- a/Source/WebCore/rendering/svg/SVGTextMetrics.cpp +++ b/Source/WebCore/rendering/svg/SVGTextMetrics.cpp @@ -74,7 +74,17 @@ SVGTextMetrics SVGTextMetrics::emptyMetrics() static TextRun constructTextRun(RenderSVGInlineText* text, const UChar* characters, unsigned position, unsigned length) { - TextRun run(characters + position, length); + RenderStyle* style = text->style(); + ASSERT(style); + + TextRun run(characters + position + , length + , false /* allowTabs */ + , 0 /* xPos, only relevant with allowTabs=true */ + , 0 /* padding, only relevant for justified text, not relevant for SVG */ + , TextRun::AllowTrailingExpansion + , !style->isLeftToRightDirection() + , style->unicodeBidi() == Override /* directionalOverride */); #if ENABLE(SVG_FONTS) run.setReferencingRenderObject(text); @@ -92,22 +102,6 @@ SVGTextMetrics SVGTextMetrics::measureCharacterRange(RenderSVGInlineText* text, return SVGTextMetrics(text, run, position, text->textLength()); } -void SVGTextMetrics::measureAllCharactersIndividually(RenderSVGInlineText* text, Vector<SVGTextMetrics>& allMetrics) -{ - ASSERT(text); - const UChar* characters = text->characters(); - unsigned length = text->textLength(); - - TextRun run(constructTextRun(text, 0, 0, 0)); - for (unsigned position = 0; position < length; ) { - run.setText(characters + position, 1); - - SVGTextMetrics metrics(text, run, position, text->textLength()); - allMetrics.append(metrics); - position += metrics.length(); - } -} - } #endif // ENABLE(SVG) diff --git a/Source/WebCore/rendering/svg/SVGTextMetrics.h b/Source/WebCore/rendering/svg/SVGTextMetrics.h index 7ef0f7d..cb016b6 100644 --- a/Source/WebCore/rendering/svg/SVGTextMetrics.h +++ b/Source/WebCore/rendering/svg/SVGTextMetrics.h @@ -32,7 +32,6 @@ class SVGTextMetrics { public: static SVGTextMetrics emptyMetrics(); static SVGTextMetrics measureCharacterRange(RenderSVGInlineText*, unsigned position, unsigned length); - static void measureAllCharactersIndividually(RenderSVGInlineText*, Vector<SVGTextMetrics>&); bool operator==(const SVGTextMetrics&); @@ -62,6 +61,10 @@ public: const Glyph& glyph() const { return m_glyph; } private: + friend class SVGTextLayoutAttributesBuilder; + void setWidth(float width) { m_width = width; } + +private: SVGTextMetrics(); SVGTextMetrics(RenderSVGInlineText*, const TextRun&, unsigned position, unsigned textLength); diff --git a/Source/WebCore/rendering/svg/SVGTextQuery.cpp b/Source/WebCore/rendering/svg/SVGTextQuery.cpp index 1a4cdab..5f3523e 100644 --- a/Source/WebCore/rendering/svg/SVGTextQuery.cpp +++ b/Source/WebCore/rendering/svg/SVGTextQuery.cpp @@ -313,7 +313,7 @@ bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGTextFragmen if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) return false; - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset + startPosition, endPosition - startPosition); + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset + startPosition, endPosition - startPosition); float fragmentLength = queryData->isVerticalText ? metrics.height() : metrics.width(); data->subStringLength += mapLengthThroughFragmentTransformation(fragment, queryData->isVerticalText, fragmentLength); @@ -353,7 +353,7 @@ bool SVGTextQuery::startPositionOfCharacterCallback(Data* queryData, const SVGTe data->startPosition = FloatPoint(fragment.x, fragment.y); if (startPosition) { - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition); + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition); if (queryData->isVerticalText) data->startPosition.move(0, metrics.height()); else @@ -399,7 +399,7 @@ bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGText data->endPosition = FloatPoint(fragment.x, fragment.y); - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition + 1); + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition + 1); if (queryData->isVerticalText) data->endPosition.move(0, metrics.height()); else @@ -478,14 +478,14 @@ static inline void calculateGlyphBoundaries(SVGTextQuery::Data* queryData, const extent.setLocation(FloatPoint(fragment.x, fragment.y - queryData->textRenderer->scaledFont().fontMetrics().floatAscent() / scalingFactor)); if (startPosition) { - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset, startPosition); + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition); if (queryData->isVerticalText) extent.move(0, metrics.height()); else extent.move(metrics.width(), 0); } - SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.positionListOffset + startPosition, 1); + SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset + startPosition, 1); extent.setSize(FloatSize(metrics.width(), metrics.height())); if (fragment.transform.isIdentity()) |