lyx_mirror/development/tools/generate_symbols_svg.py
Juergen Spitzmueller 6708c011d7 Add a script to automatically generate SVG math images from a LyX template.
This is an adaptation of Georg's generate_symbols_images script, but it uses dvisvgm instead of dvipng.
2014-10-15 16:14:05 +02:00

189 lines
6.5 KiB
Python
Executable File

#! /usr/bin/env python
# -*- coding: utf-8 -*-
# file generate_symbols_svg.py
# This file is part of LyX, the document processor.
# Licence details can be found in the file COPYING.
# author Georg Baum
# author Juergen Spitzmueller (adaptation for SVG)
# Full author contact details are available in file CREDITS
# This script generates a toolbar image for each missing math symbol
# It needs the template document generate_symbols_svg.lyx, which must
# contain the placeholder formula '$a$' for generating the SVG image via
# dvisvgm.
# The created images are not always optimal, therefore the existing manually
# created images should never be replaced by automatically created ones.
import os, re, string, sys, subprocess, tempfile, shutil
import Image
def usage(prog_name):
return ("Usage: %s lyxexe outputpath\n" % prog_name)
def error(message):
sys.stderr.write(message + '\n')
sys.exit(1)
def getlist(lyxexe, lyxfile):
""" Call LyX and get a list of symbols from mathed debug output.
This way, we can re-use the symbols file parser of LyX, and do not
need to reimplement it in python. """
# The debug is only generated if lyxfile contains a formula
cmd = "%s %s -dbg mathed -x lyx-quit" % (lyxexe, lyxfile)
proc = subprocess.Popen(cmd, shell=True, stderr=subprocess.PIPE)
(stdout, stderr) = proc.communicate()
regexp = re.compile(r'.*: read symbol \'(\S+)\s+inset:\s+(\S+)\s+draw:\s+(\S*)\s+extra:\s+(\S+)')
# These insets are more complex than simply symbols, so the images need to
# be created manually
skipinsets = ['big', 'font', 'lyxblacktext', 'matrix', 'mbox', 'oldfont', \
'ref', 'split', 'space', 'style']
mathsymbols = []
textsymbols = []
for line in stderr.split('\n'):
m = regexp.match(line)
if m:
inset = m.group(2)
if not inset in skipinsets:
if m.group(4) == 'textmode':
textsymbols.append(m.group(1))
else:
mathsymbols.append(m.group(1))
return (mathsymbols, textsymbols)
def getreplacements(filename):
replacements = {}
replacements['|'] = 'vert'
replacements['/'] = 'slash'
replacements['\\'] = 'backslash'
replacements['*'] = 'ast'
replacements['AA'] = 'textrm_AA'
replacements['O'] = 'textrm_O'
cppfile = open(filename, 'rt')
regexp = re.compile(r'.*"([^"]+)",\s*"([^"]+)"')
found = False
for line in cppfile.readlines():
if found:
m = regexp.match(line)
if m:
replacements[m.group(1)] = m.group(2)
else:
return replacements
elif line.find('PngMap sorted_png_map') == 0:
found = True
def gettoolbaritems(filename):
items = []
uifile = open(filename, 'rt')
regexp = re.compile(r'.*Item "([^"\[]+)(\[\[[^\]]+\]\])?"\s*"math-insert\s+([^"]+)"')
for line in uifile.readlines():
m = regexp.match(line)
if m:
if '\\' + m.group(1) == m.group(3):
items.append(m.group(1))
return items
def getmakefileentries(filename):
items = []
makefile = open(filename, 'rt')
regexp = re.compile(r'.*images/math/(.+)\.png')
for line in makefile.readlines():
m = regexp.match(line)
if m:
items.append(m.group(1))
return items
def createimage(name, path, template, lyxexe, tempdir, math, replacements, toolbaritems, makefileentries):
""" Create the image file for symbol name in path. """
if name in replacements.keys():
filename = replacements[name]
elif name.startswith('lyx'):
print 'Skipping ' + name
return
else:
skipchars = ['|', '/', '\\', '*', '!', '?', ':', ';', '^', '<', '>']
for i in skipchars:
if name.find(i) >= 0:
print 'Skipping ' + name
return
filename = name
svgname = os.path.join(path, filename + '.svgz')
if name in toolbaritems:
if filename in makefileentries:
suffix = ' (found in toolbar and makefile)'
else:
suffix = ' (found in only in toolbar)'
else:
if filename in makefileentries:
suffix = ' (found only in makefile)'
else:
suffix = ' (not found)'
if os.path.exists(svgname):
print 'Skipping ' + name + suffix
return
print 'Generating ' + name + suffix
lyxname = os.path.join(tempdir, filename)
lyxfile = open(lyxname + '.lyx', 'wt')
if math:
lyxfile.write(template.replace('$a$', '$\\' + name + '$'))
else:
lyxfile.write(template.replace('$a$', '$\\text{\\' + name + '}$'))
lyxfile.close()
cmd = "%s %s.lyx -e dvi" % (lyxexe, lyxname)
proc = subprocess.Popen(cmd, shell=True)
proc.wait()
if proc.returncode != 0:
print 'Error in DVI creation for ' + name
return
# The magnifaction factor is calculated such that we get an image of
# height 18 px for most symbols and document font size 11. Then we can
# add a small border to get the standard math image height of 20 px.
cmd = "dvisvgm -z --no-fonts --exact --output=%%f %s.dvi %s" % (lyxname, svgname)
proc = subprocess.Popen(cmd, shell=True)
proc.wait()
if proc.returncode != 0:
print 'Error in SVG creation for ' + name
return
def main(argv):
if len(argv) == 3:
(base, ext) = os.path.splitext(argv[0])
(mathsymbols, textsymbols) = getlist(argv[1], base)
cppfile = os.path.join(os.path.dirname(base), '../../src/frontends/qt4/GuiApplication.cpp')
replacements = getreplacements(cppfile)
uifile = os.path.join(os.path.dirname(base), '../../lib/ui/stdtoolbars.inc')
toolbaritems = gettoolbaritems(uifile)
makefile = os.path.join(os.path.dirname(base), '../../lib/Makefile.am')
makefileentries = getmakefileentries(makefile)
lyxtemplate = base + '.lyx'
templatefile = open(base + '.lyx', 'rt')
template = templatefile.read()
templatefile.close()
tempdir = tempfile.mkdtemp()
for i in mathsymbols:
createimage(i, argv[2], template, argv[1], tempdir, True, replacements, toolbaritems, makefileentries)
for i in textsymbols:
createimage(i, argv[2], template, argv[1], tempdir, False, replacements, toolbaritems, makefileentries)
shutil.rmtree(tempdir)
else:
error(usage(argv[0]))
return 0
if __name__ == "__main__":
main(sys.argv)