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.
53 ## We would like to do this for python 2.2 as well, unfortunately
54 ## python 2.2 has another bug, see Sf.net bugtracker
56 ## https://sourceforge.net/tracker/?func=detail&aid=604803&group_id=5470&atid=105470
67 if float (sys.version[0:3]) <= 2.1:
68 ## or sys.version[0:5] == '2.2.1':
69 ## still broken on python 2.2.1 / RH8.
80 # Attempt to fix problems with limited stack size set by Python!
81 # Sets unlimited stack size. Note that the resource module only
82 # is available on UNIX.
85 resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
89 errorport = sys.stderr
96 gettext.bindtextdomain ('lilypond', localedir)
97 gettext.textdomain ('lilypond')
104 errorport.write (s + '\n')
107 program_version = '@TOPLEVEL_VERSION@'
108 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
109 program_version = '1.6.0'
111 # if set, LILYPONDPREFIX must take prevalence
112 # if datadir is not set, we're doing a build and LILYPONDPREFIX
113 datadir = '@local_lilypond_datadir@'
115 if os.environ.has_key ('LILYPONDPREFIX') :
116 datadir = os.environ['LILYPONDPREFIX']
118 datadir = '@local_lilypond_datadir@'
120 while datadir[-1] == os.sep:
121 datadir= datadir[:-1]
123 kpse = os.popen ('kpsexpand \$TEXMF').read()
124 kpse = re.sub('[ \t\n]+$','', kpse)
125 type1_paths = os.popen ('kpsewhich -expand-path=\$T1FONTS').read ()
128 #binary = 'valgrind --suppressions=/home/hanwen/usr/src/guile-1.6.supp --num-callers=10 /home/hanwen/usr/src/lilypond/lily/out/lilypond'
130 # TODO: * prevent multiple addition.
131 # * clean TEXINPUTS, MFINPUTS, TFMFONTS,
132 # as these take prevalence over $TEXMF
133 # and thus may break tex run?
134 'TEXMF' : "{%s,%s}" % (datadir, kpse) ,
135 'GS_FONTPATH' : type1_paths,
136 'GS_LIB' : datadir + '/ps',
139 # tex needs lots of memory, more than it gets by default on Debian
140 non_path_environment = {
141 'extra_mem_top' : '1000000',
142 'extra_mem_bottom' : '1000000',
143 'pool_size' : '250000',
146 def setup_environment ():
147 # $TEXMF is special, previous value is already taken care of
148 if os.environ.has_key ('TEXMF'):
149 del os.environ['TEXMF']
151 for key in environment.keys ():
152 val = environment[key]
153 if os.environ.has_key (key):
154 val = val + os.pathsep + os.environ[key]
155 os.environ[key] = val
157 for key in non_path_environment.keys ():
158 val = non_path_environment[key]
159 os.environ[key] = val
161 include_path = [os.getcwd()]
164 # g_ is for global (?)
166 g_here_dir = os.getcwd ()
169 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], 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.
465 # override EndLilyPondOutput, since @tex is done
466 # in a sandbox, you can't do \input lilyponddefs at the
467 # top of the document.
469 # ugh, the <p> below breaks inline images...
470 'output-texi-noquote': r'''@tex
474 \def\EndLilyPondOutput{}
479 <p><a href="%(fn)s.png">
480 <img border=0 src="%(fn)s.png" alt="[picture of music]">
484 'output-texi-quoted': r'''@quotation
488 \def\EndLilyPondOutput{}
493 <a href="%(fn)s.png">
494 <img border=0 src="%(fn)s.png" alt="[picture of music]">
503 def output_verbatim (body, small):
504 if __main__.format == 'html':
505 body = re.sub ('&', '&', body)
506 body = re.sub ('>', '>', body)
507 body = re.sub ('<', '<', body)
508 elif __main__.format == 'texi':
509 # clumsy workaround for python 2.2 pre bug.
510 body = re.sub ('@', '@@', body)
511 body = re.sub ('{', '@{', body)
512 body = re.sub ('}', '@}', body)
515 key = 'output-small-verbatim'
517 key = 'output-verbatim'
518 return get_output (key) % body
521 # Warning: This uses extended regular expressions. Treat with care.
525 # (?P<name>regex) -- assign result of REGEX to NAME
526 # *? -- match non-greedily.
527 # (?m) -- multiline regex: make ^ and $ match at each line
528 # (?s) -- make the dot match all characters including newline
534 'preamble-end': no_match,
535 'landscape': no_match,
536 'verbatim': r'''(?s)(?P<code><pre>\s.*?</pre>\s)''',
537 'verb': r'''(?P<code><pre>.*?</pre>)''',
538 'lilypond-file': r'(?m)(?P<match><lilypondfile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</lilypondfile>)',
539 'lilypond' : '(?m)(?P<match><lilypond((?P<options>[^:]*):)(?P<code>.*?)/>)',
540 'lilypond-block': r'''(?ms)(?P<match><lilypond(?P<options>[^>]+)?>(?P<code>.*?)</lilypond>)''',
541 'option-sep' : '\s*',
542 'intertext': r',?\s*intertext=\".*?\"',
543 'multiline-comment': r"(?sm)\s*(?!@c\s+)(?P<code><!--\s.*?!-->)\s",
544 'singleline-comment': no_match,
546 'multicols': no_match,
550 'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
551 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
552 'option-sep' : ',\s*',
553 'header': r"\n*\\documentclass\s*(\[.*?\])?",
554 'preamble-end': r'(?P<code>\\begin\s*{document})',
555 'verbatim': r"(?s)(?P<code>\\begin\s*{verbatim}.*?\\end{verbatim})",
556 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
557 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
558 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
559 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
560 'def-post-re': r"\\def\\postLilypondExample",
561 'def-pre-re': r"\\def\\preLilypondExample",
562 'usepackage-graphics': r"\usepackage\s*{graphics}",
563 'intertext': r',?\s*intertext=\".*?\"',
564 'multiline-comment': no_match,
565 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
566 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
567 'multicols': r"(?P<code>\\(?P<be>begin|end)\s*{multicols}({(?P<num>\d+)?})?)",
570 # why do we have distinction between @mbinclude and @include?
573 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
576 'preamble-end': no_match,
577 'landscape': no_match,
578 'verbatim': r'''(?s)(?P<code>@example\s.*?@end example\s)''',
579 'verb': r'''(?P<code>@code{.*?})''',
580 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
581 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
582 'lilypond-block': r'''(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end +lilypond)\s''',
583 'option-sep' : ',\s*',
584 'intertext': r',?\s*intertext=\".*?\"',
585 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
586 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
588 'multicols': no_match,
593 for r in re_dict.keys ():
596 for k in olddict.keys ():
598 newdict[k] = re.compile (olddict[k])
600 print 'invalid regexp: %s' % olddict[k]
602 # we'd like to catch and reraise a more detailed error, but
603 # alas, the exceptions changed across the 1.5/2.1 boundary.
618 def get_output (name):
619 return output_dict[format][name]
622 return re_dict[format][name]
624 def bounding_box_dimensions(fname):
626 fname = os.path.join(g_outdir, fname)
630 error ("Error opening `%s'" % fname)
632 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
635 gs = map (lambda x: string.atoi (x), s.groups ())
636 return (int (gs[2] - gs[0] + 0.5),
637 int (gs[3] - gs[1] + 0.5))
642 sys.stderr.write ("\n\n" + str + "\nExiting ... \n\n")
646 def compose_full_body (body, opts):
647 '''Construct the lilypond code to send to Lilypond.
648 Add stuff to BODY using OPTS as options.'''
649 music_size = default_music_fontsize
650 if g_force_music_fontsize:
651 music_size = g_force_music_fontsize
656 if not g_force_music_fontsize:
657 m = re.match ('([0-9]+)pt', o)
659 music_size = string.atoi(m.group (1))
661 m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o)
663 f = float (m.group (1))
664 indent = 'indent = %f\\%s' % (f, m.group (2))
666 m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o)
668 f = float (m.group (1))
669 linewidth = 'linewidth = %f\\%s' % (f, m.group (2))
671 if re.search ('\\\\score', body):
675 if 'fragment' in opts:
677 if 'nofragment' in opts:
680 if is_fragment and not 'multiline' in opts:
681 opts.append('singleline')
683 if 'singleline' in opts:
685 linewidth = 'linewidth = -1.0'
687 indent = 'indent = 0.0\mm'
689 l = __main__.paperguru.get_linewidth ()
690 linewidth = 'linewidth = %f\pt' % l
692 if 'noindent' in opts:
693 indent = 'indent = 0.0\mm'
699 \remove Time_signature_engraver
704 m= re.search ('relative(.*)', o)
708 v = string.atoi (m.group (1))
715 pitch = pitch + '\,' * v
717 pitch = pitch + '\'' * v
719 body = '\\relative %s { %s }' %(pitch, body)
731 optstring = string.join (opts, ' ')
732 optstring = re.sub ('\n', ' ', optstring)
734 %% Generated automatically by: lilypond-book.py
736 \include "paper%d.ly"
742 ''' % (optstring, music_size, linewidth, indent, notime) + body
744 # ughUGH not original options
747 def scan_html_preamble (chunks):
750 def scan_latex_preamble(chunks):
751 # First we want to scan the \documentclass line
752 # it should be the first non-comment line.
753 # The only thing we really need to know about the \documentclass line
754 # is if there are one or two columns to begin with.
757 if chunks[idx][0] == 'ignore':
760 m = get_re ('header').match(chunks[idx][1])
762 error ("Latex documents must start with a \documentclass command")
764 options = re.split (',[\n \t]*', m.group(1)[1:-1])
767 if 'twocolumn' in options:
768 paperguru.m_num_cols = 2
772 # Then we add everything before \begin{document} to
773 # paperguru.m_document_preamble so that we can later write this header
774 # to a temporary file in find_latex_dims() to find textwidth.
775 while idx < len(chunks) and chunks[idx][0] != 'preamble-end':
776 if chunks[idx] == 'ignore':
779 paperguru.m_document_preamble.append(chunks[idx][1])
782 if len(chunks) == idx:
783 error ("Didn't find end of preamble (\\begin{document})")
785 paperguru.find_latex_dims()
787 def scan_texi_preamble (chunks):
788 # this is not bulletproof..., it checks the first 10 chunks
789 for c in chunks[:10]:
791 for s in ('afourpaper', 'afourwide', 'letterpaper',
792 'afourlatex', 'smallbook'):
793 if string.find(c[1], "@%s" % s) != -1:
794 paperguru.m_papersize = s
797 def scan_preamble (chunks):
798 if __main__.format == 'html':
799 scan_html_preamble (chunks)
800 elif __main__.format == 'latex':
801 scan_latex_preamble (chunks)
802 elif __main__.format == 'texi':
803 scan_texi_preamble (chunks)
806 def completize_preamble (chunks):
807 if __main__.format != 'latex':
809 pre_b = post_b = graphics_b = None
811 if chunk[0] == 'preamble-end':
813 if chunk[0] == 'input':
814 m = get_re('def-pre-re').search(chunk[1])
817 if chunk[0] == 'input':
818 m = get_re('def-post-re').search(chunk[1])
822 if chunk[0] == 'input':
823 m = get_re('usepackage-graphics').search(chunk[1])
827 while x < len (chunks) and chunks[x][0] != 'preamble-end':
834 chunks.insert(x, ('input', get_output ('output-default-pre')))
836 chunks.insert(x, ('input', get_output ('output-default-post')))
838 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
844 def find_file (name):
846 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
850 return (sys.stdin.read (), '<stdin>')
853 for a in include_path:
855 nm = os.path.join (a, name)
857 __main__.read_files.append (nm)
862 sys.stderr.write ("Reading `%s'\n" % nm)
863 return (f.read (), nm)
865 error ("File not found `%s'\n" % name)
868 def do_ignore(match_object):
869 return [('ignore', match_object.group('code'))]
870 def do_preamble_end(match_object):
871 return [('preamble-end', match_object.group('code'))]
873 def make_verbatim(match_object):
874 return [('verbatim', match_object.group('code'))]
876 def make_verb(match_object):
877 return [('verb', match_object.group('code'))]
879 def do_include_file(m):
881 return [('input', get_output ('pagebreak'))] \
882 + read_doc_file(m.group('filename')) \
883 + [('input', get_output ('pagebreak'))]
885 def do_input_file(m):
886 return read_doc_file(m.group('filename'))
888 def make_lilypond(m):
889 if m.group('options'):
890 options = m.group('options')
893 return [('input', get_output('output-lilypond-fragment') %
894 (options, m.group('code')))]
896 def make_lilypond_file(m):
899 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
900 into a @lilypond .. @end lilypond block.
904 if m.group('options'):
905 options = m.group('options')
908 (content, nm) = find_file(m.group('filename'))
909 options = "filename=%s," % nm + options
911 return [('input', get_output('output-lilypond') %
914 def make_lilypond_block(m):
918 if m.group('options'):
919 options = get_re('option-sep').split (m.group('options'))
922 options = filter(lambda s: s != '', options)
923 return [('lilypond', m.group('code'), options)]
926 if __main__.format != 'latex':
928 if m.group('num') == 'one':
929 return [('numcols', m.group('code'), 1)]
930 if m.group('num') == 'two':
931 return [('numcols', m.group('code'), 2)]
934 if __main__.format != 'latex':
936 if m.group('be') == 'begin':
937 return [('multicols', m.group('code'), int(m.group('num')))]
939 return [('multicols', m.group('code'), 1)]
942 def chop_chunks(chunks, re_name, func, use_match=0):
948 m = get_re (re_name).search (str)
950 newchunks.append (('input', str))
954 newchunks.append (('input', str[:m.start ('match')]))
956 newchunks.append (('input', str[:m.start (0)]))
957 #newchunks.extend(func(m))
958 # python 1.5 compatible:
959 newchunks = newchunks + func(m)
960 str = str [m.end(0):]
965 def determine_format (str):
966 if __main__.format == '':
968 html = re.search ('(?i)<[dh]tml', str[:200])
969 latex = re.search (r'''\\document''', str[:200])
970 texi = re.search ('@node|@setfilename', str[:200])
975 if html and not latex and not texi:
977 elif latex and not html and not texi:
979 elif texi and not html and not latex:
982 error ("can't determine format, please specify")
985 if __main__.paperguru == None:
986 if __main__.format == 'html':
988 elif __main__.format == 'latex':
990 elif __main__.format == 'texi':
993 __main__.paperguru = g
996 def read_doc_file (filename):
997 '''Read the input file, find verbatim chunks and do \input and \include
999 (str, path) = find_file(filename)
1000 determine_format (str)
1002 chunks = [('input', str)]
1004 # we have to check for verbatim before doing include,
1005 # because we don't want to include files that are mentioned
1006 # inside a verbatim environment
1007 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
1009 chunks = chop_chunks(chunks, 'verb', make_verb)
1010 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
1012 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
1013 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
1017 taken_file_names = {}
1018 def schedule_lilypond_block (chunk):
1019 '''Take the body and options from CHUNK, figure out how the
1020 real .ly should look, and what should be left MAIN_STR (meant
1021 for the main file). The .ly is written, and scheduled in
1024 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
1026 TODO has format [basename, extension, extension, ... ]
1028 (type, body, opts) = chunk
1029 assert type == 'lilypond'
1030 file_body = compose_full_body (body, opts)
1031 ## Hmm, we should hash only lilypond source, and skip the
1034 basename = 'lily-' + `abs(hash (file_body))`
1036 m = re.search ('filename="(.*?)"', o)
1038 basename = m.group (1)
1039 if not taken_file_names.has_key(basename):
1040 taken_file_names[basename] = 0
1042 taken_file_names[basename] = taken_file_names[basename] + 1
1043 basename = basename + "-%i" % taken_file_names[basename]
1045 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
1046 needed_filetypes = ['tex']
1048 if format == 'html' or format == 'texi':
1049 needed_filetypes.append ('eps')
1050 needed_filetypes.append ('png')
1051 if 'eps' in opts and not ('eps' in needed_filetypes):
1052 needed_filetypes.append('eps')
1053 pathbase = os.path.join (g_outdir, basename)
1054 def f (base, ext1, ext2):
1055 a = os.path.isfile(base + ext2)
1056 if (os.path.isfile(base + ext1) and
1057 os.path.isfile(base + ext2) and
1058 os.stat(base+ext1)[stat.ST_MTIME] >
1059 os.stat(base+ext2)[stat.ST_MTIME]) or \
1060 not os.path.isfile(base + ext2):
1063 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
1065 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
1067 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
1071 if 'printfilename' in opts:
1073 m= re.match ("filename=(.*)", o)
1075 newbody = newbody + get_output ("output-filename") % (m.group(1), basename + '.ly', m.group(1))
1079 if 'smallverbatim' in opts:
1080 newbody = newbody + output_verbatim (body, 1)
1081 elif 'verbatim' in opts:
1082 newbody = newbody + output_verbatim (body, 0)
1085 m = re.search ('intertext="(.*?)"', o)
1087 newbody = newbody + "\n"
1088 if format == 'texi':
1089 newbody = newbody + "@noindent\n"
1090 elif format == 'latex':
1091 newbody = newbody + "\\noindent\n"
1092 newbody = newbody + m.group (1) + "\n"
1094 if 'noinline' in opts:
1095 s = 'output-noinline'
1096 elif format == 'latex':
1100 if 'noquote' in opts:
1101 s = 'output-latex-noquote'
1103 s = 'output-latex-quoted'
1104 elif format == 'texi':
1105 if 'noquote' in opts:
1106 s = 'output-texi-noquote'
1108 s = 'output-texi-quoted'
1109 else: # format == 'html'
1111 newbody = newbody + get_output (s) % {'fn': basename }
1112 return ('lilypond', newbody, opts, todo, basename)
1114 def process_lilypond_blocks(chunks):#ugh rename
1116 # Count sections/chapters.
1118 if c[0] == 'lilypond':
1119 c = schedule_lilypond_block (c)
1120 elif c[0] == 'numcols':
1121 paperguru.m_num_cols = c[2]
1122 elif c[0] == 'multicols':
1123 paperguru.m_multicols = c[2]
1124 newchunks.append (c)
1130 sys.stderr.write ("invoking `%s'\n" % cmd)
1131 st = os.system (cmd)
1133 error ('Error command exited with value %d\n' % st)
1136 def quiet_system (cmd, name):
1138 progress ( _("Running %s...") % name)
1139 cmd = cmd + ' 1> /dev/null 2> /dev/null'
1143 def get_bbox (filename):
1145 # gs bbox device is ugh, it always prints of stderr.
1146 system ('gs -sDEVICE=bbox -q -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1' % (filename, filename))
1148 box = open (filename + '.bbox').read()
1149 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
1152 gr = map (string.atoi, m.groups ())
1156 def make_pixmap (name):
1157 bbox = get_bbox (name + '.eps')
1161 fo = open (name + '.trans.eps' , 'w')
1162 fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
1167 x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1168 y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1174 cmd = r'''gs -g%dx%d -sDEVICE=pnggray -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=%s -r%d -dNOPAUSE %s %s -c quit '''
1176 cmd = cmd % (x, y, name + '.png', res, name + '.trans.eps', name + '.eps')
1179 status = system (cmd)
1184 os.unlink (name + '.png')
1185 error ("Removing output file")
1187 def compile_all_files (chunks):
1194 if c[0] != 'lilypond':
1203 if base + '.ly' not in tex:
1204 tex.append (base + '.ly')
1205 elif e == 'png' and g_do_pictures:
1211 # fixme: be sys-independent.
1213 if g_outdir and x[0] != '/' :
1214 x = os.path.join (g_here_dir, x)
1217 incs = map (incl_opt, include_path)
1218 lilyopts = string.join (incs, ' ' )
1220 lilyopts = lilyopts + ' --dependencies '
1222 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1223 texfiles = string.join (tex, ' ')
1224 cmd = '%s --header=texidoc %s %s %s' \
1225 % (binary, lilyopts, g_extra_opts, texfiles)
1230 # Ugh, fixing up dependencies for .tex generation
1233 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1238 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1244 cmd = r"echo $TEXMF; latex '\nonstopmode \input %s'" % e
1245 quiet_system (cmd, 'LaTeX')
1247 cmd = r"dvips -E -o %s %s" % (e + '.eps', e)
1248 quiet_system (cmd, 'dvips')
1256 def update_file (body, name):
1258 write the body if it has changed
1269 f = open (name , 'w')
1276 def getopt_args (opts):
1277 "Construct arguments (LONG, SHORT) for getopt from list of options."
1282 short = short + o[1]
1290 return (short, long)
1292 def option_help_str (o):
1293 "Transform one option description (4-tuple ) into neatly formatted string"
1311 return ' ' + sh + sep + long + arg
1313 def options_help_str (opts):
1314 "Convert a list of options into a neatly formatted string"
1320 s = option_help_str (o)
1321 strs.append ((s, o[3]))
1327 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1331 sys.stdout.write('''Usage: lilypond-book [options] FILE\n
1332 Generate hybrid LaTeX input from latex + lilypond.\n
1335 sys.stdout.write (options_help_str (option_definitions))
1336 sys.stdout.write (r'''
1337 Warning: All output is written in the CURRENT directory.
1340 Report bugs to bug-lilypond@gnu.org.
1342 Written by Tom Cato Amundsen <tca@gnu.org> and
1343 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1349 def write_deps (fn, target, chunks):
1351 sys.stderr.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1352 f = open (os.path.join(g_outdir, fn), 'w')
1353 f.write ('%s%s: ' % (g_dep_prefix, target))
1354 for d in read_files:
1358 if c[0] == 'lilypond':
1359 (type, body, opts, todo, basename) = c;
1360 basenames.append (basename)
1363 d=g_outdir + '/' + d
1365 #if not os.isfile (d): # thinko?
1366 if not re.search ('/', d):
1367 d = g_dep_prefix + d
1368 f.write ('%s.tex ' % d)
1370 #if len (basenames):
1371 # for d in basenames:
1372 # f.write ('%s.ly ' % d)
1373 # f.write (' : %s' % target)
1378 def identify (stream):
1379 stream.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1381 def print_version ():
1382 identify (sys.stdout)
1383 sys.stdout.write (r'''Copyright 1998--1999
1384 Distributed under terms of the GNU General Public License. It comes with
1389 def check_texidoc (chunks):
1392 if c[0] == 'lilypond':
1393 (type, body, opts, todo, basename) = c;
1394 pathbase = os.path.join (g_outdir, basename)
1395 if os.path.isfile (pathbase + '.texidoc'):
1396 body = '\n@include %s.texidoc\n' % basename + body
1397 c = (type, body, opts, todo, basename)
1402 ## what's this? Docme --hwn
1404 def fix_epswidth (chunks):
1407 if c[0] != 'lilypond' or 'eps' not in c[2]:
1408 newchunks.append (c)
1413 m = re.match ('magnification=([0-9.]+)', o)
1415 mag = string.atof (m.group (1))
1417 def replace_eps_dim (match, lmag = mag):
1418 filename = match.group (1)
1419 dims = bounding_box_dimensions (filename)
1421 return '%fpt' % (dims[0] *lmag)
1423 body = re.sub (r'''\\lilypondepswidth{(.*?)}''', replace_eps_dim, c[1])
1424 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1429 ##docme: why global?
1432 def do_file(input_filename):
1433 chunks = read_doc_file(input_filename)
1434 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1435 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1436 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1437 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1438 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1439 chunks = chop_chunks(chunks, 'numcols', do_columns)
1440 chunks = chop_chunks(chunks, 'multicols', do_multicols)
1442 #for c in chunks: print "c:", c;
1444 scan_preamble(chunks)
1445 chunks = process_lilypond_blocks(chunks)
1448 if __main__.g_run_lilypond:
1449 compile_all_files (chunks)
1450 chunks = fix_epswidth (chunks)
1452 if __main__.format == 'texi':
1453 chunks = check_texidoc (chunks)
1456 chunks = completize_preamble (chunks)
1461 my_outname = outname
1462 elif input_filename == '-' or input_filename == "/dev/stdin":
1465 my_outname = os.path.basename (os.path.splitext(input_filename)[0]) + '.' + format
1466 my_depname = my_outname + '.dep'
1468 if my_outname == '-' or my_outname == '/dev/stdout':
1471 __main__.do_deps = 0
1473 foutn = os.path.join (g_outdir, my_outname)
1474 sys.stderr.write ("Writing `%s'\n" % foutn)
1475 fout = open (foutn, 'w')
1482 write_deps (my_depname, foutn, chunks)
1486 (sh, long) = getopt_args (__main__.option_definitions)
1487 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1488 except getopt.error, msg:
1489 sys.stderr.write("error: %s" % msg)
1497 if o == '--include' or o == '-I':
1498 include_path.append (a)
1499 elif o == '--version' or o == '-v':
1502 elif o == '--verbose' or o == '-V':
1503 __main__.verbose_p = 1
1504 elif o == '--format' or o == '-f':
1506 elif o == '--outname' or o == '-o':
1509 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1512 elif o == '--help' or o == '-h':
1514 elif o == '--no-lily' or o == '-n':
1515 __main__.g_run_lilypond = 0
1516 elif o == '--dependencies' or o == '-M':
1518 elif o == '--default-music-fontsize':
1519 default_music_fontsize = string.atoi (a)
1520 elif o == '--default-lilypond-fontsize':
1521 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1522 default_music_fontsize = string.atoi (a)
1523 elif o == '--extra-options':
1525 elif o == '--force-music-fontsize':
1526 g_force_music_fontsize = string.atoi(a)
1527 elif o == '--force-lilypond-fontsize':
1528 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1529 g_force_music_fontsize = string.atoi(a)
1530 elif o == '--dep-prefix':
1532 elif o == '--no-pictures':
1534 elif o == '--no-music':
1536 elif o == '--read-lys':
1538 elif o == '--outdir':
1541 identify (sys.stderr)
1543 if os.path.isfile(g_outdir):
1544 error ("outdir is a file: %s" % g_outdir)
1545 if not os.path.exists(g_outdir):
1547 setup_environment ()
1548 for input_filename in files:
1549 do_file(input_filename)
1552 # Petr, ik zou willen dat ik iets zinvoller deed,
1553 # maar wat ik kan ik doen, het verandert toch niets?