WebCore/rendering/SVGTextChunkLayoutInfo.h
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/rendering/SVGTextChunkLayoutInfo.h	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org>
+ * 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.
+ *
+ */
+
+#ifndef SVGTextChunkLayoutInfo_h
+#define SVGTextChunkLayoutInfo_h
+
+#if ENABLE(SVG)
+#include "AffineTransform.h"
+#include "SVGCharacterData.h"
+#include "SVGRenderStyle.h"
+#include "SVGTextContentElement.h"
+
+#include <wtf/Assertions.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class InlineBox;
+class InlineFlowBox;
+class SVGInlineTextBox;
+
+// A SVGTextChunk directly corresponds to the definition of a "text chunk" per SVG 1.1 specification
+// For example, each absolute positioned character starts a text chunk (much more to respect, see spec).
+// Each SVGTextChunk contains a Vector of SVGInlineBoxCharacterRange, describing how many boxes are spanned
+// by this chunk. Following two examples should clarify the code a bit:
+//
+// 1. <text x="10 20 30">ABC</text> - one InlineTextBox is created, three SVGTextChunks each with one SVGInlineBoxCharaterRange
+// [SVGTextChunk 1]
+//    [SVGInlineBoxCharacterRange 1, startOffset=0, endOffset=1, box=0x1]
+// [SVGTextChunk 2]
+//    [SVGInlineBoxCharacterRange 1, startOffset=1, endOffset=2, box=0x1]
+// [SVGTextChunk 3]
+//    [SVGInlineBoxCharacterRange 1, startOffset=2, endOffset=3, box=0x1]
+//
+// 2. <text x="10">A<tspan>B</tspan>C</text> - three InlineTextBoxs are created, one SVGTextChunk, with three SVGInlineBoxCharacterRanges
+// [SVGTextChunk 1]
+//    [SVGInlineBoxCharacterRange 1, startOffset=0, endOffset=1, box=0x1]
+//    [SVGInlineBoxCharacterRange 2, startOffset=0, endOffset=1, box=0x2]
+//    [SVGInlineBoxCharacterRange 3, startOffset=0, endOffset=1, box=0x3]
+//
+// High level overview of the SVG text layout code:
+// Step #1) - Build Vector of SVGChar objects starting from root <text> diving into children
+// Step #2) - Build Vector of SVGTextChunk objects, containing offsets into the InlineTextBoxes and SVGChar vectors
+// Step #3) - Apply chunk post processing (text-anchor / textLength support, which operate on text chunks!)
+// Step #4) - Propagate information, how many chunk "parts" are associated with each SVGInlineTextBox (see below)
+// Step #5) - Layout all InlineBoxes, only by measuring their context rect (x/y/width/height defined through SVGChars and transformations)
+// Step #6) - Layout SVGRootInlineBox, it's parent RenderSVGText block and fixup child positions, to be relative to the root box
+//
+// When painting a range of characters, we have to determine how many can be drawn in a row. Each absolute postioned
+// character is drawn individually. After step #2) we know all text chunks, and how they span across the SVGInlineTextBoxes.
+// In step #4) we build a list of text chunk "parts" and store it in each SVGInlineTextBox. A chunk "part" is a part of a
+// text chunk that lives in a SVGInlineTextBox (consists of a length, width, height and a monotonic offset from the chunk begin)
+// The SVGTextChunkPart object describes this information.
+// When painting we can follow the regular InlineBox flow, we start painting the SVGRootInlineBox, which just asks its children
+// to paint. They can paint on their own because all position information are known. Previously we used to draw _all_ characters
+// from the SVGRootInlineBox, which violates the whole concept of the multiple InlineBoxes, and made text selection very hard to
+// implement.
+
+struct SVGTextChunkPart {
+    SVGTextChunkPart()
+        : offset(-1)
+        , length(-1)
+        , width(0)
+        , height(0)
+    {
+    }
+
+    bool isValid() const
+    {
+        return offset != -1
+            && length != -1
+            && width
+            && height;
+    }
+
+    // First character of this text chunk part, defining the origin to be drawn
+    Vector<SVGChar>::const_iterator firstCharacter;
+
+    // Start offset in textRenderer()->characters() buffer.
+    int offset;
+
+    // length/width/height of chunk part
+    int length;
+    float width;
+    float height;
+};
+
+struct SVGInlineBoxCharacterRange {
+    SVGInlineBoxCharacterRange()
+        : startOffset(INT_MIN)
+        , endOffset(INT_MIN)
+        , box(0)
+    {
+    }
+
+    bool isOpen() const { return (startOffset == endOffset) && (endOffset == INT_MIN); }
+    bool isClosed() const { return startOffset != INT_MIN && endOffset != INT_MIN; }
+
+    int startOffset;
+    int endOffset;
+
+    InlineBox* box;
+};
+
+struct SVGChar;
+
+// Convenience typedef
+typedef SVGTextContentElement::SVGLengthAdjustType ELengthAdjust;
+
+struct SVGTextChunk {
+    SVGTextChunk(Vector<SVGChar>::iterator it)
+        : anchor(TA_START)
+        , textLength(0.0f)
+        , lengthAdjust(SVGTextContentElement::LENGTHADJUST_SPACING)
+        , isVerticalText(false)
+        , isTextPath(false)
+        , start(it)
+        , end(it)
+    {
+    }
+
+    // text-anchor support
+    ETextAnchor anchor;
+
+    // textLength & lengthAdjust support
+    float textLength;
+    ELengthAdjust lengthAdjust;
+    AffineTransform ctm;
+
+    // status flags
+    bool isVerticalText : 1;
+    bool isTextPath : 1;
+
+    // main chunk data
+    Vector<SVGChar>::iterator start;
+    Vector<SVGChar>::iterator end;
+
+    Vector<SVGInlineBoxCharacterRange> boxes;
+};
+
+struct SVGTextChunkLayoutInfo {
+    SVGTextChunkLayoutInfo()
+        : m_assignChunkProperties(true)
+        , m_handlingTextPath(false)
+        , m_charsIt(0)
+        , m_charsBegin(0)
+        , m_charsEnd(0)
+        , m_chunk(0)
+    {
+    }
+
+    const Vector<SVGTextChunk>& textChunks() const { return m_svgTextChunks; }
+
+    void buildTextChunks(Vector<SVGChar>::iterator charsBegin, Vector<SVGChar>::iterator charsEnd, InlineFlowBox* start);
+    void layoutTextChunks();
+
+private:
+    void startTextChunk();
+    void closeTextChunk();
+    void recursiveBuildTextChunks(InlineFlowBox* start);
+
+    bool m_assignChunkProperties : 1;
+    bool m_handlingTextPath : 1;
+
+    Vector<SVGChar>::iterator m_charsIt;
+    Vector<SVGChar>::iterator m_charsBegin;
+    Vector<SVGChar>::iterator m_charsEnd;
+
+    Vector<SVGTextChunk> m_svgTextChunks;
+    SVGTextChunk m_chunk;
+};
+
+// Helper functions
+float calculateTextAnchorShiftForTextChunk(SVGTextChunk&, ETextAnchor);
+float calculateTextLengthCorrectionForTextChunk(SVGTextChunk&, ELengthAdjust, float& computedLength);
+
+} // namespace WebCore
+
+#endif // ENABLE(SVG)
+#endif // SVGTextChunkLayoutInfo_h