OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | 2 * Copyright (C) Research In Motion Limited 2010. All rights reserved. |
3 * Copyright (C) 2013 Apple Inc. All rights reserved. | 3 * Copyright (C) 2013 Apple Inc. All rights reserved. |
4 * | 4 * |
5 * This library is free software; you can redistribute it and/or | 5 * This library is free software; you can redistribute it and/or |
6 * modify it under the terms of the GNU Library General Public | 6 * modify it under the terms of the GNU Library General Public |
7 * License as published by the Free Software Foundation; either | 7 * License as published by the Free Software Foundation; either |
8 * version 2 of the License, or (at your option) any later version. | 8 * version 2 of the License, or (at your option) any later version. |
9 * | 9 * |
10 * This library is distributed in the hope that it will be useful, | 10 * This library is distributed in the hope that it will be useful, |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 * Library General Public License for more details. | 13 * Library General Public License for more details. |
14 * | 14 * |
15 * You should have received a copy of the GNU Library General Public License | 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 | 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, | 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
18 * Boston, MA 02110-1301, USA. | 18 * Boston, MA 02110-1301, USA. |
19 */ | 19 */ |
20 | 20 |
21 #include "config.h" | 21 #include "config.h" |
22 | |
23 #include "core/svg/SVGPathStringSource.h" | 22 #include "core/svg/SVGPathStringSource.h" |
24 | 23 |
25 #include "core/svg/SVGParserUtilities.h" | 24 #include "core/svg/SVGParserUtilities.h" |
26 #include "platform/geometry/FloatPoint.h" | 25 #include "platform/geometry/FloatPoint.h" |
27 | 26 |
28 namespace blink { | 27 namespace blink { |
29 | 28 |
30 SVGPathStringSource::SVGPathStringSource(const String& string) | 29 SVGPathStringSource::SVGPathStringSource(const String& string) |
31 : m_string(string) | 30 : m_string(string) |
32 , m_is8BitSource(string.is8Bit()) | 31 , m_is8BitSource(string.is8Bit()) |
32 , m_seenError(false) | |
33 , m_previousCommand(PathSegUnknown) | |
33 { | 34 { |
34 ASSERT(!string.isEmpty()); | 35 ASSERT(!string.isEmpty()); |
35 | 36 |
36 if (m_is8BitSource) { | 37 if (m_is8BitSource) { |
37 m_current.m_character8 = string.characters8(); | 38 m_current.m_character8 = string.characters8(); |
38 m_end.m_character8 = m_current.m_character8 + string.length(); | 39 m_end.m_character8 = m_current.m_character8 + string.length(); |
39 } else { | 40 } else { |
40 m_current.m_character16 = string.characters16(); | 41 m_current.m_character16 = string.characters16(); |
41 m_end.m_character16 = m_current.m_character16 + string.length(); | 42 m_end.m_character16 = m_current.m_character16 + string.length(); |
42 } | 43 } |
44 eatWhitespace(); | |
43 } | 45 } |
44 | 46 |
45 bool SVGPathStringSource::hasMoreData() const | 47 bool SVGPathStringSource::hasMoreData() const |
46 { | 48 { |
47 if (m_is8BitSource) | 49 if (m_is8BitSource) |
48 return m_current.m_character8 < m_end.m_character8; | 50 return m_current.m_character8 < m_end.m_character8; |
49 return m_current.m_character16 < m_end.m_character16; | 51 return m_current.m_character16 < m_end.m_character16; |
50 } | 52 } |
51 | 53 |
52 bool SVGPathStringSource::moveToNextToken() | 54 void SVGPathStringSource::eatWhitespace() |
53 { | 55 { |
54 if (m_is8BitSource) | 56 if (m_is8BitSource) |
55 return skipOptionalSVGSpaces(m_current.m_character8, m_end.m_character8) ; | 57 skipOptionalSVGSpaces(m_current.m_character8, m_end.m_character8); |
56 return skipOptionalSVGSpaces(m_current.m_character16, m_end.m_character16); | 58 else |
59 skipOptionalSVGSpaces(m_current.m_character16, m_end.m_character16); | |
57 } | 60 } |
58 | 61 |
59 template <typename CharacterType> | 62 static SVGPathSegType parseSVGSegmentTypeHelper(unsigned lookahead) |
60 static bool parseSVGSegmentTypeHelper(const CharacterType*& current, SVGPathSegT ype& pathSegType) | |
61 { | 63 { |
62 switch (*(current++)) { | 64 switch (lookahead) { |
63 case 'Z': | 65 case 'Z': |
64 case 'z': | 66 case 'z': |
65 pathSegType = PathSegClosePath; | 67 return PathSegClosePath; |
66 break; | |
67 case 'M': | 68 case 'M': |
68 pathSegType = PathSegMoveToAbs; | 69 return PathSegMoveToAbs; |
69 break; | |
70 case 'm': | 70 case 'm': |
71 pathSegType = PathSegMoveToRel; | 71 return PathSegMoveToRel; |
72 break; | |
73 case 'L': | 72 case 'L': |
74 pathSegType = PathSegLineToAbs; | 73 return PathSegLineToAbs; |
75 break; | |
76 case 'l': | 74 case 'l': |
77 pathSegType = PathSegLineToRel; | 75 return PathSegLineToRel; |
78 break; | |
79 case 'C': | 76 case 'C': |
80 pathSegType = PathSegCurveToCubicAbs; | 77 return PathSegCurveToCubicAbs; |
81 break; | |
82 case 'c': | 78 case 'c': |
83 pathSegType = PathSegCurveToCubicRel; | 79 return PathSegCurveToCubicRel; |
84 break; | |
85 case 'Q': | 80 case 'Q': |
86 pathSegType = PathSegCurveToQuadraticAbs; | 81 return PathSegCurveToQuadraticAbs; |
87 break; | |
88 case 'q': | 82 case 'q': |
89 pathSegType = PathSegCurveToQuadraticRel; | 83 return PathSegCurveToQuadraticRel; |
90 break; | |
91 case 'A': | 84 case 'A': |
92 pathSegType = PathSegArcAbs; | 85 return PathSegArcAbs; |
93 break; | |
94 case 'a': | 86 case 'a': |
95 pathSegType = PathSegArcRel; | 87 return PathSegArcRel; |
96 break; | |
97 case 'H': | 88 case 'H': |
98 pathSegType = PathSegLineToHorizontalAbs; | 89 return PathSegLineToHorizontalAbs; |
99 break; | |
100 case 'h': | 90 case 'h': |
101 pathSegType = PathSegLineToHorizontalRel; | 91 return PathSegLineToHorizontalRel; |
102 break; | |
103 case 'V': | 92 case 'V': |
104 pathSegType = PathSegLineToVerticalAbs; | 93 return PathSegLineToVerticalAbs; |
105 break; | |
106 case 'v': | 94 case 'v': |
107 pathSegType = PathSegLineToVerticalRel; | 95 return PathSegLineToVerticalRel; |
108 break; | |
109 case 'S': | 96 case 'S': |
110 pathSegType = PathSegCurveToCubicSmoothAbs; | 97 return PathSegCurveToCubicSmoothAbs; |
111 break; | |
112 case 's': | 98 case 's': |
113 pathSegType = PathSegCurveToCubicSmoothRel; | 99 return PathSegCurveToCubicSmoothRel; |
114 break; | |
115 case 'T': | 100 case 'T': |
116 pathSegType = PathSegCurveToQuadraticSmoothAbs; | 101 return PathSegCurveToQuadraticSmoothAbs; |
117 break; | |
118 case 't': | 102 case 't': |
119 pathSegType = PathSegCurveToQuadraticSmoothRel; | 103 return PathSegCurveToQuadraticSmoothRel; |
120 break; | |
121 default: | 104 default: |
122 pathSegType = PathSegUnknown; | 105 return PathSegUnknown; |
123 } | 106 } |
124 return true; | |
125 } | 107 } |
126 | 108 |
127 bool SVGPathStringSource::parseSVGSegmentType(SVGPathSegType& pathSegType) | 109 static bool nextCommandHelper(unsigned lookahead, SVGPathSegType previousCommand , SVGPathSegType& nextCommand) |
128 { | |
129 if (m_is8BitSource) | |
130 return parseSVGSegmentTypeHelper(m_current.m_character8, pathSegType); | |
131 return parseSVGSegmentTypeHelper(m_current.m_character16, pathSegType); | |
132 } | |
133 | |
134 template <typename CharacterType> | |
135 static bool nextCommandHelper(const CharacterType*& current, SVGPathSegType prev iousCommand, SVGPathSegType& nextCommand) | |
136 { | 110 { |
137 // Check for remaining coordinates in the current command. | 111 // Check for remaining coordinates in the current command. |
138 if ((*current == '+' || *current == '-' || *current == '.' || (*current >= ' 0' && *current <= '9')) | 112 if ((lookahead == '+' || lookahead == '-' || lookahead == '.' || (lookahead >= '0' && lookahead <= '9')) |
139 && previousCommand != PathSegClosePath) { | 113 && previousCommand != PathSegClosePath) { |
140 if (previousCommand == PathSegMoveToAbs) { | 114 if (previousCommand == PathSegMoveToAbs) { |
141 nextCommand = PathSegLineToAbs; | 115 nextCommand = PathSegLineToAbs; |
142 return true; | 116 return true; |
143 } | 117 } |
144 if (previousCommand == PathSegMoveToRel) { | 118 if (previousCommand == PathSegMoveToRel) { |
145 nextCommand = PathSegLineToRel; | 119 nextCommand = PathSegLineToRel; |
146 return true; | 120 return true; |
147 } | 121 } |
148 nextCommand = previousCommand; | 122 nextCommand = previousCommand; |
149 return true; | 123 return true; |
150 } | 124 } |
151 | |
152 return false; | 125 return false; |
153 } | 126 } |
154 | 127 |
155 SVGPathSegType SVGPathStringSource::nextCommand(SVGPathSegType previousCommand) | 128 float SVGPathStringSource::parseNumberWithError() |
156 { | 129 { |
157 SVGPathSegType nextCommand; | 130 float numberValue = 0; |
158 if (m_is8BitSource) { | 131 if (m_is8BitSource) |
159 if (nextCommandHelper(m_current.m_character8, previousCommand, nextComma nd)) | 132 m_seenError |= !parseNumber(m_current.m_character8, m_end.m_character8, numberValue); |
160 return nextCommand; | 133 else |
134 m_seenError |= !parseNumber(m_current.m_character16, m_end.m_character16 , numberValue); | |
135 return numberValue; | |
136 } | |
137 | |
138 bool SVGPathStringSource::parseArcFlagWithError() | |
139 { | |
140 bool flagValue = false; | |
141 if (m_is8BitSource) | |
142 m_seenError |= !parseArcFlag(m_current.m_character8, m_end.m_character8, flagValue); | |
143 else | |
144 m_seenError |= !parseArcFlag(m_current.m_character16, m_end.m_character1 6, flagValue); | |
145 return flagValue; | |
146 } | |
147 | |
148 SVGPathSegType SVGPathStringSource::peekSegmentType() | |
149 { | |
150 ASSERT(hasMoreData()); | |
151 // This won't work in all cases because of the state required to "detect" im plicit commands. | |
152 unsigned lookahead = m_is8BitSource ? *m_current.m_character8 : *m_current.m _character16; | |
153 return parseSVGSegmentTypeHelper(lookahead); | |
154 } | |
155 | |
156 PathSegmentData SVGPathStringSource::parseSegment() | |
157 { | |
158 ASSERT(hasMoreData()); | |
159 PathSegmentData segment; | |
160 unsigned lookahead = m_is8BitSource ? *m_current.m_character8 : *m_current.m _character16; | |
161 SVGPathSegType command = parseSVGSegmentTypeHelper(lookahead); | |
162 if (command == PathSegUnknown) { | |
163 // Possibly an implicit command. Not allowed if this is the first comman d. | |
164 if (m_previousCommand == PathSegUnknown) | |
165 return segment; | |
166 if (!nextCommandHelper(lookahead, m_previousCommand, command)) | |
167 return segment; | |
161 } else { | 168 } else { |
162 if (nextCommandHelper(m_current.m_character16, previousCommand, nextComm and)) | 169 // Valid explicit command. |
163 return nextCommand; | 170 if (m_is8BitSource) |
171 m_current.m_character8++; | |
172 else | |
173 m_current.m_character16++; | |
164 } | 174 } |
165 | 175 |
166 parseSVGSegmentType(nextCommand); | 176 segment.command = m_previousCommand = command; |
167 return nextCommand; | |
168 } | |
169 | 177 |
170 bool SVGPathStringSource::parseMoveToSegment(FloatPoint& targetPoint) | 178 ASSERT(!m_seenError); |
Stephen Chennney
2015/03/20 19:40:55
This concerned me because I think of a command tha
fs
2015/03/23 11:22:09
Yes, error reporting here is not great (or readabl
| |
171 { | |
172 if (m_is8BitSource) | |
173 return parseFloatPoint(m_current.m_character8, m_end.m_character8, targe tPoint); | |
174 return parseFloatPoint(m_current.m_character16, m_end.m_character16, targetP oint); | |
175 } | |
176 | 179 |
177 bool SVGPathStringSource::parseLineToSegment(FloatPoint& targetPoint) | 180 switch (segment.command) { |
178 { | 181 case PathSegCurveToCubicRel: |
179 if (m_is8BitSource) | 182 case PathSegCurveToCubicAbs: |
180 return parseFloatPoint(m_current.m_character8, m_end.m_character8, targe tPoint); | 183 segment.point1.setX(parseNumberWithError()); |
181 return parseFloatPoint(m_current.m_character16, m_end.m_character16, targetP oint); | 184 segment.point1.setY(parseNumberWithError()); |
182 } | 185 /* fall through */ |
186 case PathSegCurveToCubicSmoothRel: | |
187 case PathSegCurveToCubicSmoothAbs: | |
188 segment.point2.setX(parseNumberWithError()); | |
189 segment.point2.setY(parseNumberWithError()); | |
190 /* fall through */ | |
191 case PathSegMoveToRel: | |
192 case PathSegMoveToAbs: | |
193 case PathSegLineToRel: | |
194 case PathSegLineToAbs: | |
195 case PathSegCurveToQuadraticSmoothRel: | |
196 case PathSegCurveToQuadraticSmoothAbs: | |
197 segment.targetPoint.setX(parseNumberWithError()); | |
198 segment.targetPoint.setY(parseNumberWithError()); | |
199 break; | |
200 case PathSegLineToHorizontalRel: | |
201 case PathSegLineToHorizontalAbs: | |
202 segment.targetPoint.setX(parseNumberWithError()); | |
203 break; | |
204 case PathSegLineToVerticalRel: | |
205 case PathSegLineToVerticalAbs: | |
206 segment.targetPoint.setY(parseNumberWithError()); | |
207 break; | |
208 case PathSegClosePath: | |
209 eatWhitespace(); | |
210 break; | |
211 case PathSegCurveToQuadraticRel: | |
212 case PathSegCurveToQuadraticAbs: | |
213 segment.point1.setX(parseNumberWithError()); | |
214 segment.point1.setY(parseNumberWithError()); | |
215 segment.targetPoint.setX(parseNumberWithError()); | |
216 segment.targetPoint.setY(parseNumberWithError()); | |
217 break; | |
218 case PathSegArcRel: | |
219 case PathSegArcAbs: | |
220 segment.point1.setX(parseNumberWithError()); // rx | |
221 segment.point1.setY(parseNumberWithError()); // ry | |
222 segment.point2.setX(parseNumberWithError()); // angle | |
223 segment.arcLarge = parseArcFlagWithError(); | |
224 segment.arcSweep = parseArcFlagWithError(); | |
225 segment.targetPoint.setX(parseNumberWithError()); | |
226 segment.targetPoint.setY(parseNumberWithError()); | |
227 break; | |
228 case PathSegUnknown: | |
229 ASSERT_NOT_REACHED(); | |
230 } | |
183 | 231 |
184 bool SVGPathStringSource::parseLineToHorizontalSegment(float& x) | 232 if (UNLIKELY(m_seenError)) |
185 { | 233 segment.command = PathSegUnknown; |
186 if (m_is8BitSource) | 234 return segment; |
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 | |
191 bool 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 | |
198 bool SVGPathStringSource::parseCurveToCubicSegment(FloatPoint& point1, FloatPoin t& point2, FloatPoint& targetPoint) | |
199 { | |
200 if (m_is8BitSource) | |
201 return parseFloatPoint3(m_current.m_character8, m_end.m_character8, poin t1, point2, targetPoint); | |
202 return parseFloatPoint3(m_current.m_character16, m_end.m_character16, point1 , point2, targetPoint); | |
203 } | |
204 | |
205 bool SVGPathStringSource::parseCurveToCubicSmoothSegment(FloatPoint& point1, Flo atPoint& targetPoint) | |
206 { | |
207 if (m_is8BitSource) | |
208 return parseFloatPoint2(m_current.m_character8, m_end.m_character8, poin t1, targetPoint); | |
209 return parseFloatPoint2(m_current.m_character16, m_end.m_character16, point1 , targetPoint); | |
210 } | |
211 | |
212 bool SVGPathStringSource::parseCurveToQuadraticSegment(FloatPoint& point2, Float Point& targetPoint) | |
213 { | |
214 if (m_is8BitSource) | |
215 return parseFloatPoint2(m_current.m_character8, m_end.m_character8, poin t2, targetPoint); | |
216 return parseFloatPoint2(m_current.m_character16, m_end.m_character16, point2 , targetPoint); | |
217 } | |
218 | |
219 bool SVGPathStringSource::parseCurveToQuadraticSmoothSegment(FloatPoint& targetP oint) | |
220 { | |
221 if (m_is8BitSource) | |
222 return parseFloatPoint(m_current.m_character8, m_end.m_character8, targe tPoint); | |
223 return parseFloatPoint(m_current.m_character16, m_end.m_character16, targetP oint); | |
224 } | |
225 | |
226 template <typename CharacterType> | |
227 static bool parseArcToSegmentHelper(const CharacterType*& current, const Charact erType* end, float& rx, float& ry, float& angle, bool& largeArc, bool& sweep, Fl oatPoint& 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 | |
243 bool 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_character 8, rx, ry, angle, largeArc, sweep, targetPoint); | |
247 return parseArcToSegmentHelper(m_current.m_character16, m_end.m_character16, rx, ry, angle, largeArc, sweep, targetPoint); | |
248 } | 235 } |
249 | 236 |
250 } // namespace blink | 237 } // namespace blink |
OLD | NEW |