LCOV - code coverage report
Current view: top level - core/vg - SPSvgReader.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 335 339 98.8 %
Date: 2024-05-12 00:16:13 Functions: 19 19 100.0 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #include "SPSvgReader.h"
      25             : 
      26             : namespace STAPPLER_VERSIONIZED stappler::vg {
      27             : 
      28         125 : static Mat4 svg_parseTransform(StringView &r) {
      29         125 :         Mat4 ret(Mat4::IDENTITY);
      30         350 :         while (!r.empty()) {
      31         225 :                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
      32         225 :                 if (r.is("matrix(")) {
      33          50 :                         r += "matrix("_len;
      34          50 :                         float values[6] = { 0 };
      35             : 
      36             :                         uint16_t i = 0;
      37         350 :                         for (; i < 6; ++ i) {
      38         300 :                                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>, StringView::Chars<','>>();
      39         300 :                                 if (!r.readFloat().grab(values[i])) {
      40             :                                         break;
      41             :                                 }
      42             :                         }
      43             : 
      44          50 :                         if (i != 6) {
      45             :                                 break;
      46             :                         }
      47             : 
      48          50 :                         ret *= Mat4(values[0], values[1], values[2], values[3], values[4], values[5]);
      49             : 
      50         175 :                 } else if (r.is("translate(")) {
      51          50 :                         r += "translate("_len;
      52             : 
      53          50 :                         float tx = 0.0f, ty = 0.0f;
      54          50 :                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
      55          50 :                         if (!r.readFloat().grab(tx)) {
      56             :                                 break;
      57             :                         }
      58             : 
      59          50 :                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>, StringView::Chars<','>>();
      60          50 :                         if (!r.is(')')) {
      61          50 :                                 if (!r.readFloat().grab(ty)) {
      62             :                                         break;
      63             :                                 }
      64             :                         }
      65             : 
      66          50 :                         ret.m[12] += tx;
      67          50 :                         ret.m[13] += ty;
      68             : 
      69         125 :                 } else if (r.is("scale(")) {
      70          25 :                         r += "scale("_len;
      71             : 
      72          25 :                         float sx = 0.0f, sy = 0.0f;
      73          25 :                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
      74          25 :                         if (!r.readFloat().grab(sx)) {
      75             :                                 break;
      76             :                         }
      77             : 
      78          25 :                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>, StringView::Chars<','>>();
      79          25 :                         if (!r.is(')')) {
      80          25 :                                 if (!r.readFloat().grab(sy)) {
      81             :                                         break;
      82             :                                 }
      83             :                         }
      84             : 
      85          25 :                         ret.scale(sx, (sy == 0.0f) ? sx : sy, 1.0f);
      86             : 
      87         100 :                 } else if (r.is("rotate(")) {
      88          50 :                         r += "rotate("_len;
      89             : 
      90          50 :                         float angle = 0.0f;
      91          50 :                         float cx = 0.0f, cy = 0.0f;
      92             : 
      93          50 :                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
      94          50 :                         if (!r.readFloat().grab(angle)) {
      95             :                                 break;
      96             :                         }
      97             : 
      98          50 :                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>, StringView::Chars<','>>();
      99          50 :                         if (!r.is(')')) {
     100          25 :                                 if (!r.readFloat().grab(cx)) {
     101             :                                         break;
     102             :                                 }
     103             : 
     104          25 :                                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>, StringView::Chars<','>>();
     105             : 
     106          25 :                                 if (!r.readFloat().grab(cy)) {
     107             :                                         break;
     108             :                                 }
     109             :                         }
     110             : 
     111          50 :                         if (cx == 0.0f && cy == 0.0f) {
     112          25 :                                 ret.rotateZ(math::to_rad(angle));
     113             :                         } else {
     114             :                                 // optimize matrix translate operations
     115          25 :                                 ret.m[12] += cx;
     116          25 :                                 ret.m[13] += cy;
     117             : 
     118          25 :                                 ret.rotateZ(math::to_rad(angle));
     119             : 
     120          25 :                                 ret.m[12] -= cx;
     121          25 :                                 ret.m[13] -= cy;
     122             :                         }
     123             : 
     124          50 :                 } else if (r.is("skewX(")) {
     125          25 :                         r += "skewX("_len;
     126             : 
     127          25 :                         float angle = 0.0f;
     128          25 :                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     129          25 :                         if (!r.readFloat().grab(angle)) {
     130             :                                 break;
     131             :                         }
     132          25 :                         ret *= Mat4(1, 0, tanf(math::to_rad(angle)), 1, 0, 0);
     133             : 
     134          25 :                 } else if (r.is("skewY(")) {
     135          25 :                         r += "skewY("_len;
     136             : 
     137          25 :                         float angle = 0.0f;
     138          25 :                         r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     139          25 :                         if (!r.readFloat().grab(angle)) {
     140             :                                 break;
     141             :                         }
     142             : 
     143          25 :                         ret *= Mat4(1, tanf(math::to_rad(angle)), 0, 1, 0, 0);
     144             :                 }
     145         225 :                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
     146         225 :                 if (!r.is(')')) {
     147             :                         break;
     148             :                 } else {
     149         225 :                         ++ r;
     150             :                 }
     151             :         }
     152         125 :         return ret;
     153             : }
     154             : 
     155          75 : static Rect svg_readViewBox(StringView &r) {
     156          75 :         float values[4] = { 0 };
     157             : 
     158             :         uint16_t i = 0;
     159         375 :         for (; i < 4; ++ i) {
     160         300 :                 r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>, StringView::Chars<','>>();
     161         300 :                 if (!r.readFloat().grab(values[i])) {
     162           0 :                         return Rect();
     163             :                 }
     164             :         }
     165             : 
     166          75 :         return Rect(values[0], values[1], values[2], values[3]);
     167             : }
     168             : 
     169         975 : static float svg_readCoordValue(StringView &source, float origin) {
     170         975 :         Metric m; m.metric = Metric::Px;
     171         975 :         if (m.readStyleValue(source, false, true)) {
     172         975 :                 switch (m.metric) {
     173         925 :                 case Metric::Px:
     174         925 :                         return m.value;
     175             :                         break;
     176          50 :                 case Metric::Percent:
     177          50 :                         return m.value * origin;
     178             :                         break;
     179             :                 default:
     180             :                         break;
     181             :                 }
     182             :         }
     183           0 :         return nan();
     184             : }
     185             : 
     186          50 : static void svg_readPointCoords(PathWriter &target, StringView &source) {
     187             :         float x, y;
     188         300 :         while (!source.empty()) {
     189         250 :                 source.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>, StringView::Chars<','>>();
     190         250 :                 if (!source.readFloat().grab(x)) {
     191           0 :                         return;
     192             :                 }
     193             : 
     194         250 :                 source.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>, StringView::Chars<','>>();
     195         250 :                 if (!source.readFloat().grab(y)) {
     196             :                         return;
     197             :                 }
     198             : 
     199         250 :                 if (target.empty()) {
     200          50 :                         target.moveTo(x, y);
     201             :                 } else {
     202         200 :                         target.lineTo(x, y);
     203             :                 }
     204             :         }
     205             : }
     206             : 
     207        3775 : VectorPath &SvgTag::getPath() {
     208        3775 :         return rpath;
     209             : }
     210             : 
     211         250 : PathWriter &SvgTag::getWriter() {
     212         250 :         if (!writer) {
     213         175 :                 writer = rpath.getWriter();
     214             :         }
     215         250 :         return writer;
     216             : }
     217             : 
     218         100 : SvgReader::SvgReader() { }
     219             : 
     220        1025 : void SvgReader::onBeginTag(Parser &p, Tag &tag) {
     221        1025 :         if (!p.tagStack.empty()) {
     222         925 :                 tag.rpath.setParams(p.tagStack.back().rpath.getParams());
     223             :         }
     224             : 
     225        1025 :         if (tag.name.equals("rect")) {
     226          75 :                 tag.shape = SvgTag::Rect;
     227         950 :         } else if (tag.name.equals("circle")) {
     228          50 :                 tag.shape = SvgTag::Circle;
     229         900 :         } else if (tag.name.equals("ellipse")) {
     230          25 :                 tag.shape = SvgTag::Ellipse;
     231         875 :         } else if (tag.name.equals("line")) {
     232          25 :                 tag.shape = SvgTag::Line;
     233         850 :         } else if (tag.name.equals("polyline")) {
     234          25 :                 tag.shape = SvgTag::Polyline;
     235         825 :         } else if (tag.name.equals("polygon")) {
     236          25 :                 tag.shape = SvgTag::Polygon;
     237         800 :         } else if (tag.name.equals("use")) {
     238         100 :                 tag.shape = SvgTag::Use;
     239         100 :                 tag.mat = Mat4::IDENTITY;
     240             :         }
     241        1025 : }
     242             : 
     243        1025 : void SvgReader::onEndTag(Parser &p, Tag &tag, bool isClosed) {
     244        1025 :         if (tag.name.equals("svg")) {
     245         100 :                 _squareLength = sqrtf((_width * _width + _height * _height) / 2.0f);
     246             :         }
     247             : 
     248        1025 :         switch (tag.shape) {
     249          75 :         case SvgTag::Rect:
     250         125 :                 if (!isnan(tag.mat.m[0]) && !isnan(tag.mat.m[1])
     251          25 :                                 && !isnan(tag.mat.m[2]) && tag.mat.m[2] > 0.0f && !isnan(tag.mat.m[3]) && tag.mat.m[3] > 0.0f
     252         100 :                                 && (isnan(tag.mat.m[4]) || tag.mat.m[4] >= 0.0f) && (isnan(tag.mat.m[5]) || tag.mat.m[5] >= 0.0f)) {
     253          25 :                         tag.getWriter().addRect(tag.mat.m[0], tag.mat.m[1], tag.mat.m[2], tag.mat.m[3], tag.mat.m[4], tag.mat.m[5]);
     254             :                 }
     255             :                 break;
     256          50 :         case SvgTag::Circle:
     257          50 :                 if (!isnan(tag.mat.m[0]) && !isnan(tag.mat.m[1]) && !isnan(tag.mat.m[2]) && tag.mat.m[2] >= 0.0f) {
     258          50 :                         tag.getWriter().addCircle(tag.mat.m[0], tag.mat.m[1], tag.mat.m[2]);
     259             :                 }
     260             :                 break;
     261          25 :         case SvgTag::Ellipse:
     262          25 :                 if (!isnan(tag.mat.m[0]) && !isnan(tag.mat.m[1]) && !isnan(tag.mat.m[2]) && !isnan(tag.mat.m[3]) && tag.mat.m[2] >= 0.0f && tag.mat.m[3] >= 0.0f) {
     263          25 :                         tag.getWriter().addEllipse(tag.mat.m[0], tag.mat.m[1], tag.mat.m[2], tag.mat.m[3]);
     264             :                 }
     265             :                 break;
     266          25 :         case SvgTag::Line:
     267          25 :                 if (!isnan(tag.mat.m[0]) && !isnan(tag.mat.m[1]) && !isnan(tag.mat.m[2]) && !isnan(tag.mat.m[3])) {
     268          25 :                         tag.getWriter().moveTo(tag.mat.m[0], tag.mat.m[1]);
     269          25 :                         tag.getWriter().lineTo(tag.mat.m[2], tag.mat.m[3]);
     270             :                 }
     271             :                 break;
     272          25 :         case SvgTag::Polygon:
     273          25 :                 if (!tag.getWriter().empty()) {
     274          25 :                         tag.getWriter().closePath();
     275             :                 }
     276             :                 break;
     277             :         default:
     278             :                 break;
     279             :         }
     280        1025 : }
     281             : 
     282        1950 : void SvgReader::onStyleParameter(Tag &tag, StringReader &name, StringReader &value) {
     283        1950 :         if (name.equals("opacity")) {
     284         100 :                 value.readFloat().unwrap([&] (float op) {
     285         100 :                         if (op <= 0.0f) {
     286          25 :                                 tag.getPath().setFillOpacity(0);
     287          25 :                                 tag.getPath().setStrokeOpacity(0);
     288          75 :                         } else if (op >= 1.0f) {
     289          25 :                                 tag.getPath().setFillOpacity(255);
     290          25 :                                 tag.getPath().setStrokeOpacity(255);
     291             :                         } else {
     292          50 :                                 tag.getPath().setFillOpacity(255 * op);
     293          50 :                                 tag.getPath().setStrokeOpacity(255 * op);
     294             :                         }
     295         100 :                 });
     296        1850 :         } else if (name.equals("fill")) {
     297         675 :                 if (value.equals("none")) {
     298          25 :                         tag.getPath().setStyle(tag.getPath().getStyle() & (~DrawStyle::Fill));
     299             :                 } else {
     300         650 :                         Color3B color;
     301         650 :                         if (readColor(value, color)) {
     302         175 :                                 tag.getPath().setFillColor(color, true);
     303         175 :                                 tag.getPath().setStyle(tag.getPath().getStyle() | DrawStyle::Fill);
     304             :                         }
     305             :                 }
     306        1175 :         } else if (name.equals("fill-rule")) {
     307         125 :                 if (value.equals("nonzero")) {
     308          75 :                         tag.getPath().setWindingRule(Winding::NonZero);
     309          50 :                 } else if (value.equals("evenodd")) {
     310          50 :                         tag.getPath().setWindingRule(Winding::EvenOdd);
     311             :                 }
     312        1050 :         } else if (name.equals("fill-opacity")) {
     313          75 :                 value.readFloat().unwrap([&] (float op) {
     314          75 :                         if (op <= 0.0f) {
     315          25 :                                 tag.getPath().setFillOpacity(0);
     316          50 :                         } else if (op >= 1.0f) {
     317          25 :                                 tag.getPath().setFillOpacity(255);
     318             :                         } else {
     319          25 :                                 tag.getPath().setFillOpacity(255 * op);
     320             :                         }
     321          75 :                 });
     322         975 :         } else if (name.equals("stroke")) {
     323         650 :                 if (value.equals("none")) {
     324          25 :                         tag.getPath().setStyle(tag.getPath().getStyle() & (~DrawStyle::Stroke));
     325             :                 } else {
     326         625 :                         Color3B color;
     327         625 :                         if (readColor(value, color)) {
     328         625 :                                 tag.getPath().setStrokeColor(color, true);
     329         625 :                                 tag.getPath().setStyle(tag.getPath().getStyle() | DrawStyle::Stroke);
     330             :                         }
     331             :                 }
     332         325 :         } else if (name.equals("stroke-opacity")) {
     333          75 :                 value.readFloat().unwrap([&] (float op) {
     334          75 :                         if (op <= 0.0f) {
     335          25 :                                 tag.getPath().setStrokeOpacity(0);
     336          50 :                         } else if (op >= 1.0f) {
     337          25 :                                 tag.getPath().setStrokeOpacity(255);
     338             :                         } else {
     339          25 :                                 tag.getPath().setStrokeOpacity(255 * op);
     340             :                         }
     341          75 :                 });
     342         250 :         } else if (name.equals("stroke-width")) {
     343          25 :                 auto val = svg_readCoordValue(value, _squareLength);
     344          25 :                 if (!isnan(val)) {
     345          25 :                         tag.getPath().setStrokeWidth(val);
     346             :                 }
     347         225 :         } else if (name.equals("stroke-linecap")) {
     348          75 :                 if (value.equals("butt")) {
     349          25 :                         tag.getPath().setLineCup(LineCup::Butt);
     350          50 :                 } else if (value.equals("round")) {
     351          25 :                         tag.getPath().setLineCup(LineCup::Round);
     352          25 :                 } else if (value.equals("square")) {
     353          25 :                         tag.getPath().setLineCup(LineCup::Square);
     354             :                 }
     355         150 :         } else if (name.equals("stroke-linejoin")) {
     356          75 :                 if (value.equals("miter")) {
     357          25 :                         tag.getPath().setLineJoin(LineJoin::Miter);
     358          50 :                 } else if (value.equals("round")) {
     359          25 :                         tag.getPath().setLineJoin(LineJoin::Round);
     360          25 :                 } else if (value.equals("bevel")) {
     361          25 :                         tag.getPath().setLineJoin(LineJoin::Bevel);
     362             :                 }
     363          75 :         } else if (name.equals("stroke-miterlimit")) {
     364          25 :                 value.readFloat().unwrap([&] (float op) {
     365          25 :                         if (op > 1.0f) {
     366          25 :                                 tag.getPath().setMiterLimit(op);
     367             :                         }
     368          25 :                 });
     369             :         }
     370        1950 : }
     371             : 
     372          25 : void SvgReader::onStyle(Tag &tag, StringReader &value) {
     373         100 :         while (!value.empty()) {
     374          75 :                 auto n = value.readUntil<StringReader::Chars<':'>>();
     375          75 :                 if (value.is(':')) {
     376          75 :                         ++ value;
     377          75 :                         auto v = value.readUntil<StringReader::Chars<';'>>();
     378          75 :                         if (value.is(';')) {
     379          75 :                                 ++ value;
     380             :                         }
     381          75 :                         if (!n.empty() && !v.empty()) {
     382          75 :                                 onStyleParameter(tag, n, v);
     383             :                         }
     384             :                 }
     385             :         }
     386          25 : }
     387             : 
     388        4025 : void SvgReader::onTagAttribute(Parser &p, Tag &tag, StringReader &name, StringReader &value) {
     389        4025 :         if (tag.name.equals("svg")) {
     390         475 :                 if (name.equals("height")) {
     391         100 :                         auto val = svg_readCoordValue(value, 0.0f);
     392         100 :                         if (!isnan(val)) {
     393         100 :                                 _height = val;
     394             :                         }
     395         375 :                 } else if (name.equals("width")) {
     396         100 :                         auto val = svg_readCoordValue(value, 0.0f);
     397         100 :                         if (!isnan(val)) {
     398         100 :                                 _width = val;
     399             :                         }
     400         275 :                 } else if (name.equals("viewbox")) {
     401          75 :                         _viewBox = svg_readViewBox(value);
     402             :                 }
     403        3550 :         } else if (tag.name.equals("path")) {
     404        1650 :                 if (name.equals("d")) {
     405         550 :                         tag.getPath().init(value);
     406             :                 }
     407             :         }
     408             : 
     409        7400 :         if (name.equals("fill") || name.equals("fill-rule") || name.equals("fill-opacity") || name.equals("stroke")
     410        2525 :                         || name.equals("stroke-opacity") || name.equals("stroke-width") || name.equals("stroke-linecap")
     411        6375 :                         || name.equals("stroke-linejoin") || name.equals("stroke-miterlimit") || name.equals("opacity")) {
     412        1875 :                 onStyleParameter(tag, name, value);
     413        2150 :         } else if (name.equals("transform") && tag.shape != SvgTag::Use) {
     414          50 :                 tag.getPath().applyTransform(svg_parseTransform(value));
     415        2100 :         } else if (name.equals("style")) {
     416          25 :                 onStyle(tag, value);
     417        2075 :         } else if (name.equals("id")) {
     418          75 :                 tag.id = value;
     419             :         } else {
     420        2000 :                 switch (tag.shape) {
     421         275 :                 case SvgTag::Rect:
     422         275 :                         if (name.equals("x")) {
     423          50 :                                 tag.mat.m[0] = svg_readCoordValue(value, _width);
     424         225 :                         } else if (name.equals("y")) {
     425          25 :                                 tag.mat.m[1] = svg_readCoordValue(value, _height);
     426         200 :                         } else if (name.equals("width")) {
     427          75 :                                 tag.mat.m[2] = svg_readCoordValue(value, _width);
     428         125 :                         } else if (name.equals("height")) {
     429          75 :                                 tag.mat.m[3] = svg_readCoordValue(value, _height);
     430          50 :                         } else if (name.equals("rx")) {
     431          25 :                                 tag.mat.m[4] = svg_readCoordValue(value, _width);
     432          25 :                         } else if (name.equals("ry")) {
     433          25 :                                 tag.mat.m[5] = svg_readCoordValue(value, _height);
     434             :                         }
     435             :                         break;
     436         150 :                 case SvgTag::Circle:
     437         150 :                         if (name.equals("cx")) {
     438          50 :                                 tag.mat.m[0] = svg_readCoordValue(value, _width);
     439         100 :                         } else if (name.equals("cy")) {
     440          50 :                                 tag.mat.m[1] = svg_readCoordValue(value, _height);
     441          50 :                         } else if (name.equals("r")) {
     442          50 :                                 tag.mat.m[2] = svg_readCoordValue(value, _width);
     443             :                         }
     444             :                         break;
     445         100 :                 case SvgTag::Ellipse:
     446         100 :                         if (name.equals("cx")) {
     447          25 :                                 tag.mat.m[0] = svg_readCoordValue(value, _width);
     448          75 :                         } else if (name.equals("cy")) {
     449          25 :                                 tag.mat.m[1] = svg_readCoordValue(value, _height);
     450          50 :                         } else if (name.equals("rx")) {
     451          25 :                                 tag.mat.m[2] = svg_readCoordValue(value, _width);
     452          25 :                         } else if (name.equals("ry")) {
     453          25 :                                 tag.mat.m[3] = svg_readCoordValue(value, _height);
     454             :                         }
     455             :                         break;
     456         100 :                 case SvgTag::Line:
     457         100 :                         if (name.equals("x1")) {
     458          25 :                                 tag.mat.m[0] = svg_readCoordValue(value, _width);
     459          75 :                         } else if (name.equals("y1")) {
     460          25 :                                 tag.mat.m[1] = svg_readCoordValue(value, _height);
     461          50 :                         } else if (name.equals("x2")) {
     462          25 :                                 tag.mat.m[2] = svg_readCoordValue(value, _width);
     463          25 :                         } else if (name.equals("y2")) {
     464          25 :                                 tag.mat.m[3] = svg_readCoordValue(value, _height);
     465             :                         }
     466             :                         break;
     467          25 :                 case SvgTag::Polyline:
     468          25 :                         if (name.equals("points")) {
     469          25 :                                 svg_readPointCoords(tag.getWriter(), value);
     470             :                         }
     471             :                         break;
     472          25 :                 case SvgTag::Polygon:
     473          25 :                         if (name.equals("points")) {
     474          25 :                                 svg_readPointCoords(tag.getWriter(), value);
     475             :                         }
     476             :                         break;
     477         300 :                 case SvgTag::Use:
     478         300 :                         if (name.equals("x")) {
     479          75 :                                 tag.mat.translate(svg_readCoordValue(value, _width), 0.0f, 0.0f);
     480         225 :                         } else if (name.equals("y")) {
     481          50 :                                 tag.mat.translate(0.0f, svg_readCoordValue(value, _width), 0.0f);
     482         175 :                         } else if (name.equals("transform")) {
     483          75 :                                 tag.mat.multiply(svg_parseTransform(value));
     484         100 :                         } else if (name.equals("id")) {
     485           0 :                                 tag.id = value;
     486         100 :                         } else if (name.equals("xlink:href") || name.equals("href")) {
     487         100 :                                 tag.ref = value;
     488             :                         }
     489             :                         break;
     490             :                 default:
     491             :                         break;
     492             :                 }
     493             :         }
     494        4025 : }
     495             : 
     496         150 : void SvgReader::onPushTag(Parser &p, Tag &tag) {
     497         150 :         if (tag.name == "defs") {
     498          25 :                 _defs = true;
     499             :         }
     500         150 : }
     501         150 : void SvgReader::onPopTag(Parser &p, Tag &tag) {
     502         150 :         if (tag.name == "defs") {
     503          25 :                 _defs = false;
     504             :         }
     505         150 : }
     506             : 
     507         875 : void SvgReader::onInlineTag(Parser &p, Tag &tag) {
     508         875 :         if (tag.shape == Tag::Shape::Use) {
     509         100 :                 StringView ref(tag.ref);
     510         100 :                 if (ref.is('#')) { ++ ref; }
     511         100 :                 auto pathIt = _paths.find(ref);
     512         100 :                 if (pathIt != _paths.end()) {
     513         100 :                         if (_defs) {
     514          25 :                                 if (!tag.id.empty()) {
     515          25 :                                         VectorPath npath(pathIt->second);
     516          25 :                                         npath.applyTransform(tag.mat);
     517          25 :                                         _paths.emplace(tag.id.str<Interface>(), move(npath));
     518          25 :                                 }
     519             :                         } else {
     520          75 :                                 if (tag.mat.isIdentity()) {
     521          25 :                                         _drawOrder.emplace_back(PathXRef{ref.str<Interface>()});
     522             :                                 } else {
     523          50 :                                         _drawOrder.emplace_back(PathXRef{ref.str<Interface>(), Interface::StringType(), tag.mat});
     524             :                                 }
     525             :                         }
     526             :                 }
     527         775 :         } else if (tag.rpath) {
     528         725 :                 Interface::StringType idStr;
     529         725 :                 StringView id(tag.id);
     530         725 :                 if (id.empty()) {
     531         700 :                         idStr = mem_std::toString("auto-", _nextId);
     532         700 :                         ++ _nextId;
     533         700 :                         id = idStr;
     534             :                 }
     535             : 
     536         725 :                 _paths.emplace(id.str<Interface>(), move(tag.rpath));
     537         725 :                 if (!_defs) {
     538         700 :                         _drawOrder.emplace_back(PathXRef{id.str<Interface>()});
     539             :                 }
     540         725 :         }
     541         875 : }
     542             : 
     543             : }

Generated by: LCOV version 1.14