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[dim]
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{}
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)))
500 sys.stderr.write (str + "\n Exiting ... \n\n")
504 def compose_full_body (body, opts):
505 """Construct the lilypond code to send to Lilypond.
506 Add stuff to BODY using OPTS as options."""
507 music_size = default_music_fontsize
508 latex_size = default_text_fontsize
510 if g_force_lilypond_fontsize:
511 music_size = g_force_lilypond_fontsize
513 m = re.match ('([0-9]+)pt', o)
515 music_size = string.atoi(m.group (1))
517 m = re.match ('latexfontsize=([0-9]+)pt', o)
519 latex_size = string.atoi (m.group (1))
521 if re.search ('\\\\score', body):
525 if 'fragment' in opts:
527 if 'nofragment' in opts:
530 if is_fragment and not 'multiline' in opts:
531 opts.append('singleline')
532 if 'singleline' in opts:
535 l = __main__.paperguru.get_linewidth()
537 if 'relative' in opts:#ugh only when is_fragment
538 body = '\\relative c { %s }' % body
547 optstring = string.join (opts, ' ')
548 optstring = re.sub ('\n', ' ', optstring)
550 %% Generated automatically by: lilypond-book.py
551 %% options are %s %%ughUGH not original options
552 \include "paper%d.ly"
553 \paper { linewidth = %f \pt }
554 """ % (optstring, music_size, l) + body
557 def parse_options_string(s):
559 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
560 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
561 r3 = re.compile("(\w+?)((,\s*)|$)")
566 d[m.group(2)] = re.split(",\s*", m.group(3))
571 d[m.group(2)] = m.group(3)
579 error ("format of option string invalid (was `%')" % s)
582 def scan_latex_preamble(chunks):
583 # first we want to scan the \documentclass line
584 # it should be the first non-comment line
587 if chunks[idx][0] == 'ignore':
590 m = get_re ('header').match(chunks[idx][1])
592 options = re.split (',[\n \t]*', m.group(1)[1:-1])
597 paperguru.m_landscape = 1
598 m = re.match("(.*?)paper", o)
600 paperguru.m_papersize = m.group()
602 m = re.match("(\d\d)pt", o)
604 paperguru.m_fontsize = int(m.group(1))
607 while chunks[idx][0] != 'preamble-end':
608 if chunks[idx] == 'ignore':
611 m = get_re ('geometry').search(chunks[idx][1])
613 paperguru.m_use_geometry = 1
614 o = parse_options_string(m.group('options'))
616 paperguru.set_geo_option(k, o[k])
619 def scan_texi_preamble (chunks):
620 # this is not bulletproof..., it checks the first 10 chunks
621 for c in chunks[:10]:
623 for s in ('afourpaper', 'afourwide', 'letterpaper',
624 'afourlatex', 'smallbook'):
625 if string.find(c[1], "@%s" % s) != -1:
626 paperguru.m_papersize = s
628 def scan_preamble (chunks):
629 if __main__.format == 'texi':
630 scan_texi_preamble(chunks)
632 assert __main__.format == 'latex'
633 scan_latex_preamble(chunks)
636 def completize_preamble (chunks):
637 if __main__.format == 'texi':
639 pre_b = post_b = graphics_b = None
641 if chunk[0] == 'preamble-end':
643 if chunk[0] == 'input':
644 m = get_re('def-pre-re').search(chunk[1])
647 if chunk[0] == 'input':
648 m = get_re('def-post-re').search(chunk[1])
651 if chunk[0] == 'input':
652 m = get_re('usepackage-graphics').search(chunk[1])
656 while chunks[x][0] != 'preamble-end':
659 chunks.insert(x, ('input', get_output ('output-default-pre')))
661 chunks.insert(x, ('input', get_output ('output-default-post')))
663 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
668 def find_file (name):
670 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
675 for a in include_path:
677 nm = os.path.join (a, name)
679 __main__.read_files.append (nm)
684 sys.stderr.write ("Reading `%s'\n" % nm)
685 return (f.read (), nm)
687 error ("File not found `%s'\n" % name)
690 def do_ignore(match_object):
691 return [('ignore', match_object.group('code'))]
692 def do_preamble_end(match_object):
693 return [('preamble-end', match_object.group('code'))]
695 def make_verbatim(match_object):
696 return [('verbatim', match_object.group('code'))]
698 def make_verb(match_object):
699 return [('verb', match_object.group('code'))]
701 def do_include_file(m):
703 return [('input', get_output ('pagebreak'))] \
704 + read_doc_file(m.group('filename')) \
705 + [('input', get_output ('pagebreak'))]
707 def do_input_file(m):
708 return read_doc_file(m.group('filename'))
710 def make_lilypond(m):
711 if m.group('options'):
712 options = m.group('options')
715 return [('input', get_output('output-lilypond-fragment') %
716 (options, m.group('code')))]
718 def make_lilypond_file(m):
721 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
722 into a @lilypond .. @end lilypond block.
726 if m.group('options'):
727 options = m.group('options')
730 (content, nm) = find_file(m.group('filename'))
731 options = "filename=%s," % nm + options
733 return [('input', get_output('output-lilypond') %
736 def make_lilypond_block(m):
737 if m.group('options'):
738 options = get_re('option-sep').split (m.group('options'))
741 options = filter(lambda s: s != '', options)
742 return [('lilypond', m.group('code'), options)]
745 if __main__.format != 'latex':
747 if m.group('num') == 'one':
748 return [('numcols', m.group('code'), 1)]
749 if m.group('num') == 'two':
750 return [('numcols', m.group('code'), 2)]
752 def chop_chunks(chunks, re_name, func, use_match=0):
758 m = get_re (re_name).search (str)
760 newchunks.append (('input', str))
764 newchunks.append (('input', str[:m.start ('match')]))
766 newchunks.append (('input', str[:m.start (0)]))
767 #newchunks.extend(func(m))
768 # python 1.5 compatible:
769 newchunks = newchunks + func(m)
770 str = str [m.end(0):]
775 def determine_format (str):
776 if __main__.format == '':
778 latex = re.search ('\\\\document', str[:200])
779 texinfo = re.search ('@node|@setfilename', str[:200])
784 if texinfo and latex == None:
786 elif latex and texinfo == None:
789 error("error: can't determine format, please specify")
792 if __main__.paperguru == None:
793 if __main__.format == 'texi':
798 __main__.paperguru = g
801 def read_doc_file (filename):
802 """Read the input file, find verbatim chunks and do \input and \include
804 (str, path) = find_file(filename)
805 determine_format (str)
807 chunks = [('input', str)]
809 # we have to check for verbatim before doing include,
810 # because we don't want to include files that are mentioned
811 # inside a verbatim environment
812 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
813 chunks = chop_chunks(chunks, 'verb', make_verb)
814 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
816 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
817 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
821 taken_file_names = {}
822 def schedule_lilypond_block (chunk):
823 """Take the body and options from CHUNK, figure out how the
824 real .ly should look, and what should be left MAIN_STR (meant
825 for the main file). The .ly is written, and scheduled in
828 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
830 TODO has format [basename, extension, extension, ... ]
833 (type, body, opts) = chunk
834 assert type == 'lilypond'
835 file_body = compose_full_body (body, opts)
836 basename = 'lily-' + `abs(hash (file_body))`
838 m = re.search ('filename="(.*?)"', o)
840 basename = m.group (1)
841 if not taken_file_names.has_key(basename):
842 taken_file_names[basename] = 0
844 taken_file_names[basename] = taken_file_names[basename] + 1
845 basename = basename + "-%i" % taken_file_names[basename]
847 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
848 needed_filetypes = ['tex']
851 needed_filetypes.append('eps')
852 needed_filetypes.append('png')
853 if 'eps' in opts and not ('eps' in needed_filetypes):
854 needed_filetypes.append('eps')
855 pathbase = os.path.join (g_outdir, basename)
856 def f(base, ext1, ext2):
857 a = os.path.isfile(base + ext2)
858 if (os.path.isfile(base + ext1) and
859 os.path.isfile(base + ext2) and
860 os.stat(base+ext1)[stat.ST_MTIME] >
861 os.stat(base+ext2)[stat.ST_MTIME]) or \
862 not os.path.isfile(base + ext2):
865 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
867 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
869 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
873 if 'printfilename' in opts:
875 m= re.match ("filename=(.*)", o)
877 newbody = newbody + get_output ("output-filename") % m.group(1)
881 if 'verbatim' in opts:
882 newbody = output_verbatim (body)
885 m = re.search ('intertext="(.*?)"', o)
887 newbody = newbody + m.group (1) + "\n\n"
888 if format == 'latex':
893 else: # format == 'texi'
895 newbody = newbody + get_output (s) % {'fn': basename }
896 return ('lilypond', newbody, opts, todo, basename)
898 def process_lilypond_blocks(outname, chunks):#ugh rename
900 # Count sections/chapters.
902 if c[0] == 'lilypond':
903 c = schedule_lilypond_block (c)
904 elif c[0] == 'numcols':
905 paperguru.m_num_cols = c[2]
910 def find_eps_dims (match):
911 "Fill in dimensions of EPS files."
914 dims = bounding_box_dimensions (fn)
916 fn = os.path.join(g_outdir, fn)
918 return '%ipt' % dims[0]
922 sys.stderr.write ("invoking `%s'\n" % cmd)
925 error ('Error command exited with value %d\n' % st)
928 def compile_all_files (chunks):
935 if c[0] <> 'lilypond':
944 if base + '.ly' not in tex:
945 tex.append (base + '.ly')
946 elif e == 'png' and g_do_pictures:
952 # fixme: be sys-independent.
954 if g_outdir and x[0] <> '/' :
955 x = os.path.join (g_here_dir, x)
958 incs = map (incl_opt, include_path)
959 lilyopts = string.join (incs, ' ' )
961 lilyopts = lilyopts + ' --dependencies '
963 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
964 texfiles = string.join (tex, ' ')
965 system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
968 # Ugh, fixing up dependencies for .tex generation
971 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
973 text=open (i).read ()
974 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
975 open (i, 'w').write (text)
978 system(r"tex '\nonstopmode \input %s'" % e)
979 system(r"dvips -E -o %s %s" % (e + '.eps', e))
981 cmd = r"""gs -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
982 cmd = cmd % (g + '.eps', g + '.png')
987 def update_file (body, name):
989 write the body if it has changed
1000 f = open (name , 'w')
1007 def getopt_args (opts):
1008 "Construct arguments (LONG, SHORT) for getopt from list of options."
1013 short = short + o[1]
1021 return (short, long)
1023 def option_help_str (o):
1024 "Transform one option description (4-tuple ) into neatly formatted string"
1042 return ' ' + sh + sep + long + arg
1045 def options_help_str (opts):
1046 "Convert a list of options into a neatly formatted string"
1052 s = option_help_str (o)
1053 strs.append ((s, o[3]))
1059 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1063 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1064 Generate hybrid LaTeX input from Latex + lilypond
1067 sys.stdout.write (options_help_str (option_definitions))
1068 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1072 Report bugs to bug-gnu-music@gnu.org.
1074 Written by Tom Cato Amundsen <tca@gnu.org> and
1075 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1081 def write_deps (fn, target, chunks):
1083 sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1084 f = open (os.path.join(g_outdir, fn), 'w')
1085 f.write ('%s%s: ' % (g_dep_prefix, target))
1086 for d in read_files:
1090 if c[0] == 'lilypond':
1091 (type, body, opts, todo, basename) = c;
1092 basenames.append (basename)
1095 d=g_outdir + '/' + d
1097 #if not os.isfile (d): # thinko?
1098 if not re.search ('/', d):
1099 d = g_dep_prefix + d
1100 f.write ('%s.tex ' % d)
1102 #if len (basenames):
1103 # for d in basenames:
1104 # f.write ('%s.ly ' % d)
1105 # f.write (' : %s' % target)
1111 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1113 def print_version ():
1115 sys.stdout.write (r"""Copyright 1998--1999
1116 Distributed under terms of the GNU General Public License. It comes with
1121 def check_texidoc (chunks):
1124 if c[0] == 'lilypond':
1125 (type, body, opts, todo, basename) = c;
1126 pathbase = os.path.join (g_outdir, basename)
1127 if os.path.isfile (pathbase + '.texidoc'):
1128 body = '\n@include %s.texidoc\n' % basename + body
1129 c = (type, body, opts, todo, basename)
1133 def fix_epswidth (chunks):
1136 if c[0] == 'lilypond' and 'eps' in c[2]:
1137 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", find_eps_dims, c[1])
1138 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1140 newchunks.append (c)
1145 def do_file(input_filename):
1149 my_outname = outname
1151 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1152 my_depname = my_outname + '.dep'
1154 chunks = read_doc_file(input_filename)
1155 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1156 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1157 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1158 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1159 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1160 chunks = chop_chunks(chunks, 'numcols', do_columns)
1162 #for c in chunks: print "c:", c;
1164 scan_preamble(chunks)
1165 chunks = process_lilypond_blocks(my_outname, chunks)
1167 foutn = os.path.join (g_outdir, my_outname + '.' + format)
1170 if __main__.g_run_lilypond:
1171 compile_all_files (chunks)
1172 chunks = fix_epswidth (chunks)
1174 if __main__.format == 'texi':
1175 chunks = check_texidoc (chunks)
1178 chunks = completize_preamble (chunks)
1179 sys.stderr.write ("Writing `%s'\n" % foutn)
1180 fout = open (foutn, 'w')
1187 write_deps (my_depname, foutn, chunks)
1192 (sh, long) = getopt_args (__main__.option_definitions)
1193 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1194 except getopt.error, msg:
1195 sys.stderr.write("error: %s" % msg)
1203 if o == '--include' or o == '-I':
1204 include_path.append (a)
1205 elif o == '--version' or o == '-v':
1208 elif o == '--format' or o == '-f':
1210 elif o == '--outname' or o == '-o':
1213 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1216 elif o == '--help' or o == '-h':
1218 elif o == '--no-lily' or o == '-n':
1219 __main__.g_run_lilypond = 0
1220 elif o == '--dependencies' or o == '-M':
1222 elif o == '--default-music-fontsize':
1223 default_music_fontsize = string.atoi (a)
1224 elif o == '--default-lilypond-fontsize':
1225 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1226 default_music_fontsize = string.atoi (a)
1227 elif o == '--force-music-fontsize':
1228 g_force_lilypond_fontsize = string.atoi(a)
1229 elif o == '--force-lilypond-fontsize':
1230 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1231 g_force_lilypond_fontsize = string.atoi(a)
1232 elif o == '--dep-prefix':
1234 elif o == '--no-pictures':
1236 elif o == '--read-lys':
1238 elif o == '--outdir':
1243 if os.path.isfile(g_outdir):
1244 error ("outdir is a file: %s" % g_outdir)
1245 if not os.path.exists(g_outdir):
1247 setup_environment ()
1248 for input_filename in files:
1249 do_file(input_filename)
1252 # Petr, ik zou willen dat ik iets zinvoller deed,
1253 # maar wat ik kan ik doen, het verandert toch niets?