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
67 # this code is ugly. It should be cleaned
71 # the dimensions are from geometry.sty
72 'a0paper': (mm2pt(841), mm2pt(1189)),
73 'a1paper': (mm2pt(595), mm2pt(841)),
74 'a2paper': (mm2pt(420), mm2pt(595)),
75 'a3paper': (mm2pt(297), mm2pt(420)),
76 'a4paper': (mm2pt(210), mm2pt(297)),
77 'a5paper': (mm2pt(149), mm2pt(210)),
78 'b0paper': (mm2pt(1000), mm2pt(1414)),
79 'b1paper': (mm2pt(707), mm2pt(1000)),
80 'b2paper': (mm2pt(500), mm2pt(707)),
81 'b3paper': (mm2pt(353), mm2pt(500)),
82 'b4paper': (mm2pt(250), mm2pt(353)),
83 'b5paper': (mm2pt(176), mm2pt(250)),
84 'letterpaper': (in2pt(8.5), in2pt(11)),
85 'legalpaper': (in2pt(8.5), in2pt(14)),
86 'executivepaper': (in2pt(7.25), in2pt(10.5))}
87 self.m_use_geometry = None
88 self.m_papersize = 'letterpaper'
92 self.m_geo_landscape = 0
93 self.m_geo_width = None
94 self.m_geo_textwidth = None
95 self.m_geo_lmargin = None
96 self.m_geo_rmargin = None
97 self.m_geo_includemp = None
98 self.m_geo_marginparwidth = {10: 57, 11: 50, 12: 35}
99 self.m_geo_marginparsep = {10: 11, 11: 10, 12: 10}
100 self.m_geo_x_marginparwidth = None
101 self.m_geo_x_marginparsep = None
103 def set_geo_option(self, name, value):
104 if name == 'body' or name == 'text':
105 if type(value) == type(""):
106 self.m_geo_textwidth = value
108 self.m_geo_textwidth = value[0]
110 elif name == 'portrait':
111 self.m_geo_landscape = 0
112 elif name == 'reversemp' or name == 'reversemarginpar':
113 if self.m_geo_includemp == None:
114 self.m_geo_includemp = 1
115 elif name == 'marginparwidth' or name == 'marginpar':
116 self.m_geo_x_marginparwidth = value
117 self.m_geo_includemp = 1
118 elif name == 'marginparsep':
119 self.m_geo_x_marginparsep = value
120 self.m_geo_includemp = 1
121 elif name == 'scale':
122 if type(value) == type(""):
123 self.m_geo_width = self.get_paperwidth() * float(value)
125 self.m_geo_width = self.get_paperwidth() * float(value[0])
126 elif name == 'hscale':
127 self.m_geo_width = self.get_paperwidth() * float(value)
128 elif name == 'left' or name == 'lmargin':
129 self.m_geo_lmargin = value
130 elif name == 'right' or name == 'rmargin':
131 self.m_geo_rmargin = value
132 elif name == 'hdivide' or name == 'divide':
133 if value[0] not in ('*', ''):
134 self.m_geo_lmargin = value[0]
135 if value[1] not in ('*', ''):
136 self.m_geo_width = value[1]
137 if value[2] not in ('*', ''):
138 self.m_geo_rmargin = value[2]
139 elif name == 'hmargin':
140 if type(value) == type(""):
141 self.m_geo_lmargin = value
142 self.m_geo_rmargin = value
144 self.m_geo_lmargin = value[0]
145 self.m_geo_rmargin = value[1]
146 elif name == 'margin':#ugh there is a bug about this option in
147 # the geometry documentation
148 if type(value) == type(""):
149 self.m_geo_lmargin = value
150 self.m_geo_rmargin = value
152 self.m_geo_lmargin = value[0]
153 self.m_geo_rmargin = value[0]
154 elif name == 'total':
155 if type(value) == type(""):
156 self.m_geo_width = value
158 self.m_geo_width = value[0]
159 elif name == 'width' or name == 'totalwidth':
160 self.m_geo_width = value
161 elif name == 'paper' or name == 'papername':
162 self.m_papersize = value
163 elif name[-5:] == 'paper':
164 self.m_papersize = name
166 self._set_dimen('m_geo_'+name, value)
167 def __setattr__(self, name, value):
168 if type(value) == type("") and \
169 dimension_conversion_dict.has_key (value[-2:]):
170 f = dimension_conversion_dict[dim]
171 self.__dict__[name] = f(float(value[:-2]))
173 self.__dict__[name] = value
176 s = "LatexPaper:\n-----------"
177 for v in self.__dict__.keys():
179 s = s + str (v) + ' ' + str (self.__dict__[v])
180 s = s + "-----------"
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]
208 #ugh test if this is necessary
212 if not self.m_use_geometry:
213 return latex_linewidths[self.m_papersize][self.m_fontsize]
215 geo_opts = (self.m_geo_lmargin == None,
216 self.m_geo_width == None,
217 self.m_geo_rmargin == None)
219 if geo_opts == (1, 1, 1):
220 if self.m_geo_textwidth:
221 return self.m_geo_textwidth
222 w = self.get_paperwidth() * 0.8
224 elif geo_opts == (0, 1, 1):
225 if self.m_geo_textwidth:
226 return self.m_geo_textwidth
227 return self.f1(self.m_geo_lmargin, mp)
228 elif geo_opts == (1, 1, 0):
229 if self.m_geo_textwidth:
230 return self.m_geo_textwidth
231 return self.f1(self.m_geo_rmargin, mp)
233 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
234 if self.m_geo_textwidth:
235 return self.m_geo_textwidth
236 return self.m_geo_width - mp
237 elif geo_opts in ((0, 1, 0), (0, 0, 0)):
238 w = self.get_paperwidth() \
239 - self.m_geo_lmargin - self.m_geo_rmargin - mp
243 raise "Never do this!"
245 tmp = self.get_paperwidth() - m * 2 - mp
250 tmp = self.get_paperwidth() - self.m_geo_lmargin \
258 self.m_papersize = 'letterpaper'
260 def get_linewidth(self):
261 return texi_linewidths[self.m_papersize][self.m_fontsize]
267 def em2pt(x, fontsize = 10):
268 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
269 def ex2pt(x, fontsize = 10):
270 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
275 dimension_conversion_dict ={
285 # indices are no. of columns, papersize, fontsize
286 # Why can't this be calculated?
288 'a4paper':{10: 345, 11: 360, 12: 390},
289 'a4paper-landscape': {10: 598, 11: 596, 12:592},
290 'a5paper':{10: 276, 11: 276, 12: 276},
291 'b5paper':{10: 345, 11: 356, 12: 356},
292 'letterpaper':{10: 345, 11: 360, 12: 390},
293 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
294 'legalpaper': {10: 345, 11: 360, 12: 390},
295 'executivepaper':{10: 345, 11: 360, 12: 379}}
298 'afourpaper': {12: mm2pt(160)},
299 'afourwide': {12: in2pt(6.5)},
300 'afourlatex': {12: mm2pt(150)},
301 'smallbook': {12: in2pt(5)},
302 'letterpaper': {12: in2pt(6)}}
304 option_definitions = [
305 ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'),
306 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
307 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
308 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
309 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
310 ('DIR', 'I', 'include', 'include path'),
311 ('', 'M', 'dependencies', 'write dependencies'),
312 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
313 ('', 'n', 'no-lily', 'don\'t run lilypond'),
314 ('', '', 'no-pictures', "don\'t generate pictures"),
315 ('', '', 'read-lys', "don't write ly files."),
316 ('FILE', 'o', 'outname', 'filename main output file'),
317 ('FILE', '', 'outdir', "where to place generated files"),
318 ('', 'v', 'version', 'print version information' ),
319 ('', 'h', 'help', 'print help'),
322 # format specific strings, ie. regex-es for input, and % strings for output
325 'output-lilypond-fragment' : r"""\begin[eps,singleline,%s]{lilypond}
332 'output-filename' : r'''
335 'output-lilypond': r"""\begin[%s]{lilypond}
338 'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
339 'output-default-post': "\\def\postLilypondExample{}\n",
340 'output-default-pre': "\\def\preLilypondExample{}\n",
341 'usepackage-graphics': '\\usepackage{graphics}\n',
342 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
343 'output-tex': '\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n',
344 'pagebreak': r'\pagebreak',
346 'texi' : {'output-lilypond': """@lilypond[%s]
350 'output-filename' : r'''
353 'output-lilypond-fragment': """@lilypond[%s]
354 \context Staff\context Voice{ %s }
357 'output-verbatim': r"""@example
362 # do some tweaking: @ is needed in some ps stuff.
363 # override EndLilyPondOutput, since @tex is done
364 # in a sandbox, you can't do \input lilyponddefs at the
365 # top of the document.
367 # should also support fragment in
373 \def\EndLilyPondOutput{}
385 def output_verbatim (body):
386 if __main__.format == 'texi':
387 body = re.sub ('([@{}])', '@\\1', body)
388 return get_output ('output-verbatim') % body
392 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
393 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
394 'option-sep' : ', *',
395 'header': r"\\documentclass\s*(\[.*?\])?",
396 'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
397 'preamble-end': r'(?P<code>\\begin{document})',
398 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
399 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
400 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile(\[(?P<options>.*?)\])?\{(?P<filename>.+)})',
401 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
402 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin(\[(?P<options>.*?)\])?{lilypond}(?P<code>.*?)\\end{lilypond})",
403 'def-post-re': r"\\def\\postLilypondExample",
404 'def-pre-re': r"\\def\\preLilypondExample",
405 'usepackage-graphics': r"\usepackage{graphics}",
406 'intertext': r',?\s*intertext=\".*?\"',
407 'multiline-comment': no_match,
408 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
409 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
413 # why do we have distinction between @mbinclude and @include?
415 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
418 'preamble-end': no_match,
419 'landscape': no_match,
420 'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
421 'verb': r"""(?P<code>@code{.*?})""",
422 'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
423 'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
424 'lilypond-block': r"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
425 'option-sep' : ', *',
426 'intertext': r',?\s*intertext=\".*?\"',
427 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
428 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
434 for r in re_dict.keys ():
437 for k in olddict.keys ():
438 newdict[k] = re.compile (olddict[k])
452 def get_output (name):
453 return output_dict[format][name]
456 return re_dict[format][name]
458 def bounding_box_dimensions(fname):
460 fname = os.path.join(g_outdir, fname)
464 error ("Error opening `%s'" % fname)
466 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
468 return (int(s.group(3))-int(s.group(1)),
469 int(s.group(4))-int(s.group(2)))
475 sys.stderr.write (str + "\n Exiting ... \n\n")
479 def compose_full_body (body, opts):
480 """Construct the lilypond code to send to Lilypond.
481 Add stuff to BODY using OPTS as options."""
482 music_size = default_music_fontsize
483 latex_size = default_text_fontsize
485 if g_force_lilypond_fontsize:
486 music_size = g_force_lilypond_fontsize
488 m = re.match ('([0-9]+)pt', o)
490 music_size = string.atoi(m.group (1))
492 m = re.match ('latexfontsize=([0-9]+)pt', o)
494 latex_size = string.atoi (m.group (1))
496 if re.search ('\\\\score', body):
500 if 'fragment' in opts:
502 if 'nofragment' in opts:
505 if is_fragment and not 'multiline' in opts:
506 opts.append('singleline')
507 if 'singleline' in opts:
510 l = __main__.paperguru.get_linewidth()
512 if 'relative' in opts:#ugh only when is_fragment
513 body = '\\relative c { %s }' % body
522 optstring = string.join (opts, ' ')
523 optstring = re.sub ('\n', ' ', optstring)
525 %% Generated automatically by: lilypond-book.py
526 %% options are %s %%ughUGH not original options
527 \include "paper%d.ly"
528 \paper { linewidth = %f \pt; }
529 """ % (optstring, music_size, l) + body
532 def parse_options_string(s):
534 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
535 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
536 r3 = re.compile("(\w+?)((,\s*)|$)")
541 d[m.group(2)] = re.split(",\s*", m.group(3))
546 d[m.group(2)] = m.group(3)
554 error ("format of option string invalid (was `%')" % s)
557 def scan_latex_preamble(chunks):
558 # first we want to scan the \documentclass line
559 # it should be the first non-comment line
562 if chunks[idx][0] == 'ignore':
565 m = get_re ('header').match(chunks[idx][1])
567 options = re.split (',[\n \t]*', m.group(1)[1:-1])
572 paperguru.m_landscape = 1
573 m = re.match("(.*?)paper", o)
575 paperguru.m_papersize = m.group()
577 m = re.match("(\d\d)pt", o)
579 paperguru.m_fontsize = int(m.group(1))
582 while chunks[idx][0] != 'preamble-end':
583 if chunks[idx] == 'ignore':
586 m = get_re ('geometry').search(chunks[idx][1])
588 paperguru.m_use_geometry = 1
589 o = parse_options_string(m.group('options'))
591 paperguru.set_geo_option(k, o[k])
594 def scan_texi_preamble (chunks):
595 # this is not bulletproof..., it checks the first 10 chunks
596 for c in chunks[:10]:
598 for s in ('afourpaper', 'afourwide', 'letterpaper',
599 'afourlatex', 'smallbook'):
600 if string.find(c[1], "@%s" % s) != -1:
601 paperguru.m_papersize = s
603 def scan_preamble (chunks):
604 if __main__.format == 'texi':
605 scan_texi_preamble(chunks)
607 assert __main__.format == 'latex'
608 scan_latex_preamble(chunks)
611 def completize_preamble (chunks):
612 if __main__.format == 'texi':
614 pre_b = post_b = graphics_b = None
616 if chunk[0] == 'preamble-end':
618 if chunk[0] == 'input':
619 m = get_re('def-pre-re').search(chunk[1])
622 if chunk[0] == 'input':
623 m = get_re('def-post-re').search(chunk[1])
626 if chunk[0] == 'input':
627 m = get_re('usepackage-graphics').search(chunk[1])
631 while chunks[x][0] != 'preamble-end':
634 chunks.insert(x, ('input', get_output ('output-default-pre')))
636 chunks.insert(x, ('input', get_output ('output-default-post')))
638 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
643 def find_file (name):
645 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
650 for a in include_path:
652 nm = os.path.join (a, name)
654 __main__.read_files.append (nm)
659 sys.stderr.write ("Reading `%s'\n" % nm)
660 return (f.read (), nm)
662 error ("File not found `%s'\n" % name)
665 def do_ignore(match_object):
666 return [('ignore', match_object.group('code'))]
667 def do_preamble_end(match_object):
668 return [('preamble-end', match_object.group('code'))]
670 def make_verbatim(match_object):
671 return [('verbatim', match_object.group('code'))]
673 def make_verb(match_object):
674 return [('verb', match_object.group('code'))]
676 def do_include_file(m):
678 return [('input', get_output ('pagebreak'))] \
679 + read_doc_file(m.group('filename')) \
680 + [('input', get_output ('pagebreak'))]
682 def do_input_file(m):
683 return read_doc_file(m.group('filename'))
685 def make_lilypond(m):
686 if m.group('options'):
687 options = m.group('options')
690 return [('input', get_output('output-lilypond-fragment') %
691 (options, m.group('code')))]
693 def make_lilypond_file(m):
696 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
697 into a @lilypond .. @end lilypond block.
701 if m.group('options'):
702 options = m.group('options')
705 (content, nm) = find_file(m.group('filename'))
706 options = "filename=%s," % nm + options
708 return [('input', get_output('output-lilypond') %
711 def make_lilypond_block(m):
712 if m.group('options'):
713 options = get_re('option-sep').split (m.group('options'))
716 options = filter(lambda s: s != '', options)
717 return [('lilypond', m.group('code'), options)]
720 if __main__.format != 'latex':
722 if m.group('num') == 'one':
723 return [('numcols', m.group('code'), 1)]
724 if m.group('num') == 'two':
725 return [('numcols', m.group('code'), 2)]
727 def chop_chunks(chunks, re_name, func, use_match=0):
733 m = get_re (re_name).search (str)
735 newchunks.append (('input', str))
739 newchunks.append (('input', str[:m.start ('match')]))
741 newchunks.append (('input', str[:m.start (0)]))
742 #newchunks.extend(func(m))
743 # python 1.5 compatible:
744 newchunks = newchunks + func(m)
745 str = str [m.end(0):]
750 def determine_format (str):
751 if __main__.format == '':
753 latex = re.search ('\\\\document', str[:200])
754 texinfo = re.search ('@node|@setfilename', str[:200])
759 if texinfo and latex == None:
761 elif latex and texinfo == None:
764 error("error: can't determine format, please specify")
767 if __main__.paperguru == None:
768 if __main__.format == 'texi':
773 __main__.paperguru = g
776 def read_doc_file (filename):
777 """Read the input file, find verbatim chunks and do \input and \include
779 (str, path) = find_file(filename)
780 determine_format (str)
782 chunks = [('input', str)]
784 # we have to check for verbatim before doing include,
785 # because we don't want to include files that are mentioned
786 # inside a verbatim environment
787 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
788 chunks = chop_chunks(chunks, 'verb', make_verb)
789 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
791 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
792 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
796 taken_file_names = {}
797 def schedule_lilypond_block (chunk):
798 """Take the body and options from CHUNK, figure out how the
799 real .ly should look, and what should be left MAIN_STR (meant
800 for the main file). The .ly is written, and scheduled in
803 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
805 TODO has format [basename, extension, extension, ... ]
808 (type, body, opts) = chunk
809 assert type == 'lilypond'
810 file_body = compose_full_body (body, opts)
811 basename = 'lily-' + `abs(hash (file_body))`
813 m = re.search ('filename="(.*?)"', o)
815 basename = m.group (1)
816 if not taken_file_names.has_key(basename):
817 taken_file_names[basename] = 0
819 taken_file_names[basename] = taken_file_names[basename] + 1
820 basename = basename + "-%i" % taken_file_names[basename]
822 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
823 needed_filetypes = ['tex']
826 needed_filetypes.append('eps')
827 needed_filetypes.append('png')
828 if 'eps' in opts and not ('eps' in needed_filetypes):
829 needed_filetypes.append('eps')
830 pathbase = os.path.join (g_outdir, basename)
831 def f(base, ext1, ext2):
832 a = os.path.isfile(base + ext2)
833 if (os.path.isfile(base + ext1) and
834 os.path.isfile(base + ext2) and
835 os.stat(base+ext1)[stat.ST_MTIME] >
836 os.stat(base+ext2)[stat.ST_MTIME]) or \
837 not os.path.isfile(base + ext2):
840 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
842 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
844 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
848 if 'printfilename' in opts:
850 m= re.match ("filename=(.*)", o)
852 newbody = newbody + get_output ("output-filename") % m.group(1)
856 if 'verbatim' in opts:
857 newbody = output_verbatim (body)
860 m = re.search ('intertext="(.*?)"', o)
862 newbody = newbody + m.group (1) + "\n\n"
863 if format == 'latex':
868 else: # format == 'texi'
870 newbody = newbody + get_output (s) % {'fn': basename }
871 return ('lilypond', newbody, opts, todo, basename)
873 def process_lilypond_blocks(outname, chunks):#ugh rename
875 # Count sections/chapters.
877 if c[0] == 'lilypond':
878 c = schedule_lilypond_block (c)
879 elif c[0] == 'numcols':
880 paperguru.m_num_cols = c[2]
885 def find_eps_dims (match):
886 "Fill in dimensions of EPS files."
889 dims = bounding_box_dimensions (fn)
891 fn = os.path.join(g_outdir, fn)
893 return '%ipt' % dims[0]
897 sys.stderr.write ("invoking `%s'\n" % cmd)
900 error ('Error command exited with value %d\n' % st)
903 def compile_all_files (chunks):
910 if c[0] <> 'lilypond':
919 if base + '.ly' not in tex:
920 tex.append (base + '.ly')
921 elif e == 'png' and g_do_pictures:
927 # fixme: be sys-independent.
929 if g_outdir and x[0] <> '/' :
930 x = os.path.join (g_here_dir, x)
933 incs = map (incl_opt, include_path)
934 lilyopts = string.join (incs, ' ' )
936 lilyopts = lilyopts + ' --dependencies '
938 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
939 texfiles = string.join (tex, ' ')
940 system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
943 # Ugh, fixing up dependencies for .tex generation
946 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
948 text=open (i).read ()
949 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
950 open (i, 'w').write (text)
953 system(r"tex '\nonstopmode \input %s'" % e)
954 system(r"dvips -E -o %s %s" % (e + '.eps', e))
956 cmd = r"""gs -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
957 cmd = cmd % (g + '.eps', g + '.png')
962 def update_file (body, name):
964 write the body if it has changed
975 f = open (name , 'w')
982 def getopt_args (opts):
983 "Construct arguments (LONG, SHORT) for getopt from list of options."
998 def option_help_str (o):
999 "Transform one option description (4-tuple ) into neatly formatted string"
1017 return ' ' + sh + sep + long + arg
1020 def options_help_str (opts):
1021 "Convert a list of options into a neatly formatted string"
1027 s = option_help_str (o)
1028 strs.append ((s, o[3]))
1034 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1038 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1039 Generate hybrid LaTeX input from Latex + lilypond
1042 sys.stdout.write (options_help_str (option_definitions))
1043 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1047 Report bugs to bug-gnu-music@gnu.org.
1049 Written by Tom Cato Amundsen <tca@gnu.org> and
1050 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1056 def write_deps (fn, target, chunks):
1058 sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1059 f = open (os.path.join(g_outdir, fn), 'w')
1060 f.write ('%s%s: ' % (g_dep_prefix, target))
1061 for d in read_files:
1065 if c[0] == 'lilypond':
1066 (type, body, opts, todo, basename) = c;
1067 basenames.append (basename)
1070 d=g_outdir + '/' + d
1072 #if not os.isfile (d): # thinko?
1073 if not re.search ('/', d):
1074 d = g_dep_prefix + d
1075 f.write ('%s.tex ' % d)
1077 #if len (basenames):
1078 # for d in basenames:
1079 # f.write ('%s.ly ' % d)
1080 # f.write (' : %s' % target)
1086 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1088 def print_version ():
1090 sys.stdout.write (r"""Copyright 1998--1999
1091 Distributed under terms of the GNU General Public License. It comes with
1096 def check_texidoc (chunks):
1099 if c[0] == 'lilypond':
1100 (type, body, opts, todo, basename) = c;
1101 pathbase = os.path.join (g_outdir, basename)
1102 if os.path.isfile (pathbase + '.texidoc'):
1103 body = '\n@include %s.texidoc\n' % basename + body
1104 c = (type, body, opts, todo, basename)
1108 def fix_epswidth (chunks):
1111 if c[0] == 'lilypond' and 'eps' in c[2]:
1112 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", find_eps_dims, c[1])
1113 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1115 newchunks.append (c)
1120 def do_file(input_filename):
1124 my_outname = outname
1126 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1127 my_depname = my_outname + '.dep'
1129 chunks = read_doc_file(input_filename)
1130 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1131 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1132 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1133 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1134 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1135 chunks = chop_chunks(chunks, 'numcols', do_columns)
1137 #for c in chunks: print "c:", c;
1139 scan_preamble(chunks)
1140 chunks = process_lilypond_blocks(my_outname, chunks)
1142 foutn = os.path.join (g_outdir, my_outname + '.' + format)
1145 if __main__.g_run_lilypond:
1146 compile_all_files (chunks)
1147 chunks = fix_epswidth (chunks)
1149 if __main__.format == 'texi':
1150 chunks = check_texidoc (chunks)
1153 chunks = completize_preamble (chunks)
1154 sys.stderr.write ("Writing `%s'\n" % foutn)
1155 fout = open (foutn, 'w')
1162 write_deps (my_depname, foutn, chunks)
1167 (sh, long) = getopt_args (__main__.option_definitions)
1168 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1169 except getopt.error, msg:
1170 sys.stderr.write("error: %s" % msg)
1178 if o == '--include' or o == '-I':
1179 include_path.append (a)
1180 elif o == '--version' or o == '-v':
1183 elif o == '--format' or o == '-f':
1185 elif o == '--outname' or o == '-o':
1188 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1191 elif o == '--help' or o == '-h':
1193 elif o == '--no-lily' or o == '-n':
1194 __main__.g_run_lilypond = 0
1195 elif o == '--dependencies' or o == '-M':
1197 elif o == '--default-music-fontsize':
1198 default_music_fontsize = string.atoi (a)
1199 elif o == '--default-lilypond-fontsize':
1200 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1201 default_music_fontsize = string.atoi (a)
1202 elif o == '--force-music-fontsize':
1203 g_force_lilypond_fontsize = string.atoi(a)
1204 elif o == '--force-lilypond-fontsize':
1205 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1206 g_force_lilypond_fontsize = string.atoi(a)
1207 elif o == '--dep-prefix':
1209 elif o == '--no-pictures':
1211 elif o == '--read-lys':
1213 elif o == '--outdir':
1218 if os.path.isfile(g_outdir):
1219 error ("outdir is a file: %s" % g_outdir)
1220 if not os.path.exists(g_outdir):
1222 for input_filename in files:
1223 do_file(input_filename)
1226 # Petr, ik zou willen dat ik iets zinvoller deed,
1227 # maar wat ik kan ik doen, het verandert toch niets?