diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp
index 70b9d5e1b..200e6a490 100644
--- a/src/libslic3r/Format/3mf.cpp
+++ b/src/libslic3r/Format/3mf.cpp
@@ -21,6 +21,7 @@
 #include <boost/filesystem/operations.hpp>
 #include <boost/nowide/fstream.hpp>
 #include <boost/nowide/cstdio.hpp>
+#include <boost/spirit/include/karma.hpp>
 
 #include <boost/property_tree/ptree.hpp>
 #include <boost/property_tree/xml_parser.hpp>
@@ -31,6 +32,12 @@ namespace pt = boost::property_tree;
 #include <Eigen/Dense>
 #include "miniz_extension.hpp"
 
+// Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
+// https://github.com/boostorg/spirit/pull/586
+// where the exported string is one digit shorter than it should be to guarantee lossless round trip.
+// The code is left here for the ocasion boost guys improve.
+#define EXPORT_3MF_USE_SPIRIT_KARMA_FP 0
+
 // VERSION NUMBERS
 // 0 : .3mf, files saved by older slic3r or other applications. No version definition in them.
 // 1 : Introduction of 3mf versioning. No other change in data saved into 3mf files.
@@ -2030,7 +2037,7 @@ namespace Slic3r {
         bool _add_relationships_file_to_archive(mz_zip_archive& archive);
         bool _add_model_file_to_archive(const std::string& filename, mz_zip_archive& archive, const Model& model, IdToObjectDataMap& objects_data);
         bool _add_object_to_model_stream(mz_zip_writer_staged_context &context, unsigned int& object_id, ModelObject& object, BuildItemsList& build_items, VolumeToOffsetsMap& volumes_offsets);
-        bool _add_mesh_to_object_stream(mz_zip_writer_staged_context &context, std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
+        bool _add_mesh_to_object_stream(mz_zip_writer_staged_context &context, ModelObject& object, VolumeToOffsetsMap& volumes_offsets);
         bool _add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items);
         bool _add_layer_height_profile_file_to_archive(mz_zip_archive& archive, Model& model);
         bool _add_layer_config_ranges_file_to_archive(mz_zip_archive& archive, Model& model);
@@ -2360,7 +2367,10 @@ namespace Slic3r {
 
             if (id == 0)
             {
-                if (!_add_mesh_to_object_stream(context, stream, object, volumes_offsets))
+                std::string buf = stream.str();
+                reset_stream(stream);
+                if ((! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) ||
+                    ! _add_mesh_to_object_stream(context, object, volumes_offsets))
                 {
                     add_error("Unable to add mesh to archive");
                     return false;
@@ -2388,28 +2398,76 @@ namespace Slic3r {
         return buf.empty() || mz_zip_writer_add_staged_data(&context, buf.data(), buf.size());
     }
 
-    bool _3MF_Exporter::_add_mesh_to_object_stream(mz_zip_writer_staged_context &context, std::stringstream& stream, ModelObject& object, VolumeToOffsetsMap& volumes_offsets)
+#if EXPORT_3MF_USE_SPIRIT_KARMA_FP
+    template <typename Num>
+    struct coordinate_policy_fixed : boost::spirit::karma::real_policies<Num>
     {
-        stream << "   <" << MESH_TAG << ">\n";
-        stream << "    <" << VERTICES_TAG << ">\n";
+        static int floatfield(Num n) { return fmtflags::fixed; }
+        // Number of decimal digits to maintain float accuracy when storing into a text file and parsing back.
+        static unsigned precision(Num /* n */) { return std::numeric_limits<Num>::max_digits10 + 1; }
+        // No trailing zeros, thus for fmtflags::fixed usually much less than max_digits10 decimal numbers will be produced.
+        static bool trailing_zeros(Num /* n */) { return false; }
+    };
+    template <typename Num>
+    struct coordinate_policy_scientific : coordinate_policy_fixed<Num>
+    {
+        static int floatfield(Num n) { return fmtflags::scientific; }
+    };
+    // Define a new generator type based on the new coordinate policy.
+    using coordinate_type_fixed      = boost::spirit::karma::real_generator<float, coordinate_policy_fixed<float>>;
+    using coordinate_type_scientific = boost::spirit::karma::real_generator<float, coordinate_policy_scientific<float>>;
+#endif // EXPORT_3MF_USE_SPIRIT_KARMA_FP
 
-        // Flush at the rate of 6400 lines per miniz invocation,
-        // that corresponds to roughly 5x 64kB blocks.
-        size_t       lines = 6400;
-        auto         flush = [this, &lines, &context, &stream]() {
-            if (lines == 0) {
-                lines = 6400;
-                std::string buf = stream.str();
-                reset_stream(stream);
-                if (! buf.empty() && ! mz_zip_writer_add_staged_data(&context, buf.data(), buf.size())) {
+    bool _3MF_Exporter::_add_mesh_to_object_stream(mz_zip_writer_staged_context &context, ModelObject& object, VolumeToOffsetsMap& volumes_offsets)
+    {
+        std::string output_buffer;
+        output_buffer += "   <";
+        output_buffer += MESH_TAG;
+        output_buffer += ">\n    <";
+        output_buffer += VERTICES_TAG;
+        output_buffer += ">\n";
+
+        auto flush = [this, &output_buffer, &context](bool force = false) {
+            if ((force && ! output_buffer.empty()) || output_buffer.size() >= 65536 * 16) {
+                if (! mz_zip_writer_add_staged_data(&context, output_buffer.data(), output_buffer.size())) {
                     add_error("Error during writing or compression");
                     return false;
                 }
-            } else
-                -- lines;
+                output_buffer.clear();
+            }
             return true;
         };
 
+        auto format_coordinate = [](float f, char *buf) -> char* {
+#if EXPORT_3MF_USE_SPIRIT_KARMA_FP
+            // Slightly faster than sprintf("%.9g"), but there is an issue with the karma floating point formatter,
+            // https://github.com/boostorg/spirit/pull/586
+            // where the exported string is one digit shorter than it should be to guarantee lossless round trip.
+            // The code is left here for the ocasion boost guys improve.
+            coordinate_type_fixed      const coordinate_fixed      = coordinate_type_fixed();
+            coordinate_type_scientific const coordinate_scientific = coordinate_type_scientific();
+            // Format "f" in a fixed format.
+            char *ptr = buf;
+            boost::spirit::karma::generate(ptr, coordinate_fixed, f);
+            // Format "f" in a scientific format.
+            char *ptr2 = ptr;
+            boost::spirit::karma::generate(ptr2, coordinate_scientific, f);
+            // Return end of the shorter string.
+            auto len2 = ptr2 - ptr;
+            if (ptr - buf > len2) {
+                // Move the shorter scientific form to the front.
+                memcpy(buf, ptr, len2);
+                ptr = buf + len2;
+            }
+            // Return pointer to the end.
+            return ptr;
+#else
+            // Round-trippable float, shortest possible.
+            return buf + sprintf(buf, "%.9g", f);
+#endif
+        };
+
+        char buf[256];
         unsigned int vertices_count = 0;
         for (ModelVolume* volume : object.volumes)
         {
@@ -2436,18 +2494,27 @@ namespace Slic3r {
 
             for (size_t i = 0; i < its.vertices.size(); ++i)
             {
-                stream << "     <" << VERTEX_TAG << " ";
                 Vec3f v = (matrix * its.vertices[i].cast<double>()).cast<float>();
-                stream << "x=\"" << v(0) << "\" ";
-                stream << "y=\"" << v(1) << "\" ";
-                stream << "z=\"" << v(2) << "\" />\n";
+                char *ptr = buf;
+                boost::spirit::karma::generate(ptr, boost::spirit::lit("     <") << VERTEX_TAG << " x=\"");
+                ptr = format_coordinate(v.x(), ptr);
+                boost::spirit::karma::generate(ptr, "\" y=\"");
+                ptr = format_coordinate(v.y(), ptr);
+                boost::spirit::karma::generate(ptr, "\" z=\"");
+                ptr = format_coordinate(v.z(), ptr);
+                boost::spirit::karma::generate(ptr, "\" />\n");
+                *ptr = '\0';
+                output_buffer += buf;
                 if (! flush())
                     return false;
             }
         }
 
-        stream << "    </" << VERTICES_TAG << ">\n";
-        stream << "    <" << TRIANGLES_TAG << ">\n";
+        output_buffer += "    </";
+        output_buffer += VERTICES_TAG;
+        output_buffer += ">\n    <";
+        output_buffer += TRIANGLES_TAG;
+        output_buffer += ">\n";
 
         unsigned int triangles_count = 0;
         for (ModelVolume* volume : object.volumes)
@@ -2467,33 +2534,51 @@ namespace Slic3r {
 
             for (int i = 0; i < int(its.indices.size()); ++ i)
             {
-                stream << "     <" << TRIANGLE_TAG << " ";
-                for (int j = 0; j < 3; ++j)
                 {
-                    stream << "v" << j + 1 << "=\"" << its.indices[i][j] + volume_it->second.first_vertex_id << "\" ";
+                    const Vec3i &idx = its.indices[i];
+                    char *ptr = buf;
+                    boost::spirit::karma::generate(ptr, boost::spirit::lit("     <") << TRIANGLE_TAG <<
+                        " v1=\"" << boost::spirit::int_ <<
+                        "\" v2=\"" << boost::spirit::int_ <<
+                        "\" v3=\"" << boost::spirit::int_ << "\" ",
+                        idx[0] + volume_it->second.first_vertex_id,
+                        idx[1] + volume_it->second.first_vertex_id,
+                        idx[2] + volume_it->second.first_vertex_id);
+                    *ptr = '\0';
+                    output_buffer += buf;
                 }
 
                 std::string custom_supports_data_string = volume->supported_facets.get_triangle_as_string(i);
-                if (! custom_supports_data_string.empty())
-                    stream << CUSTOM_SUPPORTS_ATTR << "=\"" << custom_supports_data_string << "\" ";
+                if (! custom_supports_data_string.empty()) {
+                    output_buffer += CUSTOM_SUPPORTS_ATTR;
+                    output_buffer += "=\"";
+                    output_buffer += custom_supports_data_string;
+                    output_buffer += "\" ";
+                }
 
                 std::string custom_seam_data_string = volume->seam_facets.get_triangle_as_string(i);
-                if (! custom_seam_data_string.empty())
-                    stream << CUSTOM_SEAM_ATTR << "=\"" << custom_seam_data_string << "\" ";
+                if (! custom_seam_data_string.empty()) {
+                    output_buffer += CUSTOM_SEAM_ATTR;
+                    output_buffer += "=\"";
+                    output_buffer += custom_seam_data_string;
+                    output_buffer += "\" ";
+                }
 
-                stream << "/>\n";
+                output_buffer += "/>\n";
 
                 if (! flush())
                     return false;
             }
         }
 
-        stream << "    </" << TRIANGLES_TAG << ">\n";
-        stream << "   </" << MESH_TAG << ">\n";
+        output_buffer += "    </";
+        output_buffer += TRIANGLES_TAG;
+        output_buffer += ">\n   </";
+        output_buffer += MESH_TAG;
+        output_buffer += ">\n";
 
         // Force flush.
-        lines = 0;
-        return flush();
+        return flush(true);
     }
 
     bool _3MF_Exporter::_add_build_to_model_stream(std::stringstream& stream, const BuildItemsList& build_items)