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': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
504 'lilypond' : '(?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):
839 m = get_re (re_name).search (str)
841 newchunks.append (('input', str))
845 newchunks.append (('input', str[:m.start ('match')]))
847 newchunks.append (('input', str[:m.start (0)]))
848 #newchunks.extend(func(m))
849 # python 1.5 compatible:
850 newchunks = newchunks + func(m)
851 str = str [m.end(0):]
856 def determine_format (str):
857 if __main__.format == '':
859 latex = re.search ('\\\\document', str[:200])
860 texinfo = re.search ('@node|@setfilename', str[:200])
865 if texinfo and latex == None:
867 elif latex and texinfo == None:
870 error("error: can't determine format, please specify")
873 if __main__.paperguru == None:
874 if __main__.format == 'texi':
879 __main__.paperguru = g
882 def read_doc_file (filename):
883 """Read the input file, find verbatim chunks and do \input and \include
885 (str, path) = find_file(filename)
886 determine_format (str)
888 chunks = [('input', str)]
890 # we have to check for verbatim before doing include,
891 # because we don't want to include files that are mentioned
892 # inside a verbatim environment
893 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
894 chunks = chop_chunks(chunks, 'verb', make_verb)
895 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
897 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
898 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
902 taken_file_names = {}
903 def schedule_lilypond_block (chunk):
904 """Take the body and options from CHUNK, figure out how the
905 real .ly should look, and what should be left MAIN_STR (meant
906 for the main file). The .ly is written, and scheduled in
909 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
911 TODO has format [basename, extension, extension, ... ]
914 (type, body, opts) = chunk
915 assert type == 'lilypond'
916 file_body = compose_full_body (body, opts)
917 basename = 'lily-' + `abs(hash (file_body))`
919 m = re.search ('filename="(.*?)"', o)
921 basename = m.group (1)
922 if not taken_file_names.has_key(basename):
923 taken_file_names[basename] = 0
925 taken_file_names[basename] = taken_file_names[basename] + 1
926 basename = basename + "-%i" % taken_file_names[basename]
928 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
929 needed_filetypes = ['tex']
932 needed_filetypes.append('eps')
933 needed_filetypes.append('png')
934 if 'eps' in opts and not ('eps' in needed_filetypes):
935 needed_filetypes.append('eps')
936 pathbase = os.path.join (g_outdir, basename)
937 def f(base, ext1, ext2):
938 a = os.path.isfile(base + ext2)
939 if (os.path.isfile(base + ext1) and
940 os.path.isfile(base + ext2) and
941 os.stat(base+ext1)[stat.ST_MTIME] >
942 os.stat(base+ext2)[stat.ST_MTIME]) or \
943 not os.path.isfile(base + ext2):
946 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
948 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
950 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
954 if 'printfilename' in opts:
956 m= re.match ("filename=(.*)", o)
958 newbody = newbody + get_output ("output-filename") % m.group(1)
962 if 'verbatim' in opts:
963 newbody = output_verbatim (body)
966 m = re.search ('intertext="(.*?)"', o)
968 newbody = newbody + m.group (1) + "\n\n"
969 if format == 'latex':
974 else: # format == 'texi'
976 newbody = newbody + get_output (s) % {'fn': basename }
977 return ('lilypond', newbody, opts, todo, basename)
979 def process_lilypond_blocks(outname, chunks):#ugh rename
981 # Count sections/chapters.
983 if c[0] == 'lilypond':
984 c = schedule_lilypond_block (c)
985 elif c[0] == 'numcols':
986 paperguru.m_num_cols = c[2]
993 sys.stderr.write ("invoking `%s'\n" % cmd)
996 error ('Error command exited with value %d\n' % st)
1000 def get_bbox (filename):
1001 system ('gs -sDEVICE=bbox -q -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename, filename))
1003 box = open (filename + '.bbox').read()
1004 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
1007 gr = map (string.atoi, m.groups ())
1011 def make_pixmap (name):
1012 bbox = get_bbox (name + '.eps')
1014 fo = open (name + '.trans.eps' , 'w')
1015 fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
1020 x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1021 y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1023 cmd = r"""gs -g%dx%d -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s"""
1025 cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
1027 status = system (cmd)
1029 os.unlink (name + '.png')
1030 error ("Removing output file")
1032 def compile_all_files (chunks):
1039 if c[0] <> 'lilypond':
1048 if base + '.ly' not in tex:
1049 tex.append (base + '.ly')
1050 elif e == 'png' and g_do_pictures:
1056 # fixme: be sys-independent.
1058 if g_outdir and x[0] <> '/' :
1059 x = os.path.join (g_here_dir, x)
1062 incs = map (incl_opt, include_path)
1063 lilyopts = string.join (incs, ' ' )
1065 lilyopts = lilyopts + ' --dependencies '
1067 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1068 texfiles = string.join (tex, ' ')
1069 system ('lilypond --header=texidoc %s %s %s' % (lilyopts, g_extra_opts, texfiles))
1072 # Ugh, fixing up dependencies for .tex generation
1075 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1080 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1086 system(r"tex '\nonstopmode \input %s'" % e)
1087 system(r"dvips -E -o %s %s" % (e + '.eps', e))
1095 def update_file (body, name):
1097 write the body if it has changed
1108 f = open (name , 'w')
1115 def getopt_args (opts):
1116 "Construct arguments (LONG, SHORT) for getopt from list of options."
1121 short = short + o[1]
1129 return (short, long)
1131 def option_help_str (o):
1132 "Transform one option description (4-tuple ) into neatly formatted string"
1150 return ' ' + sh + sep + long + arg
1153 def options_help_str (opts):
1154 "Convert a list of options into a neatly formatted string"
1160 s = option_help_str (o)
1161 strs.append ((s, o[3]))
1167 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1171 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1172 Generate hybrid LaTeX input from Latex + lilypond
1175 sys.stdout.write (options_help_str (option_definitions))
1176 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1180 Report bugs to bug-lilypond@gnu.org.
1182 Written by Tom Cato Amundsen <tca@gnu.org> and
1183 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1189 def write_deps (fn, target, chunks):
1191 sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1192 f = open (os.path.join(g_outdir, fn), 'w')
1193 f.write ('%s%s: ' % (g_dep_prefix, target))
1194 for d in read_files:
1198 if c[0] == 'lilypond':
1199 (type, body, opts, todo, basename) = c;
1200 basenames.append (basename)
1203 d=g_outdir + '/' + d
1205 #if not os.isfile (d): # thinko?
1206 if not re.search ('/', d):
1207 d = g_dep_prefix + d
1208 f.write ('%s.tex ' % d)
1210 #if len (basenames):
1211 # for d in basenames:
1212 # f.write ('%s.ly ' % d)
1213 # f.write (' : %s' % target)
1219 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1221 def print_version ():
1223 sys.stdout.write (r"""Copyright 1998--1999
1224 Distributed under terms of the GNU General Public License. It comes with
1229 def check_texidoc (chunks):
1232 if c[0] == 'lilypond':
1233 (type, body, opts, todo, basename) = c;
1234 pathbase = os.path.join (g_outdir, basename)
1235 if os.path.isfile (pathbase + '.texidoc'):
1236 body = '\n@include %s.texidoc\n' % basename + body
1237 c = (type, body, opts, todo, basename)
1242 ## what's this? Docme --hwn
1244 def fix_epswidth (chunks):
1247 if c[0] <> 'lilypond' or 'eps' not in c[2]:
1248 newchunks.append (c)
1253 m = re.match ('magnification=([0-9.]+)', o)
1255 mag = string.atof (m.group (1))
1257 def replace_eps_dim (match, lmag = mag):
1258 filename = match.group (1)
1259 dims = bounding_box_dimensions (filename)
1261 return '%fpt' % (dims[0] *lmag)
1263 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", replace_eps_dim, c[1])
1264 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1270 def do_file(input_filename):
1274 my_outname = outname
1276 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1277 my_depname = my_outname + '.dep'
1279 chunks = read_doc_file(input_filename)
1280 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1281 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1282 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1283 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1284 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1285 chunks = chop_chunks(chunks, 'numcols', do_columns)
1287 #for c in chunks: print "c:", c;
1289 scan_preamble(chunks)
1290 chunks = process_lilypond_blocks(my_outname, chunks)
1292 foutn = os.path.join (g_outdir, my_outname + '.' + format)
1295 if __main__.g_run_lilypond:
1296 compile_all_files (chunks)
1297 chunks = fix_epswidth (chunks)
1299 if __main__.format == 'texi':
1300 chunks = check_texidoc (chunks)
1303 chunks = completize_preamble (chunks)
1304 sys.stderr.write ("Writing `%s'\n" % foutn)
1305 fout = open (foutn, 'w')
1312 write_deps (my_depname, foutn, chunks)
1317 (sh, long) = getopt_args (__main__.option_definitions)
1318 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1319 except getopt.error, msg:
1320 sys.stderr.write("error: %s" % msg)
1328 if o == '--include' or o == '-I':
1329 include_path.append (a)
1330 elif o == '--version' or o == '-v':
1333 elif o == '--format' or o == '-f':
1335 elif o == '--outname' or o == '-o':
1338 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1341 elif o == '--help' or o == '-h':
1343 elif o == '--no-lily' or o == '-n':
1344 __main__.g_run_lilypond = 0
1345 elif o == '--dependencies' or o == '-M':
1347 elif o == '--default-music-fontsize':
1348 default_music_fontsize = string.atoi (a)
1349 elif o == '--default-lilypond-fontsize':
1350 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1351 default_music_fontsize = string.atoi (a)
1352 elif o == '--extra-options':
1354 elif o == '--force-music-fontsize':
1355 g_force_lilypond_fontsize = string.atoi(a)
1356 elif o == '--force-lilypond-fontsize':
1357 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1358 g_force_lilypond_fontsize = string.atoi(a)
1359 elif o == '--dep-prefix':
1361 elif o == '--no-pictures':
1363 elif o == '--read-lys':
1365 elif o == '--outdir':
1370 if os.path.isfile(g_outdir):
1371 error ("outdir is a file: %s" % g_outdir)
1372 if not os.path.exists(g_outdir):
1374 setup_environment ()
1375 for input_filename in files:
1376 do_file(input_filename)
1379 # Petr, ik zou willen dat ik iets zinvoller deed,
1380 # maar wat ik kan ik doen, het verandert toch niets?