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