summaryrefslogtreecommitdiffstats
path: root/WebCore/rendering/svg/SVGTextChunkBuilder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/rendering/svg/SVGTextChunkBuilder.cpp')
-rw-r--r--WebCore/rendering/svg/SVGTextChunkBuilder.cpp232
1 files changed, 232 insertions, 0 deletions
diff --git a/WebCore/rendering/svg/SVGTextChunkBuilder.cpp b/WebCore/rendering/svg/SVGTextChunkBuilder.cpp
new file mode 100644
index 0000000..bbbae6c
--- /dev/null
+++ b/WebCore/rendering/svg/SVGTextChunkBuilder.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#if ENABLE(SVG)
+#include "SVGTextChunkBuilder.h"
+
+#include "RenderSVGInlineText.h"
+#include "SVGElement.h"
+#include "SVGInlineTextBox.h"
+
+namespace WebCore {
+
+SVGTextChunkBuilder::SVGTextChunkBuilder()
+{
+}
+
+void SVGTextChunkBuilder::transformationForTextBox(SVGInlineTextBox* textBox, AffineTransform& transform) const
+{
+ DEFINE_STATIC_LOCAL(const AffineTransform, s_identityTransform, ());
+ if (!m_textBoxTransformations.contains(textBox)) {
+ transform = s_identityTransform;
+ return;
+ }
+
+ transform = m_textBoxTransformations.get(textBox);
+}
+
+void SVGTextChunkBuilder::buildTextChunks(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.at(boxPosition);
+ if (!textBox->startsNewTextChunk())
+ continue;
+
+ if (!foundStart) {
+ lastChunkStartPosition = boxPosition;
+ foundStart = true;
+ } else {
+ ASSERT(boxPosition > lastChunkStartPosition);
+ addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition);
+ lastChunkStartPosition = boxPosition;
+ }
+ }
+
+ if (!foundStart)
+ return;
+
+ if (boxPosition - lastChunkStartPosition > 0)
+ addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition);
+}
+
+void SVGTextChunkBuilder::layoutTextChunks(Vector<SVGInlineTextBox*>& lineLayoutBoxes)
+{
+ buildTextChunks(lineLayoutBoxes);
+ if (m_textChunks.isEmpty())
+ return;
+
+ unsigned chunkCount = m_textChunks.size();
+ for (unsigned i = 0; i < chunkCount; ++i)
+ processTextChunk(m_textChunks.at(i));
+
+ m_textChunks.clear();
+}
+
+void SVGTextChunkBuilder::addTextChunk(Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned boxStart, unsigned boxCount)
+{
+ SVGInlineTextBox* textBox = lineLayoutBoxes.at(boxStart);
+ ASSERT(textBox);
+
+ RenderSVGInlineText* textRenderer = toRenderSVGInlineText(textBox->textRenderer());
+ ASSERT(textRenderer);
+
+ const RenderStyle* style = textRenderer->style();
+ ASSERT(style);
+
+ const SVGRenderStyle* svgStyle = style->svgStyle();
+ ASSERT(svgStyle);
+
+ SVGTextContentElement::SVGLengthAdjustType lengthAdjust = SVGTextContentElement::LENGTHADJUST_UNKNOWN;
+ float desiredTextLength = 0;
+
+ if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textRenderer->parent())) {
+ lengthAdjust = static_cast<SVGTextContentElement::SVGLengthAdjustType>(textContentElement->lengthAdjust());
+ desiredTextLength = textContentElement->textLength().value(textContentElement);
+ }
+
+ SVGTextChunk chunk(svgStyle->isVerticalWritingMode(), svgStyle->textAnchor(), lengthAdjust, desiredTextLength);
+
+ Vector<SVGInlineTextBox*>& boxes = chunk.boxes();
+ for (unsigned i = boxStart; i < boxStart + boxCount; ++i)
+ boxes.append(lineLayoutBoxes.at(i));
+
+ m_textChunks.append(chunk);
+}
+
+void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk& chunk)
+{
+ 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;
+
+ // 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);
+
+ bool isVerticalText = chunk.isVerticalText();
+ if (processTextLength) {
+ if (chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACING) {
+ float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters;
+ unsigned atCharacter = 0;
+ for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
+ Vector<SVGTextFragment>& fragments = boxes.at(boxPosition)->textFragments();
+ if (fragments.isEmpty())
+ continue;
+ processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter);
+ }
+ } else {
+ ASSERT(chunk.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS);
+ float scale = chunk.desiredTextLength() / chunkLength;
+ AffineTransform spacingAndGlyphsTransform;
+
+ bool foundFirstFragment = false;
+ for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
+ SVGInlineTextBox* textBox = boxes.at(boxPosition);
+ Vector<SVGTextFragment>& fragments = textBox->textFragments();
+ if (fragments.isEmpty())
+ continue;
+
+ if (!foundFirstFragment) {
+ foundFirstFragment = true;
+ buildSpacingAndGlyphsTransform(isVerticalText, scale, fragments.first(), spacingAndGlyphsTransform);
+ }
+
+ m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform);
+ }
+ }
+ }
+
+ 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.lengthAdjust() == SVGTextContentElement::LENGTHADJUST_SPACING) {
+ chunkLength = 0;
+ chunkCharacters = 0;
+ chunk.calculateLength(chunkLength, chunkCharacters);
+ }
+
+ float textAnchorShift = chunk.calculateTextAnchorShift(chunkLength);
+ for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
+ Vector<SVGTextFragment>& fragments = boxes.at(boxPosition)->textFragments();
+ if (fragments.isEmpty())
+ continue;
+ processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments);
+ }
+}
+
+void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector<SVGTextFragment>& fragments, unsigned& atCharacter)
+{
+ unsigned fragmentCount = fragments.size();
+ for (unsigned i = 0; i < fragmentCount; ++i) {
+ SVGTextFragment& fragment = fragments.at(i);
+
+ if (isVerticalText)
+ fragment.y += textLengthShift * atCharacter;
+ else
+ fragment.x += textLengthShift * atCharacter;
+
+ atCharacter += fragment.length;
+ }
+}
+
+void SVGTextChunkBuilder::processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector<SVGTextFragment>& fragments)
+{
+ unsigned fragmentCount = fragments.size();
+ for (unsigned i = 0; i < fragmentCount; ++i) {
+ SVGTextFragment& fragment = fragments.at(i);
+
+ if (isVerticalText)
+ fragment.y += textAnchorShift;
+ else
+ fragment.x += textAnchorShift;
+ }
+}
+
+void SVGTextChunkBuilder::buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment& fragment, AffineTransform& spacingAndGlyphsTransform)
+{
+ spacingAndGlyphsTransform.translate(fragment.x, fragment.y);
+
+ if (isVerticalText)
+ spacingAndGlyphsTransform.scaleNonUniform(1, scale);
+ else
+ spacingAndGlyphsTransform.scaleNonUniform(scale, 1);
+
+ spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y);
+}
+
+}
+
+#endif // ENABLE(SVG)