summaryrefslogtreecommitdiffstats
path: root/WebCore/rendering/SVGTextChunkLayoutInfo.h
blob: e06dd1ab46c4b21b8cfab532e08bc3d2a72d62d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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