| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2021-04-22 16:53:07 -07:00
										 |  |  |  * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org> | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-04-22 01:24:48 -07:00
										 |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-17 18:17:00 +01:00
										 |  |  | #include <AK/Debug.h>
 | 
					
						
							| 
									
										
										
										
											2021-04-25 21:10:55 +02:00
										 |  |  | #include <AK/ExtraMathConstants.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | #include <AK/StringBuilder.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-23 09:44:42 -07:00
										 |  |  | #include <LibGfx/Painter.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | #include <LibGfx/Path.h>
 | 
					
						
							|  |  |  | #include <LibWeb/DOM/Document.h>
 | 
					
						
							|  |  |  | #include <LibWeb/DOM/Event.h>
 | 
					
						
							| 
									
										
										
										
											2020-11-22 15:53:01 +01:00
										 |  |  | #include <LibWeb/Layout/SVGPathBox.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-23 09:44:42 -07:00
										 |  |  | #include <LibWeb/SVG/SVGPathElement.h>
 | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | #include <ctype.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 09:44:42 -07:00
										 |  |  | namespace Web::SVG { | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-05 18:08:48 +02:00
										 |  |  | [[maybe_unused]] static void print_instruction(const PathInstruction& instruction) | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(PATH_DEBUG); | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |     auto& data = instruction.data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (instruction.type) { | 
					
						
							|  |  |  |     case PathInstructionType::Move: | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |         dbgln("Move (absolute={})", instruction.absolute); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         for (size_t i = 0; i < data.size(); i += 2) | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |             dbgln("    x={}, y={}", data[i], data[i + 1]); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case PathInstructionType::ClosePath: | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |         dbgln("ClosePath (absolute={})", instruction.absolute); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case PathInstructionType::Line: | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |         dbgln("Line (absolute={})", instruction.absolute); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         for (size_t i = 0; i < data.size(); i += 2) | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |             dbgln("    x={}, y={}", data[i], data[i + 1]); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case PathInstructionType::HorizontalLine: | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |         dbgln("HorizontalLine (absolute={})", instruction.absolute); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         for (size_t i = 0; i < data.size(); ++i) | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |             dbgln("    x={}", data[i]); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case PathInstructionType::VerticalLine: | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |         dbgln("VerticalLine (absolute={})", instruction.absolute); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         for (size_t i = 0; i < data.size(); ++i) | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |             dbgln("    y={}", data[i]); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case PathInstructionType::Curve: | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |         dbgln("Curve (absolute={})", instruction.absolute); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         for (size_t i = 0; i < data.size(); i += 6) | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |             dbgln("    (x1={}, y1={}, x2={}, y2={}), (x={}, y={})", data[i], data[i + 1], data[i + 2], data[i + 3], data[i + 4], data[i + 5]); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case PathInstructionType::SmoothCurve: | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |         dbgln("SmoothCurve (absolute={})", instruction.absolute); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         for (size_t i = 0; i < data.size(); i += 4) | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |             dbgln("    (x2={}, y2={}), (x={}, y={})", data[i], data[i + 1], data[i + 2], data[i + 3]); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case PathInstructionType::QuadraticBezierCurve: | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |         dbgln("QuadraticBezierCurve (absolute={})", instruction.absolute); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         for (size_t i = 0; i < data.size(); i += 4) | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |             dbgln("    (x1={}, y1={}), (x={}, y={})", data[i], data[i + 1], data[i + 2], data[i + 3]); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case PathInstructionType::SmoothQuadraticBezierCurve: | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |         dbgln("SmoothQuadraticBezierCurve (absolute={})", instruction.absolute); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         for (size_t i = 0; i < data.size(); i += 2) | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |             dbgln("    x={}, y={}", data[i], data[i + 1]); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case PathInstructionType::EllipticalArc: | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |         dbgln("EllipticalArc (absolute={})", instruction.absolute); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         for (size_t i = 0; i < data.size(); i += 7) | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |             dbgln("    (rx={}, ry={}) x-axis-rotation={}, large-arc-flag={}, sweep-flag={}, (x={}, y={})", | 
					
						
							|  |  |  |                 data[i], | 
					
						
							|  |  |  |                 data[i + 1], | 
					
						
							|  |  |  |                 data[i + 2], | 
					
						
							|  |  |  |                 data[i + 3], | 
					
						
							|  |  |  |                 data[i + 4], | 
					
						
							|  |  |  |                 data[i + 5], | 
					
						
							|  |  |  |                 data[i + 6]); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         break; | 
					
						
							|  |  |  |     case PathInstructionType::Invalid: | 
					
						
							| 
									
										
										
										
											2021-01-09 18:51:44 +01:00
										 |  |  |         dbgln("Invalid"); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | PathDataParser::PathDataParser(const String& source) | 
					
						
							|  |  |  |     : m_source(source) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Vector<PathInstruction> PathDataParser::parse() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     parse_whitespace(); | 
					
						
							|  |  |  |     while (!done()) | 
					
						
							|  |  |  |         parse_drawto(); | 
					
						
							|  |  |  |     if (!m_instructions.is_empty() && m_instructions[0].type != PathInstructionType::Move) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     return m_instructions; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-18 09:49:51 +02:00
										 |  |  | void PathDataParser::parse_drawto() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     if (match('M') || match('m')) { | 
					
						
							|  |  |  |         parse_moveto(); | 
					
						
							|  |  |  |     } else if (match('Z') || match('z')) { | 
					
						
							|  |  |  |         parse_closepath(); | 
					
						
							|  |  |  |     } else if (match('L') || match('l')) { | 
					
						
							|  |  |  |         parse_lineto(); | 
					
						
							|  |  |  |     } else if (match('H') || match('h')) { | 
					
						
							|  |  |  |         parse_horizontal_lineto(); | 
					
						
							|  |  |  |     } else if (match('V') || match('v')) { | 
					
						
							|  |  |  |         parse_vertical_lineto(); | 
					
						
							|  |  |  |     } else if (match('C') || match('c')) { | 
					
						
							|  |  |  |         parse_curveto(); | 
					
						
							|  |  |  |     } else if (match('S') || match('s')) { | 
					
						
							|  |  |  |         parse_smooth_curveto(); | 
					
						
							|  |  |  |     } else if (match('Q') || match('q')) { | 
					
						
							|  |  |  |         parse_quadratic_bezier_curveto(); | 
					
						
							|  |  |  |     } else if (match('T') || match('t')) { | 
					
						
							|  |  |  |         parse_smooth_quadratic_bezier_curveto(); | 
					
						
							|  |  |  |     } else if (match('A') || match('a')) { | 
					
						
							|  |  |  |         parse_elliptical_arc(); | 
					
						
							| 
									
										
										
										
											2020-08-19 22:35:31 +01:00
										 |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2021-01-17 18:17:00 +01:00
										 |  |  |         dbgln("PathDataParser::parse_drawto failed to match: '{}'", ch()); | 
					
						
							| 
									
										
										
										
											2020-08-19 22:35:31 +01:00
										 |  |  |         TODO(); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_moveto() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool absolute = consume() == 'M'; | 
					
						
							|  |  |  |     parse_whitespace(); | 
					
						
							|  |  |  |     for (auto& pair : parse_coordinate_pair_sequence()) | 
					
						
							|  |  |  |         m_instructions.append({ PathInstructionType::Move, absolute, pair }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_closepath() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool absolute = consume() == 'Z'; | 
					
						
							| 
									
										
										
										
											2020-09-07 20:50:31 +02:00
										 |  |  |     parse_whitespace(); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     m_instructions.append({ PathInstructionType::ClosePath, absolute, {} }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_lineto() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool absolute = consume() == 'L'; | 
					
						
							|  |  |  |     parse_whitespace(); | 
					
						
							|  |  |  |     for (auto& pair : parse_coordinate_pair_sequence()) | 
					
						
							|  |  |  |         m_instructions.append({ PathInstructionType::Line, absolute, pair }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_horizontal_lineto() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool absolute = consume() == 'H'; | 
					
						
							|  |  |  |     parse_whitespace(); | 
					
						
							|  |  |  |     m_instructions.append({ PathInstructionType::HorizontalLine, absolute, parse_coordinate_sequence() }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_vertical_lineto() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool absolute = consume() == 'V'; | 
					
						
							|  |  |  |     parse_whitespace(); | 
					
						
							|  |  |  |     m_instructions.append({ PathInstructionType::VerticalLine, absolute, parse_coordinate_sequence() }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_curveto() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool absolute = consume() == 'C'; | 
					
						
							|  |  |  |     parse_whitespace(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (true) { | 
					
						
							|  |  |  |         m_instructions.append({ PathInstructionType::Curve, absolute, parse_coordinate_pair_triplet() }); | 
					
						
							| 
									
										
										
										
											2020-09-07 20:50:31 +02:00
										 |  |  |         if (match_comma_whitespace()) | 
					
						
							|  |  |  |             parse_comma_whitespace(); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         if (!match_coordinate()) | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_smooth_curveto() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool absolute = consume() == 'S'; | 
					
						
							|  |  |  |     parse_whitespace(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (true) { | 
					
						
							|  |  |  |         m_instructions.append({ PathInstructionType::SmoothCurve, absolute, parse_coordinate_pair_double() }); | 
					
						
							| 
									
										
										
										
											2020-09-07 20:50:31 +02:00
										 |  |  |         if (match_comma_whitespace()) | 
					
						
							|  |  |  |             parse_comma_whitespace(); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         if (!match_coordinate()) | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_quadratic_bezier_curveto() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool absolute = consume() == 'Q'; | 
					
						
							|  |  |  |     parse_whitespace(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (true) { | 
					
						
							|  |  |  |         m_instructions.append({ PathInstructionType::QuadraticBezierCurve, absolute, parse_coordinate_pair_double() }); | 
					
						
							| 
									
										
										
										
											2020-09-07 20:50:31 +02:00
										 |  |  |         if (match_comma_whitespace()) | 
					
						
							|  |  |  |             parse_comma_whitespace(); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         if (!match_coordinate()) | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_smooth_quadratic_bezier_curveto() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool absolute = consume() == 'T'; | 
					
						
							|  |  |  |     parse_whitespace(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (true) { | 
					
						
							| 
									
										
										
										
											2020-09-07 19:46:20 +02:00
										 |  |  |         m_instructions.append({ PathInstructionType::SmoothQuadraticBezierCurve, absolute, parse_coordinate_pair() }); | 
					
						
							| 
									
										
										
										
											2020-09-07 20:50:31 +02:00
										 |  |  |         if (match_comma_whitespace()) | 
					
						
							|  |  |  |             parse_comma_whitespace(); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         if (!match_coordinate()) | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_elliptical_arc() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool absolute = consume() == 'A'; | 
					
						
							|  |  |  |     parse_whitespace(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (true) { | 
					
						
							|  |  |  |         m_instructions.append({ PathInstructionType::EllipticalArc, absolute, parse_elliptical_arg_argument() }); | 
					
						
							| 
									
										
										
										
											2020-09-07 20:50:31 +02:00
										 |  |  |         if (match_comma_whitespace()) | 
					
						
							|  |  |  |             parse_comma_whitespace(); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         if (!match_coordinate()) | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | float PathDataParser::parse_coordinate() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |     return parse_sign() * parse_number(); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Vector<float> PathDataParser::parse_coordinate_pair() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Vector<float> coordinates; | 
					
						
							|  |  |  |     coordinates.append(parse_coordinate()); | 
					
						
							|  |  |  |     if (match_comma_whitespace()) | 
					
						
							|  |  |  |         parse_comma_whitespace(); | 
					
						
							|  |  |  |     coordinates.append(parse_coordinate()); | 
					
						
							|  |  |  |     return coordinates; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Vector<float> PathDataParser::parse_coordinate_sequence() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Vector<float> sequence; | 
					
						
							|  |  |  |     while (true) { | 
					
						
							|  |  |  |         sequence.append(parse_coordinate()); | 
					
						
							|  |  |  |         if (match_comma_whitespace()) | 
					
						
							|  |  |  |             parse_comma_whitespace(); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         if (!match_comma_whitespace() && !match_coordinate()) | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return sequence; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Vector<Vector<float>> PathDataParser::parse_coordinate_pair_sequence() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Vector<Vector<float>> sequence; | 
					
						
							|  |  |  |     while (true) { | 
					
						
							|  |  |  |         sequence.append(parse_coordinate_pair()); | 
					
						
							|  |  |  |         if (match_comma_whitespace()) | 
					
						
							|  |  |  |             parse_comma_whitespace(); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |         if (!match_comma_whitespace() && !match_coordinate()) | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return sequence; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Vector<float> PathDataParser::parse_coordinate_pair_double() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Vector<float> coordinates; | 
					
						
							| 
									
										
										
										
											2021-06-12 13:24:45 +02:00
										 |  |  |     coordinates.extend(parse_coordinate_pair()); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     if (match_comma_whitespace()) | 
					
						
							|  |  |  |         parse_comma_whitespace(); | 
					
						
							| 
									
										
										
										
											2021-06-12 13:24:45 +02:00
										 |  |  |     coordinates.extend(parse_coordinate_pair()); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     return coordinates; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Vector<float> PathDataParser::parse_coordinate_pair_triplet() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Vector<float> coordinates; | 
					
						
							| 
									
										
										
										
											2021-06-12 13:24:45 +02:00
										 |  |  |     coordinates.extend(parse_coordinate_pair()); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     if (match_comma_whitespace()) | 
					
						
							|  |  |  |         parse_comma_whitespace(); | 
					
						
							| 
									
										
										
										
											2021-06-12 13:24:45 +02:00
										 |  |  |     coordinates.extend(parse_coordinate_pair()); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     if (match_comma_whitespace()) | 
					
						
							|  |  |  |         parse_comma_whitespace(); | 
					
						
							| 
									
										
										
										
											2021-06-12 13:24:45 +02:00
										 |  |  |     coordinates.extend(parse_coordinate_pair()); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     return coordinates; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Vector<float> PathDataParser::parse_elliptical_arg_argument() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     Vector<float> numbers; | 
					
						
							|  |  |  |     numbers.append(parse_number()); | 
					
						
							|  |  |  |     if (match_comma_whitespace()) | 
					
						
							|  |  |  |         parse_comma_whitespace(); | 
					
						
							|  |  |  |     numbers.append(parse_number()); | 
					
						
							|  |  |  |     if (match_comma_whitespace()) | 
					
						
							|  |  |  |         parse_comma_whitespace(); | 
					
						
							|  |  |  |     numbers.append(parse_number()); | 
					
						
							|  |  |  |     parse_comma_whitespace(); | 
					
						
							|  |  |  |     numbers.append(parse_flag()); | 
					
						
							|  |  |  |     if (match_comma_whitespace()) | 
					
						
							|  |  |  |         parse_comma_whitespace(); | 
					
						
							|  |  |  |     numbers.append(parse_flag()); | 
					
						
							|  |  |  |     if (match_comma_whitespace()) | 
					
						
							|  |  |  |         parse_comma_whitespace(); | 
					
						
							| 
									
										
										
										
											2021-06-12 13:24:45 +02:00
										 |  |  |     numbers.extend(parse_coordinate_pair()); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return numbers; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_whitespace(bool must_match_once) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     bool matched = false; | 
					
						
							|  |  |  |     while (!done() && match_whitespace()) { | 
					
						
							|  |  |  |         consume(); | 
					
						
							|  |  |  |         matched = true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |     VERIFY(!must_match_once || matched); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void PathDataParser::parse_comma_whitespace() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (match(',')) { | 
					
						
							|  |  |  |         consume(); | 
					
						
							|  |  |  |         parse_whitespace(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         parse_whitespace(1); | 
					
						
							|  |  |  |         if (match(',')) | 
					
						
							|  |  |  |             consume(); | 
					
						
							|  |  |  |         parse_whitespace(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | float PathDataParser::parse_fractional_constant() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     StringBuilder builder; | 
					
						
							|  |  |  |     bool floating_point = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while (!done() && isdigit(ch())) | 
					
						
							|  |  |  |         builder.append(consume()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (match('.')) { | 
					
						
							|  |  |  |         floating_point = true; | 
					
						
							|  |  |  |         builder.append('.'); | 
					
						
							|  |  |  |         consume(); | 
					
						
							|  |  |  |         while (!done() && isdigit(ch())) | 
					
						
							|  |  |  |             builder.append(consume()); | 
					
						
							|  |  |  |     } else { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY(builder.length() > 0); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (floating_point) | 
					
						
							|  |  |  |         return strtof(builder.to_string().characters(), nullptr); | 
					
						
							|  |  |  |     return builder.to_string().to_int().value(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | float PathDataParser::parse_number() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     auto number = parse_fractional_constant(); | 
					
						
							| 
									
										
										
										
											2021-04-21 16:03:01 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if (!match('e') && !match('E')) | 
					
						
							|  |  |  |         return number; | 
					
						
							|  |  |  |     consume(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto exponent_sign = parse_sign(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     StringBuilder exponent_builder; | 
					
						
							|  |  |  |     while (!done() && isdigit(ch())) | 
					
						
							|  |  |  |         exponent_builder.append(consume()); | 
					
						
							|  |  |  |     VERIFY(exponent_builder.length() > 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto exponent = exponent_builder.to_string().to_int().value(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Fast path: If the number is 0, there's no point in computing the exponentiation.
 | 
					
						
							|  |  |  |     if (number == 0) | 
					
						
							|  |  |  |         return number; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (exponent_sign < 0) { | 
					
						
							|  |  |  |         for (int i = 0; i < exponent; ++i) { | 
					
						
							|  |  |  |             number /= 10; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } else if (exponent_sign > 0) { | 
					
						
							|  |  |  |         for (int i = 0; i < exponent; ++i) { | 
					
						
							|  |  |  |             number *= 10; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |     return number; | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | float PathDataParser::parse_flag() | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |     if (!match('0') && !match('1')) | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |         VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |     return consume() - '0'; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int PathDataParser::parse_sign() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (match('-')) { | 
					
						
							|  |  |  |         consume(); | 
					
						
							|  |  |  |         return -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (match('+')) | 
					
						
							|  |  |  |         consume(); | 
					
						
							|  |  |  |     return 1; | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool PathDataParser::match_whitespace() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (done()) | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     char c = ch(); | 
					
						
							|  |  |  |     return c == 0x9 || c == 0x20 || c == 0xa || c == 0xc || c == 0xd; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool PathDataParser::match_comma_whitespace() const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     return match_whitespace() || match(','); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  | bool PathDataParser::match_coordinate() const | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |     return !done() && (isdigit(ch()) || ch() == '-' || ch() == '+' || ch() == '.'); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-07 11:20:15 +01:00
										 |  |  | SVGPathElement::SVGPathElement(DOM::Document& document, QualifiedName qualified_name) | 
					
						
							|  |  |  |     : SVGGeometryElement(document, move(qualified_name)) | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-06 14:27:40 +01:00
										 |  |  | RefPtr<Layout::Node> SVGPathElement::create_layout_node() | 
					
						
							| 
									
										
										
										
											2020-10-05 16:14:36 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-09-24 13:49:57 +02:00
										 |  |  |     auto style = document().style_computer().compute_style(*this); | 
					
						
							| 
									
										
										
										
											2020-10-05 16:14:36 -07:00
										 |  |  |     if (style->display() == CSS::Display::None) | 
					
						
							|  |  |  |         return nullptr; | 
					
						
							| 
									
										
										
										
											2021-04-23 16:46:57 +02:00
										 |  |  |     return adopt_ref(*new Layout::SVGPathBox(document(), *this, move(style))); | 
					
						
							| 
									
										
										
										
											2020-10-05 16:14:36 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-23 09:44:42 -07:00
										 |  |  | void SVGPathElement::parse_attribute(const FlyString& name, const String& value) | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-07-23 09:44:42 -07:00
										 |  |  |     SVGGeometryElement::parse_attribute(name, value); | 
					
						
							| 
									
										
										
										
											2020-07-22 15:17:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     if (name == "d") | 
					
						
							|  |  |  |         m_instructions = PathDataParser(value).parse(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-05 16:14:36 -07:00
										 |  |  | Gfx::Path& SVGPathElement::get_path() | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2020-10-05 16:14:36 -07:00
										 |  |  |     if (m_path.has_value()) | 
					
						
							|  |  |  |         return m_path.value(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     Gfx::Path path; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (auto& instruction : m_instructions) { | 
					
						
							|  |  |  |         auto& absolute = instruction.absolute; | 
					
						
							|  |  |  |         auto& data = instruction.data; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-23 23:59:27 +01:00
										 |  |  |         if constexpr (PATH_DEBUG) { | 
					
						
							| 
									
										
										
										
											2021-01-18 17:25:44 +01:00
										 |  |  |             print_instruction(instruction); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-07 22:58:47 +02:00
										 |  |  |         bool clear_last_control_point = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |         switch (instruction.type) { | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |         case PathInstructionType::Move: { | 
					
						
							|  |  |  |             Gfx::FloatPoint point = { data[0], data[1] }; | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             if (absolute) { | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |                 path.move_to(point); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |                 VERIFY(!path.segments().is_empty()); | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |                 path.move_to(point + path.segments().last().point()); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |         case PathInstructionType::ClosePath: | 
					
						
							|  |  |  |             path.close(); | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |         case PathInstructionType::Line: { | 
					
						
							|  |  |  |             Gfx::FloatPoint point = { data[0], data[1] }; | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             if (absolute) { | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |                 path.line_to(point); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |                 VERIFY(!path.segments().is_empty()); | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |                 path.line_to(point + path.segments().last().point()); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |         case PathInstructionType::HorizontalLine: { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |             VERIFY(!path.segments().is_empty()); | 
					
						
							| 
									
										
										
										
											2020-07-21 23:46:39 -07:00
										 |  |  |             auto last_point = path.segments().last().point(); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             if (absolute) { | 
					
						
							|  |  |  |                 path.line_to(Gfx::FloatPoint { data[0], last_point.y() }); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 path.line_to(Gfx::FloatPoint { data[0] + last_point.x(), last_point.y() }); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         case PathInstructionType::VerticalLine: { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |             VERIFY(!path.segments().is_empty()); | 
					
						
							| 
									
										
										
										
											2020-07-21 23:46:39 -07:00
										 |  |  |             auto last_point = path.segments().last().point(); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             if (absolute) { | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |                 path.line_to(Gfx::FloatPoint { last_point.x(), data[0] }); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |                 path.line_to(Gfx::FloatPoint { last_point.x(), data[0] + last_point.y() }); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-21 23:46:39 -07:00
										 |  |  |         case PathInstructionType::EllipticalArc: { | 
					
						
							|  |  |  |             double rx = data[0]; | 
					
						
							|  |  |  |             double ry = data[1]; | 
					
						
							| 
									
										
										
										
											2021-04-15 00:36:14 -07:00
										 |  |  |             double x_axis_rotation = double { data[2] } * M_DEG2RAD; | 
					
						
							| 
									
										
										
										
											2020-07-21 23:46:39 -07:00
										 |  |  |             double large_arc_flag = data[3]; | 
					
						
							|  |  |  |             double sweep_flag = data[4]; | 
					
						
							|  |  |  |             auto& last_point = path.segments().last().point(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Gfx::FloatPoint next_point; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (absolute) { | 
					
						
							|  |  |  |                 next_point = { data[5], data[6] }; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 next_point = { data[5] + last_point.x(), data[6] + last_point.y() }; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-15 03:56:04 +04:30
										 |  |  |             path.elliptical_arc_to(next_point, { rx, ry }, x_axis_rotation, large_arc_flag != 0, sweep_flag != 0); | 
					
						
							| 
									
										
										
										
											2020-07-21 23:46:39 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |         case PathInstructionType::QuadraticBezierCurve: { | 
					
						
							| 
									
										
										
										
											2020-09-07 22:58:47 +02:00
										 |  |  |             clear_last_control_point = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |             Gfx::FloatPoint through = { data[0], data[1] }; | 
					
						
							|  |  |  |             Gfx::FloatPoint point = { data[2], data[3] }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             if (absolute) { | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |                 path.quadratic_bezier_curve_to(through, point); | 
					
						
							| 
									
										
										
										
											2020-09-07 22:58:47 +02:00
										 |  |  |                 m_previous_control_point = through; | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |                 VERIFY(!path.segments().is_empty()); | 
					
						
							| 
									
										
										
										
											2020-07-21 23:46:39 -07:00
										 |  |  |                 auto last_point = path.segments().last().point(); | 
					
						
							| 
									
										
										
										
											2020-09-07 22:58:47 +02:00
										 |  |  |                 auto control_point = through + last_point; | 
					
						
							|  |  |  |                 path.quadratic_bezier_curve_to(control_point, point + last_point); | 
					
						
							|  |  |  |                 m_previous_control_point = control_point; | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							| 
									
										
										
										
											2020-07-22 14:50:54 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-09-07 22:58:47 +02:00
										 |  |  |         case PathInstructionType::SmoothQuadraticBezierCurve: { | 
					
						
							|  |  |  |             clear_last_control_point = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |             VERIFY(!path.segments().is_empty()); | 
					
						
							| 
									
										
										
										
											2020-09-07 22:58:47 +02:00
										 |  |  |             auto last_point = path.segments().last().point(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (m_previous_control_point.is_null()) { | 
					
						
							|  |  |  |                 m_previous_control_point = last_point; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             auto dx_end_control = last_point.dx_relative_to(m_previous_control_point); | 
					
						
							|  |  |  |             auto dy_end_control = last_point.dy_relative_to(m_previous_control_point); | 
					
						
							| 
									
										
										
										
											2020-09-18 09:49:51 +02:00
										 |  |  |             auto control_point = Gfx::FloatPoint { last_point.x() + dx_end_control, last_point.y() + dy_end_control }; | 
					
						
							| 
									
										
										
										
											2020-09-07 22:58:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-18 09:49:51 +02:00
										 |  |  |             Gfx::FloatPoint end_point = { data[0], data[1] }; | 
					
						
							| 
									
										
										
										
											2020-09-07 22:58:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if (absolute) { | 
					
						
							|  |  |  |                 path.quadratic_bezier_curve_to(control_point, end_point); | 
					
						
							| 
									
										
										
										
											2020-09-18 09:49:51 +02:00
										 |  |  |             } else { | 
					
						
							| 
									
										
										
										
											2020-09-07 22:58:47 +02:00
										 |  |  |                 path.quadratic_bezier_curve_to(control_point, end_point + last_point); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             m_previous_control_point = control_point; | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-15 20:03:28 +02:00
										 |  |  |         case PathInstructionType::Curve: { | 
					
						
							|  |  |  |             Gfx::FloatPoint c1 = { data[0], data[1] }; | 
					
						
							|  |  |  |             Gfx::FloatPoint c2 = { data[2], data[3] }; | 
					
						
							|  |  |  |             Gfx::FloatPoint p2 = { data[4], data[5] }; | 
					
						
							|  |  |  |             if (!absolute) { | 
					
						
							|  |  |  |                 p2 += path.segments().last().point(); | 
					
						
							|  |  |  |                 c1 += path.segments().last().point(); | 
					
						
							|  |  |  |                 c2 += path.segments().last().point(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             path.cubic_bezier_curve_to(c1, c2, p2); | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |         case PathInstructionType::SmoothCurve: | 
					
						
							| 
									
										
										
										
											2020-08-02 09:15:17 -07:00
										 |  |  |             // Instead of crashing the browser every time we come across an SVG
 | 
					
						
							|  |  |  |             // with these path instructions, let's just skip them
 | 
					
						
							|  |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |         case PathInstructionType::Invalid: | 
					
						
							| 
									
										
										
										
											2021-02-23 20:42:32 +01:00
										 |  |  |             VERIFY_NOT_REACHED(); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-09-07 22:58:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (clear_last_control_point) { | 
					
						
							|  |  |  |             m_previous_control_point = Gfx::FloatPoint {}; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-05 16:14:36 -07:00
										 |  |  |     m_path = path; | 
					
						
							|  |  |  |     return m_path.value(); | 
					
						
							| 
									
										
										
										
											2020-07-19 23:01:53 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |