]> git.donarmstrong.com Git - lilypond.git/blob - scripts/lilypond-book.py
* stepmake/stepmake/omf-targets.make (local-install): add
[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 # * texinfo: add support for @pagesize
8
9 # todo: dimension handling (all the x2y) is clumsy. (tca: Thats
10 #       because the values are taken directly from texinfo.tex,
11 #       geometry.sty and article.cls. Give me a hint, and I'll
12 #       fix it.)
13
14 #
15 # TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips.
16 #
17
18
19 #
20 # This is a slightly hairy program. The general approach is as follows 
21 # The input string is chopped up in chunks, i.e. ,  a list of tuples
22 #
23 #   with the format  (TAG_STR, MAIN_STR, OPTIONS, TODO, BASE)
24 #
25 # This list is build step by step: first ignore and verbatim commands are handled,
26 # delivering a list of chunks.
27
28 # then all chunks containing lilypnod commands are chopped up
29 #
30 # when all chunks have their final form, all bodies from lilypond blocks are 
31 # extracted, and if applicable, written do disk and run through lilypond.
32
33
34
35 # This is was the idea for handling of comments:
36
37
38 #       Multiline comments, @ignore .. @end ignore is scanned for
39 #       in read_doc_file, and the chunks are marked as 'ignore', so
40 #       lilypond-book will not touch them any more. The content of the
41 #       chunks are written to the output file. Also 'include' and 'input'
42 #       regex has to check if they are commented out.
43 #
44
45 #       Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
46 #       These three regex's has to check if they are on a commented line,
47 #       % for latex, @c for texinfo.
48 #
49 #       Then lines that are commented out with % (latex) and @c (Texinfo)
50 #       are put into chunks marked 'ignore'. This cannot be done before
51 #       searching for the lilypond-blocks because % is also the comment character
52 #       for lilypond.
53 #
54 #       The the rest of the rexeces are searched for. They don't have to test
55 #       if they are on a commented out line.
56
57
58 import stat
59 import string
60
61
62 ################################################################
63 # Users of python modules should include this snippet
64 # and customize variables below.
65
66 # We'll suffer this path init stuff as long as we don't install our
67 # python packages in <prefix>/lib/pythonx.y (and don't kludge around
68 # it as we do with teTeX on Red Hat Linux: set some environment var
69 # (PYTHONPATH) in profile)
70
71 # If set, LILYPONDPREFIX must take prevalence
72 # if datadir is not set, we're doing a build and LILYPONDPREFIX
73 import getopt, os, sys
74 datadir = '@local_lilypond_datadir@'
75 if not os.path.isdir (datadir):
76         datadir = '@lilypond_datadir@'
77 if os.environ.has_key ('LILYPONDPREFIX') :
78         datadir = os.environ['LILYPONDPREFIX']
79         while datadir[-1] == os.sep:
80                 datadir= datadir[:-1]
81
82 sys.path.insert (0, os.path.join (datadir, 'python'))
83
84 # Customize these
85 #if __name__ == '__main__':
86
87 import lilylib as ly
88 global _;_=ly._
89 global re;re = ly.re
90
91 # lilylib globals
92 program_name = 'lilypond-book'
93 verbose_p = 0
94 pseudo_filter_p = 0
95 original_dir = os.getcwd ()
96 #temp_dir = os.path.join (original_dir,  '%s.dir' % program_name)
97 #urg
98 temp_dir = '/tmp'
99 keep_temp_dir_p = 0
100 preview_resolution = 90
101
102 ## FIXME
103 ## ly2dvi: silly name?
104 ## do -P or -p by default?
105 ##help_summary = _ ("Run LilyPond using LaTeX for titling")
106 help_summary = _ ("Process LilyPond snippets in hybrid html, LaTeX or texinfo document")
107 copyright = ('Tom Cato Amundsen <tca@gnu.org>',
108              'Han-Wen Nienhuys <hanwen@cs.uu.nl>')
109
110 option_definitions = [
111         (_ ("EXT"), 'f', 'format', _ ("use output format EXT (texi [default], texi-html, latex, html)")),
112         (_ ("DIM"),  '', 'default-music-fontsize', _ ("default fontsize for music.  DIM is assumed to be in points")),
113         (_ ("DIM"),  '', 'default-lilypond-fontsize', _ ("deprecated, use --default-music-fontsize")),
114         (_ ("OPT"), '', 'extra-options', _ ("pass OPT quoted to the lilypond command line")),
115         (_ ("DIM"), '', 'force-music-fontsize', _ ("force fontsize for all inline lilypond. DIM is assumed be to in points")),
116         (_ ("DIM"), '', 'force-lilypond-fontsize', _ ("deprecated, use --force-music-fontsize")),
117         ('', 'h', 'help', _ ("this help")),
118         (_ ("DIR"), 'I', 'include', _ ("include path")),
119         ('', 'M', 'dependencies', _ ("write dependencies")),
120         (_ ("PREF"), '',  'dep-prefix', _ ("prepend PREF before each -M dependency")),
121         ('', 'n', 'no-lily', _ ("don't run lilypond")),
122         ('', '', 'no-pictures', _ ("don't generate pictures")),
123         ('', '', 'no-music', _ ("strip all lilypond blocks from output")),
124         ('', '', 'read-lys', _ ("don't write ly files.")),
125         (_ ("FILE"), 'o', 'outname', _ ("filename main output file")),
126         (_ ("FILE"), '', 'outdir', _ ("where to place generated files")),
127         (_ ('RES'), '', 'preview-resolution',
128          _ ("set the resolution of the preview to RES")),
129         ('', 'V', 'verbose', _ ("verbose")),
130         ('', 'v', 'version', _ ("print version information")),
131         ('', 'w', 'warranty', _ ("show warranty and copyright")),
132         ]
133
134 # format specific strings, ie. regex-es for input, and % strings for output
135
136 # global variables
137
138 include_path = [os.getcwd ()]
139
140
141 #lilypond_binary = 'valgrind --suppressions=/home/hanwen/usr/src/guile-1.6.supp  --num-callers=10 /home/hanwen/usr/src/lilypond/lily/out/lilypond'
142
143 lilypond_binary = os.path.join ('@bindir@', 'lilypond')
144
145 # only use installed binary  when we're installed too.
146 if '@bindir@' == ('@' + 'bindir@') or not os.path.exists (lilypond_binary):
147         lilypond_binary = 'lilypond'
148
149
150
151 ly2dvi_binary = os.path.join ('@bindir@', 'ly2dvi')
152
153 # only use installed binary  when we're installed too.
154 if '@bindir@' == ('@' + 'bindir@') or not os.path.exists (lilypond_binary):
155         lilypond_binary = 'ly2dvi'
156
157
158
159 g_extra_opts = ''
160 g_here_dir = os.getcwd ()
161 g_dep_prefix = ''
162 g_outdir = ''
163 g_force_music_fontsize = 0
164 g_read_lys = 0
165 g_do_pictures = 1
166 g_do_music = 1
167 g_make_html = 0
168
169 format = ''
170 g_run_lilypond = 1
171 no_match = 'a\ba'
172
173 default_music_fontsize = 16
174 default_text_fontsize = 12
175 paperguru = None
176
177 ################################################################
178 # Dimension handling for LaTeX.
179
180 class LatexPaper:
181         def __init__ (self):
182                 self.m_document_preamble = []
183                 self.m_num_cols = 1
184                 self.m_multicols = 1
185
186         def find_latex_dims (self):
187                 if g_outdir:
188                         fname = os.path.join (g_outdir, "lily-tmp.tex")
189                 else:
190                         fname = "lily-tmp.tex"
191                 try:
192                         f = open (fname, "w")
193                 except IOError:
194                         error ("Error creating temporary file '%s'" % fname)
195
196                 for s in self.m_document_preamble:
197                         f.write (s)
198                 f.write (r"""
199 \begin{document}
200 \typeout{---}
201 \typeout{\columnsep \the\columnsep}
202 \typeout{\textwidth \the\textwidth}
203 \typeout{---}
204 \end{document}
205                 """)
206                 f.close ()
207                 re_dim = re.compile (r"\\(\w+)\s+(\d+\.\d+)")
208
209                 cmd = "latex '\\nonstopmode \input %s'" % fname
210                 # Ugh.  (La)TeX writes progress and error messages on stdout
211                 # Redirect to stderr
212                 cmd += ' 1>/dev/stderr'
213                 status = ly.system (cmd, ignore_error = 1)
214                 signal = 0xf & status
215                 exit_status = status >> 8
216                 
217                 if status:
218                         ly.error (_ ("LaTeX failed."))
219                         ly.error (_ ("The error log is as follows:"))
220                         
221                         #URG see ly2dvi
222                         try:
223                                 lns = open ('lily-tmp.log').readlines ()
224                         except:
225                                 lns = ''
226                         countdown = -3
227                         for ln in lns:
228                                 sys.stderr.write (ln)
229                                 if re.match ('^!', ln):
230                                         countdown = 3
231
232                                 if countdown == 0:
233                                         break
234
235                                 if countdown > 0:
236                                         countdown = countdown -1
237
238                         sys.stderr.write ("  ... (further messages elided)...\n")
239                         sys.exit (1)
240
241                 lns = open ('lily-tmp.log').readlines ()
242                 for ln in lns:
243                         ln = string.strip (ln)
244                         m = re_dim.match (ln)
245                         if m:
246                                 if m.groups ()[0] in ('textwidth', 'columnsep'):
247                                         self.__dict__['m_%s' % m.groups ()[0]] = float (m.groups ()[1])
248
249                 try:
250                         os.remove (fname)
251                         os.remove (os.path.splitext (fname)[0]+".aux")
252                         os.remove (os.path.splitext (fname)[0]+".log")
253                 except:
254                         pass
255
256                 if not self.__dict__.has_key ('m_textwidth'):
257                         raise 'foo!'
258
259         def get_linewidth (self):
260                 if self.m_num_cols == 1:
261                         w = self.m_textwidth
262                 else:
263                         w = (self.m_textwidth - self.m_columnsep)/2
264                 if self.m_multicols > 1:
265                         return (w - self.m_columnsep* (self.m_multicols-1)) \
266                            / self.m_multicols
267                 return w
268
269
270 class HtmlPaper:
271         def __init__ (self):
272                 self.m_papersize = 'letterpaper'
273                 self.m_fontsize = 12
274         def get_linewidth (self):
275                 return html_linewidths[self.m_papersize][self.m_fontsize]
276
277 class TexiPaper:
278         def __init__ (self):
279                 self.m_papersize = 'letterpaper'
280                 self.m_fontsize = 12
281         def get_linewidth (self):
282                 return texi_linewidths[self.m_papersize][self.m_fontsize]
283
284 def mm2pt (x):
285         return x * 2.8452756
286 def in2pt (x):
287         return x * 72.26999
288 def em2pt (x, fontsize = 10):
289         return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
290 def ex2pt (x, fontsize = 10):
291         return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
292
293 def pt2pt (x):
294         return x
295
296 dimension_conversion_dict ={
297         'mm': mm2pt,
298         'cm': lambda x: mm2pt (10*x),
299         'in': in2pt,
300         'em': em2pt,
301         'ex': ex2pt,
302         'pt': pt2pt
303         }
304
305 # Convert numeric values, with or without specific dimension, to floats.
306 # Keep other strings
307 def conv_dimen_to_float (value):
308         if type (value) == type (""):
309                 m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
310                 if m:
311                         unit = m.group (2)
312                         num = string.atof (m.group (1))
313                         conv =  dimension_conversion_dict[m.group (2)]
314
315                         value = conv (num)
316
317                 elif re.match ("^[0-9.]+$",value):
318                         value = float (value)
319
320         return value
321
322 texi_linewidths = {
323         'afourpaper': {12: mm2pt (160)},
324         'afourwide': {12: in2pt (6.5)},
325         'afourlatex': {12: mm2pt (150)},
326         'smallbook': {12: in2pt (5)},
327         'letterpaper': {12: in2pt (6)}}
328
329 html_linewidths = {
330         'afourpaper': {12: mm2pt (160)},
331         'afourwide': {12: in2pt (6.5)},
332         'afourlatex': {12: mm2pt (150)},
333         'smallbook': {12: in2pt (5)},
334         'letterpaper': {12: in2pt (6)}}
335
336
337 ################################################################
338 # How to output various structures. 
339 output_dict= {
340
341
342         'html' : {
343
344                 'output-lilypond': '''<lilypond%s>
345 %s
346 </lilypond>''',
347                 'output-filename' : r'''
348 <!-- %s >
349 <a href="%s">
350 <pre>%s</pre></a>:''',
351                 'output-lilypond-fragment': '''<lilypond%s>
352 \context Staff\context Voice{ %s }
353 </lilypond>''',
354                 'output-noinline': r'''
355 <!-- generated: %(fn)s.png !-->
356 ''',
357                 ## maybe <hr> ?
358                 'pagebreak': None,
359                 # Verbatim text is always finished with \n.  FIXME: For HTML,
360                 # this newline should be removed.
361                 'output-verbatim': r'''<pre>
362 %s</pre>''',
363                 # Verbatim text is always finished with \n.  FIXME: For HTML,
364                 # this newline should be removed.
365                 'output-small-verbatim': r'''<font size=-1><pre>
366 %s</pre></font>''',
367                 ## Ugh we need to differentiate on origin:
368                 ## lilypond-block origin wants an extra <p>, but
369                 ## inline music doesn't.
370                 ## possibly other center options?
371                 'output-html': r'''
372 <a href="%(fn)s.png">
373 <img align="center" valign="center" border="0" src="%(fn)s.png" alt="[picture of music]"></a>
374 ''',
375                 },
376
377
378         'latex': {
379
380                 'output-lilypond-fragment' : r'''\begin[eps,singleline,%s]{lilypond}
381   \context Staff <
382     \context Voice{
383       %s
384     }
385   >
386 \end{lilypond}''',
387                 'output-filename' : r'''\verb+%s+:\\
388 %% %s
389 %% %s
390 ''',
391                 'output-lilypond': r'''\begin[%s]{lilypond}
392 %s
393 \end{lilypond}
394 ''',
395                 # verbatim text is always finished with \n
396                 'output-verbatim': r'''\begin{verbatim}
397 %s\end{verbatim}
398 ''',
399                 # verbatim text is always finished with \n
400                 'output-small-verbatim': r'''{\small\begin{verbatim}
401 %s\end{verbatim}}
402 ''',
403                 'output-default-post': "\\def\postLilypondExample{}\n",
404                 'output-default-pre': "\\def\preLilypondExample{}\n",
405                 'usepackage-graphics': '\\usepackage{graphics}\n',
406                 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s}}',
407                 'output-noinline': r'''
408 %% generated: %(fn)s.eps
409 ''',
410                 'output-latex-quoted': r'''{\preLilypondExample
411 \input %(fn)s.tex
412 \postLilypondExample}''',
413                 'output-latex-noquote': r'''{\parindent 0pt
414 \preLilypondExample
415 \input %(fn)s.tex
416 \postLilypondExample}''',
417                 'pagebreak': r'\pagebreak',
418                 },
419
420
421         'texi' : {
422
423                 'output-lilypond': '''@lilypond[%s]
424 %s
425 @end lilypond
426 ''',
427                 'output-filename' : r'''@ifnothtml
428 @file{%s}:@*
429 @end ifnothtml
430 @ifhtml
431 @uref{%s,@file{%s}}
432 @end ifhtml
433 ''',
434                 'output-lilypond-fragment': '''@lilypond[%s]
435 \context Staff\context Voice{ %s }
436 @end lilypond ''',
437                 'output-noinline': r'''
438 @c generated: %(fn)s.png
439 ''',
440                 'pagebreak': None,
441                 # verbatim text is always finished with \n
442                 'output-small-verbatim': r'''@smallexample
443 %s@end smallexample
444 ''',
445                 # verbatim text is always finished with \n
446                 'output-verbatim': r'''@example
447 %s@end example
448 ''',
449                 # do some tweaking: @ is needed in some ps stuff.
450                 #
451                 # ugh, the <p> below breaks inline images...
452                 'output-texi-noquote': r'''@tex
453 \catcode`\@=12
454 \parindent 0pt
455 \def\lilypondbook{}
456 \input %(fn)s.tex
457 \catcode`\@=0
458 @end tex
459 @html
460 <p><a href="%(fn)s.png">
461 <img border=0 src="%(fn)s.png" alt="[picture of music]">
462 </a><p>
463 @end html
464 ''',
465                 'output-texi-quoted': r'''@quotation
466 @tex
467 \catcode`\@=12
468 \def\lilypondbook{}
469 \input %(fn)s.tex
470 \catcode`\@=0
471 @end tex
472 @html
473 <a href="%(fn)s.png">
474 <img border=0 src="%(fn)s.png" alt="[picture of music]">
475 </a>
476 @end html
477 @end quotation
478 ''',
479                 }
480
481         }
482
483 def output_verbatim (body, small):
484         global format
485         if format == 'html':
486                 body = re.sub ('&', '&amp;', body)
487                 body = re.sub ('>', '&gt;', body)
488                 body = re.sub ('<', '&lt;', body)
489         elif format == 'texi':
490                 # clumsy workaround for python 2.2 pre bug.
491                 body = re.sub ('@', '@@', body)
492                 body = re.sub ('{', '@{', body)
493                 body = re.sub ('}', '@}', body)
494
495         if small:
496                 key = 'output-small-verbatim'
497         else:
498                 key = 'output-verbatim'
499         return get_output (key) % body
500
501
502 ################################################################
503 # Recognize special sequences in the input 
504
505
506 # Warning: This uses extended regular expressions.  Tread with care.
507 #
508 # legenda
509 #
510 # (?P<name>regex) -- assign result of REGEX to NAME
511 # *? -- match non-greedily.
512 # (?m) -- multiline regex: make ^ and $ match at each line
513 # (?s) -- make the dot match all characters including newline
514 re_dict = {
515         'html': {
516                 'include':  no_match,
517                 'input': no_match,
518                 'header': no_match,
519                 'preamble-end': no_match,
520                 'landscape': no_match,
521                 'verbatim': r'''(?s)(?P<code><pre>\s.*?</pre>\s)''',
522                 'verb': r'''(?P<code><pre>.*?</pre>)''',
523                 'lilypond-file': r'(?m)(?P<match><lilypondfile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</lilypondfile>)',
524                 'lilypond' : '(?m)(?P<match><lilypond((?P<options>[^:]*):)(?P<code>.*?)/>)',
525                 'lilypond-block': r'''(?ms)(?P<match><lilypond(?P<options>[^>]+)?>(?P<code>.*?)</lilypond>)''',
526                 'option-sep' : '\s*',
527                 'intertext': r',?\s*intertext=\".*?\"',
528                 'multiline-comment': r"(?sm)\s*(?!@c\s+)(?P<code><!--\s.*?!-->)\s",
529                 'singleline-comment': no_match,
530                 'numcols': no_match,
531                 'multicols': no_match,
532                 'ly2dvi': r'(?m)(?P<match><ly2dvifile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</ly2dvifile>)',
533                 },
534
535         'latex': {
536                 'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
537                 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
538                 'option-sep' : ',\s*',
539                 'header': r"\n*\\documentclass\s*(\[.*?\])?",
540                 'preamble-end': r'(?P<code>\\begin\s*{document})',
541                 'verbatim': r"(?s)(?P<code>\\begin\s*{verbatim}.*?\\end{verbatim})",
542                 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
543                 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
544                 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
545                 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
546                 'def-post-re': r"\\def\\postLilypondExample",
547                 'def-pre-re': r"\\def\\preLilypondExample",
548                 'usepackage-graphics': r"\usepackage\s*{graphics}",
549                 'intertext': r',?\s*intertext=\".*?\"',
550                 'multiline-comment': no_match,
551                 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
552                 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
553                 'multicols': r"(?P<code>\\(?P<be>begin|end)\s*{multicols}({(?P<num>\d+)?})?)",
554                 'ly2dvi': no_match,
555
556                 },
557
558         # why do we have distinction between @mbinclude and @include?
559
560         'texi': {
561                 'include':  '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
562                 'input': no_match,
563                 'header': no_match,
564                 'preamble-end': no_match,
565                 'landscape': no_match,
566                 'verbatim': r'''(?s)(?P<code>@example\s.*?@end example\s)''',
567                 'verb': r'''(?P<code>@code{.*?})''',
568                 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
569                 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
570                 'lilypond-block': r'''(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end +lilypond)\s''',
571                 'option-sep' : ',\s*',
572                 'intertext': r',?\s*intertext=\".*?\"',
573                 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
574                 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
575                 'numcols': no_match,
576                 'multicols': no_match,
577                 'ly2dvi': no_match,
578                 }
579         }
580
581
582 for r in re_dict.keys ():
583         olddict = re_dict[r]
584         newdict = {}
585         for k in olddict.keys ():
586                 try:
587                         newdict[k] = re.compile (olddict[k])
588                 except:
589                         print 'invalid regexp: %s' % olddict[k]
590
591                         ## we'd like to catch and reraise a more
592                         ## detailed error, but alas, the exceptions
593                         ## changed across the 1.5/2.1 boundary.
594
595                         raise "Invalid re"
596         re_dict[r] = newdict
597
598
599 def uniq (list):
600         list.sort ()
601         s = list
602         list = []
603         for x in s:
604                 if x not in list:
605                         list.append (x)
606         return list
607
608
609 def get_output (name):
610         return  output_dict[format][name]
611
612 def get_re (name):
613         return  re_dict[format][name]
614
615 def bounding_box_dimensions (fname):
616         if g_outdir:
617                 fname = os.path.join (g_outdir, fname)
618         try:
619                 fd = open (fname)
620         except IOError:
621                 error ("Error opening `%s'" % fname)
622         str = fd.read ()
623         s = re.search ('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
624         if s:
625
626                 gs = map (lambda x: string.atoi (x), s.groups ())
627                 return (int (gs[2] - gs[0] + 0.5),
628                         int (gs[3] - gs[1] + 0.5))
629         else:
630                 return (0,0)
631
632 def error (str):
633         sys.stderr.write ("\n\n" + str + "\nExiting ... \n\n")
634         raise 'Exiting.'
635
636
637 def compose_full_body (body, opts):
638         '''Construct the lilypond code to send to Lilypond.
639         Add stuff to BODY using OPTS as options.'''
640         music_size = default_music_fontsize
641         if g_force_music_fontsize:
642                 music_size = g_force_music_fontsize
643         indent = ''
644         linewidth = ''
645         notime = ''
646         for o in opts:
647                 if not g_force_music_fontsize:
648                         m = re.match ('([0-9]+)pt', o)
649                         if m:
650                                 music_size = string.atoi (m.group (1))
651
652                 m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o)
653                 if m:
654                         f = float (m.group (1))
655                         indent = 'indent = %f\\%s' % (f, m.group (2))
656
657                 m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o)
658                 if m:
659                         f = float (m.group (1))
660                         linewidth = 'linewidth = %f\\%s' % (f, m.group (2))
661
662         if re.search ('\\\\score', body):
663                 is_fragment = 0
664         else:
665                 is_fragment = 1
666         if 'fragment' in opts:
667                 is_fragment = 1
668         if 'nofragment' in opts:
669                 is_fragment = 0
670
671         if is_fragment and not 'multiline' in opts:
672                 opts.append ('singleline')
673
674         if 'singleline' in opts:
675                 if not linewidth:
676                         linewidth = 'linewidth = -1.0'
677                 if not indent:
678                         indent = 'indent = 0.0\mm'
679         elif not linewidth:
680                 global paperguru
681                 l = paperguru.get_linewidth ()
682                 linewidth = 'linewidth = %f\pt' % l
683
684         if 'noindent' in opts:
685                 indent = 'indent = 0.0\mm'
686
687         if 'notime' in opts:
688                 notime = r'''
689 \translator {
690   \StaffContext
691   \remove Time_signature_engraver
692 }
693 '''
694
695         for o in opts:
696                 m= re.search ('relative(.*)', o)
697                 v = 0
698                 if m:
699                         try:
700                                 v = string.atoi (m.group (1))
701                         except ValueError:
702                                 pass
703
704                         v = v + 1
705                         pitch = 'c'
706                         if v < 0:
707                                 pitch = pitch + '\,' * v
708                         elif v > 0:
709                                 pitch = pitch + '\'' * v
710
711                         body = '\\relative %s { %s }' % (pitch, body)
712
713         if is_fragment:
714                 body = r'''
715 \score {
716   \notes {
717 %s
718   }
719 }
720 ''' % body
721
722         opts = uniq (opts)
723         optstring = string.join (opts, ' ')
724         optstring = re.sub ('\n', ' ', optstring)
725         body = r'''
726 %% Generated automatically by: lilypond-book.py
727 %% options are %s
728 \include "paper%d.ly"
729 \paper  {
730   %s
731   %s
732   %s
733 }
734 ''' % (optstring, music_size, linewidth, indent, notime) + body
735
736         # ughUGH not original options
737         return body
738
739 def scan_html_preamble (chunks):
740         return
741
742 def scan_latex_preamble (chunks):
743         # First we want to scan the \documentclass line
744         # it should be the first non-comment line.
745         # The only thing we really need to know about the \documentclass line
746         # is if there are one or two columns to begin with.
747         idx = 0
748         while 1:
749                 if chunks[idx][0] == 'ignore':
750                         idx = idx + 1
751                         continue
752                 m = get_re ('header').match (chunks[idx][1])
753                 if not m:
754                         error ("Latex documents must start with a \documentclass command")
755                 if m.group (1):
756                         options = re.split (',[\n \t]*', m.group (1)[1:-1])
757                 else:
758                         options = []
759                 if 'twocolumn' in options:
760                         paperguru.m_num_cols = 2
761                 break
762
763
764         # Then we add everything before \begin{document} to
765         # paperguru.m_document_preamble so that we can later write this header
766         # to a temporary file in find_latex_dims() to find textwidth.
767         while idx < len (chunks) and chunks[idx][0] != 'preamble-end':
768                 if chunks[idx] == 'ignore':
769                         idx = idx + 1
770                         continue
771                 paperguru.m_document_preamble.append (chunks[idx][1])
772                 idx = idx + 1
773
774         if len (chunks) == idx:
775                 error ("Didn't find end of preamble (\\begin{document})")
776
777         paperguru.find_latex_dims ()
778
779 def scan_texi_preamble (chunks):
780         # this is not bulletproof..., it checks the first 10 chunks
781         for c in chunks[:10]:
782                 if c[0] == 'input':
783                         for s in ('afourpaper', 'afourwide', 'letterpaper',
784                                   'afourlatex', 'smallbook'):
785                                 if string.find (c[1], "@%s" % s) != -1:
786                                         paperguru.m_papersize = s
787
788
789 def scan_preamble (chunks):
790         global format
791         if format == 'html':
792                 scan_html_preamble (chunks)
793         elif format == 'latex':
794                 scan_latex_preamble (chunks)
795         elif format == 'texi':
796                 scan_texi_preamble (chunks)
797
798
799 def completize_preamble (chunks):
800         global format
801         if format != 'latex':
802                 return chunks
803         pre_b = post_b = graphics_b = None
804         for chunk in chunks:
805                 if chunk[0] == 'preamble-end':
806                         break
807                 if chunk[0] == 'input':
808                         m = get_re ('def-pre-re').search (chunk[1])
809                         if m:
810                                 pre_b = 1
811                 if chunk[0] == 'input':
812                         m = get_re ('def-post-re').search (chunk[1])
813                         if m:
814                                 post_b = 1
815
816                 if chunk[0] == 'input':
817                         m = get_re ('usepackage-graphics').search (chunk[1])
818                         if m:
819                                 graphics_b = 1
820         x = 0
821         while x < len (chunks) and   chunks[x][0] != 'preamble-end':
822                 x = x + 1
823
824         if x == len (chunks):
825                 return chunks
826
827         if not pre_b:
828                 chunks.insert (x, ('input', get_output ('output-default-pre')))
829         if not post_b:
830                 chunks.insert (x, ('input', get_output ('output-default-post')))
831         if not graphics_b:
832                 chunks.insert (x, ('input', get_output ('usepackage-graphics')))
833
834         return chunks
835
836
837 read_files = []
838 def find_file (name):
839         '''
840         Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
841         '''
842
843         if name == '-':
844                 return (sys.stdin.read (), '<stdin>')
845         f = None
846         nm = ''
847         for a in include_path:
848                 try:
849                         nm = os.path.join (a, name)
850                         f = open (nm)
851                         global read_files
852                         read_files.append (nm)
853                         break
854                 except IOError:
855                         pass
856         if f:
857                 sys.stderr.write ("Reading `%s'\n" % nm)
858                 return (f.read (), nm)
859         else:
860                 error ("File not found `%s'\n" % name)
861                 return ('', '')
862
863 def do_ignore (match_object):
864         return [('ignore', match_object.group ('code'))]
865 def do_preamble_end (match_object):
866         return [('preamble-end', match_object.group ('code'))]
867
868 def make_verbatim (match_object):
869         return [('verbatim', match_object.group ('code'))]
870
871 def make_verb (match_object):
872         return [('verb', match_object.group ('code'))]
873
874 def do_include_file (m):
875         "m: MatchObject"
876         return [('input', get_output ('pagebreak'))] \
877              + read_doc_file (m.group ('filename')) \
878              + [('input', get_output ('pagebreak'))]
879
880 def do_input_file (m):
881         return read_doc_file (m.group ('filename'))
882
883 def make_lilypond (m):
884         if m.group ('options'):
885                 options = m.group ('options')
886         else:
887                 options = ''
888         return [('input', get_output ('output-lilypond-fragment') %
889                         (options, m.group ('code')))]
890
891 def make_lilypond_file (m):
892         '''
893
894         Find @lilypondfile{bla.ly} occurences and substitute bla.ly
895         into a @lilypond .. @end lilypond block.
896
897         '''
898
899         if m.group ('options'):
900                 options = m.group ('options')
901         else:
902                 options = ''
903         (content, nm) = find_file (m.group ('filename'))
904         options = "filename=%s," % nm + options
905
906         return [('input', get_output ('output-lilypond') %
907                         (options, content))]
908
909 def make_ly2dvi_block (m):
910         '''
911
912         Find <ly2dvifile .. >
913         '''
914
915         return [('ly2dvi', m.group ('filename'), m.group ('options'))]
916
917
918 def make_lilypond_block (m):
919         if not g_do_music:
920                 return []
921
922         if m.group ('options'):
923                 options = get_re ('option-sep').split (m.group ('options'))
924         else:
925                 options = []
926         options = filter (lambda s: s != '', options)
927         return [('lilypond', m.group ('code'), options)]
928
929
930 def do_columns (m):
931         global format
932         if format != 'latex':
933                 return []
934         if m.group ('num') == 'one':
935                 return [('numcols', m.group ('code'), 1)]
936         if m.group ('num') == 'two':
937                 return [('numcols', m.group ('code'), 2)]
938
939 def do_multicols (m):
940         global format
941         if format != 'latex':
942                 return []
943         if m.group ('be') == 'begin':
944                 return [('multicols', m.group ('code'), int (m.group ('num')))]
945         else:
946                 return [('multicols', m.group ('code'), 1)]
947         return []
948
949 def chop_chunks (chunks, re_name, func, use_match=0):
950         newchunks = []
951         for c in chunks:
952                 if c[0] == 'input':
953                         str = c[1]
954                         while str:
955                                 m = get_re (re_name).search (str)
956                                 if m == None:
957                                         newchunks.append (('input', str))
958                                         str = ''
959                                 else:
960                                         if use_match:
961                                                 newchunks.append (('input', str[:m.start ('match')]))
962                                         else:
963                                                 newchunks.append (('input', str[:m.start (0)]))
964                                         #newchunks.extend (func (m))
965                                         # python 1.5 compatible:
966                                         newchunks = newchunks + func (m)
967                                         str = str [m.end (0):]
968                 else:
969                         newchunks.append (c)
970         return newchunks
971
972 def determine_format (str):
973         global format
974         if format == '':
975                 html = re.search ('(?i)<[dh]tml', str[:200])
976                 latex = re.search (r'''\\document''', str[:200])
977                 texi = re.search ('@node|@setfilename', str[:200])
978
979                 f = ''
980                 g = None
981
982                 if html and not latex and not texi:
983                         f = 'html'
984                 elif latex and not html and not texi:
985                         f = 'latex'
986                 elif texi and not html and not latex:
987                         f = 'texi'
988                 else:
989                         error ("can't determine format, please specify")
990                 format = f
991
992         global paperguru
993         if paperguru == None:
994                 if format == 'html':
995                         g = HtmlPaper ()
996                 elif format == 'latex':
997                         g = LatexPaper ()
998                 elif format == 'texi':
999                         g = TexiPaper ()
1000
1001                 paperguru = g
1002
1003
1004 def read_doc_file (filename):
1005         '''Read the input file, find verbatim chunks and do \input and \include
1006         '''
1007         (str, path) = find_file (filename)
1008         determine_format (str)
1009
1010         chunks = [('input', str)]
1011
1012         # we have to check for verbatim before doing include,
1013         # because we don't want to include files that are mentioned
1014         # inside a verbatim environment
1015         chunks = chop_chunks (chunks, 'verbatim', make_verbatim)
1016
1017         chunks = chop_chunks (chunks, 'verb', make_verb)
1018         chunks = chop_chunks (chunks, 'multiline-comment', do_ignore)
1019         #ugh fix input
1020         chunks = chop_chunks (chunks, 'include', do_include_file, 1)
1021         chunks = chop_chunks (chunks, 'input', do_input_file, 1)
1022         return chunks
1023
1024
1025 taken_file_names = {}
1026
1027 def unique_file_name (body):
1028         return 'lily-' + `abs (hash (body))`
1029
1030 def schedule_lilypond_block (chunk):
1031         '''Take the body and options from CHUNK, figure out how the
1032         real .ly should look, and what should be left MAIN_STR (meant
1033         for the main file).  The .ly is written, and scheduled in
1034         TODO.
1035
1036         Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
1037
1038         TODO has format [basename, extension, extension, ... ]
1039         '''
1040         (type, body, opts) = chunk
1041         assert type == 'lilypond'
1042         file_body = compose_full_body (body, opts)
1043         ## Hmm, we should hash only lilypond source, and skip the
1044         ## %options are ...
1045         ## comment line
1046         basename = unique_file_name (file_body)
1047         for o in opts:
1048                 m = re.search ('filename="(.*?)"', o)
1049                 if m:
1050                         basename = m.group (1)
1051                         if not taken_file_names.has_key (basename):
1052                                 taken_file_names[basename] = 0
1053                         else:
1054                                 taken_file_names[basename] = taken_file_names[basename] + 1
1055                                 basename = basename + "-%i" % taken_file_names[basename]
1056         if not g_read_lys:
1057                 update_file (file_body, os.path.join (g_outdir, basename) + '.ly')
1058         needed_filetypes = ['tex']
1059
1060         if format == 'html' or g_make_html:
1061                 needed_filetypes.append ('eps')
1062                 needed_filetypes.append ('png')
1063         if 'eps' in opts and not ('eps' in needed_filetypes):
1064                 needed_filetypes.append ('eps')
1065
1066         pathbase = os.path.join (g_outdir, basename)
1067         def f (base, ext1, ext2):
1068                 a = os.path.isfile (base + ext2)
1069                 if (os.path.isfile (base + ext1) and
1070                     os.path.isfile (base + ext2) and
1071                                 os.stat (base+ext1)[stat.ST_MTIME] >
1072                                 os.stat (base+ext2)[stat.ST_MTIME]) or \
1073                                 not os.path.isfile (base + ext2):
1074                         return 1
1075         todo = []
1076         if 'tex' in needed_filetypes and f (pathbase, '.ly', '.tex'):
1077                 todo.append ('tex')
1078         if 'eps' in needed_filetypes and f (pathbase, '.tex', '.eps'):
1079                 todo.append ('eps')
1080         if 'png' in needed_filetypes and f (pathbase, '.eps', '.png'):
1081                 todo.append ('png')
1082         newbody = ''
1083
1084         if 'printfilename' in opts:
1085                 for o in opts:
1086                         m= re.match ("filename=(.*)", o)
1087                         if m:
1088                                 newbody = newbody + get_output ("output-filename") % (m.group (1), basename + '.ly', m.group (1))
1089                                 break
1090
1091
1092         if 'smallverbatim' in opts:
1093                 newbody = newbody + output_verbatim (body, 1)
1094         elif 'verbatim' in opts:
1095                 newbody = newbody + output_verbatim (body, 0)
1096
1097         for o in opts:
1098                 m = re.search ('intertext="(.*?)"', o)
1099                 if m:
1100                         newbody = newbody + "\n"
1101                         if format == 'texi':
1102                                 newbody = newbody + "@noindent\n"
1103                         elif format == 'latex':
1104                                 newbody = newbody + "\\noindent\n"
1105                         newbody = newbody + m.group (1) + "\n"
1106
1107         if 'noinline' in opts:
1108                 s = 'output-noinline'
1109         elif format == 'latex':
1110                 if 'eps' in opts:
1111                         s = 'output-eps'
1112                 else:
1113                         if 'noquote' in opts:
1114                                 s = 'output-latex-noquote'
1115                         else:
1116                                 s = 'output-latex-quoted'
1117         elif format == 'texi':
1118                 if 'noquote' in opts:
1119                         s = 'output-texi-noquote'
1120                 else:
1121                         s = 'output-texi-quoted'
1122         else: # format == 'html'
1123                 s = 'output-html'
1124         newbody = newbody + get_output (s) % {'fn': basename }
1125         return ('lilypond', newbody, opts, todo, basename)
1126
1127
1128
1129
1130 def process_lilypond_blocks (chunks):#ugh rename
1131         newchunks = []
1132         # Count sections/chapters.
1133         for c in chunks:
1134                 if c[0] == 'lilypond':
1135                         c = schedule_lilypond_block (c)
1136                 elif c[0] == 'numcols':
1137                         paperguru.m_num_cols = c[2]
1138                 elif c[0] == 'multicols':
1139                         paperguru.m_multicols = c[2]
1140                 newchunks.append (c)
1141         return newchunks
1142
1143 def process_ly2dvi_blocks (chunks):
1144         
1145         def process_ly2dvi_block (chunk):
1146                 """
1147
1148 Run ly2dvi script on filename specified in CHUNK.
1149 This is only supported for HTML output.
1150
1151 In HTML output it will leave a download menu with ps/pdf/midi etc.  in
1152 a separate HTML file, and a title + preview in the main html file,
1153 linking to the menu.
1154
1155                 """
1156                 (tag, name, opts) = chunk
1157                 assert format == 'html'
1158                 (content, original_name) = find_file (name)
1159
1160                 original_name = os.path.basename (original_name)
1161                 
1162                 base = unique_file_name (content)
1163                 outname = base + '.ly'
1164                 changed = update_file (content, outname)
1165
1166                 preview = base + ".png"
1167                 if changed or not os.path.isfile (preview):
1168                         
1169                         ly.system ('%s --preview --postscript --verbose %s ' % (ly2dvi_binary, base) ) 
1170
1171                         ly.make_page_images (base)
1172                         ly.system ('gzip -9 - < %s.ps > %s.ps.gz' %  (base, base))
1173                         
1174                 def size_str (fn):
1175                         b = os.stat(fn)[stat.ST_SIZE]
1176                         if b < 1024:
1177                                 return '%d bytes' % b
1178                         elif b < (2 << 20):
1179                                 return '%d kb' % (b >> 10)
1180                         else:
1181                                 return '%d mb' % (b >> 20)
1182
1183                 exts = {
1184                         'pdf' : "Print (PDF, %s)",
1185                         'ps.gz' : "Print (gzipped PostScript, %s)",
1186                         'png' : "View (PNG, %s)",
1187                         'midi' : "Listen (MIDI, %s)",
1188                         'ly' : "View source code (%s)", 
1189                         }
1190
1191                 menu = ''
1192                 page_files = ly.read_pipe ('ls --color=no -1 %s-page*.png' % base)
1193
1194                 for p in string.split (page_files, '\n'):
1195                         p = p.strip()
1196                         if os.path.isfile (p):
1197                                 sz = size_str (p)
1198                                 page = re.sub ('.*page([0-9])+.*', 'View page \\1 (PNG picture, %s)\n', p)
1199                                 page = page % sz
1200                                 menu += '<li><a href="%s">%s</a>' % (p, page) 
1201
1202                 ext_order = ['ly', 'pdf', 'ps.gz', 'midi']
1203                 for e in ext_order:
1204                         fn =   base +  '.' + e
1205                         print 'checking,' , fn
1206                         if not os.path.isfile (fn):
1207                                 continue
1208
1209                         entry = exts[e] % size_str (fn)
1210
1211                         ## TODO: do something like
1212                         ## this for texinfo/latex as well ?
1213                         
1214                         menu += '<li><a href="%s">%s</a>\n\n' % (fn, entry)
1215
1216
1217                 explanatory_para = """The pictures are 90dpi
1218 anti-aliased snapshots of the printed output, in PNG format. Both  PDF and PS
1219 use scalable fonts and should look OK at any resolution."""
1220                 
1221                 separate_menu =r'''
1222 <title>LilyPond example %s</title>
1223
1224 <h1>%s</h1>
1225 <p><img src="%s">
1226 <p>%s
1227 <p>
1228 <ul>%s</ul>''' % (original_name,original_name, preview, explanatory_para, menu)
1229                 
1230                 open (base + '.html','w'). write (separate_menu)
1231
1232                 inline_menu = '<p/><a href="%s.html"><img src="%s"><p/></a>' % (base, original_name, preview)
1233
1234                 return ('ly2dvi', inline_menu)
1235
1236         newchunks = []
1237         for c in chunks:
1238                 if c[0] == 'ly2dvi':
1239                         c = process_ly2dvi_block (c)
1240                 newchunks.append (c)
1241
1242         return newchunks
1243
1244 def compile_all_files (chunks):
1245         global foutn
1246         eps = []
1247         tex = []
1248         png = []
1249
1250         for c in chunks:
1251                 if c[0] != 'lilypond':
1252                         continue
1253                 base  = c[4]
1254                 exts = c[3]
1255                 for e in exts:
1256                         if e == 'eps':
1257                                 eps.append (base)
1258                         elif e == 'tex':
1259                                 #ugh
1260                                 if base + '.ly' not in tex:
1261                                         tex.append (base + '.ly')
1262                         elif e == 'png' and g_do_pictures:
1263                                 png.append (base)
1264         d = os.getcwd ()
1265         if g_outdir:
1266                 os.chdir (g_outdir)
1267         if tex:
1268                 # fixme: be sys-independent.
1269                 def incl_opt (x):
1270                         if g_outdir and x[0] != '/' :
1271                                 x = os.path.join (g_here_dir, x)
1272                         return ' -I %s' % x
1273
1274                 incs = map (incl_opt, include_path)
1275                 lilyopts = string.join (incs)
1276                 if do_deps:
1277                         lilyopts += ' --dependencies'
1278                         if g_outdir:
1279                                 lilyopts += ' --dep-prefix=' + g_outdir + '/'
1280                 lilyopts += ' --header=texidoc'
1281                 texfiles = string.join (tex)
1282                 cmd = string.join ((lilypond_binary, lilyopts, g_extra_opts,
1283                                     texfiles))
1284                 ly.system (cmd, ignore_error = 0, progress_p = 1)
1285
1286                 #
1287                 # Ugh, fixing up dependencies for .tex generation
1288                 #
1289                 if do_deps:
1290                         depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep',
1291                                                         x), tex)
1292
1293                         for i in depfiles:
1294                                 f =open (i)
1295                                 text=f.read ()
1296                                 f.close ()
1297                                 text=re.sub ('\n([^:\n]*):',
1298                                              '\n' + foutn + ':', text)
1299                                 f = open (i, 'w')
1300                                 f.write (text)
1301                                 f.close ()
1302
1303         def to_eps (file):
1304                 cmd = r"latex '\nonstopmode \input %s'" % file
1305                 # Ugh.  (La)TeX writes progress and error messages on stdout
1306                 # Redirect to stderr
1307                 cmd += ' 1>/dev/stderr'
1308                 ly.system (cmd)
1309                 ly.system ("dvips -E -o %s.eps %s" % (file, file))
1310         map (to_eps, eps)
1311
1312         map (ly.make_preview, png)
1313         os.chdir (d)
1314
1315
1316 def update_file (body, name):
1317         '''
1318         write the body if it has changed. Return whether BODY has changed.
1319         '''
1320         same = 0
1321         try:
1322                 f = open (name)
1323                 fs = f.read (-1)
1324                 same = (fs == body)
1325         except:
1326                 pass
1327
1328         if not same:
1329                 f = open (name , 'w')
1330                 f.write (body)
1331                 f.close ()
1332
1333         return not same
1334
1335
1336 def write_deps (fn, target, chunks):
1337         global read_files
1338         sys.stderr.write ('Writing `%s\'\n' % os.path.join (g_outdir, fn))
1339         f = open (os.path.join (g_outdir, fn), 'w')
1340         f.write ('%s%s: ' % (g_dep_prefix, target))
1341         for d in read_files:
1342                 f.write ('%s ' %  d)
1343         basenames=[]
1344         for c in chunks:
1345                 if c[0] == 'lilypond':
1346                         (type, body, opts, todo, basename) = c;
1347                         basenames.append (basename)
1348         for d in basenames:
1349                 if g_outdir:
1350                         d=g_outdir + '/' + d
1351                 if g_dep_prefix:
1352                         #if not os.isfile (d): # thinko?
1353                         if not re.search ('/', d):
1354                                 d = g_dep_prefix + d
1355                 f.write ('%s.tex ' %  d)
1356         f.write ('\n')
1357         #if len (basenames):
1358         #       for d in basenames:
1359         #               f.write ('%s.ly ' %  d)
1360         #       f.write (' : %s' % target)
1361         f.write ('\n')
1362         f.close ()
1363         read_files = []
1364
1365 def check_texidoc (chunks):
1366         n = []
1367         for c in chunks:
1368                 if c[0] == 'lilypond':
1369                         (type, body, opts, todo, basename) = c;
1370                         pathbase = os.path.join (g_outdir, basename)
1371                         if os.path.isfile (pathbase + '.texidoc') \
1372                            and 'notexidoc' not in opts:
1373                                 body = '\n@include %s.texidoc\n' % basename + body
1374                                 c = (type, body, opts, todo, basename)
1375                 n.append (c)
1376         return n
1377
1378
1379 ## what's this? Docme --hwn
1380 ##
1381 def fix_epswidth (chunks):
1382         newchunks = []
1383         for c in chunks:
1384                 if c[0] != 'lilypond' or 'eps' not in c[2]:
1385                         newchunks.append (c)
1386                         continue
1387
1388                 mag = 1.0
1389                 for o in c[2]:
1390                         m  = re.match ('magnification=([0-9.]+)', o)
1391                         if m:
1392                                 mag = string.atof (m.group (1))
1393
1394                 def replace_eps_dim (match, lmag = mag):
1395                         filename = match.group (1)
1396                         dims = bounding_box_dimensions (filename)
1397
1398                         return '%fpt' % (dims[0] *lmag)
1399
1400                 body = re.sub (r'''\\lilypondepswidth{(.*?)}''', replace_eps_dim, c[1])
1401                 newchunks.append (('lilypond', body, c[2], c[3], c[4]))
1402
1403         return newchunks
1404
1405
1406 ##docme: why global?
1407 foutn=""
1408
1409 def do_file (input_filename):
1410         chunks = read_doc_file (input_filename)
1411         chunks = chop_chunks (chunks, 'ly2dvi', make_ly2dvi_block, 1)
1412         chunks = chop_chunks (chunks, 'lilypond', make_lilypond, 1)
1413         chunks = chop_chunks (chunks, 'lilypond-file', make_lilypond_file, 1)
1414         chunks = chop_chunks (chunks, 'lilypond-block', make_lilypond_block, 1)
1415         chunks = chop_chunks (chunks, 'singleline-comment', do_ignore, 1)
1416         chunks = chop_chunks (chunks, 'preamble-end', do_preamble_end)
1417         chunks = chop_chunks (chunks, 'numcols', do_columns)
1418         chunks = chop_chunks (chunks, 'multicols', do_multicols)
1419         #print "-" * 50
1420         #for c in chunks: print "c:", c;
1421         #sys.exit ()
1422         scan_preamble (chunks)
1423         chunks = process_lilypond_blocks (chunks)
1424         chunks = process_ly2dvi_blocks (chunks)
1425         
1426         # Do It.
1427         global g_run_lilypond
1428         if g_run_lilypond:
1429                 compile_all_files (chunks)
1430                 chunks = fix_epswidth (chunks)
1431
1432         global format
1433         if format == 'texi':
1434                 chunks = check_texidoc (chunks)
1435
1436         x = 0
1437         chunks = completize_preamble (chunks)
1438
1439         global foutn
1440
1441         if outname:
1442                 my_outname = outname
1443         elif input_filename == '-' or input_filename == "/dev/stdin":
1444                 my_outname = '-'
1445         else:
1446                 my_outname = os.path.basename (os.path.splitext (input_filename)[0]) + '.' + format
1447         my_depname = my_outname + '.dep'
1448
1449         if my_outname == '-' or my_outname == '/dev/stdout':
1450                 fout = sys.stdout
1451                 foutn = "<stdout>"
1452                 global do_deps
1453                 do_deps = 0
1454         else:
1455                 foutn = os.path.join (g_outdir, my_outname)
1456                 sys.stderr.write ("Writing `%s'\n" % foutn)
1457                 fout = open (foutn, 'w')
1458         for c in chunks:
1459                 fout.write (c[1])
1460         fout.close ()
1461         # should chmod -w
1462
1463         if do_deps:
1464                 write_deps (my_depname, foutn, chunks)
1465
1466 outname = ''
1467 try:
1468         (sh, long) = ly.getopt_args (option_definitions)
1469         (options, files) = getopt.getopt (sys.argv[1:], sh, long)
1470         
1471 except getopt.error, msg:
1472         sys.stderr.write ('\n')
1473         ly.error (_ ("getopt says: `%s\'" % s))
1474         sys.stderr.write ('\n')
1475         ly.help ()
1476         ly.exit (2)
1477
1478 do_deps = 0
1479 for opt in options:
1480         o = opt[0]
1481         a = opt[1]
1482
1483         if o == '--include' or o == '-I':
1484                 include_path.append (a)
1485         elif o == '--version' or o == '-v':
1486                 ly.identify (sys.stdout)
1487                 sys.exit (0)
1488         elif o == '--verbose' or o == '-V':
1489                 verbose_p = 1
1490         elif o == '--format' or o == '-f':
1491                 format = a
1492                 if a == 'texi-html':
1493                         format = 'texi'
1494                         g_make_html = 1
1495         elif o == '--outname' or o == '-o':
1496                 if len (files) > 1:
1497                         #HACK
1498                         sys.stderr.write ("Lilypond-book is confused by --outname on multiple files")
1499                         sys.exit (1)
1500                 outname = a
1501         elif o == '--help' or o == '-h':
1502                 ly.help ()
1503                 sys.exit (0)
1504         elif o == '--no-lily' or o == '-n':
1505                 g_run_lilypond = 0
1506         elif o == '--preview-resolution':
1507                 preview_resolution = string.atoi (a)
1508         elif o == '--dependencies' or o == '-M':
1509                 do_deps = 1
1510         elif o == '--default-music-fontsize':
1511                 default_music_fontsize = string.atoi (a)
1512         elif o == '--default-lilypond-fontsize':
1513                 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1514                 default_music_fontsize = string.atoi (a)
1515         elif o == '--extra-options':
1516                 g_extra_opts = a
1517         elif o == '--force-music-fontsize':
1518                 g_force_music_fontsize = string.atoi (a)
1519         elif o == '--force-lilypond-fontsize':
1520                 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1521                 g_force_music_fontsize = string.atoi (a)
1522         elif o == '--dep-prefix':
1523                 g_dep_prefix = a
1524         elif o == '--no-pictures':
1525                 g_do_pictures = 0
1526         elif o == '--no-music':
1527                 g_do_music = 0
1528         elif o == '--read-lys':
1529                 g_read_lys = 1
1530         elif o == '--outdir':
1531                 g_outdir = a
1532         elif o == '--warranty' or o == '-w':
1533                 #status = os.system ('lilypond -w')
1534                 if 1 or status:
1535                         ly.warranty ()
1536                 sys.exit (0)
1537
1538 ly.identify (sys.stderr)
1539
1540 if g_outdir:
1541         if os.path.isfile (g_outdir):
1542                 error ("outdir is a file: %s" % g_outdir)
1543         if not os.path.exists (g_outdir):
1544                 os.mkdir (g_outdir)
1545                 
1546 if not files:
1547         ly.help ()
1548         ly.error (_ ("no files specified on command line"))
1549         ly.exit (2)
1550
1551 ly.setup_environment ()
1552
1553 for input_filename in files:
1554         do_file (input_filename)
1555
1556
1557 #
1558 # Petr, ik zou willen dat ik iets zinvoller deed,
1559 # maar wat ik kan ik doen, het verandert toch niets?
1560 #   --hwn 20/aug/99