1/*
2 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
3 * Copyright (C) 2013 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
23#include "core/svg/SVGPathStringSource.h"
24
25#include "core/svg/SVGParserUtilities.h"
26#include "platform/geometry/FloatPoint.h"
27
28namespace blink {
29
30SVGPathStringSource::SVGPathStringSource(const String& string)
31    : m_string(string)
32    , m_is8BitSource(string.is8Bit())
33{
34    ASSERT(!string.isEmpty());
35
36    if (m_is8BitSource) {
37        m_current.m_character8 = string.characters8();
38        m_end.m_character8 = m_current.m_character8 + string.length();
39    } else {
40        m_current.m_character16 = string.characters16();
41        m_end.m_character16 = m_current.m_character16 + string.length();
42    }
43}
44
45bool SVGPathStringSource::hasMoreData() const
46{
47    if (m_is8BitSource)
48        return m_current.m_character8 < m_end.m_character8;
49    return m_current.m_character16 < m_end.m_character16;
50}
51
52bool SVGPathStringSource::moveToNextToken()
53{
54    if (m_is8BitSource)
55        return skipOptionalSVGSpaces(m_current.m_character8, m_end.m_character8);
56    return skipOptionalSVGSpaces(m_current.m_character16, m_end.m_character16);
57}
58
59template <typename CharacterType>
60static bool parseSVGSegmentTypeHelper(const CharacterType*& current, SVGPathSegType& pathSegType)
61{
62    switch (*(current++)) {
63    case 'Z':
64    case 'z':
65        pathSegType = PathSegClosePath;
66        break;
67    case 'M':
68        pathSegType = PathSegMoveToAbs;
69        break;
70    case 'm':
71        pathSegType = PathSegMoveToRel;
72        break;
73    case 'L':
74        pathSegType = PathSegLineToAbs;
75        break;
76    case 'l':
77        pathSegType = PathSegLineToRel;
78        break;
79    case 'C':
80        pathSegType = PathSegCurveToCubicAbs;
81        break;
82    case 'c':
83        pathSegType = PathSegCurveToCubicRel;
84        break;
85    case 'Q':
86        pathSegType = PathSegCurveToQuadraticAbs;
87        break;
88    case 'q':
89        pathSegType = PathSegCurveToQuadraticRel;
90        break;
91    case 'A':
92        pathSegType = PathSegArcAbs;
93        break;
94    case 'a':
95        pathSegType = PathSegArcRel;
96        break;
97    case 'H':
98        pathSegType = PathSegLineToHorizontalAbs;
99        break;
100    case 'h':
101        pathSegType = PathSegLineToHorizontalRel;
102        break;
103    case 'V':
104        pathSegType = PathSegLineToVerticalAbs;
105        break;
106    case 'v':
107        pathSegType = PathSegLineToVerticalRel;
108        break;
109    case 'S':
110        pathSegType = PathSegCurveToCubicSmoothAbs;
111        break;
112    case 's':
113        pathSegType = PathSegCurveToCubicSmoothRel;
114        break;
115    case 'T':
116        pathSegType = PathSegCurveToQuadraticSmoothAbs;
117        break;
118    case 't':
119        pathSegType = PathSegCurveToQuadraticSmoothRel;
120        break;
121    default:
122        pathSegType = PathSegUnknown;
123    }
124    return true;
125}
126
127bool SVGPathStringSource::parseSVGSegmentType(SVGPathSegType& pathSegType)
128{
129    if (m_is8BitSource)
130        return parseSVGSegmentTypeHelper(m_current.m_character8, pathSegType);
131    return parseSVGSegmentTypeHelper(m_current.m_character16, pathSegType);
132}
133
134template <typename CharacterType>
135static bool nextCommandHelper(const CharacterType*& current, SVGPathSegType previousCommand, SVGPathSegType& nextCommand)
136{
137    // Check for remaining coordinates in the current command.
138    if ((*current == '+' || *current == '-' || *current == '.' || (*current >= '0' && *current <= '9'))
139        && previousCommand != PathSegClosePath) {
140        if (previousCommand == PathSegMoveToAbs) {
141            nextCommand = PathSegLineToAbs;
142            return true;
143        }
144        if (previousCommand == PathSegMoveToRel) {
145            nextCommand = PathSegLineToRel;
146            return true;
147        }
148        nextCommand = previousCommand;
149        return true;
150    }
151
152    return false;
153}
154
155SVGPathSegType SVGPathStringSource::nextCommand(SVGPathSegType previousCommand)
156{
157    SVGPathSegType nextCommand;
158    if (m_is8BitSource) {
159        if (nextCommandHelper(m_current.m_character8, previousCommand, nextCommand))
160            return nextCommand;
161    } else {
162        if (nextCommandHelper(m_current.m_character16, previousCommand, nextCommand))
163            return nextCommand;
164    }
165
166    parseSVGSegmentType(nextCommand);
167    return nextCommand;
168}
169
170bool SVGPathStringSource::parseMoveToSegment(FloatPoint& targetPoint)
171{
172    if (m_is8BitSource)
173        return parseFloatPoint(m_current.m_character8, m_end.m_character8, targetPoint);
174    return parseFloatPoint(m_current.m_character16, m_end.m_character16, targetPoint);
175}
176
177bool SVGPathStringSource::parseLineToSegment(FloatPoint& targetPoint)
178{
179    if (m_is8BitSource)
180        return parseFloatPoint(m_current.m_character8, m_end.m_character8, targetPoint);
181    return parseFloatPoint(m_current.m_character16, m_end.m_character16, targetPoint);
182}
183
184bool SVGPathStringSource::parseLineToHorizontalSegment(float& x)
185{
186    if (m_is8BitSource)
187        return parseNumber(m_current.m_character8, m_end.m_character8, x);
188    return parseNumber(m_current.m_character16, m_end.m_character16, x);
189}
190
191bool SVGPathStringSource::parseLineToVerticalSegment(float& y)
192{
193    if (m_is8BitSource)
194        return parseNumber(m_current.m_character8, m_end.m_character8, y);
195    return parseNumber(m_current.m_character16, m_end.m_character16, y);
196}
197
198bool SVGPathStringSource::parseCurveToCubicSegment(FloatPoint& point1, FloatPoint& point2, FloatPoint& targetPoint)
199{
200    if (m_is8BitSource)
201        return parseFloatPoint3(m_current.m_character8, m_end.m_character8, point1, point2, targetPoint);
202    return parseFloatPoint3(m_current.m_character16, m_end.m_character16, point1, point2, targetPoint);
203}
204
205bool SVGPathStringSource::parseCurveToCubicSmoothSegment(FloatPoint& point1, FloatPoint& targetPoint)
206{
207    if (m_is8BitSource)
208        return parseFloatPoint2(m_current.m_character8, m_end.m_character8, point1, targetPoint);
209    return parseFloatPoint2(m_current.m_character16, m_end.m_character16, point1, targetPoint);
210}
211
212bool SVGPathStringSource::parseCurveToQuadraticSegment(FloatPoint& point2, FloatPoint& targetPoint)
213{
214    if (m_is8BitSource)
215        return parseFloatPoint2(m_current.m_character8, m_end.m_character8, point2, targetPoint);
216    return parseFloatPoint2(m_current.m_character16, m_end.m_character16, point2, targetPoint);
217}
218
219bool SVGPathStringSource::parseCurveToQuadraticSmoothSegment(FloatPoint& targetPoint)
220{
221    if (m_is8BitSource)
222        return parseFloatPoint(m_current.m_character8, m_end.m_character8, targetPoint);
223    return parseFloatPoint(m_current.m_character16, m_end.m_character16, targetPoint);
224}
225
226template <typename CharacterType>
227static bool parseArcToSegmentHelper(const CharacterType*& current, const CharacterType* end, float& rx, float& ry, float& angle, bool& largeArc, bool& sweep, FloatPoint& targetPoint)
228{
229    float toX;
230    float toY;
231    if (!parseNumber(current, end, rx)
232        || !parseNumber(current, end, ry)
233        || !parseNumber(current, end, angle)
234        || !parseArcFlag(current, end, largeArc)
235        || !parseArcFlag(current, end, sweep)
236        || !parseNumber(current, end, toX)
237        || !parseNumber(current, end, toY))
238        return false;
239    targetPoint = FloatPoint(toX, toY);
240    return true;
241}
242
243bool SVGPathStringSource::parseArcToSegment(float& rx, float& ry, float& angle, bool& largeArc, bool& sweep, FloatPoint& targetPoint)
244{
245    if (m_is8BitSource)
246        return parseArcToSegmentHelper(m_current.m_character8, m_end.m_character8, rx, ry, angle, largeArc, sweep, targetPoint);
247    return parseArcToSegmentHelper(m_current.m_character16, m_end.m_character16, rx, ry, angle, largeArc, sweep, targetPoint);
248}
249
250} // namespace blink
251