2 # -*- coding: utf-8 -*-
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.
10 # Full author contact details are available in file CREDITS
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.
20 import os, re, string, sys, subprocess, tempfile, shutil
24 return ("Usage: %s lyxexe outputpath\n" % prog_name)
28 sys.stderr.write(message + '\n')
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. """
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
44 skipinsets = ['big', 'font', 'lyxblacktext', 'matrix', 'mbox', 'oldfont', \
45 'ref', 'split', 'space', 'style']
48 for line in stderr.split('\n'):
49 m = regexp.match(line)
52 if not inset in skipinsets:
53 if m.group(4) == 'textmode':
54 textsymbols.append(m.group(1))
56 mathsymbols.append(m.group(1))
57 return (mathsymbols, textsymbols)
60 def getreplacements(filename):
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*"([^"]+)"')
71 for line in cppfile.readlines():
73 m = regexp.match(line)
75 replacements[m.group(1)] = m.group(2)
78 elif line.find('PngMap sorted_png_map') == 0:
82 def gettoolbaritems(filename):
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)
89 if '\\' + m.group(1) == m.group(3):
90 items.append(m.group(1))
94 def getmakefileentries(filename):
96 makefile = open(filename, 'rt')
97 regexp = re.compile(r'.*images/math/(.+)\.png')
98 for line in makefile.readlines():
99 m = regexp.match(line)
101 items.append(m.group(1))
105 def createimage(name, path, template, lyxexe, tempdir, math, replacements, toolbaritems, makefileentries):
106 """ Create the image file for symbol name in path. """
108 if name in replacements.keys():
109 filename = replacements[name]
110 elif name.startswith('lyx'):
111 print 'Skipping ' + name
114 skipchars = ['|', '/', '\\', '*', '!', '?', ':', ';', '^', '<', '>']
116 if name.find(i) >= 0:
117 print 'Skipping ' + 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)'
125 suffix = ' (found in only in toolbar)'
127 if filename in makefileentries:
128 suffix = ' (found only in makefile)'
130 suffix = ' (not found)'
131 if os.path.exists(pngname):
132 print 'Skipping ' + name + suffix
134 print 'Generating ' + name + suffix
135 lyxname = os.path.join(tempdir, filename)
136 lyxfile = open(lyxname + '.lyx', 'wt')
138 lyxfile.write(template.replace('$a$', '$\\' + name + '$'))
140 lyxfile.write(template.replace('$a$', '$\\text{\\' + name + '}$'))
142 cmd = "%s %s.lyx -e dvi" % (lyxexe, lyxname)
143 proc = subprocess.Popen(cmd, shell=True)
145 if proc.returncode != 0:
146 print 'Error in DVI creation for ' + name
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)
154 if proc.returncode != 0:
155 print 'Error in PNG creation for ' + name
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))
164 padded = Image.new('RGBA', (width+1, height+2), (0, 0, 0, 0))
165 padded.paste(image, (0, 1))
167 padded = Image.new('RGBA', (width+2, height+1), (0, 0, 0, 0))
168 padded.paste(image, (1, 0))
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")
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()
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)
198 error(usage(argv[0]))
203 if __name__ == "__main__":