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.
47 # Handle bug in Python 1.6-2.1
49 # there are recursion limits for some patterns in Python 1.6 til 2.1.
50 # fix this by importing the 1.5.2 implementation pre instead. Fix by Mats.
52 if float (sys.version[0:3]) < 2.2:
62 # Attempt to fix problems with limited stack size set by Python!
63 # Sets unlimited stack size. Note that the resource module only
64 # is available on UNIX.
67 resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
71 errorport = sys.stderr
78 gettext.bindtextdomain ('lilypond', localedir)
79 gettext.textdomain ('lilypond')
86 errorport.write (s + '\n')
89 program_version = '@TOPLEVEL_VERSION@'
90 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
91 program_version = '1.5.53'
93 # if set, LILYPONDPREFIX must take prevalence
94 # if datadir is not set, we're doing a build and LILYPONDPREFIX
97 if os.environ.has_key ('LILYPONDPREFIX') :
98 datadir = os.environ['LILYPONDPREFIX']
100 datadir = '@datadir@'
102 while datadir[-1] == os.sep:
103 datadir= datadir[:-1]
105 kpse = os.popen ('kpsexpand \$TEXMF').read()
106 kpse = re.sub('[ \t\n]+$','', kpse)
107 type1_paths = os.popen ('kpsewhich -expand-path=\$T1FONTS').read ()
110 # TODO: * prevent multiple addition.
111 # * clean TEXINPUTS, MFINPUTS, TFMFONTS,
112 # as these take prevalence over $TEXMF
113 # and thus may break tex run?
114 'TEXMF' : "{%s,%s}" % (datadir, kpse) ,
115 'GS_FONTPATH' : type1_paths,
116 'GS_LIB' : datadir + '/ps',
119 # tex needs lots of memory, more than it gets by default on Debian
120 non_path_environment = {
121 'extra_mem_top' : '1000000',
122 'extra_mem_bottom' : '1000000',
123 'pool_size' : '250000',
126 def setup_environment ():
127 # $TEXMF is special, previous value is already taken care of
128 if os.environ.has_key ('TEXMF'):
129 del os.environ['TEXMF']
131 for key in environment.keys ():
132 val = environment[key]
133 if os.environ.has_key (key):
134 val = val + os.pathsep + os.environ[key]
135 os.environ[key] = val
137 for key in non_path_environment.keys ():
138 val = non_path_environment[key]
139 os.environ[key] = val
141 include_path = [os.getcwd()]
144 # g_ is for global (?)
146 g_here_dir = os.getcwd ()
149 g_force_music_fontsize = 0
158 default_music_fontsize = 16
159 default_text_fontsize = 12
164 self.m_document_preamble = []
166 def find_latex_dims(self):
168 fname = os.path.join(g_outdir, "lily-tmp.tex")
170 fname = "lily-tmp.tex"
174 error ("Error creating temporary file '%s'" % fname)
175 for s in self.m_document_preamble:
180 \typeout{\columnsep \the\columnsep}
181 \typeout{\textwidth \the\textwidth}
186 re_dim = re.compile(r"\\(\w+)\s+(\d+\.\d+)")
187 p = os.popen("latex lily-tmp.tex")
190 ln = string.strip(ln)
193 if m.groups()[0] in ('textwidth', 'columnsep'):
194 self.__dict__['m_%s' % m.groups()[0]] = float(m.groups()[1])
198 os.remove (os.path.splitext(fname)[0]+".aux")
199 os.remove (os.path.splitext(fname)[0]+".log")
202 def get_linewidth(self):
203 if self.m_num_cols == 2:
204 return (self.m_textwidth-self.m_columnsep)/2
206 return self.m_textwidth
211 self.m_papersize = 'letterpaper'
213 def get_linewidth(self):
214 return html_linewidths[self.m_papersize][self.m_fontsize]
218 self.m_papersize = 'letterpaper'
220 def get_linewidth(self):
221 return texi_linewidths[self.m_papersize][self.m_fontsize]
227 def em2pt(x, fontsize = 10):
228 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
229 def ex2pt(x, fontsize = 10):
230 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
235 dimension_conversion_dict ={
237 'cm': lambda x: mm2pt(10*x),
244 # Convert numeric values, with or without specific dimension, to floats.
246 def conv_dimen_to_float(value):
247 if type(value) == type(""):
248 m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
251 num = string.atof(m.group (1))
252 conv = dimension_conversion_dict[m.group(2)]
256 elif re.match ("^[0-9.]+$",value):
262 'afourpaper': {12: mm2pt(160)},
263 'afourwide': {12: in2pt(6.5)},
264 'afourlatex': {12: mm2pt(150)},
265 'smallbook': {12: in2pt(5)},
266 'letterpaper': {12: in2pt(6)}}
269 'afourpaper': {12: mm2pt(160)},
270 'afourwide': {12: in2pt(6.5)},
271 'afourlatex': {12: mm2pt(150)},
272 'smallbook': {12: in2pt(5)},
273 'letterpaper': {12: in2pt(6)}}
275 option_definitions = [
276 ('EXT', 'f', 'format', 'use output format EXT (texi [default], latex, html)'),
277 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
278 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
279 ('OPT', '', 'extra-options' , 'Pass OPT quoted to the lilypond command line'),
280 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
281 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
282 ('', 'h', 'help', 'this help'),
283 ('DIR', 'I', 'include', 'include path'),
284 ('', 'M', 'dependencies', 'write dependencies'),
285 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
286 ('', 'n', 'no-lily', 'don\'t run lilypond'),
287 ('', '', 'no-pictures', "don\'t generate pictures"),
288 ('', '', 'no-music', "strip all lilypond blocks from output"),
289 ('', '', 'read-lys', "don't write ly files."),
290 ('FILE', 'o', 'outname', 'filename main output file'),
291 ('FILE', '', 'outdir', "where to place generated files"),
292 ('', 'V', 'verbose', 'verbose' ),
293 ('', 'v', 'version', 'print version information' ),
296 # format specific strings, ie. regex-es for input, and % strings for output
298 'html' : {'output-lilypond': '''<lilypond%s>
301 'output-filename' : r'''
304 'output-lilypond-fragment': '''<lilypond%s>
305 \context Staff\context Voice{ %s }
307 'output-noinline': r'''
308 <!-- generated: %(fn)s.png !-->
312 'output-verbatim': r'''<pre>
315 'output-small-verbatim': r'''<font size=-1><pre>
319 ## Ugh we need to differentiate on origin:
320 ## lilypond-block origin wants an extra <p>, but
321 ## inline music doesn't.
322 ## possibly other center options?
324 <a href="%(fn)s.png">
325 <img align="center" valign="center" border="0" src="%(fn)s.png" alt="[picture of music]"></a>
329 'output-lilypond-fragment' : r'''\begin[eps,singleline,%s]{lilypond}
336 'output-filename' : r'''
339 'output-lilypond': r'''\begin[%s]{lilypond}
343 'output-verbatim': r'''\begin{verbatim}%s\end{verbatim}%%
345 'output-small-verbatim': r'''{\small\begin{verbatim}%s\end{verbatim}}%%''',
346 'output-default-post': "\\def\postLilypondExample{}\n",
347 'output-default-pre': "\\def\preLilypondExample{}\n",
348 'usepackage-graphics': '\\usepackage{graphics}\n',
349 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
350 'output-noinline': r'''
351 %% generated: %(fn)s.eps
353 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
354 'pagebreak': r'\pagebreak',
357 'texi' : {'output-lilypond': '''@lilypond[%s]
361 'output-filename' : r'''
364 'output-lilypond-fragment': '''@lilypond[%s]
365 \context Staff\context Voice{ %s }
367 'output-noinline': r'''
368 @c generated: %(fn)s.png
371 'output-small-verbatim': r'''@smallexample
375 'output-verbatim': r'''@example
380 # do some tweaking: @ is needed in some ps stuff.
381 # override EndLilyPondOutput, since @tex is done
382 # in a sandbox, you can't do \input lilyponddefs at the
383 # top of the document.
385 # should also support fragment in
387 # ugh, the <p> below breaks inline images...
393 \def\EndLilyPondOutput{}
399 <a href="%(fn)s.png">
400 <img border=0 src="%(fn)s.png" alt="[picture of music]">
408 def output_verbatim (body, small):
409 if __main__.format == 'html':
410 body = re.sub ('&', '&', body)
411 body = re.sub ('>', '>', body)
412 body = re.sub ('<', '<', body)
413 elif __main__.format == 'texi':
414 body = re.sub ('([@{}])', '@\\1', body)
417 key = 'output-small-verbatim'
419 key = 'output-verbatim'
420 return get_output (key) % body
423 #warning: this uses extended regular expressions. Tread with care.
427 # (?P -- name parameter
428 # *? -- match non-greedily.
435 'preamble-end': no_match,
436 'landscape': no_match,
437 'verbatim': r'''(?s)(?P<code><pre>\s.*?</pre>\s)''',
438 'verb': r'''(?P<code><pre>.*?</pre>)''',
439 'lilypond-file': r'(?m)(?P<match><lilypondfile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</lilypondfile>)',
440 'lilypond' : '(?m)(?P<match><lilypond((?P<options>[^:]*):)(?P<code>.*?)/>)',
441 'lilypond-block': r'''(?ms)(?P<match><lilypond(?P<options>[^>]+)?>(?P<code>.*?)</lilypond>)''',
442 'option-sep' : '\s*',
443 'intertext': r',?\s*intertext=\".*?\"',
444 'multiline-comment': r"(?sm)\s*(?!@c\s+)(?P<code><!--\s.*?!-->)\s",
445 'singleline-comment': no_match,
449 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
450 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
451 'option-sep' : ',\s*',
452 'header': r"\n*\\documentclass\s*(\[.*?\])?",
453 'preamble-end': r'(?P<code>\\begin{document})',
454 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
455 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
456 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
457 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
458 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
459 'def-post-re': r"\\def\\postLilypondExample",
460 'def-pre-re': r"\\def\\preLilypondExample",
461 'usepackage-graphics': r"\usepackage{graphics}",
462 'intertext': r',?\s*intertext=\".*?\"',
463 'multiline-comment': no_match,
464 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
465 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
469 # why do we have distinction between @mbinclude and @include?
473 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
476 'preamble-end': no_match,
477 'landscape': no_match,
478 'verbatim': r'''(?s)(?P<code>@example\s.*?@end example\s)''',
479 'verb': r'''(?P<code>@code{.*?})''',
480 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
481 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
482 'lilypond-block': r'''(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end +lilypond)\s''',
483 'option-sep' : ',\s*',
484 'intertext': r',?\s*intertext=\".*?\"',
485 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
486 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
492 for r in re_dict.keys ():
495 for k in olddict.keys ():
497 newdict[k] = re.compile (olddict[k])
499 print 'invalid regexp: %s' % olddict[k]
501 # we'd like to catch and reraise a more detailed error, but
502 # alas, the exceptions changed across the 1.5/2.1 boundary.
517 def get_output (name):
518 return output_dict[format][name]
521 return re_dict[format][name]
523 def bounding_box_dimensions(fname):
525 fname = os.path.join(g_outdir, fname)
529 error ("Error opening `%s'" % fname)
531 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
534 gs = map (lambda x: string.atoi (x), s.groups ())
535 return (int (gs[2] - gs[0] + 0.5),
536 int (gs[3] - gs[1] + 0.5))
541 sys.stderr.write (str + "\n Exiting ... \n\n")
545 def compose_full_body (body, opts):
546 '''Construct the lilypond code to send to Lilypond.
547 Add stuff to BODY using OPTS as options.'''
548 music_size = default_music_fontsize
549 if g_force_music_fontsize:
550 music_size = g_force_music_fontsize
554 if not g_force_music_fontsize:
555 m = re.match ('([0-9]+)pt', o)
557 music_size = string.atoi(m.group (1))
559 m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o)
561 f = float (m.group (1))
562 indent = 'indent = %f\\%s' % (f, m.group (2))
564 m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o)
566 f = float (m.group (1))
567 linewidth = 'linewidth = %f\\%s' % (f, m.group (2))
569 if re.search ('\\\\score', body):
573 if 'fragment' in opts:
575 if 'nofragment' in opts:
578 if is_fragment and not 'multiline' in opts:
579 opts.append('singleline')
581 if 'singleline' in opts:
582 linewidth = 'linewidth = -1.0'
584 l = __main__.paperguru.get_linewidth ()
585 linewidth = 'linewidth = %f\pt' % l
587 if 'noindent' in opts:
588 indent = 'indent = 0.0\mm'
591 m= re.search ('relative(.*)', o)
595 v = string.atoi (m.group (1))
602 pitch = pitch + '\,' * v
604 pitch = pitch + '\'' * v
606 body = '\\relative %s { %s }' %(pitch, body)
615 optstring = string.join (opts, ' ')
616 optstring = re.sub ('\n', ' ', optstring)
618 %% Generated automatically by: lilypond-book.py
620 \include "paper%d.ly"
625 ''' % (optstring, music_size, linewidth, indent) + body
627 # ughUGH not original options
630 def scan_html_preamble (chunks):
633 def scan_latex_preamble(chunks):
634 # First we want to scan the \documentclass line
635 # it should be the first non-comment line.
636 # The only thing we really need to know about the \documentclass line
637 # is if there are one or two columns to begin with.
640 if chunks[idx][0] == 'ignore':
643 m = get_re ('header').match(chunks[idx][1])
645 error ("Latex documents must start with a \documentclass command")
647 options = re.split (',[\n \t]*', m.group(1)[1:-1])
650 if 'twocolumn' in options:
651 paperguru.m_num_cols = 2
654 # Then we add everythin before \begin{document} to
655 # paperguru.m_document_preamble so that we can later write this header
656 # to a temporary file in find_latex_dims() to find textwidth.
657 while idx < len(chunks) and chunks[idx][0] != 'preamble-end':
658 if chunks[idx] == 'ignore':
661 paperguru.m_document_preamble.append(chunks[idx][1])
663 paperguru.find_latex_dims()
665 def scan_texi_preamble (chunks):
666 # this is not bulletproof..., it checks the first 10 chunks
667 for c in chunks[:10]:
669 for s in ('afourpaper', 'afourwide', 'letterpaper',
670 'afourlatex', 'smallbook'):
671 if string.find(c[1], "@%s" % s) != -1:
672 paperguru.m_papersize = s
675 def scan_preamble (chunks):
676 if __main__.format == 'html':
677 scan_html_preamble (chunks)
678 elif __main__.format == 'latex':
679 scan_latex_preamble (chunks)
680 elif __main__.format == 'texi':
681 scan_texi_preamble (chunks)
684 def completize_preamble (chunks):
685 if __main__.format != 'latex':
687 pre_b = post_b = graphics_b = None
689 if chunk[0] == 'preamble-end':
691 if chunk[0] == 'input':
692 m = get_re('def-pre-re').search(chunk[1])
695 if chunk[0] == 'input':
696 m = get_re('def-post-re').search(chunk[1])
700 if chunk[0] == 'input':
701 m = get_re('usepackage-graphics').search(chunk[1])
705 while x < len (chunks) and chunks[x][0] != 'preamble-end':
712 chunks.insert(x, ('input', get_output ('output-default-pre')))
714 chunks.insert(x, ('input', get_output ('output-default-post')))
716 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
722 def find_file (name):
724 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
728 return (sys.stdin.read (), '<stdin>')
731 for a in include_path:
733 nm = os.path.join (a, name)
735 __main__.read_files.append (nm)
740 sys.stderr.write ("Reading `%s'\n" % nm)
741 return (f.read (), nm)
743 error ("File not found `%s'\n" % name)
746 def do_ignore(match_object):
747 return [('ignore', match_object.group('code'))]
748 def do_preamble_end(match_object):
749 return [('preamble-end', match_object.group('code'))]
751 def make_verbatim(match_object):
752 return [('verbatim', match_object.group('code'))]
754 def make_verb(match_object):
755 return [('verb', match_object.group('code'))]
757 def do_include_file(m):
759 return [('input', get_output ('pagebreak'))] \
760 + read_doc_file(m.group('filename')) \
761 + [('input', get_output ('pagebreak'))]
763 def do_input_file(m):
764 return read_doc_file(m.group('filename'))
766 def make_lilypond(m):
767 if m.group('options'):
768 options = m.group('options')
771 return [('input', get_output('output-lilypond-fragment') %
772 (options, m.group('code')))]
774 def make_lilypond_file(m):
777 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
778 into a @lilypond .. @end lilypond block.
782 if m.group('options'):
783 options = m.group('options')
786 (content, nm) = find_file(m.group('filename'))
787 options = "filename=%s," % nm + options
789 return [('input', get_output('output-lilypond') %
792 def make_lilypond_block(m):
796 if m.group('options'):
797 options = get_re('option-sep').split (m.group('options'))
800 options = filter(lambda s: s != '', options)
801 return [('lilypond', m.group('code'), options)]
804 if __main__.format != 'latex':
806 if m.group('num') == 'one':
807 return [('numcols', m.group('code'), 1)]
808 if m.group('num') == 'two':
809 return [('numcols', m.group('code'), 2)]
811 def chop_chunks(chunks, re_name, func, use_match=0):
817 m = get_re (re_name).search (str)
819 newchunks.append (('input', str))
823 newchunks.append (('input', str[:m.start ('match')]))
825 newchunks.append (('input', str[:m.start (0)]))
826 #newchunks.extend(func(m))
827 # python 1.5 compatible:
828 newchunks = newchunks + func(m)
829 str = str [m.end(0):]
834 def determine_format (str):
835 if __main__.format == '':
837 html = re.search ('(?i)<[dh]tml', str[:200])
838 latex = re.search (r'''\\document''', str[:200])
839 texi = re.search ('@node|@setfilename', str[:200])
844 if html and not latex and not texi:
846 elif latex and not html and not texi:
848 elif texi and not html and not latex:
851 error ("can't determine format, please specify")
854 if __main__.paperguru == None:
855 if __main__.format == 'html':
857 elif __main__.format == 'latex':
859 elif __main__.format == 'texi':
862 __main__.paperguru = g
865 def read_doc_file (filename):
866 '''Read the input file, find verbatim chunks and do \input and \include
868 (str, path) = find_file(filename)
869 determine_format (str)
871 chunks = [('input', str)]
873 # we have to check for verbatim before doing include,
874 # because we don't want to include files that are mentioned
875 # inside a verbatim environment
876 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
877 chunks = chop_chunks(chunks, 'verb', make_verb)
878 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
880 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
881 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
885 taken_file_names = {}
886 def schedule_lilypond_block (chunk):
887 '''Take the body and options from CHUNK, figure out how the
888 real .ly should look, and what should be left MAIN_STR (meant
889 for the main file). The .ly is written, and scheduled in
892 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
894 TODO has format [basename, extension, extension, ... ]
897 (type, body, opts) = chunk
898 assert type == 'lilypond'
899 file_body = compose_full_body (body, opts)
900 ## Hmm, we should hash only lilypond source, and skip the
903 basename = 'lily-' + `abs(hash (file_body))`
905 m = re.search ('filename="(.*?)"', o)
907 basename = m.group (1)
908 if not taken_file_names.has_key(basename):
909 taken_file_names[basename] = 0
911 taken_file_names[basename] = taken_file_names[basename] + 1
912 basename = basename + "-%i" % taken_file_names[basename]
914 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
915 needed_filetypes = ['tex']
917 if format == 'html' or format == 'texi':
918 needed_filetypes.append ('eps')
919 needed_filetypes.append ('png')
920 if 'eps' in opts and not ('eps' in needed_filetypes):
921 needed_filetypes.append('eps')
922 pathbase = os.path.join (g_outdir, basename)
923 def f (base, ext1, ext2):
924 a = os.path.isfile(base + ext2)
925 if (os.path.isfile(base + ext1) and
926 os.path.isfile(base + ext2) and
927 os.stat(base+ext1)[stat.ST_MTIME] >
928 os.stat(base+ext2)[stat.ST_MTIME]) or \
929 not os.path.isfile(base + ext2):
932 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
934 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
936 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
940 if 'printfilename' in opts:
942 m= re.match ("filename=(.*)", o)
944 newbody = newbody + get_output ("output-filename") % m.group(1)
948 if 'smallverbatim' in opts:
949 newbody = output_verbatim (body, 1)
950 elif 'verbatim' in opts:
951 newbody = output_verbatim (body, 0)
954 m = re.search ('intertext="(.*?)"', o)
956 newbody = newbody + m.group (1) + "\n\n"
958 if 'noinline' in opts:
959 s = 'output-noinline'
960 elif format == 'latex':
965 else: # format == 'html' or format == 'texi':
967 newbody = newbody + get_output (s) % {'fn': basename }
968 return ('lilypond', newbody, opts, todo, basename)
970 def process_lilypond_blocks(chunks):#ugh rename
972 # Count sections/chapters.
974 if c[0] == 'lilypond':
975 c = schedule_lilypond_block (c)
976 elif c[0] == 'numcols':
977 paperguru.m_num_cols = c[2]
984 sys.stderr.write ("invoking `%s'\n" % cmd)
987 error ('Error command exited with value %d\n' % st)
990 def quiet_system (cmd, name):
992 progress ( _("Running %s...") % name)
993 cmd = cmd + ' 1> /dev/null 2> /dev/null'
997 def get_bbox (filename):
998 system ('gs -sDEVICE=bbox -q -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename, filename))
1000 box = open (filename + '.bbox').read()
1001 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
1004 gr = map (string.atoi, m.groups ())
1008 def make_pixmap (name):
1009 bbox = get_bbox (name + '.eps')
1011 fo = open (name + '.trans.eps' , 'w')
1012 fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
1017 x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1018 y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1020 cmd = r'''gs -g%dx%d -sDEVICE=pnggray -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit > %s'''
1022 cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
1025 status = quiet_system (cmd, 'gs')
1030 os.unlink (name + '.png')
1031 error ("Removing output file")
1033 def compile_all_files (chunks):
1040 if c[0] <> 'lilypond':
1049 if base + '.ly' not in tex:
1050 tex.append (base + '.ly')
1051 elif e == 'png' and g_do_pictures:
1057 # fixme: be sys-independent.
1059 if g_outdir and x[0] <> '/' :
1060 x = os.path.join (g_here_dir, x)
1063 incs = map (incl_opt, include_path)
1064 lilyopts = string.join (incs, ' ' )
1066 lilyopts = lilyopts + ' --dependencies '
1068 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1069 texfiles = string.join (tex, ' ')
1070 cmd = 'lilypond --header=texidoc %s %s %s' \
1071 % (lilyopts, g_extra_opts, texfiles)
1076 # Ugh, fixing up dependencies for .tex generation
1079 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1084 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1090 cmd = r"echo $TEXMF; tex '\nonstopmode \input %s'" % e
1091 quiet_system (cmd, 'TeX')
1093 cmd = r"dvips -E -o %s %s" % (e + '.eps', e)
1094 quiet_system (cmd, 'dvips')
1102 def update_file (body, name):
1104 write the body if it has changed
1115 f = open (name , 'w')
1122 def getopt_args (opts):
1123 "Construct arguments (LONG, SHORT) for getopt from list of options."
1128 short = short + o[1]
1136 return (short, long)
1138 def option_help_str (o):
1139 "Transform one option description (4-tuple ) into neatly formatted string"
1157 return ' ' + sh + sep + long + arg
1160 def options_help_str (opts):
1161 "Convert a list of options into a neatly formatted string"
1167 s = option_help_str (o)
1168 strs.append ((s, o[3]))
1174 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1178 sys.stdout.write('''Usage: lilypond-book [options] FILE\n
1179 Generate hybrid LaTeX input from Latex + lilypond
1182 sys.stdout.write (options_help_str (option_definitions))
1183 sys.stdout.write (r'''Warning all output is written in the CURRENT directory
1187 Report bugs to bug-lilypond@gnu.org.
1189 Written by Tom Cato Amundsen <tca@gnu.org> and
1190 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1196 def write_deps (fn, target, chunks):
1198 sys.stderr.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1199 f = open (os.path.join(g_outdir, fn), 'w')
1200 f.write ('%s%s: ' % (g_dep_prefix, target))
1201 for d in read_files:
1205 if c[0] == 'lilypond':
1206 (type, body, opts, todo, basename) = c;
1207 basenames.append (basename)
1210 d=g_outdir + '/' + d
1212 #if not os.isfile (d): # thinko?
1213 if not re.search ('/', d):
1214 d = g_dep_prefix + d
1215 f.write ('%s.tex ' % d)
1217 #if len (basenames):
1218 # for d in basenames:
1219 # f.write ('%s.ly ' % d)
1220 # f.write (' : %s' % target)
1225 def identify (stream):
1226 stream.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1228 def print_version ():
1229 identify (sys.stdout)
1230 sys.stdout.write (r'''Copyright 1998--1999
1231 Distributed under terms of the GNU General Public License. It comes with
1236 def check_texidoc (chunks):
1239 if c[0] == 'lilypond':
1240 (type, body, opts, todo, basename) = c;
1241 pathbase = os.path.join (g_outdir, basename)
1242 if os.path.isfile (pathbase + '.texidoc'):
1243 body = '\n@include %s.texidoc\n' % basename + body
1244 c = (type, body, opts, todo, basename)
1249 ## what's this? Docme --hwn
1251 def fix_epswidth (chunks):
1254 if c[0] <> 'lilypond' or 'eps' not in c[2]:
1255 newchunks.append (c)
1260 m = re.match ('magnification=([0-9.]+)', o)
1262 mag = string.atof (m.group (1))
1264 def replace_eps_dim (match, lmag = mag):
1265 filename = match.group (1)
1266 dims = bounding_box_dimensions (filename)
1268 return '%fpt' % (dims[0] *lmag)
1270 body = re.sub (r'''\\lilypondepswidth{(.*?)}''', replace_eps_dim, c[1])
1271 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1276 ##docme: why global?
1278 def do_file(input_filename):
1280 chunks = read_doc_file(input_filename)
1281 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1282 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1283 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1284 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1285 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1286 chunks = chop_chunks(chunks, 'numcols', do_columns)
1288 #for c in chunks: print "c:", c;
1290 scan_preamble(chunks)
1291 chunks = process_lilypond_blocks(chunks)
1294 if __main__.g_run_lilypond:
1295 compile_all_files (chunks)
1296 chunks = fix_epswidth (chunks)
1298 if __main__.format == 'texi':
1299 chunks = check_texidoc (chunks)
1302 chunks = completize_preamble (chunks)
1308 my_outname = outname
1309 elif input_filename == '-' or input_filename == "/dev/stdin":
1312 my_outname = os.path.basename (os.path.splitext(input_filename)[0]) + '.' + format
1313 my_depname = my_outname + '.dep'
1315 if my_outname == '-' or my_outname == '/dev/stdout':
1318 __main__.do_deps = 0
1320 foutn = os.path.join (g_outdir, my_outname)
1321 sys.stderr.write ("Writing `%s'\n" % foutn)
1322 fout = open (foutn, 'w')
1329 write_deps (my_depname, foutn, chunks)
1333 (sh, long) = getopt_args (__main__.option_definitions)
1334 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1335 except getopt.error, msg:
1336 sys.stderr.write("error: %s" % msg)
1344 if o == '--include' or o == '-I':
1345 include_path.append (a)
1346 elif o == '--version' or o == '-v':
1349 elif o == '--verbose' or o == '-V':
1350 __main__.verbose_p = 1
1351 elif o == '--format' or o == '-f':
1353 elif o == '--outname' or o == '-o':
1356 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1359 elif o == '--help' or o == '-h':
1361 elif o == '--no-lily' or o == '-n':
1362 __main__.g_run_lilypond = 0
1363 elif o == '--dependencies' or o == '-M':
1365 elif o == '--default-music-fontsize':
1366 default_music_fontsize = string.atoi (a)
1367 elif o == '--default-lilypond-fontsize':
1368 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1369 default_music_fontsize = string.atoi (a)
1370 elif o == '--extra-options':
1372 elif o == '--force-music-fontsize':
1373 g_force_music_fontsize = string.atoi(a)
1374 elif o == '--force-lilypond-fontsize':
1375 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1376 g_force_music_fontsize = string.atoi(a)
1377 elif o == '--dep-prefix':
1379 elif o == '--no-pictures':
1381 elif o == '--no-music':
1383 elif o == '--read-lys':
1385 elif o == '--outdir':
1388 identify (sys.stderr)
1390 if os.path.isfile(g_outdir):
1391 error ("outdir is a file: %s" % g_outdir)
1392 if not os.path.exists(g_outdir):
1394 setup_environment ()
1395 for input_filename in files:
1396 do_file(input_filename)
1399 # Petr, ik zou willen dat ik iets zinvoller deed,
1400 # maar wat ik kan ik doen, het verandert toch niets?