# HG changeset patch # User Ben Abbott # Date 1280447047 14400 # Node ID 05ba991794d4578a938e899118d50ed649fa39a0 # Parent e5c7522319858e8122d519fa1b84179b072b17de Improvements for fltk printing. diff --git a/scripts/ChangeLog b/scripts/ChangeLog --- a/scripts/ChangeLog +++ b/scripts/ChangeLog @@ -1,3 +1,10 @@ +2010-07-29 Ben Abbott + + * plot/print.m, plot/__print_parse_opts__.m, plot/__fltk_print__.m, + plot/__gnuplot_print__.m: Improved printing for the fltk backend. + * plot/private/__ghostscript__.m, plot/private/__tight_eps_bbox__.m: + New files to support printing. + 2010-07-29 Jaroslav Hajek * linear-algebra/expm.m: Special-case scalars and diagonal matrices. diff --git a/scripts/plot/__fltk_print__.m b/scripts/plot/__fltk_print__.m --- a/scripts/plot/__fltk_print__.m +++ b/scripts/plot/__fltk_print__.m @@ -21,9 +21,122 @@ ## Undocumented internal function. ## @end deftypefn -function __fltk_print__ (varargin) +function __fltk_print__ (opts) + + if (opts.debug) + fprintf ("FLTK backend: output file = '%s' for device '%s'\n", opts.name, opts.devopt); + endif + + file2unlink = ""; - opts = __print_parse_opts__ (varargin{:}); - drawnow ("eps", opts.name); + switch lower (opts.devopt) + case {"eps", "eps2", "epsc", "epsc2"} + drawnow ("eps", opts.name); + if (opts.tight_flag) + __tight_eps_bbox__ (opts, opts.name); + endif + case {"epslatex", "epslatexstandalone", "pslatex"} + # FIXME - format GL2PS_TEX is not implemented + drawnow ("epslatex", opts.name); + if (opts.tight_flag) + __tight_eps_bbox__ (opts, opts.name); + endif + case {"tikz"} + ## FIXME - format GL2PS_PGF if not implemented + drawnow ("pgf", opts.name); + case {"ps", "ps2", "psc", "psc2"} + ## FIXME - format GL2PS_PS if not implemented + drawnow ("ps", opts.name); + case {"pdf"} + ## FIXME - format GL2PS_PDF if not implemented + drawnow ("pdf", opts.name); + case {"svg"} + ## FIXME - format GL2PS_SVG if not implemented + drawnow ("svg", opts.name); + case {"gif", "jpeg", "png", "pbm"} + opts.ghostscript_device = opts.devopt; + opts.ghostscript_output = opts.name; + opts.name = strcat (tmpnam (), ".eps"); + file2unlink = opts.name; + opts.devopt = "epsc"; + drawnow ("eps", opts.name); + if (opts.tight_flag) + __tight_eps_bbox__ (opts, opts.name); + endif + case {"aifm", "dxf", "emf", "fig", "hpgl"} + status = __pstoedit__ (opts); + case {"pstex", "mf", "emf"} + tmp_figfile = strcat (tmpnam (), ".fig"); + file2unlink = tmp_figfile; + status = __pstoedit__ (opts, "fig", tmp_figfile); + if (status == 0) + status = __fig2dev__ (opts, tmp_figfile); + endif + otherwise + error ("print:unavailabledevice", + "print.m: device '%s' is unavailable for the fltk backend.", opts.devopt) + endswitch + + if (! isempty (opts.ghostscript_device)) + __ghostscript__ (opts); + endif + + if (! isempty (file2unlink)) + [status, output] = unlink (file2unlink); + if (status != 0) + disp (output) + warning ("print.m: failed to delete temporay file, '%s'.", file2unlink) + endif + endif endfunction + +function status = __fig2dev__ (opts, figfile, devopt, devfile) + if (nargin < 4) + devfile = opts.name; + endif + if (nargin < 3) + devopt = opts.devopt; + endif + cmd = sprintf ("%s -L %s %s %s 2>&1", opts.fig2dev_binary, devopt, figfile, devfile); + [status, output] = system (cmd); + if (opts.debug) + fprintf ("fig2dev command: %s", cmd) + endif + if (status) + warning ("print:fig2devfailed", "print.m: error running fig2dev.") + disp (cmd) + disp (output) + endif +endfunction + +function status = __pstoedit__ (opts, devopt, name) + if (nargin < 3) + name = opts.name; + endif + if (nargin < 2) + devopt = opts.devopt; + endif + tmp_epsfile = strcat (tmpnam (), ".eps"); + if (opts.tight_flag) + __tight_eps_bbox__ (opts, tmp_epsfile); + endif + drawnow ("eps", tmp_epsfile) + cmd = sprintf ("%s -f %s %s %s 2>&1", opts.pstoedit_binary, devopt, tmp_epsfile, name); + [status, output] = system (cmd); + if (opts.debug) + fprintf ("pstoedit command: %s", cmd) + endif + if (status) + warning ("print:pstoeditfailed", "print.m: error running pstoedit.") + disp (cmd) + disp (output) + endif + [status, output] = unlink (tmp_epsfile); + if (status != 0) + disp (output) + warning ("print.m: failed to delete temporay file, '%s'.", tmp_epsfile) + endif +endfunction + + diff --git a/scripts/plot/__gnuplot_print__.m b/scripts/plot/__gnuplot_print__.m --- a/scripts/plot/__gnuplot_print__.m +++ b/scripts/plot/__gnuplot_print__.m @@ -29,27 +29,10 @@ persistent warn_on_inconsistent_orientation = true - persistent ghostscript_binary = ""; - if (isempty (ghostscript_binary)) - ghostscript_binary = getenv ("GSC"); - ng = 0; - if (isunix ()) - ## Unix - Includes Mac OSX and Cygwin. - gs_binaries = {"gs", "gs.exe"}; - else - ## pc - Includes Win32 and mingw. - gs_binaries = {"gs.exe", "gswin32c.exe"}; - endif - while (ng < numel (gs_binaries) && isempty (ghostscript_binary)) - ng = ng + 1; - ghostscript_binary = file_in_path (EXEC_PATH, gs_binaries{ng}); - endwhile - endif - old_fig = get (0, "currentfigure"); unwind_protect opts = __print_parse_opts__ (varargin{:}); - have_ghostscript = (exist (ghostscript_binary, "file") == 2); + have_ghostscript = ! isempty (opts.ghostscript_binary); doprint = isempty (opts.name); if (doprint) @@ -465,7 +448,7 @@ if (opts.append_to_file) ghostscript_options = "-q -dBATCH -dSAFER -dNOPAUSE"; command = sprintf ("%s %s -sDEVICE=%s -sOutputFile=%s %s %s -q", ... - ghostscript_binary, ghostscript_options, ghostscript_device, ... + opts.ghostscript_binary, ghostscript_options, ghostscript_device, ... temp_name, appended_file_name, opts.name); status1 = system (command); status2 = system (sprintf ("mv %s %s", temp_name, appended_file_name)); @@ -489,7 +472,7 @@ if (! isempty (ghostscript_output)) if (is_eps_file && opts.tight_flag) ## If gnuplot's output is an eps-file then crop at the bounding box. - fix_eps_bbox (name, ghostscript_binary); + __fix_eps_bbox__ (name); endif ghostscript_options = "-q -dBATCH -dSAFER -dNOPAUSE -dTextAlphaBits=4"; if (is_eps_file) @@ -501,8 +484,9 @@ endif ghostscript_options = sprintf ("%s -sDEVICE=%s", ghostscript_options, ghostscript_device); - command = sprintf ("\"%s\" %s -sOutputFile=\"%s\" \"%s\" 2>&1", ghostscript_binary, - ghostscript_options, ghostscript_output, opts.name); + command = sprintf ("\"%s\" %s -sOutputFile=\"%s\" \"%s\" 2>&1", + opts.ghostscript_binary, + ghostscript_options, ghostscript_output, opts.name); [errcode, output] = system (command); unlink (name); if (errcode) @@ -513,7 +497,7 @@ ## If the saved output file is an eps file, use ghostscript to set a tight bbox. ## This may result in a smaller or larger bbox geometry. if (have_ghostscript) - fix_eps_bbox (name, ghostscript_binary); + __fix_eps_bbox__ (name); endif endif @@ -551,66 +535,6 @@ endfunction -function bb = fix_eps_bbox (eps_file_name, ghostscript_binary) - - persistent warn_on_no_ghostscript = true - - box_string = "%%BoundingBox:"; - - ghostscript_options = "-q -dBATCH -dSAFER -dNOPAUSE -dTextAlphaBits=4 -sDEVICE=bbox"; - cmd = sprintf ("\"%s\" %s \"%s\" 2>&1", ghostscript_binary, - ghostscript_options, eps_file_name); - [status, output] = system (cmd); - - if (status == 0) - - pattern = strcat (box_string, "[^%]*"); - pattern = pattern(1:find(double(pattern)>32, 1, "last")); - bbox_line = regexp (output, pattern, "match"); - if (iscell (bbox_line)) - bbox_line = bbox_line{1}; - endif - ## Remore the EOL characters. - bbox_line(double(bbox_line)<32) = ""; - - fid = fopen (eps_file_name, "r+"); - unwind_protect - bbox_replaced = false; - while (! bbox_replaced) - current_line = fgetl (fid); - if (strncmpi (current_line, box_string, numel(box_string))) - line_length = numel (current_line); - num_spaces = line_length - numel (bbox_line); - if (numel (current_line) < numel (bbox_line)) - ## If there new line is longer, continue with the current line. - new_line = current_line; - else - new_line = bbox_line; - new_line(end+1:numel(current_line)) = " "; - endif - ## Back up to the beginning of the line (include EOL characters). - if (ispc ()) - fseek (fid, -line_length-2, "cof"); - else - fseek (fid, -line_length-1, "cof"); - endif - count = fprintf (fid, "%s", new_line); - bbox_replaced = true; - elseif (! ischar (current_line)) - bbox_replaced = true; - warning ("print.m: no bounding box found in '%s'.", eps_file_name) - endif - endwhile - unwind_protect_cleanup - fclose (fid); - end_unwind_protect - elseif (warn_on_no_ghostscript) - warn_on_no_ghostscript = false; - warning ("print.m: Ghostscript failed to determine the bounding box.\nError was:\n%s\n", output) - endif - -endfunction - function [h, c] = convert_color2mono (hfig) unwind_protect showhiddenhandles = get (0, "showhiddenhandles"); diff --git a/scripts/plot/__print_parse_opts__.m b/scripts/plot/__print_parse_opts__.m --- a/scripts/plot/__print_parse_opts__.m +++ b/scripts/plot/__print_parse_opts__.m @@ -26,91 +26,371 @@ function arg_st = __print_parse_opts__ (varargin) - arg_st.orientation = ""; - arg_st.use_color = 0; # 0=default, -1=mono, +1=color - arg_st.append_to_file = 0; - arg_st.force_solid = 0; # 0=default, -1=dashed, +1=solid + ## FIXME - change to numeric values: `canvas_size', `resolution', `fontsize' + arg_st.append_to_file = false; + arg_st.canvas_size = ""; + arg_st.debug = false; + arg_st.debug_file = "octave-print-commands.log"; + arg_st.devopt = ""; + arg_st.figure = get (0, "currentfigure"); + arg_st.fig2dev_binary = __find_binary__ ("fig2dev"); arg_st.fontsize = ""; arg_st.font = ""; - arg_st.canvas_size = ""; - arg_st.name = ""; - arg_st.devopt = ""; + arg_st.force_solid = 0; # 0=default, -1=dashed, +1=solid + arg_st.ghostscript_binary = __ghostscript_binary__ (); + arg_st.ghostscript_device = ""; # gs converts eps/ps to this format/printer-language + arg_st.ghostscript_output = ""; # gs converts arg_st.name to arg_st.ghostscript_output + arg_st.pstoedit_binary = __find_binary__ ("pstoedit"); + arg_st.name = ""; # This is the file produced by the backend + arg_st.orientation = ""; arg_st.printer = ""; - arg_st.debug = false; - arg_st.debug_file = "octave-print-commands.log"; + arg_st.resolution = num2str (get (0, "screenpixelsperinch")); arg_st.special_flag = "textnormal"; arg_st.tight_flag = false; - arg_st.resolution = ""; + arg_st.use_color = 0; # 0=default, -1=mono, +1=color + arg_st.send_to_printer = false; + + if (isunix ()) + arg_st.lpr_options = "-l"; + elseif (ispc ()) + arg_st.lpr_options = "-o l"; + else + ## FIXME - What other OS's might be considered. + arg_st.lpr_options = ""; + endif + arg_st.unlink = {}; - old_fig = get (0, "currentfigure"); + for i = 1:nargin + arg = varargin{i}; + if (ischar (arg)) + if (strcmp (arg, "-color")) + arg_st.use_color = 1; + elseif (strcmp (arg, "-append")) + arg_st.append_to_file = true; + elseif (strcmp (arg, "-mono")) + arg_st.use_color = -1; + elseif (strcmp (arg, "-solid")) + arg_st.force_solid = 1; + elseif (strcmp (arg, "-dashed")) + arg_st.force_solid = -1; + elseif (strcmp (arg, "-portrait")) + arg_st.orientation = "portrait"; + elseif (strcmp (arg, "-landscape")) + arg_st.orientation = "landscape"; + elseif (strcmp (arg, "-tight")) + arg_st.tight_flag = true; + elseif (strcmp (arg, "-textspecial")) + arg_st.special_flag = "textspecial"; + elseif (strncmp (arg, "-debug", 6)) + arg_st.debug = true; + if (length (arg) > 7) + arg_st.debug_file = arg(8:end); + endif + elseif (length (arg) > 2 && arg(1:2) == "-d") + arg_st.devopt = tolower (arg(3:end)); + elseif (length (arg) > 2 && arg(1:2) == "-P") + arg_st.printer = arg; + elseif ((length (arg) > 2) && arg(1:2) == "-G") + arg_st.ghostscript_binary = arg(3:end); + if (exist (arg_st.ghostscript_binary, "file") != 2) + arg_st.ghostscript_binary = file_in_path (EXEC_PATH, arg_st.ghostscript_binary); + endif + if (isempty (arg_st.ghostscript_binary)) + error ("print: Ghostscript binary ""%s"" could not be located", arg(3:end)) + endif + elseif (length (arg) > 2 && arg(1:2) == "-F") + idx = rindex (arg, ":"); + if (idx) + arg_st.font = arg(3:idx-1); + arg_st.fontsize = arg(idx+1:length(arg)); + else + arg_st.font = arg(3:length(arg)); + endif + elseif (length (arg) > 2 && arg(1:2) == "-S") + arg_st.canvas_size = arg(3:length(arg)); + elseif (length (arg) > 2 && arg(1:2) == "-r") + arg_st.resolution = arg(3:length(arg)); + elseif (length (arg) > 2 && arg(1:2) == "-f") + arg_st.figure = str2num (arg(3:end)); + elseif (length (arg) >= 1 && arg(1) == "-") + error ("print: unknown option `%s'", arg); + elseif (length (arg) > 0) + arg_st.name = arg; + endif + elseif (isfigure (arg)) + arg_st.figure = arg; + else + error ("print: expecting inputs to be character string options or a figure handle"); + endif + endfor + + if (isempty (arg_st.orientation)) + if (isfigure (arg_st.figure)) + arg_st.orientation = get (arg_st.figure, "paperorientation"); + else + ## Allows tests to be run without error. + arg_st.orientation = get (0, "defaultfigurepaperorientation"); + endif + endif + + if (isempty (arg_st.ghostscript_binary)) + arg_st.ghostscript_binary = __ghostscript_binary__ (); + endif + + dot = rindex (arg_st.name, "."); + if (isempty (arg_st.devopt)) + if (dot == 0) + arg_st.devopt = "psc"; + else + arg_st.devopt = tolower (arg_st.name(dot+1:end)); + endif + endif + + if (any (strcmp ({"ps", "ps2", "eps", "eps2"}, arg_st.devopt)) + || (! isempty (strfind (arg_st.devopt, "tex")) && arg_st.use_color == 0)) + ## Mono is the default for ps, eps, and the tex/latex, devices + arg_st.use_color = -1; + elseif (arg_st.use_color == 0) + arg_st.use_color = 1; + endif + + if (arg_st.append_to_file) + if (any (strcmpi (arg_st.devopt, {"ps", "ps2", "psc", "psc2", "pdf"}))) + have_ghostscript = ! isempty (__ghostscript_binary__ ()); + if (have_ghostscript) + file_exists = ((numel (dir (arg_st.name)) == 1) && (! isdir (arg_st.name))); + if (! file_exists) + arg_st.append_to_file = false; + end + end + else + warning ("print.m: appended output is not supported for device '%s'", arg_st.devopt) + arg_st.append_to_file = false; + endif + endif + + if (arg_st.tight_flag) + if (any (strcmpi (arg_st.devopt, {"ps", "ps2", "psc", "psc2", "pdf"}))) + arg_st.tight_flag = false; + warning ("print.m: '-tight' is not supported for device '%s'", arg_st.devopt) + endif + endif + + if (strcmp (arg_st.devopt, "tex")) + arg_st.devopt = "epslatex"; + elseif (strcmp (arg_st.devopt, "ill")) + arg_st.devopt = "aifm"; + elseif (strcmp (arg_st.devopt, "cdr")) + arg_st.devopt = "corel"; + elseif (strcmp (arg_st.devopt, "meta")) + arg_st.devopt = "emf"; + elseif (strcmp (arg_st.devopt, "jpg")) + arg_st.devopt = "jpeg"; + endif + + dev_list = {"aifm", "corel", "fig", "png", "jpeg", ... + "gif", "pbm", "dxf", "mf", "svg", "hpgl", ... + "ps", "ps2", "psc", "psc2", "eps", "eps2", ... + "epsc", "epsc2", "emf", "pdf", "pslatex", ... + "epslatex", "epslatexstandalone", "pstex", "tikz"}; + + suffixes = {"ai", "cdr", "fig", "png", "jpg", ... + "gif", "pbm", "dxf", "mf", "svg", "hpgl", ... + "ps", "ps", "ps", "ps", "eps", "eps", ... + "eps", "eps", "emf", "pdf", "tex", ... + "tex", "tex", "tex", "tikz"}; - unwind_protect - for i = 1:nargin - arg = varargin{i}; - if (ischar (arg)) - if (strcmp (arg, "-color")) - arg_st.use_color = 1; - elseif (strcmp (arg, "-append")) - arg_st.append_to_file = 1; - elseif (strcmp (arg, "-mono")) - arg_st.use_color = -1; - elseif (strcmp (arg, "-solid")) - arg_st.force_solid = 1; - elseif (strcmp (arg, "-dashed")) - arg_st.force_solid = -1; - elseif (strcmp (arg, "-portrait")) - arg_st.orientation = "portrait"; - elseif (strcmp (arg, "-landscape")) - arg_st.orientation = "landscape"; - elseif (strcmp (arg, "-tight")) - arg_st.tight_flag = true; - elseif (strcmp (arg, "-textspecial")) - arg_st.special_flag = "textspecial"; - elseif (strncmp (arg, "-debug", 6)) - arg_st.debug = true; - if (length (arg) > 7) - arg_st.debug_file = arg(8:end); - endif - elseif (length (arg) > 2 && arg(1:2) == "-d") - arg_st.devopt = tolower(arg(3:end)); - elseif (length (arg) > 2 && arg(1:2) == "-P") - arg_st.printer = arg; - elseif ((length (arg) > 2) && arg(1:2) == "-G") - arg_st.ghostscript_binary = arg(3:end); - if (exist (arg_st.ghostscript_binary, "file") != 2) - arg_st.ghostscript_binary = file_in_path (EXEC_PATH, arg_st.ghostscript_binary); - endif - if (isempty (arg_st.ghostscript_binary)) - error ("print: Ghostscript binary ""%s"" could not be located", arg(3:end)) - endif - elseif (length (arg) > 2 && arg(1:2) == "-F") - idx = rindex (arg, ":"); - if (idx) - arg_st.font = arg(3:idx-1); - arg_st.fontsize = arg(idx+1:length(arg)); - else - arg_st.font = arg(3:length(arg)); - endif - elseif (length (arg) > 2 && arg(1:2) == "-S") - arg_st.canvas_size = arg(3:length(arg)); - elseif (length (arg) > 2 && arg(1:2) == "-r") - arg_st.resolution = arg(3:length(arg)); - elseif (length (arg) >= 1 && arg(1) == "-") - error ("print: unknown option `%s'", arg); - elseif (length (arg) > 0) - arg_st.name = arg; - endif - elseif (isfigure (arg)) - arg_st.figure (arg); - else - error ("print: expecting inputs to be character string options or a figure handle"); - endif - endfor - unwind_protect_cleanup - if (isfigure (old_fig)) - figure (old_fig) + match = strcmpi (dev_list, arg_st.devopt); + if (any (match)) + default_suffix = suffixes {match}; + else + default_suffix = arg_st.devopt; + endif + + if (dot == 0 && ! isempty (arg_st.name)) + arg_st.name = strcat (arg_st.name, ".", default_suffix); + endif + + if (! isempty (arg_st.printer) || isempty (arg_st.name)) + arg_st.send_to_printer = true; + if (isempty (arg_st.name)) + arg_st.name = strcat (tmpnam (), ".", default_suffix); + arg_st.unlink{end+1} = arg_st.name; + endif + endif + + if (all (! strcmp (arg_st.devopt, dev_list))) + arg_st.ghostscript_device = arg_st.devopt; + arg_st.ghostscript_output = arg_st.name; + ## FIXME - This will not work correctly if GS is used to produce a print + ## stream that is saved to a file and not sent to the printer. + if (arg_st.send_to_printer) + arg_st.devopt = "psc"; + arg_st.name = strcat (tmpnam (), ".ps"); + arg_st.unlink{end+1} = arg_st.name; + else + ## Assume the user desires only the figuure. This is useful for producing + ## pdf figures for pdflatex + ## octave:#> print -f1 -dpdfwrite figure1.pdf + arg_st.devopt = "epsc"; + arg_st.name = strcat (tmpnam (), ".eps"); + arg_st.unlink{end+1} = arg_st.name; endif - end_unwind_protect + endif + + if (any (strncmp (arg_st.devopt(1:2), {"ps", "pdf"}, 2))) + arg_st.paperoutput = true; + else + arg_st.paperoutput = false; + endif + + if (arg_st.debug) + disp ("Printing options"); + disp (arg_st) + endif +endfunction + +%!test +%! opts = __print_parse_opts__ (); +%! assert (opts.devopt, "psc"); +%! assert (opts.use_color, 1); +%! assert (opts.send_to_printer, true); +%! assert (opts.paperoutput, true); +%! assert (opts.name, opts.unlink{1}) +%! for n = 1:numel(opts.unlink) +%! unlink (opts.unlink{n}); +%! endfor + +%!test +%! opts = __print_parse_opts__ ("-dpsc", "-append"); +%! assert (opts.devopt, "psc"); +%! assert (opts.name(end+(-2:0)), ".ps"); +%! assert (opts.send_to_printer, true); +%! assert (opts.use_color, 1); +%! assert (opts.append_to_file, false); +%! assert (opts.paperoutput, true); +%! for n = 1:numel(opts.unlink) +%! unlink (opts.unlink{n}); +%! endfor + +%!test +%! opts = __print_parse_opts__ ("-deps", "-tight"); +%! assert (opts.name, opts.unlink{1}) +%! assert (opts.tight_flag, true); +%! assert (opts.paperoutput, false) +%! assert (opts.send_to_printer, true); +%! assert (opts.use_color, -1); +%! for n = 1:numel(opts.unlink) +%! unlink (opts.unlink{n}); +%! endfor + +%!test +%! opts = __print_parse_opts__ ("-djpg", "foobar", "-mono"); +%! assert (opts.devopt, "jpeg") +%! assert (opts.name, "foobar.jpg") +%! assert (opts.ghostscript_device, "") +%! assert (opts.send_to_printer, false); +%! assert (opts.printer, ""); +%! assert (opts.paperoutput, false) +%! assert (opts.use_color, -1); + +%!test +%! opts = __print_parse_opts__ ("-ddeskjet", "foobar", "-mono", "-Pmyprinter"); +%! assert (opts.ghostscript_output, "foobar.deskjet") +%! assert (opts.ghostscript_device, "deskjet") +%! assert (opts.devopt, "psc") +%! assert (opts.send_to_printer, true); +%! assert (opts.printer, "-Pmyprinter"); +%! assert (opts.paperoutput, true) +%! assert (opts.use_color, -1); + +%!test +%! opts = __print_parse_opts__ ("-f5", "-dljet3"); +%! assert (opts.name, opts.unlink{2}) +%! assert (opts.ghostscript_output, opts.unlink{1}) +%! assert (strfind (opts.ghostscript_output, ".ljet3")) +%! assert (strfind (opts.name, ".ps")) +%! assert (opts.devopt, "psc") +%! assert (opts.send_to_printer, true); +%! assert (opts.paperoutput, true) +%! assert (opts.figure, 5) +%! for n = 1:numel(opts.unlink) +%! unlink (opts.unlink{n}); +%! endfor + +function gs = __ghostscript_binary__ () + + persistent ghostscript_binary = "" + persistent warn_on_no_ghostscript = true + persistent warn_on_bad_gsc = true + + if (isempty (ghostscript_binary)) + GSC = getenv ("GSC"); + if (exist (GSC, "file") || (! isempty (GSC) && file_in_path (EXEC_PATH, GSC))) + gs_binaries = {GSC}; + elseif (! isempty (GSC) && warn_on_bad_gsc) + warning ("print:badgscenv", + "print.m: GSC environment variable not set properly.") + warn_on_bad_gsc = false; + gs_binaries = {}; + else + gs_binaries = {}; + endif + if (isunix ()) + ## Unix - Includes Mac OSX and Cygwin. + gs_binaries = horzcat (gs_binaries, {"gs", "gs.exe"}); + else + ## pc - Includes Win32 and mingw. + gs_binaries = horzcat (gs_binaries, {"gs.exe", "gswin32c.exe"}); + endif + n = 0; + while (n < numel (gs_binaries) && isempty (ghostscript_binary)) + n = n + 1; + ghostscript_binary = file_in_path (EXEC_PATH, gs_binaries{n}); + endwhile + if (warn_on_no_ghostscript && isempty (ghostscript_binary)) + warning ("print:noghostscript", + "print.m: ghostscript not found in EXEC_PATH.") + warn_on_no_ghostscript = false; + endif + endif + + gs = ghostscript_binary; endfunction +function bin = __find_binary__ (binary) + + persistent data = struct () + + if (! isfield (data, binary)) + ## Reinitialize when `user_binaries' is present. + data.(binary).bin = ""; + data.(binary).warn_on_absence = true; + endif + + if (isempty (data.(binary).bin)) + if (isunix ()) + ## Unix - Includes Mac OSX and Cygwin. + binaries = strcat (binary, {"", ".exe"}); + else + ## pc - Includes Win32 and mingw. + binaries = strcat (binary, {".exe"}); + endif + n = 0; + while (n < numel (binaries) && isempty (data.(binary).bin)) + n = n + 1; + data.(binary).bin = file_in_path (EXEC_PATH, binaries{n}); + endwhile + if (isempty (data.(binary).bin) && data.(binary).warn_on_absence) + warning (sprintf ("print:no%s", binary), + "print.m: '%s' not found in EXEC_PATH", binary) + data.(binary).warn_on_absence = false; + endif + endif + + bin = data.(binary).bin; + +endfunction + + diff --git a/scripts/plot/module.mk b/scripts/plot/module.mk --- a/scripts/plot/module.mk +++ b/scripts/plot/module.mk @@ -18,6 +18,7 @@ plot/private/__errcomm__.m \ plot/private/__errplot__.m \ plot/private/__ezplot__.m \ + plot/private/__ghostscript__.m \ plot/private/__interp_cube__.m \ plot/private/__line__.m \ plot/private/__patch__.m \ @@ -25,7 +26,8 @@ plot/private/__pltopt__.m \ plot/private/__quiver__.m \ plot/private/__scatter__.m \ - plot/private/__stem__.m + plot/private/__stem__.m \ + plot/private/__tight_eps_bbox__.m plot_FCN_FILES = \ plot/__gnuplot_get_var__.m \ diff --git a/scripts/plot/print.m b/scripts/plot/print.m --- a/scripts/plot/print.m +++ b/scripts/plot/print.m @@ -32,7 +32,12 @@ ## the handle for the current figure is used. ## ## @var{options}: +## ## @table @code +## @item -f@var{h} +## Specify the handle, @var{h}, of the figure to be printed. The +## default is the current figure. +## ## @item -P@var{printer} ## Set the @var{printer} name to which the graph is sent if no ## @var{filename} is specified. @@ -156,6 +161,9 @@ ## ## @item ppm ## Portable Pixel Map file format +## +## @item pdfwrite +## Converts ps or eps to pdf ## @end table ## ## For a complete list, type `system ("gs -h")' to see what formats @@ -200,13 +208,219 @@ ## The filename and options can be given in any order. ## @end deftypefn -function varargout = print (varargin) +function print (varargin) + + opts = __print_parse_opts__ (varargin{:}); - f = gcf (); + if (! isfigure (opts.figure)) + error ("print: no figure to print.") + endif + + orig_figure = get (0, "currentfigure"); + figure (opts.figure) drawnow (); - backend = (get (f, "__backend__")); + backend = (get (opts.figure, "__backend__")); + + if (strcmp (backend, "gnuplot")) + ## FIXME - this can be removed when __gnuplot_print__ has been modified + ## to work consistently with __fltk_print__ + __gnuplot_print__ (varargin{:}); + return + endif + + ## FIXME - this can be moved to __print_parse_opts__ when __gnuplot_print__ + ## has been modified to work consistently with __fltk_print__ + if (! isempty (opts.canvas_size) && ischar (opts.resolution)) + opts.canvas_size = str2num (strrep (strrep (opts.canvas_size, "X", ""), "Y", "")); + endif + if (! isempty (opts.resolution) && ischar (opts.resolution)) + opts.resolution = str2num (opts.resolution); + endif + if (! isempty (opts.fontsize) && ischar (opts.fontsize)) + opts.fontsize = str2num (opts.fontsize); + endif + + if (opts.append_to_file) + saved_original_file = strcat (tmpnam (), ".", opts.devopt); + opts.unlink(end+1) = {save_original_file}; + movefile (opts.name, saved_original_file); + endif + + ## Modify properties as specified by options + ## FIXME - need an unwind_protect block + props = []; + + if ((! isempty (opts.canvas_size)) + || (! strcmpi (get (opts.figure, "paperorientation"), opts.orientation))) + m = numel (props); + props(m+1).h = opts.figure; + props(m+1).name = "paperposition"; + props(m+1).value = {get(opts.figure, "paperposition")}; + props(m+2).h = opts.figure; + props(m+2).name = "paperunits"; + props(m+2).value = {get(opts.figure, "paperunits")}; + props(m+3).h = opts.figure; + props(m+3).name = "papersize"; + props(m+3).value = {get(opts.figure, "papersize")}; + props(m+4).h = opts.figure; + props(m+4).name = "paperorientation"; + props(m+4).value = {get(opts.figure, "paperorientation")}; + props(m+5).h = opts.figure; + props(m+5).name = "papertype"; + props(m+5).value = {get(opts.figure, "papertype")}; + if (! isempty (opts.canvas_size)) + ## canvas_size is in pixels/points + set (opts.figure, "paperorientation", "portrait"); + set (opts.figure, "paperposition", [0, 0, opts.canvas_size]); + set (opts.figure, "paperunits", "points"); + set (opts.figure, "papersize", opts.canvas_size); + fpos = get (opts.figure, "position"); + props(m+6).h = opts.figure; + props(m+6).name = "position"; + props(m+6).value = {fpos}; + fpos(3:4) = opts.canvas_size; + set (opts.figure, "position", fpos); + elseif (opts.paperoutput) + ## FIXME - should the backend handle this? + orient (opts.orientation) + endif + endif + + if (opts.force_solid != 0) + h = findobj (opts.figure, "-property", "linestyle"); + m = numel (props); + for n = 1:numel(h) + props(m+n).h = h(n); + props(m+n).name = "linestyle"; + props(m+n).value = {get(h(n), "linestyle")}; + endfor + if (opts.force_solid > 0) + linestyle = "-"; + else + linestyle = "--"; + endif + set (h, "linestyle", linestyle) + endif - varargout = cell (1, nargout); - [varargout{:}] = feval (strcat ("__", backend, "_print__"), varargin{:}); + if (opts.use_color < 0) + color_props = {"color", "facecolor", "edgecolor"}; + for c = 1:numel(color_props) + h = findobj (opts.figure, "-property", color_props{c}); + hnone = findobj (opts.figure, color_props{c}, "none"); + h = setdiff (h, hnone); + m = numel (props); + for n = 1:numel(h) + rgb = get (h(n), color_props{c}); + props(m+n).h = h(n); + props(m+n).name = color_props{c}; + props(m+n).value = {get(h(n), color_props{c})}; + xfer = repmat ([0.30, 0.59, 0.11], size (rgb, 1), 1); + ## convert RGB color to RGB gray scale + ggg = repmat (sum (xfer .* rgb, 2), 1, 3); + set (h(n), color_props{c}, ggg) + endfor + endfor + endif + + if (! isempty (opts.font) || ! isempty (opts.fontsize)) + h = findobj (opts.figure, "-property", "fontname"); + m = numel (props); + for n = 1:numel(h) + if (! isempty (opts.font)) + props(end+1).h = h(n); + props(end).name = "fontname"; + props(end).value = {get(h(n), "fontname")}; + endif + if (! isempty (opts.fontsize)) + props(end+1).h = h(n); + props(end).name = "fontsize"; + props(end).value = {get(h(n), "fontsize")}; + endif + endfor + if (! isempty (opts.font)) + set (h, "fontname", opts.font) + endif + if (! isempty (opts.fontsize)) + if (ischar (opts.fontsize)) + fontsize = str2double (opts.fontsize); + else + fontsize = opts.fontsize; + endif + set (h, "fontsize", fontsize) + endif + endif + + ## call the backend print script + drawnow ("expose") + feval (strcat ("__", backend, "_print__"), opts); + + ## restore modified properties + if (isstruct (props)) + for n = 1:numel(props) + set (props(n).h, props(n).name, props(n).value{1}) + endfor + endif + + ## Send to the printer + if (opts.send_to_printer) + if (isempty (opts.ghostscript_output)) + prn_datafile = opts.name; + else + prn_datafile = opts.ghostscript_output; + endif + if (isempty (opts.printer)) + prn_cmd = sprintf ("lpr %s '%s' 2>&1", opts.lpr_options, prn_datafile); + else + prn_cmd = sprintf ("lpr %s -P %s '%s' 2>&1", opts.lpr_options, + opts.printer, prn_datafile); + endif + if (opts.debug) + fprintf ("lpr command: %s\n", prn_cmd) + endif + [status, output] = system (prn_cmd); + if (status != 0) + disp (output) + warning ("print.m: printing failed.") + endif + endif + + ## Append to file using GS + if (opts.append_to_file) + if (strcmp (opts.devopt, "pdf")) + suffix = "pdf"; + elseif (strcmp (opts.devopt(1:2), "ps")) + suffix = "ps"; + endif + tmp_combined_file = strcat (tmpnam (), ".", suffix); + opts.unlink{end+1} = tmp_combined_file; + gs_opts = "-q -dNOPAUSE -dBATCH"; + gs_cmd = sprintf ("%s %s -sDEVICE=%swrite -sOutputFile=%s %s %s", + opts.ghostscript_binary, gs_opts, suffix, tmp_combined_file, + saved_original_file, opts.name); + [status, output] = system (gs_cmd); + if (opts.debug) + fprintf ("Append files: %s\n", gs_cmd); + endif + if (status != 0) + warning ("print:failedtoappendfile", + "print.m: failed to append output to file '%s'.", opts.name) + movefile (saved_original_file, opts.name); + else + movefile (tmp_combined_file, opts.name); + endif + endif + + ## Unlink temporary files + for n = 1:numel(opts.unlink) + [status, output] = unlink (opts.unlink{n}); + if (status != 0) + disp (output) + warning ("print.m: failed to delete temporay file, '%s'.", opts.name) + endif + endfor + + if (isfigure (orig_figure)) + figure (orig_figure); + endif endfunction diff --git a/scripts/plot/private/__ghostscript__.m b/scripts/plot/private/__ghostscript__.m new file mode 100644 --- /dev/null +++ b/scripts/plot/private/__ghostscript__.m @@ -0,0 +1,60 @@ +## Copyright (C) 2010 Ben Abbott +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 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 General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . + +## -*- texinfo -*- +## @deftypefn {Function File} {} __ghostscript__ (@var{@dots{}}) +## Undocumented internal function. +## @end deftypefn + +## Author: Ben Abbott +## Created: 2010-07-26 + +function status = __ghostscript__ (opts, varargin); + + if (nargin > 1) + opts.name = varargin{1}; + endif + if (nargin > 2) + opts.ghostscript_device = varargin{2}; + endif + if (nargin > 3) + opts.ghostscript_output = varargin{3}; + endif + + if (strncmp (opts.devopt, "eps", 3)) + ## "eps" files + gs_opts = "-q -dNOPAUSE -dBATCH -dSAFER -dEPSCrop"; + else + ## "ps" or "pdf" files + gs_opts = "-q -dNOPAUSE -dBATCH -dSAFER"; + endif + + cmd = sprintf ("%s %s -sDEVICE=%s -r%d -sOutputFile=%s %s", + opts.ghostscript_binary, gs_opts, opts.ghostscript_device, + opts.resolution, opts.ghostscript_output, opts.name); + + if (opts.debug) + fprintf ("Ghostscript command: %s\n", cmd); + endif + + [status, output] = system (cmd); + + if (status != 0) + warning ("print:ghostscripterror", + "print.m: gs failed to convert output to file '%s'.", opts.ghostscript_output) + endif + +endfunction diff --git a/scripts/plot/private/__tight_eps_bbox__.m b/scripts/plot/private/__tight_eps_bbox__.m new file mode 100644 --- /dev/null +++ b/scripts/plot/private/__tight_eps_bbox__.m @@ -0,0 +1,123 @@ +## Copyright (C) 2010 Ben Abbott +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2 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 General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Octave; see the file COPYING. If not, see +## . + +## -*- texinfo -*- +## @deftypefn {Function File} {@var{bbox} =} __tight_eps_bbox__ (@var{@dots{}}) +## Undocumented internal function. +## @end deftypefn + +## Author: Ben Abbott +## Created: 2010-07-26 + +function bb = __tight_eps_bbox__ (opts, eps_file_name) + + box_string = "%%BoundingBox:"; + + cmd = sprintf ("\"%s\" \"%s\" 2>&1", "head", eps_file_name); + [status, output] = system (cmd); + + if (status == 0) + orig_bbox_line = get_bbox (output); + else + error ("print:noboundingbox", + "print.m: no bounding box found in '%s'", eps_file_name) + endif + + ghostscript_options = "-q -dBATCH -dSAFER -dNOPAUSE -dTextAlphaBits=4 -sDEVICE=bbox"; + cmd = sprintf ("\"%s\" %s \"%s\" 2>&1", opts.ghostscript_binary, + ghostscript_options, eps_file_name); + [status, output] = system (cmd); + + if (status == 0) + tight_bbox_line = get_bbox (output); + else + warning ("print:nogsboundingbox", + "print.m: ghostscript failed to determine the bounding for '%s'", + eps_file_name) + endif + + ## Attempt to fix the bbox in place. + fid = fopen (eps_file_name, "r+"); + unwind_protect + bbox_replaced = false; + looking_for_bbox = true; + while (looking_for_bbox) + current_line = fgetl (fid); + if (strncmpi (current_line, box_string, numel(box_string))) + line_length = numel (current_line); + num_spaces = line_length - numel (tight_bbox_line); + if (numel (current_line) >= numel (tight_bbox_line)) + new_line = tight_bbox_line; + new_line(end+1:numel(current_line)) = " "; + bbox_replaced = true; + ## Back up to the beginning of the line (include EOL characters). + if (ispc ()) + fseek (fid, -line_length-2, "cof"); + else + fseek (fid, -line_length-1, "cof"); + endif + count = fprintf (fid, "%s", new_line); + endif + looking_for_bbox = false; + elseif (! ischar (current_line)) + looking_for_bbox = false; + endif + endwhile + unwind_protect_cleanup + fclose (fid); + end_unwind_protect + + ## If necessary load the eps-file and replace the bbox (can be slow). + if (! bbox_replaced) + fid = fopen (eps_file_name, "r"); + unwind_protect + data = char (fread (fid, Inf)).'; + unwind_protect_cleanup + fclose (fid); + end_unwind_protect + ## FIXME - should strfind() limit the instances as find() does? + n = strfind (data, box_string); + if (numel (n) > 1) + ## Only replace one instance. + n = n(1); + elseif (isempty (n)) + error ("print:noboundingbox", ... + "print.m: no bounding box found in '%s'.", eps_file_name) + endif + m = numel (orig_bbox_line); + data = horzcat (data(1:(n-1)), tight_bbox_line, data((n+m):end)); + fid = fopen (eps_file_name, "w"); + unwind_protect + fprintf (fid, "%s", data); + unwind_protect_cleanup + fclose (fid); + end_unwind_protect + endif + +endfunction + +function bbox_line = get_bbox (lines) + box_string = "%%BoundingBox:"; + pattern = strcat (box_string, "[^%]*"); + pattern = pattern(1:find(double(pattern)>32, 1, "last")); + bbox_line = regexp (lines, pattern, "match"); + if (iscell (bbox_line)) + bbox_line = bbox_line{1}; + endif + ## Remove the EOL characters. + bbox_line(double(bbox_line)<32) = ""; +endfunction +