X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=scripts%2Fmudela-book.py;h=c60924d43498712dafdab9014699494091f3aae1;hb=03311c038c913d96eb7ed0a896e2c7d8f4815e17;hp=98fb69f81ea44b09d479f1904b0f080f90a787fc;hpb=c64a05795e51367c17589a9e7d60bad426305159;p=lilypond.git diff --git a/scripts/mudela-book.py b/scripts/mudela-book.py index 98fb69f81e..c60924d434 100644 --- a/scripts/mudela-book.py +++ b/scripts/mudela-book.py @@ -1,645 +1,817 @@ #!@PYTHON@ -# All non-english comments are NOT in swedish, they are norwegian! -# TODO: -# * center option (??) -# * mudela-book should treat \begin{verbatim} that contains inline mudela -# correctly. -# * make mudela-book understand usepackage{geometry} -# * check that linewidth set in \paper is not wider than actual linewidth? -# * the following fails because mudelabook doesn't care that the -# last } after \end{mudela} finishes the marginpar: -# \marginpar{ -# \begin{mudela} -# c d e f g -# \end{mudela}} -# * force-verbatim is maybe not that useful since latex fails with footnotes, -# marginpars and others -# log: -# 0.3: -# rewrite in Python. -# 0.4: -# much rewritten by new author. I think the work has been split more -# logical between different classes. -# 0.4.1: -# - cleanup -# - speedup, do all mudela parsing at the same time to avoid multiple -# guile startup that takes some seconds on my computer -# 0.4.2: -# - fixed default latex fontsize, it should be 10pt not 11pt -# - verbatim option no longer visible -# - mudela-book produces .dvi output -# - change to use castingalgorithm = \Gourlay instead of \Wordwrap. It gives -# better result on small linewidths. -# - mudela-book-doc.doc rewritten -# 0.5: -# - junked floating and fragment options, added eps option -# - mudela-book will scan the mudela code to find out if it has to add -# paper-definition and \score{\notes{...}} -# - possible to define commands that look like this: \mudela{ c d e } +# vim: set noexpandtab: +# TODO: Figure out clean set of options. +# add support for .lilyrc import os +import stat import string import re import getopt import sys +import __main__ + + + +program_version = '@TOPLEVEL_VERSION@' +if program_version == '@' + 'TOPLEVEL_VERSION' + '@': + program_version = '1.3.69-very-unstable' + +include_path = [os.getcwd()] + +g_dep_prefix = '' +g_outdir = '' +g_force_mudela_fontsize = 0 +g_read_lys = 0 +g_do_pictures = 1 +g_num_cols = 1 +format = '' +g_run_lilypond = 1 +no_match = 'a\ba' + +default_music_fontsize = 16 +default_text_fontsize = 12 + +# latex linewidths: +# indices are no. of columns, papersize, fontsize +# Why can't this be calculated? +latex_linewidths = { + 1: {'a4':{10: 345, 11: 360, 12: 390}, + 'a5':{10: 276, 11: 276, 12: 276}, + 'b5':{10: 345, 11: 356, 12: 356}, + 'letter':{10: 345, 11: 360, 12: 390}, + 'legal': {10: 345, 11: 360, 12: 390}, + 'executive':{10: 345, 11: 360, 12: 379}}, + 2: {'a4':{10: 167, 11: 175, 12: 190}, + 'a5':{10: 133, 11: 133, 12: 133}, + 'b5':{10: 167, 11: 173, 12: 173}, + 'letter':{10: 167, 11: 175, 12: 190}, + 'legal':{10: 167, 11: 175, 12: 190}, + 'executive':{10: 167, 11: 175, 12: 184}}} + +texi_linewidths = { + 'a4': {12: 455}, + 'a4wide': {12: 470}, + 'smallbook': {12: 361}, + 'texidefault': {12: 433}} + + +def get_linewidth(cols, paper, fontsize): + if __main__.format == 'latex': + return latex_linewidths[cols][paper][fontsize] + elif __main__.format == 'texi': + return texi_linewidths[paper][fontsize] + raise "never here" + +option_definitions = [ + ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'), + ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'), + ('DIM', '', 'default-mudela-fontsize', 'deprecated, use --default-music-fontsize'), + ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline mudela. DIM is assumed be to in points'), + ('DIM', '', 'force-mudela-fontsize', 'deprecated, use --force-music-fontsize'), + ('DIR', 'I', 'include', 'include path'), + ('', 'M', 'dependencies', 'write dependencies'), + ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'), + ('', 'n', 'no-lily', 'don\'t run lilypond'), + ('', '', 'no-pictures', "don\'t generate pictures"), + ('', '', 'read-lys', "don't write ly files."), + ('FILE', 'o', 'outname', 'filename main output file'), + ('FILE', '', 'outdir', "where to place generated files"), + ('', 'v', 'version', 'print version information' ), + ('', 'h', 'help', 'print help'), + ] + +# format specific strings, ie. regex-es for input, and % strings for output +output_dict= { + 'latex': { + 'output-mudela-fragment' : r"""\begin[eps,singleline,%s]{mudela} + \context Staff < + \context Voice{ + %s + } + > +\end{mudela}""", + 'output-mudela':r"""\begin[%s]{mudela} +%s +\end{mudela}""", + 'output-verbatim': r"""\begin{verbatim}%s\end{verbatim}""", + 'output-default-post': r"""\def\postMudelaExample{}""", + 'output-default-pre': r"""\def\preMudelaExample{}""", + 'output-eps': '\\noindent\\parbox{\\mudelaepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}', + 'output-tex': '\\preMudelaExample \\input %(fn)s.tex \\postMudelaExample\n', + 'pagebreak': r'\pagebreak', + }, + 'texi' : {'output-mudela': """@mudela[%s] +%s +@end mudela +""", + 'output-mudela-fragment': """@mudela[%s] +\context Staff\context Voice{ %s } +@end mudela """, + 'pagebreak': None, + 'output-verbatim': r"""@example +%s +@end example +""", + +# do some tweaking: @ is needed in some ps stuff. +# override EndLilyPondOutput, since @tex is done +# in a sandbox, you can't do \input lilyponddefs at the +# top of the document. + +# should also support fragment in + + 'output-all': r"""@tex +\catcode`\@=12 +\input lilyponddefs +\def\EndLilyPondOutput{} +\input %(fn)s.tex +\catcode`\@=0 +@end tex +@html +
+
+@end html
+""",
+ }
+ }
+
+def output_verbatim (body):#ugh .format
+ if __main__.format == 'texi':
+ body = re.sub ('([@{}])', '@\\1', body)
+ return get_output ('output-verbatim') % body
+
+def output_mbverbatim (body):#ugh .format
+ if __main__.format == 'texi':
+ body = re.sub ('([@{}])', '@\\1', body)
+ return get_output ('output-verbatim') % body
+
+re_dict = {
+ 'latex': {'input': '\\\\mbinput{?([^}\t \n}]*)',
+ 'include': '\\\\mbinclude{(?P\\begin{verbatim}.*?\\end{verbatim})""",
+ 'verb': r"""(?P
\\verb(?P
.).*?(?P=del))""",
+ 'mudela-file': r'\\mudelafile(\[(?P.*?)}',
+ #'mudela-block': r"""(?m)^[^%]*?\\begin(\[(?P
.*?)\\end{mudela}""",
+ 'mudela-block': r"""(?s)\\begin(\[(?P
.*?)\\end{mudela}""",
+ 'def-post-re': r"""\\def\\postMudelaExample""",
+ 'def-pre-re': r"""\\def\\preMudelaExample""",
+ 'intertext': r',?\s*intertext=\".*?\"',
+ 'ignore': r"(?m)(?P
%.*?^)",
+ 'numcols': r"(?P
\\(?P
@example\s.*?@end example\s)""",
+ 'verb': r"""(?P
@code{.*?})""",
+ 'mudela-file': '@mudelafile(\[(?P
.*?)}',
+ 'mudela-block': r"""(?s)@mudela(\[(?P
.*?)@end mudela\s""",
+ 'option-sep' : ', *',
+ 'intertext': r',?\s*intertext=\".*?\"',
+ 'ignore': r"(?s)(?P
@ignore\s.*?@end ignore)\s",
+ 'numcols': no_match,
+ }
+ }
+
+
+for r in re_dict.keys ():
+ olddict = re_dict[r]
+ newdict = {}
+ for k in olddict.keys ():
+ newdict[k] = re.compile (olddict[k])
+ re_dict[r] = newdict
-outdir = 'out'
-initfile = ''
-program_version = '0.5'
-
-out_files = []
-
-fontsize_i2a = {11:'eleven', 13:'thirteen', 16:'sixteen',
- 20:'twenty', 26:'twentysix'}
-fontsize_pt2i = {'11pt':11, '13pt':13, '16pt':16, '20pt':20, '26pt':26}
-
-begin_mudela_re = re.compile ('^ *\\\\begin{mudela}')
-extract_papersize_re = re.compile('\\\\documentclass[\[, ]*(\w*)paper[\w ,]*\]\{\w*\}')
-extract_fontsize_re = re.compile('[ ,\[]*([0-9]*)pt')
-begin_mudela_opts_re = re.compile('\[[^\]]*\]')
-end_mudela_re = re.compile ('^ *\\\\end{mudela}')
-section_re = re.compile ('\\\\section')
-chapter_re = re.compile ('\\\\chapter')
-input_re = re.compile ('^\\\\input{([^}]*)')
-include_re = re.compile ('^\\\\include{([^}]*)')
-begin_document_re = re.compile ('^ *\\\\begin{document}')
-documentclass_re = re.compile('\\\\documentclass')
-twocolumn_re = re.compile('\\\\twocolumn')
-onecolumn_re = re.compile('\\\\onecolumn')
-preMudelaExample_re = re.compile('\\\\def\\\\preMudelaExample')
-postMudelaExample_re = re.compile('\\\\def\\\\postMudelaExample')
-boundingBox_re = re.compile('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)')
-
-def file_exist_b(name):
- try:
- f = open(name)
- except IOError:
- return 0
- f.close ()
- return 1
-
-def ps_dimention(fname):
- fd = open(fname)
- lines = fd.readlines()
- for line in lines:
- s = boundingBox_re.search(line)
- if s:
- break
- return (int(s.groups()[2])-int(s.groups()[0]),
- int(s.groups()[3])-int(s.groups()[1]))
-
-
-class CompileStatus:
- pass
-class SomethingIsSeriouslyBroken:
- pass
-
-def file_mtime (name):
- return os.stat (name)[8] #mod time
-
-def need_recompile_b(infile, outfile):
- indate = file_mtime (infile)
- try:
- outdate = file_mtime (outfile)
- return indate > outdate
- except os.error:
- return 1
+
+def uniq (list):
+ list.sort ()
+ s = list
+ list = []
+ for x in s:
+ if x not in list:
+ list.append (x)
+ return list
+
-#
-# executes os.system(command) if infile is newer than
-# outfile or outfile don't exist
-#
-def compile (command, workingdir, infile, outfile):
- "Test if infile is newer than outfile. If so, cd to workingdir"
- "and execute command"
- indate = file_mtime (workingdir+infile)
- try:
- outdate = file_mtime (workingdir+outfile)
- recompile = indate > outdate
-
- except os.error:
- recompile = 1
-
- if recompile:
- sys.stderr.write ('invoking `%s\'\n' % command)
- if workingdir == '':
- status = os.system (command)
- else:
- status = os.system ('cd %s; %s' %(workingdir, command))
- if status:
- raise CompileStatus
-
-class Properties:
- #
- # init
- #
- def __init__(self):
- self.__linewidth = {
- 1: {'a4':{10: 345, 11: 360, 12: 390},
- 'a5':{10: 276, 11: 276, 12: 276},
- 'b5':{10: 345, 11: 356, 12: 356},
- 'letter':{10: 345, 11: 360, 12: 390},
- 'legal': {10: 345, 11: 360, 12: 390},
- 'executive':{10: 345, 11: 360, 12: 379}},
- 2: {'a4':{10: 167, 11: 175, 12: 190},
- 'a5':{10: 133, 11: 133, 12: 133},
- 'b5':{10: 167, 11: 173, 12: 173},
- 'letter':{10: 167, 11: 175, 12: 190},
- 'legal':{10: 167, 11: 175, 12: 190},
- 'executive':{10: 167, 11: 175, 12: 184}}}
- # >0 --> force all mudela to this pt size
- self.force_mudela_fontsize = 0
- self.force_verbatim_b = 0
- self.__data = {
- 'mudela-fontsize' : {'init': 16},
- 'papersize' : {'init': 'a4'},
- 'num-column' : {'init': 1},
- 'tex-fontsize' : {'init': 10}
- }
- def clear_for_new_file(self):
- for var in self.__data.keys():
- self.__data[var] = {'init': self.__data[var]['init']}
- def clear_for_new_block(self):
- for var in self.__data.keys():
- if self.__data[var].has_key('block'):
- del self.__data[var]['block']
- def __get_variable(self, var):
- if self.__data[var].has_key('block'):
- return self.__data[var]['block']
- elif self.__data[var].has_key('file'):
- return self.__data[var]['file']
- else:
- return self.__data[var]['init']
- def setPapersize(self, size, requester):
- self.__data['papersize'][requester] = size
- def getPapersize(self):
- return self.__get_variable('papersize')
- def setMudelaFontsize(self, size, requester):
- self.__data['mudela-fontsize'][requester] = size
- def getMudelaFontsize(self):
- if self.force_mudela_fontsize:
- return self.force_mudela_fontsize
- return self.__get_variable('mudela-fontsize')
- def setTexFontsize(self, size, requester):
- self.__data['tex-fontsize'][requester] = size
- def getTexFontsize(self):
- return self.__get_variable('tex-fontsize')
- def setNumColumn(self, num, requester):
- self.__data['num-column'][requester] = num
- def getNumColumn(self):
- return self.__get_variable('num-column')
- def getLineWidth(self):
- return self.__linewidth[self.getNumColumn()][self.getPapersize()][self.getTexFontsize()]
-
-
-class Mudela_output:
- def __init__ (self, basename):
- self.basename = basename
- self.temp_filename = "%s/%s" %(outdir, 'mudela-temp.ly')
- self.file = open (self.temp_filename, 'w')
- self.__lines = []
- # 'tex' or 'eps'
- self.graphic_type = 'tex'
- self.code_type = 'unknown'
- self.code_type_override = None
- def write (self, line):
- # match only if there is nothing but whitespace before \begin HACK
- if re.search('^\s*\\\\begin{mudela}', line):
- self.scan_begin_statement(line)
- else:
- if self.code_type == 'unknown':
- if re.search('^\s*\\\\score', line) or \
- re.search('^\s*\\\\paper', line) or \
- re.search('^\s*\\\\header', line) or \
- re.search('^\s*[A-Za-z]*\s*=', line):
- self.code_type = 'ly'
- self.__lines.append(line)
- def scan_begin_statement(self, line):
- r = begin_mudela_opts_re.search(line)
- if r:
- o = r.group()[1:-1]
- optlist = re.compile('[\s,]*').split(o)
- else:
- optlist = []
- if 'fragment' in optlist:
- self.code_type_override = 'fly'
- if 'nonfragment' in optlist:
- self.code_type_override = 'ly'
- if 'eps' in optlist:
- self.graphic_type = 'eps'
- for pt in fontsize_pt2i.keys():
- if pt in optlist:
- Props.setMudelaFontsize(fontsize_pt2i[pt], 'block')
- def write_red_tape(self):
- self.file.write ('\\include \"paper%d.ly\"\n' \
- % Props.getMudelaFontsize())
-
- s = fontsize_i2a[Props.getMudelaFontsize()]
- if self.code_type == 'fly':
- linewidth_str = 'linewidth = -1.\cm;'
- else:
- linewidth_str = 'linewidth = %i.\\pt;' % Props.getLineWidth()
- self.file.write("\\paper {"
- + "\\paper_%s " % s
- + linewidth_str
- + "castingalgorithm = \Gourlay; \n}")
- #+ "castingalgorithm = \Wordwrap; indent = 2.\cm; \n}")
- if self.code_type == 'fly':
- self.file.write('\\score{\n\\notes{')
- def close (self):
- if self.code_type == 'unknown':
- self.code_type = 'fly'
- if self.code_type_override:
- self.code_type = self.code_type_override
- print "override:", self.code_type_override
- self.write_red_tape()
- for l in self.__lines:
- self.file.write(l)
- if self.code_type == 'fly':
- self.file.write('}}')
-
- self.file.close()
-
- inf = outdir + self.basename + '.ly'
- outf = outdir + self.basename + '.tex'
- if not os.path.isfile(inf):
- status = 1
- else:
- status = os.system ('diff -q %s %s' % (self.temp_filename, inf))
- if status:
- os.rename (self.temp_filename, inf)
- if need_recompile_b(inf, outf):
- out_files.append((self.graphic_type, inf))
- def insert_me_string(self):
- "Returns a string that can be used directly in latex."
- if self.graphic_type == 'tex':
- return ['tex', self.basename]
- elif self.graphic_type == 'eps':
- return ['eps', self.basename]
- else:
- raise SomethingIsSeriouslyBroken
-
-class Tex_output:
- def __init__ (self, name):
- self.output_fn = '%s/%s' % (outdir, name)
- self.__lines = []
- def open_verbatim (self, line):
- self.__lines.append('\\begin{verbatim}\n')
- s = re.sub('[\s,]*verbatim[\s]*', '', line)
- s = re.sub('\[\]', '', s)
- self.__lines.append(s);
- def close_verbatim (self):
- self.__lines.append('\\end{verbatim}\n')
- def write (self, s):
- self.__lines.append(s)
- def create_graphics(self):
- s = ''
- g_vec = []
- for line in self.__lines:
- if type(line)==type([]):
- g_vec.append(line)
- for g in g_vec:
- if need_recompile_b(outdir+g[1]+'.ly', outdir+g[1]+'.tex'):
- s = s + ' ' + g[1]+'.ly'
- if s != '':
- e = os.system('cd %s; lilypond %s' %(outdir, s))
- if e:
- print "error: lilypond exited with value", e
- sys.exit(e)
- for g in g_vec:
- if g[0] == 'eps':
- compile('tex %s' % g[1]+'.tex', outdir, g[1]+'.tex', g[1]+'.dvi')
- compile('dvips -E -o %s %s' %(g[1]+'.eps', g[1]+'.dvi'), outdir,
- g[1]+'.dvi', g[1]+'.eps')
- def write_outfile(self):
- file = open(self.output_fn+'.latex', 'w')
- file.write('% Created by mudela-book\n')
- for line in self.__lines:
- if type(line)==type([]):
- if line[0] == 'tex':
- file.write('\\preMudelaExample\\input %s\n\postMudelaExample '\
- # TeX applies the prefix of the main source automatically.
- % (line[1]+'.tex'))
-# % (outdir+line[1]+'.tex'))
- if line[0] == 'eps':
- ps_dim = ps_dimention(outdir+line[1]+'.eps')
- file.write('\\parbox{%ipt}{\includegraphics{%s}}\n' \
- % (ps_dim[0], line[1]+'.eps'))
-# % (ps_dim[0], outdir+line[1]+'.eps'))
- else:
- file.write(line)
- file.close()
-
-class Tex_input:
- def __init__ (self, filename):
- for fn in [filename, filename+'.tex', filename+'.doc']:
- try:
- self.infile = open (fn)
- self.filename = fn
- return
- except:
- continue
- raise IOError
- def get_lines (self):
- lines = self.infile.readlines ()
- (retlines, retdeps) = ([],[self.filename])
- for line in lines:
- r_inp = input_re.search (line)
- r_inc = include_re.search (line)
-
- # Filename rules for \input :
- # input: no .tex ext
- # 1. will search for file with exact that name (tex-input.my will be found)
- # 2. will search for file with .tex ext, (tex-input.my
- # will find tex-input.my.tex)
- # input: with .tex ext
- # 1. will find exact match
-
- # Filename rules for \include :
- # 1. will only find files with name given to \include + .tex ext
- if r_inp:
- try:
- t = Tex_input (r_inp.groups()[0])
- ls = t.get_lines ()
- retlines = retlines + ls[0]
- retdeps = retdeps + ls[1]
- except:
- print "warning: can't find %s, let's hope latex will" \
- % r_inp.groups()[0]
- retlines.append (line)
- elif r_inc:
- try:
- t = Tex_input (r_inc.groups()[0]+'.tex')
- ls =t.get_lines ()
- ls[0].insert(0, '\\newpage\n')
- ls[0].append('\\newpage\n')
- retlines = retlines + ls[0]
- retdeps = retdeps + ls[1]
- except:
- print "warning: can't find %s, let's hope latex will" \
- % r_inc.groups()[0]
- retlines.append (line)
- else:
- r_mud = defined_mudela_cmd_re.search(line)
- if r_mud:
- ss = "\\\\verb(?P