# HG changeset patch
# User Pantxo Diribarne <pantxo.diribarne@gmail.com>
# Date 1510913010 -3600
#      Fri Nov 17 11:03:30 2017 +0100
# Node ID 70c34b3395080bc0b053e9c483b6f440a60b58f1
# Parent  f3d754deb41dd0a6631a153ae6b2eed09275262f
Switch to SVG convertion for PDF and raster printing in OpenGL (bug #52193)

* print.m (docstring): mark Gnuplot only devices with "*". For ps formats 
  document the limited text support. Add "pdfcrop" device (no surrounding page)
  and remove "pdfwrite" from the examples of ghostscript devices.
  (print): in warnings, replace "print:" namespace by "octave:print:". 
  Only change grid lines transparency for "ps2write" device. Add svgconvert_cmd 
  field to opt structure
  (svgconvert): new function to build the svg convertion command

* __print_parse_opts__.m: in warnings, replace "print:" namespace by
  "octave:print:". Add svgconvert_binary to arg_st structure.
  (__svgconv_binary__): New function to locate the svg conversion binary either
  using OCTAVE_SVGCONVERT environment variable or in Octave's bindir. 

* __opengl_print__.m: for other ghostscript devices than ps2write, first convert
 SVG to PDF and pass the PDF to ghostscript instead of an EPS

diff -r f3d754deb41d -r 70c34b339508 scripts/plot/util/print.m
--- a/scripts/plot/util/print.m	Wed Nov 08 16:34:23 2017 +0100
+++ b/scripts/plot/util/print.m	Fri Nov 17 11:03:30 2017 +0100
@@ -84,7 +84,8 @@
 ##
 ## @item -d@var{device}
 ##   The available output format is specified by the option @var{device}, and
-## is one of:
+## is one of (devices marked with a "*" are available only with Gnuplot
+## toolkit):
 ##
 ##   @table @code
 ##   @item  ps
@@ -92,14 +93,22 @@
 ##   @itemx psc
 ##   @itemx psc2
 ##     PostScript (level 1 and 2, mono and color).  The OpenGL-based toolkits
-## always generate PostScript level 3.0.
+## always generate PostScript level 3.0 and have a limited support for
+## text.  Only the set of ASCII characters may be used and the only
+## supported fonts are the base postscript fonts: Helvetica (the
+## default), Times, Courier and their variants (bold or italic).  Any
+## other font will be replaced by Helvetica.
 ##
 ##   @item  eps
 ##   @itemx eps2
 ##   @itemx epsc
 ##   @itemx epsc2
 ##     Encapsulated PostScript (level 1 and 2, mono and color).  The
-## OpenGL-based toolkits always generate PostScript level 3.0.
+## OpenGL-based toolkits always generate PostScript level 3.0 and have a
+## limited support for text.  Only the set of ASCII characters may be
+## used and the only supported fonts are the base postscript fonts:
+## Helvetica (the default), Times, Courier and their variants (bold or
+## italic).  Any other font will be replaced by Helvetica.
 ##
 ##   @item  pslatex
 ##   @itemx epslatex
@@ -125,31 +134,30 @@
 ## braces).  The @samp{pdflatex} device, and any of the @samp{standalone}
 ## formats, are not available with the Gnuplot toolkit.
 ##
-##   @item  epscairo
-##   @itemx pdfcairo
-##   @itemx epscairolatex
-##   @itemx pdfcairolatex
-##   @itemx epscairolatexstandalone
-##   @itemx pdfcairolatexstandalone
-##     Generate Cairo based output when using the Gnuplot graphics toolkit.
-## The @samp{epscairo} and @samp{pdfcairo} devices are synonymous with
-## the @samp{epsc} device.  The @LaTeX{} variants generate a @LaTeX{} file,
-## @file{@var{filename}.tex}, for the text portions of a plot, and an image
-## file, @file{@var{filename}.(eps|pdf)}, for the graph portion of the plot.
-## The @samp{standalone} variants behave as described for
+##   @item  epscairo*
+##   @itemx pdfcairo*
+##   @itemx epscairolatex*
+##   @itemx pdfcairolatex*
+##   @itemx epscairolatexstandalone*
+##   @itemx pdfcairolatexstandalone*
+##     Generate Cairo based output.  The @samp{epscairo} and
+## @samp{pdfcairo} devices are synonymous with the @samp{epsc} device.
+## The @LaTeX{} variants generate a @LaTeX{} file,
+## @file{@var{filename}.tex}, for the text portions of a plot, and an
+## image file, @file{@var{filename}.(eps|pdf)}, for the graph portion of
+## the plot.  The @samp{standalone} variants behave as described for
 ## @samp{epslatexstandalone} above.
 ##
 ##   @item  ill
 ##   @itemx aifm
-##     Adobe Illustrator (Obsolete for Gnuplot versions > 4.2)
+##     Adobe Illustrator (Obsolete for Gnuplot versions > 4.2).
 ##
-##   @item canvas
-##     Javascript-based drawing on HTML5 canvas viewable in a web browser
-## (only available for the Gnuplot graphics toolkit).
+##   @item canvas*
+##     Javascript-based drawing on HTML5 canvas viewable in a web browser.
 ##
-##   @item  cdr
-##   @itemx @nospell{corel}
-##     CorelDraw
+##   @item  cdr*
+##   @itemx @nospell{corel*}
+##     CorelDraw. 
 ##
 ##   @item dxf
 ##     AutoCAD
@@ -164,8 +172,8 @@
 ## whether the special flag should be set for the text in the figure.
 ## (default is @option{-textnormal})
 ##
-##   @item gif
-##     GIF image (only available for the Gnuplot graphics toolkit)
+##   @item gif*
+##     GIF image.
 ##
 ##   @item hpgl
 ##     HP plotter language
@@ -174,9 +182,8 @@
 ##   @itemx jpeg
 ##     JPEG image
 ##
-##   @item latex
-##     @LaTeX{} picture environment (only available for the Gnuplot graphics
-## toolkit).
+##   @item latex*
+##     @LaTeX{} picture environment.
 ##
 ##   @item mf
 ##     Metafont
@@ -188,18 +195,19 @@
 ##     PBMplus
 ##
 ##   @item pdf
-##     Portable document format
+##   @itemx pdfcrop
+##     Portable document format. The pdfcrop device removes the default
+## surrounding page 
 ##
 ##   @item svg
 ##     Scalable vector graphics
 ##
 ##   @item  tikz
-##   @itemx tikzstandalone
+##   @itemx tikzstandalone*
 ##     Generate a @LaTeX{} file using PGF/TikZ format.  The OpenGL-based
 ## toolkits create a PGF file while Gnuplot creates a TikZ file.  The
 ## @samp{tikzstandalone} device produces a @LaTeX{} document which includes the
-## TikZ file (@samp{tikzstandalone} and is only available for the Gnuplot
-## graphics toolkit).
+## TikZ file.
 ##   @end table
 ##
 ##   If the device is omitted, it is inferred from the file extension,
@@ -210,9 +218,6 @@
 ## Some examples are;
 ##
 ##   @table @code
-##   @item pdfwrite
-##     Produces pdf output from eps
-##
 ##   @item ljet2p
 ##     HP LaserJet @nospell{IIP}
 ##
@@ -336,6 +341,7 @@
   opts.latex_standalone = @latex_standalone;
   opts.lpr_cmd = @lpr;
   opts.epstool_cmd = @epstool;
+  opts.svgconvert_cmd = @svgconvert;
 
   if (isempty (opts.figure) || ! isfigure (opts.figure))
     error ("print: no figure to print");
@@ -387,11 +393,11 @@
       nfig += 1;
     endfor
 
-    ## FIXME: line transparency is only handled for svg output when
-    ## using gl2ps. For other formats, switch grid lines to light gray
+    ## FIXME: line transparency is not handled for postscript output when
+    ## using opengl toolkits. For ps formats, switch grid lines to light gray
     ## so that the image output approximately matches on-screen experience.
     hax = findall (opts.figure, "type", "axes");
-    if (! strcmp (tk, "gnuplot") && ! strcmp (opts.devopt, "svg"))
+    if (! strcmp (tk, "gnuplot") && strcmp (opts.devopt, "ps2write"))
       for n = 1:numel (hax)
         if (strcmp (get (hax(n), "gridcolormode"), "auto"))
           props(end+1).h = hax(n);
@@ -572,7 +578,8 @@
     for n = 1:numel (opts.unlink)
       [status, output] = unlink (opts.unlink{n});
       if (status != 0)
-        warning ("print.m: %s, '%s'", output, opts.unlink{n});
+        warning ("octave:print:unlinkerror", ...
+                 "print.m: %s, '%s'", output, opts.unlink{n});
       endif
     endfor
   end_unwind_protect
@@ -640,7 +647,7 @@
   endif
 
   if (! isempty (opts.preview) && opts.tight_flag)
-    warning ("print:previewandtight",
+    warning ("octave:print:previewandtight",
              "print.m: eps preview may not be combined with -tight");
   endif
   if (! isempty (opts.preview) || opts.tight_flag)
@@ -877,3 +884,29 @@
   endif
 
 endfunction
+
+function cmd = svgconvert (opts, devopt)
+
+  cmd = "";
+  
+  if (nargin < 2)
+    devopt = opts.devopt;
+  endif
+
+  if (isempty (opts.svgconvert_binary))
+    warning ("octave:print:nosvgconvert", ...
+             ["print.m: unale to find octave-svgconvert, ", ...
+              "falling back to eps convertion"]);
+  else
+    def_font = fullfile (__octave_config_info__ ("datadir"), "font", ...
+                         "FreeSans.otf");
+    cmd = sprintf ("%s - pdf %3.2f %s %d %%s", opts.svgconvert_binary, ...
+                   get (0, "screenpixelsperinch"), def_font, ...
+                   strcmp (opts.devopt, "pdfwrite"));
+
+    if (opts.debug)
+      fprintf ("svgconvert command: '%s'\n", cmd);
+    endif
+  endif
+
+endfunction
diff -r f3d754deb41d -r 70c34b339508 scripts/plot/util/private/__opengl_print__.m
--- a/scripts/plot/util/private/__opengl_print__.m	Wed Nov 08 16:34:23 2017 +0100
+++ b/scripts/plot/util/private/__opengl_print__.m	Fri Nov 17 11:03:30 2017 +0100
@@ -113,10 +113,22 @@
       pipeline = {sprintf("%s > %s", cmd, opts.name)};
     case {"corel", "gif"}
       error ("print:unsupporteddevice",
-             "print.m: %s output is not available for the FLTK graphics toolkit",
+             "print.m: %s output is not available for OpenGL graphics toolkits",
              upper (opts.devopt));
     case opts.ghostscript.device
-      opts.ghostscript.source = "-";
+      ## Except for postscript, use svg format and first convert to pdf
+      ## before going through ghostscript for final adjusments
+      svgcmd = opts.svgconvert_cmd (opts, opts.ghostscript.device);
+      dosvg = ! (strcmp (opts.devopt, "ps2write") || isempty (svgcmd)); 
+      if (! dosvg)
+        opts.ghostscript.source = "-";
+      else
+        tmp = tempname ();
+        opts.ghostscript.source = tmp;
+        opts.unlink = [opts.unlink tmp];
+        svgcmd = sprintf (svgcmd, tmp);
+      endif
+      
       opts.ghostscript.output = opts.name;
       if (opts.send_to_printer)
         opts.unlink(strcmp (opts.unlink, opts.ghostscript.output)) = [];
@@ -126,23 +138,34 @@
       if (opts.send_to_printer || isempty (opts.name))
         cmd_lpr = opts.lpr_cmd (opts);
         cmd = sprintf ("%s | %s", cmd_gs, cmd_lpr);
+      elseif (dosvg)
+        if (dos_shell)
+          cmd = sprintf ("%s & %s", svgcmd, cmd_gs); 
+        else
+          cmd = sprintf ("%s ; %s", svgcmd, cmd_gs); 
+        endif
       else
         cmd = sprintf ("%s", cmd_gs);
       endif
+      
+      if (dosvg)
+        gl2ps_device = {"svg"};
+      else
+        gl2ps_device = {"eps"};
+      endif
+      
       if (! isempty (cmd_cleanup))
-        gl2ps_device = {"eps"};
         if (dos_shell)
           pipeline = {sprintf("%s & %s", cmd, cmd_cleanup)};
         else
           pipeline = {sprintf("%s ; %s", cmd, cmd_cleanup)};
         endif
       else
-        gl2ps_device = {"eps"};
         pipeline = {cmd};
       endif
     otherwise
       error (sprintf ("print:no%soutput", opts.devopt),
-             "print.m: %s output is not available for GL2PS output",
+             "print.m: %s output is not available for OpenGL toolkits",
              upper (opts.devopt));
   endswitch
 
@@ -152,7 +175,7 @@
     if (opts.debug)
       fprintf ("opengl-pipeline: '%s'\n", pipeline{n});
     endif
-
+    
     if (strcmp (get (opts.figure, "visible"), "on"))
       ## Use toolkits "print_figure" method
       drawnow (gl2ps_device{n}, ['|' pipeline{n}]);
diff -r f3d754deb41d -r 70c34b339508 scripts/plot/util/private/__print_parse_opts__.m
--- a/scripts/plot/util/private/__print_parse_opts__.m	Wed Nov 08 16:34:23 2017 +0100
+++ b/scripts/plot/util/private/__print_parse_opts__.m	Fri Nov 17 11:03:30 2017 +0100
@@ -60,6 +60,7 @@
   arg_st.printer = "";
   arg_st.send_to_printer = false;
   arg_st.special_flag = "textnormal";
+  arg_st.svgconvert_binary = __quote_path__ (__svgconv_binary__ ());
   arg_st.tight_flag = false;
   arg_st.use_color = 0; # 0=default, -1=mono, +1=color
 
@@ -155,9 +156,12 @@
       elseif (length (arg) > 2 && arg(1:2) == "-f")
         arg_st.figure = str2double (arg(3:end));
       elseif (any (strcmp (arg, {"-painters", "-opengl"})))
-        warning ("print: '%s' accepted for Matlab compatibility, but is ignored", arg);
+        warning ("octave:ignoredargument", ...
+                 ["print: '%s' accepted for Matlab compatibility, ", ...
+                  "but is ignored"], arg);
       elseif (strcmp (arg, "-noui"))
-        warning ("print: option '-noui' not yet implemented");
+        warning ("octave:ignoredargument", ...
+                 "print: option '-noui' not yet implemented");
       elseif (length (arg) >= 1 && arg(1) == "-")
         error ("print: unknown option '%s'", arg);
       elseif (length (arg) > 0)
@@ -211,15 +215,17 @@
     arg_st.devopt = "emf";
   elseif (strcmp (arg_st.devopt, "jpg"))
     arg_st.devopt = "jpeg";
+  elseif (strcmp (arg_st.devopt, "pdfcrop"))
+    arg_st.devopt = "pdfwrite";
   endif
-
+  
   persistent dev_list = {"aifm", "corel", "fig", "png", "jpeg", ...
               "gif", "pbm", "pbmraw", "dxf", "mf", ...
               "svg", "hpgl", "ps", "ps2", "psc", ...
               "psc2", "eps", "eps2", "epsc", "epsc2", ...
               "emf", "pdf", "pslatex", "epslatex", "epslatexstandalone", ...
               "pslatexstandalone", "pdflatexstandalone", ...
-              "pstex", "tiff", "tiffn" "tikz", "pcxmono", ...
+              "pstex", "tiff", "tiffn" "tikz", "tikzstandalone", "pcxmono", ...
               "pcx24b", "pcx256", "pcx16", "pgm", "pgmraw", ...
               "ppm", "ppmraw", "pdflatex", "texdraw", ...
               "epscairo", "pdfcairo", "pngcairo", "cairolatex", ...
@@ -233,7 +239,7 @@
               "ps", "eps", "eps", "eps", "eps", ...
               "emf", "pdf", "tex", "tex", "tex", ...
               "tex", "tex", ...
-              "ps", "tiff", "tiff", "tikz", "pcx", ...
+              "ps", "tiff", "tiff", "tikz", "tikz", "pcx", ...
               "pcx", "pcx", "pcx", "pgm", "pgm", ...
               "ppm", "ppm", "tex", "tex", ...
               "eps", "pdf", "png", "tex", ...
@@ -371,7 +377,9 @@
 
   if (warn_on_missing_ghostscript)
     if (isempty (arg_st.ghostscript.binary))
-      warning ("print:missing_gs", "print.m: Ghostscript binary is not available.  Only eps output is possible");
+      warning ("octave:print:missing_gs", ...
+               ["print.m: Ghostscript binary is not available.  ", ...
+                "Only eps output is possible"]);
     endif
     warn_on_missing_ghostscript = false;
   endif
@@ -454,7 +462,6 @@
 
 endfunction
 
-
 function gs = __ghostscript_binary__ ()
 
   persistent ghostscript_binary = "";
@@ -466,7 +473,7 @@
         || (! isempty (GSC) && file_in_path (getenv ("PATH"), GSC)))
       gs_binaries = {GSC};
     elseif (! isempty (GSC) && warn_on_bad_gsc)
-      warning ("print:badgscenv",
+      warning ("octave:print:badgscenv",
                "print.m: GSC environment variable not set properly");
       warn_on_bad_gsc = false;
       gs_binaries = {};
@@ -492,6 +499,30 @@
 
 endfunction
 
+function bin = __svgconv_binary__ ()
+
+  persistent binary = "";
+  
+  if (isempty (binary))
+    binary = getenv ("OCTAVE_SVGCONVERT");
+    if (isempty (binary))
+      ## Look into bindir
+      binary = fullfile (__octave_config_info__ ("libdir"), ...
+                         "octave_svgconvert");
+    endif
+    
+    if (isempty (binary) || ! exist (binary, "file"))
+      binary = "";
+    elseif (! isunix ())
+      ## Unix - Includes Mac OSX and Cygwin.
+      binary = [binary, ".exe"];
+    endif
+  endif
+
+  bin = binary;
+
+endfunction
+
 function bin = __find_binary__ (binary)
 
   persistent data = struct ();
