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
95 datadir = '@local_lilypond_datadir@'
97 if os.environ.has_key ('LILYPONDPREFIX') :
98 datadir = os.environ['LILYPONDPREFIX']
100 datadir = '@local_lilypond_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 = []
167 def find_latex_dims(self):
169 fname = os.path.join(g_outdir, "lily-tmp.tex")
171 fname = "lily-tmp.tex"
175 error ("Error creating temporary file '%s'" % fname)
176 for s in self.m_document_preamble:
181 \typeout{\columnsep \the\columnsep}
182 \typeout{\textwidth \the\textwidth}
187 re_dim = re.compile(r"\\(\w+)\s+(\d+\.\d+)")
188 p = os.popen("latex %s" % fname)
191 ln = string.strip(ln)
194 if m.groups()[0] in ('textwidth', 'columnsep'):
195 self.__dict__['m_%s' % m.groups()[0]] = float(m.groups()[1])
199 os.remove (os.path.splitext(fname)[0]+".aux")
200 os.remove (os.path.splitext(fname)[0]+".log")
203 def get_linewidth(self):
204 if self.m_num_cols == 1:
207 w = (self.m_textwidth - self.m_columnsep)/2
208 if self.m_multicols > 1:
209 return (w - self.m_columnsep*(self.m_multicols-1)) \
216 self.m_papersize = 'letterpaper'
218 def get_linewidth(self):
219 return html_linewidths[self.m_papersize][self.m_fontsize]
223 self.m_papersize = 'letterpaper'
225 def get_linewidth(self):
226 return texi_linewidths[self.m_papersize][self.m_fontsize]
232 def em2pt(x, fontsize = 10):
233 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
234 def ex2pt(x, fontsize = 10):
235 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
240 dimension_conversion_dict ={
242 'cm': lambda x: mm2pt(10*x),
249 # Convert numeric values, with or without specific dimension, to floats.
251 def conv_dimen_to_float(value):
252 if type(value) == type(""):
253 m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
256 num = string.atof(m.group (1))
257 conv = dimension_conversion_dict[m.group(2)]
261 elif re.match ("^[0-9.]+$",value):
267 'afourpaper': {12: mm2pt(160)},
268 'afourwide': {12: in2pt(6.5)},
269 'afourlatex': {12: mm2pt(150)},
270 'smallbook': {12: in2pt(5)},
271 'letterpaper': {12: in2pt(6)}}
274 'afourpaper': {12: mm2pt(160)},
275 'afourwide': {12: in2pt(6.5)},
276 'afourlatex': {12: mm2pt(150)},
277 'smallbook': {12: in2pt(5)},
278 'letterpaper': {12: in2pt(6)}}
280 option_definitions = [
281 ('EXT', 'f', 'format', 'use output format EXT (texi [default], latex, html)'),
282 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
283 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
284 ('OPT', '', 'extra-options' , 'Pass OPT quoted to the lilypond command line'),
285 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
286 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
287 ('', 'h', 'help', 'this help'),
288 ('DIR', 'I', 'include', 'include path'),
289 ('', 'M', 'dependencies', 'write dependencies'),
290 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
291 ('', 'n', 'no-lily', 'don\'t run lilypond'),
292 ('', '', 'no-pictures', "don\'t generate pictures"),
293 ('', '', 'no-music', "strip all lilypond blocks from output"),
294 ('', '', 'read-lys', "don't write ly files."),
295 ('FILE', 'o', 'outname', 'filename main output file'),
296 ('FILE', '', 'outdir', "where to place generated files"),
297 ('', 'V', 'verbose', 'verbose' ),
298 ('', 'v', 'version', 'print version information' ),
301 # format specific strings, ie. regex-es for input, and % strings for output
303 'html' : {'output-lilypond': '''<lilypond%s>
306 'output-filename' : r'''
309 'output-lilypond-fragment': '''<lilypond%s>
310 \context Staff\context Voice{ %s }
312 'output-noinline': r'''
313 <!-- generated: %(fn)s.png !-->
317 'output-verbatim': r'''<pre>
320 'output-small-verbatim': r'''<font size=-1><pre>
324 ## Ugh we need to differentiate on origin:
325 ## lilypond-block origin wants an extra <p>, but
326 ## inline music doesn't.
327 ## possibly other center options?
329 <a href="%(fn)s.png">
330 <img align="center" valign="center" border="0" src="%(fn)s.png" alt="[picture of music]"></a>
334 'output-lilypond-fragment' : r'''\begin[eps,singleline,%s]{lilypond}
341 'output-filename' : r'''
344 'output-lilypond': r'''\begin[%s]{lilypond}
348 'output-verbatim': r'''\begin{verbatim}%s\end{verbatim}%%
350 'output-small-verbatim': r'''{\small\begin{verbatim}%s\end{verbatim}}%%''',
351 'output-default-post': "\\def\postLilypondExample{}\n",
352 'output-default-pre': "\\def\preLilypondExample{}\n",
353 'usepackage-graphics': '\\usepackage{graphics}\n',
354 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
355 'output-noinline': r'''
356 %% generated: %(fn)s.eps
358 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
359 'pagebreak': r'\pagebreak',
362 'texi' : {'output-lilypond': '''@lilypond[%s]
366 'output-filename' : r'''
369 'output-lilypond-fragment': '''@lilypond[%s]
370 \context Staff\context Voice{ %s }
372 'output-noinline': r'''
373 @c generated: %(fn)s.png
376 'output-small-verbatim': r'''@smallexample
380 'output-verbatim': r'''@example
385 # do some tweaking: @ is needed in some ps stuff.
386 # override EndLilyPondOutput, since @tex is done
387 # in a sandbox, you can't do \input lilyponddefs at the
388 # top of the document.
390 # should also support fragment in
392 # ugh, the <p> below breaks inline images...
398 \def\EndLilyPondOutput{}
404 <a href="%(fn)s.png">
405 <img border=0 src="%(fn)s.png" alt="[picture of music]">
413 def output_verbatim (body, small):
414 if __main__.format == 'html':
415 body = re.sub ('&', '&', body)
416 body = re.sub ('>', '>', body)
417 body = re.sub ('<', '<', body)
418 elif __main__.format == 'texi':
419 body = re.sub ('([@{}])', '@\\1', body)
422 key = 'output-small-verbatim'
424 key = 'output-verbatim'
425 return get_output (key) % body
428 #warning: this uses extended regular expressions. Tread with care.
432 # (?P -- name parameter
433 # *? -- match non-greedily.
440 'preamble-end': no_match,
441 'landscape': no_match,
442 'verbatim': r'''(?s)(?P<code><pre>\s.*?</pre>\s)''',
443 'verb': r'''(?P<code><pre>.*?</pre>)''',
444 'lilypond-file': r'(?m)(?P<match><lilypondfile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</lilypondfile>)',
445 'lilypond' : '(?m)(?P<match><lilypond((?P<options>[^:]*):)(?P<code>.*?)/>)',
446 'lilypond-block': r'''(?ms)(?P<match><lilypond(?P<options>[^>]+)?>(?P<code>.*?)</lilypond>)''',
447 'option-sep' : '\s*',
448 'intertext': r',?\s*intertext=\".*?\"',
449 'multiline-comment': r"(?sm)\s*(?!@c\s+)(?P<code><!--\s.*?!-->)\s",
450 'singleline-comment': no_match,
452 'multicols': no_match,
455 'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
456 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
457 'option-sep' : ',\s*',
458 'header': r"\n*\\documentclass\s*(\[.*?\])?",
459 'preamble-end': r'(?P<code>\\begin{document})',
460 'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
461 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
462 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
463 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
464 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
465 'def-post-re': r"\\def\\postLilypondExample",
466 'def-pre-re': r"\\def\\preLilypondExample",
467 'usepackage-graphics': r"\usepackage{graphics}",
468 'intertext': r',?\s*intertext=\".*?\"',
469 'multiline-comment': no_match,
470 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
471 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
472 'multicols': r"(?P<code>\\(?P<be>begin|end){multicols}({(?P<num>\d+)?})?)",
476 # why do we have distinction between @mbinclude and @include?
480 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
483 'preamble-end': no_match,
484 'landscape': no_match,
485 'verbatim': r'''(?s)(?P<code>@example\s.*?@end example\s)''',
486 'verb': r'''(?P<code>@code{.*?})''',
487 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
488 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
489 'lilypond-block': r'''(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end +lilypond)\s''',
490 'option-sep' : ',\s*',
491 'intertext': r',?\s*intertext=\".*?\"',
492 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
493 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
495 'multicols': no_match,
500 for r in re_dict.keys ():
503 for k in olddict.keys ():
505 newdict[k] = re.compile (olddict[k])
507 print 'invalid regexp: %s' % olddict[k]
509 # we'd like to catch and reraise a more detailed error, but
510 # alas, the exceptions changed across the 1.5/2.1 boundary.
525 def get_output (name):
526 return output_dict[format][name]
529 return re_dict[format][name]
531 def bounding_box_dimensions(fname):
533 fname = os.path.join(g_outdir, fname)
537 error ("Error opening `%s'" % fname)
539 s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
542 gs = map (lambda x: string.atoi (x), s.groups ())
543 return (int (gs[2] - gs[0] + 0.5),
544 int (gs[3] - gs[1] + 0.5))
549 sys.stderr.write (str + "\n Exiting ... \n\n")
553 def compose_full_body (body, opts):
554 '''Construct the lilypond code to send to Lilypond.
555 Add stuff to BODY using OPTS as options.'''
556 music_size = default_music_fontsize
557 if g_force_music_fontsize:
558 music_size = g_force_music_fontsize
562 if not g_force_music_fontsize:
563 m = re.match ('([0-9]+)pt', o)
565 music_size = string.atoi(m.group (1))
567 m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o)
569 f = float (m.group (1))
570 indent = 'indent = %f\\%s' % (f, m.group (2))
572 m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o)
574 f = float (m.group (1))
575 linewidth = 'linewidth = %f\\%s' % (f, m.group (2))
577 if re.search ('\\\\score', body):
581 if 'fragment' in opts:
583 if 'nofragment' in opts:
586 if is_fragment and not 'multiline' in opts:
587 opts.append('singleline')
589 if 'singleline' in opts:
590 linewidth = 'linewidth = -1.0'
592 l = __main__.paperguru.get_linewidth ()
593 linewidth = 'linewidth = %f\pt' % l
595 if 'noindent' in opts:
596 indent = 'indent = 0.0\mm'
599 m= re.search ('relative(.*)', o)
603 v = string.atoi (m.group (1))
610 pitch = pitch + '\,' * v
612 pitch = pitch + '\'' * v
614 body = '\\relative %s { %s }' %(pitch, body)
623 optstring = string.join (opts, ' ')
624 optstring = re.sub ('\n', ' ', optstring)
626 %% Generated automatically by: lilypond-book.py
628 \include "paper%d.ly"
633 ''' % (optstring, music_size, linewidth, indent) + body
635 # ughUGH not original options
638 def scan_html_preamble (chunks):
641 def scan_latex_preamble(chunks):
642 # First we want to scan the \documentclass line
643 # it should be the first non-comment line.
644 # The only thing we really need to know about the \documentclass line
645 # is if there are one or two columns to begin with.
648 if chunks[idx][0] == 'ignore':
651 m = get_re ('header').match(chunks[idx][1])
653 error ("Latex documents must start with a \documentclass command")
655 options = re.split (',[\n \t]*', m.group(1)[1:-1])
658 if 'twocolumn' in options:
659 paperguru.m_num_cols = 2
662 # Then we add everythin before \begin{document} to
663 # paperguru.m_document_preamble so that we can later write this header
664 # to a temporary file in find_latex_dims() to find textwidth.
665 while idx < len(chunks) and chunks[idx][0] != 'preamble-end':
666 if chunks[idx] == 'ignore':
669 paperguru.m_document_preamble.append(chunks[idx][1])
671 paperguru.find_latex_dims()
673 def scan_texi_preamble (chunks):
674 # this is not bulletproof..., it checks the first 10 chunks
675 for c in chunks[:10]:
677 for s in ('afourpaper', 'afourwide', 'letterpaper',
678 'afourlatex', 'smallbook'):
679 if string.find(c[1], "@%s" % s) != -1:
680 paperguru.m_papersize = s
683 def scan_preamble (chunks):
684 if __main__.format == 'html':
685 scan_html_preamble (chunks)
686 elif __main__.format == 'latex':
687 scan_latex_preamble (chunks)
688 elif __main__.format == 'texi':
689 scan_texi_preamble (chunks)
692 def completize_preamble (chunks):
693 if __main__.format != 'latex':
695 pre_b = post_b = graphics_b = None
697 if chunk[0] == 'preamble-end':
699 if chunk[0] == 'input':
700 m = get_re('def-pre-re').search(chunk[1])
703 if chunk[0] == 'input':
704 m = get_re('def-post-re').search(chunk[1])
708 if chunk[0] == 'input':
709 m = get_re('usepackage-graphics').search(chunk[1])
713 while x < len (chunks) and chunks[x][0] != 'preamble-end':
720 chunks.insert(x, ('input', get_output ('output-default-pre')))
722 chunks.insert(x, ('input', get_output ('output-default-post')))
724 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
730 def find_file (name):
732 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
736 return (sys.stdin.read (), '<stdin>')
739 for a in include_path:
741 nm = os.path.join (a, name)
743 __main__.read_files.append (nm)
748 sys.stderr.write ("Reading `%s'\n" % nm)
749 return (f.read (), nm)
751 error ("File not found `%s'\n" % name)
754 def do_ignore(match_object):
755 return [('ignore', match_object.group('code'))]
756 def do_preamble_end(match_object):
757 return [('preamble-end', match_object.group('code'))]
759 def make_verbatim(match_object):
760 return [('verbatim', match_object.group('code'))]
762 def make_verb(match_object):
763 return [('verb', match_object.group('code'))]
765 def do_include_file(m):
767 return [('input', get_output ('pagebreak'))] \
768 + read_doc_file(m.group('filename')) \
769 + [('input', get_output ('pagebreak'))]
771 def do_input_file(m):
772 return read_doc_file(m.group('filename'))
774 def make_lilypond(m):
775 if m.group('options'):
776 options = m.group('options')
779 return [('input', get_output('output-lilypond-fragment') %
780 (options, m.group('code')))]
782 def make_lilypond_file(m):
785 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
786 into a @lilypond .. @end lilypond block.
790 if m.group('options'):
791 options = m.group('options')
794 (content, nm) = find_file(m.group('filename'))
795 options = "filename=%s," % nm + options
797 return [('input', get_output('output-lilypond') %
800 def make_lilypond_block(m):
804 if m.group('options'):
805 options = get_re('option-sep').split (m.group('options'))
808 options = filter(lambda s: s != '', options)
809 return [('lilypond', m.group('code'), options)]
812 if __main__.format != 'latex':
814 if m.group('num') == 'one':
815 return [('numcols', m.group('code'), 1)]
816 if m.group('num') == 'two':
817 return [('numcols', m.group('code'), 2)]
820 if __main__.format != 'latex':
822 if m.group('be') == 'begin':
823 return [('multicols', m.group('code'), int(m.group('num')))]
825 return [('multicols', m.group('code'), 1)]
828 def chop_chunks(chunks, re_name, func, use_match=0):
834 m = get_re (re_name).search (str)
836 newchunks.append (('input', str))
840 newchunks.append (('input', str[:m.start ('match')]))
842 newchunks.append (('input', str[:m.start (0)]))
843 #newchunks.extend(func(m))
844 # python 1.5 compatible:
845 newchunks = newchunks + func(m)
846 str = str [m.end(0):]
851 def determine_format (str):
852 if __main__.format == '':
854 html = re.search ('(?i)<[dh]tml', str[:200])
855 latex = re.search (r'''\\document''', str[:200])
856 texi = re.search ('@node|@setfilename', str[:200])
861 if html and not latex and not texi:
863 elif latex and not html and not texi:
865 elif texi and not html and not latex:
868 error ("can't determine format, please specify")
871 if __main__.paperguru == None:
872 if __main__.format == 'html':
874 elif __main__.format == 'latex':
876 elif __main__.format == 'texi':
879 __main__.paperguru = g
882 def read_doc_file (filename):
883 '''Read the input file, find verbatim chunks and do \input and \include
885 (str, path) = find_file(filename)
886 determine_format (str)
888 chunks = [('input', str)]
890 # we have to check for verbatim before doing include,
891 # because we don't want to include files that are mentioned
892 # inside a verbatim environment
893 chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
894 chunks = chop_chunks(chunks, 'verb', make_verb)
895 chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
897 chunks = chop_chunks(chunks, 'include', do_include_file, 1)
898 chunks = chop_chunks(chunks, 'input', do_input_file, 1)
902 taken_file_names = {}
903 def schedule_lilypond_block (chunk):
904 '''Take the body and options from CHUNK, figure out how the
905 real .ly should look, and what should be left MAIN_STR (meant
906 for the main file). The .ly is written, and scheduled in
909 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
911 TODO has format [basename, extension, extension, ... ]
914 (type, body, opts) = chunk
915 assert type == 'lilypond'
916 file_body = compose_full_body (body, opts)
917 ## Hmm, we should hash only lilypond source, and skip the
920 basename = 'lily-' + `abs(hash (file_body))`
922 m = re.search ('filename="(.*?)"', o)
924 basename = m.group (1)
925 if not taken_file_names.has_key(basename):
926 taken_file_names[basename] = 0
928 taken_file_names[basename] = taken_file_names[basename] + 1
929 basename = basename + "-%i" % taken_file_names[basename]
931 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
932 needed_filetypes = ['tex']
934 if format == 'html' or format == 'texi':
935 needed_filetypes.append ('eps')
936 needed_filetypes.append ('png')
937 if 'eps' in opts and not ('eps' in needed_filetypes):
938 needed_filetypes.append('eps')
939 pathbase = os.path.join (g_outdir, basename)
940 def f (base, ext1, ext2):
941 a = os.path.isfile(base + ext2)
942 if (os.path.isfile(base + ext1) and
943 os.path.isfile(base + ext2) and
944 os.stat(base+ext1)[stat.ST_MTIME] >
945 os.stat(base+ext2)[stat.ST_MTIME]) or \
946 not os.path.isfile(base + ext2):
949 if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
951 if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
953 if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
957 if 'printfilename' in opts:
959 m= re.match ("filename=(.*)", o)
961 newbody = newbody + get_output ("output-filename") % m.group(1)
965 if 'smallverbatim' in opts:
966 newbody = output_verbatim (body, 1)
967 elif 'verbatim' in opts:
968 newbody = output_verbatim (body, 0)
971 m = re.search ('intertext="(.*?)"', o)
973 newbody = newbody + m.group (1) + "\n\n"
975 if 'noinline' in opts:
976 s = 'output-noinline'
977 elif format == 'latex':
982 else: # format == 'html' or format == 'texi':
984 newbody = newbody + get_output (s) % {'fn': basename }
985 return ('lilypond', newbody, opts, todo, basename)
987 def process_lilypond_blocks(chunks):#ugh rename
989 # Count sections/chapters.
991 if c[0] == 'lilypond':
992 c = schedule_lilypond_block (c)
993 elif c[0] == 'numcols':
994 paperguru.m_num_cols = c[2]
995 elif c[0] == 'multicols':
996 paperguru.m_multicols = c[2]
1003 sys.stderr.write ("invoking `%s'\n" % cmd)
1004 st = os.system (cmd)
1006 error ('Error command exited with value %d\n' % st)
1009 def quiet_system (cmd, name):
1011 progress ( _("Running %s...") % name)
1012 cmd = cmd + ' 1> /dev/null 2> /dev/null'
1016 def get_bbox (filename):
1017 system ('gs -sDEVICE=bbox -q -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename, filename))
1019 box = open (filename + '.bbox').read()
1020 m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
1023 gr = map (string.atoi, m.groups ())
1027 def make_pixmap (name):
1028 bbox = get_bbox (name + '.eps')
1030 fo = open (name + '.trans.eps' , 'w')
1031 fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
1036 x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1037 y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1039 cmd = r'''gs -g%dx%d -sDEVICE=pnggray -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit > %s'''
1041 cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
1044 status = system (cmd)
1049 os.unlink (name + '.png')
1050 error ("Removing output file")
1052 def compile_all_files (chunks):
1059 if c[0] != 'lilypond':
1068 if base + '.ly' not in tex:
1069 tex.append (base + '.ly')
1070 elif e == 'png' and g_do_pictures:
1076 # fixme: be sys-independent.
1078 if g_outdir and x[0] != '/' :
1079 x = os.path.join (g_here_dir, x)
1082 incs = map (incl_opt, include_path)
1083 lilyopts = string.join (incs, ' ' )
1085 lilyopts = lilyopts + ' --dependencies '
1087 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1088 texfiles = string.join (tex, ' ')
1089 cmd = 'lilypond --header=texidoc %s %s %s' \
1090 % (lilyopts, g_extra_opts, texfiles)
1095 # Ugh, fixing up dependencies for .tex generation
1098 depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1103 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1109 cmd = r"echo $TEXMF; tex '\nonstopmode \input %s'" % e
1110 quiet_system (cmd, 'TeX')
1112 cmd = r"dvips -E -o %s %s" % (e + '.eps', e)
1113 quiet_system (cmd, 'dvips')
1121 def update_file (body, name):
1123 write the body if it has changed
1134 f = open (name , 'w')
1141 def getopt_args (opts):
1142 "Construct arguments (LONG, SHORT) for getopt from list of options."
1147 short = short + o[1]
1155 return (short, long)
1157 def option_help_str (o):
1158 "Transform one option description (4-tuple ) into neatly formatted string"
1176 return ' ' + sh + sep + long + arg
1179 def options_help_str (opts):
1180 "Convert a list of options into a neatly formatted string"
1186 s = option_help_str (o)
1187 strs.append ((s, o[3]))
1193 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0]) + 3), s[1])
1197 sys.stdout.write('''Usage: lilypond-book [options] FILE\n
1198 Generate hybrid LaTeX input from Latex + lilypond
1201 sys.stdout.write (options_help_str (option_definitions))
1202 sys.stdout.write (r'''Warning all output is written in the CURRENT directory
1206 Report bugs to bug-lilypond@gnu.org.
1208 Written by Tom Cato Amundsen <tca@gnu.org> and
1209 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1215 def write_deps (fn, target, chunks):
1217 sys.stderr.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1218 f = open (os.path.join(g_outdir, fn), 'w')
1219 f.write ('%s%s: ' % (g_dep_prefix, target))
1220 for d in read_files:
1224 if c[0] == 'lilypond':
1225 (type, body, opts, todo, basename) = c;
1226 basenames.append (basename)
1229 d=g_outdir + '/' + d
1231 #if not os.isfile (d): # thinko?
1232 if not re.search ('/', d):
1233 d = g_dep_prefix + d
1234 f.write ('%s.tex ' % d)
1236 #if len (basenames):
1237 # for d in basenames:
1238 # f.write ('%s.ly ' % d)
1239 # f.write (' : %s' % target)
1244 def identify (stream):
1245 stream.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1247 def print_version ():
1248 identify (sys.stdout)
1249 sys.stdout.write (r'''Copyright 1998--1999
1250 Distributed under terms of the GNU General Public License. It comes with
1255 def check_texidoc (chunks):
1258 if c[0] == 'lilypond':
1259 (type, body, opts, todo, basename) = c;
1260 pathbase = os.path.join (g_outdir, basename)
1261 if os.path.isfile (pathbase + '.texidoc'):
1262 body = '\n@include %s.texidoc\n' % basename + body
1263 c = (type, body, opts, todo, basename)
1268 ## what's this? Docme --hwn
1270 def fix_epswidth (chunks):
1273 if c[0] != 'lilypond' or 'eps' not in c[2]:
1274 newchunks.append (c)
1279 m = re.match ('magnification=([0-9.]+)', o)
1281 mag = string.atof (m.group (1))
1283 def replace_eps_dim (match, lmag = mag):
1284 filename = match.group (1)
1285 dims = bounding_box_dimensions (filename)
1287 return '%fpt' % (dims[0] *lmag)
1289 body = re.sub (r'''\\lilypondepswidth{(.*?)}''', replace_eps_dim, c[1])
1290 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1295 ##docme: why global?
1297 def do_file(input_filename):
1299 chunks = read_doc_file(input_filename)
1300 chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1301 chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1302 chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1303 chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1304 chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1305 chunks = chop_chunks(chunks, 'numcols', do_columns)
1306 chunks = chop_chunks(chunks, 'multicols', do_multicols)
1308 #for c in chunks: print "c:", c;
1310 scan_preamble(chunks)
1311 chunks = process_lilypond_blocks(chunks)
1314 if __main__.g_run_lilypond:
1315 compile_all_files (chunks)
1316 chunks = fix_epswidth (chunks)
1318 if __main__.format == 'texi':
1319 chunks = check_texidoc (chunks)
1322 chunks = completize_preamble (chunks)
1328 my_outname = outname
1329 elif input_filename == '-' or input_filename == "/dev/stdin":
1332 my_outname = os.path.basename (os.path.splitext(input_filename)[0]) + '.' + format
1333 my_depname = my_outname + '.dep'
1335 if my_outname == '-' or my_outname == '/dev/stdout':
1338 __main__.do_deps = 0
1340 foutn = os.path.join (g_outdir, my_outname)
1341 sys.stderr.write ("Writing `%s'\n" % foutn)
1342 fout = open (foutn, 'w')
1349 write_deps (my_depname, foutn, chunks)
1353 (sh, long) = getopt_args (__main__.option_definitions)
1354 (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1355 except getopt.error, msg:
1356 sys.stderr.write("error: %s" % msg)
1364 if o == '--include' or o == '-I':
1365 include_path.append (a)
1366 elif o == '--version' or o == '-v':
1369 elif o == '--verbose' or o == '-V':
1370 __main__.verbose_p = 1
1371 elif o == '--format' or o == '-f':
1373 elif o == '--outname' or o == '-o':
1376 sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1379 elif o == '--help' or o == '-h':
1381 elif o == '--no-lily' or o == '-n':
1382 __main__.g_run_lilypond = 0
1383 elif o == '--dependencies' or o == '-M':
1385 elif o == '--default-music-fontsize':
1386 default_music_fontsize = string.atoi (a)
1387 elif o == '--default-lilypond-fontsize':
1388 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1389 default_music_fontsize = string.atoi (a)
1390 elif o == '--extra-options':
1392 elif o == '--force-music-fontsize':
1393 g_force_music_fontsize = string.atoi(a)
1394 elif o == '--force-lilypond-fontsize':
1395 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1396 g_force_music_fontsize = string.atoi(a)
1397 elif o == '--dep-prefix':
1399 elif o == '--no-pictures':
1401 elif o == '--no-music':
1403 elif o == '--read-lys':
1405 elif o == '--outdir':
1408 identify (sys.stderr)
1410 if os.path.isfile(g_outdir):
1411 error ("outdir is a file: %s" % g_outdir)
1412 if not os.path.exists(g_outdir):
1414 setup_environment ()
1415 for input_filename in files:
1416 do_file(input_filename)
1419 # Petr, ik zou willen dat ik iets zinvoller deed,
1420 # maar wat ik kan ik doen, het verandert toch niets?