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