Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libSavitar for openSUSE:Factory 
checked in at 2021-04-29 22:46:10
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libSavitar (Old)
 and      /work/SRC/openSUSE:Factory/.libSavitar.new.1947 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libSavitar"

Thu Apr 29 22:46:10 2021 rev:10 rq:889138 version:4.9.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/libSavitar/libSavitar.changes    2020-12-22 
13:35:46.122466200 +0100
+++ /work/SRC/openSUSE:Factory/.libSavitar.new.1947/libSavitar.changes  
2021-04-29 22:52:49.110049950 +0200
@@ -1,0 +2,7 @@
+Mon Apr 26 20:04:31 UTC 2021 - Stefan Br??ns <[email protected]>
+
+- update to 4.9.0
+  Release notes:
+  * https://github.com/Ultimaker/Cura/releases/tag/4.9
+
+-------------------------------------------------------------------

Old:
----
  libSavitar-4.8.0.tar.gz

New:
----
  libSavitar-4.9.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libSavitar.spec ++++++
--- /var/tmp/diff_new_pack.9CGbW7/_old  2021-04-29 22:52:49.626047652 +0200
+++ /var/tmp/diff_new_pack.9CGbW7/_new  2021-04-29 22:52:49.630047634 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package libSavitar
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2021 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,8 +18,8 @@
 
 %define sover 0
 Name:           libSavitar
-%define sversion        4.8
-Version:        4.8.0
+%define sversion        4.9
+Version:        4.9.0
 Release:        0
 Summary:        C++ implementation of 3mf loading with SIP python bindings
 License:        LGPL-3.0-only

++++++ libSavitar-4.8.0.tar.gz -> libSavitar-4.9.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/python/MetadataEntry.sip 
new/libSavitar-4.9/python/MetadataEntry.sip
--- old/libSavitar-4.8/python/MetadataEntry.sip 1970-01-01 01:00:00.000000000 
+0100
+++ new/libSavitar-4.9/python/MetadataEntry.sip 2021-03-02 11:33:23.000000000 
+0100
@@ -0,0 +1,33 @@
+/*
+ * This file is part of libSavitar
+ *
+ * Copyright (C) 2021 Ultimaker B.V. <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+class MetadataEntry
+{
+    %TypeHeaderCode
+    #include "MetadataEntry.h"
+    %End
+
+public:
+    MetadataEntry(std::string value);
+    MetadataEntry(std::string value, std::string type);
+    MetadataEntry(std::string value, std::string type, bool preserve);
+
+    std::string value;
+    std::string type;
+    bool preserve;
+};
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/python/Scene.sip 
new/libSavitar-4.9/python/Scene.sip
--- old/libSavitar-4.8/python/Scene.sip 2020-10-09 18:43:31.000000000 +0200
+++ new/libSavitar-4.9/python/Scene.sip 2021-03-02 11:33:23.000000000 +0100
@@ -1,7 +1,7 @@
 /*
  * This file is part of libSavitar
  *
- * Copyright (C) 2017 Ultimaker b.v. <[email protected]>
+ * Copyright (C) 2021 Ultimaker B.V. <[email protected]>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published
@@ -30,7 +30,11 @@
 
     void addSceneNode(SceneNode* node /Transfer/);
 
-    std::map<std::string, std::string> getMetadata();
+    std::map<std::string, MetadataEntry> getMetadata();
+    void setMetaDataEntry(std::string key, MetadataEntry entry);
+    void setMetaDataEntry(std::string key, std::string value);
+    void setMetaDataEntry(std::string key, std::string value, std::string 
type);
+    void setMetaDataEntry(std::string key, std::string value, std::string 
type, bool preserve);
 
     std::string getUnit();
     void setUnit(std::string unit);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/python/SceneNode.sip 
new/libSavitar-4.9/python/SceneNode.sip
--- old/libSavitar-4.8/python/SceneNode.sip     2020-10-09 18:43:31.000000000 
+0200
+++ new/libSavitar-4.9/python/SceneNode.sip     2021-03-02 11:33:23.000000000 
+0100
@@ -38,8 +38,11 @@
     std::string getId();
     void setId(std::string id);
 
-    std::map<std::string, std::string> getSettings();
+    std::map<std::string, MetadataEntry> getSettings();
+    void setSetting(std::string key, MetadataEntry entry);
     void setSetting(std::string key, std::string value);
+    void setSetting(std::string key, std::string value, std::string type);
+    void setSetting(std::string key, std::string value, std::string type, bool 
preserve);
 
     bool addChild(SceneNode* child /Transfer/);
     
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/python/ThreeMFParser.sip 
new/libSavitar-4.9/python/ThreeMFParser.sip
--- old/libSavitar-4.8/python/ThreeMFParser.sip 2020-10-09 18:43:31.000000000 
+0200
+++ new/libSavitar-4.9/python/ThreeMFParser.sip 2021-03-02 11:33:23.000000000 
+0100
@@ -1,7 +1,7 @@
 /*
  * This file is part of libSavitar
  *
- * Copyright (C) 2017 Ultimaker b.v. <[email protected]>
+ * Copyright (C) 2021 Ultimaker B.V. <[email protected]>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published
@@ -19,6 +19,7 @@
 %Module(name = Savitar, call_super_init = True)
 
 %Include Types.sip
+%Include MetadataEntry.sip
 %Include SceneNode.sip
 %Include Scene.sip
 %Include MeshData.sip
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/python/Types.sip 
new/libSavitar-4.9/python/Types.sip
--- old/libSavitar-4.8/python/Types.sip 2020-10-09 18:43:31.000000000 +0200
+++ new/libSavitar-4.9/python/Types.sip 2021-03-02 11:33:23.000000000 +0100
@@ -2,7 +2,7 @@
  * This file is part of libSavitar.
  *
  * Parts of this code have been copied from libArcus
- * Copyright (C) 2016 Ultimaker b.v. <[email protected]>
+ * Copyright (C) 2021 Ultimaker B.V. <[email protected]>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published
@@ -94,7 +94,7 @@
         for (int i = 0; i < (int)sipCpp->size(); ++i)
         {
             SceneNode *cpp = new SceneNode(*sipCpp->at(i));
-            PyObject *pobj = sipConvertFromInstance(cpp, sipClass_SceneNode, 
sipTransferObj);
+            PyObject *pobj = sipConvertFromType(cpp, sipType_SceneNode, 
sipTransferObj);
 
             // Get the Python wrapper for the Type instance, creating a new
             // one if necessary, and handle any ownership transfer.
@@ -132,7 +132,7 @@
 
         while ((item = PyIter_Next(iterator)))
         {
-            if (!sipCanConvertToInstance(item, sipClass_SceneNode, 
SIP_NOT_NONE))
+            if (!sipCanConvertToType(item, sipType_SceneNode, SIP_NOT_NONE))
             {
                 PyErr_Format(PyExc_TypeError, "object in iterable cannot be 
converted to SceneNode");
                 *sipIsErr = 1;
@@ -140,14 +140,14 @@
             }
 
             int state;
-            SceneNode* p = 
reinterpret_cast<SceneNode*>(sipConvertToInstance(item, sipClass_SceneNode, 0, 
SIP_NOT_NONE, &state, sipIsErr));
+            SceneNode* p = reinterpret_cast<SceneNode*>(sipConvertToType(item, 
sipType_SceneNode, 0, SIP_NOT_NONE, &state, sipIsErr));
 
             if (!*sipIsErr)
             {
                 result_vector->push_back(p);
             }
 
-            sipReleaseInstance(p, sipClass_SceneNode, state);
+            sipReleaseType(p, sipType_SceneNode, state);
             Py_DECREF(item);
         }
 
@@ -226,7 +226,7 @@
         for (int i = 0; i < (int)sipCpp->size(); ++i)
         {
             TYPE *cpp = new TYPE(sipCpp->at(i));
-            PyObject *pobj = sipConvertFromInstance(cpp, sipClass_TYPE, 
sipTransferObj);
+            PyObject *pobj = sipConvertFromType(cpp, sipType_TYPE, 
sipTransferObj);
 
             // Get the Python wrapper for the Type instance, creating a new
             // one if necessary, and handle any ownership transfer.
@@ -264,7 +264,7 @@
 
         while ((item = PyIter_Next(iterator)))
         {
-            if (!sipCanConvertToInstance(item, sipClass_TYPE, SIP_NOT_NONE))
+            if (!sipCanConvertToType(item, sipType_TYPE, SIP_NOT_NONE))
             {
                 PyErr_Format(PyExc_TypeError, "object in iterable cannot be 
converted to TYPE");
                 *sipIsErr = 1;
@@ -272,14 +272,14 @@
             }
 
             int state;
-            TYPE* p = reinterpret_cast<TYPE*>(sipConvertToInstance(item, 
sipClass_TYPE, 0, SIP_NOT_NONE, &state, sipIsErr));
+            TYPE* p = reinterpret_cast<TYPE*>(sipConvertToType(item, 
sipType_TYPE, 0, SIP_NOT_NONE, &state, sipIsErr));
 
             if (!*sipIsErr)
             {
                 result_vector->push_back(*p);
             }
 
-            sipReleaseInstance(p, sipClass_TYPE, state);
+            sipReleaseType(p, sipType_TYPE, state);
             Py_DECREF(item);
         }
 
@@ -410,6 +410,131 @@
 
             sipReleaseType(key, sipType_std_string, key_state);
             sipReleaseType(value, sipType_std_string, value_state);
+        }
+
+        *sipCppPtr = std_map;
+
+        return sipGetState(sipTransferObj);
+    %End
+};
+
+
+
+
+/**
+ * Convert a (Python) dict of metadata entries to std::map of MetadataEntry
+ * objects.
+ */
+%MappedType std::map<std::string, MetadataEntry>
+{
+    %TypeHeaderCode
+    #include <map>
+    %End
+
+    %ConvertFromTypeCode // From C++ to python
+        // Create the dictionary.
+        PyObject *result_dict = PyDict_New();
+
+        if (!result_dict)
+        {
+            return NULL;
+        }
+
+        // Set the dictionary elements.
+        std::map<std::string, MetadataEntry>::const_iterator i = 
sipCpp->begin();
+
+        while(i != sipCpp->end())
+        {
+            std::string* key = new std::string((*i).first);
+            MetadataEntry* value = new MetadataEntry((*i).second);
+
+            PyObject *key_object = sipConvertFromNewType(key, 
sipType_std_string, sipTransferObj);
+            PyObject *value_object = sipConvertFromInstance(value, 
sipClass_MetadataEntry, sipTransferObj);
+
+            if (key_object == NULL || value_object == NULL || 
PyDict_SetItem(result_dict, key_object, value_object) < 0)
+            {
+                Py_DECREF(result_dict);
+
+                if (key_object)
+                {
+                    Py_DECREF(key_object);
+                }
+                else
+                {
+                    delete key;
+                }
+
+                if (value_object)
+                {
+                    Py_DECREF(value_object);
+                }
+                else
+                {
+                    delete value;
+                }
+
+                return nullptr;
+            }
+
+            Py_DECREF(key_object);
+            Py_DECREF(value_object);
+
+            ++i;
+        }
+
+        return result_dict;
+    %End
+
+    %ConvertToTypeCode // From python to C++
+        PyObject *key_object, *value_object;
+        SIP_SSIZE_T i = 0;
+
+        // Check the type if that is all that is required.
+        if (sipIsErr == nullptr)
+        {
+            if (!PyDict_Check(sipPy))
+            {
+                return 0;
+            }
+
+            while (PyDict_Next(sipPy, &i, &key_object, &value_object))
+            {
+                if (!sipCanConvertToType(key_object, sipType_std_string, 
SIP_NOT_NONE))
+                {
+                    return 0;
+                }
+
+                if (!sipCanConvertToInstance(value_object, 
sipClass_MetadataEntry, SIP_NOT_NONE))
+                {
+                    return 0;
+                }
+            }
+
+            return 1;
+        }
+
+        std::map<std::string, MetadataEntry> *std_map = new 
std::map<std::string, MetadataEntry>;
+
+        while (PyDict_Next(sipPy, &i, &key_object, &value_object))
+        {
+            int key_state, value_state;
+
+            std::string *key = reinterpret_cast<std::string 
*>(sipConvertToType(key_object, sipType_std_string, sipTransferObj, 
SIP_NOT_NONE, &key_state, sipIsErr));
+            MetadataEntry *value = 
reinterpret_cast<MetadataEntry*>(sipConvertToInstance(value_object, 
sipClass_MetadataEntry, 0, SIP_NOT_NONE, &value_state, sipIsErr));
+
+            if (*sipIsErr)
+            {
+                sipReleaseType(key, sipType_std_string, key_state);
+                sipReleaseInstance(value, sipClass_MetadataEntry, value_state);
+
+                delete std_map;
+                return 0;
+            }
+
+            (*std_map).emplace(*key, *value);
+
+            sipReleaseType(key, sipType_std_string, key_state);
+            sipReleaseType(value, sipType_std_string, value_state);
         }
 
         *sipCppPtr = std_map;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/src/MeshData.cpp 
new/libSavitar-4.9/src/MeshData.cpp
--- old/libSavitar-4.8/src/MeshData.cpp 2020-10-09 18:43:31.000000000 +0200
+++ new/libSavitar-4.9/src/MeshData.cpp 2021-03-02 11:33:23.000000000 +0100
@@ -97,30 +97,52 @@
         int v1 = faces.at(i).getV1();
         int v2 = faces.at(i).getV2();
         int v3 = faces.at(i).getV3();
+        float x, y, z;
         
         // Add vertices for face 1
-        float x = vertices.at(v1).getX();
-        float y = vertices.at(v1).getY();
-        float z = vertices.at(v1).getZ();
-        vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&x), reinterpret_cast<const uint8_t*>(&x) + sizeof(float));
-        vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&y), reinterpret_cast<const uint8_t*>(&y) + sizeof(float));
-        vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&z), reinterpret_cast<const uint8_t*>(&z) + sizeof(float));
+        if (v1 >= 0 && v1 < vertices.size())
+        {
+            x = vertices.at(v1).getX();
+            y = vertices.at(v1).getY();
+            z = vertices.at(v1).getZ();
+            vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&x), reinterpret_cast<const uint8_t*>(&x) + sizeof(float));
+            vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&y), reinterpret_cast<const uint8_t*>(&y) + sizeof(float));
+            vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&z), reinterpret_cast<const uint8_t*>(&z) + sizeof(float));
+        }
+        else
+        {
+            return bytearray();
+        }
 
         // Add vertices for face 2
-        x = vertices.at(v2).getX();
-        y = vertices.at(v2).getY();
-        z = vertices.at(v2).getZ();
-        vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&x), reinterpret_cast<const uint8_t*>(&x) + sizeof(float));
-        vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&y), reinterpret_cast<const uint8_t*>(&y) + sizeof(float));
-        vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&z), reinterpret_cast<const uint8_t*>(&z) + sizeof(float));
+        if (v2 >= 0 && v2 < vertices.size())
+        {
+            x = vertices.at(v2).getX();
+            y = vertices.at(v2).getY();
+            z = vertices.at(v2).getZ();
+            vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&x), reinterpret_cast<const uint8_t*>(&x) + sizeof(float));
+            vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&y), reinterpret_cast<const uint8_t*>(&y) + sizeof(float));
+            vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&z), reinterpret_cast<const uint8_t*>(&z) + sizeof(float));
+        }
+        else
+        {
+            return bytearray();
+        }
 
         // Add vertices for face 3
-        x = vertices.at(v3).getX();
-        y = vertices.at(v3).getY();
-        z = vertices.at(v3).getZ();
-        vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&x), reinterpret_cast<const uint8_t*>(&x) + sizeof(float));
-        vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&y), reinterpret_cast<const uint8_t*>(&y) + sizeof(float));
-        vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&z), reinterpret_cast<const uint8_t*>(&z) + sizeof(float));
+        if (v3 >= 0 && v3 < vertices.size())
+        {
+            x = vertices.at(v3).getX();
+            y = vertices.at(v3).getY();
+            z = vertices.at(v3).getZ();
+            vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&x), reinterpret_cast<const uint8_t*>(&x) + sizeof(float));
+            vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&y), reinterpret_cast<const uint8_t*>(&y) + sizeof(float));
+            vertices_data.insert(vertices_data.end(), reinterpret_cast<const 
uint8_t*>(&z), reinterpret_cast<const uint8_t*>(&z) + sizeof(float));
+        }
+        else
+        {
+            return bytearray();
+        }
     }
     return vertices_data;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/src/MetadataEntry.h 
new/libSavitar-4.9/src/MetadataEntry.h
--- old/libSavitar-4.8/src/MetadataEntry.h      1970-01-01 01:00:00.000000000 
+0100
+++ new/libSavitar-4.9/src/MetadataEntry.h      2021-03-02 11:33:23.000000000 
+0100
@@ -0,0 +1,66 @@
+/*
+ * This file is part of libSavitar
+ *
+ * Copyright (C) 2021 Ultimaker B.V. <[email protected]>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef METADATAENTRY_H
+#define METADATAENTRY_H
+
+#include <string>
+
+#include "SavitarExport.h"
+
+namespace Savitar
+{
+
+/*!
+ * Represents an entry of metadata from 3MF documents.
+ *
+ * These entries are stored by a key in scenes and scene nodes.
+ */
+struct SAVITAR_EXPORT MetadataEntry
+{
+    /*!
+     * The value of the metadata entry.
+     *
+     * Regardless of the actual type, this value will be stored in serialised
+     * form here. The user of the 3MF data will need to interpret the data as
+     * is.
+     */
+    std::string value;
+
+    /*!
+     * The type of value. Must be an "XML type" as specified by the ODF, e.g.
+     * "xs:string" for text.
+     */
+    std::string type;
+
+    /*!
+     * Whether the data should be preserved when saving the scene to a new 3MF
+     * document.
+     */
+    bool preserve;
+
+    MetadataEntry(const std::string& value, const std::string& type = 
"xs:string", const bool preserve = false)
+        : value(value)
+        , type(type)
+        , preserve(preserve)
+    {}
+};
+
+}
+
+#endif
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/src/Scene.cpp 
new/libSavitar-4.9/src/Scene.cpp
--- old/libSavitar-4.8/src/Scene.cpp    2020-10-09 18:43:31.000000000 +0200
+++ new/libSavitar-4.9/src/Scene.cpp    2021-03-02 11:33:23.000000000 +0100
@@ -1,7 +1,7 @@
 /*
  * This file is part of libSavitar
  *
- * Copyright (C) 2017 Ultimaker b.v. <[email protected]>
+ * Copyright (C) 2021 Ultimaker B.V. <[email protected]>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published
@@ -56,7 +56,16 @@
     // Handle metadata:
     for(pugi::xml_node metadata_node = xml_node.child("metadata"); 
metadata_node; metadata_node = metadata_node.next_sibling("metadata"))
     {
-        setMetaDataEntry(metadata_node.attribute("name").as_string(), 
metadata_node.text().as_string());
+        const std::string key = metadata_node.attribute("name").as_string();
+        const std::string value = metadata_node.text().as_string();
+        std::string type = metadata_node.attribute("type").as_string();
+        if(type == "")
+        {
+            type = "xs:string"; //Fill in the default type if it's not present.
+        }
+        const std::string preserve_str = 
metadata_node.attribute("preserve").as_string(); //Don't use as_bool since 
3MF's boolean parsing is more lenient.
+        const bool preserve = (preserve_str != "" && preserve_str != "0");
+        setMetaDataEntry(key, value, type, preserve);
     }
 
     pugi::xml_node build = xml_node.child("build");
@@ -68,6 +77,26 @@
         {
             SceneNode* temp_scene_node = createSceneNodeFromObject(xml_node, 
object_node);
             
temp_scene_node->setTransformation(item.attribute("transform").as_string());
+            
+            // Get all metadata from the item and update that.
+            const pugi::xml_node metadatagroup_node = 
item.child("metadatagroup");
+            if (metadatagroup_node)
+            {
+                for (pugi::xml_node setting = 
metadatagroup_node.child("metadata"); setting; setting = 
setting.next_sibling("metadata"))
+                {
+                    const std::string key = 
setting.attribute("name").as_string();
+                    const std::string value = setting.text().as_string();
+                    std::string type = setting.attribute("type").as_string();
+                    if(type == "") //Not specified.
+                    {
+                        type = "xs:string";
+                    }
+                    const std::string preserve_str = 
setting.attribute("preserve").as_string(); //Needs to be true if string is not 
"0", which is less strict than .as_bool();
+                    const bool preserve = (preserve_str != "" && preserve_str 
!= "0");
+                    temp_scene_node->setSetting(key, value, type, preserve);
+                }
+            }
+            
             scene_nodes.push_back(temp_scene_node);
         }
         else
@@ -91,7 +120,7 @@
     
     if(has_mesh_node)
     {
-        mesh_node_object_id = scene_node->getSettings()["mesh_node_objectid"];
+        mesh_node_object_id = 
scene_node->getSettings().at("mesh_node_objectid").value;
     }
 
     // We have to do the checking for children outside of the SceneNode 
creation itself, because it only has references.
@@ -149,9 +178,14 @@
     return all_nodes;
 }
 
-void Scene::setMetaDataEntry(std::string key, std::string value)
+void Scene::setMetaDataEntry(const std::string& key, const MetadataEntry& 
entry)
+{
+    metadata.emplace(key, entry);
+}
+
+void Scene::setMetaDataEntry(const std::string& key, const std::string& value, 
const std::string& type, const bool preserve)
 {
-    metadata[key] = value;
+    metadata.emplace(key, MetadataEntry(value, type, preserve));
 }
 
 std::string Scene::getUnit()
@@ -159,7 +193,7 @@
     return unit;
 }
 
-std::map< std::string, std::string > Scene::getMetadata()
+const std::map<std::string, MetadataEntry>& Scene::getMetadata() const
 {
     return metadata;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/src/Scene.h 
new/libSavitar-4.9/src/Scene.h
--- old/libSavitar-4.8/src/Scene.h      2020-10-09 18:43:31.000000000 +0200
+++ new/libSavitar-4.9/src/Scene.h      2021-03-02 11:33:23.000000000 +0100
@@ -1,7 +1,7 @@
 /*
  * This file is part of libSavitar
  *
- * Copyright (C) 2017 Ultimaker b.v. <[email protected]>
+ * Copyright (C) 2021 Ultimaker B.V. <[email protected]>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published
@@ -62,19 +62,30 @@
         void fillByXMLNode(pugi::xml_node xml_node);
 
         /**
+         * Store a metadata entry as metadata.
+         * @param key The key of the metadata.
+         * @param entry A MetadataEntry object containing metadata and
+         * additional properties.
+         */
+        void setMetaDataEntry(const std::string& key, const MetadataEntry& 
entry);
+
+        /**
          * Set a meta data entry of the scene.
          *
          * Note that this not adhere to the full 3mf spec yet. All keys are 
accepted.
          *
-         * \param key The key of the meta data.
-         * \param value The value of the meta data.
+         * \param key The key of the metadata.
+         * \param value The value of the metadata.
+         * \param type The data type of the metadata.
+         * \param preserve Whether the metadata entry needs to be preserved
+         * through save-load loops of the 3MF document.
          */
-        void setMetaDataEntry(std::string key, std::string value);
+        void setMetaDataEntry(const std::string& key, const std::string& 
value, const std::string& type = "xs:string", const bool preserve = false);
 
         /**
          * Get all meta data entries
          */
-        std::map<std::string, std::string> getMetadata();
+        const std::map<std::string, MetadataEntry>& getMetadata() const;
 
         /**
         * Get the unit (milimeter, inch, etc) of the scene.
@@ -86,7 +97,7 @@
 
     protected:
         std::vector< SceneNode*> scene_nodes;
-        std::map<std::string, std::string> metadata;
+        std::map<std::string, MetadataEntry> metadata;
         std::string unit;
 
         /**
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/src/SceneNode.cpp 
new/libSavitar-4.9/src/SceneNode.cpp
--- old/libSavitar-4.8/src/SceneNode.cpp        2020-10-09 18:43:31.000000000 
+0200
+++ new/libSavitar-4.9/src/SceneNode.cpp        2021-03-02 11:33:23.000000000 
+0100
@@ -1,7 +1,7 @@
 /*
  * This file is part of libSavitar
  *
- * Copyright (C) 2017 Ultimaker b.v. <[email protected]>
+ * Copyright (C) 2021 Ultimaker B.V. <[email protected]>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published
@@ -116,7 +116,7 @@
         {
             const std::string key = setting.attribute("key").as_string();
             const std::string value = setting.text().as_string();
-            settings[key] = value;
+            setSetting(key, value);
         }
     }
 
@@ -135,15 +135,20 @@
             std::string key = setting.attribute("name").as_string();
             const size_t pos = key.find_first_of(':');
 
-            // Only accept namespaces cura and implied 'default':
-            if (pos != std::string::npos && 
cura_equivalent_namespaces.count(key.substr(0, pos)) < 1)
+            //Make 'cura' namespace behave like the default.
+            if (pos != std::string::npos && 
cura_equivalent_namespaces.count(key.substr(0, pos)) == 1)
             {
-                continue;
+                key = key.substr(pos + 1);
             }
-
-            key = (pos != std::string::npos) ? key.substr(pos + 1) : key;
             const std::string value = setting.text().as_string();
-            settings[key] = value;
+            std::string type = setting.attribute("type").as_string();
+            if(type == "")
+            {
+                type = "xs:string";
+            }
+            std::string preserve_str = 
setting.attribute("preserve").as_string(); //as_bool is too strict. Any 
non-zero value evaluates as true. Parse this ourselves.
+            const bool preserve = (preserve_str != "" && preserve_str != "0");
+            setSetting(key, value, type, preserve);
         }
     }
 }
@@ -168,14 +173,19 @@
     this->name = name;
 }
 
-std::map< std::string, std::string > SceneNode::getSettings()
+const std::map<std::string, MetadataEntry>& SceneNode::getSettings() const
 {
     return settings;
 }
 
-void SceneNode::setSetting(std::string key, std::string value)
+void SceneNode::setSetting(const std::string& key, const MetadataEntry& entry)
+{
+    settings.emplace(key, entry);
+}
+
+void SceneNode::setSetting(const std::string& key, const std::string& value, 
const std::string& type, const bool preserve)
 {
-    settings[key] = value;
+    settings.emplace(key, MetadataEntry(value, type, preserve));
 }
 
 void SceneNode::removeSetting(std::string key)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/src/SceneNode.h 
new/libSavitar-4.9/src/SceneNode.h
--- old/libSavitar-4.8/src/SceneNode.h  2020-10-09 18:43:31.000000000 +0200
+++ new/libSavitar-4.9/src/SceneNode.h  2021-03-02 11:33:23.000000000 +0100
@@ -1,7 +1,7 @@
 /*
  * This file is part of libSavitar
  *
- * Copyright (C) 2017 Ultimaker b.v. <[email protected]>
+ * Copyright (C) 2021 Ultimaker B.V. <[email protected]>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published
@@ -21,6 +21,7 @@
 
 #include "SavitarExport.h"
 #include "MeshData.h"
+#include "MetadataEntry.h"
 
 #include <string>
 #include <vector>
@@ -72,11 +73,12 @@
          * Get the (per-object) settings attached to this SceneNode.
          * Note that this is part of the Cura Extension and not 3mf Core.
          */
-        std::map<std::string, std::string> getSettings();
+        const std::map<std::string, MetadataEntry>& getSettings() const;
 
-        void setSetting(std::string key, std::string value);
+        void setSetting(const std::string& key, const MetadataEntry& entry);
+        void setSetting(const std::string& key, const std::string& value, 
const std::string& type = "xs:string", const bool preserve = false);
         void removeSetting(std::string key);
-        
+
         /**
          * Type of the scene node. Can be "model", "solidsupport", "support", 
"surface", or "other". 
          * This defaults to "model"
@@ -90,7 +92,7 @@
         std::string transformation;
         std::vector<SceneNode*> children;
         MeshData mesh_data;
-        std::map<std::string, std::string> settings;
+        std::map<std::string, MetadataEntry> settings;
         std::string id;
         std::string name;
         std::string type;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/src/ThreeMFParser.cpp 
new/libSavitar-4.9/src/ThreeMFParser.cpp
--- old/libSavitar-4.8/src/ThreeMFParser.cpp    2020-10-09 18:43:31.000000000 
+0200
+++ new/libSavitar-4.9/src/ThreeMFParser.cpp    2021-03-02 11:33:23.000000000 
+0100
@@ -59,7 +59,7 @@
     model_node.append_attribute("xmlns") = 
xml_namespace::getDefaultUri().c_str();
     model_node.append_attribute("xmlns:cura") = 
xml_namespace::getCuraUri().c_str();
     model_node.append_attribute("xml:lang") ="en-US";
-
+    
     for(int i = 0; i < scene.getAllSceneNodes().size(); i++)
     {
         SceneNode* scene_node = scene.getAllSceneNodes().at(i);
@@ -77,17 +77,23 @@
         }
         object.append_attribute("type") = scene_node->getType().c_str();
 
-        std::map<std::string, std::string> per_object_settings = 
scene_node->getSettings();
+        const std::map<std::string, MetadataEntry>& per_object_settings = 
scene_node->getSettings();
         if(!per_object_settings.empty())
         {
             pugi::xml_node settings = object.append_child("metadatagroup");
-            for(const std::pair<std::string, std::string> setting_pair: 
per_object_settings)
+            for(const std::pair<std::string, MetadataEntry>& setting_pair: 
per_object_settings)
             {
                 pugi::xml_node setting = settings.append_child("metadata");
-                setting.append_attribute("name") = (std::string("cura:") + 
setting_pair.first).c_str();
-                setting.text().set(setting_pair.second.c_str());
-                setting.append_attribute("preserve") = "true";
-                setting.append_attribute("type") = "xs:string";
+                setting.append_attribute("name") = setting_pair.first.c_str();
+                setting.text().set(setting_pair.second.value.c_str());
+                if(setting_pair.second.type != "xs:string") //xs:string is the 
default type and doesn't need to be written.
+                {
+                    setting.append_attribute("type") = 
setting_pair.second.type.c_str();
+                }
+                if(setting_pair.second.preserve)
+                {
+                    setting.append_attribute("preserve") = "true";
+                }
             }
         }
         
@@ -117,7 +123,6 @@
             mesh_node_setting.append_attribute("name") = "mesh_node_objectid";
             
mesh_node_setting.text().set(scene_node->getMeshNode()->getId().c_str());
             mesh_node_setting.append_attribute("preserve") = "true";
-            mesh_node_setting.append_attribute("type") = "xs:string";
         }
     }
 
@@ -127,7 +132,22 @@
         item.append_attribute("objectid") = scene_node->getId().c_str();
         item.append_attribute("transform") = 
scene_node->getTransformation().c_str();
     }
-
+    
+    for(const std::pair<std::string, MetadataEntry>& metadata_pair: 
scene.getMetadata())
+    {
+        pugi::xml_node metadata_node = model_node.append_child("metadata");
+        metadata_node.append_attribute("name") = metadata_pair.first.c_str();
+        metadata_node.text().set(metadata_pair.second.value.c_str());
+        if(metadata_pair.second.type != "xs:string") //xs:string is the 
default and doesn't need to get written then.
+        {
+            metadata_node.append_attribute("type") = 
metadata_pair.second.type.c_str();
+        }
+        if(metadata_pair.second.preserve)
+        {
+            metadata_node.append_attribute("preserve") = "true";
+        }
+    }
+    
     std::stringstream ss;
     document.save(ss);
     return ss.str();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/libSavitar-4.8/tests/ThreeMFParserTest.cpp 
new/libSavitar-4.9/tests/ThreeMFParserTest.cpp
--- old/libSavitar-4.8/tests/ThreeMFParserTest.cpp      2020-10-09 
18:43:31.000000000 +0200
+++ new/libSavitar-4.9/tests/ThreeMFParserTest.cpp      2021-03-02 
11:33:23.000000000 +0100
@@ -81,13 +81,13 @@
     }
     // NOTE: To/from for content of vertices/triangles is tested in 
MeshDataTest.
 
-    std::map<std::string, std::string> settings;
+    std::map<std::string, MetadataEntry> settings;
 
     settings = nodes[0]->getSettings();
     EXPECT_NE(settings.find("extruder_nr"), settings.end());
-    EXPECT_EQ(settings["extruder_nr"].compare("0"), 0);
+    EXPECT_EQ(settings.at("extruder_nr").value.compare("0"), 0);
     EXPECT_NE(settings.find("bottom_layers"), settings.end());
-    EXPECT_EQ(settings["bottom_layers"].compare("20"), 0);
+    EXPECT_EQ(settings.at("bottom_layers").value.compare("20"), 0);
     EXPECT_EQ(settings.find("infill_pattern"), settings.end());
 
     settings = nodes[1]->getSettings();
@@ -95,10 +95,10 @@
 
     settings = nodes[2]->getSettings();
     EXPECT_NE(settings.find("extruder_nr"), settings.end());
-    EXPECT_EQ(settings["extruder_nr"].compare("1"), 0);
+    EXPECT_EQ(settings.at("extruder_nr").value.compare("1"), 0);
     EXPECT_EQ(settings.find("bottom_layers"), settings.end());
     EXPECT_NE(settings.find("infill_pattern"), settings.end());
-    EXPECT_EQ(settings["infill_pattern"].compare("concentric"), 0);
+    EXPECT_EQ(settings.at("infill_pattern").value.compare("concentric"), 0);
 
     EXPECT_EQ(nodes[0]->getName().compare("test_object"), 0);
     EXPECT_EQ(nodes[1]->getName().compare(""), 0);

Reply via email to