tab patch 4
[lilypond.git] / scripts / lilypond-book.py
blob49c20ecfdab72da47b6c66bee51871ef8618c451
1 #!@PYTHON@
2 # vim: set noexpandtab:
3 # TODO:
4 # * junk --outdir for --output
5 # * Figure out clean set of options.
6 # *
7 # * EndLilyPondOutput is def'd as vfil. Causes large white gaps.
8 # * texinfo: add support for @pagesize
10 # todo: dimension handling (all the x2y) is clumsy. (tca: Thats
11 # because the values are taken directly from texinfo.tex,
12 # geometry.sty and article.cls. Give me a hint, and I'll
13 # fix it.)
16 # TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips.
19 # This is was the idea for handling of comments:
20 # Multiline comments, @ignore .. @end ignore is scanned for
21 # in read_doc_file, and the chunks are marked as 'ignore', so
22 # lilypond-book will not touch them any more. The content of the
23 # chunks are written to the output file. Also 'include' and 'input'
24 # regex has to check if they are commented out.
26 # Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
27 # These three regex's has to check if they are on a commented line,
28 # % for latex, @c for texinfo.
30 # Then lines that are commented out with % (latex) and @c (Texinfo)
31 # are put into chunks marked 'ignore'. This cannot be done before
32 # searching for the lilypond-blocks because % is also the comment character
33 # for lilypond.
35 # The the rest of the rexeces are searched for. They don't have to test
36 # if they are on a commented out line.
40 import os
41 import stat
42 import string
43 import getopt
44 import sys
45 import __main__
46 import operator
48 # Handle bug in Python 1.6-2.1
50 # there are recursion limits for some patterns in Python 1.6 til 2.1.
51 # fix this by importing the 1.5.2 implementation pre instead. Fix by Mats.
53 if float (sys.version[0:3]) < 2.2:
54 try:
55 import pre
56 re = pre
57 del pre
58 except ImportError:
59 import re
60 else:
61 import re
63 # Attempt to fix problems with limited stack size set by Python!
64 # Sets unlimited stack size. Note that the resource module only
65 # is available on UNIX.
66 try:
67 import resource
68 resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
69 except:
70 pass
72 errorport = sys.stderr
73 verbose_p = 0
77 try:
78 import gettext
79 gettext.bindtextdomain ('lilypond', localedir)
80 gettext.textdomain ('lilypond')
81 _ = gettext.gettext
82 except:
83 def _ (s):
84 return s
86 def progress (s):
87 errorport.write (s + '\n')
90 program_version = '@TOPLEVEL_VERSION@'
91 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
92 program_version = '1.5.53'
94 # if set, LILYPONDPREFIX must take prevalence
95 # if datadir is not set, we're doing a build and LILYPONDPREFIX
96 datadir = '@datadir@'
98 if os.environ.has_key ('LILYPONDPREFIX') :
99 datadir = os.environ['LILYPONDPREFIX']
100 else:
101 datadir = '@datadir@'
103 while datadir[-1] == os.sep:
104 datadir= datadir[:-1]
106 # Try to cater for bad installations of LilyPond, that have
107 # broken TeX setup. Just hope this doesn't hurt good TeX
108 # setups. Maybe we should check if kpsewhich can find
109 # feta16.{afm,mf,tex,tfm}, and only set env upon failure.
110 environment = {
111 'MFINPUTS' : datadir + '/mf:',
112 'TEXINPUTS': datadir + '/tex:' + datadir + '/ps:.:',
113 'TFMFONTS' : datadir + '/tfm:',
114 'GS_FONTPATH' : datadir + '/afm:' + datadir + '/pfa',
115 'GS_LIB' : datadir + '/ps',
118 # tex needs lots of memory, more than it gets by default on Debian
119 non_path_environment = {
120 'extra_mem_top' : '1000000',
121 'extra_mem_bottom' : '1000000',
122 'pool_size' : '250000',
125 def setup_environment ():
126 for key in environment.keys ():
127 val = environment[key]
128 if os.environ.has_key (key):
129 val = val + os.pathsep + os.environ[key]
130 os.environ[key] = val
132 for key in non_path_environment.keys ():
133 val = non_path_environment[key]
134 os.environ[key] = val
136 include_path = [os.getcwd()]
139 # g_ is for global (?)
140 g_extra_opts = ''
141 g_here_dir = os.getcwd ()
142 g_dep_prefix = ''
143 g_outdir = ''
144 g_force_lilypond_fontsize = 0
145 g_read_lys = 0
146 g_do_pictures = 1
147 g_num_cols = 1
148 g_do_music = 1
150 format = ''
151 g_run_lilypond = 1
152 no_match = 'a\ba'
154 default_music_fontsize = 16
155 default_text_fontsize = 12
156 paperguru = None
158 # this code is ugly. It should be cleaned
159 class LatexPaper:
160 def __init__(self):
161 self.m_paperdef = {
162 # the dimensions are from geometry.sty
163 'a0paper': (mm2pt(841), mm2pt(1189)),
164 'a1paper': (mm2pt(595), mm2pt(841)),
165 'a2paper': (mm2pt(420), mm2pt(595)),
166 'a3paper': (mm2pt(297), mm2pt(420)),
167 'a4paper': (mm2pt(210), mm2pt(297)),
168 'a5paper': (mm2pt(149), mm2pt(210)),
169 'b0paper': (mm2pt(1000), mm2pt(1414)),
170 'b1paper': (mm2pt(707), mm2pt(1000)),
171 'b2paper': (mm2pt(500), mm2pt(707)),
172 'b3paper': (mm2pt(353), mm2pt(500)),
173 'b4paper': (mm2pt(250), mm2pt(353)),
174 'b5paper': (mm2pt(176), mm2pt(250)),
175 'letterpaper': (in2pt(8.5), in2pt(11)),
176 'legalpaper': (in2pt(8.5), in2pt(14)),
177 'executivepaper': (in2pt(7.25), in2pt(10.5))}
178 self.m_use_geometry = None
179 self.m_papersize = 'letterpaper'
180 self.m_fontsize = 10
181 self.m_num_cols = 1
182 self.m_landscape = 0
183 self.m_geo_landscape = 0
184 self.m_geo_width = None
185 self.m_geo_textwidth = None
186 self.m_geo_lmargin = None
187 self.m_geo_rmargin = None
188 self.m_geo_includemp = None
189 self.m_geo_marginparwidth = {10: 57, 11: 50, 12: 35}
190 self.m_geo_marginparsep = {10: 11, 11: 10, 12: 10}
191 self.m_geo_x_marginparwidth = None
192 self.m_geo_x_marginparsep = None
193 self.__body = None
194 def set_geo_option(self, name, value):
196 if type(value) == type([]):
197 value = map(conv_dimen_to_float, value)
198 else:
199 value = conv_dimen_to_float(value)
201 if name == 'body' or name == 'text':
202 if type(value) == type([]):
203 self.m_geo_textwidth = value[0]
204 else:
205 self.m_geo_textwidth = value
206 self.__body = 1
207 elif name == 'portrait':
208 self.m_geo_landscape = 0
209 elif name == 'reversemp' or name == 'reversemarginpar':
210 if self.m_geo_includemp == None:
211 self.m_geo_includemp = 1
212 elif name == 'marginparwidth' or name == 'marginpar':
213 self.m_geo_x_marginparwidth = value
214 self.m_geo_includemp = 1
215 elif name == 'marginparsep':
216 self.m_geo_x_marginparsep = value
217 self.m_geo_includemp = 1
218 elif name == 'scale':
219 if type(value) == type([]):
220 self.m_geo_width = self.get_paperwidth() * value[0]
221 else:
222 self.m_geo_width = self.get_paperwidth() * value
223 elif name == 'hscale':
224 self.m_geo_width = self.get_paperwidth() * value
225 elif name == 'left' or name == 'lmargin':
226 self.m_geo_lmargin = value
227 elif name == 'right' or name == 'rmargin':
228 self.m_geo_rmargin = value
229 elif name == 'hdivide' or name == 'divide':
230 if value[0] not in ('*', ''):
231 self.m_geo_lmargin = value[0]
232 if value[1] not in ('*', ''):
233 self.m_geo_width = value[1]
234 if value[2] not in ('*', ''):
235 self.m_geo_rmargin = value[2]
236 elif name == 'hmargin':
237 if type(value) == type([]):
238 self.m_geo_lmargin = value[0]
239 self.m_geo_rmargin = value[1]
240 else:
241 self.m_geo_lmargin = value
242 self.m_geo_rmargin = value
243 elif name == 'margin':#ugh there is a bug about this option in
244 # the geometry documentation
245 if type(value) == type([]):
246 self.m_geo_lmargin = value[0]
247 self.m_geo_rmargin = value[0]
248 else:
249 self.m_geo_lmargin = value
250 self.m_geo_rmargin = value
251 elif name == 'total':
252 if type(value) == type([]):
253 self.m_geo_width = value[0]
254 else:
255 self.m_geo_width = value
256 elif name == 'width' or name == 'totalwidth':
257 self.m_geo_width = value
258 elif name == 'paper' or name == 'papername':
259 self.m_papersize = value
260 elif name[-5:] == 'paper':
261 self.m_papersize = name
262 else:
263 pass
265 def __setattr__(self, name, value):
266 if type(value) == type("") and \
267 dimension_conversion_dict.has_key (value[-2:]):
268 f = dimension_conversion_dict[value[-2:]]
269 self.__dict__[name] = f(float(value[:-2]))
270 else:
271 self.__dict__[name] = value
273 def __str__(self):
274 s = "LatexPaper:\n-----------"
275 for v in self.__dict__.keys():
276 if v[:2] == 'm_':
277 s = s + str (v) + ' ' + str (self.__dict__[v])
278 s = s + "-----------"
279 return s
281 def get_linewidth(self):
282 w = self._calc_linewidth()
283 if self.m_num_cols == 2:
284 return (w - 10) / 2
285 else:
286 return w
287 def get_paperwidth(self):
288 #if self.m_use_geometry:
289 return self.m_paperdef[self.m_papersize][self.m_landscape or self.m_geo_landscape]
290 #return self.m_paperdef[self.m_papersize][self.m_landscape]
292 def _calc_linewidth(self):
293 # since geometry sometimes ignores 'includemp', this is
294 # more complicated than it should be
295 mp = 0
296 if self.m_geo_includemp:
297 if self.m_geo_x_marginparsep is not None:
298 mp = mp + self.m_geo_x_marginparsep
299 else:
300 mp = mp + self.m_geo_marginparsep[self.m_fontsize]
301 if self.m_geo_x_marginparwidth is not None:
302 mp = mp + self.m_geo_x_marginparwidth
303 else:
304 mp = mp + self.m_geo_marginparwidth[self.m_fontsize]
306 #ugh test if this is necessary
307 if self.__body:
308 mp = 0
310 if not self.m_use_geometry:
311 return latex_linewidths[self.m_papersize][self.m_fontsize]
312 else:
313 geo_opts = (self.m_geo_lmargin == None,
314 self.m_geo_width == None,
315 self.m_geo_rmargin == None)
317 if geo_opts == (1, 1, 1):
318 if self.m_geo_textwidth:
319 return self.m_geo_textwidth
320 w = self.get_paperwidth() * 0.8
321 return w - mp
322 elif geo_opts == (0, 1, 1):
323 if self.m_geo_textwidth:
324 return self.m_geo_textwidth
325 return self.f1(self.m_geo_lmargin, mp)
326 elif geo_opts == (1, 1, 0):
327 if self.m_geo_textwidth:
328 return self.m_geo_textwidth
329 return self.f1(self.m_geo_rmargin, mp)
330 elif geo_opts \
331 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
332 if self.m_geo_textwidth:
333 return self.m_geo_textwidth
334 return self.m_geo_width - mp
335 elif geo_opts in ((0, 1, 0), (0, 0, 0)):
336 w = self.get_paperwidth() \
337 - self.m_geo_lmargin - self.m_geo_rmargin - mp
338 if w < 0:
339 w = 0
340 return w
341 raise "Never do this!"
342 def f1(self, m, mp):
343 tmp = self.get_paperwidth() - m * 2 - mp
344 if tmp < 0:
345 tmp = 0
346 return tmp
347 def f2(self):
348 tmp = self.get_paperwidth() - self.m_geo_lmargin \
349 - self.m_geo_rmargin
350 if tmp < 0:
351 return 0
352 return tmp
354 class HtmlPaper:
355 def __init__(self):
356 self.m_papersize = 'letterpaper'
357 self.m_fontsize = 12
358 def get_linewidth(self):
359 return html_linewidths[self.m_papersize][self.m_fontsize]
361 class TexiPaper:
362 def __init__(self):
363 self.m_papersize = 'letterpaper'
364 self.m_fontsize = 12
365 def get_linewidth(self):
366 return texi_linewidths[self.m_papersize][self.m_fontsize]
368 def mm2pt(x):
369 return x * 2.8452756
370 def in2pt(x):
371 return x * 72.26999
372 def em2pt(x, fontsize = 10):
373 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
374 def ex2pt(x, fontsize = 10):
375 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
377 def pt2pt(x):
378 return x
380 dimension_conversion_dict ={
381 'mm': mm2pt,
382 'cm': lambda x: mm2pt(10*x),
383 'in': in2pt,
384 'em': em2pt,
385 'ex': ex2pt,
386 'pt': pt2pt
389 # Convert numeric values, with or without specific dimension, to floats.
390 # Keep other strings
391 def conv_dimen_to_float(value):
392 if type(value) == type(""):
393 m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
394 if m:
395 unit = m.group (2)
396 num = string.atof(m.group (1))
397 conv = dimension_conversion_dict[m.group(2)]
399 value = conv(num)
401 elif re.match ("^[0-9.]+$",value):
402 value = float(value)
404 return value
407 # latex linewidths:
408 # indices are no. of columns, papersize, fontsize
409 # Why can't this be calculated?
410 latex_linewidths = {
411 'a4paper':{10: 345, 11: 360, 12: 390},
412 'a4paper-landscape': {10: 598, 11: 596, 12:592},
413 'a5paper':{10: 276, 11: 276, 12: 276},
414 'b5paper':{10: 345, 11: 356, 12: 356},
415 'letterpaper':{10: 345, 11: 360, 12: 390},
416 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
417 'legalpaper': {10: 345, 11: 360, 12: 390},
418 'executivepaper':{10: 345, 11: 360, 12: 379}}
420 texi_linewidths = {
421 'afourpaper': {12: mm2pt(160)},
422 'afourwide': {12: in2pt(6.5)},
423 'afourlatex': {12: mm2pt(150)},
424 'smallbook': {12: in2pt(5)},
425 'letterpaper': {12: in2pt(6)}}
427 html_linewidths = {
428 'afourpaper': {12: mm2pt(160)},
429 'afourwide': {12: in2pt(6.5)},
430 'afourlatex': {12: mm2pt(150)},
431 'smallbook': {12: in2pt(5)},
432 'letterpaper': {12: in2pt(6)}}
434 option_definitions = [
435 ('EXT', 'f', 'format', 'use output format EXT (texi [default], latex, html)'),
436 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
437 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
438 ('OPT', '', 'extra-options' , 'Pass OPT quoted to the lilypond command line'),
439 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
440 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
441 ('', 'h', 'help', 'this help'),
442 ('DIR', 'I', 'include', 'include path'),
443 ('', 'M', 'dependencies', 'write dependencies'),
444 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
445 ('', 'n', 'no-lily', 'don\'t run lilypond'),
446 ('', '', 'no-pictures', "don\'t generate pictures"),
447 ('', '', 'no-music', "strip all lilypond blocks from output"),
448 ('', '', 'read-lys', "don't write ly files."),
449 ('FILE', 'o', 'outname', 'filename main output file'),
450 ('FILE', '', 'outdir', "where to place generated files"),
451 ('', 'V', 'verbose', 'verbose' ),
452 ('', 'v', 'version', 'print version information' ),
455 # format specific strings, ie. regex-es for input, and % strings for output
456 output_dict= {
457 'html' : {'output-lilypond': '''<lilypond%s>
459 </lilypond>''',
460 'output-filename' : r'''
462 <pre>%s</pre>:''',
463 'output-lilypond-fragment': '''<lilypond%s>
464 \context Staff\context Voice{ %s }
465 </lilypond>''',
466 'output-noinline': r'''
467 <!-- generated: %(fn)s.png !-->
468 ''',
469 ## maybe <hr> ?
470 'pagebreak': None,
471 'output-verbatim': r'''<pre>
473 </pre>''',
474 ## Ugh we need to differentiate on origin:
475 ## lilypond-block origin wants an extra <p>, but
476 ## inline music doesn't.
477 ## possibly other center options?
478 'output-all': r'''
479 <a href="%(fn)s.png">
480 <img align="center" valign="center" border="0" src="%(fn)s.png" alt="[picture of music]"></a>
481 ''',
483 'latex': {
484 'output-lilypond-fragment' : r'''\begin[eps,singleline,%s]{lilypond}
485 \context Staff <
486 \context Voice{
490 \end{lilypond}''',
491 'output-filename' : r'''
493 \verb+%s+:''',
494 'output-lilypond': r'''\begin[%s]{lilypond}
496 \end{lilypond}
497 ''',
498 'output-verbatim': r'''\begin{verbatim}%s\end{verbatim}%%
499 ''',
500 'output-default-post': "\\def\postLilypondExample{}\n",
501 'output-default-pre': "\\def\preLilypondExample{}\n",
502 'usepackage-graphics': '\\usepackage{graphics}\n',
503 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
504 'output-noinline': r'''
505 %% generated: %(fn)s.eps
506 ''',
507 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
508 'pagebreak': r'\pagebreak',
511 'texi' : {'output-lilypond': '''@lilypond[%s]
513 @end lilypond
514 ''',
515 'output-filename' : r'''
517 @file{%s}:''',
518 'output-lilypond-fragment': '''@lilypond[%s]
519 \context Staff\context Voice{ %s }
520 @end lilypond ''',
521 'output-noinline': r'''
522 @c generated: %(fn)s.png
523 ''',
524 'pagebreak': None,
525 'output-verbatim': r'''@example
527 @end example
528 ''',
530 # do some tweaking: @ is needed in some ps stuff.
531 # override EndLilyPondOutput, since @tex is done
532 # in a sandbox, you can't do \input lilyponddefs at the
533 # top of the document.
535 # should also support fragment in
537 # ugh, the <p> below breaks inline images...
539 'output-all': r'''
540 @tex
541 \catcode`\@=12
542 \input lilyponddefs
543 \def\EndLilyPondOutput{}
544 \input %(fn)s.tex
545 \catcode`\@=0
546 @end tex
547 @html
549 <a href="%(fn)s.png">
550 <img border=0 src="%(fn)s.png" alt="[picture of music]">
551 </a>
552 @end html
553 ''',
558 def output_verbatim (body):
559 if __main__.format == 'html':
560 body = re.sub ('&', '&amp;', body)
561 body = re.sub ('>', '&gt;', body)
562 body = re.sub ('<', '&lt;', body)
563 elif __main__.format == 'texi':
564 body = re.sub ('([@{}])', '@\\1', body)
565 return get_output ('output-verbatim') % body
568 #warning: this uses extended regular expressions. Tread with care.
570 # legenda
572 # (?P -- name parameter
573 # *? -- match non-greedily.
574 # (?m) -- ?
575 re_dict = {
576 'html': {
577 'include': no_match,
578 'input': no_match,
579 'header': no_match,
580 'preamble-end': no_match,
581 'landscape': no_match,
582 'verbatim': r'''(?s)(?P<code><pre>\s.*?</pre>\s)''',
583 'verb': r'''(?P<code><pre>.*?</pre>)''',
584 'lilypond-file': r'(?m)(?P<match><lilypondfile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</lilypondfile>)',
585 'lilypond' : '(?m)(?P<match><lilypond((?P<options>[^:]*):)(?P<code>.*?)/>)',
586 'lilypond-block': r'''(?ms)(?P<match><lilypond(?P<options>[^>]+)?>(?P<code>.*?)</lilypond>)''',
587 'option-sep' : '\s*',
588 'intertext': r',?\s*intertext=\".*?\"',
589 'multiline-comment': r"(?sm)\s*(?!@c\s+)(?P<code><!--\s.*?!-->)\s",
590 'singleline-comment': no_match,
591 'numcols': no_match,
594 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
595 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
596 'option-sep' : ',\s*',
597 'header': r"\\documentclass\s*(\[.*?\])?",
598 'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
599 'preamble-end': r'(?P<code>\\begin{document})',
600 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
601 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
602 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
603 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
604 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
605 'def-post-re': r"\\def\\postLilypondExample",
606 'def-pre-re': r"\\def\\preLilypondExample",
607 'usepackage-graphics': r"\usepackage{graphics}",
608 'intertext': r',?\s*intertext=\".*?\"',
609 'multiline-comment': no_match,
610 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
611 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
615 # why do we have distinction between @mbinclude and @include?
618 'texi': {
619 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
620 'input': no_match,
621 'header': no_match,
622 'preamble-end': no_match,
623 'landscape': no_match,
624 'verbatim': r'''(?s)(?P<code>@example\s.*?@end example\s)''',
625 'verb': r'''(?P<code>@code{.*?})''',
626 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
627 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
628 'lilypond-block': r'''(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end lilypond)\s''',
629 'option-sep' : ',\s*',
630 'intertext': r',?\s*intertext=\".*?\"',
631 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
632 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
633 'numcols': no_match,
638 for r in re_dict.keys ():
639 olddict = re_dict[r]
640 newdict = {}
641 for k in olddict.keys ():
642 try:
643 newdict[k] = re.compile (olddict[k])
644 except:
645 print 'invalid regexp: %s' % olddict[k]
647 # we'd like to catch and reraise a more detailed error, but
648 # alas, the exceptions changed across the 1.5/2.1 boundary.
649 raise "Invalid re"
650 re_dict[r] = newdict
653 def uniq (list):
654 list.sort ()
655 s = list
656 list = []
657 for x in s:
658 if x not in list:
659 list.append (x)
660 return list
663 def get_output (name):
664 return output_dict[format][name]
666 def get_re (name):
667 return re_dict[format][name]
669 def bounding_box_dimensions(fname):
670 if g_outdir:
671 fname = os.path.join(g_outdir, fname)
672 try:
673 fd = open(fname)
674 except IOError:
675 error ("Error opening `%s'" % fname)
676 str = fd.read ()
677 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
678 if s:
680 gs = map (lambda x: string.atoi (x), s.groups ())
681 return (int (gs[2] - gs[0] + 0.5),
682 int (gs[3] - gs[1] + 0.5))
683 else:
684 return (0,0)
686 def error (str):
687 sys.stderr.write (str + "\n Exiting ... \n\n")
688 raise 'Exiting.'
691 def compose_full_body (body, opts):
692 '''Construct the lilypond code to send to Lilypond.
693 Add stuff to BODY using OPTS as options.'''
694 music_size = default_music_fontsize
695 latex_size = default_text_fontsize
696 indent = ''
697 linewidth = ''
698 for o in opts:
699 if g_force_lilypond_fontsize:
700 music_size = g_force_lilypond_fontsize
701 else:
702 m = re.match ('([0-9]+)pt', o)
703 if m:
704 music_size = string.atoi(m.group (1))
706 m = re.match ('latexfontsize=([0-9]+)pt', o)
707 if m:
708 latex_size = string.atoi (m.group (1))
710 m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o)
711 if m:
712 f = float (m.group (1))
713 indent = 'indent = %f\\%s' % (f, m.group (2))
715 m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o)
716 if m:
717 f = float (m.group (1))
718 linewidth = 'linewidth = %f\\%s' % (f, m.group (2))
720 if re.search ('\\\\score', body):
721 is_fragment = 0
722 else:
723 is_fragment = 1
724 if 'fragment' in opts:
725 is_fragment = 1
726 if 'nofragment' in opts:
727 is_fragment = 0
729 if is_fragment and not 'multiline' in opts:
730 opts.append('singleline')
732 if 'singleline' in opts:
733 linewidth = 'linewidth = -1.0'
734 elif not linewidth:
735 l = __main__.paperguru.get_linewidth ()
736 linewidth = 'linewidth = %f\pt' % l
738 if 'noindent' in opts:
739 indent = 'indent = 0.0\mm'
741 for o in opts:
742 m= re.search ('relative(.*)', o)
743 v = 0
744 if m:
745 try:
746 v = string.atoi (m.group (1))
747 except ValueError:
748 pass
750 v = v + 1
751 pitch = 'c'
752 if v < 0:
753 pitch = pitch + '\,' * v
754 elif v > 0:
755 pitch = pitch + '\'' * v
757 body = '\\relative %s { %s }' %(pitch, body)
759 if is_fragment:
760 body = r'''\score {
761 \notes { %s }
762 \paper { }
763 }''' % body
765 opts = uniq (opts)
766 optstring = string.join (opts, ' ')
767 optstring = re.sub ('\n', ' ', optstring)
768 body = r'''
769 %% Generated automatically by: lilypond-book.py
770 %% options are %s
771 \include "paper%d.ly"
772 \paper {
776 ''' % (optstring, music_size, linewidth, indent) + body
778 # ughUGH not original options
779 return body
781 def parse_options_string(s):
782 d = {}
783 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
784 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
785 r3 = re.compile("(\w+?)((,\s*)|$)")
786 while s:
787 m = r1.match(s)
788 if m:
789 s = s[m.end():]
790 d[m.group(2)] = re.split(",\s*", m.group(3))
791 continue
792 m = r2.match(s)
793 if m:
794 s = s[m.end():]
795 d[m.group(2)] = m.group(3)
796 continue
797 m = r3.match(s)
798 if m:
799 s = s[m.end():]
800 d[m.group(1)] = 1
801 continue
803 error ("format of option string invalid (was `%')" % s)
804 return d
806 def scan_html_preamble (chunks):
807 return
809 def scan_latex_preamble(chunks):
810 # first we want to scan the \documentclass line
811 # it should be the first non-comment line
812 idx = 0
813 while 1:
814 if chunks[idx][0] == 'ignore':
815 idx = idx + 1
816 continue
817 m = get_re ('header').match(chunks[idx][1])
818 if m <> None and m.group (1):
819 options = re.split (',[\n \t]*', m.group(1)[1:-1])
820 else:
821 options = []
822 for o in options:
823 if o == 'landscape':
824 paperguru.m_landscape = 1
825 m = re.match("(.*?)paper", o)
826 if m:
827 paperguru.m_papersize = m.group()
828 else:
829 m = re.match("(\d\d)pt", o)
830 if m:
831 paperguru.m_fontsize = int(m.group(1))
832 break
834 while idx < len(chunks) and chunks[idx][0] != 'preamble-end':
835 if chunks[idx] == 'ignore':
836 idx = idx + 1
837 continue
838 m = get_re ('geometry').search(chunks[idx][1])
839 if m:
840 paperguru.m_use_geometry = 1
841 o = parse_options_string(m.group('options'))
842 for k in o.keys():
843 paperguru.set_geo_option(k, o[k])
844 idx = idx + 1
846 def scan_texi_preamble (chunks):
847 # this is not bulletproof..., it checks the first 10 chunks
848 for c in chunks[:10]:
849 if c[0] == 'input':
850 for s in ('afourpaper', 'afourwide', 'letterpaper',
851 'afourlatex', 'smallbook'):
852 if string.find(c[1], "@%s" % s) != -1:
853 paperguru.m_papersize = s
856 def scan_preamble (chunks):
857 if __main__.format == 'html':
858 scan_html_preamble (chunks)
859 elif __main__.format == 'latex':
860 scan_latex_preamble (chunks)
861 elif __main__.format == 'texi':
862 scan_texi_preamble (chunks)
865 def completize_preamble (chunks):
866 if __main__.format != 'latex':
867 return chunks
868 pre_b = post_b = graphics_b = None
869 for chunk in chunks:
870 if chunk[0] == 'preamble-end':
871 break
872 if chunk[0] == 'input':
873 m = get_re('def-pre-re').search(chunk[1])
874 if m:
875 pre_b = 1
876 if chunk[0] == 'input':
877 m = get_re('def-post-re').search(chunk[1])
878 if m:
879 post_b = 1
881 if chunk[0] == 'input':
882 m = get_re('usepackage-graphics').search(chunk[1])
883 if m:
884 graphics_b = 1
885 x = 0
886 while x < len (chunks) and chunks[x][0] != 'preamble-end':
887 x = x + 1
889 if x == len(chunks):
890 return chunks
892 if not pre_b:
893 chunks.insert(x, ('input', get_output ('output-default-pre')))
894 if not post_b:
895 chunks.insert(x, ('input', get_output ('output-default-post')))
896 if not graphics_b:
897 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
899 return chunks
902 read_files = []
903 def find_file (name):
905 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
908 if name == '-':
909 return (sys.stdin.read (), '<stdin>')
910 f = None
911 nm = ''
912 for a in include_path:
913 try:
914 nm = os.path.join (a, name)
915 f = open (nm)
916 __main__.read_files.append (nm)
917 break
918 except IOError:
919 pass
920 if f:
921 sys.stderr.write ("Reading `%s'\n" % nm)
922 return (f.read (), nm)
923 else:
924 error ("File not found `%s'\n" % name)
925 return ('', '')
927 def do_ignore(match_object):
928 return [('ignore', match_object.group('code'))]
929 def do_preamble_end(match_object):
930 return [('preamble-end', match_object.group('code'))]
932 def make_verbatim(match_object):
933 return [('verbatim', match_object.group('code'))]
935 def make_verb(match_object):
936 return [('verb', match_object.group('code'))]
938 def do_include_file(m):
939 "m: MatchObject"
940 return [('input', get_output ('pagebreak'))] \
941 + read_doc_file(m.group('filename')) \
942 + [('input', get_output ('pagebreak'))]
944 def do_input_file(m):
945 return read_doc_file(m.group('filename'))
947 def make_lilypond(m):
948 if m.group('options'):
949 options = m.group('options')
950 else:
951 options = ''
952 return [('input', get_output('output-lilypond-fragment') %
953 (options, m.group('code')))]
955 def make_lilypond_file(m):
958 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
959 into a @lilypond .. @end lilypond block.
963 if m.group('options'):
964 options = m.group('options')
965 else:
966 options = ''
967 (content, nm) = find_file(m.group('filename'))
968 options = "filename=%s," % nm + options
970 return [('input', get_output('output-lilypond') %
971 (options, content))]
973 def make_lilypond_block(m):
974 if not g_do_music:
975 return []
977 if m.group('options'):
978 options = get_re('option-sep').split (m.group('options'))
979 else:
980 options = []
981 options = filter(lambda s: s != '', options)
982 return [('lilypond', m.group('code'), options)]
984 def do_columns(m):
985 if __main__.format != 'latex':
986 return []
987 if m.group('num') == 'one':
988 return [('numcols', m.group('code'), 1)]
989 if m.group('num') == 'two':
990 return [('numcols', m.group('code'), 2)]
992 def chop_chunks(chunks, re_name, func, use_match=0):
993 newchunks = []
994 for c in chunks:
995 if c[0] == 'input':
996 str = c[1]
997 while str:
998 m = get_re (re_name).search (str)
999 if m == None:
1000 newchunks.append (('input', str))
1001 str = ''
1002 else:
1003 if use_match:
1004 newchunks.append (('input', str[:m.start ('match')]))
1005 else:
1006 newchunks.append (('input', str[:m.start (0)]))
1007 #newchunks.extend(func(m))
1008 # python 1.5 compatible:
1009 newchunks = newchunks + func(m)
1010 str = str [m.end(0):]
1011 else:
1012 newchunks.append(c)
1013 return newchunks
1015 def determine_format (str):
1016 if __main__.format == '':
1018 html = re.search ('(?i)<[dh]tml', str[:200])
1019 latex = re.search (r'''\\document''', str[:200])
1020 texi = re.search ('@node|@setfilename', str[:200])
1022 f = ''
1023 g = None
1025 if html and not latex and not texi:
1026 f = 'html'
1027 elif latex and not html and not texi:
1028 f = 'latex'
1029 elif texi and not html and not latex:
1030 f = 'texi'
1031 else:
1032 error ("can't determine format, please specify")
1033 __main__.format = f
1035 if __main__.paperguru == None:
1036 if __main__.format == 'html':
1037 g = HtmlPaper ()
1038 elif __main__.format == 'latex':
1039 g = LatexPaper ()
1040 elif __main__.format == 'texi':
1041 g = TexiPaper ()
1043 __main__.paperguru = g
1046 def read_doc_file (filename):
1047 '''Read the input file, find verbatim chunks and do \input and \include
1049 (str, path) = find_file(filename)
1050 determine_format (str)
1052 chunks = [('input', str)]
1054 # we have to check for verbatim before doing include,
1055 # because we don't want to include files that are mentioned
1056 # inside a verbatim environment
1057 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
1058 chunks = chop_chunks(chunks, 'verb', make_verb)
1059 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
1060 #ugh fix input
1061 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
1062 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
1063 return chunks
1066 taken_file_names = {}
1067 def schedule_lilypond_block (chunk):
1068 '''Take the body and options from CHUNK, figure out how the
1069 real .ly should look, and what should be left MAIN_STR (meant
1070 for the main file). The .ly is written, and scheduled in
1071 TODO.
1073 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
1075 TODO has format [basename, extension, extension, ... ]
1078 (type, body, opts) = chunk
1079 assert type == 'lilypond'
1080 file_body = compose_full_body (body, opts)
1081 ## Hmm, we should hash only lilypond source, and skip the
1082 ## %options are ...
1083 ## comment line
1084 basename = 'lily-' + `abs(hash (file_body))`
1085 for o in opts:
1086 m = re.search ('filename="(.*?)"', o)
1087 if m:
1088 basename = m.group (1)
1089 if not taken_file_names.has_key(basename):
1090 taken_file_names[basename] = 0
1091 else:
1092 taken_file_names[basename] = taken_file_names[basename] + 1
1093 basename = basename + "-%i" % taken_file_names[basename]
1094 if not g_read_lys:
1095 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
1096 needed_filetypes = ['tex']
1098 if format == 'html' or format == 'texi':
1099 needed_filetypes.append ('eps')
1100 needed_filetypes.append ('png')
1101 if 'eps' in opts and not ('eps' in needed_filetypes):
1102 needed_filetypes.append('eps')
1103 pathbase = os.path.join (g_outdir, basename)
1104 def f (base, ext1, ext2):
1105 a = os.path.isfile(base + ext2)
1106 if (os.path.isfile(base + ext1) and
1107 os.path.isfile(base + ext2) and
1108 os.stat(base+ext1)[stat.ST_MTIME] >
1109 os.stat(base+ext2)[stat.ST_MTIME]) or \
1110 not os.path.isfile(base + ext2):
1111 return 1
1112 todo = []
1113 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
1114 todo.append('tex')
1115 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
1116 todo.append('eps')
1117 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
1118 todo.append('png')
1119 newbody = ''
1121 if 'printfilename' in opts:
1122 for o in opts:
1123 m= re.match ("filename=(.*)", o)
1124 if m:
1125 newbody = newbody + get_output ("output-filename") % m.group(1)
1126 break
1129 if 'verbatim' in opts:
1130 newbody = output_verbatim (body)
1132 for o in opts:
1133 m = re.search ('intertext="(.*?)"', o)
1134 if m:
1135 newbody = newbody + m.group (1) + "\n\n"
1137 if 'noinline' in opts:
1138 s = 'output-noinline'
1139 elif format == 'latex':
1140 if 'eps' in opts:
1141 s = 'output-eps'
1142 else:
1143 s = 'output-tex'
1144 else: # format == 'html' or format == 'texi':
1145 s = 'output-all'
1146 newbody = newbody + get_output (s) % {'fn': basename }
1147 return ('lilypond', newbody, opts, todo, basename)
1149 def process_lilypond_blocks(chunks):#ugh rename
1150 newchunks = []
1151 # Count sections/chapters.
1152 for c in chunks:
1153 if c[0] == 'lilypond':
1154 c = schedule_lilypond_block (c)
1155 elif c[0] == 'numcols':
1156 paperguru.m_num_cols = c[2]
1157 newchunks.append (c)
1158 return newchunks
1162 def system (cmd):
1163 sys.stderr.write ("invoking `%s'\n" % cmd)
1164 st = os.system (cmd)
1165 if st:
1166 error ('Error command exited with value %d\n' % st)
1167 return st
1169 def quiet_system (cmd, name):
1170 if not verbose_p:
1171 progress ( _("Running %s...") % name)
1172 cmd = cmd + ' 1> /dev/null 2> /dev/null'
1174 return system (cmd)
1176 def get_bbox (filename):
1177 system ('gs -sDEVICE=bbox -q -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename, filename))
1179 box = open (filename + '.bbox').read()
1180 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
1181 gr = []
1182 if m:
1183 gr = map (string.atoi, m.groups ())
1185 return gr
1187 def make_pixmap (name):
1188 bbox = get_bbox (name + '.eps')
1189 margin = 0
1190 fo = open (name + '.trans.eps' , 'w')
1191 fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
1192 fo.close ()
1194 res = 90
1196 x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1197 y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1199 cmd = r'''gs -g%dx%d -sDEVICE=pnggray -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit > %s'''
1201 cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
1202 quiet_system (cmd, 'gs')
1204 try:
1205 status = system (cmd)
1206 except:
1207 os.unlink (name + '.png')
1208 error ("Removing output file")
1210 def compile_all_files (chunks):
1211 global foutn
1212 eps = []
1213 tex = []
1214 png = []
1216 for c in chunks:
1217 if c[0] <> 'lilypond':
1218 continue
1219 base = c[4]
1220 exts = c[3]
1221 for e in exts:
1222 if e == 'eps':
1223 eps.append (base)
1224 elif e == 'tex':
1225 #ugh
1226 if base + '.ly' not in tex:
1227 tex.append (base + '.ly')
1228 elif e == 'png' and g_do_pictures:
1229 png.append (base)
1230 d = os.getcwd()
1231 if g_outdir:
1232 os.chdir(g_outdir)
1233 if tex:
1234 # fixme: be sys-independent.
1235 def incl_opt (x):
1236 if g_outdir and x[0] <> '/' :
1237 x = os.path.join (g_here_dir, x)
1238 return ' -I %s' % x
1240 incs = map (incl_opt, include_path)
1241 lilyopts = string.join (incs, ' ' )
1242 if do_deps:
1243 lilyopts = lilyopts + ' --dependencies '
1244 if g_outdir:
1245 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1246 texfiles = string.join (tex, ' ')
1247 cmd = 'lilypond --header=texidoc %s %s %s' \
1248 % (lilyopts, g_extra_opts, texfiles)
1250 system (cmd)
1253 # Ugh, fixing up dependencies for .tex generation
1255 if do_deps:
1256 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1257 for i in depfiles:
1258 f =open (i)
1259 text=f.read ()
1260 f.close ()
1261 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1262 f = open (i, 'w')
1263 f.write (text)
1264 f.close ()
1266 for e in eps:
1267 cmd = r"tex '\nonstopmode \input %s'" % e
1268 quiet_system (cmd, 'TeX')
1270 cmd = r"dvips -E -o %s %s" % (e + '.eps', e)
1271 quiet_system (cmd, 'dvips')
1273 for g in png:
1274 make_pixmap (g)
1276 os.chdir (d)
1279 def update_file (body, name):
1281 write the body if it has changed
1283 same = 0
1284 try:
1285 f = open (name)
1286 fs = f.read (-1)
1287 same = (fs == body)
1288 except:
1289 pass
1291 if not same:
1292 f = open (name , 'w')
1293 f.write (body)
1294 f.close ()
1296 return not same
1299 def getopt_args (opts):
1300 "Construct arguments (LONG, SHORT) for getopt from list of options."
1301 short = ''
1302 long = []
1303 for o in opts:
1304 if o[1]:
1305 short = short + o[1]
1306 if o[0]:
1307 short = short + ':'
1308 if o[2]:
1309 l = o[2]
1310 if o[0]:
1311 l = l + '='
1312 long.append (l)
1313 return (short, long)
1315 def option_help_str (o):
1316 "Transform one option description (4-tuple ) into neatly formatted string"
1317 sh = ' '
1318 if o[1]:
1319 sh = '-%s' % o[1]
1321 sep = ' '
1322 if o[1] and o[2]:
1323 sep = ','
1325 long = ''
1326 if o[2]:
1327 long= '--%s' % o[2]
1329 arg = ''
1330 if o[0]:
1331 if o[2]:
1332 arg = '='
1333 arg = arg + o[0]
1334 return ' ' + sh + sep + long + arg
1337 def options_help_str (opts):
1338 "Convert a list of options into a neatly formatted string"
1339 w = 0
1340 strs =[]
1341 helps = []
1343 for o in opts:
1344 s = option_help_str (o)
1345 strs.append ((s, o[3]))
1346 if len (s) > w:
1347 w = len (s)
1349 str = ''
1350 for s in strs:
1351 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1352 return str
1354 def help():
1355 sys.stdout.write('''Usage: lilypond-book [options] FILE\n
1356 Generate hybrid LaTeX input from Latex + lilypond
1357 Options:
1358 ''')
1359 sys.stdout.write (options_help_str (option_definitions))
1360 sys.stdout.write (r'''Warning all output is written in the CURRENT directory
1364 Report bugs to bug-lilypond@gnu.org.
1366 Written by Tom Cato Amundsen <tca@gnu.org> and
1367 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1368 ''')
1370 sys.exit (0)
1373 def write_deps (fn, target, chunks):
1374 global read_files
1375 sys.stderr.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1376 f = open (os.path.join(g_outdir, fn), 'w')
1377 f.write ('%s%s: ' % (g_dep_prefix, target))
1378 for d in read_files:
1379 f.write ('%s ' % d)
1380 basenames=[]
1381 for c in chunks:
1382 if c[0] == 'lilypond':
1383 (type, body, opts, todo, basename) = c;
1384 basenames.append (basename)
1385 for d in basenames:
1386 if g_outdir:
1387 d=g_outdir + '/' + d
1388 if g_dep_prefix:
1389 #if not os.isfile (d): # thinko?
1390 if not re.search ('/', d):
1391 d = g_dep_prefix + d
1392 f.write ('%s.tex ' % d)
1393 f.write ('\n')
1394 #if len (basenames):
1395 # for d in basenames:
1396 # f.write ('%s.ly ' % d)
1397 # f.write (' : %s' % target)
1398 f.write ('\n')
1399 f.close ()
1400 read_files = []
1402 def identify (stream):
1403 stream.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1405 def print_version ():
1406 identify (sys.stdout)
1407 sys.stdout.write (r'''Copyright 1998--1999
1408 Distributed under terms of the GNU General Public License. It comes with
1409 NO WARRANTY.
1410 ''')
1413 def check_texidoc (chunks):
1414 n = []
1415 for c in chunks:
1416 if c[0] == 'lilypond':
1417 (type, body, opts, todo, basename) = c;
1418 pathbase = os.path.join (g_outdir, basename)
1419 if os.path.isfile (pathbase + '.texidoc'):
1420 body = '\n@include %s.texidoc\n' % basename + body
1421 c = (type, body, opts, todo, basename)
1422 n.append (c)
1423 return n
1426 ## what's this? Docme --hwn
1428 def fix_epswidth (chunks):
1429 newchunks = []
1430 for c in chunks:
1431 if c[0] <> 'lilypond' or 'eps' not in c[2]:
1432 newchunks.append (c)
1433 continue
1435 mag = 1.0
1436 for o in c[2]:
1437 m = re.match ('magnification=([0-9.]+)', o)
1438 if m:
1439 mag = string.atof (m.group (1))
1441 def replace_eps_dim (match, lmag = mag):
1442 filename = match.group (1)
1443 dims = bounding_box_dimensions (filename)
1445 return '%fpt' % (dims[0] *lmag)
1447 body = re.sub (r'''\\lilypondepswidth{(.*?)}''', replace_eps_dim, c[1])
1448 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1450 return newchunks
1453 ##docme: why global?
1454 foutn=""
1455 def do_file(input_filename):
1457 chunks = read_doc_file(input_filename)
1458 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1459 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1460 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1461 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1462 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1463 chunks = chop_chunks(chunks, 'numcols', do_columns)
1464 #print "-" * 50
1465 #for c in chunks: print "c:", c;
1466 #sys.exit()
1467 scan_preamble(chunks)
1468 chunks = process_lilypond_blocks(chunks)
1470 # Do It.
1471 if __main__.g_run_lilypond:
1472 compile_all_files (chunks)
1473 chunks = fix_epswidth (chunks)
1475 if __main__.format == 'texi':
1476 chunks = check_texidoc (chunks)
1478 x = 0
1479 chunks = completize_preamble (chunks)
1482 global foutn
1484 if outname:
1485 my_outname = outname
1486 elif input_filename == '-' or input_filename == "/dev/stdin":
1487 my_outname = '-'
1488 else:
1489 my_outname = os.path.basename (os.path.splitext(input_filename)[0]) + '.' + format
1490 my_depname = my_outname + '.dep'
1492 if my_outname == '-' or my_outname == '/dev/stdout':
1493 fout = sys.stdout
1494 foutn = "<stdout>"
1495 __main__.do_deps = 0
1496 else:
1497 foutn = os.path.join (g_outdir, my_outname)
1498 sys.stderr.write ("Writing `%s'\n" % foutn)
1499 fout = open (foutn, 'w')
1500 for c in chunks:
1501 fout.write (c[1])
1502 fout.close ()
1503 # should chmod -w
1505 if do_deps:
1506 write_deps (my_depname, foutn, chunks)
1509 outname = ''
1510 try:
1511 (sh, long) = getopt_args (__main__.option_definitions)
1512 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1513 except getopt.error, msg:
1514 sys.stderr.write("error: %s" % msg)
1515 sys.exit(1)
1517 do_deps = 0
1518 for opt in options:
1519 o = opt[0]
1520 a = opt[1]
1522 if o == '--include' or o == '-I':
1523 include_path.append (a)
1524 elif o == '--version' or o == '-v':
1525 print_version ()
1526 sys.exit (0)
1527 elif o == '--verbose' or o == '-V':
1528 __main__.verbose_p = 1
1529 elif o == '--format' or o == '-f':
1530 __main__.format = a
1531 elif o == '--outname' or o == '-o':
1532 if len(files) > 1:
1533 #HACK
1534 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1535 sys.exit(1)
1536 outname = a
1537 elif o == '--help' or o == '-h':
1538 help ()
1539 elif o == '--no-lily' or o == '-n':
1540 __main__.g_run_lilypond = 0
1541 elif o == '--dependencies' or o == '-M':
1542 do_deps = 1
1543 elif o == '--default-music-fontsize':
1544 default_music_fontsize = string.atoi (a)
1545 elif o == '--default-lilypond-fontsize':
1546 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1547 default_music_fontsize = string.atoi (a)
1548 elif o == '--extra-options':
1549 g_extra_opts = a
1550 elif o == '--force-music-fontsize':
1551 g_force_lilypond_fontsize = string.atoi(a)
1552 elif o == '--force-lilypond-fontsize':
1553 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1554 g_force_lilypond_fontsize = string.atoi(a)
1555 elif o == '--dep-prefix':
1556 g_dep_prefix = a
1557 elif o == '--no-pictures':
1558 g_do_pictures = 0
1559 elif o == '--no-music':
1560 g_do_music = 0
1561 elif o == '--read-lys':
1562 g_read_lys = 1
1563 elif o == '--outdir':
1564 g_outdir = a
1566 identify (sys.stderr)
1567 if g_outdir:
1568 if os.path.isfile(g_outdir):
1569 error ("outdir is a file: %s" % g_outdir)
1570 if not os.path.exists(g_outdir):
1571 os.mkdir(g_outdir)
1572 setup_environment ()
1573 for input_filename in files:
1574 do_file(input_filename)
1577 # Petr, ik zou willen dat ik iets zinvoller deed,
1578 # maar wat ik kan ik doen, het verandert toch niets?
1579 # --hwn 20/aug/99