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
16 # TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips.
19 # This is was the idea for handling of comments:
20 # Multiline comments, @ignore .. @end ignore is scanned for
21 # in read_doc_file, and the chunks are marked as 'ignore', so
22 # lilypond-book will not touch them any more. The content of the
23 # chunks are written to the output file. Also 'include' and 'input'
24 # regex has to check if they are commented out.
26 # Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
27 # These three regex's has to check if they are on a commented line,
28 # % for latex, @c for texinfo.
30 # Then lines that are commented out with % (latex) and @c (Texinfo)
31 # are put into chunks marked 'ignore'. This cannot be done before
32 # searching for the lilypond-blocks because % is also the comment character
35 # The the rest of the rexeces are searched for. They don't have to test
36 # if they are on a commented out line.
50 program_version = '@TOPLEVEL_VERSION@'
51 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
52 program_version = '1.5.18'
55 # Try to cater for bad installations of LilyPond, that have
56 # broken TeX setup. Just hope this doesn't hurt good TeX
57 # setups. Maybe we should check if kpsewhich can find
58 # feta16.{afm,mf,tex,tfm}, and only set env upon failure.
62 'MFINPUTS' : datadir + '/mf:',
63 'TEXINPUTS': datadir + '/tex:' + datadir + '/ps:.:',
64 'TFMFONTS' : datadir + '/tfm:',
65 'GS_FONTPATH' : datadir + '/afm:' + datadir + '/pfa',
66 'GS_LIB' : datadir + '/ps',
69 def setup_environment ():
70 for key in environment.keys ():
71 val = environment[key]
72 if os.environ.has_key (key):
73 val = val + os.pathsep + os.environ[key]
78 include_path = [os.getcwd()]
81 # g_ is for global (?)
83 g_here_dir = os.getcwd ()
86 g_force_lilypond_fontsize = 0
94 default_music_fontsize = 16
95 default_text_fontsize = 12
98 # this code is ugly. It should be cleaned
102 # the dimensions are from geometry.sty
103 'a0paper': (mm2pt(841), mm2pt(1189)),
104 'a1paper': (mm2pt(595), mm2pt(841)),
105 'a2paper': (mm2pt(420), mm2pt(595)),
106 'a3paper': (mm2pt(297), mm2pt(420)),
107 'a4paper': (mm2pt(210), mm2pt(297)),
108 'a5paper': (mm2pt(149), mm2pt(210)),
109 'b0paper': (mm2pt(1000), mm2pt(1414)),
110 'b1paper': (mm2pt(707), mm2pt(1000)),
111 'b2paper': (mm2pt(500), mm2pt(707)),
112 'b3paper': (mm2pt(353), mm2pt(500)),
113 'b4paper': (mm2pt(250), mm2pt(353)),
114 'b5paper': (mm2pt(176), mm2pt(250)),
115 'letterpaper': (in2pt(8.5), in2pt(11)),
116 'legalpaper': (in2pt(8.5), in2pt(14)),
117 'executivepaper': (in2pt(7.25), in2pt(10.5))}
118 self.m_use_geometry = None
119 self.m_papersize = 'letterpaper'
123 self.m_geo_landscape = 0
124 self.m_geo_width = None
125 self.m_geo_textwidth = None
126 self.m_geo_lmargin = None
127 self.m_geo_rmargin = None
128 self.m_geo_includemp = None
129 self.m_geo_marginparwidth = {10: 57, 11: 50, 12: 35}
130 self.m_geo_marginparsep = {10: 11, 11: 10, 12: 10}
131 self.m_geo_x_marginparwidth = None
132 self.m_geo_x_marginparsep = None
134 def set_geo_option(self, name, value):
136 if type(value) == type(""):
137 m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
140 num = string.atof(m.group (1))
141 conv = dimension_conversion_dict[m.group(2)]
145 if name == 'body' or name == 'text':
146 if type(value) == type(""):
147 self.m_geo_textwidth = value
149 self.m_geo_textwidth = value[0]
151 elif name == 'portrait':
152 self.m_geo_landscape = 0
153 elif name == 'reversemp' or name == 'reversemarginpar':
154 if self.m_geo_includemp == None:
155 self.m_geo_includemp = 1
156 elif name == 'marginparwidth' or name == 'marginpar':
157 self.m_geo_x_marginparwidth = value
158 self.m_geo_includemp = 1
159 elif name == 'marginparsep':
160 self.m_geo_x_marginparsep = value
161 self.m_geo_includemp = 1
162 elif name == 'scale':
163 if type(value) == type(""):
164 self.m_geo_width = self.get_paperwidth() * float(value)
166 self.m_geo_width = self.get_paperwidth() * float(value[0])
167 elif name == 'hscale':
168 self.m_geo_width = self.get_paperwidth() * float(value)
169 elif name == 'left' or name == 'lmargin':
170 self.m_geo_lmargin = value
171 elif name == 'right' or name == 'rmargin':
172 self.m_geo_rmargin = value
173 elif name == 'hdivide' or name == 'divide':
174 if value[0] not in ('*', ''):
175 self.m_geo_lmargin = value[0]
176 if value[1] not in ('*', ''):
177 self.m_geo_width = value[1]
178 if value[2] not in ('*', ''):
179 self.m_geo_rmargin = value[2]
180 elif name == 'hmargin':
181 if type(value) == type(""):
182 self.m_geo_lmargin = value
183 self.m_geo_rmargin = value
185 self.m_geo_lmargin = value[0]
186 self.m_geo_rmargin = value[1]
187 elif name == 'margin':#ugh there is a bug about this option in
188 # the geometry documentation
189 if type(value) == type(""):
190 self.m_geo_lmargin = value
191 self.m_geo_rmargin = value
193 self.m_geo_lmargin = value[0]
194 self.m_geo_rmargin = value[0]
195 elif name == 'total':
196 if type(value) == type(""):
197 self.m_geo_width = value
199 self.m_geo_width = value[0]
200 elif name == 'width' or name == 'totalwidth':
201 self.m_geo_width = value
202 elif name == 'paper' or name == 'papername':
203 self.m_papersize = value
204 elif name[-5:] == 'paper':
205 self.m_papersize = name
208 # what is _set_dimen ?? /MB
209 #self._set_dimen('m_geo_'+name, value)
210 def __setattr__(self, name, value):
211 if type(value) == type("") and \
212 dimension_conversion_dict.has_key (value[-2:]):
213 f = dimension_conversion_dict[value[-2:]]
214 self.__dict__[name] = f(float(value[:-2]))
216 self.__dict__[name] = value
219 s = "LatexPaper:\n-----------"
220 for v in self.__dict__.keys():
222 s = s + str (v) + ' ' + str (self.__dict__[v])
223 s = s + "-----------"
226 def get_linewidth(self):
227 w = self._calc_linewidth()
228 if self.m_num_cols == 2:
232 def get_paperwidth(self):
233 #if self.m_use_geometry:
234 return self.m_paperdef[self.m_papersize][self.m_landscape or self.m_geo_landscape]
235 #return self.m_paperdef[self.m_papersize][self.m_landscape]
237 def _calc_linewidth(self):
238 # since geometry sometimes ignores 'includemp', this is
239 # more complicated than it should be
241 if self.m_geo_includemp:
242 if self.m_geo_x_marginparsep is not None:
243 mp = mp + self.m_geo_x_marginparsep
245 mp = mp + self.m_geo_marginparsep[self.m_fontsize]
246 if self.m_geo_x_marginparwidth is not None:
247 mp = mp + self.m_geo_x_marginparwidth
249 mp = mp + self.m_geo_marginparwidth[self.m_fontsize]
251 #ugh test if this is necessary
255 if not self.m_use_geometry:
256 return latex_linewidths[self.m_papersize][self.m_fontsize]
258 geo_opts = (self.m_geo_lmargin == None,
259 self.m_geo_width == None,
260 self.m_geo_rmargin == None)
262 if geo_opts == (1, 1, 1):
263 if self.m_geo_textwidth:
264 return self.m_geo_textwidth
265 w = self.get_paperwidth() * 0.8
267 elif geo_opts == (0, 1, 1):
268 if self.m_geo_textwidth:
269 return self.m_geo_textwidth
270 return self.f1(self.m_geo_lmargin, mp)
271 elif geo_opts == (1, 1, 0):
272 if self.m_geo_textwidth:
273 return self.m_geo_textwidth
274 return self.f1(self.m_geo_rmargin, mp)
276 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
277 if self.m_geo_textwidth:
278 return self.m_geo_textwidth
279 return self.m_geo_width - mp
280 elif geo_opts in ((0, 1, 0), (0, 0, 0)):
281 w = self.get_paperwidth() \
282 - self.m_geo_lmargin - self.m_geo_rmargin - mp
286 raise "Never do this!"
288 tmp = self.get_paperwidth() - m * 2 - mp
293 tmp = self.get_paperwidth() - self.m_geo_lmargin \
301 self.m_papersize = 'letterpaper'
303 def get_linewidth(self):
304 return texi_linewidths[self.m_papersize][self.m_fontsize]
310 def em2pt(x, fontsize = 10):
311 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
312 def ex2pt(x, fontsize = 10):
313 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
318 dimension_conversion_dict ={
320 'cm': lambda x: mm2pt(10*x),
329 # indices are no. of columns, papersize, fontsize
330 # Why can't this be calculated?
332 'a4paper':{10: 345, 11: 360, 12: 390},
333 'a4paper-landscape': {10: 598, 11: 596, 12:592},
334 'a5paper':{10: 276, 11: 276, 12: 276},
335 'b5paper':{10: 345, 11: 356, 12: 356},
336 'letterpaper':{10: 345, 11: 360, 12: 390},
337 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
338 'legalpaper': {10: 345, 11: 360, 12: 390},
339 'executivepaper':{10: 345, 11: 360, 12: 379}}
342 'afourpaper': {12: mm2pt(160)},
343 'afourwide': {12: in2pt(6.5)},
344 'afourlatex': {12: mm2pt(150)},
345 'smallbook': {12: in2pt(5)},
346 'letterpaper': {12: in2pt(6)}}
348 option_definitions = [
349 ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'),
350 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
351 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
352 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
353 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
354 ('DIR', 'I', 'include', 'include path'),
355 ('', 'M', 'dependencies', 'write dependencies'),
356 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
357 ('', 'n', 'no-lily', 'don\'t run lilypond'),
358 ('', '', 'no-pictures', "don\'t generate pictures"),
359 ('', '', 'read-lys', "don't write ly files."),
360 ('FILE', 'o', 'outname', 'filename main output file'),
361 ('FILE', '', 'outdir', "where to place generated files"),
362 ('', 'v', 'version', 'print version information' ),
363 ('', 'h', 'help', 'print help'),
366 # format specific strings, ie. regex-es for input, and % strings for output
369 'output-lilypond-fragment' : r"""\begin[eps,singleline,%s]{lilypond}
376 'output-filename' : r'''
379 'output-lilypond': r"""\begin[%s]{lilypond}
382 'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
383 'output-default-post': "\\def\postLilypondExample{}\n",
384 'output-default-pre': "\\def\preLilypondExample{}\n",
385 'usepackage-graphics': '\\usepackage{graphics}\n',
386 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
387 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
388 'pagebreak': r'\pagebreak',
390 'texi' : {'output-lilypond': """@lilypond[%s]
394 'output-filename' : r'''
397 'output-lilypond-fragment': """@lilypond[%s]
398 \context Staff\context Voice{ %s }
401 'output-verbatim': r"""@example
406 # do some tweaking: @ is needed in some ps stuff.
407 # override EndLilyPondOutput, since @tex is done
408 # in a sandbox, you can't do \input lilyponddefs at the
409 # top of the document.
411 # should also support fragment in
417 \def\EndLilyPondOutput{}
423 <a href="%(fn)s.png">
424 <img border=0 src="%(fn)s.png" alt="[picture of music]">
431 def output_verbatim (body):
432 if __main__.format == 'texi':
433 body = re.sub ('([@{}])', '@\\1', body)
434 return get_output ('output-verbatim') % body
438 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
439 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
440 'option-sep' : ',\s*',
441 'header': r"\\documentclass\s*(\[.*?\])?",
442 'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
443 'preamble-end': r'(?P<code>\\begin{document})',
444 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
445 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
446 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
447 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
448 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
449 'def-post-re': r"\\def\\postLilypondExample",
450 'def-pre-re': r"\\def\\preLilypondExample",
451 'usepackage-graphics': r"\usepackage{graphics}",
452 'intertext': r',?\s*intertext=\".*?\"',
453 'multiline-comment': no_match,
454 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
455 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
459 # why do we have distinction between @mbinclude and @include?
461 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
464 'preamble-end': no_match,
465 'landscape': no_match,
466 'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
467 'verb': r"""(?P<code>@code{.*?})""",
468 'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
469 'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
470 # pyton2.2b2 barfs on this
471 'lilypond-block': r"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
473 # 1.5.2 barfs on this.
474 # 'lilypond-block': r"""(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s)""",
475 'option-sep' : ',\s*',
476 'intertext': r',?\s*intertext=\".*?\"',
477 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
478 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
484 for r in re_dict.keys ():
487 for k in olddict.keys ():
488 newdict[k] = re.compile (olddict[k])
502 def get_output (name):
503 return output_dict[format][name]
506 return re_dict[format][name]
508 def bounding_box_dimensions(fname):
510 fname = os.path.join(g_outdir, fname)
514 error ("Error opening `%s'" % fname)
516 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
518 return (int (s.group (3) - s.group (1) + 0.5),
519 int (s.group (4) - s.group (2) + 0.5))
524 sys.stderr.write (str + "\n Exiting ... \n\n")
528 def compose_full_body (body, opts):
529 """Construct the lilypond code to send to Lilypond.
530 Add stuff to BODY using OPTS as options."""
531 music_size = default_music_fontsize
532 latex_size = default_text_fontsize
534 if g_force_lilypond_fontsize:
535 music_size = g_force_lilypond_fontsize
537 m = re.match ('([0-9]+)pt', o)
539 music_size = string.atoi(m.group (1))
541 m = re.match ('latexfontsize=([0-9]+)pt', o)
543 latex_size = string.atoi (m.group (1))
545 if re.search ('\\\\score', body):
549 if 'fragment' in opts:
551 if 'nofragment' in opts:
554 if is_fragment and not 'multiline' in opts:
555 opts.append('singleline')
556 if 'singleline' in opts:
559 l = __main__.paperguru.get_linewidth()
562 m= re.search ('relative(.*)', o)
566 v = string.atoi (m.group (1))
573 pitch = pitch + '\,' * v
575 pitch = pitch + '\'' * v
577 body = '\\relative %s { %s }' %(pitch, body)
586 optstring = string.join (opts, ' ')
587 optstring = re.sub ('\n', ' ', optstring)
589 %% Generated automatically by: lilypond-book.py
591 \include "paper%d.ly"
592 \paper { linewidth = %f \pt }
593 """ % (optstring, music_size, l) + body
595 # ughUGH not original options
598 def parse_options_string(s):
600 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
601 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
602 r3 = re.compile("(\w+?)((,\s*)|$)")
607 d[m.group(2)] = re.split(",\s*", m.group(3))
612 d[m.group(2)] = m.group(3)
620 error ("format of option string invalid (was `%')" % s)
623 def scan_latex_preamble(chunks):
624 # first we want to scan the \documentclass line
625 # it should be the first non-comment line
628 if chunks[idx][0] == 'ignore':
631 m = get_re ('header').match(chunks[idx][1])
632 if m <> None and m.group (1):
633 options = re.split (',[\n \t]*', m.group(1)[1:-1])
638 paperguru.m_landscape = 1
639 m = re.match("(.*?)paper", o)
641 paperguru.m_papersize = m.group()
643 m = re.match("(\d\d)pt", o)
645 paperguru.m_fontsize = int(m.group(1))
648 while chunks[idx][0] != 'preamble-end':
649 if chunks[idx] == 'ignore':
652 m = get_re ('geometry').search(chunks[idx][1])
654 paperguru.m_use_geometry = 1
655 o = parse_options_string(m.group('options'))
657 paperguru.set_geo_option(k, o[k])
660 def scan_texi_preamble (chunks):
661 # this is not bulletproof..., it checks the first 10 chunks
662 for c in chunks[:10]:
664 for s in ('afourpaper', 'afourwide', 'letterpaper',
665 'afourlatex', 'smallbook'):
666 if string.find(c[1], "@%s" % s) != -1:
667 paperguru.m_papersize = s
669 def scan_preamble (chunks):
670 if __main__.format == 'texi':
671 scan_texi_preamble(chunks)
673 assert __main__.format == 'latex'
674 scan_latex_preamble(chunks)
677 def completize_preamble (chunks):
678 if __main__.format == 'texi':
680 pre_b = post_b = graphics_b = None
682 if chunk[0] == 'preamble-end':
684 if chunk[0] == 'input':
685 m = get_re('def-pre-re').search(chunk[1])
688 if chunk[0] == 'input':
689 m = get_re('def-post-re').search(chunk[1])
692 if chunk[0] == 'input':
693 m = get_re('usepackage-graphics').search(chunk[1])
697 while chunks[x][0] != 'preamble-end':
700 chunks.insert(x, ('input', get_output ('output-default-pre')))
702 chunks.insert(x, ('input', get_output ('output-default-post')))
704 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
709 def find_file (name):
711 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
716 for a in include_path:
718 nm = os.path.join (a, name)
720 __main__.read_files.append (nm)
725 sys.stderr.write ("Reading `%s'\n" % nm)
726 return (f.read (), nm)
728 error ("File not found `%s'\n" % name)
731 def do_ignore(match_object):
732 return [('ignore', match_object.group('code'))]
733 def do_preamble_end(match_object):
734 return [('preamble-end', match_object.group('code'))]
736 def make_verbatim(match_object):
737 return [('verbatim', match_object.group('code'))]
739 def make_verb(match_object):
740 return [('verb', match_object.group('code'))]
742 def do_include_file(m):
744 return [('input', get_output ('pagebreak'))] \
745 + read_doc_file(m.group('filename')) \
746 + [('input', get_output ('pagebreak'))]
748 def do_input_file(m):
749 return read_doc_file(m.group('filename'))
751 def make_lilypond(m):
752 if m.group('options'):
753 options = m.group('options')
756 return [('input', get_output('output-lilypond-fragment') %
757 (options, m.group('code')))]
759 def make_lilypond_file(m):
762 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
763 into a @lilypond .. @end lilypond block.
767 if m.group('options'):
768 options = m.group('options')
771 (content, nm) = find_file(m.group('filename'))
772 options = "filename=%s," % nm + options
774 return [('input', get_output('output-lilypond') %
777 def make_lilypond_block(m):
778 if m.group('options'):
779 options = get_re('option-sep').split (m.group('options'))
782 options = filter(lambda s: s != '', options)
783 return [('lilypond', m.group('code'), options)]
786 if __main__.format != 'latex':
788 if m.group('num') == 'one':
789 return [('numcols', m.group('code'), 1)]
790 if m.group('num') == 'two':
791 return [('numcols', m.group('code'), 2)]
793 def chop_chunks(chunks, re_name, func, use_match=0):
799 m = get_re (re_name).search (str)
801 newchunks.append (('input', str))
805 newchunks.append (('input', str[:m.start ('match')]))
807 newchunks.append (('input', str[:m.start (0)]))
808 #newchunks.extend(func(m))
809 # python 1.5 compatible:
810 newchunks = newchunks + func(m)
811 str = str [m.end(0):]
816 def determine_format (str):
817 if __main__.format == '':
819 latex = re.search ('\\\\document', str[:200])
820 texinfo = re.search ('@node|@setfilename', str[:200])
825 if texinfo and latex == None:
827 elif latex and texinfo == None:
830 error("error: can't determine format, please specify")
833 if __main__.paperguru == None:
834 if __main__.format == 'texi':
839 __main__.paperguru = g
842 def read_doc_file (filename):
843 """Read the input file, find verbatim chunks and do \input and \include
845 (str, path) = find_file(filename)
846 determine_format (str)
848 chunks = [('input', str)]
850 # we have to check for verbatim before doing include,
851 # because we don't want to include files that are mentioned
852 # inside a verbatim environment
853 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
854 chunks = chop_chunks(chunks, 'verb', make_verb)
855 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
857 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
858 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
862 taken_file_names = {}
863 def schedule_lilypond_block (chunk):
864 """Take the body and options from CHUNK, figure out how the
865 real .ly should look, and what should be left MAIN_STR (meant
866 for the main file). The .ly is written, and scheduled in
869 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
871 TODO has format [basename, extension, extension, ... ]
874 (type, body, opts) = chunk
875 assert type == 'lilypond'
876 file_body = compose_full_body (body, opts)
877 basename = 'lily-' + `abs(hash (file_body))`
879 m = re.search ('filename="(.*?)"', o)
881 basename = m.group (1)
882 if not taken_file_names.has_key(basename):
883 taken_file_names[basename] = 0
885 taken_file_names[basename] = taken_file_names[basename] + 1
886 basename = basename + "-%i" % taken_file_names[basename]
888 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
889 needed_filetypes = ['tex']
892 needed_filetypes.append('eps')
893 needed_filetypes.append('png')
894 if 'eps' in opts and not ('eps' in needed_filetypes):
895 needed_filetypes.append('eps')
896 pathbase = os.path.join (g_outdir, basename)
897 def f(base, ext1, ext2):
898 a = os.path.isfile(base + ext2)
899 if (os.path.isfile(base + ext1) and
900 os.path.isfile(base + ext2) and
901 os.stat(base+ext1)[stat.ST_MTIME] >
902 os.stat(base+ext2)[stat.ST_MTIME]) or \
903 not os.path.isfile(base + ext2):
906 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
908 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
910 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
914 if 'printfilename' in opts:
916 m= re.match ("filename=(.*)", o)
918 newbody = newbody + get_output ("output-filename") % m.group(1)
922 if 'verbatim' in opts:
923 newbody = output_verbatim (body)
926 m = re.search ('intertext="(.*?)"', o)
928 newbody = newbody + m.group (1) + "\n\n"
929 if format == 'latex':
934 else: # format == 'texi'
936 newbody = newbody + get_output (s) % {'fn': basename }
937 return ('lilypond', newbody, opts, todo, basename)
939 def process_lilypond_blocks(outname, chunks):#ugh rename
941 # Count sections/chapters.
943 if c[0] == 'lilypond':
944 c = schedule_lilypond_block (c)
945 elif c[0] == 'numcols':
946 paperguru.m_num_cols = c[2]
953 sys.stderr.write ("invoking `%s'\n" % cmd)
956 error ('Error command exited with value %d\n' % st)
960 def get_bbox (filename):
965 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', l)
967 gr = map (string.atoi, m.groups ())
972 def make_pixmap (name):
973 bbox = get_bbox (name + '.eps')
975 fo = open (name + '.trans.eps' , 'w')
976 fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
981 x = (2* margin + bbox[2] - bbox[0]) * res / 72.
982 y = (2* margin + bbox[3] - bbox[1]) * res / 72.
984 cmd = r"""gs -g%dx%d -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s"""
986 cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
988 status = system (cmd)
990 os.unlink (name + '.png')
991 error ("Removing output file")
993 def compile_all_files (chunks):
1000 if c[0] <> 'lilypond':
1009 if base + '.ly' not in tex:
1010 tex.append (base + '.ly')
1011 elif e == 'png' and g_do_pictures:
1017 # fixme: be sys-independent.
1019 if g_outdir and x[0] <> '/' :
1020 x = os.path.join (g_here_dir, x)
1023 incs = map (incl_opt, include_path)
1024 lilyopts = string.join (incs, ' ' )
1026 lilyopts = lilyopts + ' --dependencies '
1028 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1029 texfiles = string.join (tex, ' ')
1030 system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
1033 # Ugh, fixing up dependencies for .tex generation
1036 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1041 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1047 system(r"tex '\nonstopmode \input %s'" % e)
1048 system(r"dvips -E -o %s %s" % (e + '.eps', e))
1056 def update_file (body, name):
1058 write the body if it has changed
1069 f = open (name , 'w')
1076 def getopt_args (opts):
1077 "Construct arguments (LONG, SHORT) for getopt from list of options."
1082 short = short + o[1]
1090 return (short, long)
1092 def option_help_str (o):
1093 "Transform one option description (4-tuple ) into neatly formatted string"
1111 return ' ' + sh + sep + long + arg
1114 def options_help_str (opts):
1115 "Convert a list of options into a neatly formatted string"
1121 s = option_help_str (o)
1122 strs.append ((s, o[3]))
1128 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1132 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1133 Generate hybrid LaTeX input from Latex + lilypond
1136 sys.stdout.write (options_help_str (option_definitions))
1137 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1141 Report bugs to bug-gnu-music@gnu.org.
1143 Written by Tom Cato Amundsen <tca@gnu.org> and
1144 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1150 def write_deps (fn, target, chunks):
1152 sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1153 f = open (os.path.join(g_outdir, fn), 'w')
1154 f.write ('%s%s: ' % (g_dep_prefix, target))
1155 for d in read_files:
1159 if c[0] == 'lilypond':
1160 (type, body, opts, todo, basename) = c;
1161 basenames.append (basename)
1164 d=g_outdir + '/' + d
1166 #if not os.isfile (d): # thinko?
1167 if not re.search ('/', d):
1168 d = g_dep_prefix + d
1169 f.write ('%s.tex ' % d)
1171 #if len (basenames):
1172 # for d in basenames:
1173 # f.write ('%s.ly ' % d)
1174 # f.write (' : %s' % target)
1180 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1182 def print_version ():
1184 sys.stdout.write (r"""Copyright 1998--1999
1185 Distributed under terms of the GNU General Public License. It comes with
1190 def check_texidoc (chunks):
1193 if c[0] == 'lilypond':
1194 (type, body, opts, todo, basename) = c;
1195 pathbase = os.path.join (g_outdir, basename)
1196 if os.path.isfile (pathbase + '.texidoc'):
1197 body = '\n@include %s.texidoc\n' % basename + body
1198 c = (type, body, opts, todo, basename)
1203 ## what's this? Docme --hwn
1205 def fix_epswidth (chunks):
1208 if c[0] <> 'lilypond' or 'eps' not in c[2]:
1209 newchunks.append (c)
1214 m = re.match ('magnification=([0-9.]+)', o)
1216 mag = string.atof (m.group (1))
1218 def replace_eps_dim (match, lmag = mag):
1219 filename = match.group (1)
1220 dims = bounding_box_dimensions (filename)
1222 return '%fpt' % (dims[0] *lmag)
1224 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", replace_eps_dim, c[1])
1225 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1231 def do_file(input_filename):
1235 my_outname = outname
1237 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1238 my_depname = my_outname + '.dep'
1240 chunks = read_doc_file(input_filename)
1241 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1242 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1243 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1244 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1245 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1246 chunks = chop_chunks(chunks, 'numcols', do_columns)
1248 #for c in chunks: print "c:", c;
1250 scan_preamble(chunks)
1251 chunks = process_lilypond_blocks(my_outname, chunks)
1253 foutn = os.path.join (g_outdir, my_outname + '.' + format)
1256 if __main__.g_run_lilypond:
1257 compile_all_files (chunks)
1258 chunks = fix_epswidth (chunks)
1260 if __main__.format == 'texi':
1261 chunks = check_texidoc (chunks)
1264 chunks = completize_preamble (chunks)
1265 sys.stderr.write ("Writing `%s'\n" % foutn)
1266 fout = open (foutn, 'w')
1273 write_deps (my_depname, foutn, chunks)
1278 (sh, long) = getopt_args (__main__.option_definitions)
1279 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1280 except getopt.error, msg:
1281 sys.stderr.write("error: %s" % msg)
1289 if o == '--include' or o == '-I':
1290 include_path.append (a)
1291 elif o == '--version' or o == '-v':
1294 elif o == '--format' or o == '-f':
1296 elif o == '--outname' or o == '-o':
1299 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1302 elif o == '--help' or o == '-h':
1304 elif o == '--no-lily' or o == '-n':
1305 __main__.g_run_lilypond = 0
1306 elif o == '--dependencies' or o == '-M':
1308 elif o == '--default-music-fontsize':
1309 default_music_fontsize = string.atoi (a)
1310 elif o == '--default-lilypond-fontsize':
1311 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1312 default_music_fontsize = string.atoi (a)
1313 elif o == '--force-music-fontsize':
1314 g_force_lilypond_fontsize = string.atoi(a)
1315 elif o == '--force-lilypond-fontsize':
1316 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1317 g_force_lilypond_fontsize = string.atoi(a)
1318 elif o == '--dep-prefix':
1320 elif o == '--no-pictures':
1322 elif o == '--read-lys':
1324 elif o == '--outdir':
1329 if os.path.isfile(g_outdir):
1330 error ("outdir is a file: %s" % g_outdir)
1331 if not os.path.exists(g_outdir):
1333 setup_environment ()
1334 for input_filename in files:
1335 do_file(input_filename)
1338 # Petr, ik zou willen dat ik iets zinvoller deed,
1339 # maar wat ik kan ik doen, het verandert toch niets?