summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-13 06:44:40 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2011-05-13 06:44:40 -0700
commit08014c20784f3db5df3a89b73cce46037b77eb59 (patch)
tree47749210d31e19e6e2f64036fa8fae2ad693476f /Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp
parent860220379e56aeb66424861ad602b07ee22b4055 (diff)
parent4c3661f7918f8b3f139f824efb7855bedccb4c94 (diff)
downloadexternal_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.zip
external_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.tar.gz
external_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.tar.bz2
Merge changes Ide388898,Ic49f367c,I1158a808,Iacb6ca5d,I2100dd3a,I5c1abe54,Ib0ef9902,I31dbc523,I570314b3
* changes: Merge WebKit at r75315: Update WebKit version Merge WebKit at r75315: Add FrameLoaderClient PageCache stubs Merge WebKit at r75315: Stub out AXObjectCache::remove() Merge WebKit at r75315: Fix ImageBuffer Merge WebKit at r75315: Fix PluginData::initPlugins() Merge WebKit at r75315: Fix conflicts Merge WebKit at r75315: Fix Makefiles Merge WebKit at r75315: Move Android-specific WebCore files to Source Merge WebKit at r75315: Initial merge by git.
Diffstat (limited to 'Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp')
-rw-r--r--Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp308
1 files changed, 308 insertions, 0 deletions
diff --git a/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp
new file mode 100644
index 0000000..3122912
--- /dev/null
+++ b/Source/WebCore/rendering/svg/SVGTextLayoutAttributesBuilder.cpp
@@ -0,0 +1,308 @@
+/*
+ * 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 "SVGTextLayoutAttributesBuilder.h"
+
+#include "RenderSVGInlineText.h"
+#include "RenderSVGText.h"
+#include "SVGTextPositioningElement.h"
+
+// Set to a value > 0 to dump the text layout attributes
+#define DUMP_TEXT_LAYOUT_ATTRIBUTES 0
+
+namespace WebCore {
+
+SVGTextLayoutAttributesBuilder::SVGTextLayoutAttributesBuilder()
+{
+}
+
+void SVGTextLayoutAttributesBuilder::buildLayoutAttributesForTextSubtree(RenderSVGText* textRoot)
+{
+ ASSERT(textRoot);
+ m_scopes.clear();
+
+ // Build list of x/y/dx/dy/rotate values for each subtree element that may define these values (tspan/textPath etc).
+ unsigned atCharacter = 0;
+ UChar lastCharacter = '\0';
+ buildLayoutScopes(textRoot, atCharacter, lastCharacter);
+
+ if (!atCharacter)
+ return;
+
+ // Build list of x/y/dx/dy/rotate values for the outermost <text> element.
+ buildOutermostLayoutScope(textRoot, atCharacter);
+
+ // Propagate layout attributes to each RenderSVGInlineText object.
+ atCharacter = 0;
+ lastCharacter = '\0';
+ propagateLayoutAttributes(textRoot, atCharacter, lastCharacter);
+}
+
+static inline void extractFloatValuesFromSVGLengthList(SVGElement* lengthContext, const SVGLengthList& list, Vector<float>& floatValues, unsigned textContentLength)
+{
+ ASSERT(lengthContext);
+
+ unsigned length = list.size();
+ if (length > textContentLength)
+ length = textContentLength;
+ floatValues.reserveCapacity(length);
+
+ for (unsigned i = 0; i < length; ++i) {
+ const SVGLength& length = list.at(i);
+ floatValues.append(length.value(lengthContext));
+ }
+}
+
+static inline void extractFloatValuesFromSVGNumberList(const SVGNumberList& list, Vector<float>& floatValues, unsigned textContentLength)
+{
+ unsigned length = list.size();
+ if (length > textContentLength)
+ length = textContentLength;
+ floatValues.reserveCapacity(length);
+
+ for (unsigned i = 0; i < length; ++i)
+ floatValues.append(list.at(i));
+}
+
+void SVGTextLayoutAttributesBuilder::buildLayoutScope(LayoutScope& scope, RenderObject* renderer, unsigned textContentStart, unsigned textContentLength) const
+{
+ ASSERT(renderer);
+ ASSERT(renderer->style());
+
+ scope.textContentStart = textContentStart;
+ scope.textContentLength = textContentLength;
+
+ SVGTextPositioningElement* element = SVGTextPositioningElement::elementFromRenderer(renderer);
+ if (!element)
+ return;
+
+ SVGTextLayoutAttributes& attributes = scope.attributes;
+ extractFloatValuesFromSVGLengthList(element, element->x(), attributes.xValues(), textContentLength);
+ extractFloatValuesFromSVGLengthList(element, element->y(), attributes.yValues(), textContentLength);
+ extractFloatValuesFromSVGLengthList(element, element->dx(), attributes.dxValues(), textContentLength);
+ extractFloatValuesFromSVGLengthList(element, element->dy(), attributes.dyValues(), textContentLength);
+ extractFloatValuesFromSVGNumberList(element->rotate(), attributes.rotateValues(), textContentLength);
+
+ // The last rotation value spans the whole scope.
+ Vector<float>& rotateValues = attributes.rotateValues();
+ if (rotateValues.isEmpty())
+ return;
+
+ unsigned rotateValuesSize = rotateValues.size();
+ if (rotateValuesSize == textContentLength)
+ return;
+
+ float lastRotation = rotateValues.last();
+
+ rotateValues.resize(textContentLength);
+ for (unsigned i = rotateValuesSize; i < textContentLength; ++i)
+ rotateValues.at(i) = lastRotation;
+}
+
+static inline bool characterIsSpace(const UChar& character)
+{
+ return character == ' ';
+}
+
+static inline bool characterIsSpaceOrNull(const UChar& character)
+{
+ return character == ' ' || character == '\0';
+}
+
+static inline bool shouldPreserveAllWhiteSpace(RenderStyle* style)
+{
+ ASSERT(style);
+ return style->whiteSpace() == PRE;
+}
+
+void SVGTextLayoutAttributesBuilder::buildLayoutScopes(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter)
+{
+ for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
+ if (child->isSVGInlineText()) {
+ RenderSVGInlineText* text = toRenderSVGInlineText(child);
+
+ if (!shouldPreserveAllWhiteSpace(text->style())) {
+ const UChar* characters = text->characters();
+ unsigned textLength = text->textLength();
+ for (unsigned textPosition = 0; textPosition < textLength; ++textPosition) {
+ const UChar& currentCharacter = characters[textPosition];
+ if (characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter))
+ continue;
+
+ lastCharacter = currentCharacter;
+ ++atCharacter;
+ }
+ } else
+ atCharacter += text->textLength();
+
+ continue;
+ }
+
+ if (!child->isSVGInline())
+ continue;
+
+ unsigned textContentStart = atCharacter;
+ buildLayoutScopes(child, atCharacter, lastCharacter);
+
+ LayoutScope scope;
+ buildLayoutScope(scope, child, textContentStart, atCharacter - textContentStart);
+ m_scopes.append(scope);
+ }
+}
+
+void SVGTextLayoutAttributesBuilder::buildOutermostLayoutScope(RenderSVGText* textRoot, unsigned textLength)
+{
+ LayoutScope scope;
+ buildLayoutScope(scope, textRoot, 0, textLength);
+
+ // Handle <text> x/y default attributes.
+ Vector<float>& xValues = scope.attributes.xValues();
+ if (xValues.isEmpty())
+ xValues.append(0);
+
+ Vector<float>& yValues = scope.attributes.yValues();
+ if (yValues.isEmpty())
+ yValues.append(0);
+
+ m_scopes.prepend(scope);
+}
+
+void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, unsigned& atCharacter, UChar& lastCharacter) const
+{
+ for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) {
+ if (child->isSVGInlineText()) {
+ RenderSVGInlineText* text = toRenderSVGInlineText(child);
+ const UChar* characters = text->characters();
+ unsigned textLength = text->textLength();
+ bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style());
+
+ SVGTextLayoutAttributes attributes;
+ attributes.reserveCapacity(textLength);
+
+ unsigned valueListPosition = atCharacter;
+ unsigned metricsLength = 1;
+ for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) {
+ const UChar& currentCharacter = characters[textPosition];
+
+ SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1);
+ metricsLength = metrics.length();
+
+ if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) {
+ assignEmptyLayoutAttributesForCharacter(attributes);
+ attributes.textMetricsValues().append(SVGTextMetrics::emptyMetrics());
+ continue;
+ }
+
+ assignLayoutAttributesForCharacter(attributes, metrics, valueListPosition);
+
+ if (metricsLength > 1) {
+ for (unsigned i = 0; i < metricsLength - 1; ++i)
+ assignEmptyLayoutAttributesForCharacter(attributes);
+ }
+
+ lastCharacter = currentCharacter;
+ valueListPosition += metricsLength;
+ }
+
+#if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0
+ fprintf(stderr, "\nDumping layout attributes for RenderSVGInlineText, renderer=%p, node=%p (atCharacter: %i)\n", text, text->node(), atCharacter);
+ attributes.dump();
+#endif
+
+ text->storeLayoutAttributes(attributes);
+ atCharacter = valueListPosition;
+ continue;
+ }
+
+ if (!child->isSVGInline())
+ continue;
+
+ propagateLayoutAttributes(child, atCharacter, lastCharacter);
+ }
+}
+
+float SVGTextLayoutAttributesBuilder::nextLayoutValue(LayoutValueType type, unsigned atCharacter) const
+{
+ for (int i = m_scopes.size() - 1; i >= 0; --i) {
+ const LayoutScope& scope = m_scopes.at(i);
+ if (scope.textContentStart > atCharacter || scope.textContentStart + scope.textContentLength < atCharacter)
+ continue;
+
+ const Vector<float>* valuesPointer = 0;
+ switch (type) {
+ case XValueAttribute:
+ valuesPointer = &scope.attributes.xValues();
+ break;
+ case YValueAttribute:
+ valuesPointer = &scope.attributes.yValues();
+ break;
+ case DxValueAttribute:
+ valuesPointer = &scope.attributes.dxValues();
+ break;
+ case DyValueAttribute:
+ valuesPointer = &scope.attributes.dyValues();
+ break;
+ case RotateValueAttribute:
+ valuesPointer = &scope.attributes.rotateValues();
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ ASSERT(valuesPointer);
+ const Vector<float>& values = *valuesPointer;
+ if (values.isEmpty())
+ continue;
+
+ unsigned position = atCharacter - scope.textContentStart;
+ if (position >= values.size())
+ continue;
+
+ return values.at(position);
+ }
+
+ return SVGTextLayoutAttributes::emptyValue();
+}
+
+void SVGTextLayoutAttributesBuilder::assignLayoutAttributesForCharacter(SVGTextLayoutAttributes& attributes, SVGTextMetrics& metrics, unsigned valueListPosition) const
+{
+ attributes.xValues().append(nextLayoutValue(XValueAttribute, valueListPosition));
+ attributes.yValues().append(nextLayoutValue(YValueAttribute, valueListPosition));
+ attributes.dxValues().append(nextLayoutValue(DxValueAttribute, valueListPosition));
+ attributes.dyValues().append(nextLayoutValue(DyValueAttribute, valueListPosition));
+ attributes.rotateValues().append(nextLayoutValue(RotateValueAttribute, valueListPosition));
+ attributes.textMetricsValues().append(metrics);
+}
+
+void SVGTextLayoutAttributesBuilder::assignEmptyLayoutAttributesForCharacter(SVGTextLayoutAttributes& attributes) const
+{
+ attributes.xValues().append(SVGTextLayoutAttributes::emptyValue());
+ attributes.yValues().append(SVGTextLayoutAttributes::emptyValue());
+ attributes.dxValues().append(SVGTextLayoutAttributes::emptyValue());
+ attributes.dyValues().append(SVGTextLayoutAttributes::emptyValue());
+ attributes.rotateValues().append(SVGTextLayoutAttributes::emptyValue());
+ // This doesn't add an empty value to textMetricsValues() on purpose!
+}
+
+}
+
+#endif // ENABLE(SVG)