WebCore/rendering/SVGCharacterLayoutInfo.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/rendering/SVGCharacterLayoutInfo.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,514 @@
+/*
+ * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ *
+ * 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"
+#include "SVGCharacterLayoutInfo.h"
+
+#if ENABLE(SVG)
+#include "InlineFlowBox.h"
+#include "InlineTextBox.h"
+#include "RenderSVGTextPath.h"
+#include "SVGCharacterData.h"
+#include "SVGLengthList.h"
+#include "SVGNumberList.h"
+#include "SVGTextPositioningElement.h"
+
+#include <float.h>
+
+namespace WebCore {
+
+// Helper function
+static float calculateBaselineShift(RenderObject* item)
+{
+    const Font& font = item->style()->font();
+    const SVGRenderStyle* svgStyle = item->style()->svgStyle();
+
+    float baselineShift = 0.0f;
+    if (svgStyle->baselineShift() == BS_LENGTH) {
+        CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->baselineShiftValue());
+        baselineShift = primitive->getFloatValue();
+
+        if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
+            baselineShift = baselineShift / 100.0f * font.pixelSize();
+    } else {
+        float baselineAscent = font.ascent() + font.descent();
+
+        switch (svgStyle->baselineShift()) {
+        case BS_BASELINE:
+            break;
+        case BS_SUB:
+            baselineShift = -baselineAscent / 2.0f;
+            break;
+        case BS_SUPER:
+            baselineShift = baselineAscent / 2.0f;
+            break;
+        default:
+            ASSERT_NOT_REACHED();
+        }
+    }
+
+    return baselineShift;
+}
+
+SVGCharacterLayoutInfo::SVGCharacterLayoutInfo()
+    : curx(0.0f)
+    , cury(0.0f)
+    , angle(0.0f)
+    , dx(0.0f)
+    , dy(0.0f)
+    , shiftx(0.0f)
+    , shifty(0.0f)
+    , pathExtraAdvance(0.0f)
+    , pathTextLength(0.0f)
+    , pathChunkLength(0.0f)
+    , nextDrawnSeperated(false)
+    , xStackChanged(false)
+    , yStackChanged(false)
+    , dxStackChanged(false)
+    , dyStackChanged(false)
+    , angleStackChanged(false)
+    , baselineShiftStackChanged(false)
+    , pathLayout(false)
+    , currentOffset(0.0f)
+    , startOffset(0.0f)
+    , layoutPathLength(0.0f)
+{
+}
+
+bool SVGCharacterLayoutInfo::xValueAvailable() const
+{
+    return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size();
+}
+
+bool SVGCharacterLayoutInfo::yValueAvailable() const
+{
+    return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size();
+}
+
+bool SVGCharacterLayoutInfo::dxValueAvailable() const
+{
+    return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size();
+}
+
+bool SVGCharacterLayoutInfo::dyValueAvailable() const
+{
+    return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size();
+}
+
+bool SVGCharacterLayoutInfo::angleValueAvailable() const
+{
+    return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size();
+}
+
+bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const
+{
+    return !baselineShiftStack.isEmpty();
+}
+
+float SVGCharacterLayoutInfo::xValueNext() const
+{
+    ASSERT(!xStack.isEmpty());
+    return xStack.last().valueAtCurrentPosition();
+}
+
+float SVGCharacterLayoutInfo::yValueNext() const
+{
+    ASSERT(!yStack.isEmpty());
+    return yStack.last().valueAtCurrentPosition();
+}
+
+float SVGCharacterLayoutInfo::dxValueNext() const
+{
+    ASSERT(!dxStack.isEmpty());
+    return dxStack.last().valueAtCurrentPosition();
+}
+
+float SVGCharacterLayoutInfo::dyValueNext() const
+{
+    ASSERT(!dyStack.isEmpty());
+    return dyStack.last().valueAtCurrentPosition();
+}
+
+float SVGCharacterLayoutInfo::angleValueNext() const
+{
+    ASSERT(!angleStack.isEmpty());
+    return angleStack.last().valueAtCurrentPosition();
+}
+
+float SVGCharacterLayoutInfo::baselineShiftValueNext() const
+{
+    ASSERT(!baselineShiftStack.isEmpty());
+    return baselineShiftStack.last();
+}
+
+
+bool SVGCharacterLayoutInfo::isInitialLayout() const
+{
+    return xStack.isEmpty()
+        && yStack.isEmpty()
+        && dxStack.isEmpty()
+        && dyStack.isEmpty()
+        && angleStack.isEmpty()
+        && baselineShiftStack.isEmpty()
+        && curx == 0.0f
+        && cury == 0.0f;
+}
+
+void SVGCharacterLayoutInfo::processedSingleCharacter()
+{
+    xStackWalk();
+    yStackWalk();
+    dxStackWalk();
+    dyStackWalk();
+    angleStackWalk();
+    baselineShiftStackWalk();
+}
+
+void SVGCharacterLayoutInfo::processedChunk(float savedShiftX, float savedShiftY)
+{
+    // baseline-shift doesn't span across ancestors, unlike dx/dy.
+    curx += savedShiftX - shiftx;
+    cury += savedShiftY - shifty;
+
+    if (inPathLayout()) {
+        shiftx = savedShiftX;
+        shifty = savedShiftY;
+    }
+
+    // rotation also doesn't span
+    angle = 0.0f;
+
+    if (xStackChanged) {
+        ASSERT(!xStack.isEmpty());
+        xStack.removeLast();
+        xStackChanged = false;
+    }
+
+    if (yStackChanged) {
+        ASSERT(!yStack.isEmpty());
+        yStack.removeLast();
+        yStackChanged = false;
+    }
+
+    if (dxStackChanged) {
+        ASSERT(!dxStack.isEmpty());
+        dxStack.removeLast();
+        dxStackChanged = false;
+    }
+
+    if (dyStackChanged) {
+        ASSERT(!dyStack.isEmpty());
+        dyStack.removeLast();
+        dyStackChanged = false;
+    }
+
+    if (angleStackChanged) {
+        ASSERT(!angleStack.isEmpty());
+        angleStack.removeLast();
+        angleStackChanged = false;
+    }
+
+    if (baselineShiftStackChanged) {
+        ASSERT(!baselineShiftStack.isEmpty());
+        baselineShiftStack.removeLast();
+        baselineShiftStackChanged = false;
+    }
+}
+
+bool SVGCharacterLayoutInfo::nextPathLayoutPointAndAngle(float glyphAdvance, float extraAdvance, float newOffset)
+{
+    if (layoutPathLength <= 0.0f)
+        return false;
+
+    if (newOffset != FLT_MIN)
+        currentOffset = startOffset + newOffset;
+
+    // Respect translation along path (extraAdvance is orthogonal to the path)
+    currentOffset += extraAdvance;
+
+    float offset = currentOffset + glyphAdvance / 2.0f;
+    currentOffset += glyphAdvance + pathExtraAdvance;
+
+    if (offset < 0.0f || offset > layoutPathLength)
+        return false;
+
+    bool ok = false; 
+    FloatPoint point = layoutPath.pointAtLength(offset, ok);
+    ASSERT(ok);
+
+    curx = point.x();
+    cury = point.y();
+
+    angle = layoutPath.normalAngleAtLength(offset, ok);
+    ASSERT(ok);
+
+    return true;
+}
+
+bool SVGCharacterLayoutInfo::inPathLayout() const
+{
+    return pathLayout;
+}
+
+void SVGCharacterLayoutInfo::setInPathLayout(bool value)
+{
+    pathLayout = value;
+
+    pathExtraAdvance = 0.0f;
+    pathTextLength = 0.0f;
+    pathChunkLength = 0.0f;
+}
+
+void SVGCharacterLayoutInfo::addLayoutInformation(InlineFlowBox* flowBox, float textAnchorStartOffset)
+{
+    bool wasInitialLayout = isInitialLayout();
+
+    RenderSVGTextPath* textPath = toRenderSVGTextPath(flowBox->renderer());
+    Path path = textPath->layoutPath();
+
+    float baselineShift = calculateBaselineShift(textPath);
+
+    layoutPath = path;
+    layoutPathLength = path.length();
+
+    if (layoutPathLength <= 0.0f)
+        return;
+
+    startOffset = textPath->startOffset();
+
+    if (textPath->startOffset() >= 0.0f && textPath->startOffset() <= 1.0f)
+        startOffset *= layoutPathLength;
+
+    startOffset += textAnchorStartOffset;
+    currentOffset = startOffset;
+
+    // Only baseline-shift is handled through the normal layout system
+    addStackContent(BaselineShiftStack, baselineShift);
+
+    if (wasInitialLayout) {
+        xStackChanged = false;
+        yStackChanged = false;
+        dxStackChanged = false;
+        dyStackChanged = false;
+        angleStackChanged = false;
+        baselineShiftStackChanged = false;
+    }
+}
+
+void SVGCharacterLayoutInfo::addLayoutInformation(SVGTextPositioningElement* element)
+{
+    bool wasInitialLayout = isInitialLayout();
+    float baselineShift = calculateBaselineShift(element->renderer());
+
+    addStackContent(XStack, element->x(), element);
+    addStackContent(YStack, element->y(), element);
+    addStackContent(DxStack, element->dx(), element);
+    addStackContent(DyStack, element->dy(), element);
+    addStackContent(AngleStack, element->rotate());
+    addStackContent(BaselineShiftStack, baselineShift);
+
+    if (wasInitialLayout) {
+        xStackChanged = false;
+        yStackChanged = false;
+        dxStackChanged = false;
+        dyStackChanged = false;
+        angleStackChanged = false;
+        baselineShiftStackChanged = false;
+    }
+}
+
+void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGNumberList* list)
+{
+    unsigned length = list->numberOfItems();
+    if (!length)
+        return;
+
+    PositionedFloatVector newLayoutInfo;
+
+    // TODO: Convert more efficiently!
+    ExceptionCode ec = 0;
+    for (unsigned i = 0; i < length; ++i) {
+        float value = list->getItem(i, ec);
+        ASSERT(!ec);
+
+        newLayoutInfo.append(value);
+    }
+
+    addStackContent(type, newLayoutInfo);
+}
+
+void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGLengthList* list, const SVGElement* context)
+{
+    unsigned length = list->numberOfItems();
+    if (!length)
+        return;
+
+    PositionedFloatVector newLayoutInfo;
+
+    ExceptionCode ec = 0;
+    for (unsigned i = 0; i < length; ++i) {
+        float value = list->getItem(i, ec).value(context);
+        ASSERT(!ec);
+
+        newLayoutInfo.append(value);
+    }
+
+    addStackContent(type, newLayoutInfo);
+}
+
+void SVGCharacterLayoutInfo::addStackContent(StackType type, const PositionedFloatVector& list)
+{
+    switch (type) {
+    case XStack:
+        xStackChanged = true;
+        xStack.append(list);
+        break;
+    case YStack:
+        yStackChanged = true;
+        yStack.append(list);
+        break;
+    case DxStack:
+        dxStackChanged = true;
+        dxStack.append(list);
+        break;
+    case DyStack:
+        dyStackChanged = true;
+        dyStack.append(list);
+        break;
+    case AngleStack:
+        angleStackChanged = true;
+        angleStack.append(list);
+        break; 
+    default:
+        ASSERT_NOT_REACHED();
+    }
+}
+
+void SVGCharacterLayoutInfo::addStackContent(StackType type, float value)
+{
+    if (value == 0.0f)
+        return;
+
+    switch (type) {
+    case BaselineShiftStack:
+        baselineShiftStackChanged = true;
+        baselineShiftStack.append(value);
+        break;
+    default:
+        ASSERT_NOT_REACHED();
+    }
+}
+
+void SVGCharacterLayoutInfo::xStackWalk()
+{
+    unsigned i = 1;
+
+    while (!xStack.isEmpty()) {
+        PositionedFloatVector& cur = xStack.last();
+        if (i + cur.position() < cur.size()) {
+            cur.advance(i);
+            break;
+        }
+
+        i += cur.position();
+        xStack.removeLast();
+        xStackChanged = false;
+    }
+}
+
+void SVGCharacterLayoutInfo::yStackWalk()
+{
+    unsigned i = 1;
+
+    while (!yStack.isEmpty()) {
+        PositionedFloatVector& cur = yStack.last();
+        if (i + cur.position() < cur.size()) {
+            cur.advance(i);
+            break;
+        }
+
+        i += cur.position();
+        yStack.removeLast();
+        yStackChanged = false;
+    }
+}
+
+void SVGCharacterLayoutInfo::dxStackWalk()
+{
+    unsigned i = 1;
+
+    while (!dxStack.isEmpty()) {
+        PositionedFloatVector& cur = dxStack.last();
+        if (i + cur.position() < cur.size()) {
+            cur.advance(i);
+            break;
+        }
+
+        i += cur.position();
+        dxStack.removeLast();
+        dxStackChanged = false;
+    }
+}
+
+void SVGCharacterLayoutInfo::dyStackWalk()
+{
+    unsigned i = 1;
+
+    while (!dyStack.isEmpty()) {
+        PositionedFloatVector& cur = dyStack.last();
+        if (i + cur.position() < cur.size()) {
+            cur.advance(i);
+            break;
+        }
+
+        i += cur.position();
+        dyStack.removeLast();
+        dyStackChanged = false;
+    }
+}
+
+void SVGCharacterLayoutInfo::angleStackWalk()
+{
+    unsigned i = 1;
+
+    while (!angleStack.isEmpty()) {
+        PositionedFloatVector& cur = angleStack.last();
+        if (i + cur.position() < cur.size()) {
+            cur.advance(i);
+            break;
+        }
+
+        i += cur.position();
+        angleStack.removeLast();
+        angleStackChanged = false;
+    }
+}
+
+void SVGCharacterLayoutInfo::baselineShiftStackWalk()
+{
+    if (!baselineShiftStack.isEmpty()) {
+        baselineShiftStack.removeLast();
+        baselineShiftStackChanged = false;
+    }
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)