12 program_version = '@TOPLEVEL_VERSION@'
17 # TODO: use splitting iso. \mudelagraphic.
20 default_music_fontsize = 16
21 default_text_fontsize = 12
24 # indices are no. of columns, papersize, fontsize
25 # Why can't this be calculated?
27 1: {'a4':{10: 345, 11: 360, 12: 390},
28 'a5':{10: 276, 11: 276, 12: 276},
29 'b5':{10: 345, 11: 356, 12: 356},
30 'letter':{10: 345, 11: 360, 12: 390},
31 'legal': {10: 345, 11: 360, 12: 390},
32 'executive':{10: 345, 11: 360, 12: 379}},
33 2: {'a4':{10: 167, 11: 175, 12: 190},
34 'a5':{10: 133, 11: 133, 12: 133},
35 'b5':{10: 167, 11: 173, 12: 173},
36 'letter':{10: 167, 11: 175, 12: 190},
37 'legal':{10: 167, 11: 175, 12: 190},
38 'executive':{10: 167, 11: 175, 12: 184}}}
42 ('DIM', '', 'default-mudela-fontsize', 'default fontsize for music. DIM is assumed to in points'),
43 ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'),
44 ('', 'h', 'help', 'print help'),
45 ('DIR', 'I', 'include', 'include path'),
46 ('', '', 'init', 'mudela-book initfile'),
47 # ('DIM', '', 'force-mudela-fontsize', 'force fontsize for all inline mudela. DIM is assumed to in points'),
48 ('', '', 'force-verbatim', 'make all mudela verbatim'),
49 ('', 'M', 'dependencies', 'write dependencies'),
50 ('', 'n', 'no-lily', 'don\'t run lilypond'),
51 ('FILE', 'o', 'outname', 'prefix for filenames'),
52 ('', 'v', 'version', 'print version information' )
59 # format specific strings, ie. regex-es for input, and % strings for output
62 'output-mudela-fragment' : r"""\begin[eps,fragment%s]{mudela}
69 'output-mudela':r"""\begin%s{mudela}
72 'output-verbatim': r"""\begin{verbatim}%s\end{verbatim}""",
73 'output-default-post': r"""\def\postMudelaExample{}""",
74 'output-default-pre': r"""\def\preMudelaExample{}""",
75 'output-eps': '\\noindent\\parbox{\\mudelaepswidth{%s.eps}}{\includegraphics{%s.eps}}',
76 'output-tex': '\\preMudelaExample \\input %s.tex \\postMudelaExample\n'
78 'texi' : {'output-mudela': """@mudela[%s]
82 'output-verbatim': r"""@example
86 'output-all': r"""@tex
97 'latex': {'input': '\\\\input{?([^}\t \n}]*)',
98 'include': '\\\\include{([^}]+)}',
101 'header': r"""\\documentclass(\[.*?\])?""",
102 'preamble-end': '\\\\begin{document}',
103 'verbatim': r"""(?s)\\begin{verbatim}(.*?)\\end{verbatim}""",
104 'verb': r"""\\verb(.)(.*?)\1""",
105 'mudela-file': '\\\\mudelafile(\[[^\\]]+\])?{([^}]+)}',
106 'mudela' : '\\\\mudela(\[.*?\])?{(.*?)}',
107 'mudela-block': r"""(?s)\\begin(\[.*?\])?{mudela}(.*?)\\end{mudela}""",
108 'interesting-cs': '\\\\(chapter|section|twocolumn|onecolumn)',
109 'def-post-re': r"""\\def\\postMudelaExample""",
110 'def-pre-re': r"""\\def\\preMudelaExample""",
112 'texi': {'input': '@include[ \n\t]+([^\t \n]*)',
115 'preamble-end': no_match,
116 'verbatim': r"""(?s)@example(.*?)@end example$""",
117 'verb': r"""@code{(.*?)}""",
118 'mudela-file': '@mudelafile(\[[^\\]]+\])?{([^}]+)}',
119 'mudela' : '@mudela(\[.*?\])?{(.*?)}',
120 'mudela-block': r"""(?s)@mudela(\[.*?\])?(.*?)@end mudela""",
121 'interesting-cs': r"""[\\@](node|mudelagraphic)""",
127 for r in re_dict.keys ():
130 for k in olddict.keys ():
131 newdict[k] = re.compile (olddict[k])
135 def get_output (name):
136 return output_dict[format][name]
139 return re_dict[format][name]
142 def bounding_box_dimensions(fname):
146 error ("Error opening `%s'" % fname)
148 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
150 return (int(s.group(3))-int(s.group(1)),
151 int(s.group(4))-int(s.group(2)))
156 def find_file (name):
157 for a in include_path:
159 nm = os.path.join (a, name)
167 sys.stderr.write (str + "\n Exiting ... \n\n")
171 def compose_full_body (body, opts):
172 "Construct the text of an input file: add stuff to BODY using OPTS as options."
174 music_size = default_music_fontsize
175 latex_size = default_text_fontsize
179 m = re.search ('^(.*)paper$', o)
184 m = re.match ('([0-9]+)pt', o)
186 music_size = string.atoi(m.group (1))
188 m = re.match ('latexfontsize=([0-9]+)pt', o)
190 latex_size = string.atoi (m.group (1))
193 if 'twocolumn' in opts:
196 if 'fragment' or 'singleline' in opts:
199 l = latex_linewidths[cols][paper][latex_size]
201 # urg: breaks on \include of full score
202 # if not 'nofly' in opts and not re.search ('\\\\score', body):
203 # opts.append ('fly')
215 %% Generated by mudela-book.py
216 \include "paper%d.ly"
217 \paper { linewidth = %f \pt; }
218 """ % (music_size, l) + body
222 def inclusion_func (match, surround):
223 insert = match.group (0)
225 (insert, d) = read_doc_file (match.group(1))
227 insert = surround + insert + surround
229 sys.stderr.write("warning: can't find %s, let's hope latex will\n" % m.group(1))
231 return (insert, deps)
233 def find_inclusion_chunks (regex, surround, str):
236 m = regex.search (str)
239 chunks.append (('input', str))
243 chunks.append (('input', str[: m.start (0)]))
244 chunks.append (('input', surround))
245 chunks = chunks + read_doc_file (m.group (1))
246 chunks.append (('input', surround))
248 str = str [m.end (0):]
251 def find_include_chunks (str):
252 return find_inclusion_chunks (get_re ('include'), '\\newpage', str)
254 def find_input_chunks (str):
255 return find_inclusion_chunks (get_re ('input'), '', str)
257 def read_doc_file (filename):
258 """Read the input file, substituting for \input, \include, \mudela{} and \mudelafile"""
260 for fn in [filename, filename+'.tex', filename+'.doc']:
273 if __main__.format == '':
274 latex = re.search ('\\\\document', str[:200])
275 texinfo = re.search ('@node', str[:200])
276 if (texinfo and latex) or not (texinfo or latex):
277 error("error: can't determine format, please specify")
279 __main__.format = 'texi'
281 __main__.format = 'latex'
283 chunks = find_verbatim_chunks (str)
286 for func in (find_include_chunks, find_input_chunks):
290 newchunks = newchunks + ch
299 def scan_preamble (str):
301 m = get_re ('header').search( str)
303 # should extract paper & fontsz.
304 if m and m.group (1):
305 options = options + re.split (',[\n \t]*', m.group(1)[1:-1])
307 def verbose_fontsize ( x):
309 #if o.match('[0-9]+pt'):
310 if re.match('[0-9]+pt', x):
311 return 'latexfontsize=' + x
315 options = map (verbose_fontsize, options)
320 def completize_preamble (str):
321 m = get_re ('preamble-end').search( str)
325 preamble = str [:m.start (0)]
326 str = str [m.start(0):]
328 if not get_re('def-post-re').search (preamble):
329 preamble = preamble + get_output('output-default-post')
330 if not get_re ('def-pre-re').search( preamble):
331 preamble = preamble + get_output ('output-default-pre')
334 #if re.search ('\\\\includegraphics', str) and not re.search ('usepackage{graphics}',str):
336 preamble = preamble + '\\usepackage{graphics}\n'
338 return preamble + str
340 def find_verbatim_chunks (str):
341 """Chop STR into a list of tagged chunks, ie. tuples of form
342 (TYPE_STR, CONTENT_STR), where TYPE_STR is one of 'input' and 'verbatim'
349 m = get_re ('verbatim').search( str)
350 m2 = get_re ("verb").search( str)
352 if m == None and m2 == None:
353 chunks.append (('input', str))
360 if m2 and m2.start (0) < m.start (0):
363 chunks.append (('input', str[:m.start (0)]))
364 chunks.append (('verbatim', m.group (0)))
366 str = str [m.end(0):]
370 def find_mudela_shorthand_chunks (str):
371 return [('input', find_mudela_shorthands(str))]
373 def find_mudela_shorthands (b):
374 def mudela_short (match):
375 "Find \mudela{}, and substitute appropriate \begin / \end blocks."
376 opts = match.group (1)
378 opts = ',' + opts[1:-1]
381 return get_output ('output-mudela-fragment') % (opts, match.group (2))
383 def mudela_file (match):
384 "Find \mudelafile, and substitute appropriate \begin / \end blocks."
385 d = [] #, d = retdeps
386 full_path = find_file (match.group (2))
388 error("error: can't find file `%s'\n" % match.group(2))
393 opts = match.group (1)
395 opts = re.split (',[ \n\t]*', opts[1:-1])
399 if re.search ('.fly$', full_path):
401 elif re.search ('.sly$', full_path):
402 opts = opts + [ 'fly','fragment']
403 elif re.search ('.ly$', full_path):
404 opts .append ('nofly')
406 str_opts = string.join (opts, ',')
407 if str_opts: str_opts = '[' + str_opts + ']'
410 str = "%% copied from %s" % full_path + str
411 return get_output ('output-mudela') % (str_opts, str)
413 b = get_re('mudela-file').sub (mudela_file, b)
414 b = get_re('mudela').sub (mudela_short, b)
417 def find_mudela_chunks (str):
418 """Find mudela blocks, while watching for verbatim. Returns
419 (STR,MUDS) with \mudelagraphic substituted for the blocks in STR,
420 and the blocks themselves MUDS"""
424 m = get_re ("mudela-block").search( str)
426 chunks.append (('input', str))
430 chunks.append (('input', str[:m.start (0)]))
437 optlist = get_re('comma-sep').split (opts)
440 chunks.append (('mudela', body, optlist))
442 str = str [m.end (0):]
448 def advance_counters (counter, opts, str):
449 """Advance chap/sect counters,
450 revise OPTS. Return the new counter tuple"""
452 (chapter, section, count) = counter
455 m = get_re ('interesting-cs').search(str)
461 done = done + str[:m.end (0)]
466 opts.append ('twocolumn')
467 elif g == 'onecolumn':
469 current_opts.remove ('twocolumn')
473 (chapter, section, count) = (chapter + 1, 0, 0)
474 elif g == 'section' or g == 'node':
475 (section, count) = (section + 1, 0)
478 return (chapter, section, count)
481 def schedule_mudela_block (base, chunk, extra_opts):
483 """Take the body and options from CHUNK, figure out how the
484 real .ly should look, and what should be left MAIN_STR (meant
485 for the main file). The .ly is written, and scheduled in
488 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO)
490 TODO has format [basename, extension, extension, ... ]
494 (type, body, opts) = chunk
495 assert type == 'mudela'
496 opts = opts + extra_opts
499 if 'verbatim' in opts:
500 verbatim_mangle = body
501 if __main__.format == 'texi':
502 verbatim_mangle = re.sub ('([{}])', '@\\1', body)
503 newbody = get_output ('output-verbatim') % verbatim_mangle
505 file_body = compose_full_body (body, opts)
506 updated = update_file (file_body, base + '.ly')
510 if not os.path.isfile (base + '.tex') or updated:
515 m = re.search ('intertext="(.*?)"', o)
517 newbody = newbody + m.group (1)
525 if 'eps' in opts and ('tex' in todo or
526 not os.path.isfile (base + '.eps')):
529 if 'png' in opts and ('eps' in todo or
530 not os.path.isfile (base + '.png')):
533 if format == 'latex':
535 newbody = newbody + get_output ('output-eps') % (base, base)
537 newbody = newbody + get_output ('output-tex') % base
539 elif format == 'texi':
540 newbody = newbody + get_output ('output-all') % (base, base)
544 return ('mudela', newbody, opts, todo)
546 def find_eps_dims (match):
547 "Fill in dimensions of EPS files."
549 dims = bounding_box_dimensions (fn)
551 return '%ipt' % dims[0]
554 def print_chunks (ch):
556 print '-->%s\n%s' % (c[0], c[1])
558 print '==>%s' % list (c[2:])
562 def transform_input_file (in_filename, out_filename):
563 """Read the input, and deliver a list of chunks
567 chunks = read_doc_file (in_filename)
569 #. Process \mudela and \mudelafile.
570 for func in [find_mudela_shorthand_chunks,
575 newchunks = newchunks + func (c[1])
582 opts = scan_preamble (chunks[0][1])
584 (chap,sect,count) = (0,0,0)
586 # Count sections/chapters.
589 (chap,sect,count) = advance_counters((chap,sect,count), opts, c[1])
590 elif c[0] == 'mudela':
591 base = '%s-%d.%d.%d' % (out_filename, chap, sect, count)
593 c = schedule_mudela_block (base, c, opts)
602 if not __main__.no_lily:
603 compile_all_files (chunks)
607 if c[0] == 'mudela' and 'eps' in c[2]:
608 body = re.sub (r"""\\mudelaepswidth{(.*?)}""", find_eps_dims, c[1])
609 newchunks.append (('mudela', body))
613 if chunks and chunks[0][0] == 'input':
614 chunks[0] = ('input', completize_preamble (chunks[0][1]))
619 sys.stderr.write ("invoking `%s'\n" % cmd)
622 sys.stderr.write ('Error command exited with value %d\n' % st)
625 def compile_all_files (chunks):
639 tex.append (base + '.ly')
644 lilyopts = map (lambda x: '-I ' + x, include_path)
645 lilyopts = string.join (lilyopts, ' ' )
646 texfiles = string.join (tex, ' ')
647 system ('lilypond %s %s' % (lilyopts, texfiles))
650 cmd = r"""tex %s; dvips -E -o %s %s""" % \
655 cmd = r"""gs -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
657 cmd = cmd % (g + '.eps', g + '.png')
661 def update_file (body, name):
671 f = open (name , 'w')
680 def getopt_args (opts):
681 "Construct arguments (LONG, SHORT) for getopt from list of options."
696 def option_help_str (o):
697 "Transform one option description (4-tuple ) into neatly formatted string"
715 return ' ' + sh + sep + long + arg
718 def options_help_str (opts):
719 "Convert a list of options into a neatly formatted string"
724 s = option_help_str (o)
725 strs.append ((s, o[3]))
731 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
735 sys.stdout.write("""Usage: mudela-book [options] FILE\n
736 Generate hybrid LaTeX input from Latex + mudela
739 sys.stdout.write (options_help_str (options))
740 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
744 Report bugs to bug-gnu-music@gnu.org.
746 Written by Tom Cato Amundsen <tomcato@xoommail.com> and
747 Han-Wen Nienhuys <hanwen@cs.uu.nl>
753 def write_deps (fn, target, deps):
754 sys.stdout.write('writing `%s\'\n' % fn)
758 target = target + '.latex'
759 f.write ('%s: %s\n'% (target, string.join (deps, ' ')))
764 sys.stdout.write ('mudela-book (GNU LilyPond) %s\n' % program_version)
766 def print_version ():
768 sys.stdout.write (r"""Copyright 1998--1999
769 Distributed under terms of the GNU General Public License. It comes with
775 global outdir, initfile, defined_mudela_cmd, defined_mudela_cmd_re
778 (sh, long) = getopt_args (__main__.options)
779 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
780 except getopt.error, msg:
781 sys.stderr.write("error: %s" % msg)
789 if o == '--include' or o == '-I':
790 include_path.append (a)
791 elif o == '--version':
795 elif o == '--format' or o == '-o':
797 elif o == '--outname' or o == '-o':
800 sys.stderr.write("Mudela-book is confused by --outname on multiple files")
803 elif o == '--outdir' or o == '-d':
805 elif o == '--help' or o == '-h':
807 elif o == '--no-lily' or o == '-n':
809 elif o == '--dependencies':
811 elif o == '--default-mudela-fontsize':
812 default_music_fontsize = string.atoi (a)
818 for input_filename in files:
823 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
824 my_depname = my_outname + '.dep'
826 chunks = transform_input_file (input_filename, my_outname)
828 foutn = my_outname + '.' + format
829 sys.stderr.write ("Writing `%s'\n" % foutn)
830 fout = open (foutn, 'w')
836 # write_deps (my_depname, my_outname, deps)
837 sys.stderr.write ("--dependencies broken")
844 # Petr, ik zou willen dat ik iets zinvoller deed,
845 # maar wat ik kan ik doen, het verandert toch niets?