2 # -*- coding: utf-8 -*-
4 # file legacy_lyxpreview2ppm.py
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
9 # Full author contact details are available in file CREDITS
11 # with much advice from members of the preview-latex project:
12 # David Kastrup, dak@gnu.org and
13 # Jan-Åke Larsson, jalar@mai.liu.se.
14 # and with much help testing the code under Windows from
15 # Paul A. Rubin, rubin@msu.edu.
17 # This script takes a LaTeX file and generates a collection of
18 # ppm image files, one per previewed snippet.
20 # legacy_lyxpreview2bitmap.py 0lyxpreview.tex 128 ppm 000000 faf0e6
22 # This script takes five arguments:
23 # TEXFILE: the name of the .tex file to be converted.
24 # SCALEFACTOR: a scale factor, used to ascertain the resolution of the
25 # generated image which is then passed to gs.
26 # OUTPUTFORMAT: the format of the output bitmap image files.
27 # This particular script can produce only "ppm" format output.
28 # FG_COLOR: the foreground color as a hexadecimal string, eg '000000'.
29 # BG_COLOR: the background color as a hexadecimal string, eg 'faf0e6'.
31 # Decomposing TEXFILE's name as DIR/BASE.tex, this script will,
32 # if executed successfully, leave in DIR:
33 # * a (possibly large) number of image files with names
35 # * a file BASE.metrics, containing info needed by LyX to position
36 # the images correctly on the screen.
38 # The script uses several external programs and files:
39 # * A latex executable;
43 # * pnmcrop (optional).
45 # preview.sty is part of the preview-latex project
46 # http://preview-latex.sourceforge.net/
47 # Alternatively, it can be obtained from
48 # CTAN/support/preview-latex/
50 # The script uses the deprecated dvi->ps->ppm conversion route.
51 # If possible, please grab 'dvipng'; it's faster and more robust.
52 # If you have it then this script will not be invoked by
53 # lyxpreview2bitmap.py.
54 # Warning: this legacy support will be removed one day...
56 import glob
, os
, pipes
, re
, string
, sys
58 from lyxpreview_tools
import copyfileobj
, error
, find_exe
, \
59 find_exe_or_terminate
, make_texcolor
, mkstemp
, run_command
, warning
61 # Pre-compiled regular expression.
62 latex_file_re
= re
.compile("\.tex$")
66 return "Usage: %s <latex file> <dpi> ppm <fg color> <bg color>\n"\
67 "\twhere the colors are hexadecimal strings, eg 'faf0e6'"\
71 def extract_metrics_info(log_file
, metrics_file
):
72 metrics
= open(metrics_file
, 'w')
74 log_re
= re
.compile("Preview: ([ST])")
75 data_re
= re
.compile("(-?[0-9]+) (-?[0-9]+) (-?[0-9]+) (-?[0-9]+)")
82 for line
in open(log_file
, 'r').readlines():
83 match
= log_re
.match(line
)
87 snippet
= (match
.group(1) == 'S')
89 match
= data_re
.search(line
)
91 error("Unexpected data in %s\n%s" % (log_file
, line
))
94 ascent
= string
.atoi(match
.group(2))
95 descent
= string
.atoi(match
.group(3))
98 if ascent
>= 0 and descent
>= 0:
99 ascent
= float(ascent
) + tp_ascent
100 descent
= float(descent
) - tp_descent
102 if abs(ascent
+ descent
) > 0.1:
103 frac
= ascent
/ (ascent
+ descent
)
106 if frac
< 0 or frac
> 1:
109 metrics
.write("Snippet %s %f\n" % (match
.group(1), frac
))
112 tp_descent
= string
.atof(match
.group(2))
113 tp_ascent
= string
.atof(match
.group(4))
116 # Unable to open the file, but do nothing here because
117 # the calling function will act on the value of 'success'.
118 warning('Warning in extract_metrics_info! Unable to open "%s"' % log_file
)
119 warning(`sys
.exc_type`
+ ',' + `sys
.exc_value`
)
124 def extract_resolution(log_file
, dpi
):
125 fontsize_re
= re
.compile("Preview: Fontsize")
126 magnification_re
= re
.compile("Preview: Magnification")
127 extract_decimal_re
= re
.compile("([0-9\.]+)")
128 extract_integer_re
= re
.compile("([0-9]+)")
131 found_magnification
= 0
134 magnification
= 1000.0
138 for line
in open(log_file
, 'r').readlines():
139 if found_fontsize
and found_magnification
:
142 if not found_fontsize
:
143 match
= fontsize_re
.match(line
)
145 match
= extract_decimal_re
.search(line
)
147 error("Unable to parse: %s" % line
)
148 fontsize
= string
.atof(match
.group(1))
152 if not found_magnification
:
153 match
= magnification_re
.match(line
)
155 match
= extract_integer_re
.search(line
)
157 error("Unable to parse: %s" % line
)
158 magnification
= string
.atof(match
.group(1))
159 found_magnification
= 1
163 warning('Warning in extract_resolution! Unable to open "%s"' % log_file
)
164 warning(`sys
.exc_type`
+ ',' + `sys
.exc_value`
)
166 # This is safe because both fontsize and magnification have
167 # non-zero default values.
168 return dpi
* (10.0 / fontsize
) * (1000.0 / magnification
)
171 def legacy_latex_file(latex_file
, fg_color
, bg_color
, bg_color_gr
):
172 use_preview_dvi_re
= re
.compile("(\s*\\\\usepackage\[[^]]+)(dvips\]{preview})")
173 use_preview_pdf_re
= re
.compile("(\s*\\\\usepackage\[[^]]+)(pdftex\]{preview})")
179 for line
in open(latex_file
, 'r').readlines():
180 match
= use_preview_dvi_re
.match(line
)
182 match
= use_preview_pdf_re
.match(line
)
187 tmp
.write(" \\usepackage{color}\n" \
188 " \\pagecolor[rgb]{%s}\n" \
190 % (bg_color_gr
, match
.group()))
194 tmp
.write("%stightpage,%s\n" \
195 " \\AtBeginDocument{\\AtBeginDvi{%%\n" \
196 " \\special{!userdict begin/bop-hook{//bop-hook exec\n" \
197 " <%s%s>{255 div}forall setrgbcolor\n" \
198 " clippath fill setrgbcolor}bind def end}}}\n" \
199 % (match
.group(1), match
.group(2), fg_color
, bg_color
))
202 # Unable to open the file, but do nothing here because
203 # the calling function will act on the value of 'success'.
204 warning('Warning in legacy_latex_file! Unable to open "%s"' % latex_file
)
205 warning(`sys
.exc_type`
+ ',' + `sys
.exc_value`
)
208 copyfileobj(tmp
, open(latex_file
,"wb"), 1)
213 def crop_files(pnmcrop
, basename
):
215 t
.append('%s -left' % pnmcrop
, '--')
216 t
.append('%s -right' % pnmcrop
, '--')
218 for file in glob
.glob("%s*.ppm" % basename
):
220 new
= t
.open(file, "r")
221 copyfileobj(new
, tmp
)
223 copyfileobj(tmp
, open(file,"wb"), 1)
226 def legacy_conversion(argv
):
227 latex_commands
= ["latex", "pplatex", "platex", "latex2e"]
228 # Parse and manipulate the command line arguments.
230 latex_commands
= [argv
[6]]
232 error(usage(argv
[0]))
234 dir, latex_file
= os
.path
.split(argv
[1])
238 dpi
= string
.atoi(argv
[2])
240 output_format
= argv
[3]
244 bg_color_gr
= make_texcolor(argv
[5], True)
246 # External programs used by the script.
247 path
= string
.split(os
.environ
["PATH"], os
.pathsep
)
248 latex
= find_exe_or_terminate(latex_commands
, path
)
250 # Move color information into the latex file.
251 if not legacy_latex_file(latex_file
, fg_color
, bg_color
, bg_color_gr
):
252 error("Unable to move color info into the latex file")
254 # Compile the latex file.
255 latex_call
= '%s "%s"' % (latex
, latex_file
)
257 latex_status
, latex_stdout
= run_command(latex_call
)
258 if latex_status
!= None:
259 error("%s failed to compile %s" \
260 % (os
.path
.basename(latex
), latex_file
))
262 return legacy_conversion_step2(latex_file
, dpi
, output_format
)
265 def legacy_conversion_step2(latex_file
, dpi
, output_format
):
266 # External programs used by the script.
267 path
= string
.split(os
.environ
["PATH"], os
.pathsep
)
268 dvips
= find_exe_or_terminate(["dvips"], path
)
269 gs
= find_exe_or_terminate(["gswin32c", "gs"], path
)
270 pnmcrop
= find_exe(["pnmcrop"], path
)
272 # Run the dvi file through dvips.
273 dvi_file
= latex_file_re
.sub(".dvi", latex_file
)
274 ps_file
= latex_file_re
.sub(".ps", latex_file
)
275 pdf_file
= latex_file_re
.sub(".pdf", latex_file
)
277 dvips_call
= '%s -o "%s" "%s"' % (dvips
, ps_file
, dvi_file
)
280 dvips_status
, dvips_stdout
= run_command(dvips_call
)
281 if dvips_status
!= None:
282 warning('Failed: %s %s ... looking for PDF' \
283 % (os
.path
.basename(dvips
), dvi_file
))
286 # Extract resolution data for gs from the log file.
287 log_file
= latex_file_re
.sub(".log", latex_file
)
288 resolution
= extract_resolution(log_file
, dpi
)
290 # Older versions of gs have problems with a large degree of
291 # anti-aliasing at high resolutions
298 if output_format
== "ppm":
302 # Generate the bitmap images
303 gs_call
= '%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s ' \
304 '-sOutputFile="%s%%d.%s" ' \
305 '-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \
307 % (gs
, gs_device
, latex_file_re
.sub("", latex_file
), \
308 gs_ext
, alpha
, alpha
, resolution
, ps_file
)
311 gs_call
= '%s -dNOPAUSE -dBATCH -dSAFER -sDEVICE=%s ' \
312 '-sOutputFile="%s%%d.%s" ' \
313 '-dGraphicsAlphaBit=%d -dTextAlphaBits=%d ' \
315 % (gs
, gs_device
, latex_file_re
.sub("", latex_file
), \
316 gs_ext
, alpha
, alpha
, resolution
, pdf_file
)
318 gs_status
, gs_stdout
= run_command(gs_call
)
319 if gs_status
!= None:
320 error("Failed: %s %s" % (os
.path
.basename(gs
), ps_file
))
324 crop_files(pnmcrop
, latex_file_re
.sub("", latex_file
))
326 # Extract metrics info from the log file.
327 metrics_file
= latex_file_re
.sub(".metrics", latex_file
)
328 if not extract_metrics_info(log_file
, metrics_file
):
329 error("Failed to extract metrics info from %s" % log_file
)
334 if __name__
== "__main__":
335 legacy_conversion(sys
.argv
)