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 automatically by: lilypond-book.py
524 %% options are %s %%ughUGH not original options
525 \include "paper%d.ly"
526 \paper { linewidth = %f \pt; }
527 """ % (optstring, music_size, l) + body
530 def parse_options_string(s):
532 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
533 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
534 r3 = re.compile("(\w+?)((,\s*)|$)")
539 d[m.group(2)] = re.split(",\s*", m.group(3))
544 d[m.group(2)] = m.group(3)
552 error ("format of option string invalid (was `%')" % s)
555 def scan_latex_preamble(chunks):
556 # first we want to scan the \documentclass line
557 # it should be the first non-comment line
560 if chunks[idx][0] == 'ignore':
563 m = get_re ('header').match(chunks[idx][1])
564 options = re.split (',[\n \t]*', m.group(1)[1:-1])
567 paperguru.m_landscape = 1
568 m = re.match("(.*?)paper", o)
570 paperguru.m_papersize = m.group()
572 m = re.match("(\d\d)pt", o)
574 paperguru.m_fontsize = int(m.group(1))
577 while chunks[idx][0] != 'preamble-end':
578 if chunks[idx] == 'ignore':
581 m = get_re ('geometry').search(chunks[idx][1])
583 paperguru.m_use_geometry = 1
584 o = parse_options_string(m.group('options'))
586 paperguru.set_geo_option(k, o[k])
589 def scan_texi_preamble (chunks):
590 # this is not bulletproof..., it checks the first 10 chunks
591 for c in chunks[:10]:
593 for s in ('afourpaper', 'afourwide', 'letterpaper',
594 'afourlatex', 'smallbook'):
595 if string.find(c[1], "@%s" % s) != -1:
596 paperguru.m_papersize = s
598 def scan_preamble (chunks):
599 if __main__.format == 'texi':
600 scan_texi_preamble(chunks)
602 assert __main__.format == 'latex'
603 scan_latex_preamble(chunks)
606 def completize_preamble (chunks):
607 if __main__.format == 'texi':
609 pre_b = post_b = graphics_b = None
611 if chunk[0] == 'preamble-end':
613 if chunk[0] == 'input':
614 m = get_re('def-pre-re').search(chunk[1])
617 if chunk[0] == 'input':
618 m = get_re('def-post-re').search(chunk[1])
621 if chunk[0] == 'input':
622 m = get_re('usepackage-graphics').search(chunk[1])
626 while chunks[x][0] != 'preamble-end':
629 chunks.insert(x, ('input', get_output ('output-default-pre')))
631 chunks.insert(x, ('input', get_output ('output-default-post')))
633 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
638 def find_file (name):
640 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
645 for a in include_path:
647 nm = os.path.join (a, name)
649 __main__.read_files.append (nm)
654 sys.stderr.write ("Reading `%s'\n" % nm)
655 return (f.read (), nm)
657 error ("File not found `%s'\n" % name)
660 def do_ignore(match_object):
661 return [('ignore', match_object.group('code'))]
662 def do_preamble_end(match_object):
663 return [('preamble-end', match_object.group('code'))]
665 def make_verbatim(match_object):
666 return [('verbatim', match_object.group('code'))]
668 def make_verb(match_object):
669 return [('verb', match_object.group('code'))]
671 def do_include_file(m):
673 return [('input', get_output ('pagebreak'))] \
674 + read_doc_file(m.group('filename')) \
675 + [('input', get_output ('pagebreak'))]
677 def do_input_file(m):
678 return read_doc_file(m.group('filename'))
680 def make_lilypond(m):
681 if m.group('options'):
682 options = m.group('options')
685 return [('input', get_output('output-lilypond-fragment') %
686 (options, m.group('code')))]
688 def make_lilypond_file(m):
691 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
692 into a @lilypond .. @end lilypond block.
696 if m.group('options'):
697 options = m.group('options')
700 (content, nm) = find_file(m.group('filename'))
701 options = "filename=%s," % nm + options
703 return [('input', get_output('output-lilypond') %
706 def make_lilypond_block(m):
707 if m.group('options'):
708 options = get_re('option-sep').split (m.group('options'))
711 options = filter(lambda s: s != '', options)
712 return [('lilypond', m.group('code'), options)]
715 if __main__.format != 'latex':
717 if m.group('num') == 'one':
718 return [('numcols', m.group('code'), 1)]
719 if m.group('num') == 'two':
720 return [('numcols', m.group('code'), 2)]
722 def chop_chunks(chunks, re_name, func, use_match=0):
728 m = get_re (re_name).search (str)
730 newchunks.append (('input', str))
734 newchunks.append (('input', str[:m.start ('match')]))
736 newchunks.append (('input', str[:m.start (0)]))
737 #newchunks.extend(func(m))
738 # python 1.5 compatible:
739 newchunks = newchunks + func(m)
740 str = str [m.end(0):]
745 def determine_format (str):
746 if __main__.format == '':
748 latex = re.search ('\\\\document', str[:200])
749 texinfo = re.search ('@node|@setfilename', str[:200])
754 if texinfo and latex == None:
756 elif latex and texinfo == None:
759 error("error: can't determine format, please specify")
762 if __main__.paperguru == None:
763 if __main__.format == 'texi':
768 __main__.paperguru = g
771 def read_doc_file (filename):
772 """Read the input file, find verbatim chunks and do \input and \include
774 (str, path) = find_file(filename)
775 determine_format (str)
777 chunks = [('input', str)]
779 # we have to check for verbatim before doing include,
780 # because we don't want to include files that are mentioned
781 # inside a verbatim environment
782 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
783 chunks = chop_chunks(chunks, 'verb', make_verb)
784 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
786 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
787 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
791 taken_file_names = {}
792 def schedule_lilypond_block (chunk):
793 """Take the body and options from CHUNK, figure out how the
794 real .ly should look, and what should be left MAIN_STR (meant
795 for the main file). The .ly is written, and scheduled in
798 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
800 TODO has format [basename, extension, extension, ... ]
803 (type, body, opts) = chunk
804 assert type == 'lilypond'
805 file_body = compose_full_body (body, opts)
806 basename = 'lily-' + `abs(hash (file_body))`
808 m = re.search ('filename="(.*?)"', o)
810 basename = m.group (1)
811 if not taken_file_names.has_key(basename):
812 taken_file_names[basename] = 0
814 taken_file_names[basename] = taken_file_names[basename] + 1
815 basename = basename + "-%i" % taken_file_names[basename]
817 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
818 needed_filetypes = ['tex']
821 needed_filetypes.append('eps')
822 needed_filetypes.append('png')
823 if 'eps' in opts and not ('eps' in needed_filetypes):
824 needed_filetypes.append('eps')
825 pathbase = os.path.join (g_outdir, basename)
826 def f(base, ext1, ext2):
827 a = os.path.isfile(base + ext2)
828 if (os.path.isfile(base + ext1) and
829 os.path.isfile(base + ext2) and
830 os.stat(base+ext1)[stat.ST_MTIME] >
831 os.stat(base+ext2)[stat.ST_MTIME]) or \
832 not os.path.isfile(base + ext2):
835 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
837 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
839 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
843 if 'printfilename' in opts:
845 m= re.match ("filename=(.*)", o)
847 newbody = newbody + get_output ("output-filename") % m.group(1)
851 if 'verbatim' in opts:
852 newbody = output_verbatim (body)
855 m = re.search ('intertext="(.*?)"', o)
857 newbody = newbody + m.group (1) + "\n\n"
858 if format == 'latex':
863 else: # format == 'texi'
865 newbody = newbody + get_output (s) % {'fn': basename }
866 return ('lilypond', newbody, opts, todo, basename)
868 def process_lilypond_blocks(outname, chunks):#ugh rename
870 # Count sections/chapters.
872 if c[0] == 'lilypond':
873 c = schedule_lilypond_block (c)
874 elif c[0] == 'numcols':
875 paperguru.m_num_cols = c[2]
880 def find_eps_dims (match):
881 "Fill in dimensions of EPS files."
884 dims = bounding_box_dimensions (fn)
886 fn = os.path.join(g_outdir, fn)
888 return '%ipt' % dims[0]
892 sys.stderr.write ("invoking `%s'\n" % cmd)
895 error ('Error command exited with value %d\n' % st)
898 def compile_all_files (chunks):
905 if c[0] <> 'lilypond':
914 if base + '.ly' not in tex:
915 tex.append (base + '.ly')
916 elif e == 'png' and g_do_pictures:
922 # fixme: be sys-independent.
924 if g_outdir and x[0] <> '/' :
925 x = os.path.join (g_here_dir, x)
928 incs = map (incl_opt, include_path)
929 lilyopts = string.join (incs, ' ' )
931 lilyopts = lilyopts + ' --dependencies '
933 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
934 texfiles = string.join (tex, ' ')
935 system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
938 # Ugh, fixing up dependencies for .tex generation
941 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
943 text=open (i).read ()
944 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
945 open (i, 'w').write (text)
948 system(r"tex '\nonstopmode \input %s'" % e)
949 system(r"dvips -E -o %s %s" % (e + '.eps', e))
951 cmd = r"""gs -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
952 cmd = cmd % (g + '.eps', g + '.png')
957 def update_file (body, name):
959 write the body if it has changed
970 f = open (name , 'w')
977 def getopt_args (opts):
978 "Construct arguments (LONG, SHORT) for getopt from list of options."
993 def option_help_str (o):
994 "Transform one option description (4-tuple ) into neatly formatted string"
1012 return ' ' + sh + sep + long + arg
1015 def options_help_str (opts):
1016 "Convert a list of options into a neatly formatted string"
1022 s = option_help_str (o)
1023 strs.append ((s, o[3]))
1029 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1033 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1034 Generate hybrid LaTeX input from Latex + lilypond
1037 sys.stdout.write (options_help_str (option_definitions))
1038 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1042 Report bugs to bug-gnu-music@gnu.org.
1044 Written by Tom Cato Amundsen <tca@gnu.org> and
1045 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1051 def write_deps (fn, target, chunks):
1053 sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1054 f = open (os.path.join(g_outdir, fn), 'w')
1055 f.write ('%s%s: ' % (g_dep_prefix, target))
1056 for d in read_files:
1060 if c[0] == 'lilypond':
1061 (type, body, opts, todo, basename) = c;
1062 basenames.append (basename)
1065 d=g_outdir + '/' + d
1067 #if not os.isfile (d): # thinko?
1068 if not re.search ('/', d):
1069 d = g_dep_prefix + d
1070 f.write ('%s.tex ' % d)
1072 #if len (basenames):
1073 # for d in basenames:
1074 # f.write ('%s.ly ' % d)
1075 # f.write (' : %s' % target)
1081 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1083 def print_version ():
1085 sys.stdout.write (r"""Copyright 1998--1999
1086 Distributed under terms of the GNU General Public License. It comes with
1091 def check_texidoc (chunks):
1094 if c[0] == 'lilypond':
1095 (type, body, opts, todo, basename) = c;
1096 pathbase = os.path.join (g_outdir, basename)
1097 if os.path.isfile (pathbase + '.texidoc'):
1098 body = '\n@include %s.texidoc\n' % basename + body
1099 c = (type, body, opts, todo, basename)
1103 def fix_epswidth (chunks):
1106 if c[0] == 'lilypond' and 'eps' in c[2]:
1107 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", find_eps_dims, c[1])
1108 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1110 newchunks.append (c)
1115 def do_file(input_filename):
1119 my_outname = outname
1121 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1122 my_depname = my_outname + '.dep'
1124 chunks = read_doc_file(input_filename)
1125 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1126 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1127 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1128 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1129 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1130 chunks = chop_chunks(chunks, 'numcols', do_columns)
1132 #for c in chunks: print "c:", c;
1134 scan_preamble(chunks)
1135 chunks = process_lilypond_blocks(my_outname, chunks)
1137 foutn = os.path.join (g_outdir, my_outname + '.' + format)
1140 if __main__.g_run_lilypond:
1141 compile_all_files (chunks)
1142 chunks = fix_epswidth (chunks)
1144 if __main__.format == 'texi':
1145 chunks = check_texidoc (chunks)
1148 chunks = completize_preamble (chunks)
1149 sys.stderr.write ("Writing `%s'\n" % foutn)
1150 fout = open (foutn, 'w')
1157 write_deps (my_depname, foutn, chunks)
1162 (sh, long) = getopt_args (__main__.option_definitions)
1163 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1164 except getopt.error, msg:
1165 sys.stderr.write("error: %s" % msg)
1173 if o == '--include' or o == '-I':
1174 include_path.append (a)
1175 elif o == '--version' or o == '-v':
1178 elif o == '--format' or o == '-f':
1180 elif o == '--outname' or o == '-o':
1183 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1186 elif o == '--help' or o == '-h':
1188 elif o == '--no-lily' or o == '-n':
1189 __main__.g_run_lilypond = 0
1190 elif o == '--dependencies' or o == '-M':
1192 elif o == '--default-music-fontsize':
1193 default_music_fontsize = string.atoi (a)
1194 elif o == '--default-lilypond-fontsize':
1195 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1196 default_music_fontsize = string.atoi (a)
1197 elif o == '--force-music-fontsize':
1198 g_force_lilypond_fontsize = string.atoi(a)
1199 elif o == '--force-lilypond-fontsize':
1200 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1201 g_force_lilypond_fontsize = string.atoi(a)
1202 elif o == '--dep-prefix':
1204 elif o == '--no-pictures':
1206 elif o == '--read-lys':
1208 elif o == '--outdir':
1213 if os.path.isfile(g_outdir):
1214 error ("outdir is a file: %s" % g_outdir)
1215 if not os.path.exists(g_outdir):
1217 for input_filename in files:
1218 do_file(input_filename)
1221 # Petr, ik zou willen dat ik iets zinvoller deed,
1222 # maar wat ik kan ik doen, het verandert toch niets?