2 # vim: set noexpandtab:
4 # * Figure out clean set of options. Hmm, isn't it pretty ok now?
5 # * add support for .lilyrc
6 # * EndLilyPondOutput is def'd as vfil. Causes large white gaps.
7 # * texinfo: add support for @pagesize
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
14 # This is was the idea for handling of comments:
15 # Multiline comments, @ignore .. @end ignore is scanned for
16 # in read_doc_file, and the chunks are marked as 'ignore', so
17 # lilypond-book will not touch them any more. The content of the
18 # chunks are written to the output file. Also 'include' and 'input'
19 # regex has to check if they are commented out.
21 # Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
22 # These three regex's has to check if they are on a commented line,
23 # % for latex, @c for texinfo.
25 # Then lines that are commented out with % (latex) and @c (Texinfo)
26 # are put into chunks marked 'ignore'. This cannot be done before
27 # searching for the lilypond-blocks because % is also the comment character
30 # The the rest of the rexeces are searched for. They don't have to test
31 # if they are on a commented out line.
43 program_version
= '@TOPLEVEL_VERSION@'
44 if program_version
== '@' + 'TOPLEVEL_VERSION' + '@':
45 program_version
= '1.3.113'
47 include_path
= [os
.getcwd()]
50 # g_ is for global (?)
52 g_here_dir
= os
.getcwd ()
55 g_force_lilypond_fontsize
= 0
63 default_music_fontsize
= 16
64 default_text_fontsize
= 12
67 # this code is ugly. It should be cleaned
71 # the dimensions are from geometry.sty
72 'a0paper': (mm2pt(841), mm2pt(1189)),
73 'a1paper': (mm2pt(595), mm2pt(841)),
74 'a2paper': (mm2pt(420), mm2pt(595)),
75 'a3paper': (mm2pt(297), mm2pt(420)),
76 'a4paper': (mm2pt(210), mm2pt(297)),
77 'a5paper': (mm2pt(149), mm2pt(210)),
78 'b0paper': (mm2pt(1000), mm2pt(1414)),
79 'b1paper': (mm2pt(707), mm2pt(1000)),
80 'b2paper': (mm2pt(500), mm2pt(707)),
81 'b3paper': (mm2pt(353), mm2pt(500)),
82 'b4paper': (mm2pt(250), mm2pt(353)),
83 'b5paper': (mm2pt(176), mm2pt(250)),
84 'letterpaper': (in2pt(8.5), in2pt(11)),
85 'legalpaper': (in2pt(8.5), in2pt(14)),
86 'executivepaper': (in2pt(7.25), in2pt(10.5))}
87 self
.m_use_geometry
= None
88 self
.m_papersize
= 'letterpaper'
92 self
.m_geo_landscape
= 0
93 self
.m_geo_width
= None
94 self
.m_geo_textwidth
= None
95 self
.m_geo_lmargin
= None
96 self
.m_geo_rmargin
= None
97 self
.m_geo_includemp
= None
98 self
.m_geo_marginparwidth
= {10: 57, 11: 50, 12: 35}
99 self
.m_geo_marginparsep
= {10: 11, 11: 10, 12: 10}
100 self
.m_geo_x_marginparwidth
= None
101 self
.m_geo_x_marginparsep
= None
103 def set_geo_option(self
, name
, value
):
104 if name
== 'body' or name
== 'text':
105 if type(value
) == type(""):
106 self
.m_geo_textwidth
= value
108 self
.m_geo_textwidth
= value
[0]
110 elif name
== 'portrait':
111 self
.m_geo_landscape
= 0
112 elif name
== 'reversemp' or name
== 'reversemarginpar':
113 if self
.m_geo_includemp
== None:
114 self
.m_geo_includemp
= 1
115 elif name
== 'marginparwidth' or name
== 'marginpar':
116 self
.m_geo_x_marginparwidth
= value
117 self
.m_geo_includemp
= 1
118 elif name
== 'marginparsep':
119 self
.m_geo_x_marginparsep
= value
120 self
.m_geo_includemp
= 1
121 elif name
== 'scale':
122 if type(value
) == type(""):
123 self
.m_geo_width
= self
.get_paperwidth() * float(value
)
125 self
.m_geo_width
= self
.get_paperwidth() * float(value
[0])
126 elif name
== 'hscale':
127 self
.m_geo_width
= self
.get_paperwidth() * float(value
)
128 elif name
== 'left' or name
== 'lmargin':
129 self
.m_geo_lmargin
= value
130 elif name
== 'right' or name
== 'rmargin':
131 self
.m_geo_rmargin
= value
132 elif name
== 'hdivide' or name
== 'divide':
133 if value
[0] not in ('*', ''):
134 self
.m_geo_lmargin
= value
[0]
135 if value
[1] not in ('*', ''):
136 self
.m_geo_width
= value
[1]
137 if value
[2] not in ('*', ''):
138 self
.m_geo_rmargin
= value
[2]
139 elif name
== 'hmargin':
140 if type(value
) == type(""):
141 self
.m_geo_lmargin
= value
142 self
.m_geo_rmargin
= value
144 self
.m_geo_lmargin
= value
[0]
145 self
.m_geo_rmargin
= value
[1]
146 elif name
== 'margin':#ugh there is a bug about this option in
147 # the geometry documentation
148 if type(value
) == type(""):
149 self
.m_geo_lmargin
= value
150 self
.m_geo_rmargin
= value
152 self
.m_geo_lmargin
= value
[0]
153 self
.m_geo_rmargin
= value
[0]
154 elif name
== 'total':
155 if type(value
) == type(""):
156 self
.m_geo_width
= value
158 self
.m_geo_width
= value
[0]
159 elif name
== 'width' or name
== 'totalwidth':
160 self
.m_geo_width
= value
161 elif name
== 'paper' or name
== 'papername':
162 self
.m_papersize
= value
163 elif name
[-5:] == 'paper':
164 self
.m_papersize
= name
166 self
._set
_dimen
('m_geo_'+name
, value
)
167 def __setattr__(self
, name
, value
):
168 if type(value
) == type("") and \
169 dimension_conversion_dict
.has_key (value
[-2:]):
170 f
= dimension_conversion_dict
[dim
]
171 self
.__dict
__[name
] = f(float(value
[:-2]))
173 self
.__dict
__[name
] = value
176 s
= "LatexPaper:\n-----------"
177 for v
in self
.__dict
__.keys():
179 s
= s
+ str (v
) + ' ' + str (self
.__dict
__[v
])
180 s
= s
+ "-----------"
183 def get_linewidth(self
):
184 w
= self
._calc
_linewidth
()
185 if self
.m_num_cols
== 2:
189 def get_paperwidth(self
):
190 #if self.m_use_geometry:
191 return self
.m_paperdef
[self
.m_papersize
][self
.m_landscape
or self
.m_geo_landscape
]
192 #return self.m_paperdef[self.m_papersize][self.m_landscape]
194 def _calc_linewidth(self
):
195 # since geometry sometimes ignores 'includemp', this is
196 # more complicated than it should be
198 if self
.m_geo_includemp
:
199 if self
.m_geo_x_marginparsep
is not None:
200 mp
= mp
+ self
.m_geo_x_marginparsep
202 mp
= mp
+ self
.m_geo_marginparsep
[self
.m_fontsize
]
203 if self
.m_geo_x_marginparwidth
is not None:
204 mp
= mp
+ self
.m_geo_x_marginparwidth
206 mp
= mp
+ self
.m_geo_marginparwidth
[self
.m_fontsize
]
208 #ugh test if this is necessary
212 if not self
.m_use_geometry
:
213 return latex_linewidths
[self
.m_papersize
][self
.m_fontsize
]
215 geo_opts
= (a
== None, b
== None, c
== None)
217 if geo_opts
== (1, 1, 1):
218 if self
.m_geo_textwidth
:
219 return self
.m_geo_textwidth
220 w
= self
.get_paperwidth() * 0.8
222 elif geo_opts
== (0, 1, 1):
223 if self
.m_geo_textwidth
:
224 return self
.m_geo_textwidth
225 return self
.f1(self
.m_geo_lmargin
, mp
)
226 elif geo_opts
== (1, 1, 0):
227 if self
.m_geo_textwidth
:
228 return self
.m_geo_textwidth
229 return self
.f1(self
.m_geo_rmargin
, mp
)
231 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
232 if self
.m_geo_textwidth
:
233 return self
.m_geo_textwidth
234 return self
.m_geo_width
- mp
235 elif geo_opts
in ((0, 1, 0), (0, 0, 0)):
236 w
= self
.get_paperwidth() \
237 - self
.m_geo_lmargin
- self
.m_geo_rmargin
- mp
241 raise "Never do this!"
243 tmp
= self
.get_paperwidth() - m
* 2 - mp
248 tmp
= self
.get_paperwidth() - self
.m_geo_lmargin \
256 self
.m_papersize
= 'letterpaper'
258 def get_linewidth(self
):
259 return texi_linewidths
[self
.m_papersize
][self
.m_fontsize
]
265 def em2pt(x
, fontsize
= 10):
266 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize
] * x
267 def ex2pt(x
, fontsize
= 10):
268 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize
] * x
273 dimension_conversion_dict
={
283 # indices are no. of columns, papersize, fontsize
284 # Why can't this be calculated?
286 'a4paper':{10: 345, 11: 360, 12: 390},
287 'a4paper-landscape': {10: 598, 11: 596, 12:592},
288 'a5paper':{10: 276, 11: 276, 12: 276},
289 'b5paper':{10: 345, 11: 356, 12: 356},
290 'letterpaper':{10: 345, 11: 360, 12: 390},
291 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
292 'legalpaper': {10: 345, 11: 360, 12: 390},
293 'executivepaper':{10: 345, 11: 360, 12: 379}}
296 'afourpaper': {12: mm2pt(160)},
297 'afourwide': {12: in2pt(6.5)},
298 'afourlatex': {12: mm2pt(150)},
299 'smallbook': {12: in2pt(5)},
300 'letterpaper': {12: in2pt(6)}}
302 option_definitions
= [
303 ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'),
304 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
305 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
306 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
307 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
308 ('DIR', 'I', 'include', 'include path'),
309 ('', 'M', 'dependencies', 'write dependencies'),
310 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
311 ('', 'n', 'no-lily', 'don\'t run lilypond'),
312 ('', '', 'no-pictures', "don\'t generate pictures"),
313 ('', '', 'read-lys', "don't write ly files."),
314 ('FILE', 'o', 'outname', 'filename main output file'),
315 ('FILE', '', 'outdir', "where to place generated files"),
316 ('', 'v', 'version', 'print version information' ),
317 ('', 'h', 'help', 'print help'),
320 # format specific strings, ie. regex-es for input, and % strings for output
323 'output-lilypond-fragment' : r
"""\begin[eps,singleline,%s]{lilypond}
330 'output-filename' : r
'''
333 'output-lilypond': r
"""\begin[%s]{lilypond}
336 'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
337 'output-default-post': "\\def\postLilypondExample{}\n",
338 'output-default-pre': "\\def\preLilypondExample{}\n",
339 'usepackage-graphics': '\\usepackage{graphics}\n',
340 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
341 'output-tex': '\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n',
342 'pagebreak': r
'\pagebreak',
344 'texi' : {'output-lilypond': """@lilypond[%s]
348 'output-filename' : r
'''
351 'output-lilypond-fragment': """@lilypond[%s]
352 \context Staff\context Voice{ %s }
355 'output-verbatim': r
"""@example
360 # do some tweaking: @ is needed in some ps stuff.
361 # override EndLilyPondOutput, since @tex is done
362 # in a sandbox, you can't do \input lilyponddefs at the
363 # top of the document.
365 # should also support fragment in
371 \def\EndLilyPondOutput{}
383 def output_verbatim (body
):
384 if __main__
.format
== 'texi':
385 body
= re
.sub ('([@{}])', '@\\1', body
)
386 return get_output ('output-verbatim') % body
390 'latex': {'input': r
'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
391 'include': r
'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
392 'option-sep' : ', *',
393 'header': r
"\\documentclass\s*(\[.*?\])?",
394 'geometry': r
"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
395 'preamble-end': r
'(?P<code>\\begin{document})',
396 'verbatim': r
"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
397 'verb': r
"(?P<code>\\verb(?P<del>.).*?(?P=del))",
398 'lilypond-file': r
'(?m)^[^%\n]*?(?P<match>\\lilypondfile(\[(?P<options>.*?)\])?\{(?P<filename>.+)})',
399 'lilypond' : r
'(?m)^[^%\n]*?(?P<match>\\lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
400 'lilypond-block': r
"(?sm)^[^%\n]*?(?P<match>\\begin(\[(?P<options>.*?)\])?{lilypond}(?P<code>.*?)\\end{lilypond})",
401 'def-post-re': r
"\\def\\postLilypondExample",
402 'def-pre-re': r
"\\def\\preLilypondExample",
403 'usepackage-graphics': r
"\usepackage{graphics}",
404 'intertext': r
',?\s*intertext=\".*?\"',
405 'multiline-comment': no_match
,
406 'singleline-comment': r
"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
407 'numcols': r
"(?P<code>\\(?P<num>one|two)column)",
411 # why do we have distinction between @mbinclude and @include?
413 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
416 'preamble-end': no_match
,
417 'landscape': no_match
,
418 'verbatim': r
"""(?s)(?P<code>@example\s.*?@end example\s)""",
419 'verb': r
"""(?P<code>@code{.*?})""",
420 'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
421 'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
422 'lilypond-block': r
"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
423 'option-sep' : ', *',
424 'intertext': r
',?\s*intertext=\".*?\"',
425 'multiline-comment': r
"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
426 'singleline-comment': r
"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
432 for r
in re_dict
.keys ():
435 for k
in olddict
.keys ():
436 newdict
[k
] = re
.compile (olddict
[k
])
450 def get_output (name
):
451 return output_dict
[format
][name
]
454 return re_dict
[format
][name
]
456 def bounding_box_dimensions(fname
):
458 fname
= os
.path
.join(g_outdir
, fname
)
462 error ("Error opening `%s'" % fname
)
464 s
= re
.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
466 return (int(s
.group(3))-int(s
.group(1)),
467 int(s
.group(4))-int(s
.group(2)))
473 sys
.stderr
.write (str + "\n Exiting ... \n\n")
477 def compose_full_body (body
, opts
):
478 """Construct the lilypond code to send to Lilypond.
479 Add stuff to BODY using OPTS as options."""
480 music_size
= default_music_fontsize
481 latex_size
= default_text_fontsize
483 if g_force_lilypond_fontsize
:
484 music_size
= g_force_lilypond_fontsize
486 m
= re
.match ('([0-9]+)pt', o
)
488 music_size
= string
.atoi(m
.group (1))
490 m
= re
.match ('latexfontsize=([0-9]+)pt', o
)
492 latex_size
= string
.atoi (m
.group (1))
494 if re
.search ('\\\\score', body
):
498 if 'fragment' in opts
:
500 if 'nonfragment' in opts
:
503 if is_fragment
and not 'multiline' in opts
:
504 opts
.append('singleline')
505 if 'singleline' in opts
:
508 l
= __main__
.paperguru
.get_linewidth()
510 if 'relative' in opts
:#ugh only when is_fragment
511 body
= '\\relative c { %s }' % body
520 optstring
= string
.join (opts
, ' ')
521 optstring
= re
.sub ('\n', ' ', optstring
)
523 %% Generated by lilypond-book.py; options are %s %%ughUGH not original options
524 \include "paper%d.ly"
525 \paper { linewidth = %f \pt; }
526 """ % (optstring
, music_size
, l
) + body
529 def parse_options_string(s
):
531 r1
= re
.compile("((\w+)={(.*?)})((,\s*)|$)")
532 r2
= re
.compile("((\w+)=(.*?))((,\s*)|$)")
533 r3
= re
.compile("(\w+?)((,\s*)|$)")
538 d
[m
.group(2)] = re
.split(",\s*", m
.group(3))
543 d
[m
.group(2)] = m
.group(3)
551 error ("format of option string invalid (was `%')" % s
)
554 def scan_latex_preamble(chunks
):
555 # first we want to scan the \documentclass line
556 # it should be the first non-comment line
559 if chunks
[idx
][0] == 'ignore':
562 m
= get_re ('header').match(chunks
[idx
][1])
563 options
= re
.split (',[\n \t]*', m
.group(1)[1:-1])
566 paperguru
.m_landscape
= 1
567 m
= re
.match("(.*?)paper", o
)
569 paperguru
.m_papersize
= m
.group()
571 m
= re
.match("(\d\d)pt", o
)
573 paperguru
.m_fontsize
= int(m
.group(1))
576 while chunks
[idx
][0] != 'preamble-end':
577 if chunks
[idx
] == 'ignore':
580 m
= get_re ('geometry').search(chunks
[idx
][1])
582 paperguru
.m_use_geometry
= 1
583 o
= parse_options_string(m
.group('options'))
585 paperguru
.set_geo_option(k
, o
[k
])
588 def scan_texi_preamble (chunks
):
589 # this is not bulletproof..., it checks the first 10 chunks
590 for c
in chunks
[:10]:
592 for s
in ('afourpaper', 'afourwide', 'letterpaper',
593 'afourlatex', 'smallbook'):
594 if string
.find(c
[1], "@%s" % s
) != -1:
595 paperguru
.m_papersize
= s
597 def scan_preamble (chunks
):
598 if __main__
.format
== 'texi':
599 scan_texi_preamble(chunks
)
601 assert __main__
.format
== 'latex'
602 scan_latex_preamble(chunks
)
605 def completize_preamble (chunks
):
606 if __main__
.format
== 'texi':
608 pre_b
= post_b
= graphics_b
= None
610 if chunk
[0] == 'preamble-end':
612 if chunk
[0] == 'input':
613 m
= get_re('def-pre-re').search(chunk
[1])
616 if chunk
[0] == 'input':
617 m
= get_re('def-post-re').search(chunk
[1])
620 if chunk
[0] == 'input':
621 m
= get_re('usepackage-graphics').search(chunk
[1])
625 while chunks
[x
][0] != 'preamble-end':
628 chunks
.insert(x
, ('input', get_output ('output-default-pre')))
630 chunks
.insert(x
, ('input', get_output ('output-default-post')))
632 chunks
.insert(x
, ('input', get_output ('usepackage-graphics')))
637 def find_file (name
):
639 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
644 for a
in include_path
:
646 nm
= os
.path
.join (a
, name
)
648 __main__
.read_files
.append (nm
)
653 sys
.stderr
.write ("Reading `%s'\n" % nm
)
654 return (f
.read (), nm
)
656 error ("File not found `%s'\n" % name
)
659 def do_ignore(match_object
):
660 return [('ignore', match_object
.group('code'))]
661 def do_preamble_end(match_object
):
662 return [('preamble-end', match_object
.group('code'))]
664 def make_verbatim(match_object
):
665 return [('verbatim', match_object
.group('code'))]
667 def make_verb(match_object
):
668 return [('verb', match_object
.group('code'))]
670 def do_include_file(m
):
672 return [('input', get_output ('pagebreak'))] \
673 + read_doc_file(m
.group('filename')) \
674 + [('input', get_output ('pagebreak'))]
676 def do_input_file(m
):
677 return read_doc_file(m
.group('filename'))
679 def make_lilypond(m
):
680 if m
.group('options'):
681 options
= m
.group('options')
684 return [('input', get_output('output-lilypond-fragment') %
685 (options
, m
.group('code')))]
687 def make_lilypond_file(m
):
690 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
691 into a @lilypond .. @end lilypond block.
695 if m
.group('options'):
696 options
= m
.group('options')
699 (content
, nm
) = find_file(m
.group('filename'))
700 options
= "filename=%s," % nm
+ options
702 return [('input', get_output('output-lilypond') %
705 def make_lilypond_block(m
):
706 if m
.group('options'):
707 options
= get_re('option-sep').split (m
.group('options'))
710 options
= filter(lambda s
: s
!= '', options
)
711 return [('lilypond', m
.group('code'), options
)]
714 if __main__
.format
!= 'latex':
716 if m
.group('num') == 'one':
717 return [('numcols', m
.group('code'), 1)]
718 if m
.group('num') == 'two':
719 return [('numcols', m
.group('code'), 2)]
721 def chop_chunks(chunks
, re_name
, func
, use_match
=0):
727 m
= get_re (re_name
).search (str)
729 newchunks
.append (('input', str))
733 newchunks
.append (('input', str[:m
.start ('match')]))
735 newchunks
.append (('input', str[:m
.start (0)]))
736 #newchunks.extend(func(m))
737 # python 1.5 compatible:
738 newchunks
= newchunks
+ func(m
)
739 str = str [m
.end(0):]
744 def determine_format (str):
745 if __main__
.format
== '':
747 latex
= re
.search ('\\\\document', str[:200])
748 texinfo
= re
.search ('@node|@setfilename', str[:200])
753 if texinfo
and latex
== None:
755 elif latex
and texinfo
== None:
758 error("error: can't determine format, please specify")
761 if __main__
.paperguru
== None:
762 if __main__
.format
== 'texi':
767 __main__
.paperguru
= g
770 def read_doc_file (filename
):
771 """Read the input file, find verbatim chunks and do \input and \include
773 (str, path
) = find_file(filename
)
774 determine_format (str)
776 chunks
= [('input', str)]
778 # we have to check for verbatim before doing include,
779 # because we don't want to include files that are mentioned
780 # inside a verbatim environment
781 chunks
= chop_chunks(chunks
, 'verbatim', make_verbatim
)
782 chunks
= chop_chunks(chunks
, 'verb', make_verb
)
783 chunks
= chop_chunks(chunks
, 'multiline-comment', do_ignore
)
785 chunks
= chop_chunks(chunks
, 'include', do_include_file
, 1)
786 chunks
= chop_chunks(chunks
, 'input', do_input_file
, 1)
790 taken_file_names
= {}
791 def schedule_lilypond_block (chunk
):
792 """Take the body and options from CHUNK, figure out how the
793 real .ly should look, and what should be left MAIN_STR (meant
794 for the main file). The .ly is written, and scheduled in
797 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
799 TODO has format [basename, extension, extension, ... ]
802 (type, body
, opts
) = chunk
803 assert type == 'lilypond'
804 file_body
= compose_full_body (body
, opts
)
805 basename
= `
abs(hash (file_body
))`
807 m
= re
.search ('filename="(.*?)"', o
)
809 basename
= m
.group (1)
810 if not taken_file_names
.has_key(basename
):
811 taken_file_names
[basename
] = 0
813 taken_file_names
[basename
] = taken_file_names
[basename
] + 1
814 basename
= basename
+ "-%i" % taken_file_names
[basename
]
816 update_file(file_body
, os
.path
.join(g_outdir
, basename
) + '.ly')
817 needed_filetypes
= ['tex']
820 needed_filetypes
.append('eps')
821 needed_filetypes
.append('png')
822 if 'eps' in opts
and not ('eps' in needed_filetypes
):
823 needed_filetypes
.append('eps')
824 pathbase
= os
.path
.join (g_outdir
, basename
)
825 def f(base
, ext1
, ext2
):
826 a
= os
.path
.isfile(base
+ ext2
)
827 if (os
.path
.isfile(base
+ ext1
) and
828 os
.path
.isfile(base
+ ext2
) and
829 os
.stat(base
+ext1
)[stat
.ST_MTIME
] >
830 os
.stat(base
+ext2
)[stat
.ST_MTIME
]) or \
831 not os
.path
.isfile(base
+ ext2
):
834 if 'tex' in needed_filetypes
and f(pathbase
, '.ly', '.tex'):
836 if 'eps' in needed_filetypes
and f(pathbase
, '.tex', '.eps'):
838 if 'png' in needed_filetypes
and f(pathbase
, '.eps', '.png'):
842 if 'printfilename' in opts
:
844 m
= re
.match ("filename=(.*)", o
)
846 newbody
= newbody
+ get_output ("output-filename") % m
.group(1)
850 if 'verbatim' in opts
:
851 newbody
= output_verbatim (body
)
854 m
= re
.search ('intertext="(.*?)"', o
)
856 newbody
= newbody
+ m
.group (1) + "\n\n"
857 if format
== 'latex':
862 else: # format == 'texi'
864 newbody
= newbody
+ get_output (s
) % {'fn': basename
}
865 return ('lilypond', newbody
, opts
, todo
, basename
)
867 def process_lilypond_blocks(outname
, chunks
):#ugh rename
869 # Count sections/chapters.
871 if c
[0] == 'lilypond':
872 c
= schedule_lilypond_block (c
)
873 elif c
[0] == 'numcols':
874 paperguru
.m_num_cols
= c
[2]
879 def find_eps_dims (match
):
880 "Fill in dimensions of EPS files."
883 dims
= bounding_box_dimensions (fn
)
885 fn
= os
.path
.join(g_outdir
, fn
)
887 return '%ipt' % dims
[0]
891 sys
.stderr
.write ("invoking `%s'\n" % cmd
)
894 error ('Error command exited with value %d\n' % st
)
897 def compile_all_files (chunks
):
903 if c
[0] <> 'lilypond':
912 if base
+ '.ly' not in tex
:
913 tex
.append (base
+ '.ly')
914 elif e
== 'png' and g_do_pictures
:
920 # fixme: be sys-independent.
922 if g_outdir
and x
[0] <> '/' :
923 x
= os
.path
.join (g_here_dir
, x
)
926 incs
= map (incl_opt
, include_path
)
927 lilyopts
= string
.join (incs
, ' ' )
928 texfiles
= string
.join (tex
, ' ')
929 system ('lilypond --header=texidoc %s %s' % (lilyopts
, texfiles
))
931 system(r
"tex '\nonstopmode \input %s'" % e
)
932 system(r
"dvips -E -o %s %s" % (e
+ '.eps', e
))
934 cmd
= r
"""gs -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
935 cmd
= cmd
% (g
+ '.eps', g
+ '.png')
941 def update_file (body
, name
):
943 write the body if it has changed
954 f
= open (name
, 'w')
961 def getopt_args (opts
):
962 "Construct arguments (LONG, SHORT) for getopt from list of options."
977 def option_help_str (o
):
978 "Transform one option description (4-tuple ) into neatly formatted string"
996 return ' ' + sh
+ sep
+ long + arg
999 def options_help_str (opts
):
1000 "Convert a list of options into a neatly formatted string"
1006 s
= option_help_str (o
)
1007 strs
.append ((s
, o
[3]))
1013 str = str + '%s%s%s\n' % (s
[0], ' ' * (w
- len(s
[0]) + 3), s
[1])
1017 sys
.stdout
.write("""Usage: lilypond-book [options] FILE\n
1018 Generate hybrid LaTeX input from Latex + lilypond
1021 sys
.stdout
.write (options_help_str (option_definitions
))
1022 sys
.stdout
.write (r
"""Warning all output is written in the CURRENT directory
1026 Report bugs to bug-gnu-music@gnu.org.
1028 Written by Tom Cato Amundsen <tca@gnu.org> and
1029 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1035 def write_deps (fn
, target
):
1036 sys
.stdout
.write('writing `%s\'\n' % os
.path
.join(g_outdir
, fn
))
1037 f
= open (os
.path
.join(g_outdir
, fn
), 'w')
1038 f
.write ('%s%s: ' % (g_dep_prefix
, target
))
1039 for d
in __main__
.read_files
:
1043 __main__
.read_files
= []
1046 sys
.stdout
.write ('lilypond-book (GNU LilyPond) %s\n' % program_version
)
1048 def print_version ():
1050 sys
.stdout
.write (r
"""Copyright 1998--1999
1051 Distributed under terms of the GNU General Public License. It comes with
1056 def check_texidoc (chunks
):
1059 if c
[0] == 'lilypond':
1060 (type, body
, opts
, todo
, basename
) = c
;
1061 pathbase
= os
.path
.join (g_outdir
, basename
)
1062 if os
.path
.isfile (pathbase
+ '.texidoc'):
1063 body
= '\n@include %s.texidoc\n' % basename
+ body
1064 c
= (type, body
, opts
, todo
, basename
)
1068 def fix_epswidth (chunks
):
1071 if c
[0] == 'lilypond' and 'eps' in c
[2]:
1072 body
= re
.sub (r
"""\\lilypondepswidth{(.*?)}""", find_eps_dims
, c
[1])
1073 newchunks
.append(('lilypond', body
, c
[2], c
[3], c
[4]))
1075 newchunks
.append (c
)
1079 def do_file(input_filename
):
1082 my_outname
= outname
1084 my_outname
= os
.path
.basename(os
.path
.splitext(input_filename
)[0])
1085 my_depname
= my_outname
+ '.dep'
1087 chunks
= read_doc_file(input_filename
)
1088 chunks
= chop_chunks(chunks
, 'lilypond', make_lilypond
, 1)
1089 chunks
= chop_chunks(chunks
, 'lilypond-file', make_lilypond_file
, 1)
1090 chunks
= chop_chunks(chunks
, 'lilypond-block', make_lilypond_block
, 1)
1091 chunks
= chop_chunks(chunks
, 'singleline-comment', do_ignore
, 1)
1092 chunks
= chop_chunks(chunks
, 'preamble-end', do_preamble_end
)
1093 chunks
= chop_chunks(chunks
, 'numcols', do_columns
)
1095 #for c in chunks: print "c:", c;
1097 scan_preamble(chunks
)
1098 chunks
= process_lilypond_blocks(my_outname
, chunks
)
1101 if __main__
.g_run_lilypond
:
1102 compile_all_files (chunks
)
1103 chunks
= fix_epswidth (chunks
)
1105 if __main__
.format
== 'texi':
1106 chunks
= check_texidoc (chunks
)
1109 chunks
= completize_preamble (chunks
)
1110 foutn
= os
.path
.join(g_outdir
, my_outname
+ '.' + format
)
1111 sys
.stderr
.write ("Writing `%s'\n" % foutn
)
1112 fout
= open (foutn
, 'w')
1119 write_deps (my_depname
, foutn
)
1124 (sh
, long) = getopt_args (__main__
.option_definitions
)
1125 (options
, files
) = getopt
.getopt(sys
.argv
[1:], sh
, long)
1126 except getopt
.error
, msg
:
1127 sys
.stderr
.write("error: %s" % msg
)
1135 if o
== '--include' or o
== '-I':
1136 include_path
.append (a
)
1137 elif o
== '--version' or o
== '-v':
1140 elif o
== '--format' or o
== '-f':
1142 elif o
== '--outname' or o
== '-o':
1145 sys
.stderr
.write("Lilypond-book is confused by --outname on multiple files")
1148 elif o
== '--help' or o
== '-h':
1150 elif o
== '--no-lily' or o
== '-n':
1151 __main__
.g_run_lilypond
= 0
1152 elif o
== '--dependencies' or o
== '-M':
1154 elif o
== '--default-music-fontsize':
1155 default_music_fontsize
= string
.atoi (a
)
1156 elif o
== '--default-lilypond-fontsize':
1157 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1158 default_music_fontsize
= string
.atoi (a
)
1159 elif o
== '--force-music-fontsize':
1160 g_force_lilypond_fontsize
= string
.atoi(a
)
1161 elif o
== '--force-lilypond-fontsize':
1162 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1163 g_force_lilypond_fontsize
= string
.atoi(a
)
1164 elif o
== '--dep-prefix':
1166 elif o
== '--no-pictures':
1168 elif o
== '--read-lys':
1170 elif o
== '--outdir':
1175 if os
.path
.isfile(g_outdir
):
1176 error ("outdir is a file: %s" % g_outdir
)
1177 if not os
.path
.exists(g_outdir
):
1179 for input_filename
in files
:
1180 do_file(input_filename
)
1183 # Petr, ik zou willen dat ik iets zinvoller deed,
1184 # maar wat ik kan ik doen, het verandert toch niets?