]> git.lyx.org Git - lyx.git/blob - development/tools/generate_symbols_images.py
Make sty file parser fit for mdsymbol.sty
[lyx.git] / development / tools / generate_symbols_images.py
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 # file generate_symbols_images.py
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
7
8 # author Georg Baum
9
10 # Full author contact details are available in file CREDITS
11
12 # This script generates a toolbar image for each missing math symbol
13 # It needs the template document generate_symbols_images.lyx, which must
14 # contain the placeholder formula '$a$' for generating the png image via
15 # preview.sty and dvipng.
16 # The created images are not always optimal, therefore the existing manually
17 # created images should never be replaced by automatically created ones.
18
19
20 import os, re, string, sys, subprocess, tempfile, shutil
21 import Image
22
23 def usage(prog_name):
24     return ("Usage: %s lyxexe outputpath\n" % prog_name)
25
26
27 def error(message):
28     sys.stderr.write(message + '\n')
29     sys.exit(1)
30
31
32 def getlist(lyxexe, lyxfile):
33     """ Call LyX and get a list of symbols from mathed debug output.
34         This way, we can re-use the symbols file parser of LyX, and do not
35         need to reimplement it in python. """
36
37     # The debug is only generated if lyxfile contains a formula
38     cmd = "%s %s -dbg mathed -x lyx-quit" % (lyxexe, lyxfile)
39     proc = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE)
40     (stdout, stderr) = proc.communicate()
41     regexp = re.compile(r'.*: read symbol \'(\S+)\s+inset:\s+(\S+)\s+draw:\s+(\S*)\s+extra:\s+(\S+)')
42     # These insets are more complex than simply symbols, so the images need to
43     # be created manually
44     skipinsets = ['big', 'font', 'lyxblacktext', 'matrix', 'mbox', 'oldfont', \
45                   'ref', 'split', 'space', 'style']
46     mathsymbols = []
47     textsymbols = []
48     for line in stderr.split('\n'):
49         m = regexp.match(line)
50         if m:
51             inset = m.group(2)
52             if not inset in skipinsets:
53                 if m.group(4) == 'textmode':
54                     textsymbols.append(m.group(1))
55                 else:
56                     mathsymbols.append(m.group(1))
57     return (mathsymbols, textsymbols)
58
59
60 def getreplacements(filename):
61     replacements = {}
62     replacements['|'] = 'vert'
63     replacements['/'] = 'slash'
64     replacements['\\'] = 'backslash'
65     replacements['*'] = 'ast'
66     replacements['AA'] = 'textrm_AA'
67     replacements['O'] = 'textrm_O'
68     cppfile = open(filename, 'rt')
69     regexp = re.compile(r'.*"([^"]+)",\s*"([^"]+)"')
70     found = False
71     for line in cppfile.readlines():
72         if found:
73             m = regexp.match(line)
74             if m:
75                 replacements[m.group(1)] = m.group(2)
76             else:
77                 return replacements
78         elif line.find('PngMap sorted_png_map') == 0:
79             found = True
80
81
82 def gettoolbaritems(filename):
83     items = []
84     uifile = open(filename, 'rt')
85     regexp = re.compile(r'.*Item "([^"\[]+)(\[\[[^\]]+\]\])?"\s*"math-insert\s+([^"]+)"')
86     for line in uifile.readlines():
87         m = regexp.match(line)
88         if m:
89             if '\\' + m.group(1) == m.group(3):
90                 items.append(m.group(1))
91     return items
92
93
94 def getmakefileentries(filename):
95     items = []
96     makefile = open(filename, 'rt')
97     regexp = re.compile(r'.*images/math/(.+)\.png')
98     for line in makefile.readlines():
99         m = regexp.match(line)
100         if m:
101             items.append(m.group(1))
102     return items
103
104
105 def createimage(name, path, template, lyxexe, tempdir, math, replacements, toolbaritems, makefileentries):
106     """ Create the image file for symbol name in path. """
107
108     if name in replacements.keys():
109         filename = replacements[name]
110     elif name.startswith('lyx'):
111         print 'Skipping ' + name
112         return
113     else:
114         skipchars = ['|', '/', '\\', '*', '!', '?', ':', ';', '^', '<', '>']
115         for i in skipchars:
116             if name.find(i) >= 0:
117                 print 'Skipping ' + name
118                 return
119         filename = name
120     pngname = os.path.join(path, filename + '.png')
121     if name in toolbaritems:
122         if filename in makefileentries:
123             suffix = ' (found in toolbar and makefile)'
124         else:
125             suffix = ' (found in only in toolbar)'
126     else:
127         if filename in makefileentries:
128             suffix = ' (found only in makefile)'
129         else:
130             suffix = ' (not found)'
131     if os.path.exists(pngname):
132         print 'Skipping ' + name + suffix
133         return
134     print 'Generating ' + name + suffix
135     lyxname = os.path.join(tempdir, filename)
136     lyxfile = open(lyxname + '.lyx', 'wt')
137     if math:
138         lyxfile.write(template.replace('$a$', '$\\' + name + '$'))
139     else:
140         lyxfile.write(template.replace('$a$', '$\\text{\\' + name + '}$'))
141     lyxfile.close()
142     cmd = "%s %s.lyx -e dvi" % (lyxexe, lyxname)
143     proc = subprocess.Popen(cmd, shell=True)
144     proc.wait()
145     if proc.returncode != 0:
146         print 'Error in DVI creation for ' + name
147         return
148     # The magnifaction factor is calculated such that we get an image of
149     # height 18 px for most symbols and document font size 11. Then we can
150     # add a small border to get the standard math image height of 20 px.
151     cmd = "dvipng %s.dvi -bg Transparent -D 115 -o %s" % (lyxname, pngname)
152     proc = subprocess.Popen(cmd, shell=True)
153     proc.wait()
154     if proc.returncode != 0:
155         print 'Error in PNG creation for ' + name
156         return
157     image = Image.open(pngname)
158     (width, height) = image.size
159     if width < 20 and height < 20:
160         if width == 19 and height == 19:
161             padded = Image.new('RGBA', (width+1, height+1), (0, 0, 0, 0))
162             padded.paste(image, (0, 0))
163         elif width == 19:
164             padded = Image.new('RGBA', (width+1, height+2), (0, 0, 0, 0))
165             padded.paste(image, (0, 1))
166         elif height == 19:
167             padded = Image.new('RGBA', (width+2, height+1), (0, 0, 0, 0))
168             padded.paste(image, (1, 0))
169         else:
170             padded = Image.new('RGBA', (width+2, height+2), (0, 0, 0, 0))
171             padded.paste(image, (1, 1))
172         padded.convert(image.mode)
173         padded.save(pngname, "PNG")
174
175
176 def main(argv):
177
178     if len(argv) == 3:
179         (base, ext) = os.path.splitext(argv[0])
180         (mathsymbols, textsymbols) = getlist(argv[1], base)
181         cppfile = os.path.join(os.path.dirname(base), '../../src/frontends/qt4/GuiApplication.cpp')
182         replacements = getreplacements(cppfile)
183         uifile = os.path.join(os.path.dirname(base), '../../lib/ui/stdtoolbars.inc')
184         toolbaritems = gettoolbaritems(uifile)
185         makefile = os.path.join(os.path.dirname(base), '../../lib/Makefile.am')
186         makefileentries = getmakefileentries(makefile)
187         lyxtemplate = base + '.lyx'
188         templatefile = open(base + '.lyx', 'rt')
189         template = templatefile.read()
190         templatefile.close()
191         tempdir = tempfile.mkdtemp()
192         for i in mathsymbols:
193             createimage(i, argv[2], template, argv[1], tempdir, True, replacements, toolbaritems, makefileentries)
194         for i in textsymbols:
195             createimage(i, argv[2], template, argv[1], tempdir, False, replacements, toolbaritems, makefileentries)
196         shutil.rmtree(tempdir)
197     else:
198         error(usage(argv[0]))
199
200     return 0
201
202
203 if __name__ == "__main__":
204     main(sys.argv)