2 # vim: set noexpandtab:
4 # * junk --outdir for --output
5 # * Figure out clean set of options.
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
15 # TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips.
18 # This is was the idea for handling of comments:
19 # Multiline comments, @ignore .. @end ignore is scanned for
20 # in read_doc_file, and the chunks are marked as 'ignore', so
21 # lilypond-book will not touch them any more. The content of the
22 # chunks are written to the output file. Also 'include' and 'input'
23 # regex has to check if they are commented out.
25 # Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
26 # These three regex's has to check if they are on a commented line,
27 # % for latex, @c for texinfo.
29 # Then lines that are commented out with % (latex) and @c (Texinfo)
30 # are put into chunks marked 'ignore'. This cannot be done before
31 # searching for the lilypond-blocks because % is also the comment character
34 # The the rest of the rexeces are searched for. They don't have to test
35 # if they are on a commented out line.
46 # Handle bug in Python 1.6-2.1
48 # there are recursion limits for some patterns in Python 1.6 til 2.1.
49 # fix this by importing the 1.5.2 implementation pre instead. Fix by Mats.
52 ## We would like to do this for python 2.2 as well, unfortunately
53 ## python 2.2 has another bug, see Sf.net bugtracker
55 ## https://sourceforge.net/tracker/?func=detail&aid=604803&group_id=5470&atid=105470
66 if float (sys.version[0:3]) <= 2.1:
67 ## or sys.version[0:5] == '2.2.1':
68 ## still broken on python 2.2.1 / RH8.
79 # Attempt to fix problems with limited stack size set by Python!
80 # Sets unlimited stack size. Note that the resource module only
81 # is available on UNIX.
84 resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
88 errorport = sys.stderr
95 gettext.bindtextdomain ('lilypond', localedir)
96 gettext.textdomain ('lilypond')
103 errorport.write (s + '\n')
106 program_version = '@TOPLEVEL_VERSION@'
107 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
108 program_version = '1.6.0'
110 # if set, LILYPONDPREFIX must take prevalence
111 # if datadir is not set, we're doing a build and LILYPONDPREFIX
112 datadir = '@local_lilypond_datadir@'
114 if os.environ.has_key ('LILYPONDPREFIX') :
115 datadir = os.environ['LILYPONDPREFIX']
117 datadir = '@local_lilypond_datadir@'
119 while datadir[-1] == os.sep:
120 datadir= datadir[:-1]
122 kpse = os.popen ('kpsexpand \$TEXMF').read()
123 kpse = re.sub('[ \t\n]+$','', kpse)
124 type1_paths = os.popen ('kpsewhich -expand-path=\$T1FONTS').read ()
127 #binary = 'valgrind --suppressions=/home/hanwen/usr/src/guile-1.6.supp --num-callers=10 /home/hanwen/usr/src/lilypond/lily/out/lilypond'
129 # TODO: * prevent multiple addition.
130 # * clean TEXINPUTS, MFINPUTS, TFMFONTS,
131 # as these take prevalence over $TEXMF
132 # and thus may break tex run?
133 'TEXMF' : "{%s,%s}" % (datadir, kpse) ,
134 'GS_FONTPATH' : type1_paths,
135 'GS_LIB' : datadir + '/ps',
138 # tex needs lots of memory, more than it gets by default on Debian
139 non_path_environment = {
140 'extra_mem_top' : '1000000',
141 'extra_mem_bottom' : '1000000',
142 'pool_size' : '250000',
145 def setup_environment ():
146 # $TEXMF is special, previous value is already taken care of
147 if os.environ.has_key ('TEXMF'):
148 del os.environ['TEXMF']
150 for key in environment.keys ():
151 val = environment[key]
152 if os.environ.has_key (key):
153 val = val + os.pathsep + os.environ[key]
154 os.environ[key] = val
156 for key in non_path_environment.keys ():
157 val = non_path_environment[key]
158 os.environ[key] = val
160 include_path = [os.getcwd()]
163 # g_ is for global (?)
165 g_here_dir = os.getcwd ()
168 g_force_music_fontsize = 0
178 default_music_fontsize = 16
179 default_text_fontsize = 12
184 self.m_document_preamble = []
188 def find_latex_dims(self):
190 fname = os.path.join(g_outdir, "lily-tmp.tex")
192 fname = "lily-tmp.tex"
196 error ("Error creating temporary file '%s'" % fname)
198 for s in self.m_document_preamble:
203 \typeout{\columnsep \the\columnsep}
204 \typeout{\textwidth \the\textwidth}
209 re_dim = re.compile(r"\\(\w+)\s+(\d+\.\d+)")
211 cmd = "latex '\\nonstopmode \input %s'" % fname
213 sys.stderr.write ("Invoking `%s' as pipe" % cmd)
215 status = quiet_system (cmd, "Latex for finding dimensions")
217 sys.stderr.write (_("Invoking LaTeX failed.") + '\n' )
218 sys.stderr.write (_("This is the error log:\n") + '\n')
220 lns = open ('lily-tmp.log').readlines()
224 sys.stderr.write (ln)
225 if re.match('^!', ln):
232 countdown = countdown -1
234 sys.stderr.write (" ... (further messages elided)...\n")
237 lns = open ('lily-tmp.log').readlines()
239 ln = string.strip(ln)
242 if m.groups()[0] in ('textwidth', 'columnsep'):
243 self.__dict__['m_%s' % m.groups()[0]] = float(m.groups()[1])
247 os.remove (os.path.splitext(fname)[0]+".aux")
248 os.remove (os.path.splitext(fname)[0]+".log")
252 if not self.__dict__.has_key ('m_textwidth'):
255 def get_linewidth(self):
256 if self.m_num_cols == 1:
259 w = (self.m_textwidth - self.m_columnsep)/2
260 if self.m_multicols > 1:
261 return (w - self.m_columnsep*(self.m_multicols-1)) \
268 self.m_papersize = 'letterpaper'
270 def get_linewidth(self):
271 return html_linewidths[self.m_papersize][self.m_fontsize]
275 self.m_papersize = 'letterpaper'
277 def get_linewidth(self):
278 return texi_linewidths[self.m_papersize][self.m_fontsize]
284 def em2pt(x, fontsize = 10):
285 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
286 def ex2pt(x, fontsize = 10):
287 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
292 dimension_conversion_dict ={
294 'cm': lambda x: mm2pt(10*x),
301 # Convert numeric values, with or without specific dimension, to floats.
303 def conv_dimen_to_float(value):
304 if type(value) == type(""):
305 m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
308 num = string.atof(m.group (1))
309 conv = dimension_conversion_dict[m.group(2)]
313 elif re.match ("^[0-9.]+$",value):
319 'afourpaper': {12: mm2pt(160)},
320 'afourwide': {12: in2pt(6.5)},
321 'afourlatex': {12: mm2pt(150)},
322 'smallbook': {12: in2pt(5)},
323 'letterpaper': {12: in2pt(6)}}
326 'afourpaper': {12: mm2pt(160)},
327 'afourwide': {12: in2pt(6.5)},
328 'afourlatex': {12: mm2pt(150)},
329 'smallbook': {12: in2pt(5)},
330 'letterpaper': {12: in2pt(6)}}
332 option_definitions = [
333 ('EXT', 'f', 'format', 'use output format EXT (texi [default], texi-html, latex, html)'),
334 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
335 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
336 ('OPT', '', 'extra-options' , 'pass OPT quoted to the lilypond command line'),
337 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
338 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
339 ('', 'h', 'help', 'this help'),
340 ('DIR', 'I', 'include', 'include path'),
341 ('', 'M', 'dependencies', 'write dependencies'),
342 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
343 ('', 'n', 'no-lily', 'don\'t run lilypond'),
344 ('', '', 'no-pictures', "don\'t generate pictures"),
345 ('', '', 'no-music', "strip all lilypond blocks from output"),
346 ('', '', 'read-lys', "don't write ly files."),
347 ('FILE', 'o', 'outname', 'filename main output file'),
348 ('FILE', '', 'outdir', "where to place generated files"),
349 ('', 'V', 'verbose', 'verbose' ),
350 ('', 'v', 'version', 'print version information' ),
353 # format specific strings, ie. regex-es for input, and % strings for output
359 'output-lilypond': '''<lilypond%s>
362 'output-filename' : r'''
365 <pre>%s</pre></a>:''',
366 'output-lilypond-fragment': '''<lilypond%s>
367 \context Staff\context Voice{ %s }
369 'output-noinline': r'''
370 <!-- generated: %(fn)s.png !-->
374 # Verbatim text is always finished with \n. FIXME: For HTML,
375 # this newline should be removed.
376 'output-verbatim': r'''<pre>
378 # Verbatim text is always finished with \n. FIXME: For HTML,
379 # this newline should be removed.
380 'output-small-verbatim': r'''<font size=-1><pre>
382 ## Ugh we need to differentiate on origin:
383 ## lilypond-block origin wants an extra <p>, but
384 ## inline music doesn't.
385 ## possibly other center options?
387 <a href="%(fn)s.png">
388 <img align="center" valign="center" border="0" src="%(fn)s.png" alt="[picture of music]"></a>
395 'output-lilypond-fragment' : r'''\begin[eps,singleline,%s]{lilypond}
402 'output-filename' : r'''\verb+%s+:\\
406 'output-lilypond': r'''\begin[%s]{lilypond}
410 # verbatim text is always finished with \n
411 'output-verbatim': r'''\begin{verbatim}
414 # verbatim text is always finished with \n
415 'output-small-verbatim': r'''{\small\begin{verbatim}
418 'output-default-post': "\\def\postLilypondExample{}\n",
419 'output-default-pre': "\\def\preLilypondExample{}\n",
420 'usepackage-graphics': '\\usepackage{graphics}\n',
421 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s}}',
422 'output-noinline': r'''
423 %% generated: %(fn)s.eps
425 'output-latex-quoted': r'''{\preLilypondExample
427 \postLilypondExample}''',
428 'output-latex-noquote': r'''{\parindent 0pt
431 \postLilypondExample}''',
432 'pagebreak': r'\pagebreak',
438 'output-lilypond': '''@lilypond[%s]
442 'output-filename' : r'''@ifnothtml
449 'output-lilypond-fragment': '''@lilypond[%s]
450 \context Staff\context Voice{ %s }
452 'output-noinline': r'''
453 @c generated: %(fn)s.png
456 # verbatim text is always finished with \n
457 'output-small-verbatim': r'''@smallexample
460 # verbatim text is always finished with \n
461 'output-verbatim': r'''@example
464 # do some tweaking: @ is needed in some ps stuff.
466 # ugh, the <p> below breaks inline images...
467 'output-texi-noquote': r'''@tex
475 <p><a href="%(fn)s.png">
476 <img border=0 src="%(fn)s.png" alt="[picture of music]">
480 'output-texi-quoted': r'''@quotation
488 <a href="%(fn)s.png">
489 <img border=0 src="%(fn)s.png" alt="[picture of music]">
498 def output_verbatim (body, small):
499 if __main__.format == 'html':
500 body = re.sub ('&', '&', body)
501 body = re.sub ('>', '>', body)
502 body = re.sub ('<', '<', body)
503 elif __main__.format == 'texi':
504 # clumsy workaround for python 2.2 pre bug.
505 body = re.sub ('@', '@@', body)
506 body = re.sub ('{', '@{', body)
507 body = re.sub ('}', '@}', body)
510 key = 'output-small-verbatim'
512 key = 'output-verbatim'
513 return get_output (key) % body
516 # Warning: This uses extended regular expressions. Treat with care.
520 # (?P<name>regex) -- assign result of REGEX to NAME
521 # *? -- match non-greedily.
522 # (?m) -- multiline regex: make ^ and $ match at each line
523 # (?s) -- make the dot match all characters including newline
529 'preamble-end': no_match,
530 'landscape': no_match,
531 'verbatim': r'''(?s)(?P<code><pre>\s.*?</pre>\s)''',
532 'verb': r'''(?P<code><pre>.*?</pre>)''',
533 'lilypond-file': r'(?m)(?P<match><lilypondfile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</lilypondfile>)',
534 'lilypond' : '(?m)(?P<match><lilypond((?P<options>[^:]*):)(?P<code>.*?)/>)',
535 'lilypond-block': r'''(?ms)(?P<match><lilypond(?P<options>[^>]+)?>(?P<code>.*?)</lilypond>)''',
536 'option-sep' : '\s*',
537 'intertext': r',?\s*intertext=\".*?\"',
538 'multiline-comment': r"(?sm)\s*(?!@c\s+)(?P<code><!--\s.*?!-->)\s",
539 'singleline-comment': no_match,
541 'multicols': no_match,
545 'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
546 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
547 'option-sep' : ',\s*',
548 'header': r"\n*\\documentclass\s*(\[.*?\])?",
549 'preamble-end': r'(?P<code>\\begin\s*{document})',
550 'verbatim': r"(?s)(?P<code>\\begin\s*{verbatim}.*?\\end{verbatim})",
551 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
552 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
553 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
554 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
555 'def-post-re': r"\\def\\postLilypondExample",
556 'def-pre-re': r"\\def\\preLilypondExample",
557 'usepackage-graphics': r"\usepackage\s*{graphics}",
558 'intertext': r',?\s*intertext=\".*?\"',
559 'multiline-comment': no_match,
560 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
561 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
562 'multicols': r"(?P<code>\\(?P<be>begin|end)\s*{multicols}({(?P<num>\d+)?})?)",
565 # why do we have distinction between @mbinclude and @include?
568 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
571 'preamble-end': no_match,
572 'landscape': no_match,
573 'verbatim': r'''(?s)(?P<code>@example\s.*?@end example\s)''',
574 'verb': r'''(?P<code>@code{.*?})''',
575 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
576 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
577 'lilypond-block': r'''(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end +lilypond)\s''',
578 'option-sep' : ',\s*',
579 'intertext': r',?\s*intertext=\".*?\"',
580 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
581 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
583 'multicols': no_match,
588 for r in re_dict.keys ():
591 for k in olddict.keys ():
593 newdict[k] = re.compile (olddict[k])
595 print 'invalid regexp: %s' % olddict[k]
597 # we'd like to catch and reraise a more detailed error, but
598 # alas, the exceptions changed across the 1.5/2.1 boundary.
613 def get_output (name):
614 return output_dict[format][name]
617 return re_dict[format][name]
619 def bounding_box_dimensions(fname):
621 fname = os.path.join(g_outdir, fname)
625 error ("Error opening `%s'" % fname)
627 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
630 gs = map (lambda x: string.atoi (x), s.groups ())
631 return (int (gs[2] - gs[0] + 0.5),
632 int (gs[3] - gs[1] + 0.5))
637 sys.stderr.write ("\n\n" + str + "\nExiting ... \n\n")
641 def compose_full_body (body, opts):
642 '''Construct the lilypond code to send to Lilypond.
643 Add stuff to BODY using OPTS as options.'''
644 music_size = default_music_fontsize
645 if g_force_music_fontsize:
646 music_size = g_force_music_fontsize
651 if not g_force_music_fontsize:
652 m = re.match ('([0-9]+)pt', o)
654 music_size = string.atoi(m.group (1))
656 m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o)
658 f = float (m.group (1))
659 indent = 'indent = %f\\%s' % (f, m.group (2))
661 m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o)
663 f = float (m.group (1))
664 linewidth = 'linewidth = %f\\%s' % (f, m.group (2))
666 if re.search ('\\\\score', body):
670 if 'fragment' in opts:
672 if 'nofragment' in opts:
675 if is_fragment and not 'multiline' in opts:
676 opts.append('singleline')
678 if 'singleline' in opts:
680 linewidth = 'linewidth = -1.0'
682 indent = 'indent = 0.0\mm'
684 l = __main__.paperguru.get_linewidth ()
685 linewidth = 'linewidth = %f\pt' % l
687 if 'noindent' in opts:
688 indent = 'indent = 0.0\mm'
694 \remove Time_signature_engraver
699 m= re.search ('relative(.*)', o)
703 v = string.atoi (m.group (1))
710 pitch = pitch + '\,' * v
712 pitch = pitch + '\'' * v
714 body = '\\relative %s { %s }' %(pitch, body)
726 optstring = string.join (opts, ' ')
727 optstring = re.sub ('\n', ' ', optstring)
729 %% Generated automatically by: lilypond-book.py
731 \include "paper%d.ly"
737 ''' % (optstring, music_size, linewidth, indent, notime) + body
739 # ughUGH not original options
742 def scan_html_preamble (chunks):
745 def scan_latex_preamble(chunks):
746 # First we want to scan the \documentclass line
747 # it should be the first non-comment line.
748 # The only thing we really need to know about the \documentclass line
749 # is if there are one or two columns to begin with.
752 if chunks[idx][0] == 'ignore':
755 m = get_re ('header').match(chunks[idx][1])
757 error ("Latex documents must start with a \documentclass command")
759 options = re.split (',[\n \t]*', m.group(1)[1:-1])
762 if 'twocolumn' in options:
763 paperguru.m_num_cols = 2
767 # Then we add everything before \begin{document} to
768 # paperguru.m_document_preamble so that we can later write this header
769 # to a temporary file in find_latex_dims() to find textwidth.
770 while idx < len(chunks) and chunks[idx][0] != 'preamble-end':
771 if chunks[idx] == 'ignore':
774 paperguru.m_document_preamble.append(chunks[idx][1])
777 if len(chunks) == idx:
778 error ("Didn't find end of preamble (\\begin{document})")
780 paperguru.find_latex_dims()
782 def scan_texi_preamble (chunks):
783 # this is not bulletproof..., it checks the first 10 chunks
784 for c in chunks[:10]:
786 for s in ('afourpaper', 'afourwide', 'letterpaper',
787 'afourlatex', 'smallbook'):
788 if string.find(c[1], "@%s" % s) != -1:
789 paperguru.m_papersize = s
792 def scan_preamble (chunks):
793 if __main__.format == 'html':
794 scan_html_preamble (chunks)
795 elif __main__.format == 'latex':
796 scan_latex_preamble (chunks)
797 elif __main__.format == 'texi':
798 scan_texi_preamble (chunks)
801 def completize_preamble (chunks):
802 if __main__.format != 'latex':
804 pre_b = post_b = graphics_b = None
806 if chunk[0] == 'preamble-end':
808 if chunk[0] == 'input':
809 m = get_re('def-pre-re').search(chunk[1])
812 if chunk[0] == 'input':
813 m = get_re('def-post-re').search(chunk[1])
817 if chunk[0] == 'input':
818 m = get_re('usepackage-graphics').search(chunk[1])
822 while x < len (chunks) and chunks[x][0] != 'preamble-end':
829 chunks.insert(x, ('input', get_output ('output-default-pre')))
831 chunks.insert(x, ('input', get_output ('output-default-post')))
833 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
839 def find_file (name):
841 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
845 return (sys.stdin.read (), '<stdin>')
848 for a in include_path:
850 nm = os.path.join (a, name)
852 __main__.read_files.append (nm)
857 sys.stderr.write ("Reading `%s'\n" % nm)
858 return (f.read (), nm)
860 error ("File not found `%s'\n" % name)
863 def do_ignore(match_object):
864 return [('ignore', match_object.group('code'))]
865 def do_preamble_end(match_object):
866 return [('preamble-end', match_object.group('code'))]
868 def make_verbatim(match_object):
869 return [('verbatim', match_object.group('code'))]
871 def make_verb(match_object):
872 return [('verb', match_object.group('code'))]
874 def do_include_file(m):
876 return [('input', get_output ('pagebreak'))] \
877 + read_doc_file(m.group('filename')) \
878 + [('input', get_output ('pagebreak'))]
880 def do_input_file(m):
881 return read_doc_file(m.group('filename'))
883 def make_lilypond(m):
884 if m.group('options'):
885 options = m.group('options')
888 return [('input', get_output('output-lilypond-fragment') %
889 (options, m.group('code')))]
891 def make_lilypond_file(m):
894 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
895 into a @lilypond .. @end lilypond block.
899 if m.group('options'):
900 options = m.group('options')
903 (content, nm) = find_file(m.group('filename'))
904 options = "filename=%s," % nm + options
906 return [('input', get_output('output-lilypond') %
909 def make_lilypond_block(m):
913 if m.group('options'):
914 options = get_re('option-sep').split (m.group('options'))
917 options = filter(lambda s: s != '', options)
918 return [('lilypond', m.group('code'), options)]
921 if __main__.format != 'latex':
923 if m.group('num') == 'one':
924 return [('numcols', m.group('code'), 1)]
925 if m.group('num') == 'two':
926 return [('numcols', m.group('code'), 2)]
929 if __main__.format != 'latex':
931 if m.group('be') == 'begin':
932 return [('multicols', m.group('code'), int(m.group('num')))]
934 return [('multicols', m.group('code'), 1)]
937 def chop_chunks(chunks, re_name, func, use_match=0):
943 m = get_re (re_name).search (str)
945 newchunks.append (('input', str))
949 newchunks.append (('input', str[:m.start ('match')]))
951 newchunks.append (('input', str[:m.start (0)]))
952 #newchunks.extend(func(m))
953 # python 1.5 compatible:
954 newchunks = newchunks + func(m)
955 str = str [m.end(0):]
960 def determine_format (str):
961 if __main__.format == '':
963 html = re.search ('(?i)<[dh]tml', str[:200])
964 latex = re.search (r'''\\document''', str[:200])
965 texi = re.search ('@node|@setfilename', str[:200])
970 if html and not latex and not texi:
972 elif latex and not html and not texi:
974 elif texi and not html and not latex:
977 error ("can't determine format, please specify")
980 if __main__.paperguru == None:
981 if __main__.format == 'html':
983 elif __main__.format == 'latex':
985 elif __main__.format == 'texi':
988 __main__.paperguru = g
991 def read_doc_file (filename):
992 '''Read the input file, find verbatim chunks and do \input and \include
994 (str, path) = find_file(filename)
995 determine_format (str)
997 chunks = [('input', str)]
999 # we have to check for verbatim before doing include,
1000 # because we don't want to include files that are mentioned
1001 # inside a verbatim environment
1002 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
1004 chunks = chop_chunks(chunks, 'verb', make_verb)
1005 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
1007 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
1008 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
1012 taken_file_names = {}
1013 def schedule_lilypond_block (chunk):
1014 '''Take the body and options from CHUNK, figure out how the
1015 real .ly should look, and what should be left MAIN_STR (meant
1016 for the main file). The .ly is written, and scheduled in
1019 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
1021 TODO has format [basename, extension, extension, ... ]
1023 (type, body, opts) = chunk
1024 assert type == 'lilypond'
1025 file_body = compose_full_body (body, opts)
1026 ## Hmm, we should hash only lilypond source, and skip the
1029 basename = 'lily-' + `abs(hash (file_body))`
1031 m = re.search ('filename="(.*?)"', o)
1033 basename = m.group (1)
1034 if not taken_file_names.has_key(basename):
1035 taken_file_names[basename] = 0
1037 taken_file_names[basename] = taken_file_names[basename] + 1
1038 basename = basename + "-%i" % taken_file_names[basename]
1040 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
1041 needed_filetypes = ['tex']
1043 if format == 'html' or g_make_html:
1044 needed_filetypes.append ('eps')
1045 needed_filetypes.append ('png')
1046 if 'eps' in opts and not ('eps' in needed_filetypes):
1047 needed_filetypes.append('eps')
1048 pathbase = os.path.join (g_outdir, basename)
1049 def f (base, ext1, ext2):
1050 a = os.path.isfile(base + ext2)
1051 if (os.path.isfile(base + ext1) and
1052 os.path.isfile(base + ext2) and
1053 os.stat(base+ext1)[stat.ST_MTIME] >
1054 os.stat(base+ext2)[stat.ST_MTIME]) or \
1055 not os.path.isfile(base + ext2):
1058 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
1060 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
1062 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
1066 if 'printfilename' in opts:
1068 m= re.match ("filename=(.*)", o)
1070 newbody = newbody + get_output ("output-filename") % (m.group(1), basename + '.ly', m.group(1))
1074 if 'smallverbatim' in opts:
1075 newbody = newbody + output_verbatim (body, 1)
1076 elif 'verbatim' in opts:
1077 newbody = newbody + output_verbatim (body, 0)
1080 m = re.search ('intertext="(.*?)"', o)
1082 newbody = newbody + "\n"
1083 if format == 'texi':
1084 newbody = newbody + "@noindent\n"
1085 elif format == 'latex':
1086 newbody = newbody + "\\noindent\n"
1087 newbody = newbody + m.group (1) + "\n"
1089 if 'noinline' in opts:
1090 s = 'output-noinline'
1091 elif format == 'latex':
1095 if 'noquote' in opts:
1096 s = 'output-latex-noquote'
1098 s = 'output-latex-quoted'
1099 elif format == 'texi':
1100 if 'noquote' in opts:
1101 s = 'output-texi-noquote'
1103 s = 'output-texi-quoted'
1104 else: # format == 'html'
1106 newbody = newbody + get_output (s) % {'fn': basename }
1107 return ('lilypond', newbody, opts, todo, basename)
1109 def process_lilypond_blocks(chunks):#ugh rename
1111 # Count sections/chapters.
1113 if c[0] == 'lilypond':
1114 c = schedule_lilypond_block (c)
1115 elif c[0] == 'numcols':
1116 paperguru.m_num_cols = c[2]
1117 elif c[0] == 'multicols':
1118 paperguru.m_multicols = c[2]
1119 newchunks.append (c)
1125 sys.stderr.write ("invoking `%s'\n" % cmd)
1126 st = os.system (cmd)
1128 error ('Error command exited with value %d\n' % st)
1131 def quiet_system (cmd, name):
1133 progress ( _("Running %s...") % name)
1134 cmd = cmd + ' 1> /dev/null 2> /dev/null'
1138 def get_bbox (filename):
1140 # gs bbox device is ugh, it always prints of stderr.
1141 system ('gs -sDEVICE=bbox -q -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1' % (filename, filename))
1143 box = open (filename + '.bbox').read()
1144 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
1147 gr = map (string.atoi, m.groups ())
1151 def make_pixmap (name):
1152 bbox = get_bbox (name + '.eps')
1156 fo = open (name + '.trans.eps' , 'w')
1157 fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
1162 x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1163 y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1169 cmd = r'''gs -g%dx%d -sDEVICE=pnggray -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=%s -r%d -dNOPAUSE %s %s -c quit '''
1171 cmd = cmd % (x, y, name + '.png', res, name + '.trans.eps', name + '.eps')
1174 status = system (cmd)
1179 os.unlink (name + '.png')
1180 error ("Removing output file")
1182 def compile_all_files (chunks):
1189 if c[0] != 'lilypond':
1198 if base + '.ly' not in tex:
1199 tex.append (base + '.ly')
1200 elif e == 'png' and g_do_pictures:
1206 # fixme: be sys-independent.
1208 if g_outdir and x[0] != '/' :
1209 x = os.path.join (g_here_dir, x)
1212 incs = map (incl_opt, include_path)
1213 lilyopts = string.join (incs, ' ' )
1215 lilyopts = lilyopts + ' --dependencies '
1217 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1218 texfiles = string.join (tex, ' ')
1219 cmd = '%s --header=texidoc %s %s %s' \
1220 % (binary, lilyopts, g_extra_opts, texfiles)
1225 # Ugh, fixing up dependencies for .tex generation
1228 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1233 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1239 cmd = r"echo $TEXMF; latex '\nonstopmode \input %s'" % e
1240 quiet_system (cmd, 'LaTeX')
1242 cmd = r"dvips -E -o %s %s" % (e + '.eps', e)
1243 quiet_system (cmd, 'dvips')
1251 def update_file (body, name):
1253 write the body if it has changed
1264 f = open (name , 'w')
1271 def getopt_args (opts):
1272 "Construct arguments (LONG, SHORT) for getopt from list of options."
1277 short = short + o[1]
1285 return (short, long)
1287 def option_help_str (o):
1288 "Transform one option description (4-tuple ) into neatly formatted string"
1306 return ' ' + sh + sep + long + arg
1308 def options_help_str (opts):
1309 "Convert a list of options into a neatly formatted string"
1315 s = option_help_str (o)
1316 strs.append ((s, o[3]))
1322 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1326 sys.stdout.write('''Usage: lilypond-book [options] FILE\n
1327 Generate hybrid LaTeX input from latex + lilypond.\n
1330 sys.stdout.write (options_help_str (option_definitions))
1331 sys.stdout.write (r'''
1332 Warning: All output is written in the CURRENT directory.
1335 Report bugs to bug-lilypond@gnu.org.
1337 Written by Tom Cato Amundsen <tca@gnu.org> and
1338 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1344 def write_deps (fn, target, chunks):
1346 sys.stderr.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1347 f = open (os.path.join(g_outdir, fn), 'w')
1348 f.write ('%s%s: ' % (g_dep_prefix, target))
1349 for d in read_files:
1353 if c[0] == 'lilypond':
1354 (type, body, opts, todo, basename) = c;
1355 basenames.append (basename)
1358 d=g_outdir + '/' + d
1360 #if not os.isfile (d): # thinko?
1361 if not re.search ('/', d):
1362 d = g_dep_prefix + d
1363 f.write ('%s.tex ' % d)
1365 #if len (basenames):
1366 # for d in basenames:
1367 # f.write ('%s.ly ' % d)
1368 # f.write (' : %s' % target)
1373 def identify (stream):
1374 stream.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1376 def print_version ():
1377 identify (sys.stdout)
1378 sys.stdout.write (r'''Copyright 1998--1999
1379 Distributed under terms of the GNU General Public License. It comes with
1384 def check_texidoc (chunks):
1387 if c[0] == 'lilypond':
1388 (type, body, opts, todo, basename) = c;
1389 pathbase = os.path.join (g_outdir, basename)
1390 if os.path.isfile (pathbase + '.texidoc'):
1391 body = '\n@include %s.texidoc\n' % basename + body
1392 c = (type, body, opts, todo, basename)
1397 ## what's this? Docme --hwn
1399 def fix_epswidth (chunks):
1402 if c[0] != 'lilypond' or 'eps' not in c[2]:
1403 newchunks.append (c)
1408 m = re.match ('magnification=([0-9.]+)', o)
1410 mag = string.atof (m.group (1))
1412 def replace_eps_dim (match, lmag = mag):
1413 filename = match.group (1)
1414 dims = bounding_box_dimensions (filename)
1416 return '%fpt' % (dims[0] *lmag)
1418 body = re.sub (r'''\\lilypondepswidth{(.*?)}''', replace_eps_dim, c[1])
1419 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1424 ##docme: why global?
1427 def do_file(input_filename):
1428 chunks = read_doc_file(input_filename)
1429 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1430 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1431 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1432 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1433 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1434 chunks = chop_chunks(chunks, 'numcols', do_columns)
1435 chunks = chop_chunks(chunks, 'multicols', do_multicols)
1437 #for c in chunks: print "c:", c;
1439 scan_preamble(chunks)
1440 chunks = process_lilypond_blocks(chunks)
1443 if __main__.g_run_lilypond:
1444 compile_all_files (chunks)
1445 chunks = fix_epswidth (chunks)
1447 if __main__.format == 'texi':
1448 chunks = check_texidoc (chunks)
1451 chunks = completize_preamble (chunks)
1456 my_outname = outname
1457 elif input_filename == '-' or input_filename == "/dev/stdin":
1460 my_outname = os.path.basename (os.path.splitext(input_filename)[0]) + '.' + format
1461 my_depname = my_outname + '.dep'
1463 if my_outname == '-' or my_outname == '/dev/stdout':
1466 __main__.do_deps = 0
1468 foutn = os.path.join (g_outdir, my_outname)
1469 sys.stderr.write ("Writing `%s'\n" % foutn)
1470 fout = open (foutn, 'w')
1477 write_deps (my_depname, foutn, chunks)
1481 (sh, long) = getopt_args (__main__.option_definitions)
1482 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1483 except getopt.error, msg:
1484 sys.stderr.write("error: %s" % msg)
1492 if o == '--include' or o == '-I':
1493 include_path.append (a)
1494 elif o == '--version' or o == '-v':
1497 elif o == '--verbose' or o == '-V':
1498 __main__.verbose_p = 1
1499 elif o == '--format' or o == '-f':
1501 if a == 'texi-html':
1502 __main__.format = 'texi'
1504 elif o == '--outname' or o == '-o':
1507 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1510 elif o == '--help' or o == '-h':
1512 elif o == '--no-lily' or o == '-n':
1513 __main__.g_run_lilypond = 0
1514 elif o == '--dependencies' or o == '-M':
1516 elif o == '--default-music-fontsize':
1517 default_music_fontsize = string.atoi (a)
1518 elif o == '--default-lilypond-fontsize':
1519 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1520 default_music_fontsize = string.atoi (a)
1521 elif o == '--extra-options':
1523 elif o == '--force-music-fontsize':
1524 g_force_music_fontsize = string.atoi(a)
1525 elif o == '--force-lilypond-fontsize':
1526 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1527 g_force_music_fontsize = string.atoi(a)
1528 elif o == '--dep-prefix':
1530 elif o == '--no-pictures':
1532 elif o == '--no-music':
1534 elif o == '--read-lys':
1536 elif o == '--outdir':
1539 identify (sys.stderr)
1541 if os.path.isfile(g_outdir):
1542 error ("outdir is a file: %s" % g_outdir)
1543 if not os.path.exists(g_outdir):
1545 setup_environment ()
1546 for input_filename in files:
1547 do_file(input_filename)
1550 # Petr, ik zou willen dat ik iets zinvoller deed,
1551 # maar wat ik kan ik doen, het verandert toch niets?