From 2c6833d39d1d3751beff28b63ee5d37d026b0e61 Mon Sep 17 00:00:00 2001 From: XGHeaven Date: Sat, 15 Jul 2023 20:32:25 +0800 Subject: [PATCH 1/2] feat: add element for svg --- bridge/CMakeLists.txt | 6 +++ bridge/bindings/qjs/binding_initializer.cc | 6 +++ bridge/bindings/qjs/wrapper_type_info.h | 5 +- bridge/core/svg/svg_circle_element.cc | 12 +++++ bridge/core/svg/svg_circle_element.d.ts | 10 ++++ bridge/core/svg/svg_circle_element.h | 22 ++++++++ bridge/core/svg/svg_ellipse_element.cc | 12 +++++ bridge/core/svg/svg_ellipse_element.d.ts | 10 ++++ bridge/core/svg/svg_ellipse_element.h | 22 ++++++++ bridge/core/svg/svg_style_element.cc | 12 +++++ bridge/core/svg/svg_style_element.d.ts | 9 ++++ bridge/core/svg/svg_style_element.h | 22 ++++++++ bridge/core/svg/svg_tag_names.json5 | 3 ++ integration_tests/scripts/svg_loader.js | 34 ++++++++++++- .../snapshots/svg/shapes/circle-01.svg.png | Bin 0 -> 342 bytes .../snapshots/svg/shapes/ellipse-01.svg.png | Bin 0 -> 1647 bytes .../snapshots/svg/shapes/ellipse-02.svg.png | Bin 0 -> 1898 bytes .../snapshots/svg/shapes/ellipse-03.svg.png | Bin 0 -> 1442 bytes .../snapshots/svg/shapes/ellipse-04.svg.png | Bin 0 -> 342 bytes .../snapshots/svg/shapes/ellipse-05.svg.png | Bin 0 -> 1898 bytes .../snapshots/svg/shapes/ellipse-06.svg.png | Bin 0 -> 1442 bytes .../snapshots/svg/shapes/ellipse-07.svg.png | Bin 0 -> 4232 bytes .../snapshots/svg/shapes/ellipse-08.svg.png | Bin 0 -> 4232 bytes .../snapshots/svg/shapes/ellipse-09.svg.png | Bin 0 -> 342 bytes .../snapshots/svg/shapes/rect-05.svg.png | Bin 2227 -> 342 bytes .../specs/svg/shapes/circle-01.svg | 9 ++++ .../specs/svg/shapes/ellipse-01.svg | 15 ++++++ .../specs/svg/shapes/ellipse-02.svg | 15 ++++++ .../specs/svg/shapes/ellipse-03.svg | 15 ++++++ .../specs/svg/shapes/ellipse-04.svg | 15 ++++++ .../specs/svg/shapes/ellipse-05.svg | 14 +++++ .../specs/svg/shapes/ellipse-06.svg | 14 +++++ .../specs/svg/shapes/ellipse-07.svg | 15 ++++++ .../specs/svg/shapes/ellipse-08.svg | 15 ++++++ .../specs/svg/shapes/ellipse-09.svg | 11 ++++ webf/lib/src/css/keywords.dart | 3 ++ webf/lib/src/css/render_style.dart | 6 +++ webf/lib/src/css/svg.dart | 24 +++++++++ webf/lib/src/css/values/length.dart | 23 +++++++++ webf/lib/src/dom/element.dart | 9 ++++ webf/lib/src/dom/style_node_manager.dart | 2 +- webf/lib/src/html/head.dart | 33 +++++++----- webf/lib/src/svg/circle.dart | 26 ++++++++++ webf/lib/src/svg/ellipse.dart | 37 ++++++++++++++ webf/lib/src/svg/registry.dart | 3 ++ webf/lib/src/svg/rendering/circle.dart | 24 +++++++++ webf/lib/src/svg/rendering/ellipse.dart | 35 +++++++++++++ webf/lib/src/svg/rendering/root.dart | 39 +++++++++++--- webf/lib/src/svg/style.dart | 10 ++++ webf/lib/src/svg/svg.dart | 48 ++++++++++-------- webf/lib/svg.dart | 3 ++ 51 files changed, 603 insertions(+), 45 deletions(-) create mode 100644 bridge/core/svg/svg_circle_element.cc create mode 100644 bridge/core/svg/svg_circle_element.d.ts create mode 100644 bridge/core/svg/svg_circle_element.h create mode 100644 bridge/core/svg/svg_ellipse_element.cc create mode 100644 bridge/core/svg/svg_ellipse_element.d.ts create mode 100644 bridge/core/svg/svg_ellipse_element.h create mode 100644 bridge/core/svg/svg_style_element.cc create mode 100644 bridge/core/svg/svg_style_element.d.ts create mode 100644 bridge/core/svg/svg_style_element.h create mode 100644 integration_tests/snapshots/svg/shapes/circle-01.svg.png create mode 100644 integration_tests/snapshots/svg/shapes/ellipse-01.svg.png create mode 100644 integration_tests/snapshots/svg/shapes/ellipse-02.svg.png create mode 100644 integration_tests/snapshots/svg/shapes/ellipse-03.svg.png create mode 100644 integration_tests/snapshots/svg/shapes/ellipse-04.svg.png create mode 100644 integration_tests/snapshots/svg/shapes/ellipse-05.svg.png create mode 100644 integration_tests/snapshots/svg/shapes/ellipse-06.svg.png create mode 100644 integration_tests/snapshots/svg/shapes/ellipse-07.svg.png create mode 100644 integration_tests/snapshots/svg/shapes/ellipse-08.svg.png create mode 100644 integration_tests/snapshots/svg/shapes/ellipse-09.svg.png create mode 100644 integration_tests/specs/svg/shapes/circle-01.svg create mode 100644 integration_tests/specs/svg/shapes/ellipse-01.svg create mode 100644 integration_tests/specs/svg/shapes/ellipse-02.svg create mode 100644 integration_tests/specs/svg/shapes/ellipse-03.svg create mode 100644 integration_tests/specs/svg/shapes/ellipse-04.svg create mode 100644 integration_tests/specs/svg/shapes/ellipse-05.svg create mode 100644 integration_tests/specs/svg/shapes/ellipse-06.svg create mode 100644 integration_tests/specs/svg/shapes/ellipse-07.svg create mode 100644 integration_tests/specs/svg/shapes/ellipse-08.svg create mode 100644 integration_tests/specs/svg/shapes/ellipse-09.svg create mode 100644 webf/lib/src/svg/circle.dart create mode 100644 webf/lib/src/svg/ellipse.dart create mode 100644 webf/lib/src/svg/rendering/circle.dart create mode 100644 webf/lib/src/svg/rendering/ellipse.dart create mode 100644 webf/lib/src/svg/style.dart diff --git a/bridge/CMakeLists.txt b/bridge/CMakeLists.txt index 18f62b890b..bcca43b65b 100644 --- a/bridge/CMakeLists.txt +++ b/bridge/CMakeLists.txt @@ -361,6 +361,9 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs") core/svg/svg_rect_element.cc core/svg/svg_text_element.cc core/svg/svg_g_element.cc + core/svg/svg_circle_element.cc + core/svg/svg_ellipse_element.cc + core/svg/svg_style_element.cc # Legacy implements, should remove them in the future. core/dom/legacy/element_attributes.cc @@ -506,6 +509,9 @@ if ($ENV{WEBF_JS_ENGINE} MATCHES "quickjs") out/qjs_svg_rect_element.cc out/qjs_svg_text_element.cc out/qjs_svg_g_element.cc + out/qjs_svg_circle_element.cc + out/qjs_svg_ellipse_element.cc + out/qjs_svg_style_element.cc ) diff --git a/bridge/bindings/qjs/binding_initializer.cc b/bridge/bindings/qjs/binding_initializer.cc index 4eb8c8b5b1..f4c5db3f84 100644 --- a/bridge/bindings/qjs/binding_initializer.cc +++ b/bridge/bindings/qjs/binding_initializer.cc @@ -80,6 +80,9 @@ #include "qjs_svg_text_content_element.h" #include "qjs_svg_text_element.h" #include "qjs_svg_text_positioning_element.h" +#include "qjs_svg_circle_element.h" +#include "qjs_svg_ellipse_element.h" +#include "qjs_svg_style_element.h" #include "qjs_text.h" #include "qjs_touch.h" #include "qjs_touch_event.h" @@ -179,6 +182,9 @@ void InstallBindings(ExecutingContext* context) { QJSSVGPathElement::Install(context); QJSSVGTextElement::Install(context); QJSSVGGElement::Install(context); + QJSSVGCircleElement::Install(context); + QJSSVGEllipseElement::Install(context); + QJSSVGStyleElement::Install(context); // Legacy bindings, not standard. QJSElementAttributes::Install(context); diff --git a/bridge/bindings/qjs/wrapper_type_info.h b/bridge/bindings/qjs/wrapper_type_info.h index 9e81c60293..8f39899922 100644 --- a/bridge/bindings/qjs/wrapper_type_info.h +++ b/bridge/bindings/qjs/wrapper_type_info.h @@ -97,10 +97,13 @@ enum { JS_CLASS_SVG_RECT_ELEMENT, JS_CLASS_SVG_SVG_ELEMENT, - // JS_CLASS_SVG_CIRCLE_ELEMENT, JS_CLASS_SVG_PATH_ELEMENT, JS_CLASS_SVG_TEXT_ELEMENT, JS_CLASS_SVG_G_ELEMENT, + JS_CLASS_SVG_CIRCLE_ELEMENT, + JS_CLASS_SVG_ELLIPSE_ELEMENT, + JS_CLASS_SVG_STYLE_ELEMENT, + // SVG unit JS_CLASS_SVG_LENGTH, JS_CLASS_SVG_ANIMATED_LENGTH, diff --git a/bridge/core/svg/svg_circle_element.cc b/bridge/core/svg/svg_circle_element.cc new file mode 100644 index 0000000000..81ba5a49ee --- /dev/null +++ b/bridge/core/svg/svg_circle_element.cc @@ -0,0 +1,12 @@ +/** + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "svg_circle_element.h" +#include "svg_geometry_element.h" +#include "svg_names.h" + +namespace webf { +SVGCircleElement::SVGCircleElement(Document& document) : SVGGeometryElement(svg_names::kcircle, document) {} + +} // namespace webf diff --git a/bridge/core/svg/svg_circle_element.d.ts b/bridge/core/svg/svg_circle_element.d.ts new file mode 100644 index 0000000000..ccca7aaee6 --- /dev/null +++ b/bridge/core/svg/svg_circle_element.d.ts @@ -0,0 +1,10 @@ +/** + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +import {SVGGeometryElement} from "./svg_geometry_element"; + +export interface SVGCircleElement extends SVGGeometryElement { + new(): void; + // TODO: add property in the future +} diff --git a/bridge/core/svg/svg_circle_element.h b/bridge/core/svg/svg_circle_element.h new file mode 100644 index 0000000000..e28f5eb5e3 --- /dev/null +++ b/bridge/core/svg/svg_circle_element.h @@ -0,0 +1,22 @@ +/** + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ +#ifndef BRIDGE_CORE_SVG_SVG_CIRCLE_ELEMENT_H_ +#define BRIDGE_CORE_SVG_SVG_CIRCLE_ELEMENT_H_ + +#include "core/svg/svg_geometry_element.h" + +namespace webf { + +class SVGCircleElement : public SVGGeometryElement { + DEFINE_WRAPPERTYPEINFO(); + + public: + using ImplType = SVGCircleElement*; + explicit SVGCircleElement(Document&); + + private: +}; +} // namespace webf + +#endif // BRIDGE_CORE_SVG_SVG_CIRCLE_ELEMENT_H_ diff --git a/bridge/core/svg/svg_ellipse_element.cc b/bridge/core/svg/svg_ellipse_element.cc new file mode 100644 index 0000000000..80e6df0c6b --- /dev/null +++ b/bridge/core/svg/svg_ellipse_element.cc @@ -0,0 +1,12 @@ +/** + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "svg_ellipse_element.h" +#include "svg_geometry_element.h" +#include "svg_names.h" + +namespace webf { +SVGEllipseElement::SVGEllipseElement(Document& document) : SVGGeometryElement(svg_names::kellipse, document) {} + +} // namespace webf diff --git a/bridge/core/svg/svg_ellipse_element.d.ts b/bridge/core/svg/svg_ellipse_element.d.ts new file mode 100644 index 0000000000..46e893bd93 --- /dev/null +++ b/bridge/core/svg/svg_ellipse_element.d.ts @@ -0,0 +1,10 @@ +/** + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +import {SVGGeometryElement} from "./svg_geometry_element"; + +export interface SVGEllipseElement extends SVGGeometryElement { + new(): void; + // TODO: add property in the future +} diff --git a/bridge/core/svg/svg_ellipse_element.h b/bridge/core/svg/svg_ellipse_element.h new file mode 100644 index 0000000000..1b0473d95f --- /dev/null +++ b/bridge/core/svg/svg_ellipse_element.h @@ -0,0 +1,22 @@ +/** + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ +#ifndef BRIDGE_CORE_SVG_SVG_ELLIPSE_ELEMENT_H_ +#define BRIDGE_CORE_SVG_SVG_ELLIPSE_ELEMENT_H_ + +#include "core/svg/svg_geometry_element.h" + +namespace webf { + +class SVGEllipseElement : public SVGGeometryElement { + DEFINE_WRAPPERTYPEINFO(); + + public: + using ImplType = SVGEllipseElement*; + explicit SVGEllipseElement(Document&); + + private: +}; +} // namespace webf + +#endif // BRIDGE_CORE_SVG_SVG_ELLIPSE_ELEMENT_H_ diff --git a/bridge/core/svg/svg_style_element.cc b/bridge/core/svg/svg_style_element.cc new file mode 100644 index 0000000000..bf8b3cce03 --- /dev/null +++ b/bridge/core/svg/svg_style_element.cc @@ -0,0 +1,12 @@ +/** + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +#include "svg_style_element.h" +#include "svg_element.h" +#include "svg_names.h" + +namespace webf { +SVGStyleElement::SVGStyleElement(Document& document) : SVGElement(svg_names::kstyle, &document) {} + +} // namespace webf diff --git a/bridge/core/svg/svg_style_element.d.ts b/bridge/core/svg/svg_style_element.d.ts new file mode 100644 index 0000000000..704eeb1fcd --- /dev/null +++ b/bridge/core/svg/svg_style_element.d.ts @@ -0,0 +1,9 @@ +/** + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +import {SVGElement} from "./svg_element"; + +export interface SVGStyleElement extends SVGElement { + new(): void; +} diff --git a/bridge/core/svg/svg_style_element.h b/bridge/core/svg/svg_style_element.h new file mode 100644 index 0000000000..d2e69db33b --- /dev/null +++ b/bridge/core/svg/svg_style_element.h @@ -0,0 +1,22 @@ +/** + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ +#ifndef BRIDGE_CORE_SVG_SVG_STYLE_ELEMENT_H_ +#define BRIDGE_CORE_SVG_SVG_STYLE_ELEMENT_H_ + +#include "svg_element.h" + +namespace webf { + +class SVGStyleElement : public SVGElement { + DEFINE_WRAPPERTYPEINFO(); + + public: + using ImplType = SVGStyleElement*; + SVGStyleElement(Document&); + + private: +}; +} // namespace webf + +#endif // BRIDGE_CORE_SVG_SVG_STYLE_ELEMENT_H_ diff --git a/bridge/core/svg/svg_tag_names.json5 b/bridge/core/svg/svg_tag_names.json5 index 5b21e9c922..294bec606f 100644 --- a/bridge/core/svg/svg_tag_names.json5 +++ b/bridge/core/svg/svg_tag_names.json5 @@ -31,5 +31,8 @@ "path", "text", "g", + "circle", + "ellipse", + "style" ] } diff --git a/integration_tests/scripts/svg_loader.js b/integration_tests/scripts/svg_loader.js index b56ca577e9..6a230199e1 100644 --- a/integration_tests/scripts/svg_loader.js +++ b/integration_tests/scripts/svg_loader.js @@ -4,6 +4,21 @@ const path = require('path'); const svgo = require('svgo'); +function tryParseSizeNumber(size) { + if (!size || size.endsWith('%')) { + // default size + return 512; + } else if (size.endsWith('px')) { + return parseInt(size.slice(0, -2), 10) + } else { + const parsedSize = parseInt(size, 10) + if (!Number.isNaN(parsedSize)) { + return parsedSize + } + throw new Error(`Unknown size ${width}`) + } +} + const loader = function(source) { const filepath = this.resourcePath const opts = this.query || {}; @@ -16,6 +31,9 @@ const loader = function(source) { ) ); + let svgWidth = 512; + let svgHeight = 512; + const output = svgo.optimize(source, { path: filepath, multipass: true, @@ -40,6 +58,20 @@ const loader = function(source) { } } } + }, + { + name: 'collectSVGSize', + fn: (ast, params, info) => { + ast.children.forEach(child => { + if (child.type === 'element' && child.name === 'svg') { + const {width, height} = child.attributes + svgWidth = tryParseSizeNumber(width) + svgHeight = tryParseSizeNumber(height) + } + }) + return { + } + } } ] }) @@ -56,7 +88,7 @@ ${output.data} return ` describe('SVGSpec/${testRelativePath}', () => { beforeEach(async () => { - await resizeViewport(512, 512); + await resizeViewport(${svgWidth}, ${svgHeight}); }); afterEach(async () => { diff --git a/integration_tests/snapshots/svg/shapes/circle-01.svg.png b/integration_tests/snapshots/svg/shapes/circle-01.svg.png new file mode 100644 index 0000000000000000000000000000000000000000..82a482bfc748d2f8698f8157b4a9ddb39a9e3fe1 GIT binary patch literal 342 zcmeAS@N?(olHy`uVBq!ia0vp^DIm<#B@XU7oHdBF7dXWCO#rM9t2rUP3@)z)yTyUs60y6O?{Ww5T3RV9 zpSf!nj~=r+g3?%8694f)oTiKa;fLkeDqPM|qHOkvY}hLr+LwDDE0dFC3lsfe19X$F zU$e!fo_Ml$!;>jt_A^5HBh$ggeWnlN&^g8EVX4}@qU}C#r+ucZ{i5dW+b_}qu6%+x zAB{hQZivWk*aJw%Mtj^9TNBm!z)Kmw@DH~5MDD-dJa)mLYkFtE@#=@l zfrAylkTBjp_E)YDTHby@q0_WOb7CzG4T%#Iitua;8yIYXaQXriW)S089Fh1~iD2Y` zS>dU7n)*XnNl6R9xXPzPoVMy}NxIx4=MRR;i8VMk-i%_6c<+a_(?Ly$G$O2tEG!ka!)cvH!?+VuTQ_{q6pX#yk<74A4n zt)T7NwV`VoWOZMk`xuS(O8WRQH_14wc3Zs>WgqWrE(l5flu>}>f-xufKN~Dm6n8|kbEq$fRCZ}=&5t#gC)oidy zlJIzaLDLq6-P5}Oftopq4g{VvMU9Vx>J4%eVcEwmuV3#JC9jlpfnBimtg=ofwCihY zW6xA9bB(0Pm%d5Zsm@?Hiqf9@S!1?GY7R0=oF3;Pog*ECRWjXPn&fWImwfeWi&wwg?QkQ zGK0t`&-4FvPhBUS`^1BpL{6`5m07873;ky7()&u6t9?2}ldrhLq4||OQdGTi$CjdW zdSJVa;yC_OLL-E-#29#Zrc<=pB1Po#*8ILe*osI}S%l3X} za@a81oq#xHPY&kk#%mlhn*O}wb@p^>!J6FA6 zUh}|jC(mRhB&dqU=biNt!)101+-81;tAmeL7?8nh{t}}Xm3o&_>(VjK4!7^>W``S& zX5FY8t?OJzHXP0>?kJwuZPKweUweERVg=gsXI=nTLrqxT_kZH`KPfzhX|`e2UWggb Q+b9hHmE=bh5{{<+14=FWbpQYW literal 0 HcmV?d00001 diff --git a/integration_tests/snapshots/svg/shapes/ellipse-02.svg.png b/integration_tests/snapshots/svg/shapes/ellipse-02.svg.png new file mode 100644 index 0000000000000000000000000000000000000000..7ea52323604709979d851c0c238dd963128ad893 GIT binary patch literal 1898 zcmV-w2bK7VP)Px#1am@3R0s$N2z&@+hyVZw6iGxuRCt{2oj*t%O&G?X`|_PvL@d<8J3&E&6e4I4 z)0j$&!$M7E5CuU`s@-)bkI0ugX}3Y?zxI{+}*MN*VF*DwLpD6(9{I9v;ghxKqyq*yjAeE zA(;%YzYiQ70LRC`$q6$>g-{6S=m5I9fZkr9uMg<%_9kC92>I>C@Y5$?dmGr>19CZU z+K9z~cpMlS0)~gZX`@tpVMrzetgizb8$do^*|r-Sfw3`Qa?&R~hpNc#F45p15jzl7 z|8H=Rrj@Kg*|x%A`S!=h zSSFIzoR`pM9HL=S;&!0vRv$Pjm5k_>3e3# z%8LEYMWaNCgjcPXP9ni_v&-GfLzpLq%+K5J(DZa=4^65R3M@yveVCv3rft^^nVGTg z6{(clEqX^PW#3z7X54SnRYUB|h{uVtSvOm&4B0Hpy;7#QW{BNkYI@qOR;q{HAmzLZ}(O)brS?{kKm&9cmlFM1&XFR5!VaBSb z#A4;&JeFmM)gAa_IA=Ivbxs)>DQ~+a8)9|dh{wxI=pkWs^;ORKq@>~ZpFe?T&)9Ic zk!Tb+JY-MgfENC>tgt$tq|@r#QZ{5@Ks+Sk=dmfTDHOy*ECU1T+fp*b>Yl@oqqdbMk#OhU zM9C1*goK_RwFVzEqUR}tgKFE5Hst6?{0s2I!zI6vOo}x+I+EXp^i|gSIv?xo1V%?o zyqb7~jE*u*%!stJLfVjx4L}Z}YL&uh~p<&EM44GHuL=G!!9a$le|wd-Y1LfgmE%Mx>z#$FZzT26+Db?^rkt z{Qk{8H9YtCz|*Jf)5y`0^Mr249)HVGq&dtBH+mr_Gu)a2O1hwQw$crezD~eMnWOr&ma3W zkG4JJ^prOv8r5dV(9poNGD5^OwZqns^K(Ad)~1?Ta1m)G($t8pA(xkYthrewrC=k{ zN~Ea~TSKm{_?Y>)sIf+*l}J+~wuao?@Ugl&m6U>wNGp-1Mr;i!R!A^XTwtkjMx>QU zQzN?8iO;Y#q^5?CiKhQ(#E7&KX==pQ5Yh1Z^|eY$!A7K&(<2L8L+b1KSfxu}JY+;# zi8M80Ye-WQAIoM{QVKRAtwfp{u{ETng^!(`siYKaL|Tb7HKGR>J;Ia0#bWUs}+l_P0Wb2vqWX8yW5TM(JfLb zu@>XwrEO0apD6?16rX8r1(uij_{9YDmgQ2{d| zUEnh5@6GMpf3tdXZGed~j#rwRv(>%n%~|Vo4eFt5*dC=GCia z^vj8P^y|BQ_WEvL7-B@f;NEA37|CQSF6N`uhCQ6RhyH zA!dkJy{YqaR#oTficM}VmaC|#Ve7!_>)C48mKLDB-FX?ApLkG)1OZ*^#Aj$je1k07*qoM6N<$f|?AfNdN!< literal 0 HcmV?d00001 diff --git a/integration_tests/snapshots/svg/shapes/ellipse-03.svg.png b/integration_tests/snapshots/svg/shapes/ellipse-03.svg.png new file mode 100644 index 0000000000000000000000000000000000000000..253d9151a035a061bd05efc45c1d6a009cfce772 GIT binary patch literal 1442 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 literal 0 HcmV?d00001 diff --git a/integration_tests/snapshots/svg/shapes/ellipse-06.svg.png b/integration_tests/snapshots/svg/shapes/ellipse-06.svg.png new file mode 100644 index 0000000000000000000000000000000000000000..253d9151a035a061bd05efc45c1d6a009cfce772 GIT binary patch literal 1442 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 literal 0 HcmV?d00001 diff --git a/integration_tests/snapshots/svg/shapes/ellipse-08.svg.png b/integration_tests/snapshots/svg/shapes/ellipse-08.svg.png new file mode 100644 index 0000000000000000000000000000000000000000..1ac57a511db1983ffc09b0084554085d318db9e8 GIT binary patch literal 4232 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 literal 0 HcmV?d00001 diff --git a/integration_tests/snapshots/svg/shapes/ellipse-09.svg.png b/integration_tests/snapshots/svg/shapes/ellipse-09.svg.png new file mode 100644 index 0000000000000000000000000000000000000000..82a482bfc748d2f8698f8157b4a9ddb39a9e3fe1 GIT binary patch literal 342 zcmeAS@N?(olHy`uVBq!ia0vp^DImkQ{$>CJAX!pLE-5iJ_d$+#taMs zDGUq>Cm0wUCNMHIC@?cHI09{G;sF{c2{grMRKaLCjHZXtd@))+jFyw5)xl`B2x%VZ Z^;xq2DcL#aG_c{p;OXk;vd$@?2>{a-B9s6C diff --git a/integration_tests/specs/svg/shapes/circle-01.svg b/integration_tests/specs/svg/shapes/circle-01.svg new file mode 100644 index 0000000000..8eecd6621f --- /dev/null +++ b/integration_tests/specs/svg/shapes/circle-01.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/integration_tests/specs/svg/shapes/ellipse-01.svg b/integration_tests/specs/svg/shapes/ellipse-01.svg new file mode 100644 index 0000000000..1b1124e4c5 --- /dev/null +++ b/integration_tests/specs/svg/shapes/ellipse-01.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/integration_tests/specs/svg/shapes/ellipse-02.svg b/integration_tests/specs/svg/shapes/ellipse-02.svg new file mode 100644 index 0000000000..c4727b84b2 --- /dev/null +++ b/integration_tests/specs/svg/shapes/ellipse-02.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/integration_tests/specs/svg/shapes/ellipse-03.svg b/integration_tests/specs/svg/shapes/ellipse-03.svg new file mode 100644 index 0000000000..e0aacefd70 --- /dev/null +++ b/integration_tests/specs/svg/shapes/ellipse-03.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/integration_tests/specs/svg/shapes/ellipse-04.svg b/integration_tests/specs/svg/shapes/ellipse-04.svg new file mode 100644 index 0000000000..79e6d0674a --- /dev/null +++ b/integration_tests/specs/svg/shapes/ellipse-04.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/integration_tests/specs/svg/shapes/ellipse-05.svg b/integration_tests/specs/svg/shapes/ellipse-05.svg new file mode 100644 index 0000000000..e86f1052dd --- /dev/null +++ b/integration_tests/specs/svg/shapes/ellipse-05.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/integration_tests/specs/svg/shapes/ellipse-06.svg b/integration_tests/specs/svg/shapes/ellipse-06.svg new file mode 100644 index 0000000000..80f36a9adc --- /dev/null +++ b/integration_tests/specs/svg/shapes/ellipse-06.svg @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/integration_tests/specs/svg/shapes/ellipse-07.svg b/integration_tests/specs/svg/shapes/ellipse-07.svg new file mode 100644 index 0000000000..396e5f3f62 --- /dev/null +++ b/integration_tests/specs/svg/shapes/ellipse-07.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/integration_tests/specs/svg/shapes/ellipse-08.svg b/integration_tests/specs/svg/shapes/ellipse-08.svg new file mode 100644 index 0000000000..1ff393beb0 --- /dev/null +++ b/integration_tests/specs/svg/shapes/ellipse-08.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/integration_tests/specs/svg/shapes/ellipse-09.svg b/integration_tests/specs/svg/shapes/ellipse-09.svg new file mode 100644 index 0000000000..77e3f3fe70 --- /dev/null +++ b/integration_tests/specs/svg/shapes/ellipse-09.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/webf/lib/src/css/keywords.dart b/webf/lib/src/css/keywords.dart index a1ee442da9..8058046fe8 100644 --- a/webf/lib/src/css/keywords.dart +++ b/webf/lib/src/css/keywords.dart @@ -312,6 +312,9 @@ const String X = 'x'; const String Y = 'y'; const String RX = 'rx'; const String RY = 'ry'; +const String CX = 'cx'; +const String CY = 'cy'; +const String R = 'r'; const String CONTEXT_FILL = 'context-fill'; const String CONTEXT_STROKE = 'context-stroke'; const String STROKE = 'stroke'; diff --git a/webf/lib/src/css/render_style.dart b/webf/lib/src/css/render_style.dart index 2014185058..a11c518cff 100644 --- a/webf/lib/src/css/render_style.dart +++ b/webf/lib/src/css/render_style.dart @@ -194,6 +194,9 @@ abstract class RenderStyle { CSSLengthValue get y; CSSLengthValue get rx; CSSLengthValue get ry; + CSSLengthValue get cx; + CSSLengthValue get cy; + CSSLengthValue get r; CSSLengthValue get strokeWidth; CSSPath get d; CSSFillRule get fillRule; @@ -476,6 +479,9 @@ class CSSRenderStyle extends RenderStyle case Y: case RX: case RY: + case CX: + case CY: + case R: case STROKE_WIDTH: value = CSSLength.resolveLength(propertyValue, renderStyle, propertyName); break; diff --git a/webf/lib/src/css/svg.dart b/webf/lib/src/css/svg.dart index 77c5dfdc81..d60f368ec4 100644 --- a/webf/lib/src/css/svg.dart +++ b/webf/lib/src/css/svg.dart @@ -138,6 +138,30 @@ mixin CSSSvgMixin on RenderStyle { _markShapeUpdate(); } + CSSLengthValue? _cx; + @override get cx => _cx ?? CSSLengthValue.zero; + set cx(CSSLengthValue? value) { + if (_cx == value) return; + _cx = value; + _markShapeUpdate(); + } + + CSSLengthValue? _cy; + @override get cy => _cy ?? CSSLengthValue.zero; + set cy(CSSLengthValue? value) { + if (_cy == value) return; + _cy = value; + _markShapeUpdate(); + } + + CSSLengthValue? _r; + @override get r => _r ?? CSSLengthValue.zero; + set r(CSSLengthValue? value) { + if (_r == value) return; + _r = value; + _markShapeUpdate(); + } + CSSPath? _d; @override get d => _d ?? CSSPath.None; set d(CSSPath value) { diff --git a/webf/lib/src/css/values/length.dart b/webf/lib/src/css/values/length.dart index 63323a1369..71888133fe 100644 --- a/webf/lib/src/css/values/length.dart +++ b/webf/lib/src/css/values/length.dart @@ -8,6 +8,7 @@ import 'package:flutter/rendering.dart'; import 'package:webf/css.dart'; import 'package:webf/rendering.dart'; import 'package:quiver/collection.dart'; +import 'package:webf/svg.dart'; // https://drafts.csswg.org/css-values-3/#absolute-lengths const _1in = 96; // 1in = 2.54cm = 96px @@ -369,6 +370,28 @@ class CSSLengthValue { _computedValue = value!; } break; + + case RX: + final target = renderStyle!.target; + if (target is SVGElement) { + final viewBox = target.findRoot()?.viewBox; + if (viewBox != null) { + _computedValue = viewBox.width * value!; + } + } + break; + + case RY: + final target = renderStyle!.target; + if (target is SVGElement) { + final viewBox = target + .findRoot() + ?.viewBox; + if (viewBox != null) { + _computedValue = viewBox.height * value!; + } + } + break; } break; default: diff --git a/webf/lib/src/dom/element.dart b/webf/lib/src/dom/element.dart index 541ac4f339..c5e2881ff0 100644 --- a/webf/lib/src/dom/element.dart +++ b/webf/lib/src/dom/element.dart @@ -1707,6 +1707,15 @@ abstract class Element extends ContainerNode with ElementBase, ElementEventMixin case RY: renderStyle.ry = value; break; + case CX: + renderStyle.cx = value; + break; + case CY: + renderStyle.cy = value; + break; + case R: + renderStyle.r = value; + break; case D: renderStyle.d = value; break; diff --git a/webf/lib/src/dom/style_node_manager.dart b/webf/lib/src/dom/style_node_manager.dart index d9d0539c71..77a24ad801 100644 --- a/webf/lib/src/dom/style_node_manager.dart +++ b/webf/lib/src/dom/style_node_manager.dart @@ -96,7 +96,7 @@ class StyleNodeManager { for (Node node in _styleSheetCandidateNodes) { if (node is LinkElement && !node.disabled && !node.loading && node.styleSheet != null) { styleSheetsForStyleSheetsList.add(node.styleSheet!); - } else if (node is StyleElement && node.styleSheet != null) { + } else if (node is StyleElementMixin && node.styleSheet != null) { styleSheetsForStyleSheetsList.add(node.styleSheet!); } } diff --git a/webf/lib/src/html/head.dart b/webf/lib/src/html/head.dart index f812171ea0..898896b85e 100644 --- a/webf/lib/src/html/head.dart +++ b/webf/lib/src/html/head.dart @@ -211,33 +211,35 @@ class NoScriptElement extends Element { const String _CSS_MIME = 'text/css'; -// https://www.w3.org/TR/2011/WD-html5-author-20110809/the-style-element.html -class StyleElement extends Element { - StyleElement([BindingContext? context]) : super(context); - +mixin StyleElementMixin on Element { @override Map get defaultStyle => _defaultStyle; - final String _type = _CSS_MIME; + String _type = _CSS_MIME; + + String get type => _type; + + set type(String value) { + _type = value; + } - CSSStyleSheet? get styleSheet => _styleSheet; CSSStyleSheet? _styleSheet; + CSSStyleSheet? get styleSheet => _styleSheet; + @override void initializeProperties(Map properties) { super.initializeProperties(properties); - properties['type'] = BindingObjectProperty(getter: () => type, setter: (value) => type = castToType(value)); + properties['type'] = BindingObjectProperty( + getter: () => type, + setter: (value) => type = castToType(value)); } @override void initializeAttributes(Map attributes) { super.initializeAttributes(attributes); - attributes['type'] = ElementAttributeProperty(setter: (value) => type = attributeToProperty(value)); - } - - String get type => getAttribute('type') ?? ''; - set type(String value) { - internalSetAttribute('type', value); + attributes['type'] = ElementAttributeProperty( + setter: (value) => type = attributeToProperty(value)); } void _recalculateStyle() { @@ -297,3 +299,8 @@ class StyleElement extends Element { super.disconnectedCallback(); } } + +// https://www.w3.org/TR/2011/WD-html5-author-20110809/the-style-element.html +class StyleElement extends Element with StyleElementMixin { + StyleElement([BindingContext? context]) : super(context); +} diff --git a/webf/lib/src/svg/circle.dart b/webf/lib/src/svg/circle.dart new file mode 100644 index 0000000000..727499dd5d --- /dev/null +++ b/webf/lib/src/svg/circle.dart @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +import 'package:webf/svg.dart'; + +import 'rendering/circle.dart'; + +class SVGCircleElement extends SVGGeometryElement { + late final RenderSVGCircle _renderer; + + @override + get renderBoxModel => _renderer; + + @override + get presentationAttributeConfigs => super.presentationAttributeConfigs + ..addAll([ + SVGPresentationAttributeConfig('cx'), + SVGPresentationAttributeConfig('cy'), + SVGPresentationAttributeConfig('r'), + ]); + + SVGCircleElement(super.context) { + _renderer = RenderSVGCircle(renderStyle: renderStyle, element: this); + } +} diff --git a/webf/lib/src/svg/ellipse.dart b/webf/lib/src/svg/ellipse.dart new file mode 100644 index 0000000000..059b0da515 --- /dev/null +++ b/webf/lib/src/svg/ellipse.dart @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +import 'package:webf/css.dart'; +import 'package:webf/svg.dart'; + +import 'rendering/ellipse.dart'; + +class SVGEllipseElement extends SVGGeometryElement { + late final RenderSVGEllipse _renderer; + + @override + // TODO: implement defaultStyle + Map get defaultStyle => super.defaultStyle + ..addAll({ + RX: 'auto', + RY: 'auto', + }); + + @override + get renderBoxModel => _renderer; + + @override + get presentationAttributeConfigs => super.presentationAttributeConfigs + ..addAll([ + SVGPresentationAttributeConfig('cx'), + SVGPresentationAttributeConfig('cy'), + SVGPresentationAttributeConfig('rx'), + SVGPresentationAttributeConfig('ry'), + SVGPresentationAttributeConfig('r'), + ]); + + SVGEllipseElement(super.context) { + _renderer = RenderSVGEllipse(renderStyle: renderStyle, element: this); + } +} diff --git a/webf/lib/src/svg/registry.dart b/webf/lib/src/svg/registry.dart index 71858d56ee..173ceeef0e 100644 --- a/webf/lib/src/svg/registry.dart +++ b/webf/lib/src/svg/registry.dart @@ -11,4 +11,7 @@ final Map svgElementsRegistry = { 'PATH': (context) => SVGPathElement(context), 'TEXT': (context) => SVGTextElement(context), 'G': (context) => SVGGElement(context), + 'CIRCLE': (context) => SVGCircleElement(context), + 'ELLIPSE': (context) => SVGEllipseElement(context), + 'STYLE': (context) => SVGStyleElement(context), }; diff --git a/webf/lib/src/svg/rendering/circle.dart b/webf/lib/src/svg/rendering/circle.dart new file mode 100644 index 0000000000..a7249f632e --- /dev/null +++ b/webf/lib/src/svg/rendering/circle.dart @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +import 'dart:ui'; + +import 'shape.dart'; + +class RenderSVGCircle extends RenderSVGShape { + RenderSVGCircle({required super.renderStyle, super.element}); + + @override + Path asPath() { + final cx = renderStyle.cx.computedValue; + final cy = renderStyle.cy.computedValue; + final r = renderStyle.r.computedValue; + + if (r <= 0) { + return Path(); + } + + return Path()..addOval(Rect.fromCircle(center: Offset(cx, cy), radius: r)); + } +} diff --git a/webf/lib/src/svg/rendering/ellipse.dart b/webf/lib/src/svg/rendering/ellipse.dart new file mode 100644 index 0000000000..514868c7d7 --- /dev/null +++ b/webf/lib/src/svg/rendering/ellipse.dart @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +import 'dart:ui'; + +import 'shape.dart'; + +class RenderSVGEllipse extends RenderSVGShape { + RenderSVGEllipse({required super.renderStyle, super.element}); + + @override + Path asPath() { + final rxValue = renderStyle.rx; + final ryValue = renderStyle.ry; + + final _rx = rxValue.computedValue; + final _ry = ryValue.computedValue; + + // https://svgwg.org/svg2-draft/geometry.html#RxProperty + final rx = rxValue.isAuto ? _ry : _rx; + final ry = ryValue.isAuto ? _rx : _ry; + + if (rx <= 0 || ry <= 0) { + return Path(); + } + + final cx = renderStyle.cx.computedValue; + final cy = renderStyle.cy.computedValue; + + return Path() + ..addOval(Rect.fromCenter( + center: Offset(cx, cy), width: rx * 2, height: ry * 2)); + } +} diff --git a/webf/lib/src/svg/rendering/root.dart b/webf/lib/src/svg/rendering/root.dart index a3b3996d77..445070d4d2 100644 --- a/webf/lib/src/svg/rendering/root.dart +++ b/webf/lib/src/svg/rendering/root.dart @@ -9,8 +9,9 @@ import '../core/aspect_ratio.dart'; import 'container.dart'; class RenderSVGRoot extends RenderSVGContainer { - Rect _viewBox; + Rect? _viewBox; + // the developer provided viewbox, maybe is null get viewBox => _viewBox; set viewBox(value) { @@ -18,6 +19,11 @@ class RenderSVGRoot extends RenderSVGContainer { markNeedsPaint(); } + Rect _renderViewBox = DEFAULT_VIEW_BOX; + + // the final viewbox to render + get renderViewBox => _renderViewBox; + SVGPreserveAspectRatio _ratio; get ratio => _ratio; @@ -37,16 +43,20 @@ class RenderSVGRoot extends RenderSVGContainer { RenderSVGRoot({ required super.renderStyle, super.element, - Rect viewBox = const Rect.fromLTWH(0, 0, 300, 150), + Rect? viewBox, SVGPreserveAspectRatio ratio = const SVGPreserveAspectRatio(), }) : _viewBox = viewBox, _ratio = ratio {} @override void performPaint(PaintingContext context, Offset offset) { - _outerClipLayer.layer = context.pushClipRect(true, offset, Offset.zero & size, (context, offset) { - _transformLayer.layer = context.pushTransform(false, offset, _ratio.getMatrix(_viewBox, size), (context, offset) { - _innerClipLayer.layer = context.pushClipRect(false, offset, _viewBox, (context, offset) { + _outerClipLayer.layer = context + .pushClipRect(true, offset, Offset.zero & size, (context, offset) { + _transformLayer.layer = context + .pushTransform(false, offset, _ratio.getMatrix(_renderViewBox, size), + (context, offset) { + _innerClipLayer.layer = context + .pushClipRect(false, offset, _renderViewBox, (context, offset) { // Draw debug rect // context.canvas.drawRect(_viewBox, Paint()..color = Color.fromARGB(255, 255, 0, 0)..style = PaintingStyle.stroke); visitChildren((child) { @@ -61,14 +71,26 @@ class RenderSVGRoot extends RenderSVGContainer { @override void performLayout() { - var width = renderStyle.width.isAuto ? DEFAULT_VIEW_BOX_WIDTH : renderStyle.width.computedValue; - var height = renderStyle.height.isAuto ? DEFAULT_VIEW_BOX_HEIGHT : renderStyle.height.computedValue; + var width = renderStyle.width.isAuto + ? DEFAULT_VIEW_BOX_WIDTH + : renderStyle.width.computedValue; + var height = renderStyle.height.isAuto + ? DEFAULT_VIEW_BOX_HEIGHT + : renderStyle.height.computedValue; width = width.isInfinite ? DEFAULT_VIEW_BOX_WIDTH : width; height = height.isInfinite ? DEFAULT_VIEW_BOX_HEIGHT : height; size = Size(width, height); + if (_viewBox == null) { + // When viewBox is not valid, should use width/height + // To keep same behavior with Chrome + _renderViewBox = Rect.fromLTWH(0, 0, width, height); + } else { + _renderViewBox = viewBox; + } + visitChildren((child) { // unconstraint child size child.layout(BoxConstraints()); @@ -76,7 +98,8 @@ class RenderSVGRoot extends RenderSVGContainer { // HACK: must be call this function otherwise the BoxModel cannot works correctly. // Improve it in the future. - initOverflowLayout(Rect.fromLTWH(0, 0, size.width, size.height), Rect.fromLTWH(0, 0, size.width, size.height)); + initOverflowLayout(Rect.fromLTWH(0, 0, size.width, size.height), + Rect.fromLTWH(0, 0, size.width, size.height)); } @override diff --git a/webf/lib/src/svg/style.dart b/webf/lib/src/svg/style.dart new file mode 100644 index 0000000000..2b98d86f9a --- /dev/null +++ b/webf/lib/src/svg/style.dart @@ -0,0 +1,10 @@ +/* + * Copyright (C) 2022-present The WebF authors. All rights reserved. + */ + +import 'package:webf/html.dart'; +import 'package:webf/svg.dart'; + +class SVGStyleElement extends SVGElement with StyleElementMixin { + SVGStyleElement(super.context); +} diff --git a/webf/lib/src/svg/svg.dart b/webf/lib/src/svg/svg.dart index 830dc7e1f7..56e3535d02 100644 --- a/webf/lib/src/svg/svg.dart +++ b/webf/lib/src/svg/svg.dart @@ -15,10 +15,10 @@ const DEFAULT_VIEW_BOX_TOP = 0.0; const DEFAULT_VIEW_BOX_LEFT = 0.0; const DEFAULT_VIEW_BOX_WIDTH = 300.0; const DEFAULT_VIEW_BOX_HEIGHT = 150.0; -const DEFAULT_VIEW_BOX = Rect.fromLTWH(DEFAULT_VIEW_BOX_LEFT, DEFAULT_VIEW_BOX_TOP, DEFAULT_VIEW_BOX_WIDTH, DEFAULT_VIEW_BOX_HEIGHT); +const DEFAULT_VIEW_BOX = Rect.fromLTWH(DEFAULT_VIEW_BOX_LEFT, + DEFAULT_VIEW_BOX_TOP, DEFAULT_VIEW_BOX_WIDTH, DEFAULT_VIEW_BOX_HEIGHT); class SVGSVGElement extends SVGGraphicsElement { - late final RenderSVGRoot _renderer; @override get renderBoxModel => _renderer; @@ -31,18 +31,23 @@ class SVGSVGElement extends SVGGraphicsElement { @override Map get defaultStyle => { - DISPLAY: INLINE - }; + DISPLAY: INLINE, + WIDTH: DEFAULT_VIEW_BOX_WIDTH.toString(), + HEIGHT: DEFAULT_VIEW_BOX_HEIGHT.toString(), + }; - Rect _viewBox = DEFAULT_VIEW_BOX; + Rect? _viewBox; + get viewBox => _viewBox; var _ratio = SVGPreserveAspectRatio(); + get ratio => _ratio; @override - get presentationAttributeConfigs => super.presentationAttributeConfigs..addAll([ - SVGPresentationAttributeConfig('width', property: true), - SVGPresentationAttributeConfig('height', property: true), - ]); + get presentationAttributeConfigs => super.presentationAttributeConfigs + ..addAll([ + SVGPresentationAttributeConfig('width', property: true), + SVGPresentationAttributeConfig('height', property: true), + ]); SVGSVGElement(super.context) { _renderer = RenderSVGRoot(renderStyle: renderStyle, element: this); @@ -52,13 +57,16 @@ class SVGSVGElement extends SVGGraphicsElement { void initializeAttributes(Map attributes) { super.initializeAttributes(attributes); attributes.addAll({ - 'viewBox': ElementAttributeProperty(getter: () => '${_viewBox.left} ${_viewBox.top} ${_viewBox.width} ${_viewBox.height}', setter: (val) { - final nextViewBox = parseViewBox(val); - if (nextViewBox != _renderer.viewBox) { - _viewBox = nextViewBox; - _renderer.viewBox = nextViewBox; - } - }), + 'viewBox': ElementAttributeProperty( + getter: () => + '${_viewBox?.left ?? 0} ${_viewBox?.top ?? 0} ${_viewBox?.width ?? 0} ${_viewBox?.height ?? 0}', + setter: (val) { + final nextViewBox = parseViewBox(val); + if (nextViewBox != _renderer.viewBox) { + _viewBox = nextViewBox; + _renderer.viewBox = nextViewBox; + } + }), 'preserveAspectRatio': ElementAttributeProperty(setter: (val) { final nextRatio = SVGPreserveAspectRatio.parse(val); if (nextRatio == null) { @@ -75,13 +83,11 @@ class SVGSVGElement extends SVGGraphicsElement { @override void initializeProperties(Map properties) { super.initializeProperties(properties); - properties.addAll({ - 'viewBox': BindingObjectProperty(getter: () => {}) - }); + properties.addAll({'viewBox': BindingObjectProperty(getter: () => {})}); } } -Rect parseViewBox(String viewBoxString) { +Rect? parseViewBox(String viewBoxString) { final array = viewBoxString.split(' '); final left = double.tryParse(array[0]); final top = double.tryParse(array[1]); @@ -90,5 +96,5 @@ Rect parseViewBox(String viewBoxString) { if (left != null && top != null && width != null && height != null) { return Rect.fromLTWH(left, top, width, height); } - return DEFAULT_VIEW_BOX; + return null; } diff --git a/webf/lib/svg.dart b/webf/lib/svg.dart index d62cc43ece..ca9577c7d9 100644 --- a/webf/lib/svg.dart +++ b/webf/lib/svg.dart @@ -14,3 +14,6 @@ export 'src/svg/text_positioning.dart'; export 'src/svg/text.dart'; export 'src/svg/g.dart'; export 'src/svg/unknown.dart'; +export 'src/svg/circle.dart'; +export 'src/svg/ellipse.dart'; +export 'src/svg/style.dart'; From fc5d2f1006033e731b7f3559647194b70e9b71a5 Mon Sep 17 00:00:00 2001 From: openwebf-bot Date: Mon, 17 Jul 2023 15:18:05 +0000 Subject: [PATCH 2/2] Committing clang-format changes --- bridge/bindings/qjs/binding_initializer.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bridge/bindings/qjs/binding_initializer.cc b/bridge/bindings/qjs/binding_initializer.cc index f4c5db3f84..4737a275ef 100644 --- a/bridge/bindings/qjs/binding_initializer.cc +++ b/bridge/bindings/qjs/binding_initializer.cc @@ -70,19 +70,19 @@ #include "qjs_pop_state_event.h" #include "qjs_promise_rejection_event.h" #include "qjs_screen.h" +#include "qjs_svg_circle_element.h" #include "qjs_svg_element.h" +#include "qjs_svg_ellipse_element.h" #include "qjs_svg_g_element.h" #include "qjs_svg_geometry_element.h" #include "qjs_svg_graphics_element.h" #include "qjs_svg_path_element.h" #include "qjs_svg_rect_element.h" +#include "qjs_svg_style_element.h" #include "qjs_svg_svg_element.h" #include "qjs_svg_text_content_element.h" #include "qjs_svg_text_element.h" #include "qjs_svg_text_positioning_element.h" -#include "qjs_svg_circle_element.h" -#include "qjs_svg_ellipse_element.h" -#include "qjs_svg_style_element.h" #include "qjs_text.h" #include "qjs_touch.h" #include "qjs_touch_event.h"