# HG changeset patch
# User Pantxo Diribarne <pantxo.diribarne@gmail.com>
# Date 1510913010 -3600
#      Fri Nov 17 11:03:30 2017 +0100
# Node ID 3dec89bae1f2f4d9c1ca289d88c8669a91100522
# Parent  d525ecfe928f894dd575a76236149b9f4bda62c7
Allow the use of SVG convertion for PDF and raster printing (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. 
  (svgconvert): new function to build the svg convertion command

* __print_parse_opts__.m: in warnings, replace "print:" namespace by
  "octave:print:". Add svgconvert and 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: when svgconvert is on and for other ghostscript devices
  than ps2write, first convert SVG to PDF and pass the PDF to ghostscript
  instead of an EPS.

diff -r d525ecfe928f -r 3dec89bae1f2 scripts/plot/util/print.m
--- a/scripts/plot/util/print.m	Tue Sep 04 18:55:47 2018 +0200
+++ b/scripts/plot/util/print.m	Fri Nov 17 11:03:30 2017 +0100
@@ -74,6 +74,18 @@
 ## orientation specified.  This option is equivalent to changing the figure's
 ## @qcode{"paperorientation"} property.
 ##
+## @item -svgconvert
+##   For OpenGL based toolkits, this option adds support for printing
+## arbitrary characters and fonts in PDF and raster formats (by
+## default those formats inherit the limitations of the EPS format).
+##
+## This option also avoids some antialiasing artifacts in patch and surface
+## objects rendering.  Finally it adds support for printing transparent
+## line, patch and surface objects.
+##
+## Caution: -svgconvert may lead to innacurate rendering of image
+## objects
+##
 ## @item  -TextAlphaBits=@var{n}
 ## @itemx -GraphicsAlphaBits=@var{n}
 ##   Octave is able to produce output for various printers, bitmaps, and
@@ -84,7 +96,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 +105,19 @@
 ##   @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.
 ##
 ##   @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 +143,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 @nospell{aifm}
 ##     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}
-##     @nospell{CorelDraw}
+##   @item  cdr*
+##   @itemx @nospell{corel*}
+##     CorelDraw.
 ##
 ##   @item cgm
 ##     Computer Graphics Metafile, Version 1, ANSI X3.122-1986
@@ -168,9 +185,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
@@ -179,10 +195,9 @@
 ##   @itemx jpeg
 ##     JPEG image
 ##
-##   @item latex
-##   @itemx eepic
-##     @LaTeX{} picture environment and extended picture environment
-## (only available for the Gnuplot graphics toolkit).
+##   @item latex*
+##   @itemx eepic*
+##     @LaTeX{} picture environment and extended picture environment.
 ##
 ##   @item mf
 ##     Metafont
@@ -194,7 +209,9 @@
 ##     PBMplus
 ##
 ##   @item pdf
-##     Portable Document Format
+##   @itemx pdfcrop
+##     Portable document format. The pdfcrop device removes the default
+## surrounding page.
 ##
 ##   @item svg
 ##     Scalable Vector Graphics
@@ -204,12 +221,11 @@
 ##     TIFF image, compressed
 ##
 ##   @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,
@@ -220,9 +236,6 @@
 ## Some examples are;
 ##
 ##   @table @code
-##   @item pdfwrite
-##     Produces pdf output from eps
-##
 ##   @item ljet2p
 ##     HP LaserJet @nospell{IIP}
 ##
@@ -346,6 +359,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");
@@ -397,11 +411,13 @@
       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")
+        && ((! opts.svgconvert && ! strcmp (opts.devopt, "svg"))
+            || (opts.svgconvert && strcmp (opts.devopt, "ps2write"))))
       for n = 1:numel (hax)
         if (strcmp (get (hax(n), "gridcolormode"), "auto"))
           props(end+1).h = hax(n);
@@ -585,7 +601,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
@@ -653,7 +670,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)
@@ -890,3 +907,28 @@
   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 - %%s %3.2f %s %d %%s", opts.svgconvert_binary, ...
+                   get (0, "screenpixelsperinch"), def_font, 1);
+
+    if (opts.debug)
+      fprintf ("svgconvert command: '%s'\n", cmd);
+    endif
+  endif
+
+endfunction
diff -r d525ecfe928f -r 3dec89bae1f2 scripts/plot/util/private/__opengl_print__.m
--- a/scripts/plot/util/private/__opengl_print__.m	Tue Sep 04 18:55:47 2018 +0200
+++ b/scripts/plot/util/private/__opengl_print__.m	Fri Nov 17 11:03:30 2017 +0100
@@ -78,7 +78,15 @@
     case "svg"
       ## format GL2PS_SVG
       gl2ps_device = {"svg"};
-      pipeline = {sprintf("cat > %s", opts.name)};
+      svgcmd = "";
+      if (opts.svgconvert)
+        svgcmd = opts.svgconvert_cmd (opts, opts.ghostscript.device);
+      endif
+      if (! isempty (svgcmd))
+        pipeline = {sprintf(svgcmd, "svg", opts.name)};
+      else
+        pipeline = {sprintf("cat > %s", opts.name)};
+      endif
     case fig2dev_devices
       cmd_pstoedit = opts.pstoedit_cmd (opts, "fig");
       cmd_fig2dev = opts.fig2dev_cmd (opts, opts.devopt);
@@ -110,36 +118,63 @@
       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 = "";
+      if (opts.svgconvert)
+        svgcmd = opts.svgconvert_cmd (opts, opts.ghostscript.device);
+      endif
+      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, "pdf", tmp);
+      endif
+
       opts.ghostscript.output = opts.name;
       if (opts.send_to_printer)
         opts.unlink(strcmp (opts.unlink, opts.ghostscript.output)) = [];
         opts.ghostscript.output = "-";
       endif
+
       [cmd_gs, cmd_cleanup] = __ghostscript__ (opts.ghostscript);
       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
 
diff -r d525ecfe928f -r 3dec89bae1f2 scripts/plot/util/private/__print_parse_opts__.m
--- a/scripts/plot/util/private/__print_parse_opts__.m	Tue Sep 04 18:55:47 2018 +0200
+++ b/scripts/plot/util/private/__print_parse_opts__.m	Fri Nov 17 11:03:30 2017 +0100
@@ -60,6 +60,8 @@
   arg_st.printer = "";
   arg_st.send_to_printer = false;
   arg_st.special_flag = "textnormal";
+  arg_st.svgconvert = false;
+  arg_st.svgconvert_binary = __quote_path__ (__svgconv_binary__ ());
   arg_st.tight_flag = false;
   arg_st.use_color = 0; # 0=default, -1=mono, +1=color
 
@@ -100,6 +102,8 @@
       elseif (strcmp (arg, "-tight"))
         arg_st.loose = false;
         arg_st.tight_flag = true;
+      elseif (strcmp (arg, "-svgconvert"))
+        arg_st.svgconvert = true;
       elseif (strcmp (arg, "-textspecial"))
         arg_st.special_flag = "textspecial";
       elseif (any (strcmp (arg,
@@ -155,9 +159,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)
@@ -213,6 +220,8 @@
     arg_st.devopt = "jpeg";
   elseif (strcmp (arg_st.devopt, "tif"))
     arg_st.devopt = "tiff";
+  elseif (strcmp (arg_st.devopt, "pdfcrop"))
+    arg_st.devopt = "pdfwrite";
   endif
 
   persistent dev_list = {"aifm", "corel", "fig", "png", "jpeg", ...
@@ -221,7 +230,7 @@
               "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", ...
@@ -236,7 +245,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", ...
@@ -384,7 +393,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
@@ -467,7 +478,6 @@
 
 endfunction
 
-
 function gs = __ghostscript_binary__ ()
 
   persistent ghostscript_binary = "";
@@ -479,7 +489,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 = {};
@@ -505,6 +515,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 ();
