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 m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
160 num = string.atof(m.group (1))
161 conv = dimension_conversion_dict[m.group(2)]
165 if name == 'body' or name == 'text':
166 if type(value) == type(""):
167 self.m_geo_textwidth = value
169 self.m_geo_textwidth = value[0]
171 elif name == 'portrait':
172 self.m_geo_landscape = 0
173 elif name == 'reversemp' or name == 'reversemarginpar':
174 if self.m_geo_includemp == None:
175 self.m_geo_includemp = 1
176 elif name == 'marginparwidth' or name == 'marginpar':
177 self.m_geo_x_marginparwidth = value
178 self.m_geo_includemp = 1
179 elif name == 'marginparsep':
180 self.m_geo_x_marginparsep = value
181 self.m_geo_includemp = 1
182 elif name == 'scale':
183 if type(value) == type(""):
184 self.m_geo_width = self.get_paperwidth() * float(value)
186 self.m_geo_width = self.get_paperwidth() * float(value[0])
187 elif name == 'hscale':
188 self.m_geo_width = self.get_paperwidth() * float(value)
189 elif name == 'left' or name == 'lmargin':
190 self.m_geo_lmargin = value
191 elif name == 'right' or name == 'rmargin':
192 self.m_geo_rmargin = value
193 elif name == 'hdivide' or name == 'divide':
194 if value[0] not in ('*', ''):
195 self.m_geo_lmargin = value[0]
196 if value[1] not in ('*', ''):
197 self.m_geo_width = value[1]
198 if value[2] not in ('*', ''):
199 self.m_geo_rmargin = value[2]
200 elif name == 'hmargin':
201 if type(value) == type(""):
202 self.m_geo_lmargin = value
203 self.m_geo_rmargin = value
205 self.m_geo_lmargin = value[0]
206 self.m_geo_rmargin = value[1]
207 elif name == 'margin':#ugh there is a bug about this option in
208 # the geometry documentation
209 if type(value) == type(""):
210 self.m_geo_lmargin = value
211 self.m_geo_rmargin = value
213 self.m_geo_lmargin = value[0]
214 self.m_geo_rmargin = value[0]
215 elif name == 'total':
216 if type(value) == type(""):
217 self.m_geo_width = value
219 self.m_geo_width = value[0]
220 elif name == 'width' or name == 'totalwidth':
221 self.m_geo_width = value
222 elif name == 'paper' or name == 'papername':
223 self.m_papersize = value
224 elif name[-5:] == 'paper':
225 self.m_papersize = name
228 # what is _set_dimen ?? /MB
229 #self._set_dimen('m_geo_'+name, value)
230 def __setattr__(self, name, value):
231 if type(value) == type("") and \
232 dimension_conversion_dict.has_key (value[-2:]):
233 f = dimension_conversion_dict[value[-2:]]
234 self.__dict__[name] = f(float(value[:-2]))
236 self.__dict__[name] = value
239 s = "LatexPaper:\n-----------"
240 for v in self.__dict__.keys():
242 s = s + str (v) + ' ' + str (self.__dict__[v])
243 s = s + "-----------"
246 def get_linewidth(self):
247 w = self._calc_linewidth()
248 if self.m_num_cols == 2:
252 def get_paperwidth(self):
253 #if self.m_use_geometry:
254 return self.m_paperdef[self.m_papersize][self.m_landscape or self.m_geo_landscape]
255 #return self.m_paperdef[self.m_papersize][self.m_landscape]
257 def _calc_linewidth(self):
258 # since geometry sometimes ignores 'includemp', this is
259 # more complicated than it should be
261 if self.m_geo_includemp:
262 if self.m_geo_x_marginparsep is not None:
263 mp = mp + self.m_geo_x_marginparsep
265 mp = mp + self.m_geo_marginparsep[self.m_fontsize]
266 if self.m_geo_x_marginparwidth is not None:
267 mp = mp + self.m_geo_x_marginparwidth
269 mp = mp + self.m_geo_marginparwidth[self.m_fontsize]
271 #ugh test if this is necessary
275 if not self.m_use_geometry:
276 return latex_linewidths[self.m_papersize][self.m_fontsize]
278 geo_opts = (self.m_geo_lmargin == None,
279 self.m_geo_width == None,
280 self.m_geo_rmargin == None)
282 if geo_opts == (1, 1, 1):
283 if self.m_geo_textwidth:
284 return self.m_geo_textwidth
285 w = self.get_paperwidth() * 0.8
287 elif geo_opts == (0, 1, 1):
288 if self.m_geo_textwidth:
289 return self.m_geo_textwidth
290 return self.f1(self.m_geo_lmargin, mp)
291 elif geo_opts == (1, 1, 0):
292 if self.m_geo_textwidth:
293 return self.m_geo_textwidth
294 return self.f1(self.m_geo_rmargin, mp)
296 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
297 if self.m_geo_textwidth:
298 return self.m_geo_textwidth
299 return self.m_geo_width - mp
300 elif geo_opts in ((0, 1, 0), (0, 0, 0)):
301 w = self.get_paperwidth() \
302 - self.m_geo_lmargin - self.m_geo_rmargin - mp
306 raise "Never do this!"
308 tmp = self.get_paperwidth() - m * 2 - mp
313 tmp = self.get_paperwidth() - self.m_geo_lmargin \
321 self.m_papersize = 'letterpaper'
323 def get_linewidth(self):
324 return texi_linewidths[self.m_papersize][self.m_fontsize]
330 def em2pt(x, fontsize = 10):
331 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
332 def ex2pt(x, fontsize = 10):
333 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
338 dimension_conversion_dict ={
340 'cm': lambda x: mm2pt(10*x),
349 # indices are no. of columns, papersize, fontsize
350 # Why can't this be calculated?
352 'a4paper':{10: 345, 11: 360, 12: 390},
353 'a4paper-landscape': {10: 598, 11: 596, 12:592},
354 'a5paper':{10: 276, 11: 276, 12: 276},
355 'b5paper':{10: 345, 11: 356, 12: 356},
356 'letterpaper':{10: 345, 11: 360, 12: 390},
357 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
358 'legalpaper': {10: 345, 11: 360, 12: 390},
359 'executivepaper':{10: 345, 11: 360, 12: 379}}
362 'afourpaper': {12: mm2pt(160)},
363 'afourwide': {12: in2pt(6.5)},
364 'afourlatex': {12: mm2pt(150)},
365 'smallbook': {12: in2pt(5)},
366 'letterpaper': {12: in2pt(6)}}
368 option_definitions = [
369 ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'),
370 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
371 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
372 ('OPT', '', 'extra-options' , 'Pass OPT quoted to the lilypond command line'),
373 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
374 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
375 ('DIR', 'I', 'include', 'include path'),
376 ('', 'M', 'dependencies', 'write dependencies'),
377 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
378 ('', 'n', 'no-lily', 'don\'t run lilypond'),
379 ('', '', 'no-pictures', "don\'t generate pictures"),
380 ('', '', 'read-lys', "don't write ly files."),
381 ('FILE', 'o', 'outname', 'filename main output file'),
382 ('FILE', '', 'outdir', "where to place generated files"),
383 ('', 'v', 'version', 'print version information' ),
384 ('', 'h', 'help', 'print help'),
387 # format specific strings, ie. regex-es for input, and % strings for output
390 'output-lilypond-fragment' : r"""\begin[eps,singleline,%s]{lilypond}
397 'output-filename' : r'''
400 'output-lilypond': r"""\begin[%s]{lilypond}
403 'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
404 'output-default-post': "\\def\postLilypondExample{}\n",
405 'output-default-pre': "\\def\preLilypondExample{}\n",
406 'usepackage-graphics': '\\usepackage{graphics}\n',
407 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
408 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
409 'pagebreak': r'\pagebreak',
411 'texi' : {'output-lilypond': """@lilypond[%s]
415 'output-filename' : r'''
418 'output-lilypond-fragment': """@lilypond[%s]
419 \context Staff\context Voice{ %s }
422 'output-verbatim': r"""@example
427 # do some tweaking: @ is needed in some ps stuff.
428 # override EndLilyPondOutput, since @tex is done
429 # in a sandbox, you can't do \input lilyponddefs at the
430 # top of the document.
432 # should also support fragment in
438 \def\EndLilyPondOutput{}
444 <a href="%(fn)s.png">
445 <img border=0 src="%(fn)s.png" alt="[picture of music]">
452 def output_verbatim (body):
453 if __main__.format == 'texi':
454 body = re.sub ('([@{}])', '@\\1', body)
455 return get_output ('output-verbatim') % body
459 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
460 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
461 'option-sep' : ',\s*',
462 'header': r"\\documentclass\s*(\[.*?\])?",
463 'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
464 'preamble-end': r'(?P<code>\\begin{document})',
465 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
466 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
467 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
468 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
469 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
470 'def-post-re': r"\\def\\postLilypondExample",
471 'def-pre-re': r"\\def\\preLilypondExample",
472 'usepackage-graphics': r"\usepackage{graphics}",
473 'intertext': r',?\s*intertext=\".*?\"',
474 'multiline-comment': no_match,
475 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
476 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
480 # why do we have distinction between @mbinclude and @include?
484 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
487 'preamble-end': no_match,
488 'landscape': no_match,
489 'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
490 'verb': r"""(?P<code>@code{.*?})""",
491 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
492 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
493 'lilypond-block': r"""(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end lilypond)\s""",
494 'option-sep' : ',\s*',
495 'intertext': r',?\s*intertext=\".*?\"',
496 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
497 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
503 for r in re_dict.keys ():
506 for k in olddict.keys ():
508 newdict[k] = re.compile (olddict[k])
510 print 'invalid regexp: %s' % olddict[k]
512 # we'd like to catch and reraise a more detailed error, but
513 # alas, the exceptions changed across the 1.5/2.1 boundary.
528 def get_output (name):
529 return output_dict[format][name]
532 return re_dict[format][name]
534 def bounding_box_dimensions(fname):
536 fname = os.path.join(g_outdir, fname)
540 error ("Error opening `%s'" % fname)
542 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
545 gs = map (lambda x: string.atoi (x), s.groups ())
546 return (int (gs[2] - gs[0] + 0.5),
547 int (gs[3] - gs[1] + 0.5))
552 sys.stderr.write (str + "\n Exiting ... \n\n")
556 def compose_full_body (body, opts):
557 """Construct the lilypond code to send to Lilypond.
558 Add stuff to BODY using OPTS as options."""
559 music_size = default_music_fontsize
560 latex_size = default_text_fontsize
562 if g_force_lilypond_fontsize:
563 music_size = g_force_lilypond_fontsize
565 m = re.match ('([0-9]+)pt', o)
567 music_size = string.atoi(m.group (1))
569 m = re.match ('latexfontsize=([0-9]+)pt', o)
571 latex_size = string.atoi (m.group (1))
573 if re.search ('\\\\score', body):
577 if 'fragment' in opts:
579 if 'nofragment' in opts:
582 if is_fragment and not 'multiline' in opts:
583 opts.append('singleline')
584 if 'singleline' in opts:
587 l = __main__.paperguru.get_linewidth()
590 m= re.search ('relative(.*)', o)
594 v = string.atoi (m.group (1))
601 pitch = pitch + '\,' * v
603 pitch = pitch + '\'' * v
605 body = '\\relative %s { %s }' %(pitch, body)
614 optstring = string.join (opts, ' ')
615 optstring = re.sub ('\n', ' ', optstring)
617 %% Generated automatically by: lilypond-book.py
619 \include "paper%d.ly"
620 \paper { linewidth = %f \pt }
621 """ % (optstring, music_size, l) + body
623 # ughUGH not original options
626 def parse_options_string(s):
628 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
629 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
630 r3 = re.compile("(\w+?)((,\s*)|$)")
635 d[m.group(2)] = re.split(",\s*", m.group(3))
640 d[m.group(2)] = m.group(3)
648 error ("format of option string invalid (was `%')" % s)
651 def scan_latex_preamble(chunks):
652 # first we want to scan the \documentclass line
653 # it should be the first non-comment line
656 if chunks[idx][0] == 'ignore':
659 m = get_re ('header').match(chunks[idx][1])
660 if m <> None and m.group (1):
661 options = re.split (',[\n \t]*', m.group(1)[1:-1])
666 paperguru.m_landscape = 1
667 m = re.match("(.*?)paper", o)
669 paperguru.m_papersize = m.group()
671 m = re.match("(\d\d)pt", o)
673 paperguru.m_fontsize = int(m.group(1))
676 while chunks[idx][0] != 'preamble-end':
677 if chunks[idx] == 'ignore':
680 m = get_re ('geometry').search(chunks[idx][1])
682 paperguru.m_use_geometry = 1
683 o = parse_options_string(m.group('options'))
685 paperguru.set_geo_option(k, o[k])
688 def scan_texi_preamble (chunks):
689 # this is not bulletproof..., it checks the first 10 chunks
690 for c in chunks[:10]:
692 for s in ('afourpaper', 'afourwide', 'letterpaper',
693 'afourlatex', 'smallbook'):
694 if string.find(c[1], "@%s" % s) != -1:
695 paperguru.m_papersize = s
697 def scan_preamble (chunks):
698 if __main__.format == 'texi':
699 scan_texi_preamble(chunks)
701 assert __main__.format == 'latex'
702 scan_latex_preamble(chunks)
705 def completize_preamble (chunks):
706 if __main__.format == 'texi':
708 pre_b = post_b = graphics_b = None
710 if chunk[0] == 'preamble-end':
712 if chunk[0] == 'input':
713 m = get_re('def-pre-re').search(chunk[1])
716 if chunk[0] == 'input':
717 m = get_re('def-post-re').search(chunk[1])
720 if chunk[0] == 'input':
721 m = get_re('usepackage-graphics').search(chunk[1])
725 while chunks[x][0] != 'preamble-end':
728 chunks.insert(x, ('input', get_output ('output-default-pre')))
730 chunks.insert(x, ('input', get_output ('output-default-post')))
732 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
737 def find_file (name):
739 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
744 for a in include_path:
746 nm = os.path.join (a, name)
748 __main__.read_files.append (nm)
753 sys.stderr.write ("Reading `%s'\n" % nm)
754 return (f.read (), nm)
756 error ("File not found `%s'\n" % name)
759 def do_ignore(match_object):
760 return [('ignore', match_object.group('code'))]
761 def do_preamble_end(match_object):
762 return [('preamble-end', match_object.group('code'))]
764 def make_verbatim(match_object):
765 return [('verbatim', match_object.group('code'))]
767 def make_verb(match_object):
768 return [('verb', match_object.group('code'))]
770 def do_include_file(m):
772 return [('input', get_output ('pagebreak'))] \
773 + read_doc_file(m.group('filename')) \
774 + [('input', get_output ('pagebreak'))]
776 def do_input_file(m):
777 return read_doc_file(m.group('filename'))
779 def make_lilypond(m):
780 if m.group('options'):
781 options = m.group('options')
784 return [('input', get_output('output-lilypond-fragment') %
785 (options, m.group('code')))]
787 def make_lilypond_file(m):
790 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
791 into a @lilypond .. @end lilypond block.
795 if m.group('options'):
796 options = m.group('options')
799 (content, nm) = find_file(m.group('filename'))
800 options = "filename=%s," % nm + options
802 return [('input', get_output('output-lilypond') %
805 def make_lilypond_block(m):
806 if m.group('options'):
807 options = get_re('option-sep').split (m.group('options'))
810 options = filter(lambda s: s != '', options)
811 return [('lilypond', m.group('code'), options)]
814 if __main__.format != 'latex':
816 if m.group('num') == 'one':
817 return [('numcols', m.group('code'), 1)]
818 if m.group('num') == 'two':
819 return [('numcols', m.group('code'), 2)]
821 def chop_chunks(chunks, re_name, func, use_match=0):
827 m = get_re (re_name).search (str)
829 newchunks.append (('input', str))
833 newchunks.append (('input', str[:m.start ('match')]))
835 newchunks.append (('input', str[:m.start (0)]))
836 #newchunks.extend(func(m))
837 # python 1.5 compatible:
838 newchunks = newchunks + func(m)
839 str = str [m.end(0):]
844 def determine_format (str):
845 if __main__.format == '':
847 latex = re.search ('\\\\document', str[:200])
848 texinfo = re.search ('@node|@setfilename', str[:200])
853 if texinfo and latex == None:
855 elif latex and texinfo == None:
858 error("error: can't determine format, please specify")
861 if __main__.paperguru == None:
862 if __main__.format == 'texi':
867 __main__.paperguru = g
870 def read_doc_file (filename):
871 """Read the input file, find verbatim chunks and do \input and \include
873 (str, path) = find_file(filename)
874 determine_format (str)
876 chunks = [('input', str)]
878 # we have to check for verbatim before doing include,
879 # because we don't want to include files that are mentioned
880 # inside a verbatim environment
881 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
882 chunks = chop_chunks(chunks, 'verb', make_verb)
883 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
885 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
886 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
890 taken_file_names = {}
891 def schedule_lilypond_block (chunk):
892 """Take the body and options from CHUNK, figure out how the
893 real .ly should look, and what should be left MAIN_STR (meant
894 for the main file). The .ly is written, and scheduled in
897 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
899 TODO has format [basename, extension, extension, ... ]
902 (type, body, opts) = chunk
903 assert type == 'lilypond'
904 file_body = compose_full_body (body, opts)
905 basename = 'lily-' + `abs(hash (file_body))`
907 m = re.search ('filename="(.*?)"', o)
909 basename = m.group (1)
910 if not taken_file_names.has_key(basename):
911 taken_file_names[basename] = 0
913 taken_file_names[basename] = taken_file_names[basename] + 1
914 basename = basename + "-%i" % taken_file_names[basename]
916 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
917 needed_filetypes = ['tex']
920 needed_filetypes.append('eps')
921 needed_filetypes.append('png')
922 if 'eps' in opts and not ('eps' in needed_filetypes):
923 needed_filetypes.append('eps')
924 pathbase = os.path.join (g_outdir, basename)
925 def f(base, ext1, ext2):
926 a = os.path.isfile(base + ext2)
927 if (os.path.isfile(base + ext1) and
928 os.path.isfile(base + ext2) and
929 os.stat(base+ext1)[stat.ST_MTIME] >
930 os.stat(base+ext2)[stat.ST_MTIME]) or \
931 not os.path.isfile(base + ext2):
934 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
936 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
938 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
942 if 'printfilename' in opts:
944 m= re.match ("filename=(.*)", o)
946 newbody = newbody + get_output ("output-filename") % m.group(1)
950 if 'verbatim' in opts:
951 newbody = output_verbatim (body)
954 m = re.search ('intertext="(.*?)"', o)
956 newbody = newbody + m.group (1) + "\n\n"
957 if format == 'latex':
962 else: # format == 'texi'
964 newbody = newbody + get_output (s) % {'fn': basename }
965 return ('lilypond', newbody, opts, todo, basename)
967 def process_lilypond_blocks(outname, chunks):#ugh rename
969 # Count sections/chapters.
971 if c[0] == 'lilypond':
972 c = schedule_lilypond_block (c)
973 elif c[0] == 'numcols':
974 paperguru.m_num_cols = c[2]
981 sys.stderr.write ("invoking `%s'\n" % cmd)
984 error ('Error command exited with value %d\n' % st)
988 def get_bbox (filename):
993 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', l)
995 gr = map (string.atoi, m.groups ())
1000 def make_pixmap (name):
1001 bbox = get_bbox (name + '.eps')
1003 fo = open (name + '.trans.eps' , 'w')
1004 fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
1009 x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1010 y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1012 cmd = r"""gs -g%dx%d -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s"""
1014 cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
1016 status = system (cmd)
1018 os.unlink (name + '.png')
1019 error ("Removing output file")
1021 def compile_all_files (chunks):
1028 if c[0] <> 'lilypond':
1037 if base + '.ly' not in tex:
1038 tex.append (base + '.ly')
1039 elif e == 'png' and g_do_pictures:
1045 # fixme: be sys-independent.
1047 if g_outdir and x[0] <> '/' :
1048 x = os.path.join (g_here_dir, x)
1051 incs = map (incl_opt, include_path)
1052 lilyopts = string.join (incs, ' ' )
1054 lilyopts = lilyopts + ' --dependencies '
1056 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1057 texfiles = string.join (tex, ' ')
1058 system ('lilypond --header=texidoc %s %s %s' % (lilyopts, g_extra_opts, texfiles))
1061 # Ugh, fixing up dependencies for .tex generation
1064 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1069 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1075 system(r"tex '\nonstopmode \input %s'" % e)
1076 system(r"dvips -E -o %s %s" % (e + '.eps', e))
1084 def update_file (body, name):
1086 write the body if it has changed
1097 f = open (name , 'w')
1104 def getopt_args (opts):
1105 "Construct arguments (LONG, SHORT) for getopt from list of options."
1110 short = short + o[1]
1118 return (short, long)
1120 def option_help_str (o):
1121 "Transform one option description (4-tuple ) into neatly formatted string"
1139 return ' ' + sh + sep + long + arg
1142 def options_help_str (opts):
1143 "Convert a list of options into a neatly formatted string"
1149 s = option_help_str (o)
1150 strs.append ((s, o[3]))
1156 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1160 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1161 Generate hybrid LaTeX input from Latex + lilypond
1164 sys.stdout.write (options_help_str (option_definitions))
1165 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1169 Report bugs to bug-lilypond@gnu.org.
1171 Written by Tom Cato Amundsen <tca@gnu.org> and
1172 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1178 def write_deps (fn, target, chunks):
1180 sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1181 f = open (os.path.join(g_outdir, fn), 'w')
1182 f.write ('%s%s: ' % (g_dep_prefix, target))
1183 for d in read_files:
1187 if c[0] == 'lilypond':
1188 (type, body, opts, todo, basename) = c;
1189 basenames.append (basename)
1192 d=g_outdir + '/' + d
1194 #if not os.isfile (d): # thinko?
1195 if not re.search ('/', d):
1196 d = g_dep_prefix + d
1197 f.write ('%s.tex ' % d)
1199 #if len (basenames):
1200 # for d in basenames:
1201 # f.write ('%s.ly ' % d)
1202 # f.write (' : %s' % target)
1208 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1210 def print_version ():
1212 sys.stdout.write (r"""Copyright 1998--1999
1213 Distributed under terms of the GNU General Public License. It comes with
1218 def check_texidoc (chunks):
1221 if c[0] == 'lilypond':
1222 (type, body, opts, todo, basename) = c;
1223 pathbase = os.path.join (g_outdir, basename)
1224 if os.path.isfile (pathbase + '.texidoc'):
1225 body = '\n@include %s.texidoc\n' % basename + body
1226 c = (type, body, opts, todo, basename)
1231 ## what's this? Docme --hwn
1233 def fix_epswidth (chunks):
1236 if c[0] <> 'lilypond' or 'eps' not in c[2]:
1237 newchunks.append (c)
1242 m = re.match ('magnification=([0-9.]+)', o)
1244 mag = string.atof (m.group (1))
1246 def replace_eps_dim (match, lmag = mag):
1247 filename = match.group (1)
1248 dims = bounding_box_dimensions (filename)
1250 return '%fpt' % (dims[0] *lmag)
1252 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", replace_eps_dim, c[1])
1253 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1259 def do_file(input_filename):
1263 my_outname = outname
1265 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1266 my_depname = my_outname + '.dep'
1268 chunks = read_doc_file(input_filename)
1269 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1270 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1271 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1272 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1273 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1274 chunks = chop_chunks(chunks, 'numcols', do_columns)
1276 #for c in chunks: print "c:", c;
1278 scan_preamble(chunks)
1279 chunks = process_lilypond_blocks(my_outname, chunks)
1281 foutn = os.path.join (g_outdir, my_outname + '.' + format)
1284 if __main__.g_run_lilypond:
1285 compile_all_files (chunks)
1286 chunks = fix_epswidth (chunks)
1288 if __main__.format == 'texi':
1289 chunks = check_texidoc (chunks)
1292 chunks = completize_preamble (chunks)
1293 sys.stderr.write ("Writing `%s'\n" % foutn)
1294 fout = open (foutn, 'w')
1301 write_deps (my_depname, foutn, chunks)
1306 (sh, long) = getopt_args (__main__.option_definitions)
1307 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1308 except getopt.error, msg:
1309 sys.stderr.write("error: %s" % msg)
1317 if o == '--include' or o == '-I':
1318 include_path.append (a)
1319 elif o == '--version' or o == '-v':
1322 elif o == '--format' or o == '-f':
1324 elif o == '--outname' or o == '-o':
1327 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1330 elif o == '--help' or o == '-h':
1332 elif o == '--no-lily' or o == '-n':
1333 __main__.g_run_lilypond = 0
1334 elif o == '--dependencies' or o == '-M':
1336 elif o == '--default-music-fontsize':
1337 default_music_fontsize = string.atoi (a)
1338 elif o == '--default-lilypond-fontsize':
1339 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1340 default_music_fontsize = string.atoi (a)
1341 elif o == '--extra-options':
1343 elif o == '--force-music-fontsize':
1344 g_force_lilypond_fontsize = string.atoi(a)
1345 elif o == '--force-lilypond-fontsize':
1346 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1347 g_force_lilypond_fontsize = string.atoi(a)
1348 elif o == '--dep-prefix':
1350 elif o == '--no-pictures':
1352 elif o == '--read-lys':
1354 elif o == '--outdir':
1359 if os.path.isfile(g_outdir):
1360 error ("outdir is a file: %s" % g_outdir)
1361 if not os.path.exists(g_outdir):
1363 setup_environment ()
1364 for input_filename in files:
1365 do_file(input_filename)
1368 # Petr, ik zou willen dat ik iets zinvoller deed,
1369 # maar wat ik kan ik doen, het verandert toch niets?