changeset 10834:05ba991794d4

Improvements for fltk printing.
author Ben Abbott <bpabbott@mac.com>
date Thu, 29 Jul 2010 19:44:07 -0400
parents e5c752231985
children 4bbd6f75f173
files scripts/ChangeLog scripts/plot/__fltk_print__.m scripts/plot/__gnuplot_print__.m scripts/plot/__print_parse_opts__.m scripts/plot/module.mk scripts/plot/print.m scripts/plot/private/__ghostscript__.m scripts/plot/private/__tight_eps_bbox__.m
diffstat 8 files changed, 892 insertions(+), 169 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/ChangeLog	Thu Jul 29 17:49:50 2010 -0400
+++ b/scripts/ChangeLog	Thu Jul 29 19:44:07 2010 -0400
@@ -1,3 +1,10 @@
+2010-07-29  Ben Abbott <bpabbott@mac.com>
+
+	* 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  <highegg@gmail.com>
 
 	* linear-algebra/expm.m: Special-case scalars and diagonal matrices.
--- a/scripts/plot/__fltk_print__.m	Thu Jul 29 17:49:50 2010 -0400
+++ b/scripts/plot/__fltk_print__.m	Thu Jul 29 19:44:07 2010 -0400
@@ -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
+
+
--- a/scripts/plot/__gnuplot_print__.m	Thu Jul 29 17:49:50 2010 -0400
+++ b/scripts/plot/__gnuplot_print__.m	Thu Jul 29 19:44:07 2010 -0400
@@ -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");
--- a/scripts/plot/__print_parse_opts__.m	Thu Jul 29 17:49:50 2010 -0400
+++ b/scripts/plot/__print_parse_opts__.m	Thu Jul 29 19:44:07 2010 -0400
@@ -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
+
+
--- a/scripts/plot/module.mk	Thu Jul 29 17:49:50 2010 -0400
+++ b/scripts/plot/module.mk	Thu Jul 29 19:44:07 2010 -0400
@@ -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 \
--- a/scripts/plot/print.m	Thu Jul 29 17:49:50 2010 -0400
+++ b/scripts/plot/print.m	Thu Jul 29 19:44:07 2010 -0400
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/private/__ghostscript__.m	Thu Jul 29 19:44:07 2010 -0400
@@ -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
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {} __ghostscript__ (@var{@dots{}})
+## Undocumented internal function.
+## @end deftypefn
+
+## Author: Ben Abbott <bpabbott@mac.com>
+## 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/plot/private/__tight_eps_bbox__.m	Thu Jul 29 19:44:07 2010 -0400
@@ -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
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn {Function File} {@var{bbox} =} __tight_eps_bbox__ (@var{@dots{}})
+## Undocumented internal function.
+## @end deftypefn
+
+## Author: Ben Abbott <bpabbott@mac.com>
+## 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
+