2 # vim: set noexpandtab:
4 # * junk --outdir for --output
5 # * Figure out clean set of options.
7 # * EndLilyPondOutput is def'd as vfil. Causes large white gaps.
8 # * texinfo: add support for @pagesize
10 # todo: dimension handling (all the x2y) is clumsy. (tca: Thats
11 # because the values are taken directly from texinfo.tex,
12 # geometry.sty and article.cls. Give me a hint, and I'll
15 # This is was the idea for handling of comments:
16 # Multiline comments, @ignore .. @end ignore is scanned for
17 # in read_doc_file, and the chunks are marked as 'ignore', so
18 # lilypond-book will not touch them any more. The content of the
19 # chunks are written to the output file. Also 'include' and 'input'
20 # regex has to check if they are commented out.
22 # Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
23 # These three regex's has to check if they are on a commented line,
24 # % for latex, @c for texinfo.
26 # Then lines that are commented out with % (latex) and @c (Texinfo)
27 # are put into chunks marked 'ignore'. This cannot be done before
28 # searching for the lilypond-blocks because % is also the comment character
31 # The the rest of the rexeces are searched for. They don't have to test
32 # if they are on a commented out line.
44 program_version = '@TOPLEVEL_VERSION@'
45 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
46 program_version = '1.4pre'
49 # Try to cater for bad installations of LilyPond, that have
50 # broken TeX setup. Just hope this doesn't hurt good TeX
51 # setups. Maybe we should check if kpsewhich can find
52 # feta16.{afm,mf,tex,tfm}, and only set env upon failure.
56 'MFINPUTS' : datadir + '/mf:',
57 'TEXINPUTS': datadir + '/tex:' + datadir + '/ps:.:',
58 'TFMFONTS' : datadir + '/tfm:',
59 'GS_FONTPATH' : datadir + '/afm:' + datadir + '/pfa',
60 'GS_LIB' : datadir + '/ps',
63 def setup_environment ():
64 for key in environment.keys ():
65 val = environment[key]
66 if os.environ.has_key (key):
67 val = val + os.pathsep + os.environ[key]
72 include_path = [os.getcwd()]
75 # g_ is for global (?)
77 g_here_dir = os.getcwd ()
80 g_force_lilypond_fontsize = 0
88 default_music_fontsize = 16
89 default_text_fontsize = 12
92 # this code is ugly. It should be cleaned
96 # the dimensions are from geometry.sty
97 'a0paper': (mm2pt(841), mm2pt(1189)),
98 'a1paper': (mm2pt(595), mm2pt(841)),
99 'a2paper': (mm2pt(420), mm2pt(595)),
100 'a3paper': (mm2pt(297), mm2pt(420)),
101 'a4paper': (mm2pt(210), mm2pt(297)),
102 'a5paper': (mm2pt(149), mm2pt(210)),
103 'b0paper': (mm2pt(1000), mm2pt(1414)),
104 'b1paper': (mm2pt(707), mm2pt(1000)),
105 'b2paper': (mm2pt(500), mm2pt(707)),
106 'b3paper': (mm2pt(353), mm2pt(500)),
107 'b4paper': (mm2pt(250), mm2pt(353)),
108 'b5paper': (mm2pt(176), mm2pt(250)),
109 'letterpaper': (in2pt(8.5), in2pt(11)),
110 'legalpaper': (in2pt(8.5), in2pt(14)),
111 'executivepaper': (in2pt(7.25), in2pt(10.5))}
112 self.m_use_geometry = None
113 self.m_papersize = 'letterpaper'
117 self.m_geo_landscape = 0
118 self.m_geo_width = None
119 self.m_geo_textwidth = None
120 self.m_geo_lmargin = None
121 self.m_geo_rmargin = None
122 self.m_geo_includemp = None
123 self.m_geo_marginparwidth = {10: 57, 11: 50, 12: 35}
124 self.m_geo_marginparsep = {10: 11, 11: 10, 12: 10}
125 self.m_geo_x_marginparwidth = None
126 self.m_geo_x_marginparsep = None
128 def set_geo_option(self, name, value):
129 if name == 'body' or name == 'text':
130 if type(value) == type(""):
131 self.m_geo_textwidth = value
133 self.m_geo_textwidth = value[0]
135 elif name == 'portrait':
136 self.m_geo_landscape = 0
137 elif name == 'reversemp' or name == 'reversemarginpar':
138 if self.m_geo_includemp == None:
139 self.m_geo_includemp = 1
140 elif name == 'marginparwidth' or name == 'marginpar':
141 self.m_geo_x_marginparwidth = value
142 self.m_geo_includemp = 1
143 elif name == 'marginparsep':
144 self.m_geo_x_marginparsep = value
145 self.m_geo_includemp = 1
146 elif name == 'scale':
147 if type(value) == type(""):
148 self.m_geo_width = self.get_paperwidth() * float(value)
150 self.m_geo_width = self.get_paperwidth() * float(value[0])
151 elif name == 'hscale':
152 self.m_geo_width = self.get_paperwidth() * float(value)
153 elif name == 'left' or name == 'lmargin':
154 self.m_geo_lmargin = value
155 elif name == 'right' or name == 'rmargin':
156 self.m_geo_rmargin = value
157 elif name == 'hdivide' or name == 'divide':
158 if value[0] not in ('*', ''):
159 self.m_geo_lmargin = value[0]
160 if value[1] not in ('*', ''):
161 self.m_geo_width = value[1]
162 if value[2] not in ('*', ''):
163 self.m_geo_rmargin = value[2]
164 elif name == 'hmargin':
165 if type(value) == type(""):
166 self.m_geo_lmargin = value
167 self.m_geo_rmargin = value
169 self.m_geo_lmargin = value[0]
170 self.m_geo_rmargin = value[1]
171 elif name == 'margin':#ugh there is a bug about this option in
172 # the geometry documentation
173 if type(value) == type(""):
174 self.m_geo_lmargin = value
175 self.m_geo_rmargin = value
177 self.m_geo_lmargin = value[0]
178 self.m_geo_rmargin = value[0]
179 elif name == 'total':
180 if type(value) == type(""):
181 self.m_geo_width = value
183 self.m_geo_width = value[0]
184 elif name == 'width' or name == 'totalwidth':
185 self.m_geo_width = value
186 elif name == 'paper' or name == 'papername':
187 self.m_papersize = value
188 elif name[-5:] == 'paper':
189 self.m_papersize = name
191 self._set_dimen('m_geo_'+name, value)
192 def __setattr__(self, name, value):
193 if type(value) == type("") and \
194 dimension_conversion_dict.has_key (value[-2:]):
195 f = dimension_conversion_dict[value[-2:]]
196 self.__dict__[name] = f(float(value[:-2]))
198 self.__dict__[name] = value
201 s = "LatexPaper:\n-----------"
202 for v in self.__dict__.keys():
204 s = s + str (v) + ' ' + str (self.__dict__[v])
205 s = s + "-----------"
208 def get_linewidth(self):
209 w = self._calc_linewidth()
210 if self.m_num_cols == 2:
214 def get_paperwidth(self):
215 #if self.m_use_geometry:
216 return self.m_paperdef[self.m_papersize][self.m_landscape or self.m_geo_landscape]
217 #return self.m_paperdef[self.m_papersize][self.m_landscape]
219 def _calc_linewidth(self):
220 # since geometry sometimes ignores 'includemp', this is
221 # more complicated than it should be
223 if self.m_geo_includemp:
224 if self.m_geo_x_marginparsep is not None:
225 mp = mp + self.m_geo_x_marginparsep
227 mp = mp + self.m_geo_marginparsep[self.m_fontsize]
228 if self.m_geo_x_marginparwidth is not None:
229 mp = mp + self.m_geo_x_marginparwidth
231 mp = mp + self.m_geo_marginparwidth[self.m_fontsize]
233 #ugh test if this is necessary
237 if not self.m_use_geometry:
238 return latex_linewidths[self.m_papersize][self.m_fontsize]
240 geo_opts = (self.m_geo_lmargin == None,
241 self.m_geo_width == None,
242 self.m_geo_rmargin == None)
244 if geo_opts == (1, 1, 1):
245 if self.m_geo_textwidth:
246 return self.m_geo_textwidth
247 w = self.get_paperwidth() * 0.8
249 elif geo_opts == (0, 1, 1):
250 if self.m_geo_textwidth:
251 return self.m_geo_textwidth
252 return self.f1(self.m_geo_lmargin, mp)
253 elif geo_opts == (1, 1, 0):
254 if self.m_geo_textwidth:
255 return self.m_geo_textwidth
256 return self.f1(self.m_geo_rmargin, mp)
258 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
259 if self.m_geo_textwidth:
260 return self.m_geo_textwidth
261 return self.m_geo_width - mp
262 elif geo_opts in ((0, 1, 0), (0, 0, 0)):
263 w = self.get_paperwidth() \
264 - self.m_geo_lmargin - self.m_geo_rmargin - mp
268 raise "Never do this!"
270 tmp = self.get_paperwidth() - m * 2 - mp
275 tmp = self.get_paperwidth() - self.m_geo_lmargin \
283 self.m_papersize = 'letterpaper'
285 def get_linewidth(self):
286 return texi_linewidths[self.m_papersize][self.m_fontsize]
292 def em2pt(x, fontsize = 10):
293 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
294 def ex2pt(x, fontsize = 10):
295 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
300 dimension_conversion_dict ={
310 # indices are no. of columns, papersize, fontsize
311 # Why can't this be calculated?
313 'a4paper':{10: 345, 11: 360, 12: 390},
314 'a4paper-landscape': {10: 598, 11: 596, 12:592},
315 'a5paper':{10: 276, 11: 276, 12: 276},
316 'b5paper':{10: 345, 11: 356, 12: 356},
317 'letterpaper':{10: 345, 11: 360, 12: 390},
318 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
319 'legalpaper': {10: 345, 11: 360, 12: 390},
320 'executivepaper':{10: 345, 11: 360, 12: 379}}
323 'afourpaper': {12: mm2pt(160)},
324 'afourwide': {12: in2pt(6.5)},
325 'afourlatex': {12: mm2pt(150)},
326 'smallbook': {12: in2pt(5)},
327 'letterpaper': {12: in2pt(6)}}
329 option_definitions = [
330 ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'),
331 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
332 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
333 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
334 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
335 ('DIR', 'I', 'include', 'include path'),
336 ('', 'M', 'dependencies', 'write dependencies'),
337 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
338 ('', 'n', 'no-lily', 'don\'t run lilypond'),
339 ('', '', 'no-pictures', "don\'t generate pictures"),
340 ('', '', 'read-lys', "don't write ly files."),
341 ('FILE', 'o', 'outname', 'filename main output file'),
342 ('FILE', '', 'outdir', "where to place generated files"),
343 ('', 'v', 'version', 'print version information' ),
344 ('', 'h', 'help', 'print help'),
347 # format specific strings, ie. regex-es for input, and % strings for output
350 'output-lilypond-fragment' : r"""\begin[eps,singleline,%s]{lilypond}
357 'output-filename' : r'''
360 'output-lilypond': r"""\begin[%s]{lilypond}
363 'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
364 'output-default-post': "\\def\postLilypondExample{}\n",
365 'output-default-pre': "\\def\preLilypondExample{}\n",
366 'usepackage-graphics': '\\usepackage{graphics}\n',
367 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
368 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
369 'pagebreak': r'\pagebreak',
371 'texi' : {'output-lilypond': """@lilypond[%s]
375 'output-filename' : r'''
378 'output-lilypond-fragment': """@lilypond[%s]
379 \context Staff\context Voice{ %s }
382 'output-verbatim': r"""@example
387 # do some tweaking: @ is needed in some ps stuff.
388 # override EndLilyPondOutput, since @tex is done
389 # in a sandbox, you can't do \input lilyponddefs at the
390 # top of the document.
392 # should also support fragment in
398 \def\EndLilyPondOutput{}
404 <a href="%(fn)s.png">
405 <img border=0 src="%(fn)s.png" alt="[picture of music]">
412 def output_verbatim (body):
413 if __main__.format == 'texi':
414 body = re.sub ('([@{}])', '@\\1', body)
415 return get_output ('output-verbatim') % body
419 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
420 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
421 'option-sep' : ', *',
422 'header': r"\\documentclass\s*(\[.*?\])?",
423 'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
424 'preamble-end': r'(?P<code>\\begin{document})',
425 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
426 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
427 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile(\[(?P<options>.*?)\])?\{(?P<filename>.+)})',
428 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
429 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin(\[(?P<options>.*?)\])?{lilypond}(?P<code>.*?)\\end{lilypond})",
430 'def-post-re': r"\\def\\postLilypondExample",
431 'def-pre-re': r"\\def\\preLilypondExample",
432 'usepackage-graphics': r"\usepackage{graphics}",
433 'intertext': r',?\s*intertext=\".*?\"',
434 'multiline-comment': no_match,
435 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
436 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
440 # why do we have distinction between @mbinclude and @include?
442 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
445 'preamble-end': no_match,
446 'landscape': no_match,
447 'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
448 'verb': r"""(?P<code>@code{.*?})""",
449 'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
450 'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
451 'lilypond-block': r"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
452 'option-sep' : ', *',
453 'intertext': r',?\s*intertext=\".*?\"',
454 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
455 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
461 for r in re_dict.keys ():
464 for k in olddict.keys ():
465 newdict[k] = re.compile (olddict[k])
479 def get_output (name):
480 return output_dict[format][name]
483 return re_dict[format][name]
485 def bounding_box_dimensions(fname):
487 fname = os.path.join(g_outdir, fname)
491 error ("Error opening `%s'" % fname)
493 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
495 return (int(s.group(3))-int(s.group(1)),
496 int(s.group(4))-int(s.group(2)))
501 sys.stderr.write (str + "\n Exiting ... \n\n")
505 def compose_full_body (body, opts):
506 """Construct the lilypond code to send to Lilypond.
507 Add stuff to BODY using OPTS as options."""
508 music_size = default_music_fontsize
509 latex_size = default_text_fontsize
511 if g_force_lilypond_fontsize:
512 music_size = g_force_lilypond_fontsize
514 m = re.match ('([0-9]+)pt', o)
516 music_size = string.atoi(m.group (1))
518 m = re.match ('latexfontsize=([0-9]+)pt', o)
520 latex_size = string.atoi (m.group (1))
522 if re.search ('\\\\score', body):
526 if 'fragment' in opts:
528 if 'nofragment' in opts:
531 if is_fragment and not 'multiline' in opts:
532 opts.append('singleline')
533 if 'singleline' in opts:
536 l = __main__.paperguru.get_linewidth()
539 m= re.search ('relative(.*)', o)
543 v = string.atoi (m.group (1))
550 pitch = pitch + '\,' * v
552 pitch = pitch + '\'' * v
554 body = '\\relative %s { %s }' %(pitch, body)
563 optstring = string.join (opts, ' ')
564 optstring = re.sub ('\n', ' ', optstring)
566 %% Generated automatically by: lilypond-book.py
568 \include "paper%d.ly"
569 \paper { linewidth = %f \pt }
570 """ % (optstring, music_size, l) + body
572 # ughUGH not original options
575 def parse_options_string(s):
577 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
578 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
579 r3 = re.compile("(\w+?)((,\s*)|$)")
584 d[m.group(2)] = re.split(",\s*", m.group(3))
589 d[m.group(2)] = m.group(3)
597 error ("format of option string invalid (was `%')" % s)
600 def scan_latex_preamble(chunks):
601 # first we want to scan the \documentclass line
602 # it should be the first non-comment line
605 if chunks[idx][0] == 'ignore':
608 m = get_re ('header').match(chunks[idx][1])
610 options = re.split (',[\n \t]*', m.group(1)[1:-1])
615 paperguru.m_landscape = 1
616 m = re.match("(.*?)paper", o)
618 paperguru.m_papersize = m.group()
620 m = re.match("(\d\d)pt", o)
622 paperguru.m_fontsize = int(m.group(1))
625 while chunks[idx][0] != 'preamble-end':
626 if chunks[idx] == 'ignore':
629 m = get_re ('geometry').search(chunks[idx][1])
631 paperguru.m_use_geometry = 1
632 o = parse_options_string(m.group('options'))
634 paperguru.set_geo_option(k, o[k])
637 def scan_texi_preamble (chunks):
638 # this is not bulletproof..., it checks the first 10 chunks
639 for c in chunks[:10]:
641 for s in ('afourpaper', 'afourwide', 'letterpaper',
642 'afourlatex', 'smallbook'):
643 if string.find(c[1], "@%s" % s) != -1:
644 paperguru.m_papersize = s
646 def scan_preamble (chunks):
647 if __main__.format == 'texi':
648 scan_texi_preamble(chunks)
650 assert __main__.format == 'latex'
651 scan_latex_preamble(chunks)
654 def completize_preamble (chunks):
655 if __main__.format == 'texi':
657 pre_b = post_b = graphics_b = None
659 if chunk[0] == 'preamble-end':
661 if chunk[0] == 'input':
662 m = get_re('def-pre-re').search(chunk[1])
665 if chunk[0] == 'input':
666 m = get_re('def-post-re').search(chunk[1])
669 if chunk[0] == 'input':
670 m = get_re('usepackage-graphics').search(chunk[1])
674 while chunks[x][0] != 'preamble-end':
677 chunks.insert(x, ('input', get_output ('output-default-pre')))
679 chunks.insert(x, ('input', get_output ('output-default-post')))
681 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
686 def find_file (name):
688 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
693 for a in include_path:
695 nm = os.path.join (a, name)
697 __main__.read_files.append (nm)
702 sys.stderr.write ("Reading `%s'\n" % nm)
703 return (f.read (), nm)
705 error ("File not found `%s'\n" % name)
708 def do_ignore(match_object):
709 return [('ignore', match_object.group('code'))]
710 def do_preamble_end(match_object):
711 return [('preamble-end', match_object.group('code'))]
713 def make_verbatim(match_object):
714 return [('verbatim', match_object.group('code'))]
716 def make_verb(match_object):
717 return [('verb', match_object.group('code'))]
719 def do_include_file(m):
721 return [('input', get_output ('pagebreak'))] \
722 + read_doc_file(m.group('filename')) \
723 + [('input', get_output ('pagebreak'))]
725 def do_input_file(m):
726 return read_doc_file(m.group('filename'))
728 def make_lilypond(m):
729 if m.group('options'):
730 options = m.group('options')
733 return [('input', get_output('output-lilypond-fragment') %
734 (options, m.group('code')))]
736 def make_lilypond_file(m):
739 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
740 into a @lilypond .. @end lilypond block.
744 if m.group('options'):
745 options = m.group('options')
748 (content, nm) = find_file(m.group('filename'))
749 options = "filename=%s," % nm + options
751 return [('input', get_output('output-lilypond') %
754 def make_lilypond_block(m):
755 if m.group('options'):
756 options = get_re('option-sep').split (m.group('options'))
759 options = filter(lambda s: s != '', options)
760 return [('lilypond', m.group('code'), options)]
763 if __main__.format != 'latex':
765 if m.group('num') == 'one':
766 return [('numcols', m.group('code'), 1)]
767 if m.group('num') == 'two':
768 return [('numcols', m.group('code'), 2)]
770 def chop_chunks(chunks, re_name, func, use_match=0):
776 m = get_re (re_name).search (str)
778 newchunks.append (('input', str))
782 newchunks.append (('input', str[:m.start ('match')]))
784 newchunks.append (('input', str[:m.start (0)]))
785 #newchunks.extend(func(m))
786 # python 1.5 compatible:
787 newchunks = newchunks + func(m)
788 str = str [m.end(0):]
793 def determine_format (str):
794 if __main__.format == '':
796 latex = re.search ('\\\\document', str[:200])
797 texinfo = re.search ('@node|@setfilename', str[:200])
802 if texinfo and latex == None:
804 elif latex and texinfo == None:
807 error("error: can't determine format, please specify")
810 if __main__.paperguru == None:
811 if __main__.format == 'texi':
816 __main__.paperguru = g
819 def read_doc_file (filename):
820 """Read the input file, find verbatim chunks and do \input and \include
822 (str, path) = find_file(filename)
823 determine_format (str)
825 chunks = [('input', str)]
827 # we have to check for verbatim before doing include,
828 # because we don't want to include files that are mentioned
829 # inside a verbatim environment
830 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
831 chunks = chop_chunks(chunks, 'verb', make_verb)
832 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
834 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
835 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
839 taken_file_names = {}
840 def schedule_lilypond_block (chunk):
841 """Take the body and options from CHUNK, figure out how the
842 real .ly should look, and what should be left MAIN_STR (meant
843 for the main file). The .ly is written, and scheduled in
846 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
848 TODO has format [basename, extension, extension, ... ]
851 (type, body, opts) = chunk
852 assert type == 'lilypond'
853 file_body = compose_full_body (body, opts)
854 basename = 'lily-' + `abs(hash (file_body))`
856 m = re.search ('filename="(.*?)"', o)
858 basename = m.group (1)
859 if not taken_file_names.has_key(basename):
860 taken_file_names[basename] = 0
862 taken_file_names[basename] = taken_file_names[basename] + 1
863 basename = basename + "-%i" % taken_file_names[basename]
865 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
866 needed_filetypes = ['tex']
869 needed_filetypes.append('eps')
870 needed_filetypes.append('png')
871 if 'eps' in opts and not ('eps' in needed_filetypes):
872 needed_filetypes.append('eps')
873 pathbase = os.path.join (g_outdir, basename)
874 def f(base, ext1, ext2):
875 a = os.path.isfile(base + ext2)
876 if (os.path.isfile(base + ext1) and
877 os.path.isfile(base + ext2) and
878 os.stat(base+ext1)[stat.ST_MTIME] >
879 os.stat(base+ext2)[stat.ST_MTIME]) or \
880 not os.path.isfile(base + ext2):
883 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
885 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
887 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
891 if 'printfilename' in opts:
893 m= re.match ("filename=(.*)", o)
895 newbody = newbody + get_output ("output-filename") % m.group(1)
899 if 'verbatim' in opts:
900 newbody = output_verbatim (body)
903 m = re.search ('intertext="(.*?)"', o)
905 newbody = newbody + m.group (1) + "\n\n"
906 if format == 'latex':
911 else: # format == 'texi'
913 newbody = newbody + get_output (s) % {'fn': basename }
914 return ('lilypond', newbody, opts, todo, basename)
916 def process_lilypond_blocks(outname, chunks):#ugh rename
918 # Count sections/chapters.
920 if c[0] == 'lilypond':
921 c = schedule_lilypond_block (c)
922 elif c[0] == 'numcols':
923 paperguru.m_num_cols = c[2]
928 def find_eps_dims (match):
929 "Fill in dimensions of EPS files."
932 dims = bounding_box_dimensions (fn)
934 fn = os.path.join(g_outdir, fn)
936 return '%ipt' % dims[0]
940 sys.stderr.write ("invoking `%s'\n" % cmd)
943 error ('Error command exited with value %d\n' % st)
946 def compile_all_files (chunks):
953 if c[0] <> 'lilypond':
962 if base + '.ly' not in tex:
963 tex.append (base + '.ly')
964 elif e == 'png' and g_do_pictures:
970 # fixme: be sys-independent.
972 if g_outdir and x[0] <> '/' :
973 x = os.path.join (g_here_dir, x)
976 incs = map (incl_opt, include_path)
977 lilyopts = string.join (incs, ' ' )
979 lilyopts = lilyopts + ' --dependencies '
981 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
982 texfiles = string.join (tex, ' ')
983 system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
986 # Ugh, fixing up dependencies for .tex generation
989 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
994 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1000 system(r"tex '\nonstopmode \input %s'" % e)
1001 system(r"dvips -E -o %s %s" % (e + '.eps', e))
1003 cmd = r"""gs -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
1004 cmd = cmd % (g + '.eps', g + '.png')
1006 status = system (cmd)
1008 os.unlink (g + '.png')
1009 error ("Removing output file")
1014 def update_file (body, name):
1016 write the body if it has changed
1027 f = open (name , 'w')
1034 def getopt_args (opts):
1035 "Construct arguments (LONG, SHORT) for getopt from list of options."
1040 short = short + o[1]
1048 return (short, long)
1050 def option_help_str (o):
1051 "Transform one option description (4-tuple ) into neatly formatted string"
1069 return ' ' + sh + sep + long + arg
1072 def options_help_str (opts):
1073 "Convert a list of options into a neatly formatted string"
1079 s = option_help_str (o)
1080 strs.append ((s, o[3]))
1086 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1090 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1091 Generate hybrid LaTeX input from Latex + lilypond
1094 sys.stdout.write (options_help_str (option_definitions))
1095 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1099 Report bugs to bug-gnu-music@gnu.org.
1101 Written by Tom Cato Amundsen <tca@gnu.org> and
1102 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1108 def write_deps (fn, target, chunks):
1110 sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1111 f = open (os.path.join(g_outdir, fn), 'w')
1112 f.write ('%s%s: ' % (g_dep_prefix, target))
1113 for d in read_files:
1117 if c[0] == 'lilypond':
1118 (type, body, opts, todo, basename) = c;
1119 basenames.append (basename)
1122 d=g_outdir + '/' + d
1124 #if not os.isfile (d): # thinko?
1125 if not re.search ('/', d):
1126 d = g_dep_prefix + d
1127 f.write ('%s.tex ' % d)
1129 #if len (basenames):
1130 # for d in basenames:
1131 # f.write ('%s.ly ' % d)
1132 # f.write (' : %s' % target)
1138 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1140 def print_version ():
1142 sys.stdout.write (r"""Copyright 1998--1999
1143 Distributed under terms of the GNU General Public License. It comes with
1148 def check_texidoc (chunks):
1151 if c[0] == 'lilypond':
1152 (type, body, opts, todo, basename) = c;
1153 pathbase = os.path.join (g_outdir, basename)
1154 if os.path.isfile (pathbase + '.texidoc'):
1155 body = '\n@include %s.texidoc\n' % basename + body
1156 c = (type, body, opts, todo, basename)
1160 def fix_epswidth (chunks):
1163 if c[0] == 'lilypond' and 'eps' in c[2]:
1164 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", find_eps_dims, c[1])
1165 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1167 newchunks.append (c)
1172 def do_file(input_filename):
1176 my_outname = outname
1178 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1179 my_depname = my_outname + '.dep'
1181 chunks = read_doc_file(input_filename)
1182 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1183 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1184 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1185 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1186 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1187 chunks = chop_chunks(chunks, 'numcols', do_columns)
1189 #for c in chunks: print "c:", c;
1191 scan_preamble(chunks)
1192 chunks = process_lilypond_blocks(my_outname, chunks)
1194 foutn = os.path.join (g_outdir, my_outname + '.' + format)
1197 if __main__.g_run_lilypond:
1198 compile_all_files (chunks)
1199 chunks = fix_epswidth (chunks)
1201 if __main__.format == 'texi':
1202 chunks = check_texidoc (chunks)
1205 chunks = completize_preamble (chunks)
1206 sys.stderr.write ("Writing `%s'\n" % foutn)
1207 fout = open (foutn, 'w')
1214 write_deps (my_depname, foutn, chunks)
1219 (sh, long) = getopt_args (__main__.option_definitions)
1220 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1221 except getopt.error, msg:
1222 sys.stderr.write("error: %s" % msg)
1230 if o == '--include' or o == '-I':
1231 include_path.append (a)
1232 elif o == '--version' or o == '-v':
1235 elif o == '--format' or o == '-f':
1237 elif o == '--outname' or o == '-o':
1240 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1243 elif o == '--help' or o == '-h':
1245 elif o == '--no-lily' or o == '-n':
1246 __main__.g_run_lilypond = 0
1247 elif o == '--dependencies' or o == '-M':
1249 elif o == '--default-music-fontsize':
1250 default_music_fontsize = string.atoi (a)
1251 elif o == '--default-lilypond-fontsize':
1252 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1253 default_music_fontsize = string.atoi (a)
1254 elif o == '--force-music-fontsize':
1255 g_force_lilypond_fontsize = string.atoi(a)
1256 elif o == '--force-lilypond-fontsize':
1257 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1258 g_force_lilypond_fontsize = string.atoi(a)
1259 elif o == '--dep-prefix':
1261 elif o == '--no-pictures':
1263 elif o == '--read-lys':
1265 elif o == '--outdir':
1270 if os.path.isfile(g_outdir):
1271 error ("outdir is a file: %s" % g_outdir)
1272 if not os.path.exists(g_outdir):
1274 setup_environment ()
1275 for input_filename in files:
1276 do_file(input_filename)
1279 # Petr, ik zou willen dat ik iets zinvoller deed,
1280 # maar wat ik kan ik doen, het verandert toch niets?