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 = (a == None, b == None, c == None)
217 if geo_opts == (1, 1, 1):
218 if self.m_geo_textwidth:
219 return self.m_geo_textwidth
220 w = self.get_paperwidth() * 0.8
222 elif geo_opts == (0, 1, 1):
223 if self.m_geo_textwidth:
224 return self.m_geo_textwidth
225 return self.f1(self.m_geo_lmargin, mp)
226 elif geo_opts == (1, 1, 0):
227 if self.m_geo_textwidth:
228 return self.m_geo_textwidth
229 return self.f1(self.m_geo_rmargin, mp)
231 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
232 if self.m_geo_textwidth:
233 return self.m_geo_textwidth
234 return self.m_geo_width - mp
235 elif geo_opts in ((0, 1, 0), (0, 0, 0)):
236 w = self.get_paperwidth() \
237 - self.m_geo_lmargin - self.m_geo_rmargin - mp
241 raise "Never do this!"
243 tmp = self.get_paperwidth() - m * 2 - mp
248 tmp = self.get_paperwidth() - self.m_geo_lmargin \
256 self.m_papersize = 'letterpaper'
258 def get_linewidth(self):
259 return texi_linewidths[self.m_papersize][self.m_fontsize]
265 def em2pt(x, fontsize = 10):
266 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
267 def ex2pt(x, fontsize = 10):
268 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
273 dimension_conversion_dict ={
283 # indices are no. of columns, papersize, fontsize
284 # Why can't this be calculated?
286 'a4paper':{10: 345, 11: 360, 12: 390},
287 'a4paper-landscape': {10: 598, 11: 596, 12:592},
288 'a5paper':{10: 276, 11: 276, 12: 276},
289 'b5paper':{10: 345, 11: 356, 12: 356},
290 'letterpaper':{10: 345, 11: 360, 12: 390},
291 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
292 'legalpaper': {10: 345, 11: 360, 12: 390},
293 'executivepaper':{10: 345, 11: 360, 12: 379}}
296 'afourpaper': {12: mm2pt(160)},
297 'afourwide': {12: in2pt(6.5)},
298 'afourlatex': {12: mm2pt(150)},
299 'smallbook': {12: in2pt(5)},
300 'letterpaper': {12: in2pt(6)}}
302 option_definitions = [
303 ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'),
304 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
305 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
306 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
307 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
308 ('DIR', 'I', 'include', 'include path'),
309 ('', 'M', 'dependencies', 'write dependencies'),
310 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
311 ('', 'n', 'no-lily', 'don\'t run lilypond'),
312 ('', '', 'no-pictures', "don\'t generate pictures"),
313 ('', '', 'read-lys', "don't write ly files."),
314 ('FILE', 'o', 'outname', 'filename main output file'),
315 ('FILE', '', 'outdir', "where to place generated files"),
316 ('', 'v', 'version', 'print version information' ),
317 ('', 'h', 'help', 'print help'),
320 # format specific strings, ie. regex-es for input, and % strings for output
323 'output-lilypond-fragment' : r"""\begin[eps,singleline,%s]{lilypond}
330 'output-lilypond':r"""\begin[%s]{lilypond}
333 'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
334 'output-default-post': "\\def\postLilypondExample{}\n",
335 'output-default-pre': "\\def\preLilypondExample{}\n",
336 'usepackage-graphics': '\\usepackage{graphics}\n',
337 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
338 'output-tex': '\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n',
339 'pagebreak': r'\pagebreak',
341 'texi' : {'output-lilypond': """@lilypond[%s]
345 'output-lilypond-fragment': """@lilypond[%s]
346 \context Staff\context Voice{ %s }
349 'output-verbatim': r"""@example
354 # do some tweaking: @ is needed in some ps stuff.
355 # override EndLilyPondOutput, since @tex is done
356 # in a sandbox, you can't do \input lilyponddefs at the
357 # top of the document.
359 # should also support fragment in
365 \def\EndLilyPondOutput{}
377 def output_verbatim (body):
378 if __main__.format == 'texi':
379 body = re.sub ('([@{}])', '@\\1', body)
380 return get_output ('output-verbatim') % body
384 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
385 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
386 'option-sep' : ', *',
387 'header': r"\\documentclass\s*(\[.*?\])?",
388 'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
389 'preamble-end': r'(?P<code>\\begin{document})',
390 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
391 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
392 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile(\[(?P<options>.*?)\])?\{(?P<filename>.+)})',
393 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
394 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin(\[(?P<options>.*?)\])?{lilypond}(?P<code>.*?)\\end{lilypond})",
395 'def-post-re': r"\\def\\postLilypondExample",
396 'def-pre-re': r"\\def\\preLilypondExample",
397 'usepackage-graphics': r"\usepackage{graphics}",
398 'intertext': r',?\s*intertext=\".*?\"',
399 'multiline-comment': no_match,
400 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
401 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
405 # why do we have distinction between @mbinclude and @include?
407 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
410 'preamble-end': no_match,
411 'landscape': no_match,
412 'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
413 'verb': r"""(?P<code>@code{.*?})""",
414 'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
415 'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
416 'lilypond-block': r"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
417 'option-sep' : ', *',
418 'intertext': r',?\s*intertext=\".*?\"',
419 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
420 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
426 for r in re_dict.keys ():
429 for k in olddict.keys ():
430 newdict[k] = re.compile (olddict[k])
444 def get_output (name):
445 return output_dict[format][name]
448 return re_dict[format][name]
450 def bounding_box_dimensions(fname):
452 fname = os.path.join(g_outdir, fname)
456 error ("Error opening `%s'" % fname)
458 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
460 return (int(s.group(3))-int(s.group(1)),
461 int(s.group(4))-int(s.group(2)))
467 sys.stderr.write (str + "\n Exiting ... \n\n")
471 def compose_full_body (body, opts):
472 """Construct the lilypond code to send to Lilypond.
473 Add stuff to BODY using OPTS as options."""
474 music_size = default_music_fontsize
475 latex_size = default_text_fontsize
477 if g_force_lilypond_fontsize:
478 music_size = g_force_lilypond_fontsize
480 m = re.match ('([0-9]+)pt', o)
482 music_size = string.atoi(m.group (1))
484 m = re.match ('latexfontsize=([0-9]+)pt', o)
486 latex_size = string.atoi (m.group (1))
488 if re.search ('\\\\score', body):
492 if 'fragment' in opts:
494 if 'nonfragment' in opts:
497 if is_fragment and not 'multiline' in opts:
498 opts.append('singleline')
499 if 'singleline' in opts:
502 l = __main__.paperguru.get_linewidth()
504 if 'relative' in opts:#ugh only when is_fragment
505 body = '\\relative c { %s }' % body
514 optstring = string.join (opts, ' ')
515 optstring = re.sub ('\n', ' ', optstring)
517 %% Generated by lilypond-book.py; options are %s %%ughUGH not original options
518 \include "paper%d.ly"
519 \paper { linewidth = %f \pt; }
520 """ % (optstring, music_size, l) + body
523 def parse_options_string(s):
525 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
526 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
527 r3 = re.compile("(\w+?)((,\s*)|$)")
532 d[m.group(2)] = re.split(",\s*", m.group(3))
537 d[m.group(2)] = m.group(3)
545 error ("format of option string invalid (was `%')" % s)
548 def scan_latex_preamble(chunks):
549 # first we want to scan the \documentclass line
550 # it should be the first non-comment line
553 if chunks[idx][0] == 'ignore':
556 m = get_re ('header').match(chunks[idx][1])
557 options = re.split (',[\n \t]*', m.group(1)[1:-1])
560 paperguru.m_landscape = 1
561 m = re.match("(.*?)paper", o)
563 paperguru.m_papersize = m.group()
565 m = re.match("(\d\d)pt", o)
567 paperguru.m_fontsize = int(m.group(1))
570 while chunks[idx][0] != 'preamble-end':
571 if chunks[idx] == 'ignore':
574 m = get_re ('geometry').search(chunks[idx][1])
576 paperguru.m_use_geometry = 1
577 o = parse_options_string(m.group('options'))
579 paperguru.set_geo_option(k, o[k])
582 def scan_texi_preamble (chunks):
583 # this is not bulletproof..., it checks the first 10 chunks
584 for c in chunks[:10]:
586 for s in ('afourpaper', 'afourwide', 'letterpaper',
587 'afourlatex', 'smallbook'):
588 if string.find(c[1], "@%s" % s) != -1:
589 paperguru.m_papersize = s
591 def scan_preamble (chunks):
592 if __main__.format == 'texi':
593 scan_texi_preamble(chunks)
595 assert __main__.format == 'latex'
596 scan_latex_preamble(chunks)
599 def completize_preamble (chunks):
600 if __main__.format == 'texi':
602 pre_b = post_b = graphics_b = None
604 if chunk[0] == 'preamble-end':
606 if chunk[0] == 'input':
607 m = get_re('def-pre-re').search(chunk[1])
610 if chunk[0] == 'input':
611 m = get_re('def-post-re').search(chunk[1])
614 if chunk[0] == 'input':
615 m = get_re('usepackage-graphics').search(chunk[1])
619 while chunks[x][0] != 'preamble-end':
622 chunks.insert(x, ('input', get_output ('output-default-pre')))
624 chunks.insert(x, ('input', get_output ('output-default-post')))
626 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
631 def find_file (name):
633 Search the include path for NAME. If found, return the contents of teh file.
636 for a in include_path:
638 nm = os.path.join (a, name)
640 __main__.read_files.append (nm)
645 sys.stderr.write ("Reading `%s'\n" % nm)
648 error ("File not found `%s'\n" % name)
651 def do_ignore(match_object):
652 return [('ignore', match_object.group('code'))]
653 def do_preamble_end(match_object):
654 return [('preamble-end', match_object.group('code'))]
656 def make_verbatim(match_object):
657 return [('verbatim', match_object.group('code'))]
659 def make_verb(match_object):
660 return [('verb', match_object.group('code'))]
662 def do_include_file(m):
664 return [('input', get_output ('pagebreak'))] \
665 + read_doc_file(m.group('filename')) \
666 + [('input', get_output ('pagebreak'))]
668 def do_input_file(m):
669 return read_doc_file(m.group('filename'))
671 def make_lilypond(m):
672 if m.group('options'):
673 options = m.group('options')
676 return [('input', get_output('output-lilypond-fragment') %
677 (options, m.group('code')))]
679 def make_lilypond_file(m):
680 if m.group('options'):
681 options = m.group('options')
684 return [('input', get_output('output-lilypond') %
685 (options, find_file(m.group('filename'))))]
687 def make_lilypond_block(m):
688 if m.group('options'):
689 options = get_re('option-sep').split (m.group('options'))
692 options = filter(lambda s: s != '', options)
693 return [('lilypond', m.group('code'), options)]
696 if __main__.format != 'latex':
698 if m.group('num') == 'one':
699 return [('numcols', m.group('code'), 1)]
700 if m.group('num') == 'two':
701 return [('numcols', m.group('code'), 2)]
703 def chop_chunks(chunks, re_name, func, use_match=0):
709 m = get_re (re_name).search (str)
711 newchunks.append (('input', str))
715 newchunks.append (('input', str[:m.start ('match')]))
717 newchunks.append (('input', str[:m.start (0)]))
718 #newchunks.extend(func(m))
719 # python 1.5 compatible:
720 newchunks = newchunks + func(m)
721 str = str [m.end(0):]
726 def determine_format (str):
727 if __main__.format == '':
729 latex = re.search ('\\\\document', str[:200])
730 texinfo = re.search ('@node|@setfilename', str[:200])
735 if texinfo and latex == None:
737 elif latex and texinfo == None:
740 error("error: can't determine format, please specify")
743 if __main__.paperguru == None:
744 if __main__.format == 'texi':
749 __main__.paperguru = g
752 def read_doc_file (filename):
753 """Read the input file, find verbatim chunks and do \input and \include
755 str = find_file(filename)
756 determine_format (str)
758 chunks = [('input', str)]
760 # we have to check for verbatim before doing include,
761 # because we don't want to include files that are mentioned
762 # inside a verbatim environment
763 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
764 chunks = chop_chunks(chunks, 'verb', make_verb)
765 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
767 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
768 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
772 taken_file_names = {}
773 def schedule_lilypond_block (chunk):
774 """Take the body and options from CHUNK, figure out how the
775 real .ly should look, and what should be left MAIN_STR (meant
776 for the main file). The .ly is written, and scheduled in
779 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
781 TODO has format [basename, extension, extension, ... ]
784 (type, body, opts) = chunk
785 assert type == 'lilypond'
786 file_body = compose_full_body (body, opts)
787 basename = `abs(hash (file_body))`
789 m = re.search ('filename="(.*?)"', o)
791 basename = m.group (1)
792 if not taken_file_names.has_key(basename):
793 taken_file_names[basename] = 0
795 taken_file_names[basename] = taken_file_names[basename] + 1
796 basename = basename + "-%i" % taken_file_names[basename]
798 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
799 needed_filetypes = ['tex']
802 needed_filetypes.append('eps')
803 needed_filetypes.append('png')
804 if 'eps' in opts and not ('eps' in needed_filetypes):
805 needed_filetypes.append('eps')
806 pathbase = os.path.join (g_outdir, basename)
807 def f(base, ext1, ext2):
808 a = os.path.isfile(base + ext2)
809 if (os.path.isfile(base + ext1) and
810 os.path.isfile(base + ext2) and
811 os.stat(base+ext1)[stat.ST_MTIME] >
812 os.stat(base+ext2)[stat.ST_MTIME]) or \
813 not os.path.isfile(base + ext2):
816 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
818 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
820 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
823 if 'verbatim' in opts:
824 newbody = output_verbatim (body)
827 m = re.search ('intertext="(.*?)"', o)
829 newbody = newbody + m.group (1) + "\n\n"
830 if format == 'latex':
835 else: # format == 'texi'
837 newbody = newbody + get_output (s) % {'fn': basename }
838 return ('lilypond', newbody, opts, todo, basename)
840 def process_lilypond_blocks(outname, chunks):#ugh rename
842 # Count sections/chapters.
844 if c[0] == 'lilypond':
845 c = schedule_lilypond_block (c)
846 elif c[0] == 'numcols':
847 paperguru.m_num_cols = c[2]
852 def find_eps_dims (match):
853 "Fill in dimensions of EPS files."
856 dims = bounding_box_dimensions (fn)
858 fn = os.path.join(g_outdir, fn)
860 return '%ipt' % dims[0]
864 sys.stderr.write ("invoking `%s'\n" % cmd)
867 error ('Error command exited with value %d\n' % st)
870 def compile_all_files (chunks):
876 if c[0] <> 'lilypond':
885 if base + '.ly' not in tex:
886 tex.append (base + '.ly')
887 elif e == 'png' and g_do_pictures:
893 # fixme: be sys-independent.
895 if g_outdir and x[0] <> '/' :
896 x = os.path.join (g_here_dir, x)
899 incs = map (incl_opt, include_path)
900 lilyopts = string.join (incs, ' ' )
901 texfiles = string.join (tex, ' ')
902 system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
904 system(r"tex '\nonstopmode \input %s'" % e)
905 system(r"dvips -E -o %s %s" % (e + '.eps', e))
907 cmd = r"""gs -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
908 cmd = cmd % (g + '.eps', g + '.png')
914 def update_file (body, name):
916 write the body if it has changed
927 f = open (name , 'w')
934 def getopt_args (opts):
935 "Construct arguments (LONG, SHORT) for getopt from list of options."
950 def option_help_str (o):
951 "Transform one option description (4-tuple ) into neatly formatted string"
969 return ' ' + sh + sep + long + arg
972 def options_help_str (opts):
973 "Convert a list of options into a neatly formatted string"
979 s = option_help_str (o)
980 strs.append ((s, o[3]))
986 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
990 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
991 Generate hybrid LaTeX input from Latex + lilypond
994 sys.stdout.write (options_help_str (option_definitions))
995 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
999 Report bugs to bug-gnu-music@gnu.org.
1001 Written by Tom Cato Amundsen <tca@gnu.org> and
1002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1008 def write_deps (fn, target):
1009 sys.stdout.write('writing `%s\'\n' % os.path.join(g_outdir, fn))
1010 f = open (os.path.join(g_outdir, fn), 'w')
1011 f.write ('%s%s: ' % (g_dep_prefix, target))
1012 for d in __main__.read_files:
1016 __main__.read_files = []
1019 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1021 def print_version ():
1023 sys.stdout.write (r"""Copyright 1998--1999
1024 Distributed under terms of the GNU General Public License. It comes with
1029 def check_texidoc (chunks):
1032 if c[0] == 'lilypond':
1033 (type, body, opts, todo, basename) = c;
1034 pathbase = os.path.join (g_outdir, basename)
1035 if os.path.isfile (pathbase + '.texidoc'):
1036 body = '\n@include %s.texidoc' % basename + body
1037 c = (type, body, opts, todo, basename)
1041 def fix_epswidth (chunks):
1044 if c[0] == 'lilypond' and 'eps' in c[2]:
1045 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", find_eps_dims, c[1])
1046 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1048 newchunks.append (c)
1052 def do_file(input_filename):
1055 my_outname = outname
1057 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1058 my_depname = my_outname + '.dep'
1060 chunks = read_doc_file(input_filename)
1061 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1062 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1063 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1064 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1065 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1066 chunks = chop_chunks(chunks, 'numcols', do_columns)
1068 #for c in chunks: print "c:", c;
1070 scan_preamble(chunks)
1071 chunks = process_lilypond_blocks(my_outname, chunks)
1074 if __main__.g_run_lilypond:
1075 compile_all_files (chunks)
1076 chunks = fix_epswidth (chunks)
1078 if __main__.format == 'texi':
1079 chunks = check_texidoc (chunks)
1082 chunks = completize_preamble (chunks)
1083 foutn = os.path.join(g_outdir, my_outname + '.' + format)
1084 sys.stderr.write ("Writing `%s'\n" % foutn)
1085 fout = open (foutn, 'w')
1092 write_deps (my_depname, foutn)
1097 (sh, long) = getopt_args (__main__.option_definitions)
1098 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1099 except getopt.error, msg:
1100 sys.stderr.write("error: %s" % msg)
1108 if o == '--include' or o == '-I':
1109 include_path.append (a)
1110 elif o == '--version' or o == '-v':
1113 elif o == '--format' or o == '-f':
1115 elif o == '--outname' or o == '-o':
1118 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1121 elif o == '--help' or o == '-h':
1123 elif o == '--no-lily' or o == '-n':
1124 __main__.g_run_lilypond = 0
1125 elif o == '--dependencies' or o == '-M':
1127 elif o == '--default-music-fontsize':
1128 default_music_fontsize = string.atoi (a)
1129 elif o == '--default-lilypond-fontsize':
1130 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1131 default_music_fontsize = string.atoi (a)
1132 elif o == '--force-music-fontsize':
1133 g_force_lilypond_fontsize = string.atoi(a)
1134 elif o == '--force-lilypond-fontsize':
1135 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1136 g_force_lilypond_fontsize = string.atoi(a)
1137 elif o == '--dep-prefix':
1139 elif o == '--no-pictures':
1141 elif o == '--read-lys':
1143 elif o == '--outdir':
1148 if os.path.isfile(g_outdir):
1149 error ("outdir is a file: %s" % g_outdir)
1150 if not os.path.exists(g_outdir):
1152 for input_filename in files:
1153 do_file(input_filename)
1156 # Petr, ik zou willen dat ik iets zinvoller deed,
1157 # maar wat ik kan ik doen, het verandert toch niets?