AttributeParser.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. /*
  2. * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
  3. * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "AttributeParser.h"
  8. #include <AK/StringBuilder.h>
  9. #include <ctype.h>
  10. namespace Web::SVG {
  11. AttributeParser::AttributeParser(String source)
  12. : m_source(move(source))
  13. {
  14. }
  15. Vector<PathInstruction> AttributeParser::parse_path_data()
  16. {
  17. parse_whitespace();
  18. while (!done())
  19. parse_drawto();
  20. if (!m_instructions.is_empty() && m_instructions[0].type != PathInstructionType::Move)
  21. VERIFY_NOT_REACHED();
  22. return m_instructions;
  23. }
  24. Optional<float> AttributeParser::parse_coordinate(StringView input)
  25. {
  26. AttributeParser parser { input };
  27. parser.parse_whitespace();
  28. if (parser.match_coordinate()) {
  29. float result = parser.parse_coordinate();
  30. parser.parse_whitespace();
  31. if (parser.done())
  32. return result;
  33. }
  34. return {};
  35. }
  36. Optional<float> AttributeParser::parse_length(StringView input)
  37. {
  38. AttributeParser parser { input };
  39. parser.parse_whitespace();
  40. if (parser.match_coordinate()) {
  41. float result = parser.parse_length();
  42. parser.parse_whitespace();
  43. if (parser.done())
  44. return result;
  45. }
  46. return {};
  47. }
  48. Optional<float> AttributeParser::parse_positive_length(StringView input)
  49. {
  50. // FIXME: Where this is used, the spec usually (always?) says "A negative value is an error (see Error processing)."
  51. // So, implement error processing! Maybe this should return ErrorOr.
  52. auto result = parse_length(input);
  53. if (result.has_value() && result.value() < 0)
  54. result.clear();
  55. return result;
  56. }
  57. void AttributeParser::parse_drawto()
  58. {
  59. if (match('M') || match('m')) {
  60. parse_moveto();
  61. } else if (match('Z') || match('z')) {
  62. parse_closepath();
  63. } else if (match('L') || match('l')) {
  64. parse_lineto();
  65. } else if (match('H') || match('h')) {
  66. parse_horizontal_lineto();
  67. } else if (match('V') || match('v')) {
  68. parse_vertical_lineto();
  69. } else if (match('C') || match('c')) {
  70. parse_curveto();
  71. } else if (match('S') || match('s')) {
  72. parse_smooth_curveto();
  73. } else if (match('Q') || match('q')) {
  74. parse_quadratic_bezier_curveto();
  75. } else if (match('T') || match('t')) {
  76. parse_smooth_quadratic_bezier_curveto();
  77. } else if (match('A') || match('a')) {
  78. parse_elliptical_arc();
  79. } else {
  80. dbgln("AttributeParser::parse_drawto failed to match: '{}'", ch());
  81. TODO();
  82. }
  83. }
  84. void AttributeParser::parse_moveto()
  85. {
  86. bool absolute = consume() == 'M';
  87. parse_whitespace();
  88. for (auto& pair : parse_coordinate_pair_sequence())
  89. m_instructions.append({ PathInstructionType::Move, absolute, pair });
  90. }
  91. void AttributeParser::parse_closepath()
  92. {
  93. bool absolute = consume() == 'Z';
  94. parse_whitespace();
  95. m_instructions.append({ PathInstructionType::ClosePath, absolute, {} });
  96. }
  97. void AttributeParser::parse_lineto()
  98. {
  99. bool absolute = consume() == 'L';
  100. parse_whitespace();
  101. for (auto& pair : parse_coordinate_pair_sequence())
  102. m_instructions.append({ PathInstructionType::Line, absolute, pair });
  103. }
  104. void AttributeParser::parse_horizontal_lineto()
  105. {
  106. bool absolute = consume() == 'H';
  107. parse_whitespace();
  108. m_instructions.append({ PathInstructionType::HorizontalLine, absolute, parse_coordinate_sequence() });
  109. }
  110. void AttributeParser::parse_vertical_lineto()
  111. {
  112. bool absolute = consume() == 'V';
  113. parse_whitespace();
  114. m_instructions.append({ PathInstructionType::VerticalLine, absolute, parse_coordinate_sequence() });
  115. }
  116. void AttributeParser::parse_curveto()
  117. {
  118. bool absolute = consume() == 'C';
  119. parse_whitespace();
  120. while (true) {
  121. m_instructions.append({ PathInstructionType::Curve, absolute, parse_coordinate_pair_triplet() });
  122. if (match_comma_whitespace())
  123. parse_comma_whitespace();
  124. if (!match_coordinate())
  125. break;
  126. }
  127. }
  128. void AttributeParser::parse_smooth_curveto()
  129. {
  130. bool absolute = consume() == 'S';
  131. parse_whitespace();
  132. while (true) {
  133. m_instructions.append({ PathInstructionType::SmoothCurve, absolute, parse_coordinate_pair_double() });
  134. if (match_comma_whitespace())
  135. parse_comma_whitespace();
  136. if (!match_coordinate())
  137. break;
  138. }
  139. }
  140. void AttributeParser::parse_quadratic_bezier_curveto()
  141. {
  142. bool absolute = consume() == 'Q';
  143. parse_whitespace();
  144. while (true) {
  145. m_instructions.append({ PathInstructionType::QuadraticBezierCurve, absolute, parse_coordinate_pair_double() });
  146. if (match_comma_whitespace())
  147. parse_comma_whitespace();
  148. if (!match_coordinate())
  149. break;
  150. }
  151. }
  152. void AttributeParser::parse_smooth_quadratic_bezier_curveto()
  153. {
  154. bool absolute = consume() == 'T';
  155. parse_whitespace();
  156. while (true) {
  157. m_instructions.append({ PathInstructionType::SmoothQuadraticBezierCurve, absolute, parse_coordinate_pair() });
  158. if (match_comma_whitespace())
  159. parse_comma_whitespace();
  160. if (!match_coordinate())
  161. break;
  162. }
  163. }
  164. void AttributeParser::parse_elliptical_arc()
  165. {
  166. bool absolute = consume() == 'A';
  167. parse_whitespace();
  168. while (true) {
  169. m_instructions.append({ PathInstructionType::EllipticalArc, absolute, parse_elliptical_arg_argument() });
  170. if (match_comma_whitespace())
  171. parse_comma_whitespace();
  172. if (!match_coordinate())
  173. break;
  174. }
  175. }
  176. float AttributeParser::parse_length()
  177. {
  178. return parse_sign() * parse_number();
  179. }
  180. float AttributeParser::parse_coordinate()
  181. {
  182. // https://www.w3.org/TR/SVG11/types.html#DataTypeCoordinate
  183. // coordinate ::= length
  184. return parse_length();
  185. }
  186. Vector<float> AttributeParser::parse_coordinate_pair()
  187. {
  188. Vector<float> coordinates;
  189. coordinates.append(parse_coordinate());
  190. if (match_comma_whitespace())
  191. parse_comma_whitespace();
  192. coordinates.append(parse_coordinate());
  193. return coordinates;
  194. }
  195. Vector<float> AttributeParser::parse_coordinate_sequence()
  196. {
  197. Vector<float> sequence;
  198. while (true) {
  199. sequence.append(parse_coordinate());
  200. if (match_comma_whitespace())
  201. parse_comma_whitespace();
  202. if (!match_comma_whitespace() && !match_coordinate())
  203. break;
  204. }
  205. return sequence;
  206. }
  207. Vector<Vector<float>> AttributeParser::parse_coordinate_pair_sequence()
  208. {
  209. Vector<Vector<float>> sequence;
  210. while (true) {
  211. sequence.append(parse_coordinate_pair());
  212. if (match_comma_whitespace())
  213. parse_comma_whitespace();
  214. if (!match_comma_whitespace() && !match_coordinate())
  215. break;
  216. }
  217. return sequence;
  218. }
  219. Vector<float> AttributeParser::parse_coordinate_pair_double()
  220. {
  221. Vector<float> coordinates;
  222. coordinates.extend(parse_coordinate_pair());
  223. if (match_comma_whitespace())
  224. parse_comma_whitespace();
  225. coordinates.extend(parse_coordinate_pair());
  226. return coordinates;
  227. }
  228. Vector<float> AttributeParser::parse_coordinate_pair_triplet()
  229. {
  230. Vector<float> coordinates;
  231. coordinates.extend(parse_coordinate_pair());
  232. if (match_comma_whitespace())
  233. parse_comma_whitespace();
  234. coordinates.extend(parse_coordinate_pair());
  235. if (match_comma_whitespace())
  236. parse_comma_whitespace();
  237. coordinates.extend(parse_coordinate_pair());
  238. return coordinates;
  239. }
  240. Vector<float> AttributeParser::parse_elliptical_arg_argument()
  241. {
  242. Vector<float> numbers;
  243. numbers.append(parse_number());
  244. if (match_comma_whitespace())
  245. parse_comma_whitespace();
  246. numbers.append(parse_number());
  247. if (match_comma_whitespace())
  248. parse_comma_whitespace();
  249. numbers.append(parse_number());
  250. parse_comma_whitespace();
  251. numbers.append(parse_flag());
  252. if (match_comma_whitespace())
  253. parse_comma_whitespace();
  254. numbers.append(parse_flag());
  255. if (match_comma_whitespace())
  256. parse_comma_whitespace();
  257. numbers.extend(parse_coordinate_pair());
  258. return numbers;
  259. }
  260. void AttributeParser::parse_whitespace(bool must_match_once)
  261. {
  262. bool matched = false;
  263. while (!done() && match_whitespace()) {
  264. consume();
  265. matched = true;
  266. }
  267. VERIFY(!must_match_once || matched);
  268. }
  269. void AttributeParser::parse_comma_whitespace()
  270. {
  271. if (match(',')) {
  272. consume();
  273. parse_whitespace();
  274. } else {
  275. parse_whitespace(1);
  276. if (match(','))
  277. consume();
  278. parse_whitespace();
  279. }
  280. }
  281. float AttributeParser::parse_fractional_constant()
  282. {
  283. StringBuilder builder;
  284. bool floating_point = false;
  285. while (!done() && isdigit(ch()))
  286. builder.append(consume());
  287. if (match('.')) {
  288. floating_point = true;
  289. builder.append('.');
  290. consume();
  291. while (!done() && isdigit(ch()))
  292. builder.append(consume());
  293. } else {
  294. VERIFY(builder.length() > 0);
  295. }
  296. if (floating_point)
  297. return strtof(builder.to_string().characters(), nullptr);
  298. return builder.to_string().to_int().value();
  299. }
  300. float AttributeParser::parse_number()
  301. {
  302. auto number = parse_fractional_constant();
  303. if (!match('e') && !match('E'))
  304. return number;
  305. consume();
  306. auto exponent_sign = parse_sign();
  307. StringBuilder exponent_builder;
  308. while (!done() && isdigit(ch()))
  309. exponent_builder.append(consume());
  310. VERIFY(exponent_builder.length() > 0);
  311. auto exponent = exponent_builder.to_string().to_int().value();
  312. // Fast path: If the number is 0, there's no point in computing the exponentiation.
  313. if (number == 0)
  314. return number;
  315. if (exponent_sign < 0) {
  316. for (int i = 0; i < exponent; ++i) {
  317. number /= 10;
  318. }
  319. } else if (exponent_sign > 0) {
  320. for (int i = 0; i < exponent; ++i) {
  321. number *= 10;
  322. }
  323. }
  324. return number;
  325. }
  326. float AttributeParser::parse_flag()
  327. {
  328. if (!match('0') && !match('1'))
  329. VERIFY_NOT_REACHED();
  330. return consume() - '0';
  331. }
  332. int AttributeParser::parse_sign()
  333. {
  334. if (match('-')) {
  335. consume();
  336. return -1;
  337. }
  338. if (match('+'))
  339. consume();
  340. return 1;
  341. }
  342. bool AttributeParser::match_whitespace() const
  343. {
  344. if (done())
  345. return false;
  346. char c = ch();
  347. return c == 0x9 || c == 0x20 || c == 0xa || c == 0xc || c == 0xd;
  348. }
  349. bool AttributeParser::match_comma_whitespace() const
  350. {
  351. return match_whitespace() || match(',');
  352. }
  353. bool AttributeParser::match_coordinate() const
  354. {
  355. return match_length();
  356. }
  357. bool AttributeParser::match_length() const
  358. {
  359. return !done() && (isdigit(ch()) || ch() == '-' || ch() == '+' || ch() == '.');
  360. }
  361. }