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