2 # vim: set noexpandtab:
4 # * junk --outdir for --output
5 # * Figure out clean set of options.
7 # * EndLilyPondOutput is def'd as vfil. Causes large white gaps.
8 # * texinfo: add support for @pagesize
10 # todo: dimension handling (all the x2y) is clumsy. (tca: Thats
11 # because the values are taken directly from texinfo.tex,
12 # geometry.sty and article.cls. Give me a hint, and I'll
16 # TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips.
19 # This is was the idea for handling of comments:
20 # Multiline comments, @ignore .. @end ignore is scanned for
21 # in read_doc_file, and the chunks are marked as 'ignore', so
22 # lilypond-book will not touch them any more. The content of the
23 # chunks are written to the output file. Also 'include' and 'input'
24 # regex has to check if they are commented out.
26 # Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
27 # These three regex's has to check if they are on a commented line,
28 # % for latex, @c for texinfo.
30 # Then lines that are commented out with % (latex) and @c (Texinfo)
31 # are put into chunks marked 'ignore'. This cannot be done before
32 # searching for the lilypond-blocks because % is also the comment character
35 # The the rest of the rexeces are searched for. They don't have to test
36 # if they are on a commented out line.
50 program_version = '@TOPLEVEL_VERSION@'
51 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
52 program_version = '1.5.18'
55 # Try to cater for bad installations of LilyPond, that have
56 # broken TeX setup. Just hope this doesn't hurt good TeX
57 # setups. Maybe we should check if kpsewhich can find
58 # feta16.{afm,mf,tex,tfm}, and only set env upon failure.
62 'MFINPUTS' : datadir + '/mf:',
63 'TEXINPUTS': datadir + '/tex:' + datadir + '/ps:.:',
64 'TFMFONTS' : datadir + '/tfm:',
65 'GS_FONTPATH' : datadir + '/afm:' + datadir + '/pfa',
66 'GS_LIB' : datadir + '/ps',
69 def setup_environment ():
70 for key in environment.keys ():
71 val = environment[key]
72 if os.environ.has_key (key):
73 val = val + os.pathsep + os.environ[key]
78 include_path = [os.getcwd()]
81 # g_ is for global (?)
83 g_here_dir = os.getcwd ()
86 g_force_lilypond_fontsize = 0
94 default_music_fontsize = 16
95 default_text_fontsize = 12
98 # this code is ugly. It should be cleaned
102 # the dimensions are from geometry.sty
103 'a0paper': (mm2pt(841), mm2pt(1189)),
104 'a1paper': (mm2pt(595), mm2pt(841)),
105 'a2paper': (mm2pt(420), mm2pt(595)),
106 'a3paper': (mm2pt(297), mm2pt(420)),
107 'a4paper': (mm2pt(210), mm2pt(297)),
108 'a5paper': (mm2pt(149), mm2pt(210)),
109 'b0paper': (mm2pt(1000), mm2pt(1414)),
110 'b1paper': (mm2pt(707), mm2pt(1000)),
111 'b2paper': (mm2pt(500), mm2pt(707)),
112 'b3paper': (mm2pt(353), mm2pt(500)),
113 'b4paper': (mm2pt(250), mm2pt(353)),
114 'b5paper': (mm2pt(176), mm2pt(250)),
115 'letterpaper': (in2pt(8.5), in2pt(11)),
116 'legalpaper': (in2pt(8.5), in2pt(14)),
117 'executivepaper': (in2pt(7.25), in2pt(10.5))}
118 self.m_use_geometry = None
119 self.m_papersize = 'letterpaper'
123 self.m_geo_landscape = 0
124 self.m_geo_width = None
125 self.m_geo_textwidth = None
126 self.m_geo_lmargin = None
127 self.m_geo_rmargin = None
128 self.m_geo_includemp = None
129 self.m_geo_marginparwidth = {10: 57, 11: 50, 12: 35}
130 self.m_geo_marginparsep = {10: 11, 11: 10, 12: 10}
131 self.m_geo_x_marginparwidth = None
132 self.m_geo_x_marginparsep = None
134 def set_geo_option(self, name, value):
135 if name == 'body' or name == 'text':
136 if type(value) == type(""):
137 self.m_geo_textwidth = value
139 self.m_geo_textwidth = value[0]
141 elif name == 'portrait':
142 self.m_geo_landscape = 0
143 elif name == 'reversemp' or name == 'reversemarginpar':
144 if self.m_geo_includemp == None:
145 self.m_geo_includemp = 1
146 elif name == 'marginparwidth' or name == 'marginpar':
147 self.m_geo_x_marginparwidth = value
148 self.m_geo_includemp = 1
149 elif name == 'marginparsep':
150 self.m_geo_x_marginparsep = value
151 self.m_geo_includemp = 1
152 elif name == 'scale':
153 if type(value) == type(""):
154 self.m_geo_width = self.get_paperwidth() * float(value)
156 self.m_geo_width = self.get_paperwidth() * float(value[0])
157 elif name == 'hscale':
158 self.m_geo_width = self.get_paperwidth() * float(value)
159 elif name == 'left' or name == 'lmargin':
160 self.m_geo_lmargin = value
161 elif name == 'right' or name == 'rmargin':
162 self.m_geo_rmargin = value
163 elif name == 'hdivide' or name == 'divide':
164 if value[0] not in ('*', ''):
165 self.m_geo_lmargin = value[0]
166 if value[1] not in ('*', ''):
167 self.m_geo_width = value[1]
168 if value[2] not in ('*', ''):
169 self.m_geo_rmargin = value[2]
170 elif name == 'hmargin':
171 if type(value) == type(""):
172 self.m_geo_lmargin = value
173 self.m_geo_rmargin = value
175 self.m_geo_lmargin = value[0]
176 self.m_geo_rmargin = value[1]
177 elif name == 'margin':#ugh there is a bug about this option in
178 # the geometry documentation
179 if type(value) == type(""):
180 self.m_geo_lmargin = value
181 self.m_geo_rmargin = value
183 self.m_geo_lmargin = value[0]
184 self.m_geo_rmargin = value[0]
185 elif name == 'total':
186 if type(value) == type(""):
187 self.m_geo_width = value
189 self.m_geo_width = value[0]
190 elif name == 'width' or name == 'totalwidth':
191 self.m_geo_width = value
192 elif name == 'paper' or name == 'papername':
193 self.m_papersize = value
194 elif name[-5:] == 'paper':
195 self.m_papersize = name
198 # what is _set_dimen ?? /MB
199 #self._set_dimen('m_geo_'+name, value)
200 def __setattr__(self, name, value):
201 if type(value) == type("") and \
202 dimension_conversion_dict.has_key (value[-2:]):
203 f = dimension_conversion_dict[value[-2:]]
204 self.__dict__[name] = f(float(value[:-2]))
206 self.__dict__[name] = value
209 s = "LatexPaper:\n-----------"
210 for v in self.__dict__.keys():
212 s = s + str (v) + ' ' + str (self.__dict__[v])
213 s = s + "-----------"
216 def get_linewidth(self):
217 w = self._calc_linewidth()
218 if self.m_num_cols == 2:
222 def get_paperwidth(self):
223 #if self.m_use_geometry:
224 return self.m_paperdef[self.m_papersize][self.m_landscape or self.m_geo_landscape]
225 #return self.m_paperdef[self.m_papersize][self.m_landscape]
227 def _calc_linewidth(self):
228 # since geometry sometimes ignores 'includemp', this is
229 # more complicated than it should be
231 if self.m_geo_includemp:
232 if self.m_geo_x_marginparsep is not None:
233 mp = mp + self.m_geo_x_marginparsep
235 mp = mp + self.m_geo_marginparsep[self.m_fontsize]
236 if self.m_geo_x_marginparwidth is not None:
237 mp = mp + self.m_geo_x_marginparwidth
239 mp = mp + self.m_geo_marginparwidth[self.m_fontsize]
241 #ugh test if this is necessary
245 if not self.m_use_geometry:
246 return latex_linewidths[self.m_papersize][self.m_fontsize]
248 geo_opts = (self.m_geo_lmargin == None,
249 self.m_geo_width == None,
250 self.m_geo_rmargin == None)
252 if geo_opts == (1, 1, 1):
253 if self.m_geo_textwidth:
254 return self.m_geo_textwidth
255 w = self.get_paperwidth() * 0.8
257 elif geo_opts == (0, 1, 1):
258 if self.m_geo_textwidth:
259 return self.m_geo_textwidth
260 return self.f1(self.m_geo_lmargin, mp)
261 elif geo_opts == (1, 1, 0):
262 if self.m_geo_textwidth:
263 return self.m_geo_textwidth
264 return self.f1(self.m_geo_rmargin, mp)
266 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
267 if self.m_geo_textwidth:
268 return self.m_geo_textwidth
269 return self.m_geo_width - mp
270 elif geo_opts in ((0, 1, 0), (0, 0, 0)):
271 w = self.get_paperwidth() \
272 - self.m_geo_lmargin - self.m_geo_rmargin - mp
276 raise "Never do this!"
278 tmp = self.get_paperwidth() - m * 2 - mp
283 tmp = self.get_paperwidth() - self.m_geo_lmargin \
291 self.m_papersize = 'letterpaper'
293 def get_linewidth(self):
294 return texi_linewidths[self.m_papersize][self.m_fontsize]
300 def em2pt(x, fontsize = 10):
301 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
302 def ex2pt(x, fontsize = 10):
303 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
308 dimension_conversion_dict ={
318 # indices are no. of columns, papersize, fontsize
319 # Why can't this be calculated?
321 'a4paper':{10: 345, 11: 360, 12: 390},
322 'a4paper-landscape': {10: 598, 11: 596, 12:592},
323 'a5paper':{10: 276, 11: 276, 12: 276},
324 'b5paper':{10: 345, 11: 356, 12: 356},
325 'letterpaper':{10: 345, 11: 360, 12: 390},
326 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
327 'legalpaper': {10: 345, 11: 360, 12: 390},
328 'executivepaper':{10: 345, 11: 360, 12: 379}}
331 'afourpaper': {12: mm2pt(160)},
332 'afourwide': {12: in2pt(6.5)},
333 'afourlatex': {12: mm2pt(150)},
334 'smallbook': {12: in2pt(5)},
335 'letterpaper': {12: in2pt(6)}}
337 option_definitions = [
338 ('EXT', 'f', 'format', 'set format. EXT is one of texi and latex.'),
339 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
340 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
341 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
342 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
343 ('DIR', 'I', 'include', 'include path'),
344 ('', 'M', 'dependencies', 'write dependencies'),
345 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
346 ('', 'n', 'no-lily', 'don\'t run lilypond'),
347 ('', '', 'no-pictures', "don\'t generate pictures"),
348 ('', '', 'read-lys', "don't write ly files."),
349 ('FILE', 'o', 'outname', 'filename main output file'),
350 ('FILE', '', 'outdir', "where to place generated files"),
351 ('', 'v', 'version', 'print version information' ),
352 ('', 'h', 'help', 'print help'),
355 # format specific strings, ie. regex-es for input, and % strings for output
358 'output-lilypond-fragment' : r"""\begin[eps,singleline,%s]{lilypond}
365 'output-filename' : r'''
368 'output-lilypond': r"""\begin[%s]{lilypond}
371 'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
372 'output-default-post': "\\def\postLilypondExample{}\n",
373 'output-default-pre': "\\def\preLilypondExample{}\n",
374 'usepackage-graphics': '\\usepackage{graphics}\n',
375 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
376 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
377 'pagebreak': r'\pagebreak',
379 'texi' : {'output-lilypond': """@lilypond[%s]
383 'output-filename' : r'''
386 'output-lilypond-fragment': """@lilypond[%s]
387 \context Staff\context Voice{ %s }
390 'output-verbatim': r"""@example
395 # do some tweaking: @ is needed in some ps stuff.
396 # override EndLilyPondOutput, since @tex is done
397 # in a sandbox, you can't do \input lilyponddefs at the
398 # top of the document.
400 # should also support fragment in
406 \def\EndLilyPondOutput{}
412 <a href="%(fn)s.png">
413 <img border=0 src="%(fn)s.png" alt="[picture of music]">
420 def output_verbatim (body):
421 if __main__.format == 'texi':
422 body = re.sub ('([@{}])', '@\\1', body)
423 return get_output ('output-verbatim') % body
427 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
428 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
429 'option-sep' : ',\s*',
430 'header': r"\\documentclass\s*(\[.*?\])?",
431 'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
432 'preamble-end': r'(?P<code>\\begin{document})',
433 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
434 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
435 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
436 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
437 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
438 'def-post-re': r"\\def\\postLilypondExample",
439 'def-pre-re': r"\\def\\preLilypondExample",
440 'usepackage-graphics': r"\usepackage{graphics}",
441 'intertext': r',?\s*intertext=\".*?\"',
442 'multiline-comment': no_match,
443 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
444 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
448 # why do we have distinction between @mbinclude and @include?
450 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
453 'preamble-end': no_match,
454 'landscape': no_match,
455 'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
456 'verb': r"""(?P<code>@code{.*?})""",
457 'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
458 'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
459 'lilypond-block': r"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
460 'option-sep' : ',\s*',
461 'intertext': r',?\s*intertext=\".*?\"',
462 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
463 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
469 for r in re_dict.keys ():
472 for k in olddict.keys ():
473 newdict[k] = re.compile (olddict[k])
487 def get_output (name):
488 return output_dict[format][name]
491 return re_dict[format][name]
493 def bounding_box_dimensions(fname):
495 fname = os.path.join(g_outdir, fname)
499 error ("Error opening `%s'" % fname)
501 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
503 return (int(s.group(3))-int(s.group(1)),
504 int(s.group(4))-int(s.group(2)))
509 sys.stderr.write (str + "\n Exiting ... \n\n")
513 def compose_full_body (body, opts):
514 """Construct the lilypond code to send to Lilypond.
515 Add stuff to BODY using OPTS as options."""
516 music_size = default_music_fontsize
517 latex_size = default_text_fontsize
519 if g_force_lilypond_fontsize:
520 music_size = g_force_lilypond_fontsize
522 m = re.match ('([0-9]+)pt', o)
524 music_size = string.atoi(m.group (1))
526 m = re.match ('latexfontsize=([0-9]+)pt', o)
528 latex_size = string.atoi (m.group (1))
530 if re.search ('\\\\score', body):
534 if 'fragment' in opts:
536 if 'nofragment' in opts:
539 if is_fragment and not 'multiline' in opts:
540 opts.append('singleline')
541 if 'singleline' in opts:
544 l = __main__.paperguru.get_linewidth()
547 m= re.search ('relative(.*)', o)
551 v = string.atoi (m.group (1))
558 pitch = pitch + '\,' * v
560 pitch = pitch + '\'' * v
562 body = '\\relative %s { %s }' %(pitch, body)
571 optstring = string.join (opts, ' ')
572 optstring = re.sub ('\n', ' ', optstring)
574 %% Generated automatically by: lilypond-book.py
576 \include "paper%d.ly"
577 \paper { linewidth = %f \pt }
578 """ % (optstring, music_size, l) + body
580 # ughUGH not original options
583 def parse_options_string(s):
585 r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
586 r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
587 r3 = re.compile("(\w+?)((,\s*)|$)")
592 d[m.group(2)] = re.split(",\s*", m.group(3))
597 d[m.group(2)] = m.group(3)
605 error ("format of option string invalid (was `%')" % s)
608 def scan_latex_preamble(chunks):
609 # first we want to scan the \documentclass line
610 # it should be the first non-comment line
613 if chunks[idx][0] == 'ignore':
616 m = get_re ('header').match(chunks[idx][1])
618 options = re.split (',[\n \t]*', m.group(1)[1:-1])
623 paperguru.m_landscape = 1
624 m = re.match("(.*?)paper", o)
626 paperguru.m_papersize = m.group()
628 m = re.match("(\d\d)pt", o)
630 paperguru.m_fontsize = int(m.group(1))
633 while chunks[idx][0] != 'preamble-end':
634 if chunks[idx] == 'ignore':
637 m = get_re ('geometry').search(chunks[idx][1])
639 paperguru.m_use_geometry = 1
640 o = parse_options_string(m.group('options'))
642 paperguru.set_geo_option(k, o[k])
645 def scan_texi_preamble (chunks):
646 # this is not bulletproof..., it checks the first 10 chunks
647 for c in chunks[:10]:
649 for s in ('afourpaper', 'afourwide', 'letterpaper',
650 'afourlatex', 'smallbook'):
651 if string.find(c[1], "@%s" % s) != -1:
652 paperguru.m_papersize = s
654 def scan_preamble (chunks):
655 if __main__.format == 'texi':
656 scan_texi_preamble(chunks)
658 assert __main__.format == 'latex'
659 scan_latex_preamble(chunks)
662 def completize_preamble (chunks):
663 if __main__.format == 'texi':
665 pre_b = post_b = graphics_b = None
667 if chunk[0] == 'preamble-end':
669 if chunk[0] == 'input':
670 m = get_re('def-pre-re').search(chunk[1])
673 if chunk[0] == 'input':
674 m = get_re('def-post-re').search(chunk[1])
677 if chunk[0] == 'input':
678 m = get_re('usepackage-graphics').search(chunk[1])
682 while chunks[x][0] != 'preamble-end':
685 chunks.insert(x, ('input', get_output ('output-default-pre')))
687 chunks.insert(x, ('input', get_output ('output-default-post')))
689 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
694 def find_file (name):
696 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
701 for a in include_path:
703 nm = os.path.join (a, name)
705 __main__.read_files.append (nm)
710 sys.stderr.write ("Reading `%s'\n" % nm)
711 return (f.read (), nm)
713 error ("File not found `%s'\n" % name)
716 def do_ignore(match_object):
717 return [('ignore', match_object.group('code'))]
718 def do_preamble_end(match_object):
719 return [('preamble-end', match_object.group('code'))]
721 def make_verbatim(match_object):
722 return [('verbatim', match_object.group('code'))]
724 def make_verb(match_object):
725 return [('verb', match_object.group('code'))]
727 def do_include_file(m):
729 return [('input', get_output ('pagebreak'))] \
730 + read_doc_file(m.group('filename')) \
731 + [('input', get_output ('pagebreak'))]
733 def do_input_file(m):
734 return read_doc_file(m.group('filename'))
736 def make_lilypond(m):
737 if m.group('options'):
738 options = m.group('options')
741 return [('input', get_output('output-lilypond-fragment') %
742 (options, m.group('code')))]
744 def make_lilypond_file(m):
747 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
748 into a @lilypond .. @end lilypond block.
752 if m.group('options'):
753 options = m.group('options')
756 (content, nm) = find_file(m.group('filename'))
757 options = "filename=%s," % nm + options
759 return [('input', get_output('output-lilypond') %
762 def make_lilypond_block(m):
763 if m.group('options'):
764 options = get_re('option-sep').split (m.group('options'))
767 options = filter(lambda s: s != '', options)
768 return [('lilypond', m.group('code'), options)]
771 if __main__.format != 'latex':
773 if m.group('num') == 'one':
774 return [('numcols', m.group('code'), 1)]
775 if m.group('num') == 'two':
776 return [('numcols', m.group('code'), 2)]
778 def chop_chunks(chunks, re_name, func, use_match=0):
784 m = get_re (re_name).search (str)
786 newchunks.append (('input', str))
790 newchunks.append (('input', str[:m.start ('match')]))
792 newchunks.append (('input', str[:m.start (0)]))
793 #newchunks.extend(func(m))
794 # python 1.5 compatible:
795 newchunks = newchunks + func(m)
796 str = str [m.end(0):]
801 def determine_format (str):
802 if __main__.format == '':
804 latex = re.search ('\\\\document', str[:200])
805 texinfo = re.search ('@node|@setfilename', str[:200])
810 if texinfo and latex == None:
812 elif latex and texinfo == None:
815 error("error: can't determine format, please specify")
818 if __main__.paperguru == None:
819 if __main__.format == 'texi':
824 __main__.paperguru = g
827 def read_doc_file (filename):
828 """Read the input file, find verbatim chunks and do \input and \include
830 (str, path) = find_file(filename)
831 determine_format (str)
833 chunks = [('input', str)]
835 # we have to check for verbatim before doing include,
836 # because we don't want to include files that are mentioned
837 # inside a verbatim environment
838 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
839 chunks = chop_chunks(chunks, 'verb', make_verb)
840 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
842 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
843 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
847 taken_file_names = {}
848 def schedule_lilypond_block (chunk):
849 """Take the body and options from CHUNK, figure out how the
850 real .ly should look, and what should be left MAIN_STR (meant
851 for the main file). The .ly is written, and scheduled in
854 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
856 TODO has format [basename, extension, extension, ... ]
859 (type, body, opts) = chunk
860 assert type == 'lilypond'
861 file_body = compose_full_body (body, opts)
862 basename = 'lily-' + `abs(hash (file_body))`
864 m = re.search ('filename="(.*?)"', o)
866 basename = m.group (1)
867 if not taken_file_names.has_key(basename):
868 taken_file_names[basename] = 0
870 taken_file_names[basename] = taken_file_names[basename] + 1
871 basename = basename + "-%i" % taken_file_names[basename]
873 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
874 needed_filetypes = ['tex']
877 needed_filetypes.append('eps')
878 needed_filetypes.append('png')
879 if 'eps' in opts and not ('eps' in needed_filetypes):
880 needed_filetypes.append('eps')
881 pathbase = os.path.join (g_outdir, basename)
882 def f(base, ext1, ext2):
883 a = os.path.isfile(base + ext2)
884 if (os.path.isfile(base + ext1) and
885 os.path.isfile(base + ext2) and
886 os.stat(base+ext1)[stat.ST_MTIME] >
887 os.stat(base+ext2)[stat.ST_MTIME]) or \
888 not os.path.isfile(base + ext2):
891 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
893 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
895 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
899 if 'printfilename' in opts:
901 m= re.match ("filename=(.*)", o)
903 newbody = newbody + get_output ("output-filename") % m.group(1)
907 if 'verbatim' in opts:
908 newbody = output_verbatim (body)
911 m = re.search ('intertext="(.*?)"', o)
913 newbody = newbody + m.group (1) + "\n\n"
914 if format == 'latex':
919 else: # format == 'texi'
921 newbody = newbody + get_output (s) % {'fn': basename }
922 return ('lilypond', newbody, opts, todo, basename)
924 def process_lilypond_blocks(outname, chunks):#ugh rename
926 # Count sections/chapters.
928 if c[0] == 'lilypond':
929 c = schedule_lilypond_block (c)
930 elif c[0] == 'numcols':
931 paperguru.m_num_cols = c[2]
938 sys.stderr.write ("invoking `%s'\n" % cmd)
941 error ('Error command exited with value %d\n' % st)
945 def get_bbox (filename):
950 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', l)
952 gr = map (string.atoi, m.groups ())
957 def make_pixmap (name):
958 bbox = get_bbox (name + '.eps')
960 fo = open (name + '.trans.eps' , 'w')
961 fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
966 x = (2* margin + bbox[2] - bbox[0]) * res / 72.
967 y = (2* margin + bbox[3] - bbox[1]) * res / 72.
969 cmd = r"""gs -g%dx%d -sDEVICE=pgm -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s"""
971 cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
973 status = system (cmd)
975 os.unlink (name + '.png')
976 error ("Removing output file")
978 def compile_all_files (chunks):
985 if c[0] <> 'lilypond':
994 if base + '.ly' not in tex:
995 tex.append (base + '.ly')
996 elif e == 'png' and g_do_pictures:
1002 # fixme: be sys-independent.
1004 if g_outdir and x[0] <> '/' :
1005 x = os.path.join (g_here_dir, x)
1008 incs = map (incl_opt, include_path)
1009 lilyopts = string.join (incs, ' ' )
1011 lilyopts = lilyopts + ' --dependencies '
1013 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1014 texfiles = string.join (tex, ' ')
1015 system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
1018 # Ugh, fixing up dependencies for .tex generation
1021 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1026 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1032 system(r"tex '\nonstopmode \input %s'" % e)
1033 system(r"dvips -E -o %s %s" % (e + '.eps', e))
1041 def update_file (body, name):
1043 write the body if it has changed
1054 f = open (name , 'w')
1061 def getopt_args (opts):
1062 "Construct arguments (LONG, SHORT) for getopt from list of options."
1067 short = short + o[1]
1075 return (short, long)
1077 def option_help_str (o):
1078 "Transform one option description (4-tuple ) into neatly formatted string"
1096 return ' ' + sh + sep + long + arg
1099 def options_help_str (opts):
1100 "Convert a list of options into a neatly formatted string"
1106 s = option_help_str (o)
1107 strs.append ((s, o[3]))
1113 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1117 sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1118 Generate hybrid LaTeX input from Latex + lilypond
1121 sys.stdout.write (options_help_str (option_definitions))
1122 sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1126 Report bugs to bug-gnu-music@gnu.org.
1128 Written by Tom Cato Amundsen <tca@gnu.org> and
1129 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1135 def write_deps (fn, target, chunks):
1137 sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1138 f = open (os.path.join(g_outdir, fn), 'w')
1139 f.write ('%s%s: ' % (g_dep_prefix, target))
1140 for d in read_files:
1144 if c[0] == 'lilypond':
1145 (type, body, opts, todo, basename) = c;
1146 basenames.append (basename)
1149 d=g_outdir + '/' + d
1151 #if not os.isfile (d): # thinko?
1152 if not re.search ('/', d):
1153 d = g_dep_prefix + d
1154 f.write ('%s.tex ' % d)
1156 #if len (basenames):
1157 # for d in basenames:
1158 # f.write ('%s.ly ' % d)
1159 # f.write (' : %s' % target)
1165 sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1167 def print_version ():
1169 sys.stdout.write (r"""Copyright 1998--1999
1170 Distributed under terms of the GNU General Public License. It comes with
1175 def check_texidoc (chunks):
1178 if c[0] == 'lilypond':
1179 (type, body, opts, todo, basename) = c;
1180 pathbase = os.path.join (g_outdir, basename)
1181 if os.path.isfile (pathbase + '.texidoc'):
1182 body = '\n@include %s.texidoc\n' % basename + body
1183 c = (type, body, opts, todo, basename)
1188 ## what's this? Docme --hwn
1190 def fix_epswidth (chunks):
1193 if c[0] <> 'lilypond' or 'eps' not in c[2]:
1194 newchunks.append (c)
1199 m = re.match ('magnification=([0-9.]+)', o)
1201 mag = string.atof (m.group (1))
1203 def replace_eps_dim (match, lmag = mag):
1204 filename = match.group (1)
1205 dims = bounding_box_dimensions (filename)
1207 return '%fpt' % (dims[0] *lmag)
1209 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", replace_eps_dim, c[1])
1210 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1216 def do_file(input_filename):
1220 my_outname = outname
1222 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1223 my_depname = my_outname + '.dep'
1225 chunks = read_doc_file(input_filename)
1226 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1227 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1228 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1229 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1230 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1231 chunks = chop_chunks(chunks, 'numcols', do_columns)
1233 #for c in chunks: print "c:", c;
1235 scan_preamble(chunks)
1236 chunks = process_lilypond_blocks(my_outname, chunks)
1238 foutn = os.path.join (g_outdir, my_outname + '.' + format)
1241 if __main__.g_run_lilypond:
1242 compile_all_files (chunks)
1243 chunks = fix_epswidth (chunks)
1245 if __main__.format == 'texi':
1246 chunks = check_texidoc (chunks)
1249 chunks = completize_preamble (chunks)
1250 sys.stderr.write ("Writing `%s'\n" % foutn)
1251 fout = open (foutn, 'w')
1258 write_deps (my_depname, foutn, chunks)
1263 (sh, long) = getopt_args (__main__.option_definitions)
1264 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1265 except getopt.error, msg:
1266 sys.stderr.write("error: %s" % msg)
1274 if o == '--include' or o == '-I':
1275 include_path.append (a)
1276 elif o == '--version' or o == '-v':
1279 elif o == '--format' or o == '-f':
1281 elif o == '--outname' or o == '-o':
1284 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1287 elif o == '--help' or o == '-h':
1289 elif o == '--no-lily' or o == '-n':
1290 __main__.g_run_lilypond = 0
1291 elif o == '--dependencies' or o == '-M':
1293 elif o == '--default-music-fontsize':
1294 default_music_fontsize = string.atoi (a)
1295 elif o == '--default-lilypond-fontsize':
1296 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1297 default_music_fontsize = string.atoi (a)
1298 elif o == '--force-music-fontsize':
1299 g_force_lilypond_fontsize = string.atoi(a)
1300 elif o == '--force-lilypond-fontsize':
1301 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1302 g_force_lilypond_fontsize = string.atoi(a)
1303 elif o == '--dep-prefix':
1305 elif o == '--no-pictures':
1307 elif o == '--read-lys':
1309 elif o == '--outdir':
1314 if os.path.isfile(g_outdir):
1315 error ("outdir is a file: %s" % g_outdir)
1316 if not os.path.exists(g_outdir):
1318 setup_environment ()
1319 for input_filename in files:
1320 do_file(input_filename)
1323 # Petr, ik zou willen dat ik iets zinvoller deed,
1324 # maar wat ik kan ik doen, het verandert toch niets?