mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 21:22:46 +00:00 
			
		
		
		
	LibWeb: Begin SVG element support
This commit starts adding a basic SVG element. Currently, svg elements have support for the width and height properties, as well as the stroke, stroke-width, and fill properties. The only child element supported is the path element, as most other graphical elements are just shorthand for paths.
This commit is contained in:
		
							parent
							
								
									bbcdab2baa
								
							
						
					
					
						commit
						22f0953fe2
					
				
					 11 changed files with 1013 additions and 2 deletions
				
			
		|  | @ -1412,7 +1412,7 @@ void Painter::fill_path(Path& path, Color color, WindingRule winding_rule) | ||||||
|             }); |             }); | ||||||
| #ifdef FILL_PATH_DEBUG | #ifdef FILL_PATH_DEBUG | ||||||
|             if ((int)scanline % 10 == 0) { |             if ((int)scanline % 10 == 0) { | ||||||
|                 draw_text(Rect(active_list.last().x - 20, scanline, 20, 10), String::format("%d", (int)scanline)); |                 draw_text(IntRect(active_list.last().x - 20, scanline, 20, 10), String::format("%d", (int)scanline)); | ||||||
|             } |             } | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | @ -1499,7 +1499,7 @@ void Painter::fill_path(Path& path, Color color, WindingRule winding_rule) | ||||||
| #ifdef FILL_PATH_DEBUG | #ifdef FILL_PATH_DEBUG | ||||||
|     size_t i { 0 }; |     size_t i { 0 }; | ||||||
|     for (auto& segment : segments) |     for (auto& segment : segments) | ||||||
|         draw_line(Point(segment.from.x(), segment.from.y()), Point(segment.to.x(), segment.to.y()), Color::from_hsv(++i / segments.size() * 255, 255, 255), 1); |         draw_line(IntPoint(segment.from.x(), segment.from.y()), IntPoint(segment.to.x(), segment.to.y()), Color::from_hsv(++i / segments.size() * 255, 255, 255), 1); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -46,8 +46,10 @@ set(SOURCES | ||||||
|     DOM/HTMLInputElement.cpp |     DOM/HTMLInputElement.cpp | ||||||
|     DOM/HTMLObjectElement.cpp |     DOM/HTMLObjectElement.cpp | ||||||
|     DOM/HTMLLinkElement.cpp |     DOM/HTMLLinkElement.cpp | ||||||
|  |     DOM/HTMLPathElement.cpp | ||||||
|     DOM/HTMLScriptElement.cpp |     DOM/HTMLScriptElement.cpp | ||||||
|     DOM/HTMLStyleElement.cpp |     DOM/HTMLStyleElement.cpp | ||||||
|  |     DOM/HTMLSvgElement.cpp | ||||||
|     DOM/HTMLTableElement.cpp |     DOM/HTMLTableElement.cpp | ||||||
|     DOM/HTMLTableCellElement.cpp |     DOM/HTMLTableCellElement.cpp | ||||||
|     DOM/HTMLTableRowElement.cpp |     DOM/HTMLTableRowElement.cpp | ||||||
|  | @ -79,6 +81,7 @@ set(SOURCES | ||||||
|     Layout/LayoutNode.cpp |     Layout/LayoutNode.cpp | ||||||
|     Layout/LayoutPosition.cpp |     Layout/LayoutPosition.cpp | ||||||
|     Layout/LayoutReplaced.cpp |     Layout/LayoutReplaced.cpp | ||||||
|  |     Layout/LayoutSvg.cpp | ||||||
|     Layout/LayoutTable.cpp |     Layout/LayoutTable.cpp | ||||||
|     Layout/LayoutTableCell.cpp |     Layout/LayoutTableCell.cpp | ||||||
|     Layout/LayoutTableRow.cpp |     Layout/LayoutTableRow.cpp | ||||||
|  |  | ||||||
|  | @ -41,8 +41,10 @@ | ||||||
| #include <LibWeb/DOM/HTMLInputElement.h> | #include <LibWeb/DOM/HTMLInputElement.h> | ||||||
| #include <LibWeb/DOM/HTMLLinkElement.h> | #include <LibWeb/DOM/HTMLLinkElement.h> | ||||||
| #include <LibWeb/DOM/HTMLObjectElement.h> | #include <LibWeb/DOM/HTMLObjectElement.h> | ||||||
|  | #include <LibWeb/DOM/HTMLPathElement.h> | ||||||
| #include <LibWeb/DOM/HTMLScriptElement.h> | #include <LibWeb/DOM/HTMLScriptElement.h> | ||||||
| #include <LibWeb/DOM/HTMLStyleElement.h> | #include <LibWeb/DOM/HTMLStyleElement.h> | ||||||
|  | #include <LibWeb/DOM/HTMLSvgElement.h> | ||||||
| #include <LibWeb/DOM/HTMLTableCellElement.h> | #include <LibWeb/DOM/HTMLTableCellElement.h> | ||||||
| #include <LibWeb/DOM/HTMLTableElement.h> | #include <LibWeb/DOM/HTMLTableElement.h> | ||||||
| #include <LibWeb/DOM/HTMLTableRowElement.h> | #include <LibWeb/DOM/HTMLTableRowElement.h> | ||||||
|  | @ -97,6 +99,10 @@ NonnullRefPtr<Element> create_element(Document& document, const FlyString& tag_n | ||||||
|         return adopt(*new HTMLCanvasElement(document, lowercase_tag_name)); |         return adopt(*new HTMLCanvasElement(document, lowercase_tag_name)); | ||||||
|     if (lowercase_tag_name == HTML::TagNames::object) |     if (lowercase_tag_name == HTML::TagNames::object) | ||||||
|         return adopt(*new HTMLObjectElement(document, lowercase_tag_name)); |         return adopt(*new HTMLObjectElement(document, lowercase_tag_name)); | ||||||
|  |     if (lowercase_tag_name == HTML::TagNames::svg) | ||||||
|  |         return adopt(*new HTMLSvgElement(document, lowercase_tag_name)); | ||||||
|  |     if (lowercase_tag_name == HTML::TagNames::path) | ||||||
|  |         return adopt(*new HTMLPathElement(document, lowercase_tag_name)); | ||||||
|     return adopt(*new Element(document, lowercase_tag_name)); |     return adopt(*new Element(document, lowercase_tag_name)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										499
									
								
								Libraries/LibWeb/DOM/HTMLPathElement.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										499
									
								
								Libraries/LibWeb/DOM/HTMLPathElement.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,499 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <AK/StringBuilder.h> | ||||||
|  | #include <LibGfx/Path.h> | ||||||
|  | #include <LibWeb/DOM/Document.h> | ||||||
|  | #include <LibWeb/DOM/Event.h> | ||||||
|  | #include <LibWeb/DOM/HTMLPathElement.h> | ||||||
|  | #include <ctype.h> | ||||||
|  | 
 | ||||||
|  | //#define PATH_DEBUG
 | ||||||
|  | 
 | ||||||
|  | namespace Web { | ||||||
|  | 
 | ||||||
|  | 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) | ||||||
|  |         ASSERT_NOT_REACHED(); | ||||||
|  |     return m_instructions; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PathDataParser::parse_drawto() { | ||||||
|  |     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(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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'; | ||||||
|  |     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() }); | ||||||
|  |         parse_whitespace(); | ||||||
|  |         if (!match_number()) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PathDataParser::parse_smooth_curveto() | ||||||
|  | { | ||||||
|  |     bool absolute = consume() == 'S'; | ||||||
|  |     parse_whitespace(); | ||||||
|  | 
 | ||||||
|  |     while (true) { | ||||||
|  |         m_instructions.append({ PathInstructionType::SmoothCurve, absolute, parse_coordinate_pair_double() }); | ||||||
|  |         parse_whitespace(); | ||||||
|  |         if (!match_number()) | ||||||
|  |             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() }); | ||||||
|  |         parse_whitespace(); | ||||||
|  |         if (!match_number()) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PathDataParser::parse_smooth_quadratic_bezier_curveto() | ||||||
|  | { | ||||||
|  |     bool absolute = consume() == 'T'; | ||||||
|  |     parse_whitespace(); | ||||||
|  | 
 | ||||||
|  |     while (true) { | ||||||
|  |         m_instructions.append({ PathInstructionType::SmoothQuadraticBezierCurve, absolute, parse_coordinate_pair_double() }); | ||||||
|  |         parse_whitespace(); | ||||||
|  |         if (!match_number()) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PathDataParser::parse_elliptical_arc() | ||||||
|  | { | ||||||
|  |     bool absolute = consume() == 'A'; | ||||||
|  |     parse_whitespace(); | ||||||
|  | 
 | ||||||
|  |     while (true) { | ||||||
|  |         m_instructions.append({ PathInstructionType::EllipticalArc, absolute, parse_elliptical_arg_argument() }); | ||||||
|  |         parse_whitespace(); | ||||||
|  |         if (!match_number()) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float PathDataParser::parse_coordinate() | ||||||
|  | { | ||||||
|  |     return parse_number(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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(); | ||||||
|  |         if (!match_comma_whitespace() && !match_number()) | ||||||
|  |             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(); | ||||||
|  |         if (!match_comma_whitespace() && !match_number()) | ||||||
|  |             break; | ||||||
|  |     } | ||||||
|  |     return sequence; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Vector<float> PathDataParser::parse_coordinate_pair_double() | ||||||
|  | { | ||||||
|  |     Vector<float> coordinates; | ||||||
|  |     coordinates.append(parse_coordinate_pair()); | ||||||
|  |     if (match_comma_whitespace()) | ||||||
|  |         parse_comma_whitespace(); | ||||||
|  |     coordinates.append(parse_coordinate_pair()); | ||||||
|  |     return coordinates; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Vector<float> PathDataParser::parse_coordinate_pair_triplet() | ||||||
|  | { | ||||||
|  |     Vector<float> coordinates; | ||||||
|  |     coordinates.append(parse_coordinate_pair()); | ||||||
|  |     if (match_comma_whitespace()) | ||||||
|  |         parse_comma_whitespace(); | ||||||
|  |     coordinates.append(parse_coordinate_pair()); | ||||||
|  |     if (match_comma_whitespace()) | ||||||
|  |         parse_comma_whitespace(); | ||||||
|  |     coordinates.append(parse_coordinate_pair()); | ||||||
|  |     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(); | ||||||
|  |     numbers.append(parse_coordinate_pair()); | ||||||
|  | 
 | ||||||
|  |     return numbers; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void PathDataParser::parse_whitespace(bool must_match_once) | ||||||
|  | { | ||||||
|  |     bool matched = false; | ||||||
|  |     while (!done() && match_whitespace()) { | ||||||
|  |         consume(); | ||||||
|  |         matched = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |         ASSERT(!must_match_once || matched); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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 { | ||||||
|  |         ASSERT(builder.length() > 0); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (floating_point) | ||||||
|  |         return strtof(builder.to_string().characters(), nullptr); | ||||||
|  |     return builder.to_string().to_int().value(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float PathDataParser::parse_number() | ||||||
|  | { | ||||||
|  |     bool negative = false; | ||||||
|  |     if (match('-')) { | ||||||
|  |         consume(); | ||||||
|  |         negative = true; | ||||||
|  |     } else if (match('+')) { | ||||||
|  |         consume(); | ||||||
|  |     } | ||||||
|  |     auto number = parse_fractional_constant(); | ||||||
|  |     if (match('e') || match('E')) | ||||||
|  |         TODO(); | ||||||
|  |     return negative ? number * -1 : number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | float PathDataParser::parse_flag() | ||||||
|  | { | ||||||
|  |     auto number = parse_number(); | ||||||
|  |         ASSERT(number == 0 || number == 1); | ||||||
|  |     return number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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(','); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool PathDataParser::match_number() const | ||||||
|  | { | ||||||
|  |     return !done() && (isdigit(ch()) || ch() == '-' || ch() == '+'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | HTMLPathElement::HTMLPathElement(Document& document, const FlyString& tag_name) | ||||||
|  |     : HTMLElement(document, tag_name) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef PATH_DEBUG | ||||||
|  | static void print_instruction(const PathInstruction& instruction) | ||||||
|  | { | ||||||
|  |     auto& data = instruction.data; | ||||||
|  | 
 | ||||||
|  |     switch (instruction.type) { | ||||||
|  |     case PathInstructionType::Move: | ||||||
|  |         dbg() << "Move (absolute: " << instruction.absolute << ")"; | ||||||
|  |         for (size_t i = 0; i < data.size(); i += 2) | ||||||
|  |             dbg() << "    x=" << data[i] << ", y=" << data[i + 1]; | ||||||
|  |         break; | ||||||
|  |     case PathInstructionType::ClosePath: | ||||||
|  |         dbg() << "ClosePath (absolute=" << instruction.absolute << ")"; | ||||||
|  |         break; | ||||||
|  |     case PathInstructionType::Line: | ||||||
|  |         dbg() << "Line (absolute=" << instruction.absolute << ")"; | ||||||
|  |         for (size_t i = 0; i < data.size(); i += 2) | ||||||
|  |             dbg() << "    x=" << data[i] << ", y=" << data[i + 1]; | ||||||
|  |         break; | ||||||
|  |     case PathInstructionType::HorizontalLine: | ||||||
|  |         dbg() << "HorizontalLine (absolute=" << instruction.absolute << ")"; | ||||||
|  |         for (size_t i = 0; i < data.size(); ++i) | ||||||
|  |             dbg() << "    x=" << data[i]; | ||||||
|  |         break; | ||||||
|  |     case PathInstructionType::VerticalLine: | ||||||
|  |         dbg() << "VerticalLine (absolute=" << instruction.absolute << ")"; | ||||||
|  |         for (size_t i = 0; i < data.size(); ++i) | ||||||
|  |             dbg() << "    y=" << data[i]; | ||||||
|  |         break; | ||||||
|  |     case PathInstructionType::Curve: | ||||||
|  |         dbg() << "Curve (absolute=" << instruction.absolute << ")"; | ||||||
|  |         for (size_t i = 0; i < data.size(); i += 6) | ||||||
|  |             dbg() << "    (x1=" << data[i] << ", y1=" << data[i + 1] << "), (x2=" << data[i + 2] << ", y2=" << data[i + 3] << "), (x=" << data[i + 4] << ", y=" << data[i + 5] << ")"; | ||||||
|  |         break; | ||||||
|  |     case PathInstructionType::SmoothCurve: | ||||||
|  |         dbg() << "SmoothCurve (absolute: " << instruction.absolute << ")"; | ||||||
|  |         for (size_t i = 0; i < data.size(); i += 4) | ||||||
|  |             dbg() << "    (x2=" << data[i] << ", y2=" << data[i + 1] << "), (x=" << data[i + 2] << ", y=" << data[i + 3] << ")"; | ||||||
|  |         break; | ||||||
|  |     case PathInstructionType::QuadraticBezierCurve: | ||||||
|  |         dbg() << "QuadraticBezierCurve (absolute: " << instruction.absolute << ")"; | ||||||
|  |         for (size_t i = 0; i < data.size(); i += 4) | ||||||
|  |             dbg() << "    (x1=" << data[i] << ", y1=" << data[i + 1] << "), (x=" << data[i + 2] << ", y=" << data[i + 3] << ")"; | ||||||
|  |         break; | ||||||
|  |     case PathInstructionType::SmoothQuadraticBezierCurve: | ||||||
|  |         dbg() << "SmoothQuadraticBezierCurve (absolute: " << instruction.absolute << ")"; | ||||||
|  |         for (size_t i = 0; i < data.size(); i += 2) | ||||||
|  |             dbg() << "    x=" << data[i] << ", y=" << data[i + 1]; | ||||||
|  |         break; | ||||||
|  |     case PathInstructionType::EllipticalArc: | ||||||
|  |         dbg() << "EllipticalArc (absolute: " << instruction.absolute << ")"; | ||||||
|  |         for (size_t i = 0; i < data.size(); i += 7) | ||||||
|  |             dbg() << "    (rx=" << data[i] << ", ry=" << data[i + 1] << ") x-axis-rotation=" << data[i + 2] << ", large-arc-flag=" << data[i + 3] << ", sweep-flag=" << data[i + 4] << ", (x=" << data[i + 5] << ", y=" << data[i + 6] << ")"; | ||||||
|  |         break; | ||||||
|  |     case PathInstructionType::Invalid: | ||||||
|  |         dbg() << "Invalid (absolute: " << instruction.absolute << ")"; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | void HTMLPathElement::parse_attribute(const FlyString& name, const String& value) | ||||||
|  | { | ||||||
|  |     HTMLElement::parse_attribute(name, value); | ||||||
|  |     if (name == "d") | ||||||
|  |         m_instructions = PathDataParser(value).parse(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HTMLPathElement::paint(const SvgPaintingContext& context, Gfx::Painter& painter) | ||||||
|  | { | ||||||
|  |     Gfx::Path path; | ||||||
|  | 
 | ||||||
|  |     for (auto& instruction : m_instructions) { | ||||||
|  |         auto& absolute = instruction.absolute; | ||||||
|  |         auto& data = instruction.data; | ||||||
|  | 
 | ||||||
|  | #ifdef PATH_DEBUG | ||||||
|  |         print_instruction(instruction); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |         switch (instruction.type) { | ||||||
|  |         case PathInstructionType::Move: | ||||||
|  |             if (absolute) { | ||||||
|  |                 path.move_to({ data[0], data[1] }); | ||||||
|  |             } else { | ||||||
|  |                 ASSERT(!path.segments().is_empty()); | ||||||
|  |                 path.move_to(Gfx::FloatPoint { data[0], data[1] } + path.segments().last().point); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case PathInstructionType::ClosePath: | ||||||
|  |             path.close(); | ||||||
|  |             break; | ||||||
|  |         case PathInstructionType::Line: | ||||||
|  |             if (absolute) { | ||||||
|  |                 path.line_to({ data[0], data[1] }); | ||||||
|  |             } else { | ||||||
|  |                 ASSERT(!path.segments().is_empty()); | ||||||
|  |                 path.line_to(Gfx::FloatPoint { data[0], data[1] } + path.segments().last().point); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case PathInstructionType::HorizontalLine: { | ||||||
|  |             ASSERT(!path.segments().is_empty()); | ||||||
|  |             auto last_point = path.segments().last().point; | ||||||
|  |             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: { | ||||||
|  |             ASSERT(!path.segments().is_empty()); | ||||||
|  |             auto last_point = path.segments().last().point; | ||||||
|  |             if (absolute) { | ||||||
|  |                 path.line_to(Gfx::FloatPoint{ last_point.x(), data[0] }); | ||||||
|  |             } else { | ||||||
|  |                 path.line_to(Gfx::FloatPoint{ last_point.x(), data[0] + last_point.y() }); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         case PathInstructionType::QuadraticBezierCurve: | ||||||
|  |             if (absolute) { | ||||||
|  |                 path.quadratic_bezier_curve_to({ data[0], data[1] }, { data[2], data[3] }); | ||||||
|  |             } else { | ||||||
|  |                 ASSERT(!path.segments().is_empty()); | ||||||
|  |                 auto last_point = path.segments().last().point; | ||||||
|  |                 path.quadratic_bezier_curve_to({ data[0] + last_point.x(), data[1] + last_point.y() }, { data[2] + last_point.x(), data[3] + last_point.y() }); | ||||||
|  |             } | ||||||
|  |             break; | ||||||
|  |         case PathInstructionType::Curve: | ||||||
|  |         case PathInstructionType::SmoothCurve: | ||||||
|  |         case PathInstructionType::SmoothQuadraticBezierCurve: | ||||||
|  |         case PathInstructionType::EllipticalArc: | ||||||
|  |             TODO(); | ||||||
|  |         case PathInstructionType::Invalid: | ||||||
|  |             ASSERT_NOT_REACHED(); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     painter.fill_path(path, context.fill_color, Gfx::Painter::WindingRule::EvenOdd); | ||||||
|  |     painter.stroke_path(path, context.stroke_color, context.stroke_width); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										123
									
								
								Libraries/LibWeb/DOM/HTMLPathElement.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								Libraries/LibWeb/DOM/HTMLPathElement.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,123 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <LibGfx/Bitmap.h> | ||||||
|  | #include <LibWeb/DOM/HTMLElement.h> | ||||||
|  | #include <LibWeb/DOM/SvgContext.h> | ||||||
|  | 
 | ||||||
|  | namespace Web { | ||||||
|  | 
 | ||||||
|  | enum class PathInstructionType { | ||||||
|  |     Move, | ||||||
|  |     ClosePath, | ||||||
|  |     Line, | ||||||
|  |     HorizontalLine, | ||||||
|  |     VerticalLine, | ||||||
|  |     Curve, | ||||||
|  |     SmoothCurve, | ||||||
|  |     QuadraticBezierCurve, | ||||||
|  |     SmoothQuadraticBezierCurve, | ||||||
|  |     EllipticalArc, | ||||||
|  |     Invalid, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct PathInstruction { | ||||||
|  |     PathInstructionType type; | ||||||
|  |     bool absolute; | ||||||
|  |     Vector<float> data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class PathDataParser final { | ||||||
|  | public: | ||||||
|  |     PathDataParser(const String& source); | ||||||
|  |     ~PathDataParser() = default; | ||||||
|  | 
 | ||||||
|  |     Vector<PathInstruction> parse(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void parse_drawto(); | ||||||
|  | 
 | ||||||
|  |     void parse_moveto(); | ||||||
|  |     void parse_closepath(); | ||||||
|  |     void parse_lineto(); | ||||||
|  |     void parse_horizontal_lineto(); | ||||||
|  |     void parse_vertical_lineto(); | ||||||
|  |     void parse_curveto(); | ||||||
|  |     void parse_smooth_curveto(); | ||||||
|  |     void parse_quadratic_bezier_curveto(); | ||||||
|  |     void parse_smooth_quadratic_bezier_curveto(); | ||||||
|  |     void parse_elliptical_arc(); | ||||||
|  | 
 | ||||||
|  |     float parse_coordinate(); | ||||||
|  |     Vector<float> parse_coordinate_pair(); | ||||||
|  |     Vector<float> parse_coordinate_sequence(); | ||||||
|  |     Vector<Vector<float>> parse_coordinate_pair_sequence(); | ||||||
|  |     Vector<float> parse_coordinate_pair_double(); | ||||||
|  |     Vector<float> parse_coordinate_pair_triplet(); | ||||||
|  |     Vector<float> parse_elliptical_arg_argument(); | ||||||
|  |     void parse_whitespace(bool must_match_once = false); | ||||||
|  |     void parse_comma_whitespace(); | ||||||
|  |     float parse_fractional_constant(); | ||||||
|  |     float parse_number(); | ||||||
|  |     float parse_flag(); | ||||||
|  | 
 | ||||||
|  |     bool match_whitespace() const; | ||||||
|  |     bool match_comma_whitespace() const; | ||||||
|  |     bool match_number() const; | ||||||
|  |     bool match(char c) const { return !done() && ch() == c; } | ||||||
|  | 
 | ||||||
|  |     bool done() const { return m_cursor >= m_source.length(); } | ||||||
|  |     char ch() const { return m_source[m_cursor]; } | ||||||
|  |     char consume() { return m_source[m_cursor++]; } | ||||||
|  | 
 | ||||||
|  |     String m_source; | ||||||
|  |     size_t m_cursor { 0 }; | ||||||
|  |     Vector<PathInstruction> m_instructions; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class HTMLPathElement final | ||||||
|  |     : public HTMLElement | ||||||
|  |     , public SvgGraphicElement { | ||||||
|  | public: | ||||||
|  |     HTMLPathElement(Document&, const FlyString& tag_name); | ||||||
|  |     virtual ~HTMLPathElement() override = default; | ||||||
|  | 
 | ||||||
|  |     virtual void parse_attribute(const FlyString& name, const String& value) override; | ||||||
|  |     virtual void paint(const SvgPaintingContext&, Gfx::Painter& painter) override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     Vector<PathInstruction> m_instructions; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template<> | ||||||
|  | inline bool is<HTMLPathElement>(const Node& node) | ||||||
|  | { | ||||||
|  |     return is<Element>(node) && to<Element>(node).tag_name() == HTML::TagNames::path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										151
									
								
								Libraries/LibWeb/DOM/HTMLSvgElement.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								Libraries/LibWeb/DOM/HTMLSvgElement.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,151 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <LibGfx/Path.h> | ||||||
|  | #include <LibWeb/CSS/StyleResolver.h> | ||||||
|  | #include <LibWeb/DOM/Document.h> | ||||||
|  | #include <LibWeb/DOM/Event.h> | ||||||
|  | #include <LibWeb/DOM/HTMLPathElement.h> | ||||||
|  | #include <LibWeb/DOM/HTMLSvgElement.h> | ||||||
|  | #include <LibWeb/Layout/LayoutSvg.h> | ||||||
|  | #include <ctype.h> | ||||||
|  | 
 | ||||||
|  | namespace Web { | ||||||
|  | 
 | ||||||
|  | static constexpr auto max_svg_area = 16384 * 16384; | ||||||
|  | 
 | ||||||
|  | HTMLSvgElement::HTMLSvgElement(Document& document, const FlyString& tag_name) | ||||||
|  |     : HTMLElement(document, tag_name) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HTMLSvgElement::parse_attribute(const FlyString& name, const String& value) | ||||||
|  | { | ||||||
|  |     HTMLElement::parse_attribute(name, value); | ||||||
|  |     if (name == "stroke") { | ||||||
|  |         m_stroke_color = Gfx::Color::from_string(value); | ||||||
|  |     } else if (name == "stroke-width") { | ||||||
|  |         auto result = value.to_int(); | ||||||
|  |         if (result.has_value()) | ||||||
|  |             m_stroke_width = result.value(); | ||||||
|  |     } else if (name == "fill") { | ||||||
|  |         m_fill_color = Gfx::Color::from_string(value); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | RefPtr<LayoutNode> HTMLSvgElement::create_layout_node(const StyleProperties* parent_style) | ||||||
|  | { | ||||||
|  |     auto style = document().style_resolver().resolve_style(*this, parent_style); | ||||||
|  |     if (style->display() == CSS::Display::None) | ||||||
|  |         return nullptr; | ||||||
|  |     return adopt(*new LayoutSvg(document(), *this, move(style))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static Gfx::IntSize bitmap_size_for_canvas(const HTMLSvgElement& canvas) | ||||||
|  | { | ||||||
|  |     auto width = canvas.width(); | ||||||
|  |     auto height = canvas.height(); | ||||||
|  | 
 | ||||||
|  |     Checked<size_t> area = width; | ||||||
|  |     area *= height; | ||||||
|  | 
 | ||||||
|  |     if (area.has_overflow()) { | ||||||
|  |         dbg() << "Refusing to create " << width << "x" << height << " svg (overflow)"; | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
|  |     if (area.value() > max_svg_area) { | ||||||
|  |         dbg() << "Refusing to create " << width << "x" << height << " svg (exceeds maximum size)"; | ||||||
|  |         return {}; | ||||||
|  |     } | ||||||
|  |     return Gfx::IntSize(width, height); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool HTMLSvgElement::create_bitmap() | ||||||
|  | { | ||||||
|  |     auto size = bitmap_size_for_canvas(*this); | ||||||
|  |     if (size.is_empty()) { | ||||||
|  |         m_bitmap = nullptr; | ||||||
|  |         return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!m_bitmap || m_bitmap->size() != size) | ||||||
|  |         m_bitmap = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA32, size); | ||||||
|  | 
 | ||||||
|  |     Gfx::Painter painter(*m_bitmap); | ||||||
|  |     paint(painter); | ||||||
|  | 
 | ||||||
|  |     return m_bitmap; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SvgPaintingContext HTMLSvgElement::make_context() const | ||||||
|  | { | ||||||
|  |     SvgPaintingContext context; | ||||||
|  | 
 | ||||||
|  |     if (m_stroke_width.has_value()) | ||||||
|  |         context.stroke_width = m_stroke_width.value(); | ||||||
|  |     if (m_stroke_color.has_value()) | ||||||
|  |         context.stroke_color = m_stroke_color.value(); | ||||||
|  |     if (m_fill_color.has_value()) | ||||||
|  |         context.fill_color = m_fill_color.value(); | ||||||
|  | 
 | ||||||
|  |     return context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | unsigned HTMLSvgElement::width() const | ||||||
|  | { | ||||||
|  |     return attribute(HTML::AttributeNames::width).to_uint().value_or(300); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | unsigned HTMLSvgElement::height() const | ||||||
|  | { | ||||||
|  |     return attribute(HTML::AttributeNames::height).to_uint().value_or(150); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool is_svg_graphic_element(const HTMLElement& element) | ||||||
|  | { | ||||||
|  |     return is<HTMLPathElement>(element); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static SvgGraphicElement& as_svg_graphic_element(HTMLElement& element) | ||||||
|  | { | ||||||
|  |     if (is<HTMLPathElement>(element)) | ||||||
|  |         return to<HTMLPathElement>(element); | ||||||
|  |     ASSERT_NOT_REACHED(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void HTMLSvgElement::paint(Gfx::Painter& painter) | ||||||
|  | { | ||||||
|  |     for_each_child([&](Node& child) { | ||||||
|  |         if (is<HTMLElement>(child)) { | ||||||
|  |             auto& element = to<HTMLElement>(child); | ||||||
|  |             if (is_svg_graphic_element(element)) { | ||||||
|  |                 as_svg_graphic_element(element).paint(make_context(), painter); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										66
									
								
								Libraries/LibWeb/DOM/HTMLSvgElement.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								Libraries/LibWeb/DOM/HTMLSvgElement.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <LibGfx/Bitmap.h> | ||||||
|  | #include <LibWeb/DOM/HTMLElement.h> | ||||||
|  | #include <LibWeb/DOM/SvgContext.h> | ||||||
|  | 
 | ||||||
|  | namespace Web { | ||||||
|  | 
 | ||||||
|  | class HTMLSvgElement final : public HTMLElement { | ||||||
|  | public: | ||||||
|  |     HTMLSvgElement(Document&, const FlyString& tag_name); | ||||||
|  |     virtual ~HTMLSvgElement() override = default; | ||||||
|  | 
 | ||||||
|  |     virtual void parse_attribute(const FlyString& name, const String& value) override; | ||||||
|  |     virtual RefPtr<LayoutNode> create_layout_node(const StyleProperties* parent_style) override; | ||||||
|  | 
 | ||||||
|  |     const RefPtr<Gfx::Bitmap> bitmap() const { return m_bitmap; } | ||||||
|  |     bool create_bitmap(); | ||||||
|  |     SvgPaintingContext make_context() const; | ||||||
|  | 
 | ||||||
|  |     unsigned width() const; | ||||||
|  |     unsigned height() const; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     void paint(Gfx::Painter&); | ||||||
|  | 
 | ||||||
|  |     RefPtr<Gfx::Bitmap> m_bitmap; | ||||||
|  | 
 | ||||||
|  |     Optional<float> m_stroke_width; | ||||||
|  |     Optional<Color> m_stroke_color; | ||||||
|  |     Optional<Color> m_fill_color; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template<> | ||||||
|  | inline bool is<HTMLSvgElement>(const Node& node) | ||||||
|  | { | ||||||
|  |     return is<Element>(node) && to<Element>(node).tag_name() == HTML::TagNames::svg; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								Libraries/LibWeb/DOM/SvgContext.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								Libraries/LibWeb/DOM/SvgContext.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,45 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <LibGfx/Color.h> | ||||||
|  | #include <LibGfx/Painter.h> | ||||||
|  | 
 | ||||||
|  | namespace Web { | ||||||
|  | 
 | ||||||
|  | struct SvgPaintingContext { | ||||||
|  |     Gfx::Color fill_color { Gfx::Color::Black }; | ||||||
|  |     Gfx::Color stroke_color { Gfx::Color::Black }; | ||||||
|  |     float stroke_width { 1 }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class SvgGraphicElement { | ||||||
|  | public: | ||||||
|  |     virtual void paint(const SvgPaintingContext&, Gfx::Painter& painter) = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -110,6 +110,7 @@ void initialize(); | ||||||
|     __ENUMERATE_HTML_TAG(option)     \ |     __ENUMERATE_HTML_TAG(option)     \ | ||||||
|     __ENUMERATE_HTML_TAG(p)          \ |     __ENUMERATE_HTML_TAG(p)          \ | ||||||
|     __ENUMERATE_HTML_TAG(param)      \ |     __ENUMERATE_HTML_TAG(param)      \ | ||||||
|  |     __ENUMERATE_HTML_TAG(path)       \ | ||||||
|     __ENUMERATE_HTML_TAG(plaintext)  \ |     __ENUMERATE_HTML_TAG(plaintext)  \ | ||||||
|     __ENUMERATE_HTML_TAG(pre)        \ |     __ENUMERATE_HTML_TAG(pre)        \ | ||||||
|     __ENUMERATE_HTML_TAG(ruby)       \ |     __ENUMERATE_HTML_TAG(ruby)       \ | ||||||
|  |  | ||||||
							
								
								
									
										67
									
								
								Libraries/LibWeb/Layout/LayoutSvg.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								Libraries/LibWeb/Layout/LayoutSvg.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <LibGUI/Painter.h> | ||||||
|  | #include <LibGfx/Font.h> | ||||||
|  | #include <LibGfx/StylePainter.h> | ||||||
|  | #include <LibWeb/Layout/LayoutSvg.h> | ||||||
|  | 
 | ||||||
|  | namespace Web { | ||||||
|  | 
 | ||||||
|  | LayoutSvg::LayoutSvg(Document& document, const HTMLSvgElement& element, NonnullRefPtr<StyleProperties> style) | ||||||
|  |     : LayoutReplaced(document, element, move(style)) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LayoutSvg::layout(LayoutMode layout_mode) | ||||||
|  | { | ||||||
|  |     set_has_intrinsic_width(true); | ||||||
|  |     set_has_intrinsic_height(true); | ||||||
|  |     set_intrinsic_width(node().width()); | ||||||
|  |     set_intrinsic_height(node().height()); | ||||||
|  |     LayoutReplaced::layout(layout_mode); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LayoutSvg::paint(PaintContext& context, PaintPhase phase) | ||||||
|  | { | ||||||
|  |     if (!is_visible()) | ||||||
|  |         return; | ||||||
|  | 
 | ||||||
|  |     LayoutReplaced::paint(context, phase); | ||||||
|  | 
 | ||||||
|  |     if (phase == PaintPhase::Foreground) { | ||||||
|  |         if (!context.viewport_rect().intersects(enclosing_int_rect(absolute_rect()))) | ||||||
|  |             return; | ||||||
|  | 
 | ||||||
|  |         if (!node().bitmap()) | ||||||
|  |             node().create_bitmap(); | ||||||
|  | 
 | ||||||
|  |         ASSERT(node().bitmap()); | ||||||
|  |         context.painter().draw_scaled_bitmap(enclosing_int_rect(absolute_rect()), *node().bitmap(), node().bitmap()->rect()); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								Libraries/LibWeb/Layout/LayoutSvg.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								Libraries/LibWeb/Layout/LayoutSvg.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com> | ||||||
|  |  * All rights reserved. | ||||||
|  |  * | ||||||
|  |  * Redistribution and use in source and binary forms, with or without | ||||||
|  |  * modification, are permitted provided that the following conditions are met: | ||||||
|  |  * | ||||||
|  |  * 1. Redistributions of source code must retain the above copyright notice, this | ||||||
|  |  *    list of conditions and the following disclaimer. | ||||||
|  |  * | ||||||
|  |  * 2. Redistributions in binary form must reproduce the above copyright notice, | ||||||
|  |  *    this list of conditions and the following disclaimer in the documentation | ||||||
|  |  *    and/or other materials provided with the distribution. | ||||||
|  |  * | ||||||
|  |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||||
|  |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||
|  |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
|  |  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
|  |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
|  |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
|  |  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | ||||||
|  |  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | ||||||
|  |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  |  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <LibWeb/DOM/HTMLSvgElement.h> | ||||||
|  | #include <LibWeb/Layout/LayoutReplaced.h> | ||||||
|  | 
 | ||||||
|  | namespace Web { | ||||||
|  | 
 | ||||||
|  | class HTMLSvgElement; | ||||||
|  | 
 | ||||||
|  | class LayoutSvg : public LayoutReplaced { | ||||||
|  | public: | ||||||
|  |     LayoutSvg(Document&, const HTMLSvgElement&, NonnullRefPtr<StyleProperties>); | ||||||
|  |     virtual ~LayoutSvg() override = default; | ||||||
|  |     virtual void layout(LayoutMode = LayoutMode::Default) override; | ||||||
|  |     virtual void paint(PaintContext&, PaintPhase) override; | ||||||
|  | 
 | ||||||
|  |     HTMLSvgElement& node() { return static_cast<HTMLSvgElement&>(LayoutReplaced::node()); } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     virtual const char* class_name() const override { return "LayoutSvg"; } | ||||||
|  |     virtual bool is_canvas() const override { return true; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Matthew Olsson
						Matthew Olsson