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
351 @include %(fn)s.texidoc
355 \def\EndLilyPondOutput{}
367 def output_verbatim (body):
368 if __main__.format == 'texi':
369 body = re.sub ('([@{}])', '@\\1', body)
370 return get_output ('output-verbatim') % body
374 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
375 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
376 'option-sep' : ', *',
377 'header': r"\\documentclass\s*(\[.*?\])?",
378 'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
379 'preamble-end': r'(?P<code>\\begin{document})',
380 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
381 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
382 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile(\[(?P<options>.*?)\])?\{(?P<filename>.+)})',
383 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
384 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin(\[(?P<options>.*?)\])?{lilypond}(?P<code>.*?)\\end{lilypond})",
385 'def-post-re': r"\\def\\postLilypondExample",
386 'def-pre-re': r"\\def\\preLilypondExample",
387 'usepackage-graphics': r"\usepackage{graphics}",
388 'intertext': r',?\s*intertext=\".*?\"',
389 'multiline-comment': no_match,
390 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
391 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
395 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
398 'preamble-end': no_match,
399 'landscape': no_match,
400 'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
401 'verb': r"""(?P<code>@code{.*?})""",
402 'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
403 'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
404 'lilypond-block': r"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
405 'option-sep' : ', *',
406 'intertext': r',?\s*intertext=\".*?\"',
407 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
408 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
414 for r in re_dict.keys ():
417 for k in olddict.keys ():
418 newdict[k] = re.compile (olddict[k])
432 def get_output (name):
433 return output_dict[format][name]
436 return re_dict[format][name]
438 def bounding_box_dimensions(fname):
440 fname = os.path.join(g_outdir, fname)
444 error ("Error opening `%s'" % fname)
446 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
448 return (int(s.group(3))-int(s.group(1)),
449 int(s.group(4))-int(s.group(2)))
455 sys.stderr.write (str + "\n Exiting ... \n\n")
459 def compose_full_body (body, opts):
460 """Construct the lilypond code to send to Lilypond.
461 Add stuff to BODY using OPTS as options."""
462 music_size = default_music_fontsize
463 latex_size = default_text_fontsize
465 if g_force_lilypond_fontsize:
466 music_size = g_force_lilypond_fontsize
468 m = re.match ('([0-9]+)pt', o)
470 music_size = string.atoi(m.group (1))
472 m = re.match ('latexfontsize=([0-9]+)pt', o)
474 latex_size = string.atoi (m.group (1))
476 if re.search ('\\\\score', body):
480 if 'fragment' in opts:
482 if 'nonfragment' in opts:
485 if is_fragment and not 'multiline' in opts:
486 opts.append('singleline')
487 if 'singleline' in opts:
490 l = paperguru.get_linewidth()
492 if 'relative' in opts:#ugh only when is_fragment
493 body = '\\relative c { %s }' % body
502 optstring = string.join (opts, ' ')
503 optstring = re.sub ('\n', ' ', optstring)
505 %% Generated by lilypond-book.py; options are %s %%ughUGH not original options
506 \include "paper%d.ly"
507 \paper { linewidth = %f \pt; }
508 """ % (optstring, music_size, l) + body
511 def parse_options_string(s):
513 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
514 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
515 r3 = re.compile("(\w+?)((,\s*)|$)")
520 d[m.group(2)] = re.split(",\s*", m.group(3))
525 d[m.group(2)] = m.group(3)
532 print "trøbbel:%s:" % s
535 def scan_latex_preamble(chunks):
536 # first we want to scan the \documentclass line
537 # it should be the first non-comment line
540 if chunks[idx][0] == 'ignore':
543 m = get_re ('header').match(chunks[idx][1])
544 options = re.split (',[\n \t]*', m.group(1)[1:-1])
547 paperguru.m_landscape = 1
548 m = re.match("(.*?)paper", o)
550 paperguru.m_papersize = m.group()
552 m = re.match("(\d\d)pt", o)
554 paperguru.m_fontsize = int(m.group(1))
557 while chunks[idx][0] != 'preamble-end':
558 if chunks[idx] == 'ignore':
561 m = get_re ('geometry').search(chunks[idx][1])
563 paperguru.m_use_geometry = 1
564 o = parse_options_string(m.group('options'))
566 paperguru.set_geo_option(k, o[k])
569 def scan_texi_preamble (chunks):
570 # this is not bulletproof..., it checks the first 10 chunks
573 if chunks[idx][0] == 'input':
574 for s in ('afourpaper', 'afourwide', 'letterpaper',
575 'afourlatex', 'smallbook'):
576 if string.find(chunks[idx][1], "@%s" % s) != -1:
577 paperguru.m_papersize = s
579 if idx == 10 or idx == len(chunks):
582 def scan_preamble (chunks):
583 if __main__.format == 'texi':
584 scan_texi_preamble(chunks)
586 assert __main__.format == 'latex'
587 scan_latex_preamble(chunks)
590 def completize_preamble (chunks):
591 if __main__.format == 'texi':
593 pre_b = post_b = graphics_b = None
595 if chunk[0] == 'preamble-end':
597 if chunk[0] == 'input':
598 m = get_re('def-pre-re').search(chunk[1])
601 if chunk[0] == 'input':
602 m = get_re('def-post-re').search(chunk[1])
605 if chunk[0] == 'input':
606 m = get_re('usepackage-graphics').search(chunk[1])
610 while chunks[x][0] != 'preamble-end':
613 chunks.insert(x, ('input', get_output ('output-default-pre')))
615 chunks.insert(x, ('input', get_output ('output-default-post')))
617 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
622 def find_file (name):
624 for a in include_path:
626 nm = os.path.join (a, name)
628 __main__.read_files.append (nm)
635 error ("File not found `%s'\n" % name)
638 def do_ignore(match_object):
639 return [('ignore', match_object.group('code'))]
640 def do_preamble_end(match_object):
641 return [('preamble-end', match_object.group('code'))]
643 def make_verbatim(match_object):
644 return [('verbatim', match_object.group('code'))]
646 def make_verb(match_object):
647 return [('verb', match_object.group('code'))]
649 def do_include_file(m):
651 return [('input', get_output ('pagebreak'))] \
652 + read_doc_file(m.group('filename')) \
653 + [('input', get_output ('pagebreak'))]
655 def do_input_file(m):
656 return read_doc_file(m.group('filename'))
658 def make_lilypond(m):
659 if m.group('options'):
660 options = m.group('options')
663 return [('input', get_output('output-lilypond-fragment') %
664 (options, m.group('code')))]
666 def make_lilypond_file(m):
667 if m.group('options'):
668 options = m.group('options')
671 return [('input', get_output('output-lilypond') %
672 (options, find_file(m.group('filename'))))]
674 def make_lilypond_block(m):
675 if m.group('options'):
676 options = get_re('option-sep').split (m.group('options'))
679 options = filter(lambda s: s != '', options)
680 return [('lilypond', m.group('code'), options)]
683 if __main__.format != 'latex':
685 if m.group('num') == 'one':
686 return [('numcols', m.group('code'), 1)]
687 if m.group('num') == 'two':
688 return [('numcols', m.group('code'), 2)]
690 def chop_chunks(chunks, re_name, func, use_match=0):
696 m = get_re (re_name).search (str)
698 newchunks.append (('input', str))
702 newchunks.append (('input', str[:m.start ('match')]))
704 newchunks.append (('input', str[:m.start (0)]))
705 #newchunks.extend(func(m))
706 # python 1.5 compatible:
707 newchunks = newchunks + func(m)
708 str = str [m.end(0):]
713 def read_doc_file (filename):
714 """Read the input file, find verbatim chunks and do \input and \include
717 str = find_file(filename)
719 if __main__.format == '':
720 latex = re.search ('\\\\document', str[:200])
721 texinfo = re.search ('@node|@setfilename', str[:200])
722 if (texinfo and latex) or not (texinfo or latex):
723 error("error: can't determine format, please specify")
725 __main__.format = 'texi'
727 __main__.format = 'latex'
728 if __main__.format == 'texi':
729 __main__.paperguru = TexiPaper()
731 __main__.paperguru = LatexPaper()
732 chunks = [('input', str)]
733 # we have to check for verbatim before doing include,
734 # because we don't want to include files that are mentioned
735 # inside a verbatim environment
736 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
737 chunks = chop_chunks(chunks, 'verb', make_verb)
738 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
740 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
741 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
745 taken_file_names = {}
746 def schedule_lilypond_block (chunk):
747 """Take the body and options from CHUNK, figure out how the
748 real .ly should look, and what should be left MAIN_STR (meant
749 for the main file). The .ly is written, and scheduled in
752 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
754 TODO has format [basename, extension, extension, ... ]
757 (type, body, opts) = chunk
758 assert type == 'lilypond'
759 file_body = compose_full_body (body, opts)
760 basename = `abs(hash (file_body))`
762 m = re.search ('filename="(.*?)"', o)
764 basename = m.group (1)
765 if not taken_file_names.has_key(basename):
766 taken_file_names[basename] = 0
768 taken_file_names[basename] = taken_file_names[basename] + 1
769 basename = basename + "-%i" % taken_file_names[basename]
771 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
772 needed_filetypes = ['tex']
775 needed_filetypes.append('eps')
776 needed_filetypes.append('png')
777 if 'eps' in opts and not ('eps' in needed_filetypes):
778 needed_filetypes.append('eps')
779 outname = os.path.join(g_outdir, basename)
780 def f(base, ext1, ext2):
781 a = os.path.isfile(base + ext2)
782 if (os.path.isfile(base + ext1) and
783 os.path.isfile(base + ext2) and
784 os.stat(base+ext1)[stat.ST_MTIME] >
785 os.stat(base+ext2)[stat.ST_MTIME]) or \
786 not os.path.isfile(base + ext2):
789 if 'tex' in needed_filetypes and f(outname, '.ly', '.tex'):
791 if 'eps' in needed_filetypes and f(outname, '.tex', '.eps'):
793 if 'png' in needed_filetypes and f(outname, '.eps', '.png'):
796 if 'verbatim' in opts:
797 newbody = output_verbatim (body)
800 m = re.search ('intertext="(.*?)"', o)
802 newbody = newbody + m.group (1) + "\n\n"
803 if format == 'latex':
808 else: # format == 'texi'
810 newbody = newbody + get_output(s) % {'fn': basename }
811 return ('lilypond', newbody, opts, todo, basename)
813 def process_lilypond_blocks(outname, chunks):#ugh rename
815 # Count sections/chapters.
817 if c[0] == 'lilypond':
818 c = schedule_lilypond_block (c)
819 elif c[0] == 'numcols':
820 paperguru.m_num_cols = c[2]
825 def find_eps_dims (match):
826 "Fill in dimensions of EPS files."
829 dims = bounding_box_dimensions (fn)
831 fn = os.path.join(g_outdir, fn)
833 return '%ipt' % dims[0]
837 sys.stderr.write ("invoking `%s'\n" % cmd)
840 error ('Error command exited with value %d\n' % st)
843 def compile_all_files (chunks):
849 if c[0] <> 'lilypond':
858 if base + '.ly' not in tex:
859 tex.append (base + '.ly')
860 elif e == 'png' and g_do_pictures:
866 # fixme: be sys-independent.
868 if g_outdir and x[0] <> '/' :
869 x = os.path.join (g_here_dir, x)
872 incs = map (incl_opt, include_path)
873 lilyopts = string.join (incs, ' ' )
874 texfiles = string.join (tex, ' ')
875 system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
877 system(r"tex '\nonstopmode \input %s'" % e)
878 system(r"dvips -E -o %s %s" % (e + '.eps', e))
880 cmd = r"""gs -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
881 cmd = cmd % (g + '.eps', g + '.png')
887 def update_file (body, name):
889 write the body if it has changed
900 f = open (name , 'w')
907 def getopt_args (opts):
908 "Construct arguments (LONG, SHORT) for getopt from list of options."
923 def option_help_str (o):
924 "Transform one option description (4-tuple ) into neatly formatted string"
942 return ' ' + sh + sep + long + arg
945 def options_help_str (opts):
946 "Convert a list of options into a neatly formatted string"
952 s = option_help_str (o)
953 strs.append ((s, o[3]))
959 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
963 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
964 Generate hybrid LaTeX input from Latex + lilypond
967 sys.stdout.write (options_help_str (option_definitions))
968 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
972 Report bugs to bug-gnu-music@gnu.org.
974 Written by Tom Cato Amundsen <tca@gnu.org> and
975 Han-Wen Nienhuys <hanwen@cs.uu.nl>
981 def write_deps (fn, target):
982 sys.stdout.write('writing `%s\'\n' % os.path.join(g_outdir, fn))
983 f = open (os.path.join(g_outdir, fn), 'w')
984 f.write ('%s%s: ' % (g_dep_prefix, target))
985 for d in __main__.read_files:
989 __main__.read_files = []
992 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
994 def print_version ():
996 sys.stdout.write (r"""Copyright 1998--1999
997 Distributed under terms of the GNU General Public License. It comes with
1001 def do_file(input_filename):
1004 my_outname = outname
1006 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1007 my_depname = my_outname + '.dep'
1009 chunks = read_doc_file(input_filename)
1010 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1011 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1012 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1013 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1014 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1015 chunks = chop_chunks(chunks, 'numcols', do_columns)
1017 #for c in chunks: print "c:", c;
1019 scan_preamble(chunks)
1020 chunks = process_lilypond_blocks(my_outname, chunks)
1022 if __main__.g_run_lilypond:
1023 compile_all_files (chunks)
1027 if c[0] == 'lilypond' and 'eps' in c[2]:
1028 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", find_eps_dims, c[1])
1029 newchunks.append (('lilypond', body))
1031 newchunks.append (c)
1034 chunks = completize_preamble (chunks)
1035 foutn = os.path.join(g_outdir, my_outname + '.' + format)
1036 sys.stderr.write ("Writing `%s'\n" % foutn)
1037 fout = open (foutn, 'w')
1044 write_deps (my_depname, foutn)
1049 (sh, long) = getopt_args (__main__.option_definitions)
1050 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1051 except getopt.error, msg:
1052 sys.stderr.write("error: %s" % msg)
1060 if o == '--include' or o == '-I':
1061 include_path.append (a)
1062 elif o == '--version' or o == '-v':
1065 elif o == '--format' or o == '-f':
1067 elif o == '--outname' or o == '-o':
1070 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1073 elif o == '--help' or o == '-h':
1075 elif o == '--no-lily' or o == '-n':
1076 __main__.g_run_lilypond = 0
1077 elif o == '--dependencies' or o == '-M':
1079 elif o == '--default-music-fontsize':
1080 default_music_fontsize = string.atoi (a)
1081 elif o == '--default-lilypond-fontsize':
1082 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1083 default_music_fontsize = string.atoi (a)
1084 elif o == '--force-music-fontsize':
1085 g_force_lilypond_fontsize = string.atoi(a)
1086 elif o == '--force-lilypond-fontsize':
1087 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1088 g_force_lilypond_fontsize = string.atoi(a)
1089 elif o == '--dep-prefix':
1091 elif o == '--no-pictures':
1093 elif o == '--read-lys':
1095 elif o == '--outdir':
1100 if os.path.isfile(g_outdir):
1101 error ("outdir is a file: %s" % g_outdir)
1102 if not os.path.exists(g_outdir):
1104 for input_filename in files:
1105 do_file(input_filename)
1108 # Petr, ik zou willen dat ik iets zinvoller deed,
1109 # maar wat ik kan ik doen, het verandert toch niets?