Chromium Code Reviews| 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 |