summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/svg/SVGTextChunk.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/svg/SVGTextChunk.cpp')
-rw-r--r--Source/WebCore/rendering/svg/SVGTextChunk.cpp201
1 files changed, 163 insertions, 38 deletions
diff --git a/Source/WebCore/rendering/svg/SVGTextChunk.cpp b/Source/WebCore/rendering/svg/SVGTextChunk.cpp
index b7072753b..17811864e 100644
--- a/Source/WebCore/rendering/svg/SVGTextChunk.cpp
+++ b/Source/WebCore/rendering/svg/SVGTextChunk.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. 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
@@ -18,60 +19,102 @@
*/
#include "config.h"
-
-#if ENABLE(SVG)
#include "SVGTextChunk.h"
#include "SVGInlineTextBox.h"
namespace WebCore {
-SVGTextChunk::SVGTextChunk(unsigned chunkStyle, float desiredTextLength)
- : m_chunkStyle(chunkStyle)
- , m_desiredTextLength(desiredTextLength)
-{
-}
-
-void SVGTextChunk::calculateLength(float& length, unsigned& characters) const
+SVGTextChunk::SVGTextChunk(const Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned first, unsigned limit)
{
- SVGTextFragment* lastFragment = 0;
+ ASSERT(first < limit);
+ ASSERT(limit <= lineLayoutBoxes.size());
+
+ const SVGInlineTextBox* box = lineLayoutBoxes[first];
+ const RenderStyle& style = box->renderer().style();
+ const SVGRenderStyle& svgStyle = style.svgStyle();
+
+ if (!style.isLeftToRightDirection())
+ m_chunkStyle |= SVGTextChunk::RightToLeftText;
+
+ if (style.isVerticalWritingMode())
+ m_chunkStyle |= SVGTextChunk::VerticalText;
+
+ switch (svgStyle.textAnchor()) {
+ case TA_START:
+ break;
+ case TA_MIDDLE:
+ m_chunkStyle |= MiddleAnchor;
+ break;
+ case TA_END:
+ m_chunkStyle |= EndAnchor;
+ break;
+ }
- unsigned boxCount = m_boxes.size();
- for (unsigned boxPosition = 0; boxPosition < boxCount; ++boxPosition) {
- SVGInlineTextBox* textBox = m_boxes.at(boxPosition);
- Vector<SVGTextFragment>& fragments = textBox->textFragments();
+ if (auto* textContentElement = SVGTextContentElement::elementFromRenderer(box->renderer().parent())) {
+ SVGLengthContext lengthContext(textContentElement);
+ m_desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext);
+
+ switch (textContentElement->lengthAdjust()) {
+ case SVGLengthAdjustUnknown:
+ break;
+ case SVGLengthAdjustSpacing:
+ m_chunkStyle |= LengthAdjustSpacing;
+ break;
+ case SVGLengthAdjustSpacingAndGlyphs:
+ m_chunkStyle |= LengthAdjustSpacingAndGlyphs;
+ break;
+ }
+ }
- unsigned size = fragments.size();
- if (!size)
- continue;
+ for (unsigned i = first; i < limit; ++i)
+ m_boxes.append(lineLayoutBoxes[i]);
+}
- for (unsigned i = 0; i < size; ++i) {
- SVGTextFragment& fragment = fragments.at(i);
+unsigned SVGTextChunk::totalCharacters() const
+{
+ unsigned characters = 0;
+ for (auto* box : m_boxes) {
+ for (auto& fragment : box->textFragments())
characters += fragment.length;
+ }
+ return characters;
+}
- if (m_chunkStyle & VerticalText)
- length += fragment.height;
- else
- length += fragment.width;
-
- if (!lastFragment) {
- lastFragment = &fragment;
- continue;
- }
-
- // Resepect gap between chunks.
- if (m_chunkStyle & VerticalText)
- length += fragment.y - (lastFragment->y + lastFragment->height);
- else
- length += fragment.x - (lastFragment->x + lastFragment->width);
+float SVGTextChunk::totalLength() const
+{
+ const SVGTextFragment* firstFragment = nullptr;
+ const SVGTextFragment* lastFragment = nullptr;
+
+ for (auto* box : m_boxes) {
+ auto& fragments = box->textFragments();
+ if (fragments.size()) {
+ firstFragment = &(*fragments.begin());
+ break;
+ }
+ }
- lastFragment = &fragment;
+ for (auto it = m_boxes.rbegin(), end = m_boxes.rend(); it != end; ++it) {
+ auto& fragments = (*it)->textFragments();
+ if (fragments.size()) {
+ lastFragment = &(*fragments.rbegin());
+ break;
}
}
+
+ ASSERT(!firstFragment == !lastFragment);
+ if (!firstFragment)
+ return 0;
+
+ if (m_chunkStyle & VerticalText)
+ return (lastFragment->y + lastFragment->height) - firstFragment->y;
+
+ return (lastFragment->x + lastFragment->width) - firstFragment->x;
}
-float SVGTextChunk::calculateTextAnchorShift(float length) const
+float SVGTextChunk::totalAnchorShift() const
{
+ float length = totalLength();
if (m_chunkStyle & MiddleAnchor)
return -length / 2;
if (m_chunkStyle & EndAnchor)
@@ -79,6 +122,88 @@ float SVGTextChunk::calculateTextAnchorShift(float length) const
return m_chunkStyle & RightToLeftText ? -length : 0;
}
-} // namespace WebCore
+void SVGTextChunk::layout(HashMap<SVGInlineTextBox*, AffineTransform>& textBoxTransformations) const
+{
+ if (hasDesiredTextLength()) {
+ if (hasLengthAdjustSpacing())
+ processTextLengthSpacingCorrection();
+ else {
+ ASSERT(hasLengthAdjustSpacingAndGlyphs());
+ buildBoxTransformations(textBoxTransformations);
+ }
+ }
-#endif // ENABLE(SVG)
+ if (hasTextAnchor())
+ processTextAnchorCorrection();
+}
+
+void SVGTextChunk::processTextLengthSpacingCorrection() const
+{
+ float textLengthShift = (desiredTextLength() - totalLength()) / totalCharacters();
+ bool isVerticalText = m_chunkStyle & VerticalText;
+ unsigned atCharacter = 0;
+
+ for (auto* box : m_boxes) {
+ for (auto& fragment : box->textFragments()) {
+ if (isVerticalText)
+ fragment.y += textLengthShift * atCharacter;
+ else
+ fragment.x += textLengthShift * atCharacter;
+
+ atCharacter += fragment.length;
+ }
+ }
+}
+
+void SVGTextChunk::buildBoxTransformations(HashMap<SVGInlineTextBox*, AffineTransform>& textBoxTransformations) const
+{
+ AffineTransform spacingAndGlyphsTransform;
+ bool foundFirstFragment = false;
+
+ for (auto* box : m_boxes) {
+ if (!foundFirstFragment) {
+ if (!boxSpacingAndGlyphsTransform(box, spacingAndGlyphsTransform))
+ continue;
+ foundFirstFragment = true;
+ }
+
+ textBoxTransformations.set(box, spacingAndGlyphsTransform);
+ }
+}
+
+bool SVGTextChunk::boxSpacingAndGlyphsTransform(const SVGInlineTextBox* box, AffineTransform& spacingAndGlyphsTransform) const
+{
+ auto& fragments = box->textFragments();
+ if (fragments.isEmpty())
+ return false;
+
+ const SVGTextFragment& fragment = fragments.first();
+ float scale = desiredTextLength() / totalLength();
+
+ spacingAndGlyphsTransform.translate(fragment.x, fragment.y);
+
+ if (m_chunkStyle & VerticalText)
+ spacingAndGlyphsTransform.scaleNonUniform(1, scale);
+ else
+ spacingAndGlyphsTransform.scaleNonUniform(scale, 1);
+
+ spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y);
+ return true;
+}
+
+void SVGTextChunk::processTextAnchorCorrection() const
+{
+ float textAnchorShift = totalAnchorShift();
+ bool isVerticalText = m_chunkStyle & VerticalText;
+
+ for (auto* box : m_boxes) {
+ for (auto& fragment : box->textFragments()) {
+ if (isVerticalText)
+ fragment.y += textAnchorShift;
+ else
+ fragment.x += textAnchorShift;
+ }
+ }
+}
+
+} // namespace WebCore