On Sun, Apr 26, 2015 at 08:07:47PM +0200, Kornel Benko wrote:
> Am Sonntag, 26. April 2015 um 16:21:51, schrieb Enrico Forestieri 
> <[email protected]>
> 
> > I am for reverting http://www.lyx.org/trac/changeset/0bb378ba/lyxgit
> > and adopting the much simpler patch attached to #9371 for solving that
> > specific issue.
> > 
> > If this is not the opinion of the majority, then something has to be
> > done for solving the problem of the inferior quality of the previous
> > generated by the legacy method and the problem of its slowness.
> 
> Yes, something should be done.

And somebody has to do it.

> > I think that the poor quality is due to the use of the T1 font encoding
> > that forbids the use of vector fonts for the previews. This could be solved
> > by using either the "lmodern" or the "ae" package. But there would still
> > the problem of the slowness of the legacy method (it's about 10 times
> > slower than the dvipng method), mainly due to the use of ghostscript.
> > Also in this case there could be a solution, by using alternative methods
> > for generating pngs. For example, pdftocairo can generate pngs and is much
> > faster than gs. 

The attached patch against master implements those ideas. Now the pdf
generated by pdflatex contains vector fonts, so that the preview of
math and text snippets is much improved. Moreover, if pdftocairo is
detected, it is used instead of ghostscript for producing bitmaps.
I see both a speed improvement in the order of a factor of 4÷5 with
respect to gs (this makes this route only about a factor 2 slower than
the dvipng route) and also an improved quality of the generated bitmaps.

> > Still, I would see this as a palliative and think that reverting the
> > offending commit is better. Let's see what others think.

Though, I have not changed idea...

-- 
Enrico
diff --git a/lib/scripts/legacy_lyxpreview2ppm.py 
b/lib/scripts/legacy_lyxpreview2ppm.py
index e6d5371..1454a6b 100644
--- a/lib/scripts/legacy_lyxpreview2ppm.py
+++ b/lib/scripts/legacy_lyxpreview2ppm.py
@@ -42,6 +42,8 @@
 # * gs;
 # * pdflatex (optional);
 # * pnmcrop (optional).
+# * pdftocairo (optional).
+# * epstopdf (optional).
 
 # preview.sty is part of the preview-latex project
 #   http://preview-latex.sourceforge.net/
@@ -56,11 +58,11 @@
 # [legacy_conversion_step2]
 # 2) Call dvips to create one PS file for each DVI page
 # [legacy_conversion_step3]
-# 3) If dvips fails look for PDF and call gs to produce bitmaps
-# 4) Otherwise call gs on each PostScript file to produce bitmaps
+# 3) If dvips fails look for PDF and call pdftocairo or gs to produce bitmaps
+# 4) Otherwise call pdftocairo or gs on each PostScript file to produce bitmaps
 # [legacy_conversion_pdflatex]
 # 5) Keep track of pages on which gs failed and pass them to pdflatex
-# 6) Call gs on the PDF output from pdflatex to produce bitmaps
+# 6) Call pdftocairo or gs on the PDF output from pdflatex to produce bitmaps
 # 7) Extract and write to file (or return to lyxpreview2bitmap)
 #    metrics from both methods (standard and pdflatex)
 
@@ -74,6 +76,8 @@
 # is required in certain cases, if hyperref is active for instance,
 # (step 5, 6).
 # If possible, dvipng should be used, as it's much faster.
+# If possible, the script will use pdftocairo instead of gs,
+# as it's much faster and gives better results.
 
 import glob, os, pipes, re, string, sys
 
@@ -229,6 +233,7 @@ def legacy_latex_file(latex_file, fg_color, bg_color):
 \definecolor{bg}{rgb}{%s}
 \pagecolor{bg}
 \usepackage[%s,tightpage]{preview}
+\usepackage{ae,aecompl}
 \makeatletter
 \g@addto@macro\preview{\begingroup\color{bg}\special{ps::clippath 
fill}\color{fg}}
 \g@addto@macro\endpreview{\endgroup}
@@ -307,8 +312,8 @@ def legacy_conversion_step1(latex_file, dpi, output_format, 
fg_color, bg_color,
 # Creates a new LaTeX file from the original with pages specified in
 # failed_pages, pass it through pdflatex and updates the metrics
 # from the standard legacy route
-def legacy_conversion_pdflatex(latex_file, failed_pages, legacy_metrics, gs,
-    gs_device, gs_ext, alpha, resolution, output_format):
+def legacy_conversion_pdflatex(latex_file, failed_pages, legacy_metrics,
+    use_pdftocairo, conv, gs_device, gs_ext, alpha, resolution, output_format):
 
     # Search for pdflatex executable
     pdflatex = find_exe(["pdflatex"])
@@ -323,16 +328,30 @@ def legacy_conversion_pdflatex(latex_file, failed_pages, 
legacy_metrics, gs,
         pdflatex_status, pdflatex_stdout = run_latex(pdflatex, pdf_latex_file)
 
         pdf_file = latex_file_re.sub(".pdf", pdf_latex_file)
-
-        # GhostScript call to produce bitmaps
-        gs_call = '%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s ' \
-                    '-sOutputFile="%s%%d.%s" ' \
-                    '-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \
-                    '-r%f "%s"' \
-                    % (gs, gs_device, latex_file_re.sub("", pdf_latex_file), \
-                        gs_ext, alpha, alpha, resolution, pdf_file)
-        gs_status, gs_stdout = run_command(gs_call)
-        if gs_status:
+        latex_file_root = latex_file_re.sub("", pdf_latex_file)
+
+        # Converter call to produce bitmaps
+        if use_pdftocairo:
+            conv_call = '%s -png -transp -r %d "%s" "%s"' \
+                        % (conv, resolution, pdf_file, latex_file_root)
+            conv_status, conv_stdout = run_command(conv_call)
+            if not conv_status:
+                seqnum_re = re.compile("-([0-9]+)")
+                for name in glob.glob("%s-*.png" % latex_file_root):
+                    match = seqnum_re.search(name)
+                    if match != None:
+                        new_name = seqnum_re.sub(str(int(match.group(1))), 
name)
+                        os.rename(name, new_name)
+        else:
+            conv_call = '%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s ' \
+                        '-sOutputFile="%s%%d.%s" ' \
+                        '-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \
+                        '-r%f "%s"' \
+                        % (conv, gs_device, latex_file_root, \
+                            gs_ext, alpha, alpha, resolution, pdf_file)
+            conv_status, conv_stdout = run_command(conv_call)
+
+        if conv_status:
             # Give up!
             warning("Some pages failed with all the possible routes")
         else:
@@ -371,17 +390,27 @@ def legacy_conversion_step2(latex_file, dpi, 
output_format, skipMetrics = False)
 
 
 # Either latex and dvips have been run and we have a ps file, or
-# pdflatex has been run and we have a pdf file. Proceed with gs.
+# pdflatex has been run and we have a pdf file. Proceed with pdftocairo or gs.
 def legacy_conversion_step3(latex_file, dpi, output_format, dvips_failed, 
skipMetrics = False):
     # External programs used by the script.
     gs      = find_exe_or_terminate(["gswin32c", "gswin64c", "gs"])
     pnmcrop = find_exe(["pnmcrop"])
+    pdftocairo = find_exe(["pdftocairo"])
+    epstopdf   = find_exe(["epstopdf"])
+    use_pdftocairo = pdftocairo != None and output_format == "png"
+    if use_pdftocairo:
+        conv = pdftocairo
+    else:
+        conv = gs
 
     # Files to process
     pdf_file  = latex_file_re.sub(".pdf", latex_file)
     ps_file  = latex_file_re.sub(".ps",  latex_file)
 
-    # Extract resolution data for gs from the log file.
+    # The latex file name without extension
+    latex_file_root = latex_file_re.sub("", latex_file)
+
+    # Extract resolution data for the converter from the log file.
     log_file = latex_file_re.sub(".log", latex_file)
     resolution = extract_resolution(log_file, dpi)
 
@@ -406,48 +435,76 @@ def legacy_conversion_step3(latex_file, dpi, 
output_format, dvips_failed, skipMe
     # Generate the bitmap images
     if dvips_failed:
         # dvips failed, maybe there's a PDF, try to produce bitmaps
-        gs_call = '%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s ' \
-                  '-sOutputFile="%s%%d.%s" ' \
-                  '-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \
-                  '-r%f "%s"' \
-                  % (gs, gs_device, latex_file_re.sub("", latex_file), \
-                     gs_ext, alpha, alpha, resolution, pdf_file)
-
-        gs_status, gs_stdout = run_command(gs_call)
-        if gs_status:
-            error("Failed: %s %s" % (os.path.basename(gs), ps_file))
+        if use_pdftocairo:
+            conv_call = '%s -png -transp -r %d "%s" "%s"' \
+                        % (pdftocairo, resolution, pdf_file, latex_file_root)
+
+            conv_status, conv_stdout = run_command(conv_call)
+            if not conv_status:
+                seqnum_re = re.compile("-([0-9]+)")
+                for name in glob.glob("%s-*.png" % latex_file_root):
+                    match = seqnum_re.search(name)
+                    if match != None:
+                        new_name = seqnum_re.sub(str(int(match.group(1))), 
name)
+                        os.rename(name, new_name)
+        else:
+            conv_call = '%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s ' \
+                      '-sOutputFile="%s%%d.%s" ' \
+                      '-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \
+                      '-r%f "%s"' \
+                      % (gs, gs_device, latex_file_root, \
+                         gs_ext, alpha, alpha, resolution, pdf_file)
+
+            conv_status, conv_stdout = run_command(conv_call)
+
+        if conv_status:
+            error("Failed: %s %s" % (os.path.basename(conv), pdf_file))
     else:
-        # Model for calling gs on each file
-        gs_call = '%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s ' \
-                  '-sOutputFile="%s%%d.%s" ' \
-                  '-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \
-                  '-r%f "%%s"' \
-                  % (gs, gs_device, latex_file_re.sub("", latex_file), \
-                     gs_ext, alpha, alpha, resolution)
+        # Model for calling the converter on each file
+        if use_pdftocairo and epstopdf != None:
+            conv_call = '%s -png -transp -singlefile -r %d "%%s.pdf" "%s%%d"' \
+                        % (pdftocairo, resolution, latex_file_root)
+        else:
+            conv_call = '%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s ' \
+                        '-sOutputFile="%s%%d.%s" ' \
+                        '-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \
+                        '-r%f "%%s"' \
+                        % (gs, gs_device, latex_file_root, \
+                           gs_ext, alpha, alpha, resolution)
 
         i = 0
         # Collect all the PostScript files (like *.001, *.002, ...)
-        ps_files = glob.glob("%s.[0-9][0-9][0-9]" % latex_file_re.sub("", 
latex_file))
+        ps_files = glob.glob("%s.[0-9][0-9][0-9]" % latex_file_root)
         ps_files.sort()
 
-        # Call GhostScript for each file
+        # Call the converter for each file
         for file in ps_files:
             i = i + 1
             progress("Processing page %s, file %s" % (i, file))
-            gs_status, gs_stdout = run_command(gs_call % (i, file))
-            if gs_status:
-                # gs failed, keep track of this
-                warning("Ghostscript failed on page %s, file %s" % (i, file))
+            if use_pdftocairo and epstopdf != None:
+                conv_name = "PdfToCairo"
+                conv_status, conv_stdout = run_command("%s --outfile=%s.pdf %s"
+                                                       % (epstopdf, file, 
file))
+                if not conv_status:
+                    conv_status, conv_stdout = run_command(conv_call % (file, 
i))
+            else:
+                conv_name = "Ghostscript"
+                conv_status, conv_stdout = run_command(conv_call % (i, file))
+
+            if conv_status:
+                # The converter failed, keep track of this
+                warning("%s failed on page %s, file %s" % (conv_name, i, file))
                 failed_pages.append(i)
 
     # Pass failed pages to pdflatex
     if len(failed_pages) > 0:
-        legacy_conversion_pdflatex(latex_file, failed_pages, legacy_metrics, 
gs,
-            gs_device, gs_ext, alpha, resolution, output_format)
+        legacy_conversion_pdflatex(latex_file, failed_pages, legacy_metrics,
+            use_pdftocairo, conv, gs_device, gs_ext, alpha, resolution,
+            output_format)
 
-    # Crop the images
-    if pnmcrop != None:
-        crop_files(pnmcrop, latex_file_re.sub("", latex_file))
+    # Crop the ppm images
+    if pnmcrop != None and output_format == "ppm":
+        crop_files(pnmcrop, latex_file_root)
 
     # Allow to skip .metrics creation for custom management
     # (see the dvipng method)

Reply via email to