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.
48 # Handle bug in Python 1.6-2.1
50 # there are recursion limits for some patterns in Python 1.6 til 2.1.
51 # fix this by importing the 1.5.2 implementation pre instead. Fix by Mats.
53 if float (sys
.version
[0:3]) < 2.2:
63 # Attempt to fix problems with limited stack size set by Python!
64 # Sets unlimited stack size. Note that the resource module only
65 # is available on UNIX.
68 resource
.setrlimit (resource
.RLIMIT_STACK
, (-1, -1))
72 errorport
= sys
.stderr
79 gettext
.bindtextdomain ('lilypond', localedir
)
80 gettext
.textdomain ('lilypond')
87 errorport
.write (s
+ '\n')
90 program_version
= '@TOPLEVEL_VERSION@'
91 if program_version
== '@' + 'TOPLEVEL_VERSION' + '@':
92 program_version
= '1.5.53'
94 # if set, LILYPONDPREFIX must take prevalence
95 # if datadir is not set, we're doing a build and LILYPONDPREFIX
98 if os
.environ
.has_key ('LILYPONDPREFIX') :
99 datadir
= os
.environ
['LILYPONDPREFIX']
101 datadir
= '@datadir@'
103 while datadir
[-1] == os
.sep
:
104 datadir
= datadir
[:-1]
106 # Try to cater for bad installations of LilyPond, that have
107 # broken TeX setup. Just hope this doesn't hurt good TeX
108 # setups. Maybe we should check if kpsewhich can find
109 # feta16.{afm,mf,tex,tfm}, and only set env upon failure.
111 'MFINPUTS' : datadir
+ '/mf:',
112 'TEXINPUTS': datadir
+ '/tex:' + datadir
+ '/ps:.:',
113 'TFMFONTS' : datadir
+ '/tfm:',
114 'GS_FONTPATH' : datadir
+ '/afm:' + datadir
+ '/pfa',
115 'GS_LIB' : datadir
+ '/ps',
118 # tex needs lots of memory, more than it gets by default on Debian
119 non_path_environment
= {
120 'extra_mem_top' : '1000000',
121 'extra_mem_bottom' : '1000000',
122 'pool_size' : '250000',
125 def setup_environment ():
126 for key
in environment
.keys ():
127 val
= environment
[key
]
128 if os
.environ
.has_key (key
):
129 val
= val
+ os
.pathsep
+ os
.environ
[key
]
130 os
.environ
[key
] = val
132 for key
in non_path_environment
.keys ():
133 val
= non_path_environment
[key
]
134 os
.environ
[key
] = val
136 include_path
= [os
.getcwd()]
139 # g_ is for global (?)
141 g_here_dir
= os
.getcwd ()
144 g_force_lilypond_fontsize
= 0
154 default_music_fontsize
= 16
155 default_text_fontsize
= 12
158 # this code is ugly. It should be cleaned
162 # the dimensions are from geometry.sty
163 'a0paper': (mm2pt(841), mm2pt(1189)),
164 'a1paper': (mm2pt(595), mm2pt(841)),
165 'a2paper': (mm2pt(420), mm2pt(595)),
166 'a3paper': (mm2pt(297), mm2pt(420)),
167 'a4paper': (mm2pt(210), mm2pt(297)),
168 'a5paper': (mm2pt(149), mm2pt(210)),
169 'b0paper': (mm2pt(1000), mm2pt(1414)),
170 'b1paper': (mm2pt(707), mm2pt(1000)),
171 'b2paper': (mm2pt(500), mm2pt(707)),
172 'b3paper': (mm2pt(353), mm2pt(500)),
173 'b4paper': (mm2pt(250), mm2pt(353)),
174 'b5paper': (mm2pt(176), mm2pt(250)),
175 'letterpaper': (in2pt(8.5), in2pt(11)),
176 'legalpaper': (in2pt(8.5), in2pt(14)),
177 'executivepaper': (in2pt(7.25), in2pt(10.5))}
178 self
.m_use_geometry
= None
179 self
.m_papersize
= 'letterpaper'
183 self
.m_geo_landscape
= 0
184 self
.m_geo_width
= None
185 self
.m_geo_textwidth
= None
186 self
.m_geo_lmargin
= None
187 self
.m_geo_rmargin
= None
188 self
.m_geo_includemp
= None
189 self
.m_geo_marginparwidth
= {10: 57, 11: 50, 12: 35}
190 self
.m_geo_marginparsep
= {10: 11, 11: 10, 12: 10}
191 self
.m_geo_x_marginparwidth
= None
192 self
.m_geo_x_marginparsep
= None
194 def set_geo_option(self
, name
, value
):
196 if type(value
) == type([]):
197 value
= map(conv_dimen_to_float
, value
)
199 value
= conv_dimen_to_float(value
)
201 if name
== 'body' or name
== 'text':
202 if type(value
) == type([]):
203 self
.m_geo_textwidth
= value
[0]
205 self
.m_geo_textwidth
= value
207 elif name
== 'portrait':
208 self
.m_geo_landscape
= 0
209 elif name
== 'reversemp' or name
== 'reversemarginpar':
210 if self
.m_geo_includemp
== None:
211 self
.m_geo_includemp
= 1
212 elif name
== 'marginparwidth' or name
== 'marginpar':
213 self
.m_geo_x_marginparwidth
= value
214 self
.m_geo_includemp
= 1
215 elif name
== 'marginparsep':
216 self
.m_geo_x_marginparsep
= value
217 self
.m_geo_includemp
= 1
218 elif name
== 'scale':
219 if type(value
) == type([]):
220 self
.m_geo_width
= self
.get_paperwidth() * value
[0]
222 self
.m_geo_width
= self
.get_paperwidth() * value
223 elif name
== 'hscale':
224 self
.m_geo_width
= self
.get_paperwidth() * value
225 elif name
== 'left' or name
== 'lmargin':
226 self
.m_geo_lmargin
= value
227 elif name
== 'right' or name
== 'rmargin':
228 self
.m_geo_rmargin
= value
229 elif name
== 'hdivide' or name
== 'divide':
230 if value
[0] not in ('*', ''):
231 self
.m_geo_lmargin
= value
[0]
232 if value
[1] not in ('*', ''):
233 self
.m_geo_width
= value
[1]
234 if value
[2] not in ('*', ''):
235 self
.m_geo_rmargin
= value
[2]
236 elif name
== 'hmargin':
237 if type(value
) == type([]):
238 self
.m_geo_lmargin
= value
[0]
239 self
.m_geo_rmargin
= value
[1]
241 self
.m_geo_lmargin
= value
242 self
.m_geo_rmargin
= value
243 elif name
== 'margin':#ugh there is a bug about this option in
244 # the geometry documentation
245 if type(value
) == type([]):
246 self
.m_geo_lmargin
= value
[0]
247 self
.m_geo_rmargin
= value
[0]
249 self
.m_geo_lmargin
= value
250 self
.m_geo_rmargin
= value
251 elif name
== 'total':
252 if type(value
) == type([]):
253 self
.m_geo_width
= value
[0]
255 self
.m_geo_width
= value
256 elif name
== 'width' or name
== 'totalwidth':
257 self
.m_geo_width
= value
258 elif name
== 'paper' or name
== 'papername':
259 self
.m_papersize
= value
260 elif name
[-5:] == 'paper':
261 self
.m_papersize
= name
265 def __setattr__(self
, name
, value
):
266 if type(value
) == type("") and \
267 dimension_conversion_dict
.has_key (value
[-2:]):
268 f
= dimension_conversion_dict
[value
[-2:]]
269 self
.__dict
__[name
] = f(float(value
[:-2]))
271 self
.__dict
__[name
] = value
274 s
= "LatexPaper:\n-----------"
275 for v
in self
.__dict
__.keys():
277 s
= s
+ str (v
) + ' ' + str (self
.__dict
__[v
])
278 s
= s
+ "-----------"
281 def get_linewidth(self
):
282 w
= self
._calc
_linewidth
()
283 if self
.m_num_cols
== 2:
287 def get_paperwidth(self
):
288 #if self.m_use_geometry:
289 return self
.m_paperdef
[self
.m_papersize
][self
.m_landscape
or self
.m_geo_landscape
]
290 #return self.m_paperdef[self.m_papersize][self.m_landscape]
292 def _calc_linewidth(self
):
293 # since geometry sometimes ignores 'includemp', this is
294 # more complicated than it should be
296 if self
.m_geo_includemp
:
297 if self
.m_geo_x_marginparsep
is not None:
298 mp
= mp
+ self
.m_geo_x_marginparsep
300 mp
= mp
+ self
.m_geo_marginparsep
[self
.m_fontsize
]
301 if self
.m_geo_x_marginparwidth
is not None:
302 mp
= mp
+ self
.m_geo_x_marginparwidth
304 mp
= mp
+ self
.m_geo_marginparwidth
[self
.m_fontsize
]
306 #ugh test if this is necessary
310 if not self
.m_use_geometry
:
311 return latex_linewidths
[self
.m_papersize
][self
.m_fontsize
]
313 geo_opts
= (self
.m_geo_lmargin
== None,
314 self
.m_geo_width
== None,
315 self
.m_geo_rmargin
== None)
317 if geo_opts
== (1, 1, 1):
318 if self
.m_geo_textwidth
:
319 return self
.m_geo_textwidth
320 w
= self
.get_paperwidth() * 0.8
322 elif geo_opts
== (0, 1, 1):
323 if self
.m_geo_textwidth
:
324 return self
.m_geo_textwidth
325 return self
.f1(self
.m_geo_lmargin
, mp
)
326 elif geo_opts
== (1, 1, 0):
327 if self
.m_geo_textwidth
:
328 return self
.m_geo_textwidth
329 return self
.f1(self
.m_geo_rmargin
, mp
)
331 in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
332 if self
.m_geo_textwidth
:
333 return self
.m_geo_textwidth
334 return self
.m_geo_width
- mp
335 elif geo_opts
in ((0, 1, 0), (0, 0, 0)):
336 w
= self
.get_paperwidth() \
337 - self
.m_geo_lmargin
- self
.m_geo_rmargin
- mp
341 raise "Never do this!"
343 tmp
= self
.get_paperwidth() - m
* 2 - mp
348 tmp
= self
.get_paperwidth() - self
.m_geo_lmargin \
356 self
.m_papersize
= 'letterpaper'
358 def get_linewidth(self
):
359 return html_linewidths
[self
.m_papersize
][self
.m_fontsize
]
363 self
.m_papersize
= 'letterpaper'
365 def get_linewidth(self
):
366 return texi_linewidths
[self
.m_papersize
][self
.m_fontsize
]
372 def em2pt(x
, fontsize
= 10):
373 return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize
] * x
374 def ex2pt(x
, fontsize
= 10):
375 return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize
] * x
380 dimension_conversion_dict
={
382 'cm': lambda x
: mm2pt(10*x
),
389 # Convert numeric values, with or without specific dimension, to floats.
391 def conv_dimen_to_float(value
):
392 if type(value
) == type(""):
393 m
= re
.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value
)
396 num
= string
.atof(m
.group (1))
397 conv
= dimension_conversion_dict
[m
.group(2)]
401 elif re
.match ("^[0-9.]+$",value
):
408 # indices are no. of columns, papersize, fontsize
409 # Why can't this be calculated?
411 'a4paper':{10: 345, 11: 360, 12: 390},
412 'a4paper-landscape': {10: 598, 11: 596, 12:592},
413 'a5paper':{10: 276, 11: 276, 12: 276},
414 'b5paper':{10: 345, 11: 356, 12: 356},
415 'letterpaper':{10: 345, 11: 360, 12: 390},
416 'letterpaper-landscape':{10: 598, 11: 596, 12:596},
417 'legalpaper': {10: 345, 11: 360, 12: 390},
418 'executivepaper':{10: 345, 11: 360, 12: 379}}
421 'afourpaper': {12: mm2pt(160)},
422 'afourwide': {12: in2pt(6.5)},
423 'afourlatex': {12: mm2pt(150)},
424 'smallbook': {12: in2pt(5)},
425 'letterpaper': {12: in2pt(6)}}
428 'afourpaper': {12: mm2pt(160)},
429 'afourwide': {12: in2pt(6.5)},
430 'afourlatex': {12: mm2pt(150)},
431 'smallbook': {12: in2pt(5)},
432 'letterpaper': {12: in2pt(6)}}
434 option_definitions
= [
435 ('EXT', 'f', 'format', 'use output format EXT (texi [default], latex, html)'),
436 ('DIM', '', 'default-music-fontsize', 'default fontsize for music. DIM is assumed to be in points'),
437 ('DIM', '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
438 ('OPT', '', 'extra-options' , 'Pass OPT quoted to the lilypond command line'),
439 ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
440 ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
441 ('', 'h', 'help', 'this help'),
442 ('DIR', 'I', 'include', 'include path'),
443 ('', 'M', 'dependencies', 'write dependencies'),
444 ('PREF', '', 'dep-prefix', 'prepend PREF before each -M dependency'),
445 ('', 'n', 'no-lily', 'don\'t run lilypond'),
446 ('', '', 'no-pictures', "don\'t generate pictures"),
447 ('', '', 'no-music', "strip all lilypond blocks from output"),
448 ('', '', 'read-lys', "don't write ly files."),
449 ('FILE', 'o', 'outname', 'filename main output file'),
450 ('FILE', '', 'outdir', "where to place generated files"),
451 ('', 'V', 'verbose', 'verbose' ),
452 ('', 'v', 'version', 'print version information' ),
455 # format specific strings, ie. regex-es for input, and % strings for output
457 'html' : {'output-lilypond': '''<lilypond%s>
460 'output-filename' : r
'''
463 'output-lilypond-fragment': '''<lilypond%s>
464 \context Staff\context Voice{ %s }
466 'output-noinline': r
'''
467 <!-- generated: %(fn)s.png !-->
471 'output-verbatim': r
'''<pre>
474 ## Ugh we need to differentiate on origin:
475 ## lilypond-block origin wants an extra <p>, but
476 ## inline music doesn't.
477 ## possibly other center options?
479 <a href="%(fn)s.png">
480 <img align="center" valign="center" border="0" src="%(fn)s.png" alt="[picture of music]"></a>
484 'output-lilypond-fragment' : r
'''\begin[eps,singleline,%s]{lilypond}
491 'output-filename' : r
'''
494 'output-lilypond': r
'''\begin[%s]{lilypond}
498 'output-verbatim': r
'''\begin{verbatim}%s\end{verbatim}%%
500 'output-default-post': "\\def\postLilypondExample{}\n",
501 'output-default-pre': "\\def\preLilypondExample{}\n",
502 'usepackage-graphics': '\\usepackage{graphics}\n',
503 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
504 'output-noinline': r
'''
505 %% generated: %(fn)s.eps
507 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
508 'pagebreak': r
'\pagebreak',
511 'texi' : {'output-lilypond': '''@lilypond[%s]
515 'output-filename' : r
'''
518 'output-lilypond-fragment': '''@lilypond[%s]
519 \context Staff\context Voice{ %s }
521 'output-noinline': r
'''
522 @c generated: %(fn)s.png
525 'output-verbatim': r
'''@example
530 # do some tweaking: @ is needed in some ps stuff.
531 # override EndLilyPondOutput, since @tex is done
532 # in a sandbox, you can't do \input lilyponddefs at the
533 # top of the document.
535 # should also support fragment in
537 # ugh, the <p> below breaks inline images...
543 \def\EndLilyPondOutput{}
549 <a href="%(fn)s.png">
550 <img border=0 src="%(fn)s.png" alt="[picture of music]">
558 def output_verbatim (body
):
559 if __main__
.format
== 'html':
560 body
= re
.sub ('&', '&', body
)
561 body
= re
.sub ('>', '>', body
)
562 body
= re
.sub ('<', '<', body
)
563 elif __main__
.format
== 'texi':
564 body
= re
.sub ('([@{}])', '@\\1', body
)
565 return get_output ('output-verbatim') % body
568 #warning: this uses extended regular expressions. Tread with care.
572 # (?P -- name parameter
573 # *? -- match non-greedily.
580 'preamble-end': no_match
,
581 'landscape': no_match
,
582 'verbatim': r
'''(?s)(?P<code><pre>\s.*?</pre>\s)''',
583 'verb': r
'''(?P<code><pre>.*?</pre>)''',
584 'lilypond-file': r
'(?m)(?P<match><lilypondfile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</lilypondfile>)',
585 'lilypond' : '(?m)(?P<match><lilypond((?P<options>[^:]*):)(?P<code>.*?)/>)',
586 'lilypond-block': r
'''(?ms)(?P<match><lilypond(?P<options>[^>]+)?>(?P<code>.*?)</lilypond>)''',
587 'option-sep' : '\s*',
588 'intertext': r
',?\s*intertext=\".*?\"',
589 'multiline-comment': r
"(?sm)\s*(?!@c\s+)(?P<code><!--\s.*?!-->)\s",
590 'singleline-comment': no_match
,
594 'latex': {'input': r
'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
595 'include': r
'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
596 'option-sep' : ',\s*',
597 'header': r
"\\documentclass\s*(\[.*?\])?",
598 'geometry': r
"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
599 'preamble-end': r
'(?P<code>\\begin{document})',
600 'verbatim': r
"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
601 'verb': r
"(?P<code>\\verb(?P<del>.).*?(?P=del))",
602 'lilypond-file': r
'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
603 'lilypond' : r
'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
604 'lilypond-block': r
"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
605 'def-post-re': r
"\\def\\postLilypondExample",
606 'def-pre-re': r
"\\def\\preLilypondExample",
607 'usepackage-graphics': r
"\usepackage{graphics}",
608 'intertext': r
',?\s*intertext=\".*?\"',
609 'multiline-comment': no_match
,
610 'singleline-comment': r
"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
611 'numcols': r
"(?P<code>\\(?P<num>one|two)column)",
615 # why do we have distinction between @mbinclude and @include?
619 'include': '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
622 'preamble-end': no_match
,
623 'landscape': no_match
,
624 'verbatim': r
'''(?s)(?P<code>@example\s.*?@end example\s)''',
625 'verb': r
'''(?P<code>@code{.*?})''',
626 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
627 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
628 'lilypond-block': r
'''(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end lilypond)\s''',
629 'option-sep' : ',\s*',
630 'intertext': r
',?\s*intertext=\".*?\"',
631 'multiline-comment': r
"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
632 'singleline-comment': r
"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
638 for r
in re_dict
.keys ():
641 for k
in olddict
.keys ():
643 newdict
[k
] = re
.compile (olddict
[k
])
645 print 'invalid regexp: %s' % olddict
[k
]
647 # we'd like to catch and reraise a more detailed error, but
648 # alas, the exceptions changed across the 1.5/2.1 boundary.
663 def get_output (name
):
664 return output_dict
[format
][name
]
667 return re_dict
[format
][name
]
669 def bounding_box_dimensions(fname
):
671 fname
= os
.path
.join(g_outdir
, fname
)
675 error ("Error opening `%s'" % fname
)
677 s
= re
.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
680 gs
= map (lambda x
: string
.atoi (x
), s
.groups ())
681 return (int (gs
[2] - gs
[0] + 0.5),
682 int (gs
[3] - gs
[1] + 0.5))
687 sys
.stderr
.write (str + "\n Exiting ... \n\n")
691 def compose_full_body (body
, opts
):
692 '''Construct the lilypond code to send to Lilypond.
693 Add stuff to BODY using OPTS as options.'''
694 music_size
= default_music_fontsize
695 latex_size
= default_text_fontsize
699 if g_force_lilypond_fontsize
:
700 music_size
= g_force_lilypond_fontsize
702 m
= re
.match ('([0-9]+)pt', o
)
704 music_size
= string
.atoi(m
.group (1))
706 m
= re
.match ('latexfontsize=([0-9]+)pt', o
)
708 latex_size
= string
.atoi (m
.group (1))
710 m
= re
.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o
)
712 f
= float (m
.group (1))
713 indent
= 'indent = %f\\%s' % (f
, m
.group (2))
715 m
= re
.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o
)
717 f
= float (m
.group (1))
718 linewidth
= 'linewidth = %f\\%s' % (f
, m
.group (2))
720 if re
.search ('\\\\score', body
):
724 if 'fragment' in opts
:
726 if 'nofragment' in opts
:
729 if is_fragment
and not 'multiline' in opts
:
730 opts
.append('singleline')
732 if 'singleline' in opts
:
733 linewidth
= 'linewidth = -1.0'
735 l
= __main__
.paperguru
.get_linewidth ()
736 linewidth
= 'linewidth = %f\pt' % l
738 if 'noindent' in opts
:
739 indent
= 'indent = 0.0\mm'
742 m
= re
.search ('relative(.*)', o
)
746 v
= string
.atoi (m
.group (1))
753 pitch
= pitch
+ '\,' * v
755 pitch
= pitch
+ '\'' * v
757 body
= '\\relative %s { %s }' %(pitch
, body
)
766 optstring
= string
.join (opts
, ' ')
767 optstring
= re
.sub ('\n', ' ', optstring
)
769 %% Generated automatically by: lilypond-book.py
771 \include "paper%d.ly"
776 ''' % (optstring
, music_size
, linewidth
, indent
) + body
778 # ughUGH not original options
781 def parse_options_string(s
):
783 r1
= re
.compile("((\w+)={(.*?)})((,\s*)|$)")
784 r2
= re
.compile("((\w+)=(.*?))((,\s*)|$)")
785 r3
= re
.compile("(\w+?)((,\s*)|$)")
790 d
[m
.group(2)] = re
.split(",\s*", m
.group(3))
795 d
[m
.group(2)] = m
.group(3)
803 error ("format of option string invalid (was `%')" % s
)
806 def scan_html_preamble (chunks
):
809 def scan_latex_preamble(chunks
):
810 # first we want to scan the \documentclass line
811 # it should be the first non-comment line
814 if chunks
[idx
][0] == 'ignore':
817 m
= get_re ('header').match(chunks
[idx
][1])
818 if m
<> None and m
.group (1):
819 options
= re
.split (',[\n \t]*', m
.group(1)[1:-1])
824 paperguru
.m_landscape
= 1
825 m
= re
.match("(.*?)paper", o
)
827 paperguru
.m_papersize
= m
.group()
829 m
= re
.match("(\d\d)pt", o
)
831 paperguru
.m_fontsize
= int(m
.group(1))
834 while idx
< len(chunks
) and chunks
[idx
][0] != 'preamble-end':
835 if chunks
[idx
] == 'ignore':
838 m
= get_re ('geometry').search(chunks
[idx
][1])
840 paperguru
.m_use_geometry
= 1
841 o
= parse_options_string(m
.group('options'))
843 paperguru
.set_geo_option(k
, o
[k
])
846 def scan_texi_preamble (chunks
):
847 # this is not bulletproof..., it checks the first 10 chunks
848 for c
in chunks
[:10]:
850 for s
in ('afourpaper', 'afourwide', 'letterpaper',
851 'afourlatex', 'smallbook'):
852 if string
.find(c
[1], "@%s" % s
) != -1:
853 paperguru
.m_papersize
= s
856 def scan_preamble (chunks
):
857 if __main__
.format
== 'html':
858 scan_html_preamble (chunks
)
859 elif __main__
.format
== 'latex':
860 scan_latex_preamble (chunks
)
861 elif __main__
.format
== 'texi':
862 scan_texi_preamble (chunks
)
865 def completize_preamble (chunks
):
866 if __main__
.format
!= 'latex':
868 pre_b
= post_b
= graphics_b
= None
870 if chunk
[0] == 'preamble-end':
872 if chunk
[0] == 'input':
873 m
= get_re('def-pre-re').search(chunk
[1])
876 if chunk
[0] == 'input':
877 m
= get_re('def-post-re').search(chunk
[1])
881 if chunk
[0] == 'input':
882 m
= get_re('usepackage-graphics').search(chunk
[1])
886 while x
< len (chunks
) and chunks
[x
][0] != 'preamble-end':
893 chunks
.insert(x
, ('input', get_output ('output-default-pre')))
895 chunks
.insert(x
, ('input', get_output ('output-default-post')))
897 chunks
.insert(x
, ('input', get_output ('usepackage-graphics')))
903 def find_file (name
):
905 Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
909 return (sys
.stdin
.read (), '<stdin>')
912 for a
in include_path
:
914 nm
= os
.path
.join (a
, name
)
916 __main__
.read_files
.append (nm
)
921 sys
.stderr
.write ("Reading `%s'\n" % nm
)
922 return (f
.read (), nm
)
924 error ("File not found `%s'\n" % name
)
927 def do_ignore(match_object
):
928 return [('ignore', match_object
.group('code'))]
929 def do_preamble_end(match_object
):
930 return [('preamble-end', match_object
.group('code'))]
932 def make_verbatim(match_object
):
933 return [('verbatim', match_object
.group('code'))]
935 def make_verb(match_object
):
936 return [('verb', match_object
.group('code'))]
938 def do_include_file(m
):
940 return [('input', get_output ('pagebreak'))] \
941 + read_doc_file(m
.group('filename')) \
942 + [('input', get_output ('pagebreak'))]
944 def do_input_file(m
):
945 return read_doc_file(m
.group('filename'))
947 def make_lilypond(m
):
948 if m
.group('options'):
949 options
= m
.group('options')
952 return [('input', get_output('output-lilypond-fragment') %
953 (options
, m
.group('code')))]
955 def make_lilypond_file(m
):
958 Find @lilypondfile{bla.ly} occurences and substitute bla.ly
959 into a @lilypond .. @end lilypond block.
963 if m
.group('options'):
964 options
= m
.group('options')
967 (content
, nm
) = find_file(m
.group('filename'))
968 options
= "filename=%s," % nm
+ options
970 return [('input', get_output('output-lilypond') %
973 def make_lilypond_block(m
):
977 if m
.group('options'):
978 options
= get_re('option-sep').split (m
.group('options'))
981 options
= filter(lambda s
: s
!= '', options
)
982 return [('lilypond', m
.group('code'), options
)]
985 if __main__
.format
!= 'latex':
987 if m
.group('num') == 'one':
988 return [('numcols', m
.group('code'), 1)]
989 if m
.group('num') == 'two':
990 return [('numcols', m
.group('code'), 2)]
992 def chop_chunks(chunks
, re_name
, func
, use_match
=0):
998 m
= get_re (re_name
).search (str)
1000 newchunks
.append (('input', str))
1004 newchunks
.append (('input', str[:m
.start ('match')]))
1006 newchunks
.append (('input', str[:m
.start (0)]))
1007 #newchunks.extend(func(m))
1008 # python 1.5 compatible:
1009 newchunks
= newchunks
+ func(m
)
1010 str = str [m
.end(0):]
1015 def determine_format (str):
1016 if __main__
.format
== '':
1018 html
= re
.search ('(?i)<[dh]tml', str[:200])
1019 latex
= re
.search (r
'''\\document''', str[:200])
1020 texi
= re
.search ('@node|@setfilename', str[:200])
1025 if html
and not latex
and not texi
:
1027 elif latex
and not html
and not texi
:
1029 elif texi
and not html
and not latex
:
1032 error ("can't determine format, please specify")
1035 if __main__
.paperguru
== None:
1036 if __main__
.format
== 'html':
1038 elif __main__
.format
== 'latex':
1040 elif __main__
.format
== 'texi':
1043 __main__
.paperguru
= g
1046 def read_doc_file (filename
):
1047 '''Read the input file, find verbatim chunks and do \input and \include
1049 (str, path
) = find_file(filename
)
1050 determine_format (str)
1052 chunks
= [('input', str)]
1054 # we have to check for verbatim before doing include,
1055 # because we don't want to include files that are mentioned
1056 # inside a verbatim environment
1057 chunks
= chop_chunks(chunks
, 'verbatim', make_verbatim
)
1058 chunks
= chop_chunks(chunks
, 'verb', make_verb
)
1059 chunks
= chop_chunks(chunks
, 'multiline-comment', do_ignore
)
1061 chunks
= chop_chunks(chunks
, 'include', do_include_file
, 1)
1062 chunks
= chop_chunks(chunks
, 'input', do_input_file
, 1)
1066 taken_file_names
= {}
1067 def schedule_lilypond_block (chunk
):
1068 '''Take the body and options from CHUNK, figure out how the
1069 real .ly should look, and what should be left MAIN_STR (meant
1070 for the main file). The .ly is written, and scheduled in
1073 Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
1075 TODO has format [basename, extension, extension, ... ]
1078 (type, body
, opts
) = chunk
1079 assert type == 'lilypond'
1080 file_body
= compose_full_body (body
, opts
)
1081 ## Hmm, we should hash only lilypond source, and skip the
1084 basename
= 'lily-' + `
abs(hash (file_body
))`
1086 m
= re
.search ('filename="(.*?)"', o
)
1088 basename
= m
.group (1)
1089 if not taken_file_names
.has_key(basename
):
1090 taken_file_names
[basename
] = 0
1092 taken_file_names
[basename
] = taken_file_names
[basename
] + 1
1093 basename
= basename
+ "-%i" % taken_file_names
[basename
]
1095 update_file(file_body
, os
.path
.join(g_outdir
, basename
) + '.ly')
1096 needed_filetypes
= ['tex']
1098 if format
== 'html' or format
== 'texi':
1099 needed_filetypes
.append ('eps')
1100 needed_filetypes
.append ('png')
1101 if 'eps' in opts
and not ('eps' in needed_filetypes
):
1102 needed_filetypes
.append('eps')
1103 pathbase
= os
.path
.join (g_outdir
, basename
)
1104 def f (base
, ext1
, ext2
):
1105 a
= os
.path
.isfile(base
+ ext2
)
1106 if (os
.path
.isfile(base
+ ext1
) and
1107 os
.path
.isfile(base
+ ext2
) and
1108 os
.stat(base
+ext1
)[stat
.ST_MTIME
] >
1109 os
.stat(base
+ext2
)[stat
.ST_MTIME
]) or \
1110 not os
.path
.isfile(base
+ ext2
):
1113 if 'tex' in needed_filetypes
and f(pathbase
, '.ly', '.tex'):
1115 if 'eps' in needed_filetypes
and f(pathbase
, '.tex', '.eps'):
1117 if 'png' in needed_filetypes
and f(pathbase
, '.eps', '.png'):
1121 if 'printfilename' in opts
:
1123 m
= re
.match ("filename=(.*)", o
)
1125 newbody
= newbody
+ get_output ("output-filename") % m
.group(1)
1129 if 'verbatim' in opts
:
1130 newbody
= output_verbatim (body
)
1133 m
= re
.search ('intertext="(.*?)"', o
)
1135 newbody
= newbody
+ m
.group (1) + "\n\n"
1137 if 'noinline' in opts
:
1138 s
= 'output-noinline'
1139 elif format
== 'latex':
1144 else: # format == 'html' or format == 'texi':
1146 newbody
= newbody
+ get_output (s
) % {'fn': basename
}
1147 return ('lilypond', newbody
, opts
, todo
, basename
)
1149 def process_lilypond_blocks(chunks
):#ugh rename
1151 # Count sections/chapters.
1153 if c
[0] == 'lilypond':
1154 c
= schedule_lilypond_block (c
)
1155 elif c
[0] == 'numcols':
1156 paperguru
.m_num_cols
= c
[2]
1157 newchunks
.append (c
)
1163 sys
.stderr
.write ("invoking `%s'\n" % cmd
)
1164 st
= os
.system (cmd
)
1166 error ('Error command exited with value %d\n' % st
)
1169 def quiet_system (cmd
, name
):
1171 progress ( _("Running %s...") % name
)
1172 cmd
= cmd
+ ' 1> /dev/null 2> /dev/null'
1176 def get_bbox (filename
):
1177 system ('gs -sDEVICE=bbox -q -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename
, filename
))
1179 box
= open (filename
+ '.bbox').read()
1180 m
= re
.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box
)
1183 gr
= map (string
.atoi
, m
.groups ())
1187 def make_pixmap (name
):
1188 bbox
= get_bbox (name
+ '.eps')
1190 fo
= open (name
+ '.trans.eps' , 'w')
1191 fo
.write ('%d %d translate\n' % (-bbox
[0]+margin
, -bbox
[1]+margin
))
1196 x
= (2* margin
+ bbox
[2] - bbox
[0]) * res
/ 72.
1197 y
= (2* margin
+ bbox
[3] - bbox
[1]) * res
/ 72.
1199 cmd
= r
'''gs -g%dx%d -sDEVICE=pnggray -dTextAlphaBits=4 -dGraphicsAlphaBits=4 -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit > %s'''
1201 cmd
= cmd
% (x
, y
, res
, name
+ '.trans.eps', name
+ '.eps',name
+ '.png')
1202 quiet_system (cmd
, 'gs')
1205 status
= system (cmd
)
1207 os
.unlink (name
+ '.png')
1208 error ("Removing output file")
1210 def compile_all_files (chunks
):
1217 if c
[0] <> 'lilypond':
1226 if base
+ '.ly' not in tex
:
1227 tex
.append (base
+ '.ly')
1228 elif e
== 'png' and g_do_pictures
:
1234 # fixme: be sys-independent.
1236 if g_outdir
and x
[0] <> '/' :
1237 x
= os
.path
.join (g_here_dir
, x
)
1240 incs
= map (incl_opt
, include_path
)
1241 lilyopts
= string
.join (incs
, ' ' )
1243 lilyopts
= lilyopts
+ ' --dependencies '
1245 lilyopts
= lilyopts
+ '--dep-prefix=' + g_outdir
+ '/'
1246 texfiles
= string
.join (tex
, ' ')
1247 cmd
= 'lilypond --header=texidoc %s %s %s' \
1248 % (lilyopts
, g_extra_opts
, texfiles
)
1253 # Ugh, fixing up dependencies for .tex generation
1256 depfiles
=map (lambda x
: re
.sub ('(.*)\.ly', '\\1.dep', x
), tex
)
1261 text
=re
.sub ('\n([^:\n]*):', '\n' + foutn
+ ':', text
)
1267 cmd
= r
"tex '\nonstopmode \input %s'" % e
1268 quiet_system (cmd
, 'TeX')
1270 cmd
= r
"dvips -E -o %s %s" % (e
+ '.eps', e
)
1271 quiet_system (cmd
, 'dvips')
1279 def update_file (body
, name
):
1281 write the body if it has changed
1292 f
= open (name
, 'w')
1299 def getopt_args (opts
):
1300 "Construct arguments (LONG, SHORT) for getopt from list of options."
1305 short
= short
+ o
[1]
1313 return (short
, long)
1315 def option_help_str (o
):
1316 "Transform one option description (4-tuple ) into neatly formatted string"
1334 return ' ' + sh
+ sep
+ long + arg
1337 def options_help_str (opts
):
1338 "Convert a list of options into a neatly formatted string"
1344 s
= option_help_str (o
)
1345 strs
.append ((s
, o
[3]))
1351 str = str + '%s%s%s\n' % (s
[0], ' ' * (w
- len(s
[0]) + 3), s
[1])
1355 sys
.stdout
.write('''Usage: lilypond-book [options] FILE\n
1356 Generate hybrid LaTeX input from Latex + lilypond
1359 sys
.stdout
.write (options_help_str (option_definitions
))
1360 sys
.stdout
.write (r
'''Warning all output is written in the CURRENT directory
1364 Report bugs to bug-lilypond@gnu.org.
1366 Written by Tom Cato Amundsen <tca@gnu.org> and
1367 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1373 def write_deps (fn
, target
, chunks
):
1375 sys
.stderr
.write('Writing `%s\'\n' % os
.path
.join(g_outdir
, fn
))
1376 f
= open (os
.path
.join(g_outdir
, fn
), 'w')
1377 f
.write ('%s%s: ' % (g_dep_prefix
, target
))
1378 for d
in read_files
:
1382 if c
[0] == 'lilypond':
1383 (type, body
, opts
, todo
, basename
) = c
;
1384 basenames
.append (basename
)
1387 d
=g_outdir
+ '/' + d
1389 #if not os.isfile (d): # thinko?
1390 if not re
.search ('/', d
):
1391 d
= g_dep_prefix
+ d
1392 f
.write ('%s.tex ' % d
)
1394 #if len (basenames):
1395 # for d in basenames:
1396 # f.write ('%s.ly ' % d)
1397 # f.write (' : %s' % target)
1402 def identify (stream
):
1403 stream
.write ('lilypond-book (GNU LilyPond) %s\n' % program_version
)
1405 def print_version ():
1406 identify (sys
.stdout
)
1407 sys
.stdout
.write (r
'''Copyright 1998--1999
1408 Distributed under terms of the GNU General Public License. It comes with
1413 def check_texidoc (chunks
):
1416 if c
[0] == 'lilypond':
1417 (type, body
, opts
, todo
, basename
) = c
;
1418 pathbase
= os
.path
.join (g_outdir
, basename
)
1419 if os
.path
.isfile (pathbase
+ '.texidoc'):
1420 body
= '\n@include %s.texidoc\n' % basename
+ body
1421 c
= (type, body
, opts
, todo
, basename
)
1426 ## what's this? Docme --hwn
1428 def fix_epswidth (chunks
):
1431 if c
[0] <> 'lilypond' or 'eps' not in c
[2]:
1432 newchunks
.append (c
)
1437 m
= re
.match ('magnification=([0-9.]+)', o
)
1439 mag
= string
.atof (m
.group (1))
1441 def replace_eps_dim (match
, lmag
= mag
):
1442 filename
= match
.group (1)
1443 dims
= bounding_box_dimensions (filename
)
1445 return '%fpt' % (dims
[0] *lmag
)
1447 body
= re
.sub (r
'''\\lilypondepswidth{(.*?)}''', replace_eps_dim
, c
[1])
1448 newchunks
.append(('lilypond', body
, c
[2], c
[3], c
[4]))
1453 ##docme: why global?
1455 def do_file(input_filename
):
1457 chunks
= read_doc_file(input_filename
)
1458 chunks
= chop_chunks(chunks
, 'lilypond', make_lilypond
, 1)
1459 chunks
= chop_chunks(chunks
, 'lilypond-file', make_lilypond_file
, 1)
1460 chunks
= chop_chunks(chunks
, 'lilypond-block', make_lilypond_block
, 1)
1461 chunks
= chop_chunks(chunks
, 'singleline-comment', do_ignore
, 1)
1462 chunks
= chop_chunks(chunks
, 'preamble-end', do_preamble_end
)
1463 chunks
= chop_chunks(chunks
, 'numcols', do_columns
)
1465 #for c in chunks: print "c:", c;
1467 scan_preamble(chunks
)
1468 chunks
= process_lilypond_blocks(chunks
)
1471 if __main__
.g_run_lilypond
:
1472 compile_all_files (chunks
)
1473 chunks
= fix_epswidth (chunks
)
1475 if __main__
.format
== 'texi':
1476 chunks
= check_texidoc (chunks
)
1479 chunks
= completize_preamble (chunks
)
1485 my_outname
= outname
1486 elif input_filename
== '-' or input_filename
== "/dev/stdin":
1489 my_outname
= os
.path
.basename (os
.path
.splitext(input_filename
)[0]) + '.' + format
1490 my_depname
= my_outname
+ '.dep'
1492 if my_outname
== '-' or my_outname
== '/dev/stdout':
1495 __main__
.do_deps
= 0
1497 foutn
= os
.path
.join (g_outdir
, my_outname
)
1498 sys
.stderr
.write ("Writing `%s'\n" % foutn
)
1499 fout
= open (foutn
, 'w')
1506 write_deps (my_depname
, foutn
, chunks
)
1511 (sh
, long) = getopt_args (__main__
.option_definitions
)
1512 (options
, files
) = getopt
.getopt(sys
.argv
[1:], sh
, long)
1513 except getopt
.error
, msg
:
1514 sys
.stderr
.write("error: %s" % msg
)
1522 if o
== '--include' or o
== '-I':
1523 include_path
.append (a
)
1524 elif o
== '--version' or o
== '-v':
1527 elif o
== '--verbose' or o
== '-V':
1528 __main__
.verbose_p
= 1
1529 elif o
== '--format' or o
== '-f':
1531 elif o
== '--outname' or o
== '-o':
1534 sys
.stderr
.write("Lilypond-book is confused by --outname on multiple files")
1537 elif o
== '--help' or o
== '-h':
1539 elif o
== '--no-lily' or o
== '-n':
1540 __main__
.g_run_lilypond
= 0
1541 elif o
== '--dependencies' or o
== '-M':
1543 elif o
== '--default-music-fontsize':
1544 default_music_fontsize
= string
.atoi (a
)
1545 elif o
== '--default-lilypond-fontsize':
1546 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1547 default_music_fontsize
= string
.atoi (a
)
1548 elif o
== '--extra-options':
1550 elif o
== '--force-music-fontsize':
1551 g_force_lilypond_fontsize
= string
.atoi(a
)
1552 elif o
== '--force-lilypond-fontsize':
1553 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1554 g_force_lilypond_fontsize
= string
.atoi(a
)
1555 elif o
== '--dep-prefix':
1557 elif o
== '--no-pictures':
1559 elif o
== '--no-music':
1561 elif o
== '--read-lys':
1563 elif o
== '--outdir':
1566 identify (sys
.stderr
)
1568 if os
.path
.isfile(g_outdir
):
1569 error ("outdir is a file: %s" % g_outdir
)
1570 if not os
.path
.exists(g_outdir
):
1572 setup_environment ()
1573 for input_filename
in files
:
1574 do_file(input_filename
)
1577 # Petr, ik zou willen dat ik iets zinvoller deed,
1578 # maar wat ik kan ik doen, het verandert toch niets?