root/latexformulamacro/0.8/formula.py

Revision 130, 8.9 kB (checked in by vgough, 3 years ago)

LatexFormulaMacro:

Enable 'text_mag' configuration option which can be used to change the size of
text rendered in latex formula. 1000 is the default.

Line 
1 """
2 Convert a latex formula into an image.
3 by Valient Gough <vgough@pobox.com>
4
5 Changes:
6     2005-10-03:
7         * make image format selectable via 'image_format' configuration option
8           (defaults to 'jpg')
9         * allow paths to executables to be specified in configuration by
10           setting 'latex_path', 'dvips_path', 'convert_path' to point to
11           executable. Based on code by Reed Cartwright.
12     2005-10-01:
13         * add #display and #fleqn options to add html formatting around image
14           (Christian Marquardt).
15     2005-09-21:
16         * add #center and #indent options to add html formatting around image.
17     2005-08-02:
18         * remove hard-coded paths, read from configuration. Fixes #26
19     2005-07-27:
20         * figured out how to get rid of the annoying internal error after latex
21         was run.  Redirected latex output to /dev/null..
22         * found out that {{{#!figure ... }}} runs wiki macro, and doesn't have
23         the problem of not being able to use paranthesis.  So this is the
24         default usage now.  Can still use [[formula(...)]] for simple formula.
25         * add "nomode" command, which can be used to turn off automatic
26           enclosure of commands in display-math mode ("$$ ... $$")
27     2005-07-26: first release
28
29 Installation:
30     1. Copy into wiki-macros directory.
31     2. Edit conf/trac.ini and add a [latex] group with three values:
32         [latex]
33         # temp_dir points to directory where temporary files are created
34         temp_dir = /var/tmp/trac
35         # image_path is directory where final images are written
36         image_path = /var/www/html/formula
37         # display_path is URL where formula images can be accessed
38         display_path = http://foo.net/formula
39         # Set to 1 for fleqn style equations (default is centered)
40         fleqn = 0
41         # Indentation width for fleqn style equations
42         fleqn_width = '5%'
43
44 Usage:
45
46 {{{
47 #!formula
48 [latex code]
49 }}}
50
51 or, additional keywords can be specified before the latex code:
52 {{{
53 #!formula
54 #density=100
55 [latex code]
56 }}}
57
58 Optional keywords (must be specified before the latex code):
59     #density=100
60         Density defaults to 100.  Larger values produces larger images.
61     #nomode
62         Disable the default display mode setting.  Use this if you want to
63         include things outside of tex's display mode.
64     #display
65         Create a displayed equation (either centered or fleqn style,
66         depending on the fleqn variable in the config file.
67     #center
68         Center the equation on the page.
69     #fleqn
70         fleqn style equation; indentation is controlled by fleqn_witdh in
71         conf/trac.ini.
72     #indent [=class name]
73         places image link in a paragraph <p>...</p>
74         If class name is specified, then it is used to specify a CSS class for
75         the paragraph.
76
77 Notes:
78     A matrix macro is included in the tex code.  This allows you to do things
79     like:
80       \mat{1&2\\3&4}  to get a 2x2 matrix.  The "\\" separates rows, and "&"
81       separates columns.  Any size up to around 25? will work..
82
83 Images are automatically named based on a sha1 hash of the formula, the
84 density, and the script version.  This way the image doesn't have to be
85 regenerated every time it is used, and if anything is changed then a new image
86 is created.
87
88 Note that temporary files can build up in the tmpdir, and every time a formula
89 is modified, a new image will be created in the imagePath directory.  These can
90 be considered as cached files.  You can safely let the tmp file cleaner process
91 remove old files from these directories.
92
93 PS.  This is my first python program, so it is probably pretty ugly by python
94 standards (whatever those may be).  Feedback is welcome, but complaints about
95 ugliness will be redirected to /dev/null.
96 """
97
98 # if the output version string changes, then images will be regenerated
99 outputVersion = "0.1"
100
101
102 import re
103 import string
104 import os
105 import sha
106
107
108 def render(hdf, env, texData, density, fleqnMode, mathMode):
109     # gets paths from configuration
110     tmpdir = env.get_config('latex', 'temp_dir')
111     imagePath = env.get_config('latex', 'image_path')
112     displayPath = env.get_config('latex', 'display_path')
113     fleqnIndent = env.get_config('latex', 'fleqn_indent')
114     latexPath = env.get_config('latex', 'latex_path')
115     dvipsPath = env.get_config('latex', 'dvips_path')
116     convertPath = env.get_config('latex', 'convert_path')
117     texMag = env.get_config('latex', 'text_mag')
118     imageFormat = env.get_config('latex', 'image_format')
119
120     if not tmpdir or not imagePath or not displayPath:
121         return "<b>Error: missing configuration settings in 'latex' macro</b><br>"
122
123     # set defaults
124     if not fleqnIndent:
125         fleqnIndent = '5%'
126     if not latexPath:
127         latexPath = 'latex'
128     if not dvipsPath:
129         dvipsPath = 'dvips'
130     if not convertPath:
131         convertPath = 'convert'
132     if not texMag:
133         texMag = 1000 # I'm told this is latex's default value
134     if not imageFormat:
135         imageFormat = 'jpg'
136
137     path = tmpdir # + hdf.getValue("project.name.encoded", "default")
138     # create temporary directory if necessary
139     try:
140         if not os.path.exists(path):
141             mkdir(path)
142     except:
143         return "Unable to create temporary directory " + path
144    
145     # generate final image name.  Use a hash of the parameters which affect
146     # the image, so we don't have to recreate it unless they change.
147     hash = sha.new(texData)
148     # include some options in the hash, as they affect the output image
149     hash.update( "%d %d" % (density, int(texMag)) )
150     hash.update( outputVersion )
151     name = hash.hexdigest()
152     imageFile = "%s/%s.%s" % (imagePath, name, imageFormat)
153
154     log = "<br>"
155     if not os.path.exists(imageFile):
156         # latex writes out lots of stuff to the current directory, so we have
157         # to run it from there.
158         cwd = os.getcwd()
159         os.chdir(path)
160
161         texFile = name + ".tex"
162         makeTexFile(texFile, texData, mathMode, texMag)
163
164         # the output from latex on stdout seems to cause problems, so sent it
165         # to /dev/null
166         cmd = "%s %s > /dev/null" % (latexPath, texFile)
167         log += execprog( cmd )
168         os.chdir(cwd)
169
170         # use dvips to convert to eps
171         dviFile = "%s/%s.dvi" % (path, name)
172         epsFile = "%s/%s.eps" % (path, name)
173         cmd = "%s -q -D 600 -E -n 1 -p 1 -o %s %s" % (dvipsPath, epsFile, dviFile)
174         log += execprog( cmd )
175
176         # and finally, ImageMagick to convert from eps to [imageFormat] type
177         cmd = "%s -antialias -density %ix%i %s %s" % (convertPath, density, density, epsFile, imageFile)
178         log += execprog( cmd )
179
180     if fleqnMode:
181         margin = " margin-left: %s" % fleqnIndent
182     else:
183         margin = ""
184        
185     html = "<img src='%s/%s.%s' border='0' style='vertical-align: middle;%s' alt='formula' />" % (displayPath, name, imageFormat, margin)
186     return html
187
188 def execprog(cmd):
189     os.system( cmd )
190     return cmd + "<br>"
191
192 def makeTexFile(texFile, texData, mathMode, texMag):
193     tex = "\\batchmode\n"
194     tex += "\\documentclass{article}\n"
195     tex += "\\usepackage{amsmath}\n"
196     tex += "\\usepackage{amssymb}\n"
197     tex += "\\usepackage{epsfig}\n"
198     tex += "\\pagestyle{empty}\n"
199     tex += "\\mag=%s\n" % texMag
200     # matrix macro
201     tex += "\\newcommand{\\mat}[2][rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr]{\n"
202     tex += \\left[\\begin{array}{#1}\n"
203     tex += "  #2\\\\\n"
204     tex += \\end{array}\n"
205     tex += \\right]}\n"
206     # start the document
207     tex += "\\begin{document}\n"
208     if mathMode:
209         tex += "$$\n"
210     tex += "%s\n" % texData
211     if mathMode:
212         tex += "$$\n"
213     tex += "\\pagebreak\n"
214     tex += "\\end{document}\n"
215        
216     FILE = open(texFile, "w")
217     FILE.write( tex )
218     FILE.close()
219
220 # arguments start with "#" on the beginning of a line
221 def execute(hdf, text, env):
222     # TODO: unescape all html escape codes
223     text = text.replace("&amp;", "&")
224    
225     # defaults
226     density = 100
227     mathMode = 1    # default to using display-math mode for LaTeX processing
228     displayMode = 0 # default to generating inline formula
229     fleqnMode   = env.get_config('latex', 'fleqn')
230     centerImage = 0
231     indentImage = 0
232     indentClass = ""
233
234     # find some number of arguments, followed by the formula
235     command = re.compile('^\s*#([^=]+)=?(.*)')
236     formula = ""
237     errors = ""
238     for line in text.split("\n"):
239         m = command.match(line)
240         if m:
241             if m.group(1) == "density":
242                 density = int(m.group(2))
243             elif m.group(1) == "nomode":
244                 mathMode = 0
245             elif m.group(1) == "center":
246                 centerImage = 1
247                 fleqnMode   = 0
248             elif m.group(1) == "indent":
249                 indentImage = 1
250                 indentClass = m.group(2)
251             elif m.group(1) == "display":
252                 displayMode = 1
253             elif m.group(1) == "fleqn":
254                 displayMode = 1
255                 fleqnMode   = 1
256             else:
257                 errors = '<br>Unknown <i>formula</i> command "%s"<br>' % m.group(1)
258         else:
259             formula += line + "\n"
260
261     # Set display and fleqn defaults
262     if displayMode:
263         if fleqnMode:
264             centerImage = 0
265         else:
266             centerImage = 1
267
268     # Render formula
269     format = '%s'
270     if centerImage:
271         format = '<center>%s</center>' % format
272     if indentImage:
273         if indentClass:
274             format = '<p class="%s">%s</p>' % (indentClass, format)
275         else:
276             format = '<p>%s</p>' % format
277    
278     result = errors + render(hdf, env, formula, density, fleqnMode, mathMode)
279     return format % result
Note: See TracBrowser for help on using the browser.