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
70 # the dimentions are from geometry.sty
71 'a0paper': (mm2pt(841), mm2pt(1189)),
72 'a1paper': (mm2pt(595), mm2pt(841)),
73 'a2paper': (mm2pt(420), mm2pt(595)),
74 'a3paper': (mm2pt(297), mm2pt(420)),
75 'a4paper': (mm2pt(210), mm2pt(297)),
76 'a5paper': (mm2pt(149), mm2pt(210)),
77 'b0paper': (mm2pt(1000), mm2pt(1414)),
78 'b1paper': (mm2pt(707), mm2pt(1000)),
79 'b2paper': (mm2pt(500), mm2pt(707)),
80 'b3paper': (mm2pt(353), mm2pt(500)),
81 'b4paper': (mm2pt(250), mm2pt(353)),
82 'b5paper': (mm2pt(176), mm2pt(250)),
83 'letterpaper': (in2pt(8.5), in2pt(11)),
84 'legalpaper': (in2pt(8.5), in2pt(14)),
85 'executivepaper': (in2pt(7.25), in2pt(10.5))}
86 self.m_use_geometry = None
87 self.m_papersize = 'letterpaper'
91 self.m_geo_landscape = 0
92 self.m_geo_width = None
93 self.m_geo_textwidth = None
94 self.m_geo_lmargin = None
95 self.m_geo_rmargin = None
96 self.m_geo_includemp = None
97 self.m_geo_marginparwidth = {10: 57, 11: 50, 12: 35}
98 self.m_geo_marginparsep = {10: 11, 11: 10, 12: 10}
99 self.m_geo_x_marginparwidth = None
100 self.m_geo_x_marginparsep = None
102 def set_geo_option(self, name, value):
103 if name == 'body' or name == 'text':
104 if type(value) == type(""):
105 self._set_dimen('m_geo_textwidth', value)
107 self._set_dimen('m_geo_textwidth', value[0])
109 elif name == 'portrait':
110 self.m_geo_landscape = 0
111 elif name == 'reversemp' or name == 'reversemarginpar':
112 if self.m_geo_includemp == None:
113 self.m_geo_includemp = 1
114 elif name == 'marginparwidth' or name == 'marginpar':
115 self._set_dimen('m_geo_x_marginparwidth', value)
116 self.m_geo_includemp = 1
117 elif name == 'marginparsep':
118 self._set_dimen('m_geo_x_marginparsep', value)
119 self.m_geo_includemp = 1
120 elif name == 'scale':
121 if type(value) == type(""):
122 self.m_geo_width = self.get_paperwidth() * float(value)
124 self.m_geo_width = self.get_paperwidth() * float(value[0])
125 elif name == 'hscale':
126 self.m_geo_width = self.get_paperwidth() * float(value)
127 elif name == 'left' or name == 'lmargin':
128 self._set_dimen('m_geo_lmargin', value)
129 elif name == 'right' or name == 'rmargin':
130 self._set_dimen('m_geo_rmargin', value)
131 elif name == 'hdivide' or name == 'divide':
132 if value[0] not in ('*', ''):
133 self._set_dimen('m_geo_lmargin', value[0])
134 if value[1] not in ('*', ''):
135 self._set_dimen('m_geo_width', value[1])
136 if value[2] not in ('*', ''):
137 self._set_dimen('m_geo_rmargin', value[2])
138 elif name == 'hmargin':
139 if type(value) == type(""):
140 self._set_dimen('m_geo_lmargin', value)
141 self._set_dimen('m_geo_rmargin', value)
143 self._set_dimen('m_geo_lmargin', value[0])
144 self._set_dimen('m_geo_rmargin', value[1])
145 elif name == 'margin':#ugh there is a bug about this option in
146 # the geometry documentation
147 if type(value) == type(""):
148 self._set_dimen('m_geo_lmargin', value)
149 self._set_dimen('m_geo_rmargin', value)
151 self._set_dimen('m_geo_lmargin', value[0])
152 self._set_dimen('m_geo_rmargin', value[0])
153 elif name == 'total':
154 if type(value) == type(""):
155 self._set_dimen('m_geo_width', value)
157 self._set_dimen('m_geo_width', value[0])
158 elif name == 'width' or name == 'totalwidth':
159 self._set_dimen('m_geo_width', value)
160 elif name == 'paper' or name == 'papername':
161 self.m_papersize = value
162 elif name[-5:] == 'paper':
163 self.m_papersize = name
165 self._set_dimen('m_geo_'+name, value)
166 def _set_dimen(self, name, value):
167 if type(value) == type("") and value[-2:] == 'pt':
168 self.__dict__[name] = float(value[:-2])
169 elif type(value) == type("") and value[-2:] == 'mm':
170 self.__dict__[name] = mm2pt(float(value[:-2]))
171 elif type(value) == type("") and value[-2:] == 'cm':
172 self.__dict__[name] = 10 * mm2pt(float(value[:-2]))
173 elif type(value) == type("") and value[-2:] == 'in':
174 self.__dict__[name] = in2pt(float(value[:-2]))
176 self.__dict__[name] = value
178 print "LatexPaper:\n-----------"
179 for v in self.__dict__.keys():
181 print v, self.__dict__[v]
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]
207 if self.__body:#ugh test if this is necessary
210 return a == None, b == None, c == None
211 if not self.m_use_geometry:
212 return latex_linewidths[self.m_papersize][self.m_fontsize]
214 if tNone(self.m_geo_lmargin, self.m_geo_width,
215 self.m_geo_rmargin) == (1, 1, 1):
216 if self.m_geo_textwidth:
217 return self.m_geo_textwidth
218 w = self.get_paperwidth() * 0.8
220 elif tNone(self.m_geo_lmargin, self.m_geo_width,
221 self.m_geo_rmargin) == (0, 1, 1):
222 if self.m_geo_textwidth:
223 return self.m_geo_textwidth
224 return self.f1(self.m_geo_lmargin, mp)
225 elif tNone(self.m_geo_lmargin, self.m_geo_width,
226 self.m_geo_rmargin) == (1, 1, 0):
227 if self.m_geo_textwidth:
228 return self.m_geo_textwidth
229 return self.f1(self.m_geo_rmargin, mp)
230 elif tNone(self.m_geo_lmargin, self.m_geo_width,
231 self.m_geo_rmargin) \
232 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
233 if self.m_geo_textwidth:
234 return self.m_geo_textwidth
235 return self.m_geo_width - mp
236 elif tNone(self.m_geo_lmargin, self.m_geo_width,
237 self.m_geo_rmargin) in ((0, 1, 0), (0, 0, 0)):
238 w = self.get_paperwidth() - self.m_geo_lmargin - self.m_geo_rmargin - mp
242 raise "Never do this!"
244 tmp = self.get_paperwidth() - m * 2 - mp
249 tmp = self.get_paperwidth() - self.m_geo_lmargin \
257 self.m_papersize = 'letterpaper'
259 def get_linewidth(self):
260 return texi_linewidths[self.m_papersize][self.m_fontsize]
266 def em2pt(x, fontsize):
267 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
268 def ex2pt(x, fontsize):
269 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
272 # indices are no. of columns, papersize, fontsize
273 # Why can't this be calculated?
275 'a4paper':{10: 345, 11: 360, 12: 390},
276 'a4paper-landscape': {10: 598, 11: 596, 12:592},
277 'a5paper':{10: 276, 11: 276, 12: 276},
278 'b5paper':{10: 345, 11: 356, 12: 356},
279 'letterpaper':{10: 345, 11: 360, 12: 390},
280 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
281 'legalpaper': {10: 345, 11: 360, 12: 390},
282 'executivepaper':{10: 345, 11: 360, 12: 379}}
285 'afourpaper': {12: mm2pt(160)},
286 'afourwide': {12: in2pt(6.5)},
287 'afourlatex': {12: mm2pt(150)},
288 'smallbook': {12: in2pt(5)},
289 'letterpaper': {12: in2pt(6)}}
291 option_definitions = [
292 ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'),
293 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
294 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
295 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
296 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
297 ('DIR', 'I', 'include', 'include path'),
298 ('', 'M', 'dependencies', 'write dependencies'),
299 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
300 ('', 'n', 'no-lily', 'don\'t run lilypond'),
301 ('', '', 'no-pictures', "don\'t generate pictures"),
302 ('', '', 'read-lys', "don't write ly files."),
303 ('FILE', 'o', 'outname', 'filename main output file'),
304 ('FILE', '', 'outdir', "where to place generated files"),
305 ('', 'v', 'version', 'print version information' ),
306 ('', 'h', 'help', 'print help'),
309 # format specific strings, ie. regex-es for input, and % strings for output
312 'output-lilypond-fragment' : r"""\begin[eps,singleline,%s]{lilypond}
319 'output-lilypond':r"""\begin[%s]{lilypond}
322 'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
323 'output-default-post': "\\def\postLilypondExample{}\n",
324 'output-default-pre': "\\def\preLilypondExample{}\n",
325 'usepackage-graphics': '\\usepackage{graphics}\n',
326 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
327 'output-tex': '\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n',
328 'pagebreak': r'\pagebreak',
330 'texi' : {'output-lilypond': """@lilypond[%s]
334 'output-lilypond-fragment': """@lilypond[%s]
335 \context Staff\context Voice{ %s }
338 'output-verbatim': r"""@example
343 # do some tweaking: @ is needed in some ps stuff.
344 # override EndLilyPondOutput, since @tex is done
345 # in a sandbox, you can't do \input lilyponddefs at the
346 # top of the document.
348 # should also support fragment in
354 \def\EndLilyPondOutput{}
366 def output_verbatim (body):
367 if __main__.format == 'texi':
368 body = re.sub ('([@{}])', '@\\1', body)
369 return get_output ('output-verbatim') % body
373 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
374 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
375 'option-sep' : ', *',
376 'header': r"\\documentclass\s*(\[.*?\])?",
377 'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
378 'preamble-end': r'(?P<code>\\begin{document})',
379 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
380 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
381 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile(\[(?P<options>.*?)\])?\{(?P<filename>.+)})',
382 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
383 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin(\[(?P<options>.*?)\])?{lilypond}(?P<code>.*?)\\end{lilypond})",
384 'def-post-re': r"\\def\\postLilypondExample",
385 'def-pre-re': r"\\def\\preLilypondExample",
386 'usepackage-graphics': r"\usepackage{graphics}",
387 'intertext': r',?\s*intertext=\".*?\"',
388 'multiline-comment': no_match,
389 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
390 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
394 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
397 'preamble-end': no_match,
398 'landscape': no_match,
399 'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
400 'verb': r"""(?P<code>@code{.*?})""",
401 'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
402 'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
403 'lilypond-block': r"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
404 'option-sep' : ', *',
405 'intertext': r',?\s*intertext=\".*?\"',
406 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
407 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
413 for r in re_dict.keys ():
416 for k in olddict.keys ():
417 newdict[k] = re.compile (olddict[k])
431 def get_output (name):
432 return output_dict[format][name]
435 return re_dict[format][name]
437 def bounding_box_dimensions(fname):
439 fname = os.path.join(g_outdir, fname)
443 error ("Error opening `%s'" % fname)
445 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
447 return (int(s.group(3))-int(s.group(1)),
448 int(s.group(4))-int(s.group(2)))
454 sys.stderr.write (str + "\n Exiting ... \n\n")
458 def compose_full_body (body, opts):
459 """Construct the lilypond code to send to Lilypond.
460 Add stuff to BODY using OPTS as options."""
461 music_size = default_music_fontsize
462 latex_size = default_text_fontsize
464 if g_force_lilypond_fontsize:
465 music_size = g_force_lilypond_fontsize
467 m = re.match ('([0-9]+)pt', o)
469 music_size = string.atoi(m.group (1))
471 m = re.match ('latexfontsize=([0-9]+)pt', o)
473 latex_size = string.atoi (m.group (1))
475 if re.search ('\\\\score', body):
479 if 'fragment' in opts:
481 if 'nonfragment' in opts:
484 if is_fragment and not 'multiline' in opts:
485 opts.append('singleline')
486 if 'singleline' in opts:
489 l = paperguru.get_linewidth()
491 if 'relative' in opts:#ugh only when is_fragment
492 body = '\\relative c { %s }' % body
501 optstring = string.join (opts, ' ')
502 optstring = re.sub ('\n', ' ', optstring)
504 %% Generated by lilypond-book.py; options are %s %%ughUGH not original options
505 \include "paper%d.ly"
506 \paper { linewidth = %f \pt; }
507 """ % (optstring, music_size, l) + body
510 def parse_options_string(s):
512 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
513 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
514 r3 = re.compile("(\w+?)((,\s*)|$)")
519 d[m.group(2)] = re.split(",\s*", m.group(3))
524 d[m.group(2)] = m.group(3)
531 print "trøbbel:%s:" % s
534 def scan_latex_preamble(chunks):
535 # first we want to scan the \documentclass line
536 # it should be the first non-comment line
539 if chunks[idx][0] == 'ignore':
542 m = get_re ('header').match(chunks[idx][1])
543 options = re.split (',[\n \t]*', m.group(1)[1:-1])
546 paperguru.m_landscape = 1
547 m = re.match("(.*?)paper", o)
549 paperguru.m_papersize = m.group()
551 m = re.match("(\d\d)pt", o)
553 paperguru.m_fontsize = int(m.group(1))
556 while chunks[idx][0] != 'preamble-end':
557 if chunks[idx] == 'ignore':
560 m = get_re ('geometry').search(chunks[idx][1])
562 paperguru.m_use_geometry = 1
563 o = parse_options_string(m.group('options'))
565 paperguru.set_geo_option(k, o[k])
568 def scan_texi_preamble (chunks):
569 # this is not bulletproof..., it checks the first 10 chunks
572 if chunks[idx][0] == 'input':
573 for s in ('afourpaper', 'afourwide', 'letterpaper',
574 'afourlatex', 'smallbook'):
575 if string.find(chunks[idx][1], "@%s" % s) != -1:
576 paperguru.m_papersize = s
578 if idx == 10 or idx == len(chunks):
581 def scan_preamble (chunks):
582 if __main__.format == 'texi':
583 scan_texi_preamble(chunks)
585 assert __main__.format == 'latex'
586 scan_latex_preamble(chunks)
589 def completize_preamble (chunks):
590 if __main__.format == 'texi':
592 pre_b = post_b = graphics_b = None
594 if chunk[0] == 'preamble-end':
596 if chunk[0] == 'input':
597 m = get_re('def-pre-re').search(chunk[1])
600 if chunk[0] == 'input':
601 m = get_re('def-post-re').search(chunk[1])
604 if chunk[0] == 'input':
605 m = get_re('usepackage-graphics').search(chunk[1])
609 while chunks[x][0] != 'preamble-end':
612 chunks.insert(x, ('input', get_output ('output-default-pre')))
614 chunks.insert(x, ('input', get_output ('output-default-post')))
616 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
621 def find_file (name):
623 for a in include_path:
625 nm = os.path.join (a, name)
627 __main__.read_files.append (nm)
634 error ("File not found `%s'\n" % name)
637 def do_ignore(match_object):
638 return [('ignore', match_object.group('code'))]
639 def do_preamble_end(match_object):
640 return [('preamble-end', match_object.group('code'))]
642 def make_verbatim(match_object):
643 return [('verbatim', match_object.group('code'))]
645 def make_verb(match_object):
646 return [('verb', match_object.group('code'))]
648 def do_include_file(m):
650 return [('input', get_output ('pagebreak'))] \
651 + read_doc_file(m.group('filename')) \
652 + [('input', get_output ('pagebreak'))]
654 def do_input_file(m):
655 return read_doc_file(m.group('filename'))
657 def make_lilypond(m):
658 if m.group('options'):
659 options = m.group('options')
662 return [('input', get_output('output-lilypond-fragment') %
663 (options, m.group('code')))]
665 def make_lilypond_file(m):
666 if m.group('options'):
667 options = m.group('options')
670 return [('input', get_output('output-lilypond') %
671 (options, find_file(m.group('filename'))))]
673 def make_lilypond_block(m):
674 if m.group('options'):
675 options = get_re('option-sep').split (m.group('options'))
678 options = filter(lambda s: s != '', options)
679 return [('lilypond', m.group('code'), options)]
682 if __main__.format != 'latex':
684 if m.group('num') == 'one':
685 return [('numcols', m.group('code'), 1)]
686 if m.group('num') == 'two':
687 return [('numcols', m.group('code'), 2)]
689 def chop_chunks(chunks, re_name, func, use_match=0):
695 m = get_re (re_name).search (str)
697 newchunks.append (('input', str))
701 newchunks.append (('input', str[:m.start ('match')]))
703 newchunks.append (('input', str[:m.start (0)]))
704 #newchunks.extend(func(m))
705 # python 1.5 compatible:
706 newchunks = newchunks + func(m)
707 str = str [m.end(0):]
712 def read_doc_file (filename):
713 """Read the input file, find verbatim chunks and do \input and \include
716 str = find_file(filename)
718 if __main__.format == '':
719 latex = re.search ('\\\\document', str[:200])
720 texinfo = re.search ('@node|@setfilename', str[:200])
721 if (texinfo and latex) or not (texinfo or latex):
722 error("error: can't determine format, please specify")
724 __main__.format = 'texi'
726 __main__.format = 'latex'
727 if __main__.format == 'texi':
728 __main__.paperguru = TexiPaper()
730 __main__.paperguru = LatexPaper()
731 chunks = [('input', str)]
732 # we have to check for verbatim before doing include,
733 # because we don't want to include files that are mentioned
734 # inside a verbatim environment
735 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
736 chunks = chop_chunks(chunks, 'verb', make_verb)
737 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
739 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
740 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
744 taken_file_names = {}
745 def schedule_lilypond_block (chunk):
746 """Take the body and options from CHUNK, figure out how the
747 real .ly should look, and what should be left MAIN_STR (meant
748 for the main file). The .ly is written, and scheduled in
751 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
753 TODO has format [basename, extension, extension, ... ]
756 (type, body, opts) = chunk
757 assert type == 'lilypond'
758 file_body = compose_full_body (body, opts)
759 basename = `abs(hash (file_body))`
761 m = re.search ('filename="(.*?)"', o)
763 basename = m.group (1)
764 if not taken_file_names.has_key(basename):
765 taken_file_names[basename] = 0
767 taken_file_names[basename] = taken_file_names[basename] + 1
768 basename = basename + "-%i" % taken_file_names[basename]
770 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
771 needed_filetypes = ['tex']
774 needed_filetypes.append('eps')
775 needed_filetypes.append('png')
776 if 'eps' in opts and not ('eps' in needed_filetypes):
777 needed_filetypes.append('eps')
778 pathbase = os.path.join (g_outdir, basename)
779 def f(base, ext1, ext2):
780 a = os.path.isfile(base + ext2)
781 if (os.path.isfile(base + ext1) and
782 os.path.isfile(base + ext2) and
783 os.stat(base+ext1)[stat.ST_MTIME] >
784 os.stat(base+ext2)[stat.ST_MTIME]) or \
785 not os.path.isfile(base + ext2):
788 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
790 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
792 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
795 if 'verbatim' in opts:
796 newbody = output_verbatim (body)
799 m = re.search ('intertext="(.*?)"', o)
801 newbody = newbody + m.group (1) + "\n\n"
802 if format == 'latex':
807 else: # format == 'texi'
809 newbody = newbody + get_output (s) % {'fn': basename }
810 return ('lilypond', newbody, opts, todo, basename)
812 def process_lilypond_blocks(outname, chunks):#ugh rename
814 # Count sections/chapters.
816 if c[0] == 'lilypond':
817 c = schedule_lilypond_block (c)
818 elif c[0] == 'numcols':
819 paperguru.m_num_cols = c[2]
824 def find_eps_dims (match):
825 "Fill in dimensions of EPS files."
828 dims = bounding_box_dimensions (fn)
830 fn = os.path.join(g_outdir, fn)
832 return '%ipt' % dims[0]
836 sys.stderr.write ("invoking `%s'\n" % cmd)
839 error ('Error command exited with value %d\n' % st)
842 def compile_all_files (chunks):
848 if c[0] <> 'lilypond':
857 if base + '.ly' not in tex:
858 tex.append (base + '.ly')
859 elif e == 'png' and g_do_pictures:
865 # fixme: be sys-independent.
867 if g_outdir and x[0] <> '/' :
868 x = os.path.join (g_here_dir, x)
871 incs = map (incl_opt, include_path)
872 lilyopts = string.join (incs, ' ' )
873 texfiles = string.join (tex, ' ')
874 system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
876 system(r"tex '\nonstopmode \input %s'" % e)
877 system(r"dvips -E -o %s %s" % (e + '.eps', e))
879 cmd = r"""gs -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
880 cmd = cmd % (g + '.eps', g + '.png')
886 def update_file (body, name):
888 write the body if it has changed
899 f = open (name , 'w')
906 def getopt_args (opts):
907 "Construct arguments (LONG, SHORT) for getopt from list of options."
922 def option_help_str (o):
923 "Transform one option description (4-tuple ) into neatly formatted string"
941 return ' ' + sh + sep + long + arg
944 def options_help_str (opts):
945 "Convert a list of options into a neatly formatted string"
951 s = option_help_str (o)
952 strs.append ((s, o[3]))
958 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
962 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
963 Generate hybrid LaTeX input from Latex + lilypond
966 sys.stdout.write (options_help_str (option_definitions))
967 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
971 Report bugs to bug-gnu-music@gnu.org.
973 Written by Tom Cato Amundsen <tca@gnu.org> and
974 Han-Wen Nienhuys <hanwen@cs.uu.nl>
980 def write_deps (fn, target):
981 sys.stdout.write('writing `%s\'\n' % os.path.join(g_outdir, fn))
982 f = open (os.path.join(g_outdir, fn), 'w')
983 f.write ('%s%s: ' % (g_dep_prefix, target))
984 for d in __main__.read_files:
988 __main__.read_files = []
991 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
993 def print_version ():
995 sys.stdout.write (r"""Copyright 1998--1999
996 Distributed under terms of the GNU General Public License. It comes with
1001 def check_texidoc (chunks):
1004 if c[0] == 'lilypond':
1005 (type, body, opts, todo, basename) = c;
1006 pathbase = os.path.join (g_outdir, basename)
1007 if os.path.isfile (pathbase + '.texidoc'):
1008 body = '\n@include %s.texidoc' % basename + body
1009 c = (type, body, opts, todo, basename)
1013 def fix_epswidth (chunks):
1016 if c[0] == 'lilypond' and 'eps' in c[2]:
1017 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", find_eps_dims, c[1])
1018 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1020 newchunks.append (c)
1024 def do_file(input_filename):
1027 my_outname = outname
1029 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1030 my_depname = my_outname + '.dep'
1032 chunks = read_doc_file(input_filename)
1033 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1034 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1035 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1036 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1037 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1038 chunks = chop_chunks(chunks, 'numcols', do_columns)
1040 #for c in chunks: print "c:", c;
1042 scan_preamble(chunks)
1043 chunks = process_lilypond_blocks(my_outname, chunks)
1046 if __main__.g_run_lilypond:
1047 compile_all_files (chunks)
1048 chunks = fix_epswidth (chunks)
1050 if __main__.format == 'texi':
1051 chunks = check_texidoc (chunks)
1054 chunks = completize_preamble (chunks)
1055 foutn = os.path.join(g_outdir, my_outname + '.' + format)
1056 sys.stderr.write ("Writing `%s'\n" % foutn)
1057 fout = open (foutn, 'w')
1064 write_deps (my_depname, foutn)
1069 (sh, long) = getopt_args (__main__.option_definitions)
1070 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1071 except getopt.error, msg:
1072 sys.stderr.write("error: %s" % msg)
1080 if o == '--include' or o == '-I':
1081 include_path.append (a)
1082 elif o == '--version' or o == '-v':
1085 elif o == '--format' or o == '-f':
1087 elif o == '--outname' or o == '-o':
1090 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1093 elif o == '--help' or o == '-h':
1095 elif o == '--no-lily' or o == '-n':
1096 __main__.g_run_lilypond = 0
1097 elif o == '--dependencies' or o == '-M':
1099 elif o == '--default-music-fontsize':
1100 default_music_fontsize = string.atoi (a)
1101 elif o == '--default-lilypond-fontsize':
1102 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1103 default_music_fontsize = string.atoi (a)
1104 elif o == '--force-music-fontsize':
1105 g_force_lilypond_fontsize = string.atoi(a)
1106 elif o == '--force-lilypond-fontsize':
1107 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1108 g_force_lilypond_fontsize = string.atoi(a)
1109 elif o == '--dep-prefix':
1111 elif o == '--no-pictures':
1113 elif o == '--read-lys':
1115 elif o == '--outdir':
1120 if os.path.isfile(g_outdir):
1121 error ("outdir is a file: %s" % g_outdir)
1122 if not os.path.exists(g_outdir):
1124 for input_filename in files:
1125 do_file(input_filename)
1128 # Petr, ik zou willen dat ik iets zinvoller deed,
1129 # maar wat ik kan ik doen, het verandert toch niets?