summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/rendering/svg
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/svg')
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGAllInOne.cpp1
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGText.cpp7
-rw-r--r--Source/WebCore/rendering/svg/RenderSVGText.h8
-rw-r--r--Source/WebCore/rendering/svg/SVGInlineTextBox.cpp6
-rw-r--r--Source/WebCore/rendering/svg/SVGRenderTreeAsText.cpp6
-rw-r--r--Source/WebCore/rendering/svg/SVGRootInlineBox.cpp102
-rw-r--r--Source/WebCore/rendering/svg/SVGRootInlineBox.h1
-rw-r--r--Source/WebCore/rendering/svg/SVGShadowTreeElements.cpp4
-rw-r--r--Source/WebCore/rendering/svg/SVGShadowTreeElements.h1
-rw-r--r--Source/WebCore/rendering/svg/SVGTextChunk.cpp24
-rw-r--r--Source/WebCore/rendering/svg/SVGTextChunk.h31
-rw-r--r--Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp46
-rw-r--r--Source/WebCore/rendering/svg/SVGTextFragment.h8
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutAttributes.cpp5
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutAttributes.h7
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp31
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.h2
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp237
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutEngine.h19
-rw-r--r--Source/WebCore/rendering/svg/SVGTextMetrics.cpp28
-rw-r--r--Source/WebCore/rendering/svg/SVGTextMetrics.h5
-rw-r--r--Source/WebCore/rendering/svg/SVGTextQuery.cpp10
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 = &current;
+ if (!last && lastContext == current.context())
+ last = &current;
+ 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())