2 # vim: set noexpandtab:
4 # * Figure out clean set of options. Hmm, isn't it pretty ok now?
5 # * add support for .lilyrc
6 # * EndLilyPondOutput is def'd as vfil. Causes large white gaps.
7 # * texinfo: add support for @pagesize
9 # todo: dimension handling (all the x2y) is clumsy. (tca: Thats
10 # because the values are taken directly from texinfo.tex,
11 # geometry.sty and article.cls. Give me a hint, and I'll
14 # This is was the idea for handling of comments:
15 # Multiline comments, @ignore .. @end ignore is scanned for
16 # in read_doc_file, and the chunks are marked as 'ignore', so
17 # lilypond-book will not touch them any more. The content of the
18 # chunks are written to the output file. Also 'include' and 'input'
19 # regex has to check if they are commented out.
21 # Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
22 # These three regex's has to check if they are on a commented line,
23 # % for latex, @c for texinfo.
25 # Then lines that are commented out with % (latex) and @c (Texinfo)
26 # are put into chunks marked 'ignore'. This cannot be done before
27 # searching for the lilypond-blocks because % is also the comment character
30 # The the rest of the rexeces are searched for. They don't have to test
31 # if they are on a commented out line.
43 program_version = '@TOPLEVEL_VERSION@'
44 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
45 program_version = '1.4pre'
48 # Try to cater for bad installations of LilyPond, that have
49 # broken TeX setup. Just hope this doesn't hurt good TeX
50 # setups. Maybe we should check if kpsewhich can find
51 # feta16.{afm,mf,tex,tfm}, and only set env upon failure.
55 'MFINPUTS' : datadir + '/mf:',
56 'TEXINPUTS': datadir + '/tex:' + datadir + '/ps:.:',
57 'TFMFONTS' : datadir + '/tfm:',
58 'GS_FONTPATH' : datadir + '/afm:' + datadir + '/pfa',
59 'GS_LIB' : datadir + '/ps',
62 def setup_environment ():
63 for key in environment.keys ():
64 val = environment[key]
65 if os.environ.has_key (key):
66 val = val + os.pathsep + os.environ[key]
71 include_path = [os.getcwd()]
74 # g_ is for global (?)
76 g_here_dir = os.getcwd ()
79 g_force_lilypond_fontsize = 0
87 default_music_fontsize = 16
88 default_text_fontsize = 12
91 # this code is ugly. It should be cleaned
95 # the dimensions are from geometry.sty
96 'a0paper': (mm2pt(841), mm2pt(1189)),
97 'a1paper': (mm2pt(595), mm2pt(841)),
98 'a2paper': (mm2pt(420), mm2pt(595)),
99 'a3paper': (mm2pt(297), mm2pt(420)),
100 'a4paper': (mm2pt(210), mm2pt(297)),
101 'a5paper': (mm2pt(149), mm2pt(210)),
102 'b0paper': (mm2pt(1000), mm2pt(1414)),
103 'b1paper': (mm2pt(707), mm2pt(1000)),
104 'b2paper': (mm2pt(500), mm2pt(707)),
105 'b3paper': (mm2pt(353), mm2pt(500)),
106 'b4paper': (mm2pt(250), mm2pt(353)),
107 'b5paper': (mm2pt(176), mm2pt(250)),
108 'letterpaper': (in2pt(8.5), in2pt(11)),
109 'legalpaper': (in2pt(8.5), in2pt(14)),
110 'executivepaper': (in2pt(7.25), in2pt(10.5))}
111 self.m_use_geometry = None
112 self.m_papersize = 'letterpaper'
116 self.m_geo_landscape = 0
117 self.m_geo_width = None
118 self.m_geo_textwidth = None
119 self.m_geo_lmargin = None
120 self.m_geo_rmargin = None
121 self.m_geo_includemp = None
122 self.m_geo_marginparwidth = {10: 57, 11: 50, 12: 35}
123 self.m_geo_marginparsep = {10: 11, 11: 10, 12: 10}
124 self.m_geo_x_marginparwidth = None
125 self.m_geo_x_marginparsep = None
127 def set_geo_option(self, name, value):
128 if name == 'body' or name == 'text':
129 if type(value) == type(""):
130 self.m_geo_textwidth = value
132 self.m_geo_textwidth = value[0]
134 elif name == 'portrait':
135 self.m_geo_landscape = 0
136 elif name == 'reversemp' or name == 'reversemarginpar':
137 if self.m_geo_includemp == None:
138 self.m_geo_includemp = 1
139 elif name == 'marginparwidth' or name == 'marginpar':
140 self.m_geo_x_marginparwidth = value
141 self.m_geo_includemp = 1
142 elif name == 'marginparsep':
143 self.m_geo_x_marginparsep = value
144 self.m_geo_includemp = 1
145 elif name == 'scale':
146 if type(value) == type(""):
147 self.m_geo_width = self.get_paperwidth() * float(value)
149 self.m_geo_width = self.get_paperwidth() * float(value[0])
150 elif name == 'hscale':
151 self.m_geo_width = self.get_paperwidth() * float(value)
152 elif name == 'left' or name == 'lmargin':
153 self.m_geo_lmargin = value
154 elif name == 'right' or name == 'rmargin':
155 self.m_geo_rmargin = value
156 elif name == 'hdivide' or name == 'divide':
157 if value[0] not in ('*', ''):
158 self.m_geo_lmargin = value[0]
159 if value[1] not in ('*', ''):
160 self.m_geo_width = value[1]
161 if value[2] not in ('*', ''):
162 self.m_geo_rmargin = value[2]
163 elif name == 'hmargin':
164 if type(value) == type(""):
165 self.m_geo_lmargin = value
166 self.m_geo_rmargin = value
168 self.m_geo_lmargin = value[0]
169 self.m_geo_rmargin = value[1]
170 elif name == 'margin':#ugh there is a bug about this option in
171 # the geometry documentation
172 if type(value) == type(""):
173 self.m_geo_lmargin = value
174 self.m_geo_rmargin = value
176 self.m_geo_lmargin = value[0]
177 self.m_geo_rmargin = value[0]
178 elif name == 'total':
179 if type(value) == type(""):
180 self.m_geo_width = value
182 self.m_geo_width = value[0]
183 elif name == 'width' or name == 'totalwidth':
184 self.m_geo_width = value
185 elif name == 'paper' or name == 'papername':
186 self.m_papersize = value
187 elif name[-5:] == 'paper':
188 self.m_papersize = name
190 self._set_dimen('m_geo_'+name, value)
191 def __setattr__(self, name, value):
192 if type(value) == type("") and \
193 dimension_conversion_dict.has_key (value[-2:]):
194 f = dimension_conversion_dict[dim]
195 self.__dict__[name] = f(float(value[:-2]))
197 self.__dict__[name] = value
200 s = "LatexPaper:\n-----------"
201 for v in self.__dict__.keys():
203 s = s + str (v) + ' ' + str (self.__dict__[v])
204 s = s + "-----------"
207 def get_linewidth(self):
208 w = self._calc_linewidth()
209 if self.m_num_cols == 2:
213 def get_paperwidth(self):
214 #if self.m_use_geometry:
215 return self.m_paperdef[self.m_papersize][self.m_landscape or self.m_geo_landscape]
216 #return self.m_paperdef[self.m_papersize][self.m_landscape]
218 def _calc_linewidth(self):
219 # since geometry sometimes ignores 'includemp', this is
220 # more complicated than it should be
222 if self.m_geo_includemp:
223 if self.m_geo_x_marginparsep is not None:
224 mp = mp + self.m_geo_x_marginparsep
226 mp = mp + self.m_geo_marginparsep[self.m_fontsize]
227 if self.m_geo_x_marginparwidth is not None:
228 mp = mp + self.m_geo_x_marginparwidth
230 mp = mp + self.m_geo_marginparwidth[self.m_fontsize]
232 #ugh test if this is necessary
236 if not self.m_use_geometry:
237 return latex_linewidths[self.m_papersize][self.m_fontsize]
239 geo_opts = (self.m_geo_lmargin == None,
240 self.m_geo_width == None,
241 self.m_geo_rmargin == None)
243 if geo_opts == (1, 1, 1):
244 if self.m_geo_textwidth:
245 return self.m_geo_textwidth
246 w = self.get_paperwidth() * 0.8
248 elif geo_opts == (0, 1, 1):
249 if self.m_geo_textwidth:
250 return self.m_geo_textwidth
251 return self.f1(self.m_geo_lmargin, mp)
252 elif geo_opts == (1, 1, 0):
253 if self.m_geo_textwidth:
254 return self.m_geo_textwidth
255 return self.f1(self.m_geo_rmargin, mp)
257 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
258 if self.m_geo_textwidth:
259 return self.m_geo_textwidth
260 return self.m_geo_width - mp
261 elif geo_opts in ((0, 1, 0), (0, 0, 0)):
262 w = self.get_paperwidth() \
263 - self.m_geo_lmargin - self.m_geo_rmargin - mp
267 raise "Never do this!"
269 tmp = self.get_paperwidth() - m * 2 - mp
274 tmp = self.get_paperwidth() - self.m_geo_lmargin \
282 self.m_papersize = 'letterpaper'
284 def get_linewidth(self):
285 return texi_linewidths[self.m_papersize][self.m_fontsize]
291 def em2pt(x, fontsize = 10):
292 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
293 def ex2pt(x, fontsize = 10):
294 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
299 dimension_conversion_dict ={
309 # indices are no. of columns, papersize, fontsize
310 # Why can't this be calculated?
312 'a4paper':{10: 345, 11: 360, 12: 390},
313 'a4paper-landscape': {10: 598, 11: 596, 12:592},
314 'a5paper':{10: 276, 11: 276, 12: 276},
315 'b5paper':{10: 345, 11: 356, 12: 356},
316 'letterpaper':{10: 345, 11: 360, 12: 390},
317 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
318 'legalpaper': {10: 345, 11: 360, 12: 390},
319 'executivepaper':{10: 345, 11: 360, 12: 379}}
322 'afourpaper': {12: mm2pt(160)},
323 'afourwide': {12: in2pt(6.5)},
324 'afourlatex': {12: mm2pt(150)},
325 'smallbook': {12: in2pt(5)},
326 'letterpaper': {12: in2pt(6)}}
328 option_definitions = [
329 ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'),
330 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
331 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
332 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
333 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
334 ('DIR', 'I', 'include', 'include path'),
335 ('', 'M', 'dependencies', 'write dependencies'),
336 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
337 ('', 'n', 'no-lily', 'don\'t run lilypond'),
338 ('', '', 'no-pictures', "don\'t generate pictures"),
339 ('', '', 'read-lys', "don't write ly files."),
340 ('FILE', 'o', 'outname', 'filename main output file'),
341 ('FILE', '', 'outdir', "where to place generated files"),
342 ('', 'v', 'version', 'print version information' ),
343 ('', 'h', 'help', 'print help'),
346 # format specific strings, ie. regex-es for input, and % strings for output
349 'output-lilypond-fragment' : r"""\begin[eps,singleline,%s]{lilypond}
356 'output-filename' : r'''
359 'output-lilypond': r"""\begin[%s]{lilypond}
362 'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
363 'output-default-post': "\\def\postLilypondExample{}\n",
364 'output-default-pre': "\\def\preLilypondExample{}\n",
365 'usepackage-graphics': '\\usepackage{graphics}\n',
366 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
367 'output-tex': '\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n',
368 'pagebreak': r'\pagebreak',
370 'texi' : {'output-lilypond': """@lilypond[%s]
374 'output-filename' : r'''
377 'output-lilypond-fragment': """@lilypond[%s]
378 \context Staff\context Voice{ %s }
381 'output-verbatim': r"""@example
386 # do some tweaking: @ is needed in some ps stuff.
387 # override EndLilyPondOutput, since @tex is done
388 # in a sandbox, you can't do \input lilyponddefs at the
389 # top of the document.
391 # should also support fragment in
397 \def\EndLilyPondOutput{}
409 def output_verbatim (body):
410 if __main__.format == 'texi':
411 body = re.sub ('([@{}])', '@\\1', body)
412 return get_output ('output-verbatim') % body
416 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
417 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
418 'option-sep' : ', *',
419 'header': r"\\documentclass\s*(\[.*?\])?",
420 'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
421 'preamble-end': r'(?P<code>\\begin{document})',
422 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
423 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
424 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile(\[(?P<options>.*?)\])?\{(?P<filename>.+)})',
425 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
426 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin(\[(?P<options>.*?)\])?{lilypond}(?P<code>.*?)\\end{lilypond})",
427 'def-post-re': r"\\def\\postLilypondExample",
428 'def-pre-re': r"\\def\\preLilypondExample",
429 'usepackage-graphics': r"\usepackage{graphics}",
430 'intertext': r',?\s*intertext=\".*?\"',
431 'multiline-comment': no_match,
432 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
433 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
437 # why do we have distinction between @mbinclude and @include?
439 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
442 'preamble-end': no_match,
443 'landscape': no_match,
444 'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
445 'verb': r"""(?P<code>@code{.*?})""",
446 'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
447 'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
448 'lilypond-block': r"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
449 'option-sep' : ', *',
450 'intertext': r',?\s*intertext=\".*?\"',
451 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
452 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
458 for r in re_dict.keys ():
461 for k in olddict.keys ():
462 newdict[k] = re.compile (olddict[k])
476 def get_output (name):
477 return output_dict[format][name]
480 return re_dict[format][name]
482 def bounding_box_dimensions(fname):
484 fname = os.path.join(g_outdir, fname)
488 error ("Error opening `%s'" % fname)
490 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
492 return (int(s.group(3))-int(s.group(1)),
493 int(s.group(4))-int(s.group(2)))
499 sys.stderr.write (str + "\n Exiting ... \n\n")
503 def compose_full_body (body, opts):
504 """Construct the lilypond code to send to Lilypond.
505 Add stuff to BODY using OPTS as options."""
506 music_size = default_music_fontsize
507 latex_size = default_text_fontsize
509 if g_force_lilypond_fontsize:
510 music_size = g_force_lilypond_fontsize
512 m = re.match ('([0-9]+)pt', o)
514 music_size = string.atoi(m.group (1))
516 m = re.match ('latexfontsize=([0-9]+)pt', o)
518 latex_size = string.atoi (m.group (1))
520 if re.search ('\\\\score', body):
524 if 'fragment' in opts:
526 if 'nofragment' in opts:
529 if is_fragment and not 'multiline' in opts:
530 opts.append('singleline')
531 if 'singleline' in opts:
534 l = __main__.paperguru.get_linewidth()
536 if 'relative' in opts:#ugh only when is_fragment
537 body = '\\relative c { %s }' % body
546 optstring = string.join (opts, ' ')
547 optstring = re.sub ('\n', ' ', optstring)
549 %% Generated automatically by: lilypond-book.py
550 %% options are %s %%ughUGH not original options
551 \include "paper%d.ly"
552 \paper { linewidth = %f \pt; }
553 """ % (optstring, music_size, l) + body
556 def parse_options_string(s):
558 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
559 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
560 r3 = re.compile("(\w+?)((,\s*)|$)")
565 d[m.group(2)] = re.split(",\s*", m.group(3))
570 d[m.group(2)] = m.group(3)
578 error ("format of option string invalid (was `%')" % s)
581 def scan_latex_preamble(chunks):
582 # first we want to scan the \documentclass line
583 # it should be the first non-comment line
586 if chunks[idx][0] == 'ignore':
589 m = get_re ('header').match(chunks[idx][1])
591 options = re.split (',[\n \t]*', m.group(1)[1:-1])
596 paperguru.m_landscape = 1
597 m = re.match("(.*?)paper", o)
599 paperguru.m_papersize = m.group()
601 m = re.match("(\d\d)pt", o)
603 paperguru.m_fontsize = int(m.group(1))
606 while chunks[idx][0] != 'preamble-end':
607 if chunks[idx] == 'ignore':
610 m = get_re ('geometry').search(chunks[idx][1])
612 paperguru.m_use_geometry = 1
613 o = parse_options_string(m.group('options'))
615 paperguru.set_geo_option(k, o[k])
618 def scan_texi_preamble (chunks):
619 # this is not bulletproof..., it checks the first 10 chunks
620 for c in chunks[:10]:
622 for s in ('afourpaper', 'afourwide', 'letterpaper',
623 'afourlatex', 'smallbook'):
624 if string.find(c[1], "@%s" % s) != -1:
625 paperguru.m_papersize = s
627 def scan_preamble (chunks):
628 if __main__.format == 'texi':
629 scan_texi_preamble(chunks)
631 assert __main__.format == 'latex'
632 scan_latex_preamble(chunks)
635 def completize_preamble (chunks):
636 if __main__.format == 'texi':
638 pre_b = post_b = graphics_b = None
640 if chunk[0] == 'preamble-end':
642 if chunk[0] == 'input':
643 m = get_re('def-pre-re').search(chunk[1])
646 if chunk[0] == 'input':
647 m = get_re('def-post-re').search(chunk[1])
650 if chunk[0] == 'input':
651 m = get_re('usepackage-graphics').search(chunk[1])
655 while chunks[x][0] != 'preamble-end':
658 chunks.insert(x, ('input', get_output ('output-default-pre')))
660 chunks.insert(x, ('input', get_output ('output-default-post')))
662 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
667 def find_file (name):
669 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
674 for a in include_path:
676 nm = os.path.join (a, name)
678 __main__.read_files.append (nm)
683 sys.stderr.write ("Reading `%s'\n" % nm)
684 return (f.read (), nm)
686 error ("File not found `%s'\n" % name)
689 def do_ignore(match_object):
690 return [('ignore', match_object.group('code'))]
691 def do_preamble_end(match_object):
692 return [('preamble-end', match_object.group('code'))]
694 def make_verbatim(match_object):
695 return [('verbatim', match_object.group('code'))]
697 def make_verb(match_object):
698 return [('verb', match_object.group('code'))]
700 def do_include_file(m):
702 return [('input', get_output ('pagebreak'))] \
703 + read_doc_file(m.group('filename')) \
704 + [('input', get_output ('pagebreak'))]
706 def do_input_file(m):
707 return read_doc_file(m.group('filename'))
709 def make_lilypond(m):
710 if m.group('options'):
711 options = m.group('options')
714 return [('input', get_output('output-lilypond-fragment') %
715 (options, m.group('code')))]
717 def make_lilypond_file(m):
720 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
721 into a @lilypond .. @end lilypond block.
725 if m.group('options'):
726 options = m.group('options')
729 (content, nm) = find_file(m.group('filename'))
730 options = "filename=%s," % nm + options
732 return [('input', get_output('output-lilypond') %
735 def make_lilypond_block(m):
736 if m.group('options'):
737 options = get_re('option-sep').split (m.group('options'))
740 options = filter(lambda s: s != '', options)
741 return [('lilypond', m.group('code'), options)]
744 if __main__.format != 'latex':
746 if m.group('num') == 'one':
747 return [('numcols', m.group('code'), 1)]
748 if m.group('num') == 'two':
749 return [('numcols', m.group('code'), 2)]
751 def chop_chunks(chunks, re_name, func, use_match=0):
757 m = get_re (re_name).search (str)
759 newchunks.append (('input', str))
763 newchunks.append (('input', str[:m.start ('match')]))
765 newchunks.append (('input', str[:m.start (0)]))
766 #newchunks.extend(func(m))
767 # python 1.5 compatible:
768 newchunks = newchunks + func(m)
769 str = str [m.end(0):]
774 def determine_format (str):
775 if __main__.format == '':
777 latex = re.search ('\\\\document', str[:200])
778 texinfo = re.search ('@node|@setfilename', str[:200])
783 if texinfo and latex == None:
785 elif latex and texinfo == None:
788 error("error: can't determine format, please specify")
791 if __main__.paperguru == None:
792 if __main__.format == 'texi':
797 __main__.paperguru = g
800 def read_doc_file (filename):
801 """Read the input file, find verbatim chunks and do \input and \include
803 (str, path) = find_file(filename)
804 determine_format (str)
806 chunks = [('input', str)]
808 # we have to check for verbatim before doing include,
809 # because we don't want to include files that are mentioned
810 # inside a verbatim environment
811 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
812 chunks = chop_chunks(chunks, 'verb', make_verb)
813 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
815 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
816 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
820 taken_file_names = {}
821 def schedule_lilypond_block (chunk):
822 """Take the body and options from CHUNK, figure out how the
823 real .ly should look, and what should be left MAIN_STR (meant
824 for the main file). The .ly is written, and scheduled in
827 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
829 TODO has format [basename, extension, extension, ... ]
832 (type, body, opts) = chunk
833 assert type == 'lilypond'
834 file_body = compose_full_body (body, opts)
835 basename = 'lily-' + `abs(hash (file_body))`
837 m = re.search ('filename="(.*?)"', o)
839 basename = m.group (1)
840 if not taken_file_names.has_key(basename):
841 taken_file_names[basename] = 0
843 taken_file_names[basename] = taken_file_names[basename] + 1
844 basename = basename + "-%i" % taken_file_names[basename]
846 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
847 needed_filetypes = ['tex']
850 needed_filetypes.append('eps')
851 needed_filetypes.append('png')
852 if 'eps' in opts and not ('eps' in needed_filetypes):
853 needed_filetypes.append('eps')
854 pathbase = os.path.join (g_outdir, basename)
855 def f(base, ext1, ext2):
856 a = os.path.isfile(base + ext2)
857 if (os.path.isfile(base + ext1) and
858 os.path.isfile(base + ext2) and
859 os.stat(base+ext1)[stat.ST_MTIME] >
860 os.stat(base+ext2)[stat.ST_MTIME]) or \
861 not os.path.isfile(base + ext2):
864 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
866 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
868 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
872 if 'printfilename' in opts:
874 m= re.match ("filename=(.*)", o)
876 newbody = newbody + get_output ("output-filename") % m.group(1)
880 if 'verbatim' in opts:
881 newbody = output_verbatim (body)
884 m = re.search ('intertext="(.*?)"', o)
886 newbody = newbody + m.group (1) + "\n\n"
887 if format == 'latex':
892 else: # format == 'texi'
894 newbody = newbody + get_output (s) % {'fn': basename }
895 return ('lilypond', newbody, opts, todo, basename)
897 def process_lilypond_blocks(outname, chunks):#ugh rename
899 # Count sections/chapters.
901 if c[0] == 'lilypond':
902 c = schedule_lilypond_block (c)
903 elif c[0] == 'numcols':
904 paperguru.m_num_cols = c[2]
909 def find_eps_dims (match):
910 "Fill in dimensions of EPS files."
913 dims = bounding_box_dimensions (fn)
915 fn = os.path.join(g_outdir, fn)
917 return '%ipt' % dims[0]
921 sys.stderr.write ("invoking `%s'\n" % cmd)
924 error ('Error command exited with value %d\n' % st)
927 def compile_all_files (chunks):
934 if c[0] <> 'lilypond':
943 if base + '.ly' not in tex:
944 tex.append (base + '.ly')
945 elif e == 'png' and g_do_pictures:
951 # fixme: be sys-independent.
953 if g_outdir and x[0] <> '/' :
954 x = os.path.join (g_here_dir, x)
957 incs = map (incl_opt, include_path)
958 lilyopts = string.join (incs, ' ' )
960 lilyopts = lilyopts + ' --dependencies '
962 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
963 texfiles = string.join (tex, ' ')
964 system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
967 # Ugh, fixing up dependencies for .tex generation
970 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
972 text=open (i).read ()
973 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
974 open (i, 'w').write (text)
977 system(r"tex '\nonstopmode \input %s'" % e)
978 system(r"dvips -E -o %s %s" % (e + '.eps', e))
980 cmd = r"""gs -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
981 cmd = cmd % (g + '.eps', g + '.png')
986 def update_file (body, name):
988 write the body if it has changed
999 f = open (name , 'w')
1006 def getopt_args (opts):
1007 "Construct arguments (LONG, SHORT) for getopt from list of options."
1012 short = short + o[1]
1020 return (short, long)
1022 def option_help_str (o):
1023 "Transform one option description (4-tuple ) into neatly formatted string"
1041 return ' ' + sh + sep + long + arg
1044 def options_help_str (opts):
1045 "Convert a list of options into a neatly formatted string"
1051 s = option_help_str (o)
1052 strs.append ((s, o[3]))
1058 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1062 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1063 Generate hybrid LaTeX input from Latex + lilypond
1066 sys.stdout.write (options_help_str (option_definitions))
1067 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1071 Report bugs to bug-gnu-music@gnu.org.
1073 Written by Tom Cato Amundsen <tca@gnu.org> and
1074 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1080 def write_deps (fn, target, chunks):
1082 sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1083 f = open (os.path.join(g_outdir, fn), 'w')
1084 f.write ('%s%s: ' % (g_dep_prefix, target))
1085 for d in read_files:
1089 if c[0] == 'lilypond':
1090 (type, body, opts, todo, basename) = c;
1091 basenames.append (basename)
1094 d=g_outdir + '/' + d
1096 #if not os.isfile (d): # thinko?
1097 if not re.search ('/', d):
1098 d = g_dep_prefix + d
1099 f.write ('%s.tex ' % d)
1101 #if len (basenames):
1102 # for d in basenames:
1103 # f.write ('%s.ly ' % d)
1104 # f.write (' : %s' % target)
1110 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1112 def print_version ():
1114 sys.stdout.write (r"""Copyright 1998--1999
1115 Distributed under terms of the GNU General Public License. It comes with
1120 def check_texidoc (chunks):
1123 if c[0] == 'lilypond':
1124 (type, body, opts, todo, basename) = c;
1125 pathbase = os.path.join (g_outdir, basename)
1126 if os.path.isfile (pathbase + '.texidoc'):
1127 body = '\n@include %s.texidoc\n' % basename + body
1128 c = (type, body, opts, todo, basename)
1132 def fix_epswidth (chunks):
1135 if c[0] == 'lilypond' and 'eps' in c[2]:
1136 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", find_eps_dims, c[1])
1137 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1139 newchunks.append (c)
1144 def do_file(input_filename):
1148 my_outname = outname
1150 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1151 my_depname = my_outname + '.dep'
1153 chunks = read_doc_file(input_filename)
1154 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1155 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1156 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1157 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1158 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1159 chunks = chop_chunks(chunks, 'numcols', do_columns)
1161 #for c in chunks: print "c:", c;
1163 scan_preamble(chunks)
1164 chunks = process_lilypond_blocks(my_outname, chunks)
1166 foutn = os.path.join (g_outdir, my_outname + '.' + format)
1169 if __main__.g_run_lilypond:
1170 compile_all_files (chunks)
1171 chunks = fix_epswidth (chunks)
1173 if __main__.format == 'texi':
1174 chunks = check_texidoc (chunks)
1177 chunks = completize_preamble (chunks)
1178 sys.stderr.write ("Writing `%s'\n" % foutn)
1179 fout = open (foutn, 'w')
1186 write_deps (my_depname, foutn, chunks)
1191 (sh, long) = getopt_args (__main__.option_definitions)
1192 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1193 except getopt.error, msg:
1194 sys.stderr.write("error: %s" % msg)
1202 if o == '--include' or o == '-I':
1203 include_path.append (a)
1204 elif o == '--version' or o == '-v':
1207 elif o == '--format' or o == '-f':
1209 elif o == '--outname' or o == '-o':
1212 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1215 elif o == '--help' or o == '-h':
1217 elif o == '--no-lily' or o == '-n':
1218 __main__.g_run_lilypond = 0
1219 elif o == '--dependencies' or o == '-M':
1221 elif o == '--default-music-fontsize':
1222 default_music_fontsize = string.atoi (a)
1223 elif o == '--default-lilypond-fontsize':
1224 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1225 default_music_fontsize = string.atoi (a)
1226 elif o == '--force-music-fontsize':
1227 g_force_lilypond_fontsize = string.atoi(a)
1228 elif o == '--force-lilypond-fontsize':
1229 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1230 g_force_lilypond_fontsize = string.atoi(a)
1231 elif o == '--dep-prefix':
1233 elif o == '--no-pictures':
1235 elif o == '--read-lys':
1237 elif o == '--outdir':
1242 if os.path.isfile(g_outdir):
1243 error ("outdir is a file: %s" % g_outdir)
1244 if not os.path.exists(g_outdir):
1246 setup_environment ()
1247 for input_filename in files:
1248 do_file(input_filename)
1251 # Petr, ik zou willen dat ik iets zinvoller deed,
1252 # maar wat ik kan ik doen, het verandert toch niets?