]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/lilypond-book.py
release: 1.4.14
[lilypond.git] / scripts / lilypond-book.py
index 8b560e8959eb26ad81dc7f3c8c87c0996023752b..90d0eb1b2e0e5871e2dd65d1c8dcf09935a7830d 100644 (file)
 #       geometry.sty and article.cls. Give me a hint, and I'll
 #       fix it.)
 
+#
+# TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips. 
+# 
+
 # This is was the idea for handling of comments:
 #      Multiline comments, @ignore .. @end ignore is scanned for
 #      in read_doc_file, and the chunks are marked as 'ignore', so
 #      The the rest of the rexeces are searched for. They don't have to test
 #      if they are on a commented out line.
 
+
+
 import os
 import stat
 import string
-import re
+
+
 import getopt
 import sys
 import __main__
 import operator
 
+# Handle bug in Python 1.6-2.1
+#
+# there are recursion limits for some patterns in Python 1.6 til 2.1. 
+# fix this by importing the 1.5.2 implementation pre instead. Fix by Mats.
+
+if float (sys.version[0:3]) < 2.2:
+        try:
+                import pre
+                re = pre
+                del pre
+        except ImportError:
+                import re
+else:
+        import re
+
+# Attempt to fix problems with limited stack size set by Python!
+# Sets unlimited stack size. Note that the resource module only
+# is available on UNIX.
+try:
+       import resource
+       resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
+except:
+       pass
+
+
 
 program_version = '@TOPLEVEL_VERSION@'
 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
-       program_version = '1.4pre'
+       program_version = '1.4.12'
+
+# if set, LILYPONDPREFIX must take prevalence
+# if datadir is not set, we're doing a build and LILYPONDPREFIX 
+datadir = '@datadir@'
+
+if os.environ.has_key ('LILYPONDPREFIX') :
+       datadir = os.environ['LILYPONDPREFIX']
+else:
+       datadir = '@datadir@'
+
+while datadir[-1] == os.sep:
+       datadir= datadir[:-1]
 
-#
 # Try to cater for bad installations of LilyPond, that have
 # broken TeX setup.  Just hope this doesn't hurt good TeX
 # setups.  Maybe we should check if kpsewhich can find
 # feta16.{afm,mf,tex,tfm}, and only set env upon failure.
-#
-datadir = '@datadir@'
 environment = {
        'MFINPUTS' : datadir + '/mf:',
        'TEXINPUTS': datadir + '/tex:' + datadir + '/ps:.:',
@@ -60,6 +101,13 @@ environment = {
        'GS_LIB' : datadir + '/ps',
 }
 
+# tex needs lots of memory, more than it gets by default on Debian
+non_path_environment = {
+       'extra_mem_top' : '1000000',
+       'extra_mem_bottom' : '1000000',
+       'pool_size' : '250000',
+}
+
 def setup_environment ():
        for key in environment.keys ():
                val = environment[key]
@@ -67,13 +115,16 @@ def setup_environment ():
                        val = val + os.pathsep + os.environ[key]
                os.environ[key] = val
 
-
+       for key in non_path_environment.keys ():
+               val = non_path_environment[key]
+               print '%s=%s' % (key,val)
+               os.environ[key] = val
 
 include_path = [os.getcwd()]
 
 
 # g_ is for global (?)
-
+g_extra_opts = ''
 g_here_dir = os.getcwd ()
 g_dep_prefix = ''
 g_outdir = ''
@@ -81,6 +132,7 @@ g_force_lilypond_fontsize = 0
 g_read_lys = 0
 g_do_pictures = 1
 g_num_cols = 1
+
 format = ''
 g_run_lilypond = 1
 no_match = 'a\ba'
@@ -126,11 +178,17 @@ class LatexPaper:
                self.m_geo_x_marginparsep = None
                self.__body = None
        def set_geo_option(self, name, value):
+
+               if type(value) == type([]):
+                       value = map(conv_dimen_to_float, value)
+               else:
+                       value = conv_dimen_to_float(value)
+
                if name == 'body' or name == 'text':
-                       if type(value) == type(""):
-                               self.m_geo_textwidth =  value
-                       else:
+                       if type(value) == type([]):
                                self.m_geo_textwidth =  value[0]
+                       else:
+                               self.m_geo_textwidth =  value
                        self.__body = 1
                elif name == 'portrait':
                        self.m_geo_landscape = 0
@@ -144,12 +202,12 @@ class LatexPaper:
                        self.m_geo_x_marginparsep =  value
                        self.m_geo_includemp = 1
                elif name == 'scale':
-                       if type(value) == type(""):
-                               self.m_geo_width = self.get_paperwidth() * float(value)
+                       if type(value) == type([]):
+                               self.m_geo_width = self.get_paperwidth() * value[0]
                        else:
-                               self.m_geo_width = self.get_paperwidth() * float(value[0])
+                               self.m_geo_width = self.get_paperwidth() * value
                elif name == 'hscale':
-                       self.m_geo_width = self.get_paperwidth() * float(value)
+                       self.m_geo_width = self.get_paperwidth() * value
                elif name == 'left' or name == 'lmargin':
                        self.m_geo_lmargin =  value
                elif name == 'right' or name == 'rmargin':
@@ -162,25 +220,25 @@ class LatexPaper:
                        if value[2] not in ('*', ''):
                                self.m_geo_rmargin =  value[2]
                elif name == 'hmargin':
-                       if type(value) == type(""):
-                               self.m_geo_lmargin =  value
-                               self.m_geo_rmargin =  value
-                       else:
+                       if type(value) == type([]):
                                self.m_geo_lmargin =  value[0]
                                self.m_geo_rmargin =  value[1]
-               elif name == 'margin':#ugh there is a bug about this option in
-                                       # the geometry documentation
-                       if type(value) == type(""):
+                       else:
                                self.m_geo_lmargin =  value
                                self.m_geo_rmargin =  value
-                       else:
+               elif name == 'margin':#ugh there is a bug about this option in
+                                       # the geometry documentation
+                       if type(value) == type([]):
                                self.m_geo_lmargin =  value[0]
                                self.m_geo_rmargin =  value[0]
-               elif name == 'total':
-                       if type(value) == type(""):
-                               self.m_geo_width =  value
                        else:
+                               self.m_geo_lmargin =  value
+                               self.m_geo_rmargin =  value
+               elif name == 'total':
+                       if type(value) == type([]):
                                self.m_geo_width =  value[0]
+                       else:
+                               self.m_geo_width =  value
                elif name == 'width' or name == 'totalwidth':
                        self.m_geo_width =  value
                elif name == 'paper' or name == 'papername':
@@ -188,11 +246,12 @@ class LatexPaper:
                elif name[-5:] == 'paper':
                        self.m_papersize = name
                else:
-                       self._set_dimen('m_geo_'+name, value)
+                               pass 
+
        def __setattr__(self, name, value):
                if type(value) == type("") and \
                   dimension_conversion_dict.has_key (value[-2:]):
-                       f = dimension_conversion_dict[dim]
+                       f = dimension_conversion_dict[value[-2:]]
                        self.__dict__[name] = f(float(value[:-2]))
                else:
                        self.__dict__[name] = value
@@ -299,12 +358,30 @@ def pt2pt(x):
 
 dimension_conversion_dict ={
        'mm': mm2pt,
+       'cm': lambda x: mm2pt(10*x),
        'in': in2pt,
        'em': em2pt,
        'ex': ex2pt,
        'pt': pt2pt
        }
 
+# Convert numeric values, with or without specific dimension, to floats.
+# Keep other strings
+def conv_dimen_to_float(value):
+       if type(value) == type(""):
+               m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
+               if m:
+                       unit = m.group (2)
+                       num = string.atof(m.group (1))
+                       conv =  dimension_conversion_dict[m.group(2)]
+                       
+                       value = conv(num)
+               
+               elif re.match ("^[0-9.]+$",value):
+                       value = float(value)
+
+       return value
+                       
        
 # latex linewidths:
 # indices are no. of columns, papersize,  fontsize
@@ -330,6 +407,7 @@ option_definitions = [
   ('EXT', 'f', 'format', 'set format.  EXT is one of texi and latex.'),
   ('DIM',  '', 'default-music-fontsize', 'default fontsize for music.  DIM is assumed to be in points'),
   ('DIM',  '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
+  ('OPT', '', 'extra-options' , 'Pass OPT quoted to the lilypond command line'),
   ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
   ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
   ('DIR', 'I', 'include', 'include path'),
@@ -359,15 +437,21 @@ output_dict= {
 \verb+%s+:''',
                'output-lilypond': r"""\begin[%s]{lilypond}
 %s
-\end{lilypond}""",
-               'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
+\end{lilypond}
+""",
+               'output-verbatim': r'''\begin{verbatim}%s\end{verbatim}%%
+''',
                'output-default-post': "\\def\postLilypondExample{}\n",
                'output-default-pre': "\\def\preLilypondExample{}\n",
                'usepackage-graphics': '\\usepackage{graphics}\n',
                'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
-               'output-tex': '\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n',
+               'output-noinline': r'''
+%% generated: %(fn)s.eps
+''',
+               'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
                'pagebreak': r'\pagebreak',
                },
+       
        'texi' : {'output-lilypond': """@lilypond[%s]
 %s
 @end lilypond 
@@ -378,6 +462,9 @@ output_dict= {
                  'output-lilypond-fragment': """@lilypond[%s]
 \context Staff\context Voice{ %s }
 @end lilypond """,
+                 'output-noinline': r'''
+@c generated: %(fn)s.png                 
+''',
                  'pagebreak': None,
                  'output-verbatim': r"""@example
 %s
@@ -401,7 +488,9 @@ output_dict= {
 @end tex
 @html
 <p>
-<img src="%(fn)s.png" alt="">
+<a href="%(fn)s.png">
+<img border=0 src="%(fn)s.png" alt="[picture of music]">
+</a>
 @end html
 """,
                }
@@ -413,18 +502,23 @@ def output_verbatim (body):
        return get_output ('output-verbatim') % body
 
 
+#warning: this uses extended regular expressions. Tread with care.
+
+# legenda (?P  name parameter
+# *? match non-greedily.
+
 re_dict = {
        'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
                  'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
-                 'option-sep' : ', *',
+                 'option-sep' : ',\s*',
                  'header': r"\\documentclass\s*(\[.*?\])?",
                  'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
                  'preamble-end': r'(?P<code>\\begin{document})',
                  'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
                  'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
-                 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile(\[(?P<options>.*?)\])?\{(?P<filename>.+)})',
-                 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
-                 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin(\[(?P<options>.*?)\])?{lilypond}(?P<code>.*?)\\end{lilypond})",
+                 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
+                 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
+                 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
                  'def-post-re': r"\\def\\postLilypondExample",
                  'def-pre-re': r"\\def\\preLilypondExample",             
                  'usepackage-graphics': r"\usepackage{graphics}",
@@ -435,7 +529,9 @@ re_dict = {
                  },
 
 
-       # why do we have distinction between @mbinclude and @include? 
+       # why do we have distinction between @mbinclude and @include?
+
+       
        'texi': {
                 'include':  '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
                 'input': no_match,
@@ -444,10 +540,10 @@ re_dict = {
                 'landscape': no_match,
                 'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
                 'verb': r"""(?P<code>@code{.*?})""",
-                'lilypond-file': '(?m)^(?!@c)(?P<match>@lilypondfile(\[(?P<options>.*?)\])?{(?P<filename>[^}]+)})',
-                'lilypond' : '(?m)^(?!@c)(?P<match>@lilypond(\[(?P<options>.*?)\])?{(?P<code>.*?)})',
-                'lilypond-block': r"""(?m)^(?!@c)(?P<match>(?s)(?P<match>@lilypond(\[(?P<options>.*?)\])?\s(?P<code>.*?)@end lilypond\s))""",
-                 'option-sep' : ', *',
+                'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
+                'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
+                'lilypond-block': r"""(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end lilypond)\s""",
+                 'option-sep' : ',\s*',
                  'intertext': r',?\s*intertext=\".*?\"',
                  'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
                  'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
@@ -460,7 +556,14 @@ for r in re_dict.keys ():
        olddict = re_dict[r]
        newdict = {}
        for k in olddict.keys ():
-               newdict[k] = re.compile (olddict[k])
+               try:
+                       newdict[k] = re.compile (olddict[k])
+               except:
+                       print 'invalid regexp: %s' % olddict[k]
+
+                       # we'd like to catch and reraise a more detailed  error, but
+                       # alas, the exceptions changed across the 1.5/2.1 boundary.
+                       raise "Invalid re"
        re_dict[r] = newdict
 
        
@@ -490,8 +593,10 @@ def bounding_box_dimensions(fname):
        str = fd.read ()
        s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
        if s:
-               return (int(s.group(3))-int(s.group(1)), 
-                       int(s.group(4))-int(s.group(2)))
+               
+               gs = map (lambda x: string.atoi (x), s.groups ())
+               return (int (gs[2] - gs[0] + 0.5),
+                       int (gs[3] - gs[1] + 0.5))
        else:
                return (0,0)
 
@@ -505,6 +610,8 @@ def compose_full_body (body, opts):
        Add stuff to BODY using OPTS as options."""
        music_size = default_music_fontsize
        latex_size = default_text_fontsize
+       indent = ''
+       linewidth = ''
        for o in opts:
                if g_force_lilypond_fontsize:
                        music_size = g_force_lilypond_fontsize
@@ -516,6 +623,16 @@ def compose_full_body (body, opts):
                m = re.match ('latexfontsize=([0-9]+)pt', o)
                if m:
                        latex_size = string.atoi (m.group (1))
+                       
+               m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o)
+               if m:
+                       f = float (m.group (1))
+                       indent = 'indent = %f\\%s' % (f, m.group (2))
+                       
+               m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o)
+               if m:
+                       f = float (m.group (1))
+                       linewidth = 'linewidth = %f\\%s' % (f, m.group (2))
 
        if re.search ('\\\\score', body):
                is_fragment = 0
@@ -528,10 +645,15 @@ def compose_full_body (body, opts):
 
        if is_fragment and not 'multiline' in opts:
                opts.append('singleline')
+               
        if 'singleline' in opts:
-               l = -1.0;
-       else:
-               l = __main__.paperguru.get_linewidth()
+               linewidth = 'linewidth = -1.0'
+       elif not linewidth:
+               l = __main__.paperguru.get_linewidth ()
+               linewidth = 'linewidth = %f\pt' % l
+
+       if 'noindent' in opts:
+               indent = 'indent = 0.0\mm'
 
        for o in opts:
                m= re.search ('relative(.*)', o)
@@ -552,20 +674,23 @@ def compose_full_body (body, opts):
                        body = '\\relative %s { %s }' %(pitch, body)
        
        if is_fragment:
-               body = r"""\score { 
+               body = r'''\score { 
  \notes { %s }
   \paper { }  
-}""" % body
+}''' % body
 
        opts = uniq (opts)
        optstring = string.join (opts, ' ')
        optstring = re.sub ('\n', ' ', optstring)
-       body = r"""
+       body = r'''
 %% Generated automatically by: lilypond-book.py
 %% options are %s  
 \include "paper%d.ly"
-\paper  { linewidth = %f \pt } 
-""" % (optstring, music_size, l) + body
+\paper  {
+  %s
+  %s
+} 
+''' % (optstring, music_size, linewidth, indent) + body
 
        # ughUGH not original options
        return body
@@ -604,7 +729,7 @@ def scan_latex_preamble(chunks):
                        idx = idx + 1
                        continue
                m = get_re ('header').match(chunks[idx][1])
-               if m.group (1):
+               if m <> None and m.group (1):
                        options = re.split (',[\n \t]*', m.group(1)[1:-1])
                else:
                        options = []
@@ -618,8 +743,8 @@ def scan_latex_preamble(chunks):
                                m = re.match("(\d\d)pt", o)
                                if m:
                                        paperguru.m_fontsize = int(m.group(1))
-                       
                break
+       
        while chunks[idx][0] != 'preamble-end':
                if chunks[idx] == 'ignore':
                        idx = idx + 1
@@ -901,7 +1026,10 @@ def schedule_lilypond_block (chunk):
                m = re.search ('intertext="(.*?)"', o)
                if m:
                        newbody = newbody  + m.group (1) + "\n\n"
-       if format == 'latex':
+       
+       if 'noinline' in opts:
+               s = 'output-noinline'
+       elif format == 'latex':
                if 'eps' in opts:
                        s = 'output-eps'
                else:
@@ -923,16 +1051,6 @@ def process_lilypond_blocks(outname, chunks):#ugh rename
        return newchunks
 
 
-def find_eps_dims (match):
-       "Fill in dimensions of EPS files."
-       
-       fn =match.group (1)
-       dims = bounding_box_dimensions (fn)
-       if g_outdir:
-               fn = os.path.join(g_outdir, fn)
-       
-       return '%ipt' % dims[0]
-
 
 def system (cmd):
        sys.stderr.write ("invoking `%s'\n" % cmd)
@@ -941,6 +1059,39 @@ def system (cmd):
                error ('Error command exited with value %d\n' % st)
        return st
 
+
+def get_bbox (filename):
+       system ('gs -sDEVICE=bbox -q  -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename, filename))
+
+       box = open (filename + '.bbox').read()
+       m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
+       gr = []
+       if m:
+               gr = map (string.atoi, m.groups ())
+       
+       return gr
+
+def make_pixmap (name):
+       bbox = get_bbox (name + '.eps')
+       margin = 0
+       fo = open (name + '.trans.eps' , 'w')
+       fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
+       fo.close ()
+       
+       res = 90
+
+       x = (2* margin + bbox[2] - bbox[0]) * res / 72.
+       y = (2* margin + bbox[3] - bbox[1]) * res / 72.
+
+       cmd = r"""gs -g%dx%d -sDEVICE=pnggray  -dTextAlphaBits=4 -dGraphicsAlphaBits=4  -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit  > %s"""
+       
+       cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
+       try:
+               status = system (cmd)
+       except:
+               os.unlink (name + '.png')
+               error ("Removing output file")
+
 def compile_all_files (chunks):
        global foutn
        eps = []
@@ -978,7 +1129,7 @@ def compile_all_files (chunks):
                        if g_outdir:
                                lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
                texfiles = string.join (tex, ' ')
-               system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
+               system ('lilypond --header=texidoc %s %s %s' % (lilyopts, g_extra_opts, texfiles))
 
                #
                # Ugh, fixing up dependencies for .tex generation
@@ -986,21 +1137,20 @@ def compile_all_files (chunks):
                if do_deps:
                        depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
                        for i in depfiles:
-                               text=open (i).read ()
+                               f =open (i)
+                               text=f.read ()
+                               f.close ()
                                text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
-                               open (i, 'w').write (text)
+                               f = open (i, 'w')
+                               f.write (text)
+                               f.close ()
 
        for e in eps:
                system(r"tex '\nonstopmode \input %s'" % e)
                system(r"dvips -E -o %s %s" % (e + '.eps', e))
+               
        for g in png:
-               cmd = r"""gs -sDEVICE=pgm  -dTextAlphaBits=4 -dGraphicsAlphaBits=4  -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
-               cmd = cmd % (g + '.eps', g + '.png')
-               try:
-                       status = system (cmd)
-               except:
-                       os.unlink (g + '.png')
-                       error ("Removing output file")
+               make_pixmap (g)
                
        os.chdir (d)
 
@@ -1090,7 +1240,7 @@ Options:
 
 
 
-Report bugs to bug-gnu-music@gnu.org.
+Report bugs to bug-lilypond@gnu.org.
 
 Written by Tom Cato Amundsen <tca@gnu.org> and
 Han-Wen Nienhuys <hanwen@cs.uu.nl>
@@ -1151,14 +1301,31 @@ def check_texidoc (chunks):
                n.append (c)
        return n
 
+
+## what's this? Docme --hwn
+##
 def fix_epswidth (chunks):
        newchunks = []
        for c in chunks:
-               if c[0] == 'lilypond' and 'eps' in c[2]:
-                       body = re.sub (r"""\\lilypondepswidth{(.*?)}""", find_eps_dims, c[1])
-                       newchunks.append(('lilypond', body, c[2], c[3], c[4]))
-               else:
+               if c[0] <> 'lilypond' or 'eps' not in c[2]:
                        newchunks.append (c)
+                       continue
+
+               mag = 1.0
+               for o in c[2]:
+                       m  = re.match ('magnification=([0-9.]+)', o)
+                       if m:
+                               mag = string.atof (m.group (1))
+
+               def replace_eps_dim (match, lmag = mag):
+                       filename = match.group (1)
+                       dims = bounding_box_dimensions (filename)
+
+                       return '%fpt' % (dims[0] *lmag)
+       
+               body = re.sub (r"""\\lilypondepswidth{(.*?)}""", replace_eps_dim, c[1])
+               newchunks.append(('lilypond', body, c[2], c[3], c[4]))
+                       
        return newchunks
 
 
@@ -1245,6 +1412,8 @@ for opt in options:
        elif o == '--default-lilypond-fontsize':
                print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
                default_music_fontsize = string.atoi (a)
+       elif o == '--extra-options':
+               g_extra_opts = a
        elif o == '--force-music-fontsize':
                g_force_lilypond_fontsize = string.atoi(a)
        elif o == '--force-lilypond-fontsize':