2 # -*- coding: utf-8 -*-
4 # file lyxpreview2bitmap.py
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
9 # with much advice from members of the preview-latex project:
10 # David Kastrup, dak@gnu.org and
11 # Jan-Åke Larsson, jalar@mai.liu.se.
13 # Full author contact details are available in file CREDITS
15 # This script takes a LaTeX file and generates a collection of
16 # png or ppm image files, one per previewed snippet.
19 # * A latex executable;
22 # * pngtoppm (if outputing ppm format images).
24 # preview.sty and dvipng are part of the preview-latex project
25 # http://preview-latex.sourceforge.net/
27 # preview.sty can alternatively be obtained from
28 # CTAN/support/preview-latex/
31 # lyxpreview2bitmap.py png 0lyxpreview.tex 128 000000 faf0e6
33 # This script takes five arguments:
34 # FORMAT: The desired output format. Either 'png' or 'ppm'.
35 # TEXFILE: the name of the .tex file to be converted.
36 # DPI: a scale factor, used to ascertain the resolution of the
37 # generated image which is then passed to gs.
38 # FG_COLOR: the foreground color as a hexadecimal string, eg '000000'.
39 # BG_COLOR: the background color as a hexadecimal string, eg 'faf0e6'.
41 # Decomposing TEXFILE's name as DIR/BASE.tex, this script will,
42 # if executed successfully, leave in DIR:
43 # * a (possibly large) number of image files with names
45 # * a file BASE.metrics, containing info needed by LyX to position
46 # the images correctly on the screen.
48 import glob
, os
, re
, string
, sys
50 from legacy_lyxpreview2ppm
import legacy_conversion
, \
51 legacy_conversion_step2
53 from lyxpreview_tools
import copyfileobj
, error
, find_exe
, \
54 find_exe_or_terminate
, make_texcolor
, mkstemp
, run_command
, warning
57 # Pre-compiled regular expressions.
58 latex_file_re
= re
.compile("\.tex$")
62 return "Usage: %s <format> <latex file> <dpi> <fg color> <bg color>\n"\
63 "\twhere the colors are hexadecimal strings, eg 'faf0e6'"\
67 def extract_metrics_info(dvipng_stdout
, metrics_file
):
68 metrics
= open(metrics_file
, 'w')
69 # "\[[0-9]+" can match two kinds of numbers: page numbers from dvipng
70 # and glyph numbers from mktexpk. The glyph numbers always match
71 # "\[[0-9]+\]" while the page number never is followed by "\]". Thus:
72 page_re
= re
.compile("\[([0-9]+)[^]]");
73 metrics_re
= re
.compile("depth=(-?[0-9]+) height=(-?[0-9]+)")
79 match
= page_re
.search(dvipng_stdout
, pos
)
84 match
= metrics_re
.search(dvipng_stdout
, pos
)
89 # Calculate the 'ascent fraction'.
90 descent
= string
.atof(match
.group(1))
91 ascent
= string
.atof(match
.group(2))
94 if ascent
>= 0 or descent
>= 0:
95 if abs(ascent
+ descent
) > 0.1:
96 frac
= ascent
/ (ascent
+ descent
)
102 metrics
.write("Snippet %s %f\n" % (page
, frac
))
103 pos
= match
.end() + 2
108 def color_pdf(latex_file
, bg_color
):
109 use_preview_pdf_re
= re
.compile("(\s*\\\\usepackage\[[^]]+)(pdftex\]{preview})")
115 for line
in open(latex_file
, 'r').readlines():
116 match
= use_preview_pdf_re
.match(line
)
121 tmp
.write(" \\usepackage{color}\n" \
122 " \\pagecolor[rgb]{%s}\n" \
124 % (bg_color
, match
.group()))
128 # Unable to open the file, but do nothing here because
129 # the calling function will act on the value of 'success'.
130 warning('Warning in color_pdf! Unable to open "%s"' % latex_file
)
131 warning(`sys
.exc_type`
+ ',' + `sys
.exc_value`
)
134 copyfileobj(tmp
, open(latex_file
,"wb"), 1)
139 def convert_to_ppm_format(pngtopnm
, basename
):
140 png_file_re
= re
.compile("\.png$")
142 for png_file
in glob
.glob("%s*.png" % basename
):
143 ppm_file
= png_file_re
.sub(".ppm", png_file
)
145 p2p_cmd
= '%s "%s"' % (pngtopnm
, png_file
)
146 p2p_status
, p2p_stdout
= run_command(p2p_cmd
)
147 if p2p_status
!= None:
148 error("Unable to convert %s to ppm format" % png_file
)
150 ppm
= open(ppm_file
, 'w')
151 ppm
.write(p2p_stdout
)
156 # Parse and manipulate the command line arguments.
158 error(usage(argv
[0]))
160 output_format
= string
.lower(argv
[1])
162 dir, latex_file
= os
.path
.split(argv
[2])
166 dpi
= string
.atoi(argv
[3])
167 fg_color
= make_texcolor(argv
[4], False)
168 bg_color
= make_texcolor(argv
[5], False)
170 bg_color_gr
= make_texcolor(argv
[5], True)
172 # External programs used by the script.
173 path
= string
.split(os
.environ
["PATH"], os
.pathsep
)
174 latex
= find_exe_or_terminate(["latex", "pplatex", "platex", "latex2e"], path
)
176 # This can go once dvipng becomes widespread.
177 dvipng
= find_exe(["dvipng"], path
)
179 # The data is input to legacy_conversion in as similar
180 # as possible a manner to that input to the code used in
182 vec
= [ argv
[0], argv
[2], argv
[3], argv
[1], argv
[4], argv
[5] ]
183 return legacy_conversion(vec
)
186 if output_format
== "ppm":
187 pngtopnm
= find_exe_or_terminate(["pngtopnm"], path
)
189 # Move color information for PDF into the latex file.
190 if not color_pdf(latex_file
, bg_color_gr
):
191 error("Unable to move color info into the latex file")
193 # Compile the latex file.
194 latex_call
= '%s "%s"' % (latex
, latex_file
)
196 latex_status
, latex_stdout
= run_command(latex_call
)
197 if latex_status
!= None:
198 warning("%s failed to compile %s" \
199 % (os
.path
.basename(latex
), latex_file
))
201 # Run the dvi file through dvipng.
202 dvi_file
= latex_file_re
.sub(".dvi", latex_file
)
203 dvipng_call
= '%s -Ttight -depth -height -D %d -fg "%s" -bg "%s" "%s"' \
204 % (dvipng
, dpi
, fg_color
, bg_color
, dvi_file
)
206 dvipng_status
, dvipng_stdout
= run_command(dvipng_call
)
207 if dvipng_status
!= None:
208 warning("%s failed to generate images from %s ... looking for PDF" \
209 % (os
.path
.basename(dvipng
), dvi_file
))
210 return legacy_conversion_step2(latex_file
, dpi
, output_format
)
212 # Extract metrics info from dvipng_stdout.
213 metrics_file
= latex_file_re
.sub(".metrics", latex_file
)
214 if not extract_metrics_info(dvipng_stdout
, metrics_file
):
215 error("Failed to extract metrics info from dvipng")
217 # Convert images to ppm format if necessary.
218 if output_format
== "ppm":
219 convert_to_ppm_format(pngtopnm
, latex_file_re
.sub("", latex_file
))
224 if __name__
== "__main__":