]> git.donarmstrong.com Git - lilypond.git/blob - scripts/lilypond-book.py
release: 1.5.44
[lilypond.git] / scripts / lilypond-book.py
1 #!@PYTHON@
2 # vim: set noexpandtab:
3 # TODO:
4 # * junk --outdir for --output 
5 # * Figure out clean set of options.
6 # * 
7 # * EndLilyPondOutput is def'd as vfil. Causes large white gaps.
8 # * texinfo: add support for @pagesize
9
10 # todo: dimension handling (all the x2y) is clumsy. (tca: Thats
11 #       because the values are taken directly from texinfo.tex,
12 #       geometry.sty and article.cls. Give me a hint, and I'll
13 #       fix it.)
14
15 #
16 # TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips. 
17
18
19 # This is was the idea for handling of comments:
20 #       Multiline comments, @ignore .. @end ignore is scanned for
21 #       in read_doc_file, and the chunks are marked as 'ignore', so
22 #       lilypond-book will not touch them any more. The content of the
23 #       chunks are written to the output file. Also 'include' and 'input'
24 #       regex has to check if they are commented out.
25 #
26 #       Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
27 #       These three regex's has to check if they are on a commented line,
28 #       % for latex, @c for texinfo.
29 #
30 #       Then lines that are commented out with % (latex) and @c (Texinfo)
31 #       are put into chunks marked 'ignore'. This cannot be done before
32 #       searching for the lilypond-blocks because % is also the comment character
33 #       for lilypond.
34 #
35 #       The the rest of the rexeces are searched for. They don't have to test
36 #       if they are on a commented out line.
37
38
39
40 import os
41 import stat
42 import string
43 import re
44 import getopt
45 import sys
46 import __main__
47 import operator
48
49
50 program_version = '@TOPLEVEL_VERSION@'
51 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
52         program_version = '1.5.18'
53
54 # if set, LILYPONDPREFIX must take prevalence
55 # if datadir is not set, we're doing a build and LILYPONDPREFIX 
56 datadir = '@datadir@'
57
58 if os.environ.has_key ('LILYPONDPREFIX') :
59         datadir = os.environ['LILYPONDPREFIX']
60 else:
61         datadir = '@datadir@'
62
63 while datadir[-1] == os.sep:
64         datadir= datadir[:-1]
65
66 # Try to cater for bad installations of LilyPond, that have
67 # broken TeX setup.  Just hope this doesn't hurt good TeX
68 # setups.  Maybe we should check if kpsewhich can find
69 # feta16.{afm,mf,tex,tfm}, and only set env upon failure.
70 environment = {
71         'MFINPUTS' : datadir + '/mf:',
72         'TEXINPUTS': datadir + '/tex:' + datadir + '/ps:.:',
73         'TFMFONTS' : datadir + '/tfm:',
74         'GS_FONTPATH' : datadir + '/afm:' + datadir + '/pfa',
75         'GS_LIB' : datadir + '/ps',
76 }
77
78 # tex needs lots of memory, more than it gets by default on Debian
79 non_path_environment = {
80         'extra_mem_top' : '1000000',
81         'extra_mem_bottom' : '1000000',
82         'pool_size' : '250000',
83 }
84
85 def setup_environment ():
86         for key in environment.keys ():
87                 val = environment[key]
88                 if os.environ.has_key (key):
89                         val = val + os.pathsep + os.environ[key]
90                 os.environ[key] = val
91
92         for key in non_path_environment.keys ():
93                 val = non_path_environment[key]
94                 print '%s=%s' % (key,val)
95                 os.environ[key] = val
96
97 include_path = [os.getcwd()]
98
99
100 # g_ is for global (?)
101 g_extra_opts = ''
102 g_here_dir = os.getcwd ()
103 g_dep_prefix = ''
104 g_outdir = ''
105 g_force_lilypond_fontsize = 0
106 g_read_lys = 0
107 g_do_pictures = 1
108 g_num_cols = 1
109
110 format = ''
111 g_run_lilypond = 1
112 no_match = 'a\ba'
113
114 default_music_fontsize = 16
115 default_text_fontsize = 12
116 paperguru = None
117
118 # this code is ugly. It should be cleaned
119 class LatexPaper:
120         def __init__(self):
121                 self.m_paperdef =  {
122                         # the dimensions are from geometry.sty
123                         'a0paper': (mm2pt(841), mm2pt(1189)),
124                         'a1paper': (mm2pt(595), mm2pt(841)),
125                         'a2paper': (mm2pt(420), mm2pt(595)),
126                         'a3paper': (mm2pt(297), mm2pt(420)),
127                         'a4paper': (mm2pt(210), mm2pt(297)),
128                         'a5paper': (mm2pt(149), mm2pt(210)),
129                         'b0paper': (mm2pt(1000), mm2pt(1414)),
130                         'b1paper': (mm2pt(707), mm2pt(1000)),
131                         'b2paper': (mm2pt(500), mm2pt(707)),
132                         'b3paper': (mm2pt(353), mm2pt(500)),
133                         'b4paper': (mm2pt(250), mm2pt(353)),
134                         'b5paper': (mm2pt(176), mm2pt(250)),
135                         'letterpaper': (in2pt(8.5), in2pt(11)),
136                         'legalpaper': (in2pt(8.5), in2pt(14)),
137                         'executivepaper': (in2pt(7.25), in2pt(10.5))}
138                 self.m_use_geometry = None
139                 self.m_papersize = 'letterpaper'
140                 self.m_fontsize = 10
141                 self.m_num_cols = 1
142                 self.m_landscape = 0
143                 self.m_geo_landscape = 0
144                 self.m_geo_width = None
145                 self.m_geo_textwidth = None
146                 self.m_geo_lmargin = None
147                 self.m_geo_rmargin = None
148                 self.m_geo_includemp = None
149                 self.m_geo_marginparwidth = {10: 57, 11: 50, 12: 35}
150                 self.m_geo_marginparsep = {10: 11, 11: 10, 12: 10}
151                 self.m_geo_x_marginparwidth = None
152                 self.m_geo_x_marginparsep = None
153                 self.__body = None
154         def set_geo_option(self, name, value):
155
156                 if type(value) == type(""):
157                         m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
158                         if m:
159                                 unit = m.group (2)
160                                 num = string.atof(m.group (1))
161                                 conv =  dimension_conversion_dict[m.group(2)]
162
163                                 value = conv(num)
164
165                 if name == 'body' or name == 'text':
166                         if type(value) == type(""):
167                                 self.m_geo_textwidth =  value
168                         else:
169                                 self.m_geo_textwidth =  value[0]
170                         self.__body = 1
171                 elif name == 'portrait':
172                         self.m_geo_landscape = 0
173                 elif name == 'reversemp' or name == 'reversemarginpar':
174                         if self.m_geo_includemp == None:
175                                 self.m_geo_includemp = 1
176                 elif name == 'marginparwidth' or name == 'marginpar':
177                         self.m_geo_x_marginparwidth =  value
178                         self.m_geo_includemp = 1
179                 elif name == 'marginparsep':
180                         self.m_geo_x_marginparsep =  value
181                         self.m_geo_includemp = 1
182                 elif name == 'scale':
183                         if type(value) == type(""):
184                                 self.m_geo_width = self.get_paperwidth() * float(value)
185                         else:
186                                 self.m_geo_width = self.get_paperwidth() * float(value[0])
187                 elif name == 'hscale':
188                         self.m_geo_width = self.get_paperwidth() * float(value)
189                 elif name == 'left' or name == 'lmargin':
190                         self.m_geo_lmargin =  value
191                 elif name == 'right' or name == 'rmargin':
192                         self.m_geo_rmargin =  value
193                 elif name == 'hdivide' or name == 'divide':
194                         if value[0] not in ('*', ''):
195                                 self.m_geo_lmargin =  value[0]
196                         if value[1] not in ('*', ''):
197                                 self.m_geo_width =  value[1]
198                         if value[2] not in ('*', ''):
199                                 self.m_geo_rmargin =  value[2]
200                 elif name == 'hmargin':
201                         if type(value) == type(""):
202                                 self.m_geo_lmargin =  value
203                                 self.m_geo_rmargin =  value
204                         else:
205                                 self.m_geo_lmargin =  value[0]
206                                 self.m_geo_rmargin =  value[1]
207                 elif name == 'margin':#ugh there is a bug about this option in
208                                         # the geometry documentation
209                         if type(value) == type(""):
210                                 self.m_geo_lmargin =  value
211                                 self.m_geo_rmargin =  value
212                         else:
213                                 self.m_geo_lmargin =  value[0]
214                                 self.m_geo_rmargin =  value[0]
215                 elif name == 'total':
216                         if type(value) == type(""):
217                                 self.m_geo_width =  value
218                         else:
219                                 self.m_geo_width =  value[0]
220                 elif name == 'width' or name == 'totalwidth':
221                         self.m_geo_width =  value
222                 elif name == 'paper' or name == 'papername':
223                         self.m_papersize = value
224                 elif name[-5:] == 'paper':
225                         self.m_papersize = name
226                 else:
227                         pass 
228                         # what is _set_dimen ?? /MB
229                         #self._set_dimen('m_geo_'+name, value)
230         def __setattr__(self, name, value):
231                 if type(value) == type("") and \
232                    dimension_conversion_dict.has_key (value[-2:]):
233                         f = dimension_conversion_dict[value[-2:]]
234                         self.__dict__[name] = f(float(value[:-2]))
235                 else:
236                         self.__dict__[name] = value
237                         
238         def __str__(self):
239                 s =  "LatexPaper:\n-----------"
240                 for v in self.__dict__.keys():
241                         if v[:2] == 'm_':
242                                 s = s +  str (v) + ' ' + str (self.__dict__[v])
243                 s = s +  "-----------"
244                 return s
245         
246         def get_linewidth(self):
247                 w = self._calc_linewidth()
248                 if self.m_num_cols == 2:
249                         return (w - 10) / 2
250                 else:
251                         return w
252         def get_paperwidth(self):
253                 #if self.m_use_geometry:
254                         return self.m_paperdef[self.m_papersize][self.m_landscape or self.m_geo_landscape]
255                 #return self.m_paperdef[self.m_papersize][self.m_landscape]
256         
257         def _calc_linewidth(self):
258                 # since geometry sometimes ignores 'includemp', this is
259                 # more complicated than it should be
260                 mp = 0
261                 if self.m_geo_includemp:
262                         if self.m_geo_x_marginparsep is not None:
263                                 mp = mp + self.m_geo_x_marginparsep
264                         else:
265                                 mp = mp + self.m_geo_marginparsep[self.m_fontsize]
266                         if self.m_geo_x_marginparwidth is not None:
267                                 mp = mp + self.m_geo_x_marginparwidth
268                         else:
269                                 mp = mp + self.m_geo_marginparwidth[self.m_fontsize]
270
271                 #ugh test if this is necessary                          
272                 if self.__body:
273                         mp = 0
274
275                 if not self.m_use_geometry:
276                         return latex_linewidths[self.m_papersize][self.m_fontsize]
277                 else:
278                         geo_opts = (self.m_geo_lmargin == None,
279                                     self.m_geo_width == None,
280                                     self.m_geo_rmargin == None)
281
282                         if geo_opts == (1, 1, 1):
283                                 if self.m_geo_textwidth:
284                                         return self.m_geo_textwidth
285                                 w = self.get_paperwidth() * 0.8
286                                 return w - mp
287                         elif geo_opts == (0, 1, 1):
288                                  if self.m_geo_textwidth:
289                                         return self.m_geo_textwidth
290                                  return self.f1(self.m_geo_lmargin, mp)
291                         elif geo_opts == (1, 1, 0):
292                                  if self.m_geo_textwidth:
293                                         return self.m_geo_textwidth
294                                  return self.f1(self.m_geo_rmargin, mp)
295                         elif geo_opts \
296                                         in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
297                                 if self.m_geo_textwidth:
298                                         return self.m_geo_textwidth
299                                 return self.m_geo_width - mp
300                         elif geo_opts in ((0, 1, 0), (0, 0, 0)):
301                                 w = self.get_paperwidth() \
302                                   - self.m_geo_lmargin - self.m_geo_rmargin - mp
303                                 if w < 0:
304                                         w = 0
305                                 return w
306                         raise "Never do this!"
307         def f1(self, m, mp):
308                 tmp = self.get_paperwidth() - m * 2 - mp
309                 if tmp < 0:
310                         tmp = 0
311                 return tmp
312         def f2(self):
313                 tmp = self.get_paperwidth() - self.m_geo_lmargin \
314                         - self.m_geo_rmargin
315                 if tmp < 0:
316                         return 0
317                 return tmp
318
319 class TexiPaper:
320         def __init__(self):
321                 self.m_papersize = 'letterpaper'
322                 self.m_fontsize = 12
323         def get_linewidth(self):
324                 return texi_linewidths[self.m_papersize][self.m_fontsize]
325
326 def mm2pt(x):
327         return x * 2.8452756
328 def in2pt(x):
329         return x * 72.26999
330 def em2pt(x, fontsize = 10):
331         return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
332 def ex2pt(x, fontsize = 10):
333         return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
334
335 def pt2pt(x):
336         return x
337
338 dimension_conversion_dict ={
339         'mm': mm2pt,
340         'cm': lambda x: mm2pt(10*x),
341         'in': in2pt,
342         'em': em2pt,
343         'ex': ex2pt,
344         'pt': pt2pt
345         }
346
347         
348 # latex linewidths:
349 # indices are no. of columns, papersize,  fontsize
350 # Why can't this be calculated?
351 latex_linewidths = {
352         'a4paper':{10: 345, 11: 360, 12: 390},
353         'a4paper-landscape': {10: 598, 11: 596, 12:592},
354         'a5paper':{10: 276, 11: 276, 12: 276},
355         'b5paper':{10: 345, 11: 356, 12: 356},
356         'letterpaper':{10: 345, 11: 360, 12: 390},
357         'letterpaper-landscape':{10: 598, 11: 596, 12:596},
358         'legalpaper': {10: 345, 11: 360, 12: 390},
359         'executivepaper':{10: 345, 11: 360, 12: 379}}
360
361 texi_linewidths = {
362         'afourpaper': {12: mm2pt(160)},
363         'afourwide': {12: in2pt(6.5)},
364         'afourlatex': {12: mm2pt(150)},
365         'smallbook': {12: in2pt(5)},
366         'letterpaper': {12: in2pt(6)}}
367
368 option_definitions = [
369   ('EXT', 'f', 'format', 'set format.  EXT is one of texi and latex.'),
370   ('DIM',  '', 'default-music-fontsize', 'default fontsize for music.  DIM is assumed to be in points'),
371   ('DIM',  '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
372   ('OPT', '', 'extra-options' , 'Pass OPT quoted to the lilypond command line'),
373   ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
374   ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
375   ('DIR', 'I', 'include', 'include path'),
376   ('', 'M', 'dependencies', 'write dependencies'),
377   ('PREF', '',  'dep-prefix', 'prepend PREF before each -M dependency'),
378   ('', 'n', 'no-lily', 'don\'t run lilypond'),
379   ('', '', 'no-pictures', "don\'t generate pictures"),
380   ('', '', 'read-lys', "don't write ly files."),
381   ('FILE', 'o', 'outname', 'filename main output file'),
382   ('FILE', '', 'outdir', "where to place generated files"),
383   ('', 'v', 'version', 'print version information' ),
384   ('', 'h', 'help', 'print help'),
385   ]
386
387 # format specific strings, ie. regex-es for input, and % strings for output
388 output_dict= {
389         'latex': {
390                 'output-lilypond-fragment' : r"""\begin[eps,singleline,%s]{lilypond}
391   \context Staff <
392     \context Voice{
393       %s
394     }
395   >
396 \end{lilypond}""",
397                 'output-filename' : r'''
398
399 \verb+%s+:''',
400                 'output-lilypond': r"""\begin[%s]{lilypond}
401 %s
402 \end{lilypond}""",
403                 'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
404                 'output-default-post': "\\def\postLilypondExample{}\n",
405                 'output-default-pre': "\\def\preLilypondExample{}\n",
406                 'usepackage-graphics': '\\usepackage{graphics}\n',
407                 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
408                 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
409                 'pagebreak': r'\pagebreak',
410                 },
411         'texi' : {'output-lilypond': """@lilypond[%s]
412 %s
413 @end lilypond 
414 """,
415                 'output-filename' : r'''
416
417 @file{%s}:''',    
418                   'output-lilypond-fragment': """@lilypond[%s]
419 \context Staff\context Voice{ %s }
420 @end lilypond """,
421                   'pagebreak': None,
422                   'output-verbatim': r"""@example
423 %s
424 @end example
425 """,
426
427 # do some tweaking: @ is needed in some ps stuff.
428 # override EndLilyPondOutput, since @tex is done
429 # in a sandbox, you can't do \input lilyponddefs at the
430 # top of the document.
431
432 # should also support fragment in
433                   
434                   'output-all': r"""
435 @tex
436 \catcode`\@=12
437 \input lilyponddefs
438 \def\EndLilyPondOutput{}
439 \input %(fn)s.tex
440 \catcode`\@=0
441 @end tex
442 @html
443 <p>
444 <a href="%(fn)s.png">
445 <img border=0 src="%(fn)s.png" alt="[picture of music]">
446 </a>
447 @end html
448 """,
449                 }
450         }
451
452 def output_verbatim (body):
453         if __main__.format == 'texi':
454                 body = re.sub ('([@{}])', '@\\1', body)
455         return get_output ('output-verbatim') % body
456
457
458 re_dict = {
459         'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
460                   'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
461                   'option-sep' : ',\s*',
462                   'header': r"\\documentclass\s*(\[.*?\])?",
463                   'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
464                   'preamble-end': r'(?P<code>\\begin{document})',
465                   'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
466                   'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
467                   'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
468                   'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
469                   'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
470                   'def-post-re': r"\\def\\postLilypondExample",
471                   'def-pre-re': r"\\def\\preLilypondExample",             
472                   'usepackage-graphics': r"\usepackage{graphics}",
473                   'intertext': r',?\s*intertext=\".*?\"',
474                   'multiline-comment': no_match,
475                   'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
476                   'numcols': r"(?P<code>\\(?P<num>one|two)column)",
477                   },
478
479
480         # why do we have distinction between @mbinclude and @include?
481
482         
483         'texi': {
484                  'include':  '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
485                  'input': no_match,
486                  'header': no_match,
487                  'preamble-end': no_match,
488                  'landscape': no_match,
489                  'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
490                  'verb': r"""(?P<code>@code{.*?})""",
491                  'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
492                  'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
493                  'lilypond-block': r"""(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end lilypond)\s""",
494                   'option-sep' : ',\s*',
495                   'intertext': r',?\s*intertext=\".*?\"',
496                   'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
497                   'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
498                   'numcols': no_match,
499                  }
500         }
501
502
503 for r in re_dict.keys ():
504         olddict = re_dict[r]
505         newdict = {}
506         for k in olddict.keys ():
507                 try:
508                         newdict[k] = re.compile (olddict[k])
509                 except:
510                         print 'invalid regexp: %s' % olddict[k]
511
512                         # we'd like to catch and reraise a more detailed  error, but
513                         # alas, the exceptions changed across the 1.5/2.1 boundary.
514                         raise "Invalid re"
515         re_dict[r] = newdict
516
517         
518 def uniq (list):
519         list.sort ()
520         s = list
521         list = []
522         for x in s:
523                 if x not in list:
524                         list.append (x)
525         return list
526                 
527
528 def get_output (name):
529         return  output_dict[format][name]
530
531 def get_re (name):
532         return  re_dict[format][name]
533
534 def bounding_box_dimensions(fname):
535         if g_outdir:
536                 fname = os.path.join(g_outdir, fname)
537         try:
538                 fd = open(fname)
539         except IOError:
540                 error ("Error opening `%s'" % fname)
541         str = fd.read ()
542         s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
543         if s:
544                 
545                 gs = map (lambda x: string.atoi (x), s.groups ())
546                 return (int (gs[2] - gs[0] + 0.5),
547                         int (gs[3] - gs[1] + 0.5))
548         else:
549                 return (0,0)
550
551 def error (str):
552         sys.stderr.write (str + "\n  Exiting ... \n\n")
553         raise 'Exiting.'
554
555
556 def compose_full_body (body, opts):
557         """Construct the lilypond code to send to Lilypond.
558         Add stuff to BODY using OPTS as options."""
559         music_size = default_music_fontsize
560         latex_size = default_text_fontsize
561         for o in opts:
562                 if g_force_lilypond_fontsize:
563                         music_size = g_force_lilypond_fontsize
564                 else:
565                         m = re.match ('([0-9]+)pt', o)
566                         if m:
567                                 music_size = string.atoi(m.group (1))
568
569                 m = re.match ('latexfontsize=([0-9]+)pt', o)
570                 if m:
571                         latex_size = string.atoi (m.group (1))
572
573         if re.search ('\\\\score', body):
574                 is_fragment = 0
575         else:
576                 is_fragment = 1
577         if 'fragment' in opts:
578                 is_fragment = 1
579         if 'nofragment' in opts:
580                 is_fragment = 0
581
582         if is_fragment and not 'multiline' in opts:
583                 opts.append('singleline')
584         if 'singleline' in opts:
585                 l = -1.0;
586         else:
587                 l = __main__.paperguru.get_linewidth()
588
589         for o in opts:
590                 m= re.search ('relative(.*)', o)
591                 v = 0
592                 if m:
593                         try:
594                                 v = string.atoi (m.group (1))
595                         except ValueError:
596                                 pass
597
598                         v = v + 1
599                         pitch = 'c'
600                         if v < 0:
601                                 pitch = pitch + '\,' * v
602                         elif v > 0:
603                                 pitch = pitch + '\'' * v
604
605                         body = '\\relative %s { %s }' %(pitch, body)
606         
607         if is_fragment:
608                 body = r"""\score { 
609  \notes { %s }
610   \paper { }  
611 }""" % body
612
613         opts = uniq (opts)
614         optstring = string.join (opts, ' ')
615         optstring = re.sub ('\n', ' ', optstring)
616         body = r"""
617 %% Generated automatically by: lilypond-book.py
618 %% options are %s  
619 \include "paper%d.ly"
620 \paper  { linewidth = %f \pt } 
621 """ % (optstring, music_size, l) + body
622
623         # ughUGH not original options
624         return body
625
626 def parse_options_string(s):
627         d = {}
628         r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
629         r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
630         r3 = re.compile("(\w+?)((,\s*)|$)")
631         while s:
632                 m = r1.match(s)
633                 if m:
634                         s = s[m.end():]
635                         d[m.group(2)] = re.split(",\s*", m.group(3))
636                         continue
637                 m = r2.match(s)
638                 if m:
639                         s = s[m.end():]
640                         d[m.group(2)] = m.group(3)
641                         continue
642                 m = r3.match(s)
643                 if m:
644                         s = s[m.end():]
645                         d[m.group(1)] = 1
646                         continue
647                 
648                 error ("format of option string invalid (was `%')" % s)
649         return d
650
651 def scan_latex_preamble(chunks):
652         # first we want to scan the \documentclass line
653         # it should be the first non-comment line
654         idx = 0
655         while 1:
656                 if chunks[idx][0] == 'ignore':
657                         idx = idx + 1
658                         continue
659                 m = get_re ('header').match(chunks[idx][1])
660                 if m <> None and m.group (1):
661                         options = re.split (',[\n \t]*', m.group(1)[1:-1])
662                 else:
663                         options = []
664                 for o in options:
665                         if o == 'landscape':
666                                 paperguru.m_landscape = 1
667                         m = re.match("(.*?)paper", o)
668                         if m:
669                                 paperguru.m_papersize = m.group()
670                         else:
671                                 m = re.match("(\d\d)pt", o)
672                                 if m:
673                                         paperguru.m_fontsize = int(m.group(1))
674                 break
675         
676         while chunks[idx][0] != 'preamble-end':
677                 if chunks[idx] == 'ignore':
678                         idx = idx + 1
679                         continue
680                 m = get_re ('geometry').search(chunks[idx][1])
681                 if m:
682                         paperguru.m_use_geometry = 1
683                         o = parse_options_string(m.group('options'))
684                         for k in o.keys():
685                                 paperguru.set_geo_option(k, o[k])
686                 idx = idx + 1
687
688 def scan_texi_preamble (chunks):
689         # this is not bulletproof..., it checks the first 10 chunks
690         for c in chunks[:10]: 
691                 if c[0] == 'input':
692                         for s in ('afourpaper', 'afourwide', 'letterpaper',
693                                   'afourlatex', 'smallbook'):
694                                 if string.find(c[1], "@%s" % s) != -1:
695                                         paperguru.m_papersize = s
696
697 def scan_preamble (chunks):
698         if __main__.format == 'texi':
699                 scan_texi_preamble(chunks)
700         else:
701                 assert __main__.format == 'latex'
702                 scan_latex_preamble(chunks)
703                 
704
705 def completize_preamble (chunks):
706         if __main__.format == 'texi':
707                 return chunks
708         pre_b = post_b = graphics_b = None
709         for chunk in chunks:
710                 if chunk[0] == 'preamble-end':
711                         break
712                 if chunk[0] == 'input':
713                         m = get_re('def-pre-re').search(chunk[1])
714                         if m:
715                                 pre_b = 1
716                 if chunk[0] == 'input':
717                         m = get_re('def-post-re').search(chunk[1])
718                         if m:
719                                 post_b = 1
720                 if chunk[0] == 'input':
721                         m = get_re('usepackage-graphics').search(chunk[1])
722                         if m:
723                                 graphics_b = 1
724         x = 0
725         while chunks[x][0] != 'preamble-end':
726                 x = x + 1
727         if not pre_b:
728                 chunks.insert(x, ('input', get_output ('output-default-pre')))
729         if not post_b:
730                 chunks.insert(x, ('input', get_output ('output-default-post')))
731         if not graphics_b:
732                 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
733         return chunks
734
735
736 read_files = []
737 def find_file (name):
738         """
739         Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
740         """
741         
742         f = None
743         nm = ''
744         for a in include_path:
745                 try:
746                         nm = os.path.join (a, name)
747                         f = open (nm)
748                         __main__.read_files.append (nm)
749                         break
750                 except IOError:
751                         pass
752         if f:
753                 sys.stderr.write ("Reading `%s'\n" % nm)
754                 return (f.read (), nm)
755         else:
756                 error ("File not found `%s'\n" % name)
757                 return ('', '')
758
759 def do_ignore(match_object):
760         return [('ignore', match_object.group('code'))]
761 def do_preamble_end(match_object):
762         return [('preamble-end', match_object.group('code'))]
763
764 def make_verbatim(match_object):
765         return [('verbatim', match_object.group('code'))]
766
767 def make_verb(match_object):
768         return [('verb', match_object.group('code'))]
769
770 def do_include_file(m):
771         "m: MatchObject"
772         return [('input', get_output ('pagebreak'))] \
773              + read_doc_file(m.group('filename')) \
774              + [('input', get_output ('pagebreak'))] 
775
776 def do_input_file(m):
777         return read_doc_file(m.group('filename'))
778
779 def make_lilypond(m):
780         if m.group('options'):
781                 options = m.group('options')
782         else:
783                 options = ''
784         return [('input', get_output('output-lilypond-fragment') % 
785                         (options, m.group('code')))]
786
787 def make_lilypond_file(m):
788         """
789
790         Find @lilypondfile{bla.ly} occurences and substitute bla.ly
791         into a @lilypond .. @end lilypond block.
792         
793         """
794         
795         if m.group('options'):
796                 options = m.group('options')
797         else:
798                 options = ''
799         (content, nm) = find_file(m.group('filename'))
800         options = "filename=%s," % nm + options
801
802         return [('input', get_output('output-lilypond') %
803                         (options, content))]
804
805 def make_lilypond_block(m):
806         if m.group('options'):
807                 options = get_re('option-sep').split (m.group('options'))
808         else:
809             options = []
810         options = filter(lambda s: s != '', options)
811         return [('lilypond', m.group('code'), options)]
812
813 def do_columns(m):
814         if __main__.format != 'latex':
815                 return []
816         if m.group('num') == 'one':
817                 return [('numcols', m.group('code'), 1)]
818         if m.group('num') == 'two':
819                 return [('numcols', m.group('code'), 2)]
820         
821 def chop_chunks(chunks, re_name, func, use_match=0):
822     newchunks = []
823     for c in chunks:
824         if c[0] == 'input':
825             str = c[1]
826             while str:
827                 m = get_re (re_name).search (str)
828                 if m == None:
829                     newchunks.append (('input', str))
830                     str = ''
831                 else:
832                     if use_match:
833                         newchunks.append (('input', str[:m.start ('match')]))
834                     else:
835                         newchunks.append (('input', str[:m.start (0)]))
836                     #newchunks.extend(func(m))
837                     # python 1.5 compatible:
838                     newchunks = newchunks + func(m)
839                     str = str [m.end(0):]
840         else:
841             newchunks.append(c)
842     return newchunks
843
844 def determine_format (str):
845         if __main__.format == '':
846                 
847                 latex =  re.search ('\\\\document', str[:200])
848                 texinfo =  re.search ('@node|@setfilename', str[:200])
849
850                 f = ''
851                 g = None
852                 
853                 if texinfo and latex == None:
854                         f = 'texi'
855                 elif latex and texinfo == None: 
856                         f = 'latex'
857                 else:
858                         error("error: can't determine format, please specify")
859                 __main__.format = f
860
861         if __main__.paperguru == None:
862                 if __main__.format == 'texi':
863                         g = TexiPaper()
864                 else:
865                         g = LatexPaper()
866                         
867                 __main__.paperguru = g
868
869
870 def read_doc_file (filename):
871         """Read the input file, find verbatim chunks and do \input and \include
872         """
873         (str, path) = find_file(filename)
874         determine_format (str)
875         
876         chunks = [('input', str)]
877         
878         # we have to check for verbatim before doing include,
879         # because we don't want to include files that are mentioned
880         # inside a verbatim environment
881         chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
882         chunks = chop_chunks(chunks, 'verb', make_verb)
883         chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
884         #ugh fix input
885         chunks = chop_chunks(chunks, 'include', do_include_file, 1)
886         chunks = chop_chunks(chunks, 'input', do_input_file, 1)
887         return chunks
888
889
890 taken_file_names = {}
891 def schedule_lilypond_block (chunk):
892         """Take the body and options from CHUNK, figure out how the
893         real .ly should look, and what should be left MAIN_STR (meant
894         for the main file).  The .ly is written, and scheduled in
895         TODO.
896
897         Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
898
899         TODO has format [basename, extension, extension, ... ]
900         
901         """
902         (type, body, opts) = chunk
903         assert type == 'lilypond'
904         file_body = compose_full_body (body, opts)
905         basename = 'lily-' + `abs(hash (file_body))`
906         for o in opts:
907                 m = re.search ('filename="(.*?)"', o)
908                 if m:
909                         basename = m.group (1)
910                         if not taken_file_names.has_key(basename):
911                             taken_file_names[basename] = 0
912                         else:
913                             taken_file_names[basename] = taken_file_names[basename] + 1
914                             basename = basename + "-%i" % taken_file_names[basename]
915         if not g_read_lys:
916                 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
917         needed_filetypes = ['tex']
918
919         if format  == 'texi':
920                 needed_filetypes.append('eps')
921                 needed_filetypes.append('png')
922         if 'eps' in opts and not ('eps' in needed_filetypes):
923                 needed_filetypes.append('eps')
924         pathbase = os.path.join (g_outdir, basename)
925         def f(base, ext1, ext2):
926                 a = os.path.isfile(base + ext2)
927                 if (os.path.isfile(base + ext1) and
928                     os.path.isfile(base + ext2) and
929                                 os.stat(base+ext1)[stat.ST_MTIME] >
930                                 os.stat(base+ext2)[stat.ST_MTIME]) or \
931                                 not os.path.isfile(base + ext2):
932                         return 1
933         todo = []
934         if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
935                 todo.append('tex')
936         if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
937                 todo.append('eps')
938         if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
939                 todo.append('png')
940         newbody = ''
941
942         if 'printfilename' in opts:
943                 for o in opts:
944                         m= re.match ("filename=(.*)", o)
945                         if m:
946                                 newbody = newbody + get_output ("output-filename") % m.group(1)
947                                 break
948                 
949         
950         if 'verbatim' in opts:
951                 newbody = output_verbatim (body)
952
953         for o in opts:
954                 m = re.search ('intertext="(.*?)"', o)
955                 if m:
956                         newbody = newbody  + m.group (1) + "\n\n"
957         if format == 'latex':
958                 if 'eps' in opts:
959                         s = 'output-eps'
960                 else:
961                         s = 'output-tex'
962         else: # format == 'texi'
963                 s = 'output-all'
964         newbody = newbody + get_output (s) % {'fn': basename }
965         return ('lilypond', newbody, opts, todo, basename)
966
967 def process_lilypond_blocks(outname, chunks):#ugh rename
968         newchunks = []
969         # Count sections/chapters.
970         for c in chunks:
971                 if c[0] == 'lilypond':
972                         c = schedule_lilypond_block (c)
973                 elif c[0] == 'numcols':
974                         paperguru.m_num_cols = c[2]
975                 newchunks.append (c)
976         return newchunks
977
978
979
980 def system (cmd):
981         sys.stderr.write ("invoking `%s'\n" % cmd)
982         st = os.system (cmd)
983         if st:
984                 error ('Error command exited with value %d\n' % st)
985         return st
986
987
988 def get_bbox (filename):
989         f = open (filename)
990         gr = []
991         while 1:
992                 l =f.readline ()
993                 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', l)
994                 if m:
995                         gr = map (string.atoi, m.groups ())
996                         break
997         
998         return gr
999
1000 def make_pixmap (name):
1001         bbox = get_bbox (name + '.eps')
1002         margin = 3
1003         fo = open (name + '.trans.eps' , 'w')
1004         fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
1005         fo.close ()
1006         
1007         res = 90
1008
1009         x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1010         y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1011
1012         cmd = r"""gs -g%dx%d -sDEVICE=pgm  -dTextAlphaBits=4 -dGraphicsAlphaBits=4  -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s"""
1013         
1014         cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
1015         try:
1016                 status = system (cmd)
1017         except:
1018                 os.unlink (name + '.png')
1019                 error ("Removing output file")
1020
1021 def compile_all_files (chunks):
1022         global foutn
1023         eps = []
1024         tex = []
1025         png = []
1026
1027         for c in chunks:
1028                 if c[0] <> 'lilypond':
1029                         continue
1030                 base  = c[4]
1031                 exts = c[3]
1032                 for e in exts:
1033                         if e == 'eps':
1034                                 eps.append (base)
1035                         elif e == 'tex':
1036                                 #ugh
1037                                 if base + '.ly' not in tex:
1038                                         tex.append (base + '.ly')
1039                         elif e == 'png' and g_do_pictures:
1040                                 png.append (base)
1041         d = os.getcwd()
1042         if g_outdir:
1043                 os.chdir(g_outdir)
1044         if tex:
1045                 # fixme: be sys-independent.
1046                 def incl_opt (x):
1047                         if g_outdir and x[0] <> '/' :
1048                                 x = os.path.join (g_here_dir, x)
1049                         return ' -I %s' % x
1050
1051                 incs = map (incl_opt, include_path)
1052                 lilyopts = string.join (incs, ' ' )
1053                 if do_deps:
1054                         lilyopts = lilyopts + ' --dependencies '
1055                         if g_outdir:
1056                                 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1057                 texfiles = string.join (tex, ' ')
1058                 system ('lilypond --header=texidoc %s %s %s' % (lilyopts, g_extra_opts, texfiles))
1059
1060                 #
1061                 # Ugh, fixing up dependencies for .tex generation
1062                 #
1063                 if do_deps:
1064                         depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1065                         for i in depfiles:
1066                                 f =open (i)
1067                                 text=f.read ()
1068                                 f.close ()
1069                                 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1070                                 f = open (i, 'w')
1071                                 f.write (text)
1072                                 f.close ()
1073
1074         for e in eps:
1075                 system(r"tex '\nonstopmode \input %s'" % e)
1076                 system(r"dvips -E -o %s %s" % (e + '.eps', e))
1077                 
1078         for g in png:
1079                 make_pixmap (g)
1080                 
1081         os.chdir (d)
1082
1083
1084 def update_file (body, name):
1085         """
1086         write the body if it has changed
1087         """
1088         same = 0
1089         try:
1090                 f = open (name)
1091                 fs = f.read (-1)
1092                 same = (fs == body)
1093         except:
1094                 pass
1095
1096         if not same:
1097                 f = open (name , 'w')
1098                 f.write (body)
1099                 f.close ()
1100         
1101         return not same
1102
1103
1104 def getopt_args (opts):
1105         "Construct arguments (LONG, SHORT) for getopt from  list of options."
1106         short = ''
1107         long = []
1108         for o in opts:
1109                 if o[1]:
1110                         short = short + o[1]
1111                         if o[0]:
1112                                 short = short + ':'
1113                 if o[2]:
1114                         l = o[2]
1115                         if o[0]:
1116                                 l = l + '='
1117                         long.append (l)
1118         return (short, long)
1119
1120 def option_help_str (o):
1121         "Transform one option description (4-tuple ) into neatly formatted string"
1122         sh = '  '       
1123         if o[1]:
1124                 sh = '-%s' % o[1]
1125
1126         sep = ' '
1127         if o[1] and o[2]:
1128                 sep = ','
1129                 
1130         long = ''
1131         if o[2]:
1132                 long= '--%s' % o[2]
1133
1134         arg = ''
1135         if o[0]:
1136                 if o[2]:
1137                         arg = '='
1138                 arg = arg + o[0]
1139         return '  ' + sh + sep + long + arg
1140
1141
1142 def options_help_str (opts):
1143         "Convert a list of options into a neatly formatted string"
1144         w = 0
1145         strs =[]
1146         helps = []
1147
1148         for o in opts:
1149                 s = option_help_str (o)
1150                 strs.append ((s, o[3]))
1151                 if len (s) > w:
1152                         w = len (s)
1153
1154         str = ''
1155         for s in strs:
1156                 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0])  + 3), s[1])
1157         return str
1158
1159 def help():
1160         sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1161 Generate hybrid LaTeX input from Latex + lilypond
1162 Options:
1163 """)
1164         sys.stdout.write (options_help_str (option_definitions))
1165         sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1166
1167
1168
1169 Report bugs to bug-lilypond@gnu.org.
1170
1171 Written by Tom Cato Amundsen <tca@gnu.org> and
1172 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1173 """)
1174
1175         sys.exit (0)
1176
1177
1178 def write_deps (fn, target, chunks):
1179         global read_files
1180         sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1181         f = open (os.path.join(g_outdir, fn), 'w')
1182         f.write ('%s%s: ' % (g_dep_prefix, target))
1183         for d in read_files:
1184                 f.write ('%s ' %  d)
1185         basenames=[]
1186         for c in chunks:
1187                 if c[0] == 'lilypond':
1188                         (type, body, opts, todo, basename) = c;
1189                         basenames.append (basename)
1190         for d in basenames:
1191                 if g_outdir:
1192                         d=g_outdir + '/' + d
1193                 if g_dep_prefix:
1194                         #if not os.isfile (d): # thinko?
1195                         if not re.search ('/', d):
1196                                 d = g_dep_prefix + d
1197                 f.write ('%s.tex ' %  d)
1198         f.write ('\n')
1199         #if len (basenames):
1200         #       for d in basenames:
1201         #               f.write ('%s.ly ' %  d)
1202         #       f.write (' : %s' % target)
1203         f.write ('\n')
1204         f.close ()
1205         read_files = []
1206
1207 def identify():
1208         sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1209
1210 def print_version ():
1211         identify()
1212         sys.stdout.write (r"""Copyright 1998--1999
1213 Distributed under terms of the GNU General Public License. It comes with
1214 NO WARRANTY.
1215 """)
1216
1217
1218 def check_texidoc (chunks):
1219         n = []
1220         for c in chunks:
1221                 if c[0] == 'lilypond':
1222                         (type, body, opts, todo, basename) = c;
1223                         pathbase = os.path.join (g_outdir, basename)
1224                         if os.path.isfile (pathbase + '.texidoc'):
1225                                 body = '\n@include %s.texidoc\n' % basename + body
1226                                 c = (type, body, opts, todo, basename)
1227                 n.append (c)
1228         return n
1229
1230
1231 ## what's this? Docme --hwn
1232 ##
1233 def fix_epswidth (chunks):
1234         newchunks = []
1235         for c in chunks:
1236                 if c[0] <> 'lilypond' or 'eps' not in c[2]:
1237                         newchunks.append (c)
1238                         continue
1239
1240                 mag = 1.0
1241                 for o in c[2]:
1242                         m  = re.match ('magnification=([0-9.]+)', o)
1243                         if m:
1244                                 mag = string.atof (m.group (1))
1245
1246                 def replace_eps_dim (match, lmag = mag):
1247                         filename = match.group (1)
1248                         dims = bounding_box_dimensions (filename)
1249
1250                         return '%fpt' % (dims[0] *lmag)
1251         
1252                 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", replace_eps_dim, c[1])
1253                 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1254                         
1255         return newchunks
1256
1257
1258 foutn=""
1259 def do_file(input_filename):
1260         global foutn
1261         file_settings = {}
1262         if outname:
1263                 my_outname = outname
1264         else:
1265                 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1266         my_depname = my_outname + '.dep'                
1267
1268         chunks = read_doc_file(input_filename)
1269         chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1270         chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1271         chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1272         chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1273         chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1274         chunks = chop_chunks(chunks, 'numcols', do_columns)
1275         #print "-" * 50
1276         #for c in chunks: print "c:", c;
1277         #sys.exit()
1278         scan_preamble(chunks)
1279         chunks = process_lilypond_blocks(my_outname, chunks)
1280
1281         foutn = os.path.join (g_outdir, my_outname + '.' + format)
1282
1283         # Do It.
1284         if __main__.g_run_lilypond:
1285                 compile_all_files (chunks)
1286                 chunks = fix_epswidth (chunks)
1287
1288         if __main__.format == 'texi':
1289                 chunks = check_texidoc (chunks)
1290
1291         x = 0
1292         chunks = completize_preamble (chunks)
1293         sys.stderr.write ("Writing `%s'\n" % foutn)
1294         fout = open (foutn, 'w')
1295         for c in chunks:
1296                 fout.write (c[1])
1297         fout.close ()
1298         # should chmod -w
1299
1300         if do_deps:
1301                 write_deps (my_depname, foutn, chunks)
1302
1303
1304 outname = ''
1305 try:
1306         (sh, long) = getopt_args (__main__.option_definitions)
1307         (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1308 except getopt.error, msg:
1309         sys.stderr.write("error: %s" % msg)
1310         sys.exit(1)
1311
1312 do_deps = 0
1313 for opt in options:     
1314         o = opt[0]
1315         a = opt[1]
1316
1317         if o == '--include' or o == '-I':
1318                 include_path.append (a)
1319         elif o == '--version' or o == '-v':
1320                 print_version ()
1321                 sys.exit  (0)
1322         elif o == '--format' or o == '-f':
1323                 __main__.format = a
1324         elif o == '--outname' or o == '-o':
1325                 if len(files) > 1:
1326                         #HACK
1327                         sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1328                         sys.exit(1)
1329                 outname = a
1330         elif o == '--help' or o == '-h':
1331                 help ()
1332         elif o == '--no-lily' or o == '-n':
1333                 __main__.g_run_lilypond = 0
1334         elif o == '--dependencies' or o == '-M':
1335                 do_deps = 1
1336         elif o == '--default-music-fontsize':
1337                 default_music_fontsize = string.atoi (a)
1338         elif o == '--default-lilypond-fontsize':
1339                 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1340                 default_music_fontsize = string.atoi (a)
1341         elif o == '--extra-options':
1342                 g_extra_opts = a
1343         elif o == '--force-music-fontsize':
1344                 g_force_lilypond_fontsize = string.atoi(a)
1345         elif o == '--force-lilypond-fontsize':
1346                 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1347                 g_force_lilypond_fontsize = string.atoi(a)
1348         elif o == '--dep-prefix':
1349                 g_dep_prefix = a
1350         elif o == '--no-pictures':
1351                 g_do_pictures = 0
1352         elif o == '--read-lys':
1353                 g_read_lys = 1
1354         elif o == '--outdir':
1355                 g_outdir = a
1356
1357 identify()
1358 if g_outdir:
1359         if os.path.isfile(g_outdir):
1360                 error ("outdir is a file: %s" % g_outdir)
1361         if not os.path.exists(g_outdir):
1362                 os.mkdir(g_outdir)
1363 setup_environment ()
1364 for input_filename in files:
1365         do_file(input_filename)
1366         
1367 #
1368 # Petr, ik zou willen dat ik iets zinvoller deed,
1369 # maar wat ik kan ik doen, het verandert toch niets?
1370 #   --hwn 20/aug/99