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.
42 ################################################################
43 # Users of python modules should include this snippet
44 # and customize variables below.
46 # We'll suffer this path init stuff as long as we don't install our
47 # python packages in <prefix>/lib/pythonx.y (and don't kludge around
48 # it as we do with teTeX on Red Hat Linux: set some environment var
49 # (PYTHONPATH) in profile)
51 # If set, LILYPONDPREFIX must take prevalence
52 # if datadir is not set, we're doing a build and LILYPONDPREFIX
53 import getopt
, os
, sys
54 datadir
= '@local_lilypond_datadir@'
55 if not os
.path
.isdir (datadir
):
56 datadir
= '@lilypond_datadir@'
57 if os
.environ
.has_key ('LILYPONDPREFIX') :
58 datadir
= os
.environ
['LILYPONDPREFIX']
59 while datadir
[-1] == os
.sep
:
62 sys
.path
.insert (0, os
.path
.join (datadir
, 'python'))
65 #if __name__ == '__main__':
72 program_name
= 'lilypond-book'
75 original_dir
= os
.getcwd ()
76 #temp_dir = os.path.join (original_dir, '%s.dir' % program_name)
80 preview_resolution
= 90
83 ## ly2dvi: silly name?
84 ## do -P or -p by default?
85 ##help_summary = _ ("Run LilyPond using LaTeX for titling")
86 help_summary
= _ ("Process LilyPond snippets in hybrid html, LaTeX or texinfo document")
87 copyright
= ('Tom Cato Amundsen <tca@gnu.org>',
88 'Han-Wen Nienhuys <hanwen@cs.uu.nl>')
90 option_definitions
= [
91 (_ ("EXT"), 'f', 'format', _ ("use output format EXT (texi [default], texi-html, latex, html)")),
92 (_ ("DIM"), '', 'default-music-fontsize', _ ("default fontsize for music. DIM is assumed to be in points")),
93 (_ ("DIM"), '', 'default-lilypond-fontsize', _ ("deprecated, use --default-music-fontsize")),
94 (_ ("OPT"), '', 'extra-options', _ ("pass OPT quoted to the lilypond command line")),
95 (_ ("DIM"), '', 'force-music-fontsize', _ ("force fontsize for all inline lilypond. DIM is assumed be to in points")),
96 (_ ("DIM"), '', 'force-lilypond-fontsize', _ ("deprecated, use --force-music-fontsize")),
97 ('', 'h', 'help', _ ("this help")),
98 (_ ("DIR"), 'I', 'include', _ ("include path")),
99 ('', 'M', 'dependencies', _ ("write dependencies")),
100 (_ ("PREF"), '', 'dep-prefix', _ ("prepend PREF before each -M dependency")),
101 ('', 'n', 'no-lily', _ ("don't run lilypond")),
102 ('', '', 'no-pictures', _ ("don't generate pictures")),
103 ('', '', 'no-music', _ ("strip all lilypond blocks from output")),
104 ('', '', 'read-lys', _ ("don't write ly files.")),
105 (_ ("FILE"), 'o', 'outname', _ ("filename main output file")),
106 (_ ("FILE"), '', 'outdir', _ ("where to place generated files")),
107 (_ ('RES'), '', 'preview-resolution',
108 _ ("set the resolution of the preview to RES")),
109 ('', 'V', 'verbose', _ ("verbose")),
110 ('', 'v', 'version', _ ("print version information")),
111 ('', 'w', 'warranty', _ ("show warranty and copyright")),
114 # format specific strings, ie. regex-es for input, and % strings for output
118 include_path
= [os
.getcwd ()]
121 lilypond_cmd
= 'lilypond'
122 #lilypond_cmd = 'valgrind --suppressions=/home/hanwen/usr/src/guile-1.6.supp --num-callers=10 /home/hanwen/usr/src/lilypond/lily/out/lilypond'
126 g_here_dir
= os
.getcwd ()
129 g_force_music_fontsize
= 0
139 default_music_fontsize
= 16
140 default_text_fontsize
= 12
146 self
.m_document_preamble
= []
150 def find_latex_dims (self
):
152 fname
= os
.path
.join (g_outdir
, "lily-tmp.tex")
154 fname
= "lily-tmp.tex"
156 f
= open (fname
, "w")
158 error ("Error creating temporary file '%s'" % fname
)
160 for s
in self
.m_document_preamble
:
165 \typeout{\columnsep \the\columnsep}
166 \typeout{\textwidth \the\textwidth}
171 re_dim
= re
.compile (r
"\\(\w+)\s+(\d+\.\d+)")
173 cmd
= "latex '\\nonstopmode \input %s'" % fname
174 # Ugh. (La)TeX writes progress and error messages on stdout
176 cmd
+= ' 1>/dev/stderr'
177 status
= ly
.system (cmd
, ignore_error
= 1)
178 signal
= 0xf & status
179 exit_status
= status
>> 8
182 ly
.error (_ ("LaTeX failed."))
183 ly
.error (_ ("The error log is as follows:"))
187 lns
= open ('lily-tmp.log').readlines ()
192 sys
.stderr
.write (ln
)
193 if re
.match ('^!', ln
):
200 countdown
= countdown
-1
202 sys
.stderr
.write (" ... (further messages elided)...\n")
205 lns
= open ('lily-tmp.log').readlines ()
207 ln
= string
.strip (ln
)
208 m
= re_dim
.match (ln
)
210 if m
.groups ()[0] in ('textwidth', 'columnsep'):
211 self
.__dict
__['m_%s' % m
.groups ()[0]] = float (m
.groups ()[1])
215 os
.remove (os
.path
.splitext (fname
)[0]+".aux")
216 os
.remove (os
.path
.splitext (fname
)[0]+".log")
220 if not self
.__dict
__.has_key ('m_textwidth'):
223 def get_linewidth (self
):
224 if self
.m_num_cols
== 1:
227 w
= (self
.m_textwidth
- self
.m_columnsep
)/2
228 if self
.m_multicols
> 1:
229 return (w
- self
.m_columnsep
* (self
.m_multicols
-1)) \
236 self
.m_papersize
= 'letterpaper'
238 def get_linewidth (self
):
239 return html_linewidths
[self
.m_papersize
][self
.m_fontsize
]
243 self
.m_papersize
= 'letterpaper'
245 def get_linewidth (self
):
246 return texi_linewidths
[self
.m_papersize
][self
.m_fontsize
]
252 def em2pt (x
, fontsize
= 10):
253 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize
] * x
254 def ex2pt (x
, fontsize
= 10):
255 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize
] * x
260 dimension_conversion_dict
={
262 'cm': lambda x
: mm2pt (10*x
),
269 # Convert numeric values, with or without specific dimension, to floats.
271 def conv_dimen_to_float (value
):
272 if type (value
) == type (""):
273 m
= re
.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value
)
276 num
= string
.atof (m
.group (1))
277 conv
= dimension_conversion_dict
[m
.group (2)]
281 elif re
.match ("^[0-9.]+$",value
):
282 value
= float (value
)
287 'afourpaper': {12: mm2pt (160)},
288 'afourwide': {12: in2pt (6.5)},
289 'afourlatex': {12: mm2pt (150)},
290 'smallbook': {12: in2pt (5)},
291 'letterpaper': {12: in2pt (6)}}
294 'afourpaper': {12: mm2pt (160)},
295 'afourwide': {12: in2pt (6.5)},
296 'afourlatex': {12: mm2pt (150)},
297 'smallbook': {12: in2pt (5)},
298 'letterpaper': {12: in2pt (6)}}
305 'output-lilypond': '''<lilypond%s>
308 'output-filename' : r
'''
311 <pre>%s</pre></a>:''',
312 'output-lilypond-fragment': '''<lilypond%s>
313 \context Staff\context Voice{ %s }
315 'output-noinline': r
'''
316 <!-- generated: %(fn)s.png !-->
320 # Verbatim text is always finished with \n. FIXME: For HTML,
321 # this newline should be removed.
322 'output-verbatim': r
'''<pre>
324 # Verbatim text is always finished with \n. FIXME: For HTML,
325 # this newline should be removed.
326 'output-small-verbatim': r
'''<font size=-1><pre>
328 ## Ugh we need to differentiate on origin:
329 ## lilypond-block origin wants an extra <p>, but
330 ## inline music doesn't.
331 ## possibly other center options?
333 <a href="%(fn)s.png">
334 <img align="center" valign="center" border="0" src="%(fn)s.png" alt="[picture of music]"></a>
341 'output-lilypond-fragment' : r
'''\begin[eps,singleline,%s]{lilypond}
348 'output-filename' : r
'''\verb+%s+:\\
352 'output-lilypond': r
'''\begin[%s]{lilypond}
356 # verbatim text is always finished with \n
357 'output-verbatim': r
'''\begin{verbatim}
360 # verbatim text is always finished with \n
361 'output-small-verbatim': r
'''{\small\begin{verbatim}
364 'output-default-post': "\\def\postLilypondExample{}\n",
365 'output-default-pre': "\\def\preLilypondExample{}\n",
366 'usepackage-graphics': '\\usepackage{graphics}\n',
367 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s}}',
368 'output-noinline': r
'''
369 %% generated: %(fn)s.eps
371 'output-latex-quoted': r
'''{\preLilypondExample
373 \postLilypondExample}''',
374 'output-latex-noquote': r
'''{\parindent 0pt
377 \postLilypondExample}''',
378 'pagebreak': r
'\pagebreak',
384 'output-lilypond': '''@lilypond[%s]
388 'output-filename' : r
'''@ifnothtml
395 'output-lilypond-fragment': '''@lilypond[%s]
396 \context Staff\context Voice{ %s }
398 'output-noinline': r
'''
399 @c generated: %(fn)s.png
402 # verbatim text is always finished with \n
403 'output-small-verbatim': r
'''@smallexample
406 # verbatim text is always finished with \n
407 'output-verbatim': r
'''@example
410 # do some tweaking: @ is needed in some ps stuff.
412 # ugh, the <p> below breaks inline images...
413 'output-texi-noquote': r
'''@tex
421 <p><a href="%(fn)s.png">
422 <img border=0 src="%(fn)s.png" alt="[picture of music]">
426 'output-texi-quoted': r
'''@quotation
434 <a href="%(fn)s.png">
435 <img border=0 src="%(fn)s.png" alt="[picture of music]">
444 def output_verbatim (body
, small
):
447 body
= re
.sub ('&', '&', body
)
448 body
= re
.sub ('>', '>', body
)
449 body
= re
.sub ('<', '<', body
)
450 elif format
== 'texi':
451 # clumsy workaround for python 2.2 pre bug.
452 body
= re
.sub ('@', '@@', body
)
453 body
= re
.sub ('{', '@{', body
)
454 body
= re
.sub ('}', '@}', body
)
457 key
= 'output-small-verbatim'
459 key
= 'output-verbatim'
460 return get_output (key
) % body
463 # Warning: This uses extended regular expressions. Treat with care.
467 # (?P<name>regex) -- assign result of REGEX to NAME
468 # *? -- match non-greedily.
469 # (?m) -- multiline regex: make ^ and $ match at each line
470 # (?s) -- make the dot match all characters including newline
476 'preamble-end': no_match
,
477 'landscape': no_match
,
478 'verbatim': r
'''(?s)(?P<code><pre>\s.*?</pre>\s)''',
479 'verb': r
'''(?P<code><pre>.*?</pre>)''',
480 'lilypond-file': r
'(?m)(?P<match><lilypondfile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</lilypondfile>)',
481 'lilypond' : '(?m)(?P<match><lilypond((?P<options>[^:]*):)(?P<code>.*?)/>)',
482 'lilypond-block': r
'''(?ms)(?P<match><lilypond(?P<options>[^>]+)?>(?P<code>.*?)</lilypond>)''',
483 'option-sep' : '\s*',
484 'intertext': r
',?\s*intertext=\".*?\"',
485 'multiline-comment': r
"(?sm)\s*(?!@c\s+)(?P<code><!--\s.*?!-->)\s",
486 'singleline-comment': no_match
,
488 'multicols': no_match
,
492 'input': r
'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
493 'include': r
'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
494 'option-sep' : ',\s*',
495 'header': r
"\n*\\documentclass\s*(\[.*?\])?",
496 'preamble-end': r
'(?P<code>\\begin\s*{document})',
497 'verbatim': r
"(?s)(?P<code>\\begin\s*{verbatim}.*?\\end{verbatim})",
498 'verb': r
"(?P<code>\\verb(?P<del>.).*?(?P=del))",
499 'lilypond-file': r
'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
500 'lilypond' : r
'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
501 'lilypond-block': r
"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
502 'def-post-re': r
"\\def\\postLilypondExample",
503 'def-pre-re': r
"\\def\\preLilypondExample",
504 'usepackage-graphics': r
"\usepackage\s*{graphics}",
505 'intertext': r
',?\s*intertext=\".*?\"',
506 'multiline-comment': no_match
,
507 'singleline-comment': r
"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
508 'numcols': r
"(?P<code>\\(?P<num>one|two)column)",
509 'multicols': r
"(?P<code>\\(?P<be>begin|end)\s*{multicols}({(?P<num>\d+)?})?)",
512 # why do we have distinction between @mbinclude and @include?
515 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
518 'preamble-end': no_match
,
519 'landscape': no_match
,
520 'verbatim': r
'''(?s)(?P<code>@example\s.*?@end example\s)''',
521 'verb': r
'''(?P<code>@code{.*?})''',
522 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
523 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
524 'lilypond-block': r
'''(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end +lilypond)\s''',
525 'option-sep' : ',\s*',
526 'intertext': r
',?\s*intertext=\".*?\"',
527 'multiline-comment': r
"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
528 'singleline-comment': r
"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
530 'multicols': no_match
,
535 for r
in re_dict
.keys ():
538 for k
in olddict
.keys ():
540 newdict
[k
] = re
.compile (olddict
[k
])
542 print 'invalid regexp: %s' % olddict
[k
]
544 ## we'd like to catch and reraise a more
545 ## detailed error, but alas, the exceptions
546 ## changed across the 1.5/2.1 boundary.
562 def get_output (name
):
563 return output_dict
[format
][name
]
566 return re_dict
[format
][name
]
568 def bounding_box_dimensions (fname
):
570 fname
= os
.path
.join (g_outdir
, fname
)
574 error ("Error opening `%s'" % fname
)
576 s
= re
.search ('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
579 gs
= map (lambda x
: string
.atoi (x
), s
.groups ())
580 return (int (gs
[2] - gs
[0] + 0.5),
581 int (gs
[3] - gs
[1] + 0.5))
586 sys
.stderr
.write ("\n\n" + str + "\nExiting ... \n\n")
590 def compose_full_body (body
, opts
):
591 '''Construct the lilypond code to send to Lilypond.
592 Add stuff to BODY using OPTS as options.'''
593 music_size
= default_music_fontsize
594 if g_force_music_fontsize
:
595 music_size
= g_force_music_fontsize
600 if not g_force_music_fontsize
:
601 m
= re
.match ('([0-9]+)pt', o
)
603 music_size
= string
.atoi (m
.group (1))
605 m
= re
.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o
)
607 f
= float (m
.group (1))
608 indent
= 'indent = %f\\%s' % (f
, m
.group (2))
610 m
= re
.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o
)
612 f
= float (m
.group (1))
613 linewidth
= 'linewidth = %f\\%s' % (f
, m
.group (2))
615 if re
.search ('\\\\score', body
):
619 if 'fragment' in opts
:
621 if 'nofragment' in opts
:
624 if is_fragment
and not 'multiline' in opts
:
625 opts
.append ('singleline')
627 if 'singleline' in opts
:
629 linewidth
= 'linewidth = -1.0'
631 indent
= 'indent = 0.0\mm'
634 l
= paperguru
.get_linewidth ()
635 linewidth
= 'linewidth = %f\pt' % l
637 if 'noindent' in opts
:
638 indent
= 'indent = 0.0\mm'
644 \remove Time_signature_engraver
649 m
= re
.search ('relative(.*)', o
)
653 v
= string
.atoi (m
.group (1))
660 pitch
= pitch
+ '\,' * v
662 pitch
= pitch
+ '\'' * v
664 body
= '\\relative %s { %s }' % (pitch
, body
)
676 optstring
= string
.join (opts
, ' ')
677 optstring
= re
.sub ('\n', ' ', optstring
)
679 %% Generated automatically by: lilypond-book.py
681 \include "paper%d.ly"
687 ''' % (optstring
, music_size
, linewidth
, indent
, notime
) + body
689 # ughUGH not original options
692 def scan_html_preamble (chunks
):
695 def scan_latex_preamble (chunks
):
696 # First we want to scan the \documentclass line
697 # it should be the first non-comment line.
698 # The only thing we really need to know about the \documentclass line
699 # is if there are one or two columns to begin with.
702 if chunks
[idx
][0] == 'ignore':
705 m
= get_re ('header').match (chunks
[idx
][1])
707 error ("Latex documents must start with a \documentclass command")
709 options
= re
.split (',[\n \t]*', m
.group (1)[1:-1])
712 if 'twocolumn' in options
:
713 paperguru
.m_num_cols
= 2
717 # Then we add everything before \begin{document} to
718 # paperguru.m_document_preamble so that we can later write this header
719 # to a temporary file in find_latex_dims() to find textwidth.
720 while idx
< len (chunks
) and chunks
[idx
][0] != 'preamble-end':
721 if chunks
[idx
] == 'ignore':
724 paperguru
.m_document_preamble
.append (chunks
[idx
][1])
727 if len (chunks
) == idx
:
728 error ("Didn't find end of preamble (\\begin{document})")
730 paperguru
.find_latex_dims ()
732 def scan_texi_preamble (chunks
):
733 # this is not bulletproof..., it checks the first 10 chunks
734 for c
in chunks
[:10]:
736 for s
in ('afourpaper', 'afourwide', 'letterpaper',
737 'afourlatex', 'smallbook'):
738 if string
.find (c
[1], "@%s" % s
) != -1:
739 paperguru
.m_papersize
= s
742 def scan_preamble (chunks
):
745 scan_html_preamble (chunks
)
746 elif format
== 'latex':
747 scan_latex_preamble (chunks
)
748 elif format
== 'texi':
749 scan_texi_preamble (chunks
)
752 def completize_preamble (chunks
):
754 if format
!= 'latex':
756 pre_b
= post_b
= graphics_b
= None
758 if chunk
[0] == 'preamble-end':
760 if chunk
[0] == 'input':
761 m
= get_re ('def-pre-re').search (chunk
[1])
764 if chunk
[0] == 'input':
765 m
= get_re ('def-post-re').search (chunk
[1])
769 if chunk
[0] == 'input':
770 m
= get_re ('usepackage-graphics').search (chunk
[1])
774 while x
< len (chunks
) and chunks
[x
][0] != 'preamble-end':
777 if x
== len (chunks
):
781 chunks
.insert (x
, ('input', get_output ('output-default-pre')))
783 chunks
.insert (x
, ('input', get_output ('output-default-post')))
785 chunks
.insert (x
, ('input', get_output ('usepackage-graphics')))
791 def find_file (name
):
793 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
797 return (sys
.stdin
.read (), '<stdin>')
800 for a
in include_path
:
802 nm
= os
.path
.join (a
, name
)
805 read_files
.append (nm
)
810 sys
.stderr
.write ("Reading `%s'\n" % nm
)
811 return (f
.read (), nm
)
813 error ("File not found `%s'\n" % name
)
816 def do_ignore (match_object
):
817 return [('ignore', match_object
.group ('code'))]
818 def do_preamble_end (match_object
):
819 return [('preamble-end', match_object
.group ('code'))]
821 def make_verbatim (match_object
):
822 return [('verbatim', match_object
.group ('code'))]
824 def make_verb (match_object
):
825 return [('verb', match_object
.group ('code'))]
827 def do_include_file (m
):
829 return [('input', get_output ('pagebreak'))] \
830 + read_doc_file (m
.group ('filename')) \
831 + [('input', get_output ('pagebreak'))]
833 def do_input_file (m
):
834 return read_doc_file (m
.group ('filename'))
836 def make_lilypond (m
):
837 if m
.group ('options'):
838 options
= m
.group ('options')
841 return [('input', get_output ('output-lilypond-fragment') %
842 (options
, m
.group ('code')))]
844 def make_lilypond_file (m
):
847 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
848 into a @lilypond .. @end lilypond block.
852 if m
.group ('options'):
853 options
= m
.group ('options')
856 (content
, nm
) = find_file (m
.group ('filename'))
857 options
= "filename=%s," % nm
+ options
859 return [('input', get_output ('output-lilypond') %
862 def make_lilypond_block (m
):
866 if m
.group ('options'):
867 options
= get_re ('option-sep').split (m
.group ('options'))
870 options
= filter (lambda s
: s
!= '', options
)
871 return [('lilypond', m
.group ('code'), options
)]
875 if format
!= 'latex':
877 if m
.group ('num') == 'one':
878 return [('numcols', m
.group ('code'), 1)]
879 if m
.group ('num') == 'two':
880 return [('numcols', m
.group ('code'), 2)]
882 def do_multicols (m
):
884 if format
!= 'latex':
886 if m
.group ('be') == 'begin':
887 return [('multicols', m
.group ('code'), int (m
.group ('num')))]
889 return [('multicols', m
.group ('code'), 1)]
892 def chop_chunks (chunks
, re_name
, func
, use_match
=0):
898 m
= get_re (re_name
).search (str)
900 newchunks
.append (('input', str))
904 newchunks
.append (('input', str[:m
.start ('match')]))
906 newchunks
.append (('input', str[:m
.start (0)]))
907 #newchunks.extend (func (m))
908 # python 1.5 compatible:
909 newchunks
= newchunks
+ func (m
)
910 str = str [m
.end (0):]
915 def determine_format (str):
918 html
= re
.search ('(?i)<[dh]tml', str[:200])
919 latex
= re
.search (r
'''\\document''', str[:200])
920 texi
= re
.search ('@node|@setfilename', str[:200])
925 if html
and not latex
and not texi
:
927 elif latex
and not html
and not texi
:
929 elif texi
and not html
and not latex
:
932 error ("can't determine format, please specify")
936 if paperguru
== None:
939 elif format
== 'latex':
941 elif format
== 'texi':
947 def read_doc_file (filename
):
948 '''Read the input file, find verbatim chunks and do \input and \include
950 (str, path
) = find_file (filename
)
951 determine_format (str)
953 chunks
= [('input', str)]
955 # we have to check for verbatim before doing include,
956 # because we don't want to include files that are mentioned
957 # inside a verbatim environment
958 chunks
= chop_chunks (chunks
, 'verbatim', make_verbatim
)
960 chunks
= chop_chunks (chunks
, 'verb', make_verb
)
961 chunks
= chop_chunks (chunks
, 'multiline-comment', do_ignore
)
963 chunks
= chop_chunks (chunks
, 'include', do_include_file
, 1)
964 chunks
= chop_chunks (chunks
, 'input', do_input_file
, 1)
968 taken_file_names
= {}
969 def schedule_lilypond_block (chunk
):
970 '''Take the body and options from CHUNK, figure out how the
971 real .ly should look, and what should be left MAIN_STR (meant
972 for the main file). The .ly is written, and scheduled in
975 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
977 TODO has format [basename, extension, extension, ... ]
979 (type, body
, opts
) = chunk
980 assert type == 'lilypond'
981 file_body
= compose_full_body (body
, opts
)
982 ## Hmm, we should hash only lilypond source, and skip the
985 basename
= 'lily-' + `
abs (hash (file_body
))`
987 m
= re
.search ('filename="(.*?)"', o
)
989 basename
= m
.group (1)
990 if not taken_file_names
.has_key (basename
):
991 taken_file_names
[basename
] = 0
993 taken_file_names
[basename
] = taken_file_names
[basename
] + 1
994 basename
= basename
+ "-%i" % taken_file_names
[basename
]
996 update_file (file_body
, os
.path
.join (g_outdir
, basename
) + '.ly')
997 needed_filetypes
= ['tex']
999 if format
== 'html' or g_make_html
:
1000 needed_filetypes
.append ('eps')
1001 needed_filetypes
.append ('png')
1002 if 'eps' in opts
and not ('eps' in needed_filetypes
):
1003 needed_filetypes
.append ('eps')
1004 pathbase
= os
.path
.join (g_outdir
, basename
)
1005 def f (base
, ext1
, ext2
):
1006 a
= os
.path
.isfile (base
+ ext2
)
1007 if (os
.path
.isfile (base
+ ext1
) and
1008 os
.path
.isfile (base
+ ext2
) and
1009 os
.stat (base
+ext1
)[stat
.ST_MTIME
] >
1010 os
.stat (base
+ext2
)[stat
.ST_MTIME
]) or \
1011 not os
.path
.isfile (base
+ ext2
):
1014 if 'tex' in needed_filetypes
and f (pathbase
, '.ly', '.tex'):
1016 if 'eps' in needed_filetypes
and f (pathbase
, '.tex', '.eps'):
1018 if 'png' in needed_filetypes
and f (pathbase
, '.eps', '.png'):
1022 if 'printfilename' in opts
:
1024 m
= re
.match ("filename=(.*)", o
)
1026 newbody
= newbody
+ get_output ("output-filename") % (m
.group (1), basename
+ '.ly', m
.group (1))
1030 if 'smallverbatim' in opts
:
1031 newbody
= newbody
+ output_verbatim (body
, 1)
1032 elif 'verbatim' in opts
:
1033 newbody
= newbody
+ output_verbatim (body
, 0)
1036 m
= re
.search ('intertext="(.*?)"', o
)
1038 newbody
= newbody
+ "\n"
1039 if format
== 'texi':
1040 newbody
= newbody
+ "@noindent\n"
1041 elif format
== 'latex':
1042 newbody
= newbody
+ "\\noindent\n"
1043 newbody
= newbody
+ m
.group (1) + "\n"
1045 if 'noinline' in opts
:
1046 s
= 'output-noinline'
1047 elif format
== 'latex':
1051 if 'noquote' in opts
:
1052 s
= 'output-latex-noquote'
1054 s
= 'output-latex-quoted'
1055 elif format
== 'texi':
1056 if 'noquote' in opts
:
1057 s
= 'output-texi-noquote'
1059 s
= 'output-texi-quoted'
1060 else: # format == 'html'
1062 newbody
= newbody
+ get_output (s
) % {'fn': basename
}
1063 return ('lilypond', newbody
, opts
, todo
, basename
)
1065 def process_lilypond_blocks (chunks
):#ugh rename
1067 # Count sections/chapters.
1069 if c
[0] == 'lilypond':
1070 c
= schedule_lilypond_block (c
)
1071 elif c
[0] == 'numcols':
1072 paperguru
.m_num_cols
= c
[2]
1073 elif c
[0] == 'multicols':
1074 paperguru
.m_multicols
= c
[2]
1075 newchunks
.append (c
)
1078 def compile_all_files (chunks
):
1085 if c
[0] != 'lilypond':
1094 if base
+ '.ly' not in tex
:
1095 tex
.append (base
+ '.ly')
1096 elif e
== 'png' and g_do_pictures
:
1102 # fixme: be sys-independent.
1104 if g_outdir
and x
[0] != '/' :
1105 x
= os
.path
.join (g_here_dir
, x
)
1108 incs
= map (incl_opt
, include_path
)
1109 lilyopts
= string
.join (incs
)
1111 lilyopts
+= ' --dependencies'
1113 lilyopts
+= ' --dep-prefix=' + g_outdir
+ '/'
1114 lilyopts
+= ' --header=texidoc'
1115 texfiles
= string
.join (tex
)
1116 cmd
= string
.join ((lilypond_cmd
, lilyopts
, g_extra_opts
,
1118 ly
.system (cmd
, ignore_error
= 0, progress_p
= 1)
1121 # Ugh, fixing up dependencies for .tex generation
1124 depfiles
=map (lambda x
: re
.sub ('(.*)\.ly', '\\1.dep',
1131 text
=re
.sub ('\n([^:\n]*):',
1132 '\n' + foutn
+ ':', text
)
1138 cmd
= r
"latex '\nonstopmode \input %s'" % file
1139 # Ugh. (La)TeX writes progress and error messages on stdout
1140 # Redirect to stderr
1141 cmd
+= ' 1>/dev/stderr'
1143 ly
.system ("dvips -E -o %s.eps %s" % (file, file))
1146 map (ly
.make_preview
, png
)
1150 def update_file (body
, name
):
1152 write the body if it has changed
1163 f
= open (name
, 'w')
1170 def write_deps (fn
, target
, chunks
):
1172 sys
.stderr
.write ('Writing `%s\'\n' % os
.path
.join (g_outdir
, fn
))
1173 f
= open (os
.path
.join (g_outdir
, fn
), 'w')
1174 f
.write ('%s%s: ' % (g_dep_prefix
, target
))
1175 for d
in read_files
:
1179 if c
[0] == 'lilypond':
1180 (type, body
, opts
, todo
, basename
) = c
;
1181 basenames
.append (basename
)
1184 d
=g_outdir
+ '/' + d
1186 #if not os.isfile (d): # thinko?
1187 if not re
.search ('/', d
):
1188 d
= g_dep_prefix
+ d
1189 f
.write ('%s.tex ' % d
)
1191 #if len (basenames):
1192 # for d in basenames:
1193 # f.write ('%s.ly ' % d)
1194 # f.write (' : %s' % target)
1199 def check_texidoc (chunks
):
1202 if c
[0] == 'lilypond':
1203 (type, body
, opts
, todo
, basename
) = c
;
1204 pathbase
= os
.path
.join (g_outdir
, basename
)
1205 if os
.path
.isfile (pathbase
+ '.texidoc'):
1206 body
= '\n@include %s.texidoc\n' % basename
+ body
1207 c
= (type, body
, opts
, todo
, basename
)
1212 ## what's this? Docme --hwn
1214 def fix_epswidth (chunks
):
1217 if c
[0] != 'lilypond' or 'eps' not in c
[2]:
1218 newchunks
.append (c
)
1223 m
= re
.match ('magnification=([0-9.]+)', o
)
1225 mag
= string
.atof (m
.group (1))
1227 def replace_eps_dim (match
, lmag
= mag
):
1228 filename
= match
.group (1)
1229 dims
= bounding_box_dimensions (filename
)
1231 return '%fpt' % (dims
[0] *lmag
)
1233 body
= re
.sub (r
'''\\lilypondepswidth{(.*?)}''', replace_eps_dim
, c
[1])
1234 newchunks
.append (('lilypond', body
, c
[2], c
[3], c
[4]))
1239 ##docme: why global?
1242 def do_file (input_filename
):
1243 chunks
= read_doc_file (input_filename
)
1244 chunks
= chop_chunks (chunks
, 'lilypond', make_lilypond
, 1)
1245 chunks
= chop_chunks (chunks
, 'lilypond-file', make_lilypond_file
, 1)
1246 chunks
= chop_chunks (chunks
, 'lilypond-block', make_lilypond_block
, 1)
1247 chunks
= chop_chunks (chunks
, 'singleline-comment', do_ignore
, 1)
1248 chunks
= chop_chunks (chunks
, 'preamble-end', do_preamble_end
)
1249 chunks
= chop_chunks (chunks
, 'numcols', do_columns
)
1250 chunks
= chop_chunks (chunks
, 'multicols', do_multicols
)
1252 #for c in chunks: print "c:", c;
1254 scan_preamble (chunks
)
1255 chunks
= process_lilypond_blocks (chunks
)
1258 global g_run_lilypond
1260 compile_all_files (chunks
)
1261 chunks
= fix_epswidth (chunks
)
1264 if format
== 'texi':
1265 chunks
= check_texidoc (chunks
)
1268 chunks
= completize_preamble (chunks
)
1273 my_outname
= outname
1274 elif input_filename
== '-' or input_filename
== "/dev/stdin":
1277 my_outname
= os
.path
.basename (os
.path
.splitext (input_filename
)[0]) + '.' + format
1278 my_depname
= my_outname
+ '.dep'
1280 if my_outname
== '-' or my_outname
== '/dev/stdout':
1286 foutn
= os
.path
.join (g_outdir
, my_outname
)
1287 sys
.stderr
.write ("Writing `%s'\n" % foutn
)
1288 fout
= open (foutn
, 'w')
1295 write_deps (my_depname
, foutn
, chunks
)
1299 (sh
, long) = ly
.getopt_args (option_definitions
)
1300 (options
, files
) = getopt
.getopt (sys
.argv
[1:], sh
, long)
1302 except getopt
.error
, msg
:
1303 sys
.stderr
.write ('\n')
1304 ly
.error (_ ("getopt says: `%s\'" % s
))
1305 sys
.stderr
.write ('\n')
1314 if o
== '--include' or o
== '-I':
1315 include_path
.append (a
)
1316 elif o
== '--version' or o
== '-v':
1317 ly
.identify (sys
.stdout
)
1319 elif o
== '--verbose' or o
== '-V':
1321 elif o
== '--format' or o
== '-f':
1323 if a
== 'texi-html':
1326 elif o
== '--outname' or o
== '-o':
1329 sys
.stderr
.write ("Lilypond-book is confused by --outname on multiple files")
1332 elif o
== '--help' or o
== '-h':
1335 elif o
== '--no-lily' or o
== '-n':
1337 elif o
== '--preview-resolution':
1338 preview_resolution
= string
.atoi (a
)
1339 elif o
== '--dependencies' or o
== '-M':
1341 elif o
== '--default-music-fontsize':
1342 default_music_fontsize
= string
.atoi (a
)
1343 elif o
== '--default-lilypond-fontsize':
1344 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1345 default_music_fontsize
= string
.atoi (a
)
1346 elif o
== '--extra-options':
1348 elif o
== '--force-music-fontsize':
1349 g_force_music_fontsize
= string
.atoi (a
)
1350 elif o
== '--force-lilypond-fontsize':
1351 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1352 g_force_music_fontsize
= string
.atoi (a
)
1353 elif o
== '--dep-prefix':
1355 elif o
== '--no-pictures':
1357 elif o
== '--no-music':
1359 elif o
== '--read-lys':
1361 elif o
== '--outdir':
1363 elif o
== '--warranty' or o
== '-w':
1364 #status = os.system ('lilypond -w')
1369 ly
.identify (sys
.stderr
)
1372 if os
.path
.isfile (g_outdir
):
1373 error ("outdir is a file: %s" % g_outdir
)
1374 if not os
.path
.exists (g_outdir
):
1379 ly
.error (_ ("no files specified on command line"))
1382 ly
.setup_environment ()
1384 for input_filename
in files
:
1385 do_file (input_filename
)
1389 # Petr, ik zou willen dat ik iets zinvoller deed,
1390 # maar wat ik kan ik doen, het verandert toch niets?