Use pdftocairo (if available) for instant preview.

If pdftocairo is detected, it is used for generating bitmaps in the
legacy method route in preference to ghostscript. This is justified
by the fact that the conversion is 4 to 5 times faster and the
quality of the generated images is superior.
This commit is contained in:
Enrico Forestieri 2015-04-27 21:37:26 +02:00
parent 691fdea346
commit c9b553fd2f

View File

@ -42,6 +42,8 @@
# * gs; # * gs;
# * pdflatex (optional); # * pdflatex (optional);
# * pnmcrop (optional). # * pnmcrop (optional).
# * pdftocairo (optional).
# * epstopdf (optional).
# preview.sty is part of the preview-latex project # preview.sty is part of the preview-latex project
# http://preview-latex.sourceforge.net/ # http://preview-latex.sourceforge.net/
@ -56,11 +58,11 @@
# [legacy_conversion_step2] # [legacy_conversion_step2]
# 2) Call dvips to create one PS file for each DVI page # 2) Call dvips to create one PS file for each DVI page
# [legacy_conversion_step3] # [legacy_conversion_step3]
# 3) If dvips fails look for PDF and call gs to produce bitmaps # 3) If dvips fails look for PDF and call pdftocairo or gs to produce bitmaps
# 4) Otherwise call gs on each PostScript file to produce bitmaps # 4) Otherwise call pdftocairo or gs on each PostScript file to produce bitmaps
# [legacy_conversion_pdflatex] # [legacy_conversion_pdflatex]
# 5) Keep track of pages on which gs failed and pass them to 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) # 7) Extract and write to file (or return to lyxpreview2bitmap)
# metrics from both methods (standard and pdflatex) # metrics from both methods (standard and pdflatex)
@ -74,6 +76,8 @@
# is required in certain cases, if hyperref is active for instance, # is required in certain cases, if hyperref is active for instance,
# (step 5, 6). # (step 5, 6).
# If possible, dvipng should be used, as it's much faster. # 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 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} \definecolor{bg}{rgb}{%s}
\pagecolor{bg} \pagecolor{bg}
\usepackage[%s,tightpage]{preview} \usepackage[%s,tightpage]{preview}
\IfFileExists{lmodern.sty}{\usepackage{lmodern}}{\usepackage{ae,aecomp}}
\makeatletter \makeatletter
\g@addto@macro\preview{\begingroup\color{bg}\special{ps::clippath fill}\color{fg}} \g@addto@macro\preview{\begingroup\color{bg}\special{ps::clippath fill}\color{fg}}
\g@addto@macro\endpreview{\endgroup} \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 # Creates a new LaTeX file from the original with pages specified in
# failed_pages, pass it through pdflatex and updates the metrics # failed_pages, pass it through pdflatex and updates the metrics
# from the standard legacy route # from the standard legacy route
def legacy_conversion_pdflatex(latex_file, failed_pages, legacy_metrics, gs, def legacy_conversion_pdflatex(latex_file, failed_pages, legacy_metrics,
gs_device, gs_ext, alpha, resolution, output_format): use_pdftocairo, conv, gs_device, gs_ext, alpha, resolution, output_format):
# Search for pdflatex executable # Search for pdflatex executable
pdflatex = find_exe(["pdflatex"]) 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) pdflatex_status, pdflatex_stdout = run_latex(pdflatex, pdf_latex_file)
pdf_file = latex_file_re.sub(".pdf", pdf_latex_file) pdf_file = latex_file_re.sub(".pdf", pdf_latex_file)
latex_file_root = latex_file_re.sub("", pdf_latex_file)
# GhostScript call to produce bitmaps # Converter call to produce bitmaps
gs_call = '%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s ' \ 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" ' \ '-sOutputFile="%s%%d.%s" ' \
'-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \ '-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \
'-r%f "%s"' \ '-r%f "%s"' \
% (gs, gs_device, latex_file_re.sub("", pdf_latex_file), \ % (conv, gs_device, latex_file_root, \
gs_ext, alpha, alpha, resolution, pdf_file) gs_ext, alpha, alpha, resolution, pdf_file)
gs_status, gs_stdout = run_command(gs_call) conv_status, conv_stdout = run_command(conv_call)
if gs_status:
if conv_status:
# Give up! # Give up!
warning("Some pages failed with all the possible routes") warning("Some pages failed with all the possible routes")
else: 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 # 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): def legacy_conversion_step3(latex_file, dpi, output_format, dvips_failed, skipMetrics = False):
# External programs used by the script. # External programs used by the script.
gs = find_exe_or_terminate(["gswin32c", "gswin64c", "gs"]) gs = find_exe_or_terminate(["gswin32c", "gswin64c", "gs"])
pnmcrop = find_exe(["pnmcrop"]) 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 # Files to process
pdf_file = latex_file_re.sub(".pdf", latex_file) pdf_file = latex_file_re.sub(".pdf", latex_file)
ps_file = latex_file_re.sub(".ps", 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) log_file = latex_file_re.sub(".log", latex_file)
resolution = extract_resolution(log_file, dpi) 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 # Generate the bitmap images
if dvips_failed: if dvips_failed:
# dvips failed, maybe there's a PDF, try to produce bitmaps # dvips failed, maybe there's a PDF, try to produce bitmaps
gs_call = '%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s ' \ 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" ' \ '-sOutputFile="%s%%d.%s" ' \
'-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \ '-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \
'-r%f "%s"' \ '-r%f "%s"' \
% (gs, gs_device, latex_file_re.sub("", latex_file), \ % (gs, gs_device, latex_file_root, \
gs_ext, alpha, alpha, resolution, pdf_file) gs_ext, alpha, alpha, resolution, pdf_file)
gs_status, gs_stdout = run_command(gs_call) conv_status, conv_stdout = run_command(conv_call)
if gs_status:
error("Failed: %s %s" % (os.path.basename(gs), ps_file)) if conv_status:
error("Failed: %s %s" % (os.path.basename(conv), pdf_file))
else: else:
# Model for calling gs on each file # Model for calling the converter on each file
gs_call = '%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s ' \ 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" ' \ '-sOutputFile="%s%%d.%s" ' \
'-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \ '-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \
'-r%f "%%s"' \ '-r%f "%%s"' \
% (gs, gs_device, latex_file_re.sub("", latex_file), \ % (gs, gs_device, latex_file_root, \
gs_ext, alpha, alpha, resolution) gs_ext, alpha, alpha, resolution)
i = 0 i = 0
# Collect all the PostScript files (like *.001, *.002, ...) # 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() ps_files.sort()
# Call GhostScript for each file # Call the converter for each file
for file in ps_files: for file in ps_files:
i = i + 1 i = i + 1
progress("Processing page %s, file %s" % (i, file)) progress("Processing page %s, file %s" % (i, file))
gs_status, gs_stdout = run_command(gs_call % (i, file)) if use_pdftocairo and epstopdf != None:
if gs_status: conv_name = "PdfToCairo"
# gs failed, keep track of this conv_status, conv_stdout = run_command("%s --outfile=%s.pdf %s"
warning("Ghostscript failed on page %s, file %s" % (i, file)) % (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) failed_pages.append(i)
# Pass failed pages to pdflatex # Pass failed pages to pdflatex
if len(failed_pages) > 0: if len(failed_pages) > 0:
legacy_conversion_pdflatex(latex_file, failed_pages, legacy_metrics, gs, legacy_conversion_pdflatex(latex_file, failed_pages, legacy_metrics,
gs_device, gs_ext, alpha, resolution, output_format) use_pdftocairo, conv, gs_device, gs_ext, alpha, resolution,
output_format)
# Crop the images # Crop the ppm images
if pnmcrop != None: if pnmcrop != None and output_format == "ppm":
crop_files(pnmcrop, latex_file_re.sub("", latex_file)) crop_files(pnmcrop, latex_file_root)
# Allow to skip .metrics creation for custom management # Allow to skip .metrics creation for custom management
# (see the dvipng method) # (see the dvipng method)