diff options
author | bubnikv <bubnikv@gmail.com> | 2017-12-21 18:56:33 +0300 |
---|---|---|
committer | bubnikv <bubnikv@gmail.com> | 2017-12-21 18:56:33 +0300 |
commit | f5160b7a72dcb9305e6220a88092e3bd1032387e (patch) | |
tree | b899493a87377c4ae9de17e3b68be150f5ce62f5 | |
parent | 1bf67b4b62c09d06eefab368b8698c291f05afde (diff) |
Fixed "Slic3r crashes when sending STLs with special characters to the printer"
https://github.com/prusa3d/Slic3r/issues/597
The "Send to OctoPrint" function will now send the file name encoded
in UTF-8, so the file name will not get mangled.
The C++ Slic3r::encode_path() function was returning a string to Perl,
which was marked as UTF-8. This has been fixed, now encode_path() returns
a plain Perl string.
Added path_to_filename, path_to_stem, path_to_extension, path_to_parent_path
Perl wrappers to boost::filesystem::path splitting functionality
to be able to split UTF-8 encoded files on Windows correctly.
-rw-r--r-- | lib/Slic3r/GUI/Plater.pm | 12 | ||||
-rw-r--r-- | xs/src/libslic3r/Utils.hpp | 25 | ||||
-rw-r--r-- | xs/src/libslic3r/utils.cpp | 40 | ||||
-rw-r--r-- | xs/xsp/XS.xsp | 30 | ||||
-rw-r--r-- | xs/xsp/my.map | 12 |
5 files changed, 113 insertions, 6 deletions
diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 91905b2b6..f05ea86bf 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1460,15 +1460,19 @@ sub send_gcode { my $ua = LWP::UserAgent->new; $ua->timeout(180); - my $enc_path = Slic3r::encode_path($self->{send_gcode_file}); my $res = $ua->post( "http://" . $self->{config}->octoprint_host . "/api/files/local", Content_Type => 'form-data', 'X-Api-Key' => $self->{config}->octoprint_apikey, Content => [ - # OctoPrint doesn't like Windows paths so we use basename() - # Also, since we need to read from filesystem we process it through encode_path() - file => [ $enc_path, basename($enc_path) ], + file => [ + # On Windows, the path has to be encoded in local code page for perl to be able to open it. + Slic3r::encode_path($self->{send_gcode_file}), + # Remove the UTF-8 flag from the perl string, so the LWP::UserAgent can insert + # the UTF-8 encoded string into the request as a byte stream. + Encode::encode_utf8(Slic3r::path_to_filename($self->{send_gcode_file})) + ], + print => $self->{send_gcode_file_print} ? 1 : 0, ], ); diff --git a/xs/src/libslic3r/Utils.hpp b/xs/src/libslic3r/Utils.hpp index 3afbc912f..fd60cf91a 100644 --- a/xs/src/libslic3r/Utils.hpp +++ b/xs/src/libslic3r/Utils.hpp @@ -25,16 +25,39 @@ void set_data_dir(const std::string &path); // Return a full path to the GUI resource files. const std::string& data_dir(); -extern std::string encode_path(const char *src); +// A special type for strings encoded in the local Windows 8-bit code page. +// This type is only needed for Perl bindings to relay to Perl that the string is raw, not UTF-8 encoded. +typedef std::string local_encoded_string; + +// Convert an UTF-8 encoded string into local coding. +// On Windows, the UTF-8 string is converted to a local 8-bit code page. +// On OSX and Linux, this function does no conversion and returns a copy of the source string. +extern local_encoded_string encode_path(const char *src); extern std::string decode_path(const char *src); extern std::string normalize_utf8_nfc(const char *src); +// File path / name / extension splitting utilities, working with UTF-8, +// to be published to Perl. +namespace PerlUtils { + // Get a file name including the extension. + extern std::string path_to_filename(const char *src); + // Get a file name without the extension. + extern std::string path_to_stem(const char *src); + // Get just the extension. + extern std::string path_to_extension(const char *src); + // Get a directory without the trailing slash. + extern std::string path_to_parent_path(const char *src); +}; + // Timestamp formatted for header_slic3r_generated(). extern std::string timestamp_str(); // Standard "generated by Slic3r version xxx timestamp xxx" header string, // to be placed at the top of Slic3r generated files. inline std::string header_slic3r_generated() { return std::string("generated by " SLIC3R_FORK_NAME " " SLIC3R_VERSION " " ) + timestamp_str(); } +// Encode a file into a multi-part HTTP response with a given boundary. +std::string octoprint_encode_file_send_request_content(const char *path, bool select, bool print, const char *boundary); + // Compute the next highest power of 2 of 32-bit v // http://graphics.stanford.edu/~seander/bithacks.html template<typename T> diff --git a/xs/src/libslic3r/utils.cpp b/xs/src/libslic3r/utils.cpp index ef05dcae7..1a7e16f11 100644 --- a/xs/src/libslic3r/utils.cpp +++ b/xs/src/libslic3r/utils.cpp @@ -10,6 +10,8 @@ #include <boost/algorithm/string/predicate.hpp> #include <boost/date_time/local_time/local_time.hpp> #include <boost/filesystem.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/nowide/fstream.hpp> #include <boost/nowide/integration/filesystem.hpp> #include <boost/nowide/convert.hpp> @@ -235,6 +237,17 @@ std::string normalize_utf8_nfc(const char *src) return boost::locale::normalize(src, boost::locale::norm_nfc, locale_utf8); } +namespace PerlUtils { + // Get a file name including the extension. + std::string path_to_filename(const char *src) { return boost::filesystem::path(src).filename().string(); } + // Get a file name without the extension. + std::string path_to_stem(const char *src) { return boost::filesystem::path(src).stem().string(); } + // Get just the extension. + std::string path_to_extension(const char *src) { return boost::filesystem::path(src).extension().string(); } + // Get a directory without the trailing slash. + std::string path_to_parent_path(const char *src) { return boost::filesystem::path(src).parent_path().string(); } +}; + std::string timestamp_str() { const auto now = boost::posix_time::second_clock::local_time(); @@ -247,4 +260,31 @@ std::string timestamp_str() return buf; } +std::string octoprint_encode_file_send_request_content(const char *cpath, bool select, bool print, const char *boundary) +{ + // Read the complete G-code string into a string buffer. + // It will throw if the file cannot be open or read. + std::stringstream str_stream; + { + boost::nowide::ifstream ifs(cpath); + str_stream << ifs.rdbuf(); + } + + boost::filesystem::path path(cpath); + std::string request = boundary + '\n'; + request += "Content-Disposition: form-data; name=\""; + request += path.stem().string() + "\"; filename=\"" + path.filename().string() + "\"\n"; + request += "Content-Type: application/octet-stream\n\n"; + request += str_stream.str(); + request += boundary + '\n'; + request += "Content-Disposition: form-data; name=\"select\"\n\n"; + request += select ? "true\n" : "false\n"; + request += boundary + '\n'; + request += "Content-Disposition: form-data; name=\"print\"\n\n"; + request += print ? "true\n" : "false\n"; + request += boundary + '\n'; + + return request; +} + }; // namespace Slic3r diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp index b2f772834..e0f6ab18b 100644 --- a/xs/xsp/XS.xsp +++ b/xs/xsp/XS.xsp @@ -91,7 +91,7 @@ data_dir() RETVAL = const_cast<char*>(Slic3r::data_dir().c_str()); OUTPUT: RETVAL -std::string +local_encoded_string encode_path(src) const char *src; CODE: @@ -112,6 +112,34 @@ normalize_utf8_nfc(src) RETVAL = Slic3r::normalize_utf8_nfc(src); OUTPUT: RETVAL +std::string +path_to_filename(src) + const char *src; + CODE: + RETVAL = Slic3r::PerlUtils::path_to_filename(src); + OUTPUT: RETVAL + +std::string +path_to_stem(src) + const char *src; + CODE: + RETVAL = Slic3r::PerlUtils::path_to_stem(src); + OUTPUT: RETVAL + +std::string +path_to_extension(src) + const char *src; + CODE: + RETVAL = Slic3r::PerlUtils::path_to_extension(src); + OUTPUT: RETVAL + +std::string +path_to_parent_path(src) + const char *src; + CODE: + RETVAL = Slic3r::PerlUtils::path_to_parent_path(src); + OUTPUT: RETVAL + void xspp_test_croak_hangs_on_strawberry() CODE: diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 7b59623d4..ba4f52f46 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -1,6 +1,7 @@ coordf_t T_NV std::string T_STD_STRING +local_encoded_string T_STD_STRING_LOCAL_ENCODING t_config_option_key T_STD_STRING t_model_material_id T_STD_STRING @@ -279,6 +280,14 @@ T_STD_STRING $var = std::string(c, len); } +INPUT +T_STD_STRING_LOCAL_ENCODING + { + size_t len; + const char * c = SvPV($arg, len); + $var = std::string(c, len); + } + T_STD_VECTOR_STD_STRING if (SvROK($arg) && SvTYPE(SvRV($arg))==SVt_PVAV) { AV* av = (AV*)SvRV($arg); @@ -438,6 +447,9 @@ OUTPUT T_STD_STRING $arg = newSVpvn_utf8( $var.c_str(), $var.length(), true ); +T_STD_STRING_LOCAL_ENCODING + $arg = newSVpvn( $var.c_str(), $var.length() ); + T_STD_VECTOR_STD_STRING AV* av = newAV(); $arg = newRV_noinc((SV*)av); |