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'
54 # if set, LILYPONDPREFIX must take prevalence
55 # if datadir is not set, we're doing a build and LILYPONDPREFIX
58 if os.environ.has_key ('LILYPONDPREFIX') :
59 datadir = os.environ['LILYPONDPREFIX']
63 while datadir[-1] == os.sep:
66 # Try to cater for bad installations of LilyPond, that have
67 # broken TeX setup. Just hope this doesn't hurt good TeX
68 # setups. Maybe we should check if kpsewhich can find
69 # feta16.{afm,mf,tex,tfm}, and only set env upon failure.
71 'MFINPUTS' : datadir + '/mf:',
72 'TEXINPUTS': datadir + '/tex:' + datadir + '/ps:.:',
73 'TFMFONTS' : datadir + '/tfm:',
74 'GS_FONTPATH' : datadir + '/afm:' + datadir + '/pfa',
75 'GS_LIB' : datadir + '/ps',
78 # tex needs lots of memory, more than it gets by default on Debian
79 non_path_environment = {
80 'extra_mem_top' : '1000000',
81 'extra_mem_bottom' : '1000000',
82 'pool_size' : '250000',
85 def setup_environment ():
86 for key in environment.keys ():
87 val = environment[key]
88 if os.environ.has_key (key):
89 val = val + os.pathsep + os.environ[key]
92 for key in non_path_environment.keys ():
93 val = non_path_environment[key]
94 print '%s=%s' % (key,val)
97 include_path = [os.getcwd()]
100 # g_ is for global (?)
102 g_here_dir = os.getcwd ()
105 g_force_lilypond_fontsize = 0
114 default_music_fontsize = 16
115 default_text_fontsize = 12
118 # this code is ugly. It should be cleaned
122 # the dimensions are from geometry.sty
123 'a0paper': (mm2pt(841), mm2pt(1189)),
124 'a1paper': (mm2pt(595), mm2pt(841)),
125 'a2paper': (mm2pt(420), mm2pt(595)),
126 'a3paper': (mm2pt(297), mm2pt(420)),
127 'a4paper': (mm2pt(210), mm2pt(297)),
128 'a5paper': (mm2pt(149), mm2pt(210)),
129 'b0paper': (mm2pt(1000), mm2pt(1414)),
130 'b1paper': (mm2pt(707), mm2pt(1000)),
131 'b2paper': (mm2pt(500), mm2pt(707)),
132 'b3paper': (mm2pt(353), mm2pt(500)),
133 'b4paper': (mm2pt(250), mm2pt(353)),
134 'b5paper': (mm2pt(176), mm2pt(250)),
135 'letterpaper': (in2pt(8.5), in2pt(11)),
136 'legalpaper': (in2pt(8.5), in2pt(14)),
137 'executivepaper': (in2pt(7.25), in2pt(10.5))}
138 self.m_use_geometry = None
139 self.m_papersize = 'letterpaper'
143 self.m_geo_landscape = 0
144 self.m_geo_width = None
145 self.m_geo_textwidth = None
146 self.m_geo_lmargin = None
147 self.m_geo_rmargin = None
148 self.m_geo_includemp = None
149 self.m_geo_marginparwidth = {10: 57, 11: 50, 12: 35}
150 self.m_geo_marginparsep = {10: 11, 11: 10, 12: 10}
151 self.m_geo_x_marginparwidth = None
152 self.m_geo_x_marginparsep = None
154 def set_geo_option(self, name, value):
156 if type(value) == type([]):
157 value = map(conv_dimen_to_float, value)
159 value = conv_dimen_to_float(value)
161 if name == 'body' or name == 'text':
162 if type(value) == type([]):
163 self.m_geo_textwidth = value[0]
165 self.m_geo_textwidth = value
167 elif name == 'portrait':
168 self.m_geo_landscape = 0
169 elif name == 'reversemp' or name == 'reversemarginpar':
170 if self.m_geo_includemp == None:
171 self.m_geo_includemp = 1
172 elif name == 'marginparwidth' or name == 'marginpar':
173 self.m_geo_x_marginparwidth = value
174 self.m_geo_includemp = 1
175 elif name == 'marginparsep':
176 self.m_geo_x_marginparsep = value
177 self.m_geo_includemp = 1
178 elif name == 'scale':
179 if type(value) == type([]):
180 self.m_geo_width = self.get_paperwidth() * value[0]
182 self.m_geo_width = self.get_paperwidth() * value
183 elif name == 'hscale':
184 self.m_geo_width = self.get_paperwidth() * value
185 elif name == 'left' or name == 'lmargin':
186 self.m_geo_lmargin = value
187 elif name == 'right' or name == 'rmargin':
188 self.m_geo_rmargin = value
189 elif name == 'hdivide' or name == 'divide':
190 if value[0] not in ('*', ''):
191 self.m_geo_lmargin = value[0]
192 if value[1] not in ('*', ''):
193 self.m_geo_width = value[1]
194 if value[2] not in ('*', ''):
195 self.m_geo_rmargin = value[2]
196 elif name == 'hmargin':
197 if type(value) == type([]):
198 self.m_geo_lmargin = value[0]
199 self.m_geo_rmargin = value[1]
201 self.m_geo_lmargin = value
202 self.m_geo_rmargin = value
203 elif name == 'margin':#ugh there is a bug about this option in
204 # the geometry documentation
205 if type(value) == type([]):
206 self.m_geo_lmargin = value[0]
207 self.m_geo_rmargin = value[0]
209 self.m_geo_lmargin = value
210 self.m_geo_rmargin = value
211 elif name == 'total':
212 if type(value) == type([]):
213 self.m_geo_width = value[0]
215 self.m_geo_width = value
216 elif name == 'width' or name == 'totalwidth':
217 self.m_geo_width = value
218 elif name == 'paper' or name == 'papername':
219 self.m_papersize = value
220 elif name[-5:] == 'paper':
221 self.m_papersize = name
225 def __setattr__(self, name, value):
226 if type(value) == type("") and \
227 dimension_conversion_dict.has_key (value[-2:]):
228 f = dimension_conversion_dict[value[-2:]]
229 self.__dict__[name] = f(float(value[:-2]))
231 self.__dict__[name] = value
234 s = "LatexPaper:\n-----------"
235 for v in self.__dict__.keys():
237 s = s + str (v) + ' ' + str (self.__dict__[v])
238 s = s + "-----------"
241 def get_linewidth(self):
242 w = self._calc_linewidth()
243 if self.m_num_cols == 2:
247 def get_paperwidth(self):
248 #if self.m_use_geometry:
249 return self.m_paperdef[self.m_papersize][self.m_landscape or self.m_geo_landscape]
250 #return self.m_paperdef[self.m_papersize][self.m_landscape]
252 def _calc_linewidth(self):
253 # since geometry sometimes ignores 'includemp', this is
254 # more complicated than it should be
256 if self.m_geo_includemp:
257 if self.m_geo_x_marginparsep is not None:
258 mp = mp + self.m_geo_x_marginparsep
260 mp = mp + self.m_geo_marginparsep[self.m_fontsize]
261 if self.m_geo_x_marginparwidth is not None:
262 mp = mp + self.m_geo_x_marginparwidth
264 mp = mp + self.m_geo_marginparwidth[self.m_fontsize]
266 #ugh test if this is necessary
270 if not self.m_use_geometry:
271 return latex_linewidths[self.m_papersize][self.m_fontsize]
273 geo_opts = (self.m_geo_lmargin == None,
274 self.m_geo_width == None,
275 self.m_geo_rmargin == None)
277 if geo_opts == (1, 1, 1):
278 if self.m_geo_textwidth:
279 return self.m_geo_textwidth
280 w = self.get_paperwidth() * 0.8
282 elif geo_opts == (0, 1, 1):
283 if self.m_geo_textwidth:
284 return self.m_geo_textwidth
285 return self.f1(self.m_geo_lmargin, mp)
286 elif geo_opts == (1, 1, 0):
287 if self.m_geo_textwidth:
288 return self.m_geo_textwidth
289 return self.f1(self.m_geo_rmargin, mp)
291 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
292 if self.m_geo_textwidth:
293 return self.m_geo_textwidth
294 return self.m_geo_width - mp
295 elif geo_opts in ((0, 1, 0), (0, 0, 0)):
296 w = self.get_paperwidth() \
297 - self.m_geo_lmargin - self.m_geo_rmargin - mp
301 raise "Never do this!"
303 tmp = self.get_paperwidth() - m * 2 - mp
308 tmp = self.get_paperwidth() - self.m_geo_lmargin \
316 self.m_papersize = 'letterpaper'
318 def get_linewidth(self):
319 return texi_linewidths[self.m_papersize][self.m_fontsize]
325 def em2pt(x, fontsize = 10):
326 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
327 def ex2pt(x, fontsize = 10):
328 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
333 dimension_conversion_dict ={
335 'cm': lambda x: mm2pt(10*x),
342 # Convert numeric values, with or without specific dimension, to floats.
344 def conv_dimen_to_float(value):
345 if type(value) == type(""):
346 m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
349 num = string.atof(m.group (1))
350 conv = dimension_conversion_dict[m.group(2)]
354 elif re.match ("^[0-9.]+$",value):
361 # indices are no. of columns, papersize, fontsize
362 # Why can't this be calculated?
364 'a4paper':{10: 345, 11: 360, 12: 390},
365 'a4paper-landscape': {10: 598, 11: 596, 12:592},
366 'a5paper':{10: 276, 11: 276, 12: 276},
367 'b5paper':{10: 345, 11: 356, 12: 356},
368 'letterpaper':{10: 345, 11: 360, 12: 390},
369 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
370 'legalpaper': {10: 345, 11: 360, 12: 390},
371 'executivepaper':{10: 345, 11: 360, 12: 379}}
374 'afourpaper': {12: mm2pt(160)},
375 'afourwide': {12: in2pt(6.5)},
376 'afourlatex': {12: mm2pt(150)},
377 'smallbook': {12: in2pt(5)},
378 'letterpaper': {12: in2pt(6)}}
380 option_definitions = [
381 ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'),
382 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
383 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
384 ('OPT', '', 'extra-options' , 'Pass OPT quoted to the lilypond command line'),
385 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
386 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
387 ('DIR', 'I', 'include', 'include path'),
388 ('', 'M', 'dependencies', 'write dependencies'),
389 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
390 ('', 'n', 'no-lily', 'don\'t run lilypond'),
391 ('', '', 'no-pictures', "don\'t generate pictures"),
392 ('', '', 'read-lys', "don't write ly files."),
393 ('FILE', 'o', 'outname', 'filename main output file'),
394 ('FILE', '', 'outdir', "where to place generated files"),
395 ('', 'v', 'version', 'print version information' ),
396 ('', 'h', 'help', 'print help'),
399 # format specific strings, ie. regex-es for input, and % strings for output
402 'output-lilypond-fragment' : r"""\begin[eps,singleline,%s]{lilypond}
409 'output-filename' : r'''
412 'output-lilypond': r"""\begin[%s]{lilypond}
415 'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
416 'output-default-post': "\\def\postLilypondExample{}\n",
417 'output-default-pre': "\\def\preLilypondExample{}\n",
418 'usepackage-graphics': '\\usepackage{graphics}\n',
419 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
420 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
421 'pagebreak': r'\pagebreak',
423 'texi' : {'output-lilypond': """@lilypond[%s]
427 'output-filename' : r'''
430 'output-lilypond-fragment': """@lilypond[%s]
431 \context Staff\context Voice{ %s }
434 'output-verbatim': r"""@example
439 # do some tweaking: @ is needed in some ps stuff.
440 # override EndLilyPondOutput, since @tex is done
441 # in a sandbox, you can't do \input lilyponddefs at the
442 # top of the document.
444 # should also support fragment in
450 \def\EndLilyPondOutput{}
456 <a href="%(fn)s.png">
457 <img border=0 src="%(fn)s.png" alt="[picture of music]">
464 def output_verbatim (body):
465 if __main__.format == 'texi':
466 body = re.sub ('([@{}])', '@\\1', body)
467 return get_output ('output-verbatim') % body
471 'latex': {'input': r'(?m)^[^%\n]*(?P<match>\\mbinput{?([^}\t \n}]*))',
472 'include': r'(?m)^[^%\n]*(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
473 'option-sep' : ',\s*',
474 'header': r"\\documentclass\s*(\[.*\])?",
475 'geometry': r"^(?m)[^%\n]*\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
476 'preamble-end': r'(?P<code>\\begin{document})',
477 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*\\end{verbatim})",
478 'verb': r"(?P<code>\\verb(?P<del>.).*(?P=del))",
479 'lilypond-file': r'(?m)^[^%\n]*(?P<match>\\lilypondfile\s*(\[(?P<options>.*)\])?\s*{(?P<filename>[^}]+)})',
480 'lilypond' : r'(?m)^[^%\n]*(?P<match>\\lilypond\s*(\[(?P<options>.*)\])?\s*{(?P<code>.*)})',
481 'lilypond-block': r"(?sm)^[^%\n]*(?P<match>\\begin\s*(\[(?P<options>.*)\])?\s*{lilypond}(?P<code>.*)\\end{lilypond})",
482 'def-post-re': r"\\def\\postLilypondExample",
483 'def-pre-re': r"\\def\\preLilypondExample",
484 'usepackage-graphics': r"\usepackage{graphics}",
485 'intertext': r',?\s*intertext=\".*\"',
486 'multiline-comment': no_match,
487 'singleline-comment': r"(?m)^.*(?P<match>(?P<code>^%.*$\n+))",
488 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
492 # why do we have distinction between @mbinclude and @include?
496 'include': '(?m)^[^%\n]*(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
499 'preamble-end': no_match,
500 'landscape': no_match,
501 'verbatim': r"""(?s)(?P<code>@example\s.*@end example\s)""",
502 'verb': r"""(?P<code>@code{.*})""",
503 'lilypond-file': r"""(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})""",
504 'lilypond' : r"""(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*)})""",
505 'lilypond-block': r"""(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*)@end lilypond)\s""",
506 'option-sep' : ',\s*',
507 'intertext': r',?\s*intertext=\".*\"',
508 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*@end ignore)\s",
509 'singleline-comment': r"(?m)^.*(?P<match>(?P<code>@c.*$\n+))",
515 for r in re_dict.keys ():
518 for k in olddict.keys ():
520 newdict[k] = re.compile (olddict[k])
522 print 'invalid regexp: %s' % olddict[k]
524 # we'd like to catch and reraise a more detailed error, but
525 # alas, the exceptions changed across the 1.5/2.1 boundary.
540 def get_output (name):
541 return output_dict[format][name]
544 return re_dict[format][name]
546 def bounding_box_dimensions(fname):
548 fname = os.path.join(g_outdir, fname)
552 error ("Error opening `%s'" % fname)
554 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
557 gs = map (lambda x: string.atoi (x), s.groups ())
558 return (int (gs[2] - gs[0] + 0.5),
559 int (gs[3] - gs[1] + 0.5))
564 sys.stderr.write (str + "\n Exiting ... \n\n")
568 def compose_full_body (body, opts):
569 """Construct the lilypond code to send to Lilypond.
570 Add stuff to BODY using OPTS as options."""
571 music_size = default_music_fontsize
572 latex_size = default_text_fontsize
574 if g_force_lilypond_fontsize:
575 music_size = g_force_lilypond_fontsize
577 m = re.match ('([0-9]+)pt', o)
579 music_size = string.atoi(m.group (1))
581 m = re.match ('latexfontsize=([0-9]+)pt', o)
583 latex_size = string.atoi (m.group (1))
585 if re.search ('\\\\score', body):
589 if 'fragment' in opts:
591 if 'nofragment' in opts:
594 if is_fragment and not 'multiline' in opts:
595 opts.append('singleline')
596 if 'singleline' in opts:
599 l = __main__.paperguru.get_linewidth()
602 m= re.search ('relative(.*)', o)
606 v = string.atoi (m.group (1))
613 pitch = pitch + '\,' * v
615 pitch = pitch + '\'' * v
617 body = '\\relative %s { %s }' %(pitch, body)
626 optstring = string.join (opts, ' ')
627 optstring = re.sub ('\n', ' ', optstring)
629 %% Generated automatically by: lilypond-book.py
631 \include "paper%d.ly"
632 \paper { linewidth = %f \pt }
633 """ % (optstring, music_size, l) + body
635 # ughUGH not original options
638 def parse_options_string(s):
640 r1 = re.compile("((\w+)={(.*)})((,\s*)|$)")
641 r2 = re.compile("((\w+)=(.*))((,\s*)|$)")
642 r3 = re.compile("(\w+?)((,\s*)|$)")
647 d[m.group(2)] = re.split(",\s*", m.group(3))
652 d[m.group(2)] = m.group(3)
660 error ("format of option string invalid (was `%')" % s)
663 def scan_latex_preamble(chunks):
664 # first we want to scan the \documentclass line
665 # it should be the first non-comment line
668 if chunks[idx][0] == 'ignore':
671 m = get_re ('header').match(chunks[idx][1])
672 if m <> None and m.group (1):
673 options = re.split (',[\n \t]*', m.group(1)[1:-1])
678 paperguru.m_landscape = 1
679 m = re.match("(.*)paper", o)
681 paperguru.m_papersize = m.group()
683 m = re.match("(\d\d)pt", o)
685 paperguru.m_fontsize = int(m.group(1))
688 while chunks[idx][0] != 'preamble-end':
689 if chunks[idx] == 'ignore':
692 m = get_re ('geometry').search(chunks[idx][1])
694 paperguru.m_use_geometry = 1
695 o = parse_options_string(m.group('options'))
697 paperguru.set_geo_option(k, o[k])
700 def scan_texi_preamble (chunks):
701 # this is not bulletproof..., it checks the first 10 chunks
702 for c in chunks[:10]:
704 for s in ('afourpaper', 'afourwide', 'letterpaper',
705 'afourlatex', 'smallbook'):
706 if string.find(c[1], "@%s" % s) != -1:
707 paperguru.m_papersize = s
709 def scan_preamble (chunks):
710 if __main__.format == 'texi':
711 scan_texi_preamble(chunks)
713 assert __main__.format == 'latex'
714 scan_latex_preamble(chunks)
717 def completize_preamble (chunks):
718 if __main__.format == 'texi':
720 pre_b = post_b = graphics_b = None
722 if chunk[0] == 'preamble-end':
724 if chunk[0] == 'input':
725 m = get_re('def-pre-re').search(chunk[1])
728 if chunk[0] == 'input':
729 m = get_re('def-post-re').search(chunk[1])
732 if chunk[0] == 'input':
733 m = get_re('usepackage-graphics').search(chunk[1])
737 while chunks[x][0] != 'preamble-end':
740 chunks.insert(x, ('input', get_output ('output-default-pre')))
742 chunks.insert(x, ('input', get_output ('output-default-post')))
744 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
749 def find_file (name):
751 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
756 for a in include_path:
758 nm = os.path.join (a, name)
760 __main__.read_files.append (nm)
765 sys.stderr.write ("Reading `%s'\n" % nm)
766 return (f.read (), nm)
768 error ("File not found `%s'\n" % name)
771 def do_ignore(match_object):
772 return [('ignore', match_object.group('code'))]
773 def do_preamble_end(match_object):
774 return [('preamble-end', match_object.group('code'))]
776 def make_verbatim(match_object):
777 return [('verbatim', match_object.group('code'))]
779 def make_verb(match_object):
780 return [('verb', match_object.group('code'))]
782 def do_include_file(m):
784 return [('input', get_output ('pagebreak'))] \
785 + read_doc_file(m.group('filename')) \
786 + [('input', get_output ('pagebreak'))]
788 def do_input_file(m):
789 return read_doc_file(m.group('filename'))
791 def make_lilypond(m):
792 if m.group('options'):
793 options = m.group('options')
796 return [('input', get_output('output-lilypond-fragment') %
797 (options, m.group('code')))]
799 def make_lilypond_file(m):
802 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
803 into a @lilypond .. @end lilypond block.
807 if m.group('options'):
808 options = m.group('options')
811 (content, nm) = find_file(m.group('filename'))
812 options = "filename=%s," % nm + options
814 return [('input', get_output('output-lilypond') %
817 def make_lilypond_block(m):
818 if m.group('options'):
819 options = get_re('option-sep').split (m.group('options'))
822 options = filter(lambda s: s != '', options)
823 return [('lilypond', m.group('code'), options)]
826 if __main__.format != 'latex':
828 if m.group('num') == 'one':
829 return [('numcols', m.group('code'), 1)]
830 if m.group('num') == 'two':
831 return [('numcols', m.group('code'), 2)]
833 def chop_chunks(chunks, re_name, func, use_match=0):
840 m = get_re (re_name).search (str)
842 newchunks.append (('input', str))
846 newchunks.append (('input', str[:m.start ('match')]))
848 newchunks.append (('input', str[:m.start (0)]))
849 #newchunks.extend(func(m))
850 # python 1.5 compatible:
851 newchunks = newchunks + func(m)
852 str = str [m.end(0):]
857 def determine_format (str):
858 if __main__.format == '':
860 latex = re.search ('\\\\document', str[:200])
861 texinfo = re.search ('@node|@setfilename', str[:200])
866 if texinfo and latex == None:
868 elif latex and texinfo == None:
871 error("error: can't determine format, please specify")
874 if __main__.paperguru == None:
875 if __main__.format == 'texi':
880 __main__.paperguru = g
883 def read_doc_file (filename):
884 """Read the input file, find verbatim chunks and do \input and \include
886 (str, path) = find_file(filename)
887 determine_format (str)
889 chunks = [('input', str)]
891 # we have to check for verbatim before doing include,
892 # because we don't want to include files that are mentioned
893 # inside a verbatim environment
894 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
895 chunks = chop_chunks(chunks, 'verb', make_verb)
896 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
898 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
899 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
903 taken_file_names = {}
904 def schedule_lilypond_block (chunk):
905 """Take the body and options from CHUNK, figure out how the
906 real .ly should look, and what should be left MAIN_STR (meant
907 for the main file). The .ly is written, and scheduled in
910 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
912 TODO has format [basename, extension, extension, ... ]
915 (type, body, opts) = chunk
916 assert type == 'lilypond'
917 file_body = compose_full_body (body, opts)
918 basename = 'lily-' + `abs(hash (file_body))`
920 m = re.search ('filename="(.*)"', o)
922 basename = m.group (1)
923 if not taken_file_names.has_key(basename):
924 taken_file_names[basename] = 0
926 taken_file_names[basename] = taken_file_names[basename] + 1
927 basename = basename + "-%i" % taken_file_names[basename]
929 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
930 needed_filetypes = ['tex']
933 needed_filetypes.append('eps')
934 needed_filetypes.append('png')
935 if 'eps' in opts and not ('eps' in needed_filetypes):
936 needed_filetypes.append('eps')
937 pathbase = os.path.join (g_outdir, basename)
938 def f(base, ext1, ext2):
939 a = os.path.isfile(base + ext2)
940 if (os.path.isfile(base + ext1) and
941 os.path.isfile(base + ext2) and
942 os.stat(base+ext1)[stat.ST_MTIME] >
943 os.stat(base+ext2)[stat.ST_MTIME]) or \
944 not os.path.isfile(base + ext2):
947 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
949 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
951 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
955 if 'printfilename' in opts:
957 m= re.match ("filename=(.*)", o)
959 newbody = newbody + get_output ("output-filename") % m.group(1)
963 if 'verbatim' in opts:
964 newbody = output_verbatim (body)
967 m = re.search ('intertext="(.*)"', o)
969 newbody = newbody + m.group (1) + "\n\n"
970 if format == 'latex':
975 else: # format == 'texi'
977 newbody = newbody + get_output (s) % {'fn': basename }
978 return ('lilypond', newbody, opts, todo, basename)
980 def process_lilypond_blocks(outname, chunks):#ugh rename
982 # Count sections/chapters.
984 if c[0] == 'lilypond':
985 c = schedule_lilypond_block (c)
986 elif c[0] == 'numcols':
987 paperguru.m_num_cols = c[2]
994 sys.stderr.write ("invoking `%s'\n" % cmd)
997 error ('Error command exited with value %d\n' % st)
1001 def get_bbox (filename):
1002 system ('gs -sDEVICE=bbox -q -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename, filename))
1004 box = open (filename + '.bbox').read()
1005 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
1008 gr = map (string.atoi, m.groups ())
1012 def make_pixmap (name):
1013 bbox = get_bbox (name + '.eps')
1015 fo = open (name + '.trans.eps' , 'w')
1016 fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
1021 x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1022 y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1024 cmd = r"""gs -g%dx%d -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s"""
1026 cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
1028 status = system (cmd)
1030 os.unlink (name + '.png')
1031 error ("Removing output file")
1033 def compile_all_files (chunks):
1040 if c[0] <> 'lilypond':
1049 if base + '.ly' not in tex:
1050 tex.append (base + '.ly')
1051 elif e == 'png' and g_do_pictures:
1057 # fixme: be sys-independent.
1059 if g_outdir and x[0] <> '/' :
1060 x = os.path.join (g_here_dir, x)
1063 incs = map (incl_opt, include_path)
1064 lilyopts = string.join (incs, ' ' )
1066 lilyopts = lilyopts + ' --dependencies '
1068 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1069 texfiles = string.join (tex, ' ')
1070 system ('lilypond --header=texidoc %s %s %s' % (lilyopts, g_extra_opts, texfiles))
1073 # Ugh, fixing up dependencies for .tex generation
1076 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1081 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1087 system(r"tex '\nonstopmode \input %s'" % e)
1088 system(r"dvips -E -o %s %s" % (e + '.eps', e))
1096 def update_file (body, name):
1098 write the body if it has changed
1109 f = open (name , 'w')
1116 def getopt_args (opts):
1117 "Construct arguments (LONG, SHORT) for getopt from list of options."
1122 short = short + o[1]
1130 return (short, long)
1132 def option_help_str (o):
1133 "Transform one option description (4-tuple ) into neatly formatted string"
1151 return ' ' + sh + sep + long + arg
1154 def options_help_str (opts):
1155 "Convert a list of options into a neatly formatted string"
1161 s = option_help_str (o)
1162 strs.append ((s, o[3]))
1168 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1172 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1173 Generate hybrid LaTeX input from Latex + lilypond
1176 sys.stdout.write (options_help_str (option_definitions))
1177 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1181 Report bugs to bug-lilypond@gnu.org.
1183 Written by Tom Cato Amundsen <tca@gnu.org> and
1184 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1190 def write_deps (fn, target, chunks):
1192 sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1193 f = open (os.path.join(g_outdir, fn), 'w')
1194 f.write ('%s%s: ' % (g_dep_prefix, target))
1195 for d in read_files:
1199 if c[0] == 'lilypond':
1200 (type, body, opts, todo, basename) = c;
1201 basenames.append (basename)
1204 d=g_outdir + '/' + d
1206 #if not os.isfile (d): # thinko?
1207 if not re.search ('/', d):
1208 d = g_dep_prefix + d
1209 f.write ('%s.tex ' % d)
1211 #if len (basenames):
1212 # for d in basenames:
1213 # f.write ('%s.ly ' % d)
1214 # f.write (' : %s' % target)
1220 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1222 def print_version ():
1224 sys.stdout.write (r"""Copyright 1998--1999
1225 Distributed under terms of the GNU General Public License. It comes with
1230 def check_texidoc (chunks):
1233 if c[0] == 'lilypond':
1234 (type, body, opts, todo, basename) = c;
1235 pathbase = os.path.join (g_outdir, basename)
1236 if os.path.isfile (pathbase + '.texidoc'):
1237 body = '\n@include %s.texidoc\n' % basename + body
1238 c = (type, body, opts, todo, basename)
1243 ## what's this? Docme --hwn
1245 def fix_epswidth (chunks):
1248 if c[0] <> 'lilypond' or 'eps' not in c[2]:
1249 newchunks.append (c)
1254 m = re.match ('magnification=([0-9.]+)', o)
1256 mag = string.atof (m.group (1))
1258 def replace_eps_dim (match, lmag = mag):
1259 filename = match.group (1)
1260 dims = bounding_box_dimensions (filename)
1262 return '%fpt' % (dims[0] *lmag)
1264 body = re.sub (r"""\\lilypondepswidth{(.*)}""", replace_eps_dim, c[1])
1265 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1271 def do_file(input_filename):
1275 my_outname = outname
1277 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1278 my_depname = my_outname + '.dep'
1280 chunks = read_doc_file(input_filename)
1281 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1282 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1283 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1284 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1285 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1286 chunks = chop_chunks(chunks, 'numcols', do_columns)
1288 #for c in chunks: print "c:", c;
1290 scan_preamble(chunks)
1291 chunks = process_lilypond_blocks(my_outname, chunks)
1293 foutn = os.path.join (g_outdir, my_outname + '.' + format)
1296 if __main__.g_run_lilypond:
1297 compile_all_files (chunks)
1298 chunks = fix_epswidth (chunks)
1300 if __main__.format == 'texi':
1301 chunks = check_texidoc (chunks)
1304 chunks = completize_preamble (chunks)
1305 sys.stderr.write ("Writing `%s'\n" % foutn)
1306 fout = open (foutn, 'w')
1313 write_deps (my_depname, foutn, chunks)
1318 (sh, long) = getopt_args (__main__.option_definitions)
1319 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1320 except getopt.error, msg:
1321 sys.stderr.write("error: %s" % msg)
1329 if o == '--include' or o == '-I':
1330 include_path.append (a)
1331 elif o == '--version' or o == '-v':
1334 elif o == '--format' or o == '-f':
1336 elif o == '--outname' or o == '-o':
1339 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1342 elif o == '--help' or o == '-h':
1344 elif o == '--no-lily' or o == '-n':
1345 __main__.g_run_lilypond = 0
1346 elif o == '--dependencies' or o == '-M':
1348 elif o == '--default-music-fontsize':
1349 default_music_fontsize = string.atoi (a)
1350 elif o == '--default-lilypond-fontsize':
1351 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1352 default_music_fontsize = string.atoi (a)
1353 elif o == '--extra-options':
1355 elif o == '--force-music-fontsize':
1356 g_force_lilypond_fontsize = string.atoi(a)
1357 elif o == '--force-lilypond-fontsize':
1358 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1359 g_force_lilypond_fontsize = string.atoi(a)
1360 elif o == '--dep-prefix':
1362 elif o == '--no-pictures':
1364 elif o == '--read-lys':
1366 elif o == '--outdir':
1371 if os.path.isfile(g_outdir):
1372 error ("outdir is a file: %s" % g_outdir)
1373 if not os.path.exists(g_outdir):
1375 setup_environment ()
1376 for input_filename in files:
1377 do_file(input_filename)
1380 # Petr, ik zou willen dat ik iets zinvoller deed,
1381 # maar wat ik kan ik doen, het verandert toch niets?