Unicode handling:

Removed the Perl dependencies on Encode, Encode::Locale and Unicode::Normalize.
Added dependency on boost::locale.
Added encode_path, decode_path, normalize_utf8 functions to Slic3r.xs

Slic3r.xs has been made mostly utf8 safe by using the boost::nowide library,
thanks to @alexrj for the idea.

Simplified the encode_path / decode_path stuff:
wxWidgets are unicode already, so there is no need to decode_path() from it.
Perl / win32 interfacing is non-unicode, so decode_path() is executed
on ARGV just at the beginning of the perl scripts.
This commit is contained in:
bubnikv 2017-08-03 17:31:31 +02:00
parent 31085fb1d7
commit 1385018724
33 changed files with 236 additions and 186 deletions

View File

@ -8,8 +8,6 @@ use File::Spec;
my %prereqs = qw( my %prereqs = qw(
Devel::CheckLib 0 Devel::CheckLib 0
Encode 0
Encode::Locale 1.05
ExtUtils::MakeMaker 6.80 ExtUtils::MakeMaker 6.80
ExtUtils::ParseXS 3.22 ExtUtils::ParseXS 3.22
File::Basename 0 File::Basename 0
@ -24,7 +22,6 @@ my %prereqs = qw(
IO::Scalar 0 IO::Scalar 0
threads 1.96 threads 1.96
Time::HiRes 0 Time::HiRes 0
Unicode::Normalize 0
); );
my %recommends = qw( my %recommends = qw(
Class::XSAccessor 0 Class::XSAccessor 0

View File

@ -75,20 +75,10 @@ use Slic3r::Print::Simple;
use Slic3r::Surface; use Slic3r::Surface;
our $build = eval "use Slic3r::Build; 1"; our $build = eval "use Slic3r::Build; 1";
use Thread::Semaphore; use Thread::Semaphore;
use Encode::Locale 1.05;
use Encode;
use Unicode::Normalize;
# Scaling between the float and integer coordinates. # Scaling between the float and integer coordinates.
# Floats are in mm. # Floats are in mm.
use constant SCALING_FACTOR => 0.000001; use constant SCALING_FACTOR => 0.000001;
use constant LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER => 0.15;
# Following constants are used by the infill algorithms and integration tests.
# Resolution to simplify perimeters to. These constants are now used in C++ code only. Better to publish them to Perl from the C++ code.
# use constant RESOLUTION => 0.0125;
# use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR;
use constant INFILL_OVERLAP_OVER_SPACING => 0.3;
# Keep track of threads we created. Perl worker threads shall not create further threads. # Keep track of threads we created. Perl worker threads shall not create further threads.
my @threads = (); my @threads = ();
@ -211,39 +201,6 @@ sub resume_all_threads {
$pause_sema->up; $pause_sema->up;
} }
# Convert a Unicode path to a file system locale.
# The encoding is (from Encode::Locale POD):
# Alias | Windows | Mac OS X | POSIX
# locale_fs | ANSI | UTF-8 | nl_langinfo
# where nl_langinfo is en-US.UTF-8 on a modern Linux as well.
# So this conversion seems to make the most sense on Windows.
sub encode_path {
my ($path) = @_;
# UTF8 encoding is not unique. Normalize the UTF8 string to make the file names unique.
# Unicode::Normalize::NFC() returns the Normalization Form C (formed by canonical decomposition followed by canonical composition).
$path = Unicode::Normalize::NFC($path);
$path = Encode::encode(locale_fs => $path);
return $path;
}
# Convert a path coded by a file system locale to Unicode.
sub decode_path {
my ($path) = @_;
$path = Encode::decode(locale_fs => $path)
unless Encode::is_utf8($path);
# The filesystem might force a normalization form (like HFS+ does) so
# if we rely on the filename being comparable after the open() + readdir()
# roundtrip (like when creating and then selecting a preset), we need to
# restore our normalization form.
$path = Unicode::Normalize::NFC($path);
return $path;
}
# Open a file by converting $filename to local file system locales. # Open a file by converting $filename to local file system locales.
sub open { sub open {
my ($fh, $mode, $filename) = @_; my ($fh, $mode, $filename) = @_;

View File

@ -101,7 +101,7 @@ sub OnInit {
# Unix: ~/.Slic3r # Unix: ~/.Slic3r
# Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" # Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r"
# Mac: "~/Library/Application Support/Slic3r" # Mac: "~/Library/Application Support/Slic3r"
$datadir ||= Slic3r::decode_path(Wx::StandardPaths::Get->GetUserDataDir); $datadir ||= Wx::StandardPaths::Get->GetUserDataDir;
my $enc_datadir = Slic3r::encode_path($datadir); my $enc_datadir = Slic3r::encode_path($datadir);
Slic3r::debugf "Data directory: %s\n", $datadir; Slic3r::debugf "Data directory: %s\n", $datadir;
@ -370,9 +370,8 @@ sub open_model {
$dialog->Destroy; $dialog->Destroy;
return; return;
} }
my @input_files = map Slic3r::decode_path($_), $dialog->GetPaths; my @input_files = $dialog->GetPaths;
$dialog->Destroy; $dialog->Destroy;
return @input_files; return @input_files;
} }

View File

@ -286,7 +286,7 @@ sub _load_stl {
$dialog->Destroy; $dialog->Destroy;
return; return;
} }
my $input_file = Slic3r::decode_path($dialog->GetPaths); my $input_file = $dialog->GetPaths;
$dialog->Destroy; $dialog->Destroy;
my $model = Slic3r::Model->read_from_file($input_file); my $model = Slic3r::Model->read_from_file($input_file);

View File

@ -368,7 +368,7 @@ sub quick_slice {
$dialog->Destroy; $dialog->Destroy;
return; return;
} }
$input_file = Slic3r::decode_path($dialog->GetPaths); $input_file = $dialog->GetPaths;
$dialog->Destroy; $dialog->Destroy;
$qs_last_input_file = $input_file unless $params{export_svg}; $qs_last_input_file = $input_file unless $params{export_svg};
} else { } else {
@ -428,7 +428,7 @@ sub quick_slice {
$dlg->Destroy; $dlg->Destroy;
return; return;
} }
$output_file = Slic3r::decode_path($dlg->GetPath); $output_file = $dlg->GetPath;
$qs_last_output_file = $output_file unless $params{export_svg}; $qs_last_output_file = $output_file unless $params{export_svg};
$Slic3r::GUI::Settings->{_}{last_output_path} = dirname($output_file); $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($output_file);
wxTheApp->save_settings; wxTheApp->save_settings;
@ -482,7 +482,7 @@ sub repair_stl {
$dialog->Destroy; $dialog->Destroy;
return; return;
} }
$input_file = Slic3r::decode_path($dialog->GetPaths); $input_file = $dialog->GetPaths;
$dialog->Destroy; $dialog->Destroy;
} }
@ -495,14 +495,14 @@ sub repair_stl {
$dlg->Destroy; $dlg->Destroy;
return undef; return undef;
} }
$output_file = Slic3r::decode_path($dlg->GetPath); $output_file = $dlg->GetPath;
$dlg->Destroy; $dlg->Destroy;
} }
my $tmesh = Slic3r::TriangleMesh->new; my $tmesh = Slic3r::TriangleMesh->new;
$tmesh->ReadSTLFile(Slic3r::encode_path($input_file)); $tmesh->ReadSTLFile($input_file);
$tmesh->repair; $tmesh->repair;
$tmesh->WriteOBJFile(Slic3r::encode_path($output_file)); $tmesh->WriteOBJFile($output_file);
Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair"); Slic3r::GUI::show_info($self, "Your file was repaired.", "Repair");
} }
@ -529,7 +529,7 @@ sub export_config {
my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename, my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename,
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if ($dlg->ShowModal == wxID_OK) { if ($dlg->ShowModal == wxID_OK) {
my $file = Slic3r::decode_path($dlg->GetPath); my $file = $dlg->GetPath;
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
wxTheApp->save_settings; wxTheApp->save_settings;
$last_config = $file; $last_config = $file;
@ -548,7 +548,7 @@ sub load_config_file {
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini", my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST); 'INI files (*.ini, *.gcode)|*.ini;*.INI;*.gcode;*.g', wxFD_OPEN | wxFD_FILE_MUST_EXIST);
return unless $dlg->ShowModal == wxID_OK; return unless $dlg->ShowModal == wxID_OK;
$file = Slic3r::decode_path($dlg->GetPaths); $file = $dlg->GetPaths;
$dlg->Destroy; $dlg->Destroy;
} }
for my $tab (values %{$self->{options_tabs}}) { for my $tab (values %{$self->{options_tabs}}) {
@ -574,7 +574,7 @@ sub export_configbundle {
my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', $dir, $filename, my $dlg = Wx::FileDialog->new($self, 'Save presets bundle as:', $dir, $filename,
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if ($dlg->ShowModal == wxID_OK) { if ($dlg->ShowModal == wxID_OK) {
my $file = Slic3r::decode_path($dlg->GetPath); my $file = $dlg->GetPath;
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file); $Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
wxTheApp->save_settings; wxTheApp->save_settings;
@ -604,7 +604,7 @@ sub load_configbundle {
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini", my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
&Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST); &Slic3r::GUI::FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
return unless $dlg->ShowModal == wxID_OK; return unless $dlg->ShowModal == wxID_OK;
$file = Slic3r::decode_path($dlg->GetPaths); $file = $dlg->GetPaths;
$dlg->Destroy; $dlg->Destroy;
} }

View File

@ -1408,7 +1408,7 @@ sub export_gcode {
$dlg->Destroy; $dlg->Destroy;
return; return;
} }
my $path = Slic3r::decode_path($dlg->GetPath); my $path = $dlg->GetPath;
$Slic3r::GUI::Settings->{_}{last_output_path} = dirname($path); $Slic3r::GUI::Settings->{_}{last_output_path} = dirname($path);
wxTheApp->save_settings; wxTheApp->save_settings;
$self->{export_gcode_output_file} = $path; $self->{export_gcode_output_file} = $path;
@ -1574,7 +1574,7 @@ sub send_gcode {
my $ua = LWP::UserAgent->new; my $ua = LWP::UserAgent->new;
$ua->timeout(180); $ua->timeout(180);
my $path = Slic3r::encode_path($self->{send_gcode_file}); my $enc_path = Slic3r::encode_path($self->{send_gcode_file});
my $res = $ua->post( my $res = $ua->post(
"http://" . $self->{config}->octoprint_host . "/api/files/local", "http://" . $self->{config}->octoprint_host . "/api/files/local",
Content_Type => 'form-data', Content_Type => 'form-data',
@ -1582,7 +1582,7 @@ sub send_gcode {
Content => [ Content => [
# OctoPrint doesn't like Windows paths so we use basename() # OctoPrint doesn't like Windows paths so we use basename()
# Also, since we need to read from filesystem we process it through encode_path() # Also, since we need to read from filesystem we process it through encode_path()
file => [ $path, basename($path) ], file => [ $enc_path, basename($enc_path) ],
], ],
); );
@ -1603,7 +1603,7 @@ sub export_stl {
return if !@{$self->{objects}}; return if !@{$self->{objects}};
my $output_file = $self->_get_export_file('STL') or return; my $output_file = $self->_get_export_file('STL') or return;
$self->{model}->store_stl(Slic3r::encode_path($output_file), 1); $self->{model}->store_stl($output_file, 1);
$self->statusbar->SetStatusText("STL file exported to $output_file"); $self->statusbar->SetStatusText("STL file exported to $output_file");
} }
@ -1644,7 +1644,7 @@ sub export_object_stl {
my $model_object = $self->{model}->objects->[$obj_idx]; my $model_object = $self->{model}->objects->[$obj_idx];
my $output_file = $self->_get_export_file('STL') or return; my $output_file = $self->_get_export_file('STL') or return;
$model_object->mesh->write_binary(Slic3r::encode_path($output_file)); $model_object->mesh->write_binary($output_file);
$self->statusbar->SetStatusText("STL file exported to $output_file"); $self->statusbar->SetStatusText("STL file exported to $output_file");
} }
@ -1654,7 +1654,7 @@ sub export_amf {
return if !@{$self->{objects}}; return if !@{$self->{objects}};
my $output_file = $self->_get_export_file('AMF') or return; my $output_file = $self->_get_export_file('AMF') or return;
$self->{model}->store_amf(Slic3r::encode_path($output_file)); $self->{model}->store_amf($output_file);
$self->statusbar->SetStatusText("AMF file exported to $output_file"); $self->statusbar->SetStatusText("AMF file exported to $output_file");
} }
@ -1674,7 +1674,7 @@ sub _get_export_file {
$dlg->Destroy; $dlg->Destroy;
return undef; return undef;
} }
$output_file = Slic3r::decode_path($dlg->GetPath); $output_file = $dlg->GetPath;
$dlg->Destroy; $dlg->Destroy;
} }
return $output_file; return $output_file;

View File

@ -112,8 +112,9 @@ sub new {
my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal; my $res = Wx::MessageDialog->new($self, "Are you sure you want to delete the selected preset?", 'Delete Preset', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION)->ShowModal;
return unless $res == wxID_YES; return unless $res == wxID_YES;
# Delete the file. # Delete the file.
my $path = Slic3r::encode_path($self->{presets}[$i]->file); my $path = $self->{presets}[$i]->file;
if (-e $path && ! unlink $path) { my $enc_path = Slic3r::encode_path($path);
if (-e $enc_path && ! unlink $enc_path) {
# Cannot delete the file, therefore the item will not be removed from the selection. # Cannot delete the file, therefore the item will not be removed from the selection.
Slic3r::GUI::show_error($self, "Cannot delete file $path : $!"); Slic3r::GUI::show_error($self, "Cannot delete file $path : $!");
return; return;
@ -255,7 +256,7 @@ sub select_preset {
sub select_preset_by_name { sub select_preset_by_name {
my ($self, $name) = @_; my ($self, $name) = @_;
$name = Unicode::Normalize::NFC($name); $name = Slic3r::normalize_utf8_nfc($name);
$self->select_preset(first { $self->{presets}[$_]->name eq $name } 0 .. $#{$self->{presets}}); $self->select_preset(first { $self->{presets}[$_]->name eq $name } 0 .. $#{$self->{presets}});
} }

View File

@ -75,21 +75,9 @@ sub export_gcode {
# output everything to a G-code file # output everything to a G-code file
my $output_file = $self->output_filepath($params{output_file} // ''); my $output_file = $self->output_filepath($params{output_file} // '');
$self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : "")); $self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : ""));
{ die "G-code export to " . $output_file . " failed\n"
# open output gcode file if we weren't supplied a file-handle if ! Slic3r::GCode->new->do_export($self, $output_file);
my $tempfile = "$output_file.tmp";
my $gcode = Slic3r::GCode->new();
my $result = $gcode->do_export($self, Slic3r::encode_path($tempfile));
die $result . "\n" if ($result ne '');
my $i;
for ($i = 0; $i < 5; $i += 1) {
last if (rename Slic3r::encode_path($tempfile), Slic3r::encode_path($output_file));
# Wait for 1/4 seconds and try to rename once again.
select(undef, undef, undef, 0.25);
}
Slic3r::debugf "Failed to remove the output G-code file from $tempfile to $output_file. Is $tempfile locked?\n" if ($i == 5);
}
# run post-processing scripts # run post-processing scripts
if (@{$self->config->post_process}) { if (@{$self->config->post_process}) {

View File

@ -18,6 +18,9 @@ use Time::HiRes qw(gettimeofday tv_interval);
$|++; $|++;
binmode STDOUT, ':utf8'; binmode STDOUT, ':utf8';
# Convert all parameters from the local code page to utf8 on Windows.
@ARGV = map Slic3r::decode_path($_), @ARGV if $^O eq 'MSWin32';
our %opt = (); our %opt = ();
my %cli_options = (); my %cli_options = ();
{ {
@ -65,7 +68,6 @@ my %cli_options = ();
my @external_configs = (); my @external_configs = ();
if ($opt{load}) { if ($opt{load}) {
foreach my $configfile (@{$opt{load}}) { foreach my $configfile (@{$opt{load}}) {
$configfile = Slic3r::decode_path($configfile);
if (-e $configfile) { if (-e $configfile) {
push @external_configs, Slic3r::Config->load($configfile); push @external_configs, Slic3r::Config->load($configfile);
} elsif (-e "$FindBin::Bin/$configfile") { } elsif (-e "$FindBin::Bin/$configfile") {
@ -102,7 +104,7 @@ my $gui;
if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") { if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
{ {
no warnings 'once'; no warnings 'once';
$Slic3r::GUI::datadir = Slic3r::decode_path($opt{datadir} // ''); $Slic3r::GUI::datadir = $opt{datadir} // '';
$Slic3r::GUI::no_controller = $opt{no_controller}; $Slic3r::GUI::no_controller = $opt{no_controller};
$Slic3r::GUI::no_plater = $opt{no_plater}; $Slic3r::GUI::no_plater = $opt{no_plater};
$Slic3r::GUI::autosave = $opt{autosave}; $Slic3r::GUI::autosave = $opt{autosave};
@ -111,7 +113,7 @@ if ((!@ARGV || $opt{gui}) && !$opt{save} && eval "require Slic3r::GUI; 1") {
setlocale(LC_NUMERIC, 'C'); setlocale(LC_NUMERIC, 'C');
$gui->{mainframe}->load_config_file($_) for @{$opt{load}}; $gui->{mainframe}->load_config_file($_) for @{$opt{load}};
$gui->{mainframe}->load_config($cli_config); $gui->{mainframe}->load_config($cli_config);
my @input_files = map Slic3r::decode_path($_), @ARGV; my @input_files = @ARGV;
$gui->{mainframe}{plater}->load_files(\@input_files) unless $opt{no_plater}; $gui->{mainframe}{plater}->load_files(\@input_files) unless $opt{no_plater};
$gui->MainLoop; $gui->MainLoop;
exit; exit;
@ -123,7 +125,6 @@ if (@ARGV) { # slicing from command line
if ($opt{repair}) { if ($opt{repair}) {
foreach my $file (@ARGV) { foreach my $file (@ARGV) {
$file = Slic3r::decode_path($file);
die "Repair is currently supported only on STL files\n" die "Repair is currently supported only on STL files\n"
if $file !~ /\.[sS][tT][lL]$/; if $file !~ /\.[sS][tT][lL]$/;
@ -139,7 +140,6 @@ if (@ARGV) { # slicing from command line
if ($opt{cut}) { if ($opt{cut}) {
foreach my $file (@ARGV) { foreach my $file (@ARGV) {
$file = Slic3r::decode_path($file);
my $model = Slic3r::Model->read_from_file($file); my $model = Slic3r::Model->read_from_file($file);
my $mesh = $model->mesh; my $mesh = $model->mesh;
$mesh->translate(0, 0, -$mesh->bounding_box->z_min); $mesh->translate(0, 0, -$mesh->bounding_box->z_min);
@ -158,7 +158,6 @@ if (@ARGV) { # slicing from command line
if ($opt{split}) { if ($opt{split}) {
foreach my $file (@ARGV) { foreach my $file (@ARGV) {
$file = Slic3r::decode_path($file);
my $model = Slic3r::Model->read_from_file($file); my $model = Slic3r::Model->read_from_file($file);
my $mesh = $model->mesh; my $mesh = $model->mesh;
$mesh->repair; $mesh->repair;
@ -167,14 +166,13 @@ if (@ARGV) { # slicing from command line
foreach my $new_mesh (@{$mesh->split}) { foreach my $new_mesh (@{$mesh->split}) {
my $output_file = sprintf '%s_%02d.stl', $file, ++$part_count; my $output_file = sprintf '%s_%02d.stl', $file, ++$part_count;
printf "Writing to %s\n", basename($output_file); printf "Writing to %s\n", basename($output_file);
$new_mesh->write_binary(Slic3r::encode_path($output_file)); $new_mesh->write_binary($output_file);
} }
} }
exit; exit;
} }
while (my $input_file = shift @ARGV) { while (my $input_file = shift @ARGV) {
$input_file = Slic3r::decode_path($input_file);
my $model; my $model;
if ($opt{merge}) { if ($opt{merge}) {
my @models = map Slic3r::Model->read_from_file($_), $input_file, (splice @ARGV, 0); my @models = map Slic3r::Model->read_from_file($_), $input_file, (splice @ARGV, 0);

View File

@ -14,6 +14,9 @@ use Getopt::Long qw(:config no_auto_abbrev);
use Slic3r; use Slic3r;
$|++; $|++;
# Convert all parameters from the local code page to utf8 on Windows.
@ARGV = map Slic3r::decode_path($_), @ARGV if $^O eq 'MSWin32';
my %opt = (); my %opt = ();
{ {
my %options = ( my %options = (
@ -25,12 +28,12 @@ my %opt = ();
} }
{ {
my $model = Slic3r::Model->load_amf(Slic3r::encode_path($ARGV[0])); my $model = Slic3r::Model->load_amf($ARGV[0]);
my $output_file = $ARGV[0]; my $output_file = $ARGV[0];
$output_file =~ s/\.[aA][mM][fF](?:\.[xX][mM][lL])?$/\.stl/; $output_file =~ s/\.[aA][mM][fF](?:\.[xX][mM][lL])?$/\.stl/;
printf "Writing to %s\n", basename($output_file); printf "Writing to %s\n", basename($output_file);
$model->store_stl(Slic3r::encode_path($output_file), binary => !$opt{ascii}); $model->store_stl($output_file, binary => !$opt{ascii});
} }

View File

@ -18,7 +18,7 @@ $|++;
$ARGV[0] or usage(1); $ARGV[0] or usage(1);
if (-e $ARGV[0]) { if (-e $ARGV[0]) {
my $model = Slic3r::Model->load_stl(Slic3r::encode_path($ARGV[0]), basename($ARGV[0])); my $model = Slic3r::Model->load_stl($ARGV[0], basename($ARGV[0]));
$model->objects->[0]->add_instance(offset => Slic3r::Pointf->new(0,0)); $model->objects->[0]->add_instance(offset => Slic3r::Pointf->new(0,0));
my $mesh = $model->mesh; my $mesh = $model->mesh;
$mesh->repair; $mesh->repair;
@ -27,7 +27,7 @@ if (-e $ARGV[0]) {
exit 0; exit 0;
} elsif ((my $model = Slic3r::Test::model($ARGV[0]))) { } elsif ((my $model = Slic3r::Test::model($ARGV[0]))) {
$ARGV[1] or die "Missing writeable destination as second argument\n"; $ARGV[1] or die "Missing writeable destination as second argument\n";
$model->store_stl(Slic3r::encode_path($ARGV[1]), 1); $model->store_stl($ARGV[1], 1);
printf "Model $ARGV[0] written to $ARGV[1]\n"; printf "Model $ARGV[0] written to $ARGV[1]\n";
exit 0; exit 0;
} else { } else {

View File

@ -25,7 +25,7 @@ my %opt = ();
} }
{ {
my $model = Slic3r::Model->load_stl(Slic3r::encode_path($ARGV[0]), basename($ARGV[0])); my $model = Slic3r::Model->load_stl($ARGV[0], basename($ARGV[0]));
my $basename = $ARGV[0]; my $basename = $ARGV[0];
$basename =~ s/\.[sS][tT][lL]$//; $basename =~ s/\.[sS][tT][lL]$//;
@ -43,7 +43,7 @@ my %opt = ();
my $output_file = sprintf '%s_%02d.stl', $basename, ++$part_count; my $output_file = sprintf '%s_%02d.stl', $basename, ++$part_count;
printf "Writing to %s\n", basename($output_file); printf "Writing to %s\n", basename($output_file);
$new_model->store_stl(Slic3r::encode_path($output_file), !$opt{ascii}); $new_model->store_stl($output_file, !$opt{ascii});
} }
} }

View File

@ -25,7 +25,7 @@ my %opt = ();
} }
{ {
my @models = map Slic3r::Model->load_stl(Slic3r::encode_path($_), basename($_)), @ARGV; my @models = map Slic3r::Model->load_stl($_, basename($_)), @ARGV;
my $output_file = $ARGV[0]; my $output_file = $ARGV[0];
$output_file =~ s/\.[sS][tT][lL]$/.amf.xml/; $output_file =~ s/\.[sS][tT][lL]$/.amf.xml/;
@ -53,7 +53,7 @@ my %opt = ();
} }
printf "Writing to %s\n", basename($output_file); printf "Writing to %s\n", basename($output_file);
$new_model->store_amf(Slic3r::encode_path($output_file)); $new_model->store_amf($output_file);
} }

View File

@ -129,11 +129,11 @@ if (defined $ENV{BOOST_LIBRARYDIR}) {
# In order to generate the -l switches we need to know how Boost libraries are named # In order to generate the -l switches we need to know how Boost libraries are named
my $have_boost = 0; my $have_boost = 0;
my @boost_libraries = qw(system thread filesystem log); # we need these my @boost_libraries = qw(system thread filesystem locale log); # we need these
if (!$ENV{SLIC3R_STATIC}) { if (!$ENV{SLIC3R_STATIC}) {
# Dynamic linking of boost libraries. # Dynamic linking of boost libraries.
push @cflags, qw(-DBOOST_LOG_DYN_LINK); push @cflags, qw(-DBOOST_LOG_DYN_LINK -DBOOST_LOCALE_DYN_LINK);
if (! $mswin) { if (! $mswin) {
# Check without explicit lib path (works on Linux and OSX). # Check without explicit lib path (works on Linux and OSX).
$have_boost = 1 $have_boost = 1
@ -193,10 +193,10 @@ path through the BOOST_DIR environment variable:
Or you may specify BOOST_INCLUDEDIR and BOOST_LIBRARYDIR separatly, which Or you may specify BOOST_INCLUDEDIR and BOOST_LIBRARYDIR separatly, which
is handy, if you have built Boost libraries with mutliple settings. is handy, if you have built Boost libraries with mutliple settings.
Following boost libraries are needed by Slic3r Prusa Edition: system, filesystem, thread, log. Following boost libraries are needed by Slic3r Prusa Edition: system, filesystem, thread, locale, log.
On Debian, you need to run On Debian, you need to run
sudo apt-get install libboost-thread-dev libboost-system-dev libboost-filesystem-dev libboost-log-dev sudo apt-get install libboost-thread-dev libboost-system-dev libboost-filesystem-dev libboost-locale-dev libboost-log-dev
EOF EOF

View File

@ -1,13 +1,13 @@
Build.PL Build.PL
lib/Slic3r/XS.pm lib/Slic3r/XS.pm
MANIFEST This list of files MANIFEST This list of files
src/admesh/connect.c src/admesh/connect.cpp
src/admesh/normals.c src/admesh/normals.cpp
src/admesh/shared.c src/admesh/shared.cpp
src/admesh/stl.h src/admesh/stl.h
src/admesh/stl_io.c src/admesh/stl_io.cpp
src/admesh/stlinit.c src/admesh/stlinit.cpp
src/admesh/util.c src/admesh/util.cpp
src/boost/nowide/args.hpp src/boost/nowide/args.hpp
src/boost/nowide/cenv.hpp src/boost/nowide/cenv.hpp
src/boost/nowide/config.hpp src/boost/nowide/config.hpp

View File

@ -27,9 +27,13 @@
#include "stl.h" #include "stl.h"
static void stl_reverse_facet(stl_file *stl, int facet_num); static void stl_reverse_vector(float v[]) {
static void stl_reverse_vector(float v[]); v[0] *= -1;
int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag); v[1] *= -1;
v[2] *= -1;
}
static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag);
static void static void
stl_reverse_facet(stl_file *stl, int facet_num) { stl_reverse_facet(stl_file *stl, int facet_num) {
@ -136,10 +140,8 @@ stl_fix_normal_directions(stl_file *stl) {
/* Reverse the neighboring facets if necessary. */ /* Reverse the neighboring facets if necessary. */
if(stl->neighbors_start[facet_num].which_vertex_not[j] > 2) { if(stl->neighbors_start[facet_num].which_vertex_not[j] > 2) {
/* If the facet has a neighbor that is -1, it means that edge isn't shared by another facet */ /* If the facet has a neighbor that is -1, it means that edge isn't shared by another facet */
if(stl->neighbors_start[facet_num].neighbor[j] != -1) { if(stl->neighbors_start[facet_num].neighbor[j] != -1)
stl_reverse_facet stl_reverse_facet(stl, stl->neighbors_start[facet_num].neighbor[j]);
(stl, stl->neighbors_start[facet_num].neighbor[j]);
}
} }
/* If this edge of the facet is connected: */ /* If this edge of the facet is connected: */
if(stl->neighbors_start[facet_num].neighbor[j] != -1) { if(stl->neighbors_start[facet_num].neighbor[j] != -1) {
@ -193,8 +195,7 @@ stl_fix_normal_directions(stl_file *stl) {
free(norm_sw); free(norm_sw);
} }
int static int stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) {
stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) {
/* Returns 0 if the normal is within tolerance */ /* Returns 0 if the normal is within tolerance */
/* Returns 1 if the normal is not within tolerance, but direction is OK */ /* Returns 1 if the normal is not within tolerance, but direction is OK */
/* Returns 2 if the normal is not within tolerance and backwards */ /* Returns 2 if the normal is not within tolerance and backwards */
@ -259,26 +260,17 @@ stl_check_normal_vector(stl_file *stl, int facet_num, int normal_fix_flag) {
return 4; return 4;
} }
static void void stl_calculate_normal(float normal[], stl_facet *facet) {
stl_reverse_vector(float v[]) { float v1[3] = {
v[0] *= -1; facet->vertex[1].x - facet->vertex[0].x,
v[1] *= -1; facet->vertex[1].y - facet->vertex[0].y,
v[2] *= -1; facet->vertex[1].z - facet->vertex[0].z
} };
float v2[3] = {
facet->vertex[2].x - facet->vertex[0].x,
void facet->vertex[2].y - facet->vertex[0].y,
stl_calculate_normal(float normal[], stl_facet *facet) { facet->vertex[2].z - facet->vertex[0].z
float v1[3]; };
float v2[3];
v1[0] = facet->vertex[1].x - facet->vertex[0].x;
v1[1] = facet->vertex[1].y - facet->vertex[0].y;
v1[2] = facet->vertex[1].z - facet->vertex[0].z;
v2[0] = facet->vertex[2].x - facet->vertex[0].x;
v2[1] = facet->vertex[2].y - facet->vertex[0].y;
v2[2] = facet->vertex[2].z - facet->vertex[0].z;
normal[0] = (float)((double)v1[1] * (double)v2[2]) - ((double)v1[2] * (double)v2[1]); normal[0] = (float)((double)v1[1] * (double)v2[2]) - ((double)v1[2] * (double)v2[1]);
normal[1] = (float)((double)v1[2] * (double)v2[0]) - ((double)v1[0] * (double)v2[2]); normal[1] = (float)((double)v1[2] * (double)v2[0]) - ((double)v1[0] * (double)v2[2]);
normal[2] = (float)((double)v1[0] * (double)v2[1]) - ((double)v1[1] * (double)v2[0]); normal[2] = (float)((double)v1[0] * (double)v2[1]) - ((double)v1[1] * (double)v2[0]);

View File

@ -23,6 +23,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <boost/nowide/cstdio.hpp>
#include "stl.h" #include "stl.h"
void void
@ -149,7 +151,7 @@ stl_write_off(stl_file *stl, char *file) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = boost::nowide::fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
@ -185,7 +187,7 @@ stl_write_vrml(stl_file *stl, char *file) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = boost::nowide::fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
@ -241,7 +243,7 @@ void stl_write_obj (stl_file *stl, char *file) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = boost::nowide::fopen(file, "w");
if (fp == NULL) { if (fp == NULL) {
char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ char* error_msg = (char*)malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file); sprintf(error_msg, "stl_write_ascii: Couldn't open %s for writing", file);

View File

@ -32,10 +32,6 @@
#error "admesh works correctly on little endian machines only!" #error "admesh works correctly on little endian machines only!"
#endif #endif
#ifdef __cplusplus
extern "C" {
#endif
#define STL_MAX(A,B) ((A)>(B)? (A):(B)) #define STL_MAX(A,B) ((A)>(B)? (A):(B))
#define STL_MIN(A,B) ((A)<(B)? (A):(B)) #define STL_MIN(A,B) ((A)<(B)? (A):(B))
#define ABS(X) ((X) < 0 ? -(X) : (X)) #define ABS(X) ((X) < 0 ? -(X) : (X))
@ -223,8 +219,4 @@ extern void stl_clear_error(stl_file *stl);
extern int stl_get_error(stl_file *stl); extern int stl_get_error(stl_file *stl);
extern void stl_exit_on_error(stl_file *stl); extern void stl_exit_on_error(stl_file *stl);
#ifdef __cplusplus
}
#endif
#endif #endif

View File

@ -25,6 +25,8 @@
#include "stl.h" #include "stl.h"
#include "config.h" #include "config.h"
#include <boost/nowide/cstdio.hpp>
#if !defined(SEEK_SET) #if !defined(SEEK_SET)
#define SEEK_SET 0 #define SEEK_SET 0
#define SEEK_CUR 1 #define SEEK_CUR 1
@ -126,13 +128,12 @@ Normals fixed : %5d\n", stl->stats.normals_fixed);
void void
stl_write_ascii(stl_file *stl, const char *file, const char *label) { stl_write_ascii(stl_file *stl, const char *file, const char *label) {
int i; int i;
FILE *fp;
char *error_msg; char *error_msg;
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); FILE *fp = boost::nowide::fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
@ -178,7 +179,7 @@ stl_print_neighbors(stl_file *stl, char *file) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = boost::nowide::fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
@ -212,7 +213,7 @@ stl_write_binary(stl_file *stl, const char *file, const char *label) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "wb"); fp = boost::nowide::fopen(file, "wb");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
@ -292,7 +293,7 @@ stl_write_quad_object(stl_file *stl, char *file) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = boost::nowide::fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
@ -360,7 +361,7 @@ stl_write_dxf(stl_file *stl, char *file, char *label) {
if (stl->error) return; if (stl->error) return;
/* Open the file */ /* Open the file */
fp = fopen(file, "w"); fp = boost::nowide::fopen(file, "w");
if(fp == NULL) { if(fp == NULL) {
error_msg = (char*) error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */

View File

@ -26,6 +26,8 @@
#include <math.h> #include <math.h>
#include <assert.h> #include <assert.h>
#include <boost/nowide/cstdio.hpp>
#include "stl.h" #include "stl.h"
#ifndef SEEK_SET #ifndef SEEK_SET
@ -62,7 +64,7 @@ stl_count_facets(stl_file *stl, const char *file) {
if (stl->error) return; if (stl->error) return;
/* Open the file in binary mode first */ /* Open the file in binary mode first */
stl->fp = fopen(file, "rb"); stl->fp = boost::nowide::fopen(file, "rb");
if(stl->fp == NULL) { if(stl->fp == NULL) {
error_msg = (char*) error_msg = (char*)
malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */ malloc(81 + strlen(file)); /* Allow 80 chars+file size for message */
@ -121,7 +123,7 @@ stl_count_facets(stl_file *stl, const char *file) {
/* Reopen the file in text mode (for getting correct newlines on Windows) */ /* Reopen the file in text mode (for getting correct newlines on Windows) */
// fix to silence a warning about unused return value. // fix to silence a warning about unused return value.
// obviously if it fails we have problems.... // obviously if it fails we have problems....
stl->fp = freopen(file, "r", stl->fp); stl->fp = boost::nowide::freopen(file, "r", stl->fp);
// do another null check to be safe // do another null check to be safe
if(stl->fp == NULL) { if(stl->fp == NULL) {

View File

@ -186,7 +186,7 @@ static void calculate_normals(stl_file *stl) {
} }
void stl_transform(stl_file *stl, float *trafo3x4) { void stl_transform(stl_file *stl, float *trafo3x4) {
int i_face, i_vertex, i, j; int i_face, i_vertex;
if (stl->error) if (stl->error)
return; return;
for (i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) { for (i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) {
@ -404,7 +404,7 @@ static float get_volume(stl_file *stl) {
n = stl->facet_start[i].normal; n = stl->facet_start[i].normal;
height = (n.x * p.x) + (n.y * p.y) + (n.z * p.z); height = (n.x * p.x) + (n.y * p.y) + (n.z * p.z);
area = get_area(&stl->facet_start[i]); area = get_area(&stl->facet_start[i]);
volume += (area * height) / 3.0; volume += (area * height) / 3.0f;
} }
return volume; return volume;
} }

View File

@ -3,6 +3,8 @@
#include <string> #include <string>
#include <expat/expat.h> #include <expat/expat.h>
#include <boost/nowide/cstdio.hpp>
#include "../libslic3r.h" #include "../libslic3r.h"
#include "../Model.hpp" #include "../Model.hpp"
#include "AMF.hpp" #include "AMF.hpp"
@ -480,7 +482,7 @@ bool load_amf(const char *path, Model *model)
return false; return false;
} }
FILE *pFile = ::fopen(path, "rt"); FILE *pFile = boost::nowide::fopen(path, "rt");
if (pFile == nullptr) { if (pFile == nullptr) {
printf("Cannot open file %s\n", path); printf("Cannot open file %s\n", path);
return false; return false;
@ -522,7 +524,7 @@ bool load_amf(const char *path, Model *model)
bool store_amf(const char *path, Model *model) bool store_amf(const char *path, Model *model)
{ {
FILE *file = ::fopen(path, "wb"); FILE *file = boost::nowide::fopen(path, "wb");
if (file == nullptr) if (file == nullptr)
return false; return false;

View File

@ -2,6 +2,8 @@
#include <string.h> #include <string.h>
#include <boost/nowide/convert.hpp>
#include <wx/string.h> #include <wx/string.h>
#include <wx/wfstream.h> #include <wx/wfstream.h>
#include <wx/zipstrm.h> #include <wx/zipstrm.h>
@ -119,7 +121,14 @@ bool load_prus(const char *path, Model *model)
{ {
// To receive the content of the zipped 'scene.xml' file. // To receive the content of the zipped 'scene.xml' file.
std::vector<char> scene_xml_data; std::vector<char> scene_xml_data;
wxFFileInputStream in(path); wxFFileInputStream in(
#ifdef WIN32
// On Windows, convert to a 16bit unicode string.
boost::nowide::widen(path).c_str()
#else
path
#endif
);
wxZipInputStream zip(in); wxZipInputStream zip(in);
std::unique_ptr<wxZipEntry> entry; std::unique_ptr<wxZipEntry> entry;
size_t num_models = 0; size_t num_models = 0;

View File

@ -1,6 +1,8 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <boost/nowide/cstdio.hpp>
#include "objparser.hpp" #include "objparser.hpp"
namespace ObjParser { namespace ObjParser {
@ -318,7 +320,7 @@ static bool obj_parseline(const char *line, ObjData &data)
bool objparse(const char *path, ObjData &data) bool objparse(const char *path, ObjData &data)
{ {
FILE *pFile = ::fopen(path, "rt"); FILE *pFile = boost::nowide::fopen(path, "rt");
if (pFile == 0) if (pFile == 0)
return false; return false;
@ -446,7 +448,7 @@ bool loadvectornameidx(FILE *pFile, std::vector<T> &v)
bool objbinsave(const char *path, const ObjData &data) bool objbinsave(const char *path, const ObjData &data)
{ {
FILE *pFile = ::fopen(path, "wb"); FILE *pFile = boost::nowide::fopen(path, "wb");
if (pFile == 0) if (pFile == 0)
return false; return false;
@ -471,7 +473,7 @@ bool objbinsave(const char *path, const ObjData &data)
bool objbinload(const char *path, ObjData &data) bool objbinload(const char *path, ObjData &data)
{ {
FILE *pFile = ::fopen(path, "rb"); FILE *pFile = boost::nowide::fopen(path, "rb");
if (pFile == 0) if (pFile == 0)
return false; return false;

View File

@ -13,6 +13,10 @@
#include <boost/date_time/local_time/local_time.hpp> #include <boost/date_time/local_time/local_time.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/nowide/iostream.hpp>
#include <boost/nowide/cstdio.hpp>
#include <boost/nowide/cstdlib.hpp>
#include "SVG.hpp" #include "SVG.hpp"
#if 0 #if 0
@ -306,7 +310,33 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
return layers_to_print; return layers_to_print;
} }
bool GCode::do_export(FILE *file, Print &print) bool GCode::do_export(Print *print, const char *path)
{
// Remove the old g-code if it exists.
boost::nowide::remove(path);
std::string path_tmp(path);
path_tmp += ".tmp";
FILE *file = boost::nowide::fopen(path_tmp.c_str(), "wb");
if (file == nullptr)
return false;
bool result = this->_do_export(*print, file);
fclose(file);
if (result && boost::nowide::rename(path_tmp.c_str(), path) != 0) {
boost::nowide::cerr << "Failed to remove the output G-code file from " << path_tmp << " to " << path
<< ". Is " << path_tmp << " locked?" << std::endl;
result = false;
}
if (! result)
boost::nowide::remove(path_tmp.c_str());
return result;
}
bool GCode::_do_export(Print &print, FILE *file)
{ {
// How many times will be change_layer() called? // How many times will be change_layer() called?
// change_layer() in turn increments the progress bar status. // change_layer() in turn increments the progress bar status.

View File

@ -126,7 +126,7 @@ public:
{} {}
~GCode() {} ~GCode() {}
bool do_export(FILE *file, Print &print); bool do_export(Print *print, const char *path);
// Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests.
const Pointf& origin() const { return m_origin; } const Pointf& origin() const { return m_origin; }
@ -146,6 +146,8 @@ public:
void apply_print_config(const PrintConfig &print_config); void apply_print_config(const PrintConfig &print_config);
protected: protected:
bool _do_export(Print &print, FILE *file);
// Object and support extrusions of the same PrintObject at the same print_z. // Object and support extrusions of the same PrintObject at the same print_z.
struct LayerToPrint struct LayerToPrint
{ {

View File

@ -10,6 +10,7 @@
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/nowide/iostream.hpp>
namespace Slic3r { namespace Slic3r {
@ -739,7 +740,7 @@ void ModelObject::print_info() const
{ {
using namespace std; using namespace std;
cout << fixed; cout << fixed;
cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl; boost::nowide::cout << "[" << boost::filesystem::path(this->input_file).filename().string() << "]" << endl;
TriangleMesh mesh = this->raw_mesh(); TriangleMesh mesh = this->raw_mesh();
mesh.check_topology(); mesh.check_topology();

View File

@ -1,6 +1,8 @@
#include "SVG.hpp" #include "SVG.hpp"
#include <iostream> #include <iostream>
#include <boost/nowide/cstdio.hpp>
#define COORD(x) ((float)unscale((x))*10) #define COORD(x) ((float)unscale((x))*10)
namespace Slic3r { namespace Slic3r {
@ -8,7 +10,7 @@ namespace Slic3r {
bool SVG::open(const char* afilename) bool SVG::open(const char* afilename)
{ {
this->filename = afilename; this->filename = afilename;
this->f = fopen(afilename, "w"); this->f = boost::nowide::fopen(afilename, "w");
if (this->f == NULL) if (this->f == NULL)
return false; return false;
fprintf(this->f, fprintf(this->f,
@ -27,7 +29,7 @@ bool SVG::open(const char* afilename, const BoundingBox &bbox, const coord_t bbo
this->filename = afilename; this->filename = afilename;
this->origin = bbox.min - Point(bbox_offset, bbox_offset); this->origin = bbox.min - Point(bbox_offset, bbox_offset);
this->flipY = aflipY; this->flipY = aflipY;
this->f = ::fopen(afilename, "w"); this->f = boost::nowide::fopen(afilename, "w");
if (f == NULL) if (f == NULL)
return false; return false;
float w = COORD(bbox.max.x - bbox.min.x + 2 * bbox_offset); float w = COORD(bbox.max.x - bbox.min.x + 2 * bbox_offset);

View File

@ -6,6 +6,10 @@ namespace Slic3r {
extern void set_logging_level(unsigned int level); extern void set_logging_level(unsigned int level);
extern void trace(unsigned int level, const char *message); extern void trace(unsigned int level, const char *message);
extern std::string encode_path(const char *src);
extern std::string decode_path(const char *src);
extern std::string normalize_utf8_nfc(const char *src);
// Compute the next highest power of 2 of 32-bit v // Compute the next highest power of 2 of 32-bit v
// http://graphics.stanford.edu/~seander/bithacks.html // http://graphics.stanford.edu/~seander/bithacks.html
template<typename T> template<typename T>

View File

@ -1,7 +1,21 @@
#include <locale>
#include <boost/log/core.hpp> #include <boost/log/core.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp> #include <boost/log/expressions.hpp>
#include <boost/locale.hpp>
#include <boost/nowide/integration/filesystem.hpp>
#include <boost/nowide/convert.hpp>
#ifdef WIN32
extern "C" {
__declspec(dllimport) int WideCharToMultiByte(unsigned int, unsigned long, wchar_t const *, int, char *, int, char const *, int *);
__declspec(dllimport) int MultiByteToWideChar(unsigned int, unsigned long, char const *, int, wchar_t *, int);
}
#endif /* WIN32 */
namespace Slic3r { namespace Slic3r {
static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error; static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error;
@ -30,9 +44,13 @@ void set_logging_level(unsigned int level)
} }
// Force set_logging_level(<=error) after loading of the DLL. // Force set_logging_level(<=error) after loading of the DLL.
static struct SetLoggingLevelOnInit { // Switch boost::filesystem to utf8.
SetLoggingLevelOnInit() { set_logging_level(1); } static struct RunOnInit {
} g_SetLoggingLevelOnInit; RunOnInit() {
boost::nowide::nowide_filesystem();
set_logging_level(1);
}
} g_RunOnInit;
void trace(unsigned int level, const char *message) void trace(unsigned int level, const char *message)
{ {
@ -56,6 +74,46 @@ void trace(unsigned int level, const char *message)
(::boost::log::keywords::severity = severity)) << message; (::boost::log::keywords::severity = severity)) << message;
} }
std::string encode_path(const char *src)
{
#ifdef WIN32
// Convert the source utf8 encoded string to a wide string.
std::wstring wstr_src = boost::nowide::widen(src);
if (wstr_src.length() == 0)
return std::string();
// Convert a wide string to a local code page.
int size_needed = ::WideCharToMultiByte(0, 0, wstr_src.data(), (int)wstr_src.size(), nullptr, 0, nullptr, nullptr);
std::string str_dst(size_needed, 0);
::WideCharToMultiByte(0, 0, wstr_src.data(), (int)wstr_src.size(), const_cast<char*>(str_dst.data()), size_needed, nullptr, nullptr);
return str_dst;
#else /* WIN32 */
return src;
#endif /* WIN32 */
}
std::string decode_path(const char *src)
{
#ifdef WIN32
int len = strlen(src);
if (len == 0)
return std::string();
// Convert the string encoded using the local code page to a wide string.
int size_needed = ::MultiByteToWideChar(0, 0, src, len, nullptr, 0);
std::wstring wstr_dst(size_needed, 0);
::MultiByteToWideChar(0, 0, src, len, const_cast<wchar_t*>(wstr_dst.data()), size_needed);
// Convert a wide string to utf8.
return boost::nowide::narrow(wstr_dst.c_str());
#else /* WIN32 */
return src;
#endif /* WIN32 */
}
std::string normalize_utf8_nfc(const char *src)
{
static std::locale locale_utf8("en_US.UTF-8");
return boost::locale::normalize(src, boost::locale::norm_nfc, locale_utf8);
}
} // namespace Slic3r } // namespace Slic3r
#ifdef SLIC3R_HAS_BROKEN_CROAK #ifdef SLIC3R_HAS_BROKEN_CROAK

View File

@ -17,17 +17,7 @@
%name{Slic3r::GCode} class GCode { %name{Slic3r::GCode} class GCode {
GCode(); GCode();
~GCode(); ~GCode();
std::string do_export(Print *print, const char *path) std::string do_export(Print *print, const char *path);
%code{%
FILE *file = fopen(path, "wb");
if (file == nullptr) {
RETVAL = std::string("Failed to open ") + path + " for writing.";
} else {
THIS->do_export(file, *print);
fclose(file);
RETVAL = std::string();
}
%};
Ref<Pointf> origin() Ref<Pointf> origin()
%code{% RETVAL = &(THIS->origin()); %}; %code{% RETVAL = &(THIS->origin()); %};

View File

@ -48,6 +48,24 @@ trace(level, message)
CODE: CODE:
Slic3r::trace(level, message); Slic3r::trace(level, message);
std::string
encode_path(src)
const char *src;
CODE:
Slic3r::encode_path(src);
std::string
decode_path(src)
const char *src;
CODE:
Slic3r::decode_path(src);
std::string
normalize_utf8_nfc(src)
const char *src;
CODE:
Slic3r::normalize_utf8_nfc(src);
void void
xspp_test_croak_hangs_on_strawberry() xspp_test_croak_hangs_on_strawberry()
CODE: CODE: