svg_loader: handling svgs without viewBox/viewPort 36/290636/1
authorMira Grudzinska <veleveta@gmail.com>
Wed, 1 Feb 2023 22:46:48 +0000 (23:46 +0100)
committerjykeon <jykeon@samsung.com>
Thu, 30 Mar 2023 04:49:02 +0000 (13:49 +0900)
Additionally:
- cases of inforrect viewBox values are handled
- cases of zero width/height of a viewBox and/or viewPort

@Issue: https://github.com/Samsung/thorvg/issues/1239

Change-Id: I7f6b557ae451db27d1a2033eb2976ed87e0e7f7d
Signed-off-by: jykeon <jykeon@samsung.com>
src/lib/tvgPicture.cpp
src/lib/tvgPictureImpl.h
src/loaders/svg/tvgSvgLoader.cpp
src/loaders/svg/tvgSvgLoader.h
src/loaders/svg/tvgSvgLoaderCommon.h
src/loaders/svg/tvgSvgSceneBuilder.cpp
src/loaders/svg/tvgSvgSceneBuilder.h

index 2b1d0f5..0af5430 100644 (file)
@@ -98,6 +98,7 @@ Result Picture::size(float w, float h) noexcept
 Result Picture::size(float* w, float* h) const noexcept
 {
     if (!pImpl->loader) return Result::InsufficientCondition;
+    pImpl->reload();
     if (w) *w = pImpl->w;
     if (h) *h = pImpl->h;
     return Result::Success;
index 6ef2313..4c9b2a1 100644 (file)
@@ -98,6 +98,10 @@ struct Picture::Impl
                     paint = p.release();
                     loader->close();
                     if (w != loader->w || h != loader->h) {
+                        if (!resizing) {
+                            w = loader->w;
+                            h = loader->h;
+                        }
                         loader->resize(paint, w, h);
                         resizing = false;
                     }
@@ -155,9 +159,10 @@ struct Picture::Impl
         return false;
     }
 
-    bool viewbox(float* x, float* y, float* w, float* h) const
+    bool viewbox(float* x, float* y, float* w, float* h)
     {
         if (!loader) return false;
+        reload();
         if (x) *x = loader->vx;
         if (y) *y = loader->vy;
         if (w) *w = loader->vw;
index 073f0cf..e05aa49 100644 (file)
@@ -831,20 +831,32 @@ static bool _attrParseSvgNode(void* data, const char* key, const char* value)
 
     if (!strcmp(key, "width")) {
         doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+        doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag | (uint32_t)SvgViewFlag::Width);
     } else if (!strcmp(key, "height")) {
         doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
+        doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag | (uint32_t)SvgViewFlag::Height);
     } else if (!strcmp(key, "viewBox")) {
         if (_parseNumber(&value, &doc->vx)) {
             if (_parseNumber(&value, &doc->vy)) {
                 if (_parseNumber(&value, &doc->vw)) {
-                    _parseNumber(&value, &doc->vh);
-                    loader->svgParse->global.h = doc->vh;
+                    if (_parseNumber(&value, &doc->vh)) {
+                        doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag | (uint32_t)SvgViewFlag::Viewbox);
+                        loader->svgParse->global.h = doc->vh;
+                    }
+                    loader->svgParse->global.w = doc->vw;
                 }
-                loader->svgParse->global.w = doc->vw;
+                loader->svgParse->global.y = doc->vy;
             }
-            loader->svgParse->global.y = doc->vy;
+            loader->svgParse->global.x = doc->vx;
+        }
+        if (((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Viewbox) && (doc->vw < 0.0f || doc->vh < 0.0f)) {
+            doc->viewFlag = (SvgViewFlag)((uint32_t)doc->viewFlag & ~(uint32_t)SvgViewFlag::Viewbox);
+            TVGLOG("SVG", "Negative values of the <viewBox> width and/or height - the attribute invalidated.");
+        }
+        if (!((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Viewbox)) {
+            loader->svgParse->global.x = loader->svgParse->global.y = 0.0f;
+            loader->svgParse->global.w = loader->svgParse->global.h = 1.0f;
         }
-        loader->svgParse->global.x = doc->vx;
     } else if (!strcmp(key, "preserveAspectRatio")) {
         _parseAspectRatio(&value, &doc->align, &doc->meetOrSlice);
     } else if (!strcmp(key, "style")) {
@@ -1289,22 +1301,22 @@ static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const cha
     if (!loader->svgParse->node) return nullptr;
     SvgDocNode* doc = &(loader->svgParse->node->node.doc);
 
-    loader->svgParse->global.w = 0;
-    loader->svgParse->global.h = 0;
+    loader->svgParse->global.w = 1.0f;
+    loader->svgParse->global.h = 1.0f;
 
     doc->align = AspectRatioAlign::XMidYMid;
     doc->meetOrSlice = AspectRatioMeetOrSlice::Meet;
+    doc->viewFlag = SvgViewFlag::None;
     func(buf, bufLength, _attrParseSvgNode, loader);
 
-    if (loader->svgParse->global.w == 0) {
-        if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1;
-        else loader->svgParse->global.w = doc->w;
-    }
-    if (loader->svgParse->global.h == 0) {
-        if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1;
-        else loader->svgParse->global.h = doc->h;
+    if (!((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Viewbox)) {
+        if ((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Width) {
+            loader->svgParse->global.w = doc->w;
+        }
+        if ((uint32_t)doc->viewFlag & (uint32_t)SvgViewFlag::Height) {
+            loader->svgParse->global.h = doc->h;
+        }
     }
-
     return loader->svgParse->node;
 }
 
@@ -3192,7 +3204,7 @@ void SvgLoader::run(unsigned tid)
 
         _updateStyle(loaderData.doc, nullptr);
     }
-    root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath);
+    root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, align, meetOrSlice, svgPath, viewFlag);
 }
 
 
@@ -3205,28 +3217,37 @@ bool SvgLoader::header()
     if (!loaderData.svgParse) return false;
 
     loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault;
+    viewFlag = SvgViewFlag::None;
 
     simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
 
     if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
-        //Return the brief resource info such as viewbox:
-        vx = loaderData.doc->node.doc.vx;
-        vy = loaderData.doc->node.doc.vy;
-        w = vw = loaderData.doc->node.doc.vw;
-        h = vh = loaderData.doc->node.doc.vh;
+        viewFlag = loaderData.doc->node.doc.viewFlag;
+        align = loaderData.doc->node.doc.align;
+        meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
+        w = 1.0f;
+        h = 1.0f;
 
-        //Override size
-        if (loaderData.doc->node.doc.w > 0) {
+        //Return the brief resource info such as viewbox:
+        if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width) {
             w = loaderData.doc->node.doc.w;
-            if (vw < FLT_EPSILON) vw = w;
         }
-        if (loaderData.doc->node.doc.h > 0) {
+        if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height) {
             h = loaderData.doc->node.doc.h;
-            if (vh < FLT_EPSILON) vh = h;
         }
-
-        align = loaderData.doc->node.doc.align;
-        meetOrSlice = loaderData.doc->node.doc.meetOrSlice;
+        //Override size
+        if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Viewbox) {
+            vx = loaderData.doc->node.doc.vx;
+            vy = loaderData.doc->node.doc.vy;
+            vw = loaderData.doc->node.doc.vw;
+            vh = loaderData.doc->node.doc.vh;
+
+            if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width)) w = vw;
+            if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height)) h = vh;
+        } else {
+            vw = w;
+            vh = h;
+        }
     } else {
         TVGLOG("SVG", "No SVG File. There is no <svg/>");
         return false;
@@ -3292,6 +3313,12 @@ bool SvgLoader::read()
 {
     if (!content || size == 0) return false;
 
+    if (((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Viewbox) &&
+        (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) {
+        TVGLOG("SVG", "The <viewBox> width and/or height set to 0 - rendering disabled.");
+        return false;
+    }
+
     TaskScheduler::request(this);
 
     return true;
index fc42e5b..5c74184 100644 (file)
@@ -51,6 +51,7 @@ public:
     unique_ptr<Paint> paint() override;
 
 private:
+    SvgViewFlag viewFlag = SvgViewFlag::None;
     AspectRatioAlign align = AspectRatioAlign::XMidYMid;
     AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet;
 
index 9cb78de..ebcbf01 100644 (file)
@@ -146,6 +146,14 @@ enum class SvgParserLengthType
     Other
 };
 
+enum class SvgViewFlag
+{
+    None = 0x0,
+    Width = 0x01,   //viewPort width
+    Height = 0x02,  //viewPort height
+    Viewbox = 0x04  //viewBox x,y,w,h - used only if all 4 are correctly set
+};
+
 enum class AspectRatioAlign
 {
     None,
@@ -174,6 +182,7 @@ struct SvgDocNode
     float vy;
     float vw;
     float vh;
+    SvgViewFlag viewFlag;
     SvgNode* defs;
     SvgNode* style;
     AspectRatioAlign align;
index 5e97b74..f00b373 100644 (file)
@@ -66,7 +66,6 @@ struct Box
     float x, y, w, h;
 };
 
-
 static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
 static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite = nullptr);
 
@@ -755,11 +754,24 @@ static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox,
 }
 
 
+static void _applySvgViewFlag(const Scene* scene, float& vx, float& vy, float& vw, float& vh, float& w, float& h, SvgViewFlag viewFlag)
+{
+    if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Viewbox)) {
+        scene->bounds(nullptr, nullptr, &vw, &vh, false);
+        vx = 0.0f;
+        vy = 0.0f;
+        if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width) vw = w;
+        if ((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height) vh = h;
+    }
+    if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Width)) w = vw;
+    if (!((uint32_t)viewFlag & (uint32_t)SvgViewFlag::Height)) h = vh;
+}
+
 /************************************************************************/
 /* External Class Implementation                                        */
 /************************************************************************/
 
-unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath)
+unique_ptr<Scene> svgSceneBuild(SvgNode* node, float& vx, float& vy, float& vw, float& vh, float& w, float& h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag)
 {
     //TODO: aspect ratio is valid only if viewBox was set
 
@@ -767,9 +779,10 @@ unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, flo
 
     Box vBox = {vx, vy, vw, vh};
     auto docNode = _sceneBuildHelper(node, vBox, svgPath, false, 0);
+    _applySvgViewFlag(docNode.get(), vx, vy, vw, vh, w, h, viewFlag);
 
     if (!mathEqual(w, vw) || !mathEqual(h, vh)) {
-        Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox);
+        Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, {vx, vy, vw, vh});
         docNode->transform(m);
     } else if (!mathZero(vx) || !mathZero(vy)) {
         docNode->translate(-vx, -vy);
index b4fbcf1..0de8c9a 100644 (file)
@@ -25,6 +25,6 @@
 
 #include "tvgCommon.h"
 
-unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath);
+unique_ptr<Scene> svgSceneBuild(SvgNode* node, float& vx, float& vy, float& vw, float& vh, float& w, float& h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag);
 
 #endif //_TVG_SVG_SCENE_BUILDER_H_