[CSS Parser] Unprefix -webkit-writing-mode
[WebKit-https.git] / Source / WebCore / rendering / svg / SVGTextChunk.cpp
1 /*
2  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
3  * Copyright (C) 2015 Apple Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #include "config.h"
22 #include "SVGTextChunk.h"
23
24 #include "SVGInlineTextBox.h"
25
26 namespace WebCore {
27
28 SVGTextChunk::SVGTextChunk(const Vector<SVGInlineTextBox*>& lineLayoutBoxes, unsigned first, unsigned limit)
29 {
30     ASSERT(first < limit);
31     ASSERT(limit <= lineLayoutBoxes.size());
32
33     const SVGInlineTextBox* box = lineLayoutBoxes[first];
34     const RenderStyle& style = box->renderer().style();
35     const SVGRenderStyle& svgStyle = style.svgStyle();
36
37     if (!style.isLeftToRightDirection())
38         m_chunkStyle |= SVGTextChunk::RightToLeftText;
39
40     if (style.isVerticalWritingMode())
41         m_chunkStyle |= SVGTextChunk::VerticalText;
42     
43     switch (svgStyle.textAnchor()) {
44     case TA_START:
45         break;
46     case TA_MIDDLE:
47         m_chunkStyle |= MiddleAnchor;
48         break;
49     case TA_END:
50         m_chunkStyle |= EndAnchor;
51         break;
52     }
53
54     if (auto* textContentElement = SVGTextContentElement::elementFromRenderer(box->renderer().parent())) {
55         SVGLengthContext lengthContext(textContentElement);
56         m_desiredTextLength = textContentElement->specifiedTextLength().value(lengthContext);
57
58         switch (textContentElement->lengthAdjust()) {
59         case SVGLengthAdjustUnknown:
60             break;
61         case SVGLengthAdjustSpacing:
62             m_chunkStyle |= LengthAdjustSpacing;
63             break;
64         case SVGLengthAdjustSpacingAndGlyphs:
65             m_chunkStyle |= LengthAdjustSpacingAndGlyphs;
66             break;
67         }
68     }
69
70     for (unsigned i = first; i < limit; ++i)
71         m_boxes.append(lineLayoutBoxes[i]);
72 }
73
74 unsigned SVGTextChunk::totalCharacters() const
75 {
76     unsigned characters = 0;
77     for (auto* box : m_boxes) {
78         for (auto& fragment : box->textFragments())
79             characters += fragment.length;
80     }
81     return characters;
82 }
83
84 float SVGTextChunk::totalLength() const
85 {
86     const SVGTextFragment* firstFragment = nullptr;
87     const SVGTextFragment* lastFragment = nullptr;
88
89     for (auto* box : m_boxes) {
90         auto& fragments = box->textFragments();
91         if (fragments.size()) {
92             firstFragment = &(*fragments.begin());
93             break;
94         }
95     }
96
97     for (auto it = m_boxes.rbegin(), end = m_boxes.rend(); it != end; ++it) {
98         auto& fragments = (*it)->textFragments();
99         if (fragments.size()) {
100             lastFragment = &(*fragments.rbegin());
101             break;
102         }
103     }
104
105     ASSERT(!firstFragment == !lastFragment);
106     if (!firstFragment)
107         return 0;
108
109     if (m_chunkStyle & VerticalText)
110         return (lastFragment->y + lastFragment->height) - firstFragment->y;
111
112     return (lastFragment->x + lastFragment->width) - firstFragment->x;
113 }
114
115 float SVGTextChunk::totalAnchorShift() const
116 {
117     float length = totalLength();
118     if (m_chunkStyle & MiddleAnchor)
119         return -length / 2;
120     if (m_chunkStyle & EndAnchor)
121         return m_chunkStyle & RightToLeftText ? 0 : -length;
122     return m_chunkStyle & RightToLeftText ? -length : 0;
123 }
124
125 void SVGTextChunk::layout(HashMap<SVGInlineTextBox*, AffineTransform>& textBoxTransformations) const
126 {
127     if (hasDesiredTextLength()) {
128         if (hasLengthAdjustSpacing())
129             processTextLengthSpacingCorrection();
130         else {
131             ASSERT(hasLengthAdjustSpacingAndGlyphs());
132             buildBoxTransformations(textBoxTransformations);
133         }
134     }
135
136     if (hasTextAnchor())
137         processTextAnchorCorrection();
138 }
139
140 void SVGTextChunk::processTextLengthSpacingCorrection() const
141 {
142     float textLengthShift = (desiredTextLength() - totalLength()) / totalCharacters();
143     bool isVerticalText = m_chunkStyle & VerticalText;
144     unsigned atCharacter = 0;
145
146     for (auto* box : m_boxes) {
147         for (auto& fragment : box->textFragments()) {
148             if (isVerticalText)
149                 fragment.y += textLengthShift * atCharacter;
150             else
151                 fragment.x += textLengthShift * atCharacter;
152             
153             atCharacter += fragment.length;
154         }
155     }
156 }
157
158 void SVGTextChunk::buildBoxTransformations(HashMap<SVGInlineTextBox*, AffineTransform>& textBoxTransformations) const
159 {
160     AffineTransform spacingAndGlyphsTransform;
161     bool foundFirstFragment = false;
162
163     for (auto* box : m_boxes) {
164         if (!foundFirstFragment) {
165             if (!boxSpacingAndGlyphsTransform(box, spacingAndGlyphsTransform))
166                 continue;
167             foundFirstFragment = true;
168         }
169
170         textBoxTransformations.set(box, spacingAndGlyphsTransform);
171     }
172 }
173
174 bool SVGTextChunk::boxSpacingAndGlyphsTransform(const SVGInlineTextBox* box, AffineTransform& spacingAndGlyphsTransform) const
175 {
176     auto& fragments = box->textFragments();
177     if (fragments.isEmpty())
178         return false;
179
180     const SVGTextFragment& fragment = fragments.first();
181     float scale = desiredTextLength() / totalLength();
182
183     spacingAndGlyphsTransform.translate(fragment.x, fragment.y);
184
185     if (m_chunkStyle & VerticalText)
186         spacingAndGlyphsTransform.scaleNonUniform(1, scale);
187     else
188         spacingAndGlyphsTransform.scaleNonUniform(scale, 1);
189
190     spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y);
191     return true;
192 }
193
194 void SVGTextChunk::processTextAnchorCorrection() const
195 {
196     float textAnchorShift = totalAnchorShift();
197     bool isVerticalText = m_chunkStyle & VerticalText;
198
199     for (auto* box : m_boxes) {
200         for (auto& fragment : box->textFragments()) {
201             if (isVerticalText)
202                 fragment.y += textAnchorShift;
203             else
204                 fragment.x += textAnchorShift;
205         }
206     }
207 }
208
209 } // namespace WebCore