Index: Source/core/layout/svg/SVGTextChunkBuilder.cpp |
diff --git a/Source/core/layout/svg/SVGTextChunkBuilder.cpp b/Source/core/layout/svg/SVGTextChunkBuilder.cpp |
index 2e1109d3d39d867567e803c65efda1a7f0993e55..84e2dc5f37c48b90447383654ce3962d281fb7c9 100644 |
--- a/Source/core/layout/svg/SVGTextChunkBuilder.cpp |
+++ b/Source/core/layout/svg/SVGTextChunkBuilder.cpp |
@@ -28,57 +28,12 @@ |
namespace blink { |
-SVGTextChunkBuilder::SVGTextChunkBuilder() |
-{ |
-} |
- |
-void SVGTextChunkBuilder::processTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes) |
-{ |
- if (lineLayoutBoxes.isEmpty()) |
- return; |
- |
- bool foundStart = false; |
- unsigned lastChunkStartPosition = 0; |
- unsigned boxPosition = 0; |
- unsigned boxCount = lineLayoutBoxes.size(); |
- for (; boxPosition < boxCount; ++boxPosition) { |
- SVGInlineTextBox* textBox = lineLayoutBoxes[boxPosition]; |
- if (!textBox->startsNewTextChunk()) |
- continue; |
- |
- if (!foundStart) { |
- foundStart = true; |
- } else { |
- ASSERT(boxPosition > lastChunkStartPosition); |
- handleTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition); |
- } |
- lastChunkStartPosition = boxPosition; |
- } |
- |
- if (!foundStart) |
- return; |
- |
- if (boxPosition - lastChunkStartPosition > 0) |
- handleTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition); |
-} |
+namespace { |
-SVGTextPathChunkBuilder::SVGTextPathChunkBuilder() |
- : SVGTextChunkBuilder() |
- , m_totalLength(0) |
- , m_totalCharacters(0) |
- , m_totalTextAnchorShift(0) |
+SVGTextChunk createTextChunk(const SVGInlineTextBox& textBox) |
{ |
-} |
- |
-static SVGTextChunk createTextChunk(const Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxStart, unsigned boxCount) |
-{ |
- SVGInlineTextBox* textBox = lineLayoutBoxes[boxStart]; |
- ASSERT(textBox); |
- |
- LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText(textBox->layoutObject()); |
- |
- const ComputedStyle& style = toLayoutSVGInlineText(textBox->layoutObject()).styleRef(); |
- |
+ LayoutSVGInlineText& textLayoutObject = toLayoutSVGInlineText(textBox.layoutObject()); |
+ const ComputedStyle& style = textLayoutObject.styleRef(); |
const SVGComputedStyle& svgStyle = style.svgStyle(); |
// Build chunk style flags. |
@@ -102,7 +57,7 @@ static SVGTextChunk createTextChunk(const Vector<SVGInlineTextBox*>& lineLayoutB |
case TA_END: |
chunkStyle |= SVGTextChunk::EndAnchor; |
break; |
- }; |
+ } |
// Handle 'lengthAdjust' property. |
float desiredTextLength = 0; |
@@ -122,31 +77,122 @@ static SVGTextChunk createTextChunk(const Vector<SVGInlineTextBox*>& lineLayoutB |
case SVGLengthAdjustSpacingAndGlyphs: |
chunkStyle |= SVGTextChunk::LengthAdjustSpacingAndGlyphs; |
break; |
- }; |
+ } |
+ } |
+ |
+ return SVGTextChunk(chunkStyle, desiredTextLength); |
+} |
+ |
+class ChunkLengthAccumulator { |
+public: |
+ ChunkLengthAccumulator(bool isVertical) |
+ : m_numCharacters(0) |
+ , m_length(0) |
+ , m_isVertical(isVertical) |
+ { |
+ } |
+ |
+ typedef Vector<SVGInlineTextBox*>::const_iterator BoxListConstIterator; |
+ |
+ void processRange(BoxListConstIterator boxStart, BoxListConstIterator boxEnd); |
+ void reset() |
+ { |
+ m_numCharacters = 0; |
+ m_length = 0; |
+ } |
+ |
+ float length() const { return m_length; } |
+ unsigned numCharacters() const { return m_numCharacters; } |
+ |
+private: |
+ unsigned m_numCharacters; |
+ float m_length; |
+ const bool m_isVertical; |
+}; |
+ |
+void ChunkLengthAccumulator::processRange(BoxListConstIterator boxStart, BoxListConstIterator boxEnd) |
+{ |
+ SVGTextFragment* lastFragment = nullptr; |
+ for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
+ for (SVGTextFragment& fragment : (*boxIter)->textFragments()) { |
+ m_numCharacters += fragment.length; |
+ |
+ if (m_isVertical) |
+ m_length += fragment.height; |
+ else |
+ m_length += fragment.width; |
+ |
+ if (!lastFragment) { |
+ lastFragment = &fragment; |
+ continue; |
+ } |
+ |
+ // Respect gap between chunks. |
+ if (m_isVertical) |
+ m_length += fragment.y - (lastFragment->y + lastFragment->height); |
+ else |
+ m_length += fragment.x - (lastFragment->x + lastFragment->width); |
+ |
+ lastFragment = &fragment; |
+ } |
+ } |
+} |
+ |
+} |
+ |
+SVGTextChunkBuilder::SVGTextChunkBuilder() |
+{ |
+} |
+ |
+void SVGTextChunkBuilder::processTextChunks(const Vector<SVGInlineTextBox*>& lineLayoutBoxes) |
+{ |
+ if (lineLayoutBoxes.isEmpty()) |
+ return; |
+ |
+ bool foundStart = false; |
+ auto boxIter = lineLayoutBoxes.begin(); |
+ auto endBox = lineLayoutBoxes.end(); |
+ auto chunkStartBox = boxIter; |
+ for (; boxIter != endBox; ++boxIter) { |
+ if (!(*boxIter)->startsNewTextChunk()) |
+ continue; |
+ |
+ if (!foundStart) { |
+ foundStart = true; |
+ } else { |
+ ASSERT(boxIter != chunkStartBox); |
+ handleTextChunk(chunkStartBox, boxIter); |
+ } |
+ chunkStartBox = boxIter; |
} |
- SVGTextChunk chunk(chunkStyle, desiredTextLength); |
+ if (!foundStart) |
+ return; |
- Vector<SVGInlineTextBox*>& boxes = chunk.boxes(); |
- for (unsigned i = boxStart; i < boxStart + boxCount; ++i) |
- boxes.append(lineLayoutBoxes[i]); |
+ if (boxIter != chunkStartBox) |
+ handleTextChunk(chunkStartBox, boxIter); |
+} |
- return chunk; |
+SVGTextPathChunkBuilder::SVGTextPathChunkBuilder() |
+ : SVGTextChunkBuilder() |
+ , m_totalLength(0) |
+ , m_totalCharacters(0) |
+ , m_totalTextAnchorShift(0) |
+{ |
} |
-void SVGTextPathChunkBuilder::handleTextChunk(const Vector<SVGInlineTextBox*>& boxes, unsigned boxStart, unsigned boxEnd) |
+void SVGTextPathChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, BoxListConstIterator boxEnd) |
{ |
- SVGTextChunk chunk = createTextChunk(boxes, boxStart, boxEnd - boxStart); |
+ SVGTextChunk chunk = createTextChunk(**boxStart); |
- float length = 0; |
- unsigned characters = 0; |
- chunk.calculateLength(length, characters); |
+ ChunkLengthAccumulator lengthAccumulator(chunk.isVerticalText()); |
+ lengthAccumulator.processRange(boxStart, boxEnd); |
// Handle text-anchor as additional start offset for text paths. |
- m_totalTextAnchorShift += chunk.calculateTextAnchorShift(length); |
+ m_totalTextAnchorShift += chunk.calculateTextAnchorShift(lengthAccumulator.length()); |
- m_totalLength += length; |
- m_totalCharacters += characters; |
+ m_totalLength += lengthAccumulator.length(); |
+ m_totalCharacters += lengthAccumulator.numCharacters(); |
} |
static void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform) |
@@ -161,44 +207,47 @@ static void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, con |
spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y); |
} |
-void SVGTextChunkBuilder::handleTextChunk(const Vector<SVGInlineTextBox*>& lineBoxes, unsigned boxStart, unsigned boxEnd) |
+void SVGTextChunkBuilder::handleTextChunk(BoxListConstIterator boxStart, BoxListConstIterator boxEnd) |
{ |
- SVGTextChunk chunk = createTextChunk(lineBoxes, boxStart, boxEnd - boxStart); |
+ SVGTextChunk chunk = createTextChunk(**boxStart); |
bool processTextLength = chunk.hasDesiredTextLength(); |
bool processTextAnchor = chunk.hasTextAnchor(); |
if (!processTextAnchor && !processTextLength) |
return; |
- const Vector<SVGInlineTextBox*>& boxes = chunk.boxes(); |
- unsigned boxCount = boxes.size(); |
- if (!boxCount) |
- return; |
+ bool isVerticalText = chunk.isVerticalText(); |
// Calculate absolute length of whole text chunk (starting from text box 'start', spanning 'length' text boxes). |
- float chunkLength = 0; |
- unsigned chunkCharacters = 0; |
- chunk.calculateLength(chunkLength, chunkCharacters); |
+ ChunkLengthAccumulator lengthAccumulator(isVerticalText); |
+ lengthAccumulator.processRange(boxStart, boxEnd); |
- bool isVerticalText = chunk.isVerticalText(); |
if (processTextLength) { |
+ float chunkLength = lengthAccumulator.length(); |
if (chunk.hasLengthAdjustSpacing()) { |
- float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters; |
+ float textLengthShift = (chunk.desiredTextLength() - chunkLength) / lengthAccumulator.numCharacters(); |
unsigned atCharacter = 0; |
- for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { |
- Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments(); |
+ for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
+ Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); |
if (fragments.isEmpty()) |
continue; |
processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter); |
} |
+ |
+ // Fragments have been adjusted, we have to recalculate the chunk |
+ // length, to be able to apply the text-anchor shift. |
+ if (processTextAnchor) { |
+ lengthAccumulator.reset(); |
+ lengthAccumulator.processRange(boxStart, boxEnd); |
+ } |
} else { |
ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs()); |
float textLengthScale = chunk.desiredTextLength() / chunkLength; |
AffineTransform spacingAndGlyphsTransform; |
bool foundFirstFragment = false; |
- for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { |
- SVGInlineTextBox* textBox = boxes[boxPosition]; |
+ for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
+ SVGInlineTextBox* textBox = *boxIter; |
Vector<SVGTextFragment>& fragments = textBox->textFragments(); |
if (fragments.isEmpty()) |
continue; |
@@ -216,16 +265,9 @@ void SVGTextChunkBuilder::handleTextChunk(const Vector<SVGInlineTextBox*>& lineB |
if (!processTextAnchor) |
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.hasLengthAdjustSpacing()) { |
- chunkLength = 0; |
- chunkCharacters = 0; |
- chunk.calculateLength(chunkLength, chunkCharacters); |
- } |
- |
- float textAnchorShift = chunk.calculateTextAnchorShift(chunkLength); |
- for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) { |
- Vector<SVGTextFragment>& fragments = boxes[boxPosition]->textFragments(); |
+ float textAnchorShift = chunk.calculateTextAnchorShift(lengthAccumulator.length()); |
+ for (auto boxIter = boxStart; boxIter != boxEnd; ++boxIter) { |
+ Vector<SVGTextFragment>& fragments = (*boxIter)->textFragments(); |
if (fragments.isEmpty()) |
continue; |
processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments); |