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 <img src="%(fn)s.png" alt="">
410 def output_verbatim (body):
411 if __main__.format == 'texi':
412 body = re.sub ('([@{}])', '@\\1', body)
413 return get_output ('output-verbatim') % body
417 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
418 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
419 'option-sep' : ', *',
420 'header': r"\\documentclass\s*(\[.*?\])?",
421 'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
422 'preamble-end': r'(?P<code>\\begin{document})',
423 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
424 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
425 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile(\[(?P<options>.*?)\])?\{(?P<filename>.+)})',
426 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
427 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin(\[(?P<options>.*?)\])?{lilypond}(?P<code>.*?)\\end{lilypond})",
428 'def-post-re': r"\\def\\postLilypondExample",
429 'def-pre-re': r"\\def\\preLilypondExample",
430 'usepackage-graphics': r"\usepackage{graphics}",
431 'intertext': r',?\s*intertext=\".*?\"',
432 'multiline-comment': no_match,
433 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
434 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
438 # why do we have distinction between @mbinclude and @include?
440 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
443 'preamble-end': no_match,
444 'landscape': no_match,
445 'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
446 'verb': r"""(?P<code>@code{.*?})""",
447 'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
448 'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
449 'lilypond-block': r"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
450 'option-sep' : ', *',
451 'intertext': r',?\s*intertext=\".*?\"',
452 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
453 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
459 for r in re_dict.keys ():
462 for k in olddict.keys ():
463 newdict[k] = re.compile (olddict[k])
477 def get_output (name):
478 return output_dict[format][name]
481 return re_dict[format][name]
483 def bounding_box_dimensions(fname):
485 fname = os.path.join(g_outdir, fname)
489 error ("Error opening `%s'" % fname)
491 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
493 return (int(s.group(3))-int(s.group(1)),
494 int(s.group(4))-int(s.group(2)))
499 sys.stderr.write (str + "\n Exiting ... \n\n")
503 def compose_full_body (body, opts):
504 """Construct the lilypond code to send to Lilypond.
505 Add stuff to BODY using OPTS as options."""
506 music_size = default_music_fontsize
507 latex_size = default_text_fontsize
509 if g_force_lilypond_fontsize:
510 music_size = g_force_lilypond_fontsize
512 m = re.match ('([0-9]+)pt', o)
514 music_size = string.atoi(m.group (1))
516 m = re.match ('latexfontsize=([0-9]+)pt', o)
518 latex_size = string.atoi (m.group (1))
520 if re.search ('\\\\score', body):
524 if 'fragment' in opts:
526 if 'nofragment' in opts:
529 if is_fragment and not 'multiline' in opts:
530 opts.append('singleline')
531 if 'singleline' in opts:
534 l = __main__.paperguru.get_linewidth()
537 m= re.search ('relative(.*)', o)
541 v = string.atoi (m.group (1))
548 pitch = pitch + '\,' * v
550 pitch = pitch + '\'' * v
552 body = '\\relative %s { %s }' %(pitch, body)
561 optstring = string.join (opts, ' ')
562 optstring = re.sub ('\n', ' ', optstring)
564 %% Generated automatically by: lilypond-book.py
566 \include "paper%d.ly"
567 \paper { linewidth = %f \pt }
568 """ % (optstring, music_size, l) + body
570 # ughUGH not original options
573 def parse_options_string(s):
575 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
576 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
577 r3 = re.compile("(\w+?)((,\s*)|$)")
582 d[m.group(2)] = re.split(",\s*", m.group(3))
587 d[m.group(2)] = m.group(3)
595 error ("format of option string invalid (was `%')" % s)
598 def scan_latex_preamble(chunks):
599 # first we want to scan the \documentclass line
600 # it should be the first non-comment line
603 if chunks[idx][0] == 'ignore':
606 m = get_re ('header').match(chunks[idx][1])
608 options = re.split (',[\n \t]*', m.group(1)[1:-1])
613 paperguru.m_landscape = 1
614 m = re.match("(.*?)paper", o)
616 paperguru.m_papersize = m.group()
618 m = re.match("(\d\d)pt", o)
620 paperguru.m_fontsize = int(m.group(1))
623 while chunks[idx][0] != 'preamble-end':
624 if chunks[idx] == 'ignore':
627 m = get_re ('geometry').search(chunks[idx][1])
629 paperguru.m_use_geometry = 1
630 o = parse_options_string(m.group('options'))
632 paperguru.set_geo_option(k, o[k])
635 def scan_texi_preamble (chunks):
636 # this is not bulletproof..., it checks the first 10 chunks
637 for c in chunks[:10]:
639 for s in ('afourpaper', 'afourwide', 'letterpaper',
640 'afourlatex', 'smallbook'):
641 if string.find(c[1], "@%s" % s) != -1:
642 paperguru.m_papersize = s
644 def scan_preamble (chunks):
645 if __main__.format == 'texi':
646 scan_texi_preamble(chunks)
648 assert __main__.format == 'latex'
649 scan_latex_preamble(chunks)
652 def completize_preamble (chunks):
653 if __main__.format == 'texi':
655 pre_b = post_b = graphics_b = None
657 if chunk[0] == 'preamble-end':
659 if chunk[0] == 'input':
660 m = get_re('def-pre-re').search(chunk[1])
663 if chunk[0] == 'input':
664 m = get_re('def-post-re').search(chunk[1])
667 if chunk[0] == 'input':
668 m = get_re('usepackage-graphics').search(chunk[1])
672 while chunks[x][0] != 'preamble-end':
675 chunks.insert(x, ('input', get_output ('output-default-pre')))
677 chunks.insert(x, ('input', get_output ('output-default-post')))
679 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
684 def find_file (name):
686 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
691 for a in include_path:
693 nm = os.path.join (a, name)
695 __main__.read_files.append (nm)
700 sys.stderr.write ("Reading `%s'\n" % nm)
701 return (f.read (), nm)
703 error ("File not found `%s'\n" % name)
706 def do_ignore(match_object):
707 return [('ignore', match_object.group('code'))]
708 def do_preamble_end(match_object):
709 return [('preamble-end', match_object.group('code'))]
711 def make_verbatim(match_object):
712 return [('verbatim', match_object.group('code'))]
714 def make_verb(match_object):
715 return [('verb', match_object.group('code'))]
717 def do_include_file(m):
719 return [('input', get_output ('pagebreak'))] \
720 + read_doc_file(m.group('filename')) \
721 + [('input', get_output ('pagebreak'))]
723 def do_input_file(m):
724 return read_doc_file(m.group('filename'))
726 def make_lilypond(m):
727 if m.group('options'):
728 options = m.group('options')
731 return [('input', get_output('output-lilypond-fragment') %
732 (options, m.group('code')))]
734 def make_lilypond_file(m):
737 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
738 into a @lilypond .. @end lilypond block.
742 if m.group('options'):
743 options = m.group('options')
746 (content, nm) = find_file(m.group('filename'))
747 options = "filename=%s," % nm + options
749 return [('input', get_output('output-lilypond') %
752 def make_lilypond_block(m):
753 if m.group('options'):
754 options = get_re('option-sep').split (m.group('options'))
757 options = filter(lambda s: s != '', options)
758 return [('lilypond', m.group('code'), options)]
761 if __main__.format != 'latex':
763 if m.group('num') == 'one':
764 return [('numcols', m.group('code'), 1)]
765 if m.group('num') == 'two':
766 return [('numcols', m.group('code'), 2)]
768 def chop_chunks(chunks, re_name, func, use_match=0):
774 m = get_re (re_name).search (str)
776 newchunks.append (('input', str))
780 newchunks.append (('input', str[:m.start ('match')]))
782 newchunks.append (('input', str[:m.start (0)]))
783 #newchunks.extend(func(m))
784 # python 1.5 compatible:
785 newchunks = newchunks + func(m)
786 str = str [m.end(0):]
791 def determine_format (str):
792 if __main__.format == '':
794 latex = re.search ('\\\\document', str[:200])
795 texinfo = re.search ('@node|@setfilename', str[:200])
800 if texinfo and latex == None:
802 elif latex and texinfo == None:
805 error("error: can't determine format, please specify")
808 if __main__.paperguru == None:
809 if __main__.format == 'texi':
814 __main__.paperguru = g
817 def read_doc_file (filename):
818 """Read the input file, find verbatim chunks and do \input and \include
820 (str, path) = find_file(filename)
821 determine_format (str)
823 chunks = [('input', str)]
825 # we have to check for verbatim before doing include,
826 # because we don't want to include files that are mentioned
827 # inside a verbatim environment
828 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
829 chunks = chop_chunks(chunks, 'verb', make_verb)
830 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
832 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
833 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
837 taken_file_names = {}
838 def schedule_lilypond_block (chunk):
839 """Take the body and options from CHUNK, figure out how the
840 real .ly should look, and what should be left MAIN_STR (meant
841 for the main file). The .ly is written, and scheduled in
844 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
846 TODO has format [basename, extension, extension, ... ]
849 (type, body, opts) = chunk
850 assert type == 'lilypond'
851 file_body = compose_full_body (body, opts)
852 basename = 'lily-' + `abs(hash (file_body))`
854 m = re.search ('filename="(.*?)"', o)
856 basename = m.group (1)
857 if not taken_file_names.has_key(basename):
858 taken_file_names[basename] = 0
860 taken_file_names[basename] = taken_file_names[basename] + 1
861 basename = basename + "-%i" % taken_file_names[basename]
863 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
864 needed_filetypes = ['tex']
867 needed_filetypes.append('eps')
868 needed_filetypes.append('png')
869 if 'eps' in opts and not ('eps' in needed_filetypes):
870 needed_filetypes.append('eps')
871 pathbase = os.path.join (g_outdir, basename)
872 def f(base, ext1, ext2):
873 a = os.path.isfile(base + ext2)
874 if (os.path.isfile(base + ext1) and
875 os.path.isfile(base + ext2) and
876 os.stat(base+ext1)[stat.ST_MTIME] >
877 os.stat(base+ext2)[stat.ST_MTIME]) or \
878 not os.path.isfile(base + ext2):
881 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
883 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
885 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
889 if 'printfilename' in opts:
891 m= re.match ("filename=(.*)", o)
893 newbody = newbody + get_output ("output-filename") % m.group(1)
897 if 'verbatim' in opts:
898 newbody = output_verbatim (body)
901 m = re.search ('intertext="(.*?)"', o)
903 newbody = newbody + m.group (1) + "\n\n"
904 if format == 'latex':
909 else: # format == 'texi'
911 newbody = newbody + get_output (s) % {'fn': basename }
912 return ('lilypond', newbody, opts, todo, basename)
914 def process_lilypond_blocks(outname, chunks):#ugh rename
916 # Count sections/chapters.
918 if c[0] == 'lilypond':
919 c = schedule_lilypond_block (c)
920 elif c[0] == 'numcols':
921 paperguru.m_num_cols = c[2]
926 def find_eps_dims (match):
927 "Fill in dimensions of EPS files."
930 dims = bounding_box_dimensions (fn)
932 fn = os.path.join(g_outdir, fn)
934 return '%ipt' % dims[0]
938 sys.stderr.write ("invoking `%s'\n" % cmd)
941 error ('Error command exited with value %d\n' % st)
944 def compile_all_files (chunks):
951 if c[0] <> 'lilypond':
960 if base + '.ly' not in tex:
961 tex.append (base + '.ly')
962 elif e == 'png' and g_do_pictures:
968 # fixme: be sys-independent.
970 if g_outdir and x[0] <> '/' :
971 x = os.path.join (g_here_dir, x)
974 incs = map (incl_opt, include_path)
975 lilyopts = string.join (incs, ' ' )
977 lilyopts = lilyopts + ' --dependencies '
979 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
980 texfiles = string.join (tex, ' ')
981 system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
984 # Ugh, fixing up dependencies for .tex generation
987 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
989 text=open (i).read ()
990 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
991 open (i, 'w').write (text)
994 system(r"tex '\nonstopmode \input %s'" % e)
995 system(r"dvips -E -o %s %s" % (e + '.eps', e))
997 cmd = r"""gs -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
998 cmd = cmd % (g + '.eps', g + '.png')
1000 status = system (cmd)
1002 os.unlink (g + '.png')
1003 error ("Removing output file")
1008 def update_file (body, name):
1010 write the body if it has changed
1021 f = open (name , 'w')
1028 def getopt_args (opts):
1029 "Construct arguments (LONG, SHORT) for getopt from list of options."
1034 short = short + o[1]
1042 return (short, long)
1044 def option_help_str (o):
1045 "Transform one option description (4-tuple ) into neatly formatted string"
1063 return ' ' + sh + sep + long + arg
1066 def options_help_str (opts):
1067 "Convert a list of options into a neatly formatted string"
1073 s = option_help_str (o)
1074 strs.append ((s, o[3]))
1080 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1084 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1085 Generate hybrid LaTeX input from Latex + lilypond
1088 sys.stdout.write (options_help_str (option_definitions))
1089 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1093 Report bugs to bug-gnu-music@gnu.org.
1095 Written by Tom Cato Amundsen <tca@gnu.org> and
1096 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1102 def write_deps (fn, target, chunks):
1104 sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1105 f = open (os.path.join(g_outdir, fn), 'w')
1106 f.write ('%s%s: ' % (g_dep_prefix, target))
1107 for d in read_files:
1111 if c[0] == 'lilypond':
1112 (type, body, opts, todo, basename) = c;
1113 basenames.append (basename)
1116 d=g_outdir + '/' + d
1118 #if not os.isfile (d): # thinko?
1119 if not re.search ('/', d):
1120 d = g_dep_prefix + d
1121 f.write ('%s.tex ' % d)
1123 #if len (basenames):
1124 # for d in basenames:
1125 # f.write ('%s.ly ' % d)
1126 # f.write (' : %s' % target)
1132 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1134 def print_version ():
1136 sys.stdout.write (r"""Copyright 1998--1999
1137 Distributed under terms of the GNU General Public License. It comes with
1142 def check_texidoc (chunks):
1145 if c[0] == 'lilypond':
1146 (type, body, opts, todo, basename) = c;
1147 pathbase = os.path.join (g_outdir, basename)
1148 if os.path.isfile (pathbase + '.texidoc'):
1149 body = '\n@include %s.texidoc\n' % basename + body
1150 c = (type, body, opts, todo, basename)
1154 def fix_epswidth (chunks):
1157 if c[0] == 'lilypond' and 'eps' in c[2]:
1158 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", find_eps_dims, c[1])
1159 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1161 newchunks.append (c)
1166 def do_file(input_filename):
1170 my_outname = outname
1172 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1173 my_depname = my_outname + '.dep'
1175 chunks = read_doc_file(input_filename)
1176 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1177 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1178 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1179 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1180 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1181 chunks = chop_chunks(chunks, 'numcols', do_columns)
1183 #for c in chunks: print "c:", c;
1185 scan_preamble(chunks)
1186 chunks = process_lilypond_blocks(my_outname, chunks)
1188 foutn = os.path.join (g_outdir, my_outname + '.' + format)
1191 if __main__.g_run_lilypond:
1192 compile_all_files (chunks)
1193 chunks = fix_epswidth (chunks)
1195 if __main__.format == 'texi':
1196 chunks = check_texidoc (chunks)
1199 chunks = completize_preamble (chunks)
1200 sys.stderr.write ("Writing `%s'\n" % foutn)
1201 fout = open (foutn, 'w')
1208 write_deps (my_depname, foutn, chunks)
1213 (sh, long) = getopt_args (__main__.option_definitions)
1214 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1215 except getopt.error, msg:
1216 sys.stderr.write("error: %s" % msg)
1224 if o == '--include' or o == '-I':
1225 include_path.append (a)
1226 elif o == '--version' or o == '-v':
1229 elif o == '--format' or o == '-f':
1231 elif o == '--outname' or o == '-o':
1234 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1237 elif o == '--help' or o == '-h':
1239 elif o == '--no-lily' or o == '-n':
1240 __main__.g_run_lilypond = 0
1241 elif o == '--dependencies' or o == '-M':
1243 elif o == '--default-music-fontsize':
1244 default_music_fontsize = string.atoi (a)
1245 elif o == '--default-lilypond-fontsize':
1246 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1247 default_music_fontsize = string.atoi (a)
1248 elif o == '--force-music-fontsize':
1249 g_force_lilypond_fontsize = string.atoi(a)
1250 elif o == '--force-lilypond-fontsize':
1251 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1252 g_force_lilypond_fontsize = string.atoi(a)
1253 elif o == '--dep-prefix':
1255 elif o == '--no-pictures':
1257 elif o == '--read-lys':
1259 elif o == '--outdir':
1264 if os.path.isfile(g_outdir):
1265 error ("outdir is a file: %s" % g_outdir)
1266 if not os.path.exists(g_outdir):
1268 setup_environment ()
1269 for input_filename in files:
1270 do_file(input_filename)
1273 # Petr, ik zou willen dat ik iets zinvoller deed,
1274 # maar wat ik kan ik doen, het verandert toch niets?