]> git.donarmstrong.com Git - lilypond.git/blob - scripts/lilypond-book.py
release: 1.5.34
[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         'texi': {
482                  'include':  '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
483                  'input': no_match,
484                  'header': no_match,
485                  'preamble-end': no_match,
486                  'landscape': no_match,
487                  'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
488                  'verb': r"""(?P<code>@code{.*?})""",
489                  'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
490                  'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
491 # pyton2.2b2 barfs on this
492                  'lilypond-block': r"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
493
494 # 1.5.2 barfs on this. 
495 # 'lilypond-block': r"""(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s)""",
496                   'option-sep' : ',\s*',
497                   'intertext': r',?\s*intertext=\".*?\"',
498                   'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
499                   'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
500                   'numcols': no_match,
501                  }
502         }
503
504
505 for r in re_dict.keys ():
506         olddict = re_dict[r]
507         newdict = {}
508         for k in olddict.keys ():
509                 newdict[k] = re.compile (olddict[k])
510         re_dict[r] = newdict
511
512         
513 def uniq (list):
514         list.sort ()
515         s = list
516         list = []
517         for x in s:
518                 if x not in list:
519                         list.append (x)
520         return list
521                 
522
523 def get_output (name):
524         return  output_dict[format][name]
525
526 def get_re (name):
527         return  re_dict[format][name]
528
529 def bounding_box_dimensions(fname):
530         if g_outdir:
531                 fname = os.path.join(g_outdir, fname)
532         try:
533                 fd = open(fname)
534         except IOError:
535                 error ("Error opening `%s'" % fname)
536         str = fd.read ()
537         s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
538         if s:
539                 return (int (s.group (3) - s.group (1) + 0.5),
540                         int (s.group (4) - s.group (2) + 0.5))
541         else:
542                 return (0,0)
543
544 def error (str):
545         sys.stderr.write (str + "\n  Exiting ... \n\n")
546         raise 'Exiting.'
547
548
549 def compose_full_body (body, opts):
550         """Construct the lilypond code to send to Lilypond.
551         Add stuff to BODY using OPTS as options."""
552         music_size = default_music_fontsize
553         latex_size = default_text_fontsize
554         for o in opts:
555                 if g_force_lilypond_fontsize:
556                         music_size = g_force_lilypond_fontsize
557                 else:
558                         m = re.match ('([0-9]+)pt', o)
559                         if m:
560                                 music_size = string.atoi(m.group (1))
561
562                 m = re.match ('latexfontsize=([0-9]+)pt', o)
563                 if m:
564                         latex_size = string.atoi (m.group (1))
565
566         if re.search ('\\\\score', body):
567                 is_fragment = 0
568         else:
569                 is_fragment = 1
570         if 'fragment' in opts:
571                 is_fragment = 1
572         if 'nofragment' in opts:
573                 is_fragment = 0
574
575         if is_fragment and not 'multiline' in opts:
576                 opts.append('singleline')
577         if 'singleline' in opts:
578                 l = -1.0;
579         else:
580                 l = __main__.paperguru.get_linewidth()
581
582         for o in opts:
583                 m= re.search ('relative(.*)', o)
584                 v = 0
585                 if m:
586                         try:
587                                 v = string.atoi (m.group (1))
588                         except ValueError:
589                                 pass
590
591                         v = v + 1
592                         pitch = 'c'
593                         if v < 0:
594                                 pitch = pitch + '\,' * v
595                         elif v > 0:
596                                 pitch = pitch + '\'' * v
597
598                         body = '\\relative %s { %s }' %(pitch, body)
599         
600         if is_fragment:
601                 body = r"""\score { 
602  \notes { %s }
603   \paper { }  
604 }""" % body
605
606         opts = uniq (opts)
607         optstring = string.join (opts, ' ')
608         optstring = re.sub ('\n', ' ', optstring)
609         body = r"""
610 %% Generated automatically by: lilypond-book.py
611 %% options are %s  
612 \include "paper%d.ly"
613 \paper  { linewidth = %f \pt } 
614 """ % (optstring, music_size, l) + body
615
616         # ughUGH not original options
617         return body
618
619 def parse_options_string(s):
620         d = {}
621         r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
622         r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
623         r3 = re.compile("(\w+?)((,\s*)|$)")
624         while s:
625                 m = r1.match(s)
626                 if m:
627                         s = s[m.end():]
628                         d[m.group(2)] = re.split(",\s*", m.group(3))
629                         continue
630                 m = r2.match(s)
631                 if m:
632                         s = s[m.end():]
633                         d[m.group(2)] = m.group(3)
634                         continue
635                 m = r3.match(s)
636                 if m:
637                         s = s[m.end():]
638                         d[m.group(1)] = 1
639                         continue
640                 
641                 error ("format of option string invalid (was `%')" % s)
642         return d
643
644 def scan_latex_preamble(chunks):
645         # first we want to scan the \documentclass line
646         # it should be the first non-comment line
647         idx = 0
648         while 1:
649                 if chunks[idx][0] == 'ignore':
650                         idx = idx + 1
651                         continue
652                 m = get_re ('header').match(chunks[idx][1])
653                 if m <> None and m.group (1):
654                         options = re.split (',[\n \t]*', m.group(1)[1:-1])
655                 else:
656                         options = []
657                 for o in options:
658                         if o == 'landscape':
659                                 paperguru.m_landscape = 1
660                         m = re.match("(.*?)paper", o)
661                         if m:
662                                 paperguru.m_papersize = m.group()
663                         else:
664                                 m = re.match("(\d\d)pt", o)
665                                 if m:
666                                         paperguru.m_fontsize = int(m.group(1))
667                 break
668         
669         while chunks[idx][0] != 'preamble-end':
670                 if chunks[idx] == 'ignore':
671                         idx = idx + 1
672                         continue
673                 m = get_re ('geometry').search(chunks[idx][1])
674                 if m:
675                         paperguru.m_use_geometry = 1
676                         o = parse_options_string(m.group('options'))
677                         for k in o.keys():
678                                 paperguru.set_geo_option(k, o[k])
679                 idx = idx + 1
680
681 def scan_texi_preamble (chunks):
682         # this is not bulletproof..., it checks the first 10 chunks
683         for c in chunks[:10]: 
684                 if c[0] == 'input':
685                         for s in ('afourpaper', 'afourwide', 'letterpaper',
686                                   'afourlatex', 'smallbook'):
687                                 if string.find(c[1], "@%s" % s) != -1:
688                                         paperguru.m_papersize = s
689
690 def scan_preamble (chunks):
691         if __main__.format == 'texi':
692                 scan_texi_preamble(chunks)
693         else:
694                 assert __main__.format == 'latex'
695                 scan_latex_preamble(chunks)
696                 
697
698 def completize_preamble (chunks):
699         if __main__.format == 'texi':
700                 return chunks
701         pre_b = post_b = graphics_b = None
702         for chunk in chunks:
703                 if chunk[0] == 'preamble-end':
704                         break
705                 if chunk[0] == 'input':
706                         m = get_re('def-pre-re').search(chunk[1])
707                         if m:
708                                 pre_b = 1
709                 if chunk[0] == 'input':
710                         m = get_re('def-post-re').search(chunk[1])
711                         if m:
712                                 post_b = 1
713                 if chunk[0] == 'input':
714                         m = get_re('usepackage-graphics').search(chunk[1])
715                         if m:
716                                 graphics_b = 1
717         x = 0
718         while chunks[x][0] != 'preamble-end':
719                 x = x + 1
720         if not pre_b:
721                 chunks.insert(x, ('input', get_output ('output-default-pre')))
722         if not post_b:
723                 chunks.insert(x, ('input', get_output ('output-default-post')))
724         if not graphics_b:
725                 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
726         return chunks
727
728
729 read_files = []
730 def find_file (name):
731         """
732         Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
733         """
734         
735         f = None
736         nm = ''
737         for a in include_path:
738                 try:
739                         nm = os.path.join (a, name)
740                         f = open (nm)
741                         __main__.read_files.append (nm)
742                         break
743                 except IOError:
744                         pass
745         if f:
746                 sys.stderr.write ("Reading `%s'\n" % nm)
747                 return (f.read (), nm)
748         else:
749                 error ("File not found `%s'\n" % name)
750                 return ('', '')
751
752 def do_ignore(match_object):
753         return [('ignore', match_object.group('code'))]
754 def do_preamble_end(match_object):
755         return [('preamble-end', match_object.group('code'))]
756
757 def make_verbatim(match_object):
758         return [('verbatim', match_object.group('code'))]
759
760 def make_verb(match_object):
761         return [('verb', match_object.group('code'))]
762
763 def do_include_file(m):
764         "m: MatchObject"
765         return [('input', get_output ('pagebreak'))] \
766              + read_doc_file(m.group('filename')) \
767              + [('input', get_output ('pagebreak'))] 
768
769 def do_input_file(m):
770         return read_doc_file(m.group('filename'))
771
772 def make_lilypond(m):
773         if m.group('options'):
774                 options = m.group('options')
775         else:
776                 options = ''
777         return [('input', get_output('output-lilypond-fragment') % 
778                         (options, m.group('code')))]
779
780 def make_lilypond_file(m):
781         """
782
783         Find @lilypondfile{bla.ly} occurences and substitute bla.ly
784         into a @lilypond .. @end lilypond block.
785         
786         """
787         
788         if m.group('options'):
789                 options = m.group('options')
790         else:
791                 options = ''
792         (content, nm) = find_file(m.group('filename'))
793         options = "filename=%s," % nm + options
794
795         return [('input', get_output('output-lilypond') %
796                         (options, content))]
797
798 def make_lilypond_block(m):
799         if m.group('options'):
800                 options = get_re('option-sep').split (m.group('options'))
801         else:
802             options = []
803         options = filter(lambda s: s != '', options)
804         return [('lilypond', m.group('code'), options)]
805
806 def do_columns(m):
807         if __main__.format != 'latex':
808                 return []
809         if m.group('num') == 'one':
810                 return [('numcols', m.group('code'), 1)]
811         if m.group('num') == 'two':
812                 return [('numcols', m.group('code'), 2)]
813         
814 def chop_chunks(chunks, re_name, func, use_match=0):
815     newchunks = []
816     for c in chunks:
817         if c[0] == 'input':
818             str = c[1]
819             while str:
820                 m = get_re (re_name).search (str)
821                 if m == None:
822                     newchunks.append (('input', str))
823                     str = ''
824                 else:
825                     if use_match:
826                         newchunks.append (('input', str[:m.start ('match')]))
827                     else:
828                         newchunks.append (('input', str[:m.start (0)]))
829                     #newchunks.extend(func(m))
830                     # python 1.5 compatible:
831                     newchunks = newchunks + func(m)
832                     str = str [m.end(0):]
833         else:
834             newchunks.append(c)
835     return newchunks
836
837 def determine_format (str):
838         if __main__.format == '':
839                 
840                 latex =  re.search ('\\\\document', str[:200])
841                 texinfo =  re.search ('@node|@setfilename', str[:200])
842
843                 f = ''
844                 g = None
845                 
846                 if texinfo and latex == None:
847                         f = 'texi'
848                 elif latex and texinfo == None: 
849                         f = 'latex'
850                 else:
851                         error("error: can't determine format, please specify")
852                 __main__.format = f
853
854         if __main__.paperguru == None:
855                 if __main__.format == 'texi':
856                         g = TexiPaper()
857                 else:
858                         g = LatexPaper()
859                         
860                 __main__.paperguru = g
861
862
863 def read_doc_file (filename):
864         """Read the input file, find verbatim chunks and do \input and \include
865         """
866         (str, path) = find_file(filename)
867         determine_format (str)
868         
869         chunks = [('input', str)]
870         
871         # we have to check for verbatim before doing include,
872         # because we don't want to include files that are mentioned
873         # inside a verbatim environment
874         chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
875         chunks = chop_chunks(chunks, 'verb', make_verb)
876         chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
877         #ugh fix input
878         chunks = chop_chunks(chunks, 'include', do_include_file, 1)
879         chunks = chop_chunks(chunks, 'input', do_input_file, 1)
880         return chunks
881
882
883 taken_file_names = {}
884 def schedule_lilypond_block (chunk):
885         """Take the body and options from CHUNK, figure out how the
886         real .ly should look, and what should be left MAIN_STR (meant
887         for the main file).  The .ly is written, and scheduled in
888         TODO.
889
890         Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
891
892         TODO has format [basename, extension, extension, ... ]
893         
894         """
895         (type, body, opts) = chunk
896         assert type == 'lilypond'
897         file_body = compose_full_body (body, opts)
898         basename = 'lily-' + `abs(hash (file_body))`
899         for o in opts:
900                 m = re.search ('filename="(.*?)"', o)
901                 if m:
902                         basename = m.group (1)
903                         if not taken_file_names.has_key(basename):
904                             taken_file_names[basename] = 0
905                         else:
906                             taken_file_names[basename] = taken_file_names[basename] + 1
907                             basename = basename + "-%i" % taken_file_names[basename]
908         if not g_read_lys:
909                 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
910         needed_filetypes = ['tex']
911
912         if format  == 'texi':
913                 needed_filetypes.append('eps')
914                 needed_filetypes.append('png')
915         if 'eps' in opts and not ('eps' in needed_filetypes):
916                 needed_filetypes.append('eps')
917         pathbase = os.path.join (g_outdir, basename)
918         def f(base, ext1, ext2):
919                 a = os.path.isfile(base + ext2)
920                 if (os.path.isfile(base + ext1) and
921                     os.path.isfile(base + ext2) and
922                                 os.stat(base+ext1)[stat.ST_MTIME] >
923                                 os.stat(base+ext2)[stat.ST_MTIME]) or \
924                                 not os.path.isfile(base + ext2):
925                         return 1
926         todo = []
927         if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
928                 todo.append('tex')
929         if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
930                 todo.append('eps')
931         if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
932                 todo.append('png')
933         newbody = ''
934
935         if 'printfilename' in opts:
936                 for o in opts:
937                         m= re.match ("filename=(.*)", o)
938                         if m:
939                                 newbody = newbody + get_output ("output-filename") % m.group(1)
940                                 break
941                 
942         
943         if 'verbatim' in opts:
944                 newbody = output_verbatim (body)
945
946         for o in opts:
947                 m = re.search ('intertext="(.*?)"', o)
948                 if m:
949                         newbody = newbody  + m.group (1) + "\n\n"
950         if format == 'latex':
951                 if 'eps' in opts:
952                         s = 'output-eps'
953                 else:
954                         s = 'output-tex'
955         else: # format == 'texi'
956                 s = 'output-all'
957         newbody = newbody + get_output (s) % {'fn': basename }
958         return ('lilypond', newbody, opts, todo, basename)
959
960 def process_lilypond_blocks(outname, chunks):#ugh rename
961         newchunks = []
962         # Count sections/chapters.
963         for c in chunks:
964                 if c[0] == 'lilypond':
965                         c = schedule_lilypond_block (c)
966                 elif c[0] == 'numcols':
967                         paperguru.m_num_cols = c[2]
968                 newchunks.append (c)
969         return newchunks
970
971
972
973 def system (cmd):
974         sys.stderr.write ("invoking `%s'\n" % cmd)
975         st = os.system (cmd)
976         if st:
977                 error ('Error command exited with value %d\n' % st)
978         return st
979
980
981 def get_bbox (filename):
982         f = open (filename)
983         gr = []
984         while 1:
985                 l =f.readline ()
986                 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', l)
987                 if m:
988                         gr = map (string.atoi, m.groups ())
989                         break
990         
991         return gr
992
993 def make_pixmap (name):
994         bbox = get_bbox (name + '.eps')
995         margin = 3
996         fo = open (name + '.trans.eps' , 'w')
997         fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
998         fo.close ()
999         
1000         res = 90
1001
1002         x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1003         y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1004
1005         cmd = r"""gs -g%dx%d -sDEVICE=pgm  -dTextAlphaBits=4 -dGraphicsAlphaBits=4  -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s"""
1006         
1007         cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
1008         try:
1009                 status = system (cmd)
1010         except:
1011                 os.unlink (name + '.png')
1012                 error ("Removing output file")
1013
1014 def compile_all_files (chunks):
1015         global foutn
1016         eps = []
1017         tex = []
1018         png = []
1019
1020         for c in chunks:
1021                 if c[0] <> 'lilypond':
1022                         continue
1023                 base  = c[4]
1024                 exts = c[3]
1025                 for e in exts:
1026                         if e == 'eps':
1027                                 eps.append (base)
1028                         elif e == 'tex':
1029                                 #ugh
1030                                 if base + '.ly' not in tex:
1031                                         tex.append (base + '.ly')
1032                         elif e == 'png' and g_do_pictures:
1033                                 png.append (base)
1034         d = os.getcwd()
1035         if g_outdir:
1036                 os.chdir(g_outdir)
1037         if tex:
1038                 # fixme: be sys-independent.
1039                 def incl_opt (x):
1040                         if g_outdir and x[0] <> '/' :
1041                                 x = os.path.join (g_here_dir, x)
1042                         return ' -I %s' % x
1043
1044                 incs = map (incl_opt, include_path)
1045                 lilyopts = string.join (incs, ' ' )
1046                 if do_deps:
1047                         lilyopts = lilyopts + ' --dependencies '
1048                         if g_outdir:
1049                                 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1050                 texfiles = string.join (tex, ' ')
1051                 system ('lilypond --header=texidoc %s %s %s' % (lilyopts, g_extra_opts, texfiles))
1052
1053                 #
1054                 # Ugh, fixing up dependencies for .tex generation
1055                 #
1056                 if do_deps:
1057                         depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1058                         for i in depfiles:
1059                                 f =open (i)
1060                                 text=f.read ()
1061                                 f.close ()
1062                                 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1063                                 f = open (i, 'w')
1064                                 f.write (text)
1065                                 f.close ()
1066
1067         for e in eps:
1068                 system(r"tex '\nonstopmode \input %s'" % e)
1069                 system(r"dvips -E -o %s %s" % (e + '.eps', e))
1070                 
1071         for g in png:
1072                 make_pixmap (g)
1073                 
1074         os.chdir (d)
1075
1076
1077 def update_file (body, name):
1078         """
1079         write the body if it has changed
1080         """
1081         same = 0
1082         try:
1083                 f = open (name)
1084                 fs = f.read (-1)
1085                 same = (fs == body)
1086         except:
1087                 pass
1088
1089         if not same:
1090                 f = open (name , 'w')
1091                 f.write (body)
1092                 f.close ()
1093         
1094         return not same
1095
1096
1097 def getopt_args (opts):
1098         "Construct arguments (LONG, SHORT) for getopt from  list of options."
1099         short = ''
1100         long = []
1101         for o in opts:
1102                 if o[1]:
1103                         short = short + o[1]
1104                         if o[0]:
1105                                 short = short + ':'
1106                 if o[2]:
1107                         l = o[2]
1108                         if o[0]:
1109                                 l = l + '='
1110                         long.append (l)
1111         return (short, long)
1112
1113 def option_help_str (o):
1114         "Transform one option description (4-tuple ) into neatly formatted string"
1115         sh = '  '       
1116         if o[1]:
1117                 sh = '-%s' % o[1]
1118
1119         sep = ' '
1120         if o[1] and o[2]:
1121                 sep = ','
1122                 
1123         long = ''
1124         if o[2]:
1125                 long= '--%s' % o[2]
1126
1127         arg = ''
1128         if o[0]:
1129                 if o[2]:
1130                         arg = '='
1131                 arg = arg + o[0]
1132         return '  ' + sh + sep + long + arg
1133
1134
1135 def options_help_str (opts):
1136         "Convert a list of options into a neatly formatted string"
1137         w = 0
1138         strs =[]
1139         helps = []
1140
1141         for o in opts:
1142                 s = option_help_str (o)
1143                 strs.append ((s, o[3]))
1144                 if len (s) > w:
1145                         w = len (s)
1146
1147         str = ''
1148         for s in strs:
1149                 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0])  + 3), s[1])
1150         return str
1151
1152 def help():
1153         sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1154 Generate hybrid LaTeX input from Latex + lilypond
1155 Options:
1156 """)
1157         sys.stdout.write (options_help_str (option_definitions))
1158         sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1159
1160
1161
1162 Report bugs to bug-lilypond@gnu.org.
1163
1164 Written by Tom Cato Amundsen <tca@gnu.org> and
1165 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1166 """)
1167
1168         sys.exit (0)
1169
1170
1171 def write_deps (fn, target, chunks):
1172         global read_files
1173         sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1174         f = open (os.path.join(g_outdir, fn), 'w')
1175         f.write ('%s%s: ' % (g_dep_prefix, target))
1176         for d in read_files:
1177                 f.write ('%s ' %  d)
1178         basenames=[]
1179         for c in chunks:
1180                 if c[0] == 'lilypond':
1181                         (type, body, opts, todo, basename) = c;
1182                         basenames.append (basename)
1183         for d in basenames:
1184                 if g_outdir:
1185                         d=g_outdir + '/' + d
1186                 if g_dep_prefix:
1187                         #if not os.isfile (d): # thinko?
1188                         if not re.search ('/', d):
1189                                 d = g_dep_prefix + d
1190                 f.write ('%s.tex ' %  d)
1191         f.write ('\n')
1192         #if len (basenames):
1193         #       for d in basenames:
1194         #               f.write ('%s.ly ' %  d)
1195         #       f.write (' : %s' % target)
1196         f.write ('\n')
1197         f.close ()
1198         read_files = []
1199
1200 def identify():
1201         sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1202
1203 def print_version ():
1204         identify()
1205         sys.stdout.write (r"""Copyright 1998--1999
1206 Distributed under terms of the GNU General Public License. It comes with
1207 NO WARRANTY.
1208 """)
1209
1210
1211 def check_texidoc (chunks):
1212         n = []
1213         for c in chunks:
1214                 if c[0] == 'lilypond':
1215                         (type, body, opts, todo, basename) = c;
1216                         pathbase = os.path.join (g_outdir, basename)
1217                         if os.path.isfile (pathbase + '.texidoc'):
1218                                 body = '\n@include %s.texidoc\n' % basename + body
1219                                 c = (type, body, opts, todo, basename)
1220                 n.append (c)
1221         return n
1222
1223
1224 ## what's this? Docme --hwn
1225 ##
1226 def fix_epswidth (chunks):
1227         newchunks = []
1228         for c in chunks:
1229                 if c[0] <> 'lilypond' or 'eps' not in c[2]:
1230                         newchunks.append (c)
1231                         continue
1232
1233                 mag = 1.0
1234                 for o in c[2]:
1235                         m  = re.match ('magnification=([0-9.]+)', o)
1236                         if m:
1237                                 mag = string.atof (m.group (1))
1238
1239                 def replace_eps_dim (match, lmag = mag):
1240                         filename = match.group (1)
1241                         dims = bounding_box_dimensions (filename)
1242
1243                         return '%fpt' % (dims[0] *lmag)
1244         
1245                 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", replace_eps_dim, c[1])
1246                 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1247                         
1248         return newchunks
1249
1250
1251 foutn=""
1252 def do_file(input_filename):
1253         global foutn
1254         file_settings = {}
1255         if outname:
1256                 my_outname = outname
1257         else:
1258                 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1259         my_depname = my_outname + '.dep'                
1260
1261         chunks = read_doc_file(input_filename)
1262         chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1263         chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1264         chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1265         chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1266         chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1267         chunks = chop_chunks(chunks, 'numcols', do_columns)
1268         #print "-" * 50
1269         #for c in chunks: print "c:", c;
1270         #sys.exit()
1271         scan_preamble(chunks)
1272         chunks = process_lilypond_blocks(my_outname, chunks)
1273
1274         foutn = os.path.join (g_outdir, my_outname + '.' + format)
1275
1276         # Do It.
1277         if __main__.g_run_lilypond:
1278                 compile_all_files (chunks)
1279                 chunks = fix_epswidth (chunks)
1280
1281         if __main__.format == 'texi':
1282                 chunks = check_texidoc (chunks)
1283
1284         x = 0
1285         chunks = completize_preamble (chunks)
1286         sys.stderr.write ("Writing `%s'\n" % foutn)
1287         fout = open (foutn, 'w')
1288         for c in chunks:
1289                 fout.write (c[1])
1290         fout.close ()
1291         # should chmod -w
1292
1293         if do_deps:
1294                 write_deps (my_depname, foutn, chunks)
1295
1296
1297 outname = ''
1298 try:
1299         (sh, long) = getopt_args (__main__.option_definitions)
1300         (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1301 except getopt.error, msg:
1302         sys.stderr.write("error: %s" % msg)
1303         sys.exit(1)
1304
1305 do_deps = 0
1306 for opt in options:     
1307         o = opt[0]
1308         a = opt[1]
1309
1310         if o == '--include' or o == '-I':
1311                 include_path.append (a)
1312         elif o == '--version' or o == '-v':
1313                 print_version ()
1314                 sys.exit  (0)
1315         elif o == '--format' or o == '-f':
1316                 __main__.format = a
1317         elif o == '--outname' or o == '-o':
1318                 if len(files) > 1:
1319                         #HACK
1320                         sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1321                         sys.exit(1)
1322                 outname = a
1323         elif o == '--help' or o == '-h':
1324                 help ()
1325         elif o == '--no-lily' or o == '-n':
1326                 __main__.g_run_lilypond = 0
1327         elif o == '--dependencies' or o == '-M':
1328                 do_deps = 1
1329         elif o == '--default-music-fontsize':
1330                 default_music_fontsize = string.atoi (a)
1331         elif o == '--default-lilypond-fontsize':
1332                 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1333                 default_music_fontsize = string.atoi (a)
1334         elif o == '--extra-options':
1335                 g_extra_opts = a
1336         elif o == '--force-music-fontsize':
1337                 g_force_lilypond_fontsize = string.atoi(a)
1338         elif o == '--force-lilypond-fontsize':
1339                 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1340                 g_force_lilypond_fontsize = string.atoi(a)
1341         elif o == '--dep-prefix':
1342                 g_dep_prefix = a
1343         elif o == '--no-pictures':
1344                 g_do_pictures = 0
1345         elif o == '--read-lys':
1346                 g_read_lys = 1
1347         elif o == '--outdir':
1348                 g_outdir = a
1349
1350 identify()
1351 if g_outdir:
1352         if os.path.isfile(g_outdir):
1353                 error ("outdir is a file: %s" % g_outdir)
1354         if not os.path.exists(g_outdir):
1355                 os.mkdir(g_outdir)
1356 setup_environment ()
1357 for input_filename in files:
1358         do_file(input_filename)
1359         
1360 #
1361 # Petr, ik zou willen dat ik iets zinvoller deed,
1362 # maar wat ik kan ik doen, het verandert toch niets?
1363 #   --hwn 20/aug/99