]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/lilypond-book.py
patch::: 1.5.23.jcn2
[lilypond.git] / scripts / lilypond-book.py
index b6a957b27c1b539fec4379e38aa719434841678f..efd48214a34096cda1404358694c4b12cbfc28a1 100644 (file)
@@ -1,8 +1,9 @@
 #!@PYTHON@
 # vim: set noexpandtab:
 # TODO:
-# * Figure out clean set of options. Hmm, isn't it pretty ok now?
-# * add support for .lilyrc
+# * junk --outdir for --output 
+# * Figure out clean set of options.
+# * 
 # * EndLilyPondOutput is def'd as vfil. Causes large white gaps.
 # * texinfo: add support for @pagesize
 
 #       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
@@ -30,6 +35,8 @@
 #      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
@@ -42,7 +49,31 @@ import operator
 
 program_version = '@TOPLEVEL_VERSION@'
 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
-       program_version = '1.3.113'
+       program_version = '1.5.18'
+
+#
+# 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:.:',
+       'TFMFONTS' : datadir + '/tfm:',
+       'GS_FONTPATH' : datadir + '/afm:' + datadir + '/pfa',
+       'GS_LIB' : datadir + '/ps',
+}
+
+def setup_environment ():
+       for key in environment.keys ():
+               val = environment[key]
+               if os.environ.has_key (key):
+                       val = val + os.pathsep + os.environ[key]
+               os.environ[key] = val
+
+
 
 include_path = [os.getcwd()]
 
@@ -163,11 +194,13 @@ class LatexPaper:
                elif name[-5:] == 'paper':
                        self.m_papersize = name
                else:
-                       self._set_dimen('m_geo_'+name, value)
+                               pass 
+                       # what is _set_dimen ?? /MB
+                               #self._set_dimen('m_geo_'+name, value)
        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
@@ -212,8 +245,10 @@ class LatexPaper:
                if not self.m_use_geometry:
                        return latex_linewidths[self.m_papersize][self.m_fontsize]
                else:
-                       geo_opts = (a == None, b == None, c == None)
-                       
+                       geo_opts = (self.m_geo_lmargin == None,
+                                   self.m_geo_width == None,
+                                   self.m_geo_rmargin == None)
+
                        if geo_opts == (1, 1, 1):
                                if self.m_geo_textwidth:
                                        return self.m_geo_textwidth
@@ -338,7 +373,7 @@ output_dict= {
                '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-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
                'pagebreak': r'\pagebreak',
                },
        'texi' : {'output-lilypond': """@lilypond[%s]
@@ -374,7 +409,9 @@ output_dict= {
 @end tex
 @html
 <p>
-<img src=%(fn)s.png>
+<a href="%(fn)s.png">
+<img border=0 src="%(fn)s.png" alt="[picture of music]">
+</a>
 @end html
 """,
                }
@@ -389,15 +426,15 @@ def output_verbatim (body):
 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}",
@@ -420,7 +457,7 @@ re_dict = {
                 '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' : ', *',
+                 '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+))",
@@ -468,7 +505,6 @@ def bounding_box_dimensions(fname):
        else:
                return (0,0)
 
-
 def error (str):
        sys.stderr.write (str + "\n  Exiting ... \n\n")
        raise 'Exiting.'
@@ -497,7 +533,7 @@ def compose_full_body (body, opts):
                is_fragment = 1
        if 'fragment' in opts:
                is_fragment = 1
-       if 'nonfragment' in opts:
+       if 'nofragment' in opts:
                is_fragment = 0
 
        if is_fragment and not 'multiline' in opts:
@@ -506,9 +542,24 @@ def compose_full_body (body, opts):
                l = -1.0;
        else:
                l = __main__.paperguru.get_linewidth()
-       
-       if 'relative' in opts:#ugh only when is_fragment
-               body = '\\relative c { %s }' % body
+
+       for o in opts:
+               m= re.search ('relative(.*)', o)
+               v = 0
+               if m:
+                       try:
+                               v = string.atoi (m.group (1))
+                       except ValueError:
+                               pass
+
+                       v = v + 1
+                       pitch = 'c'
+                       if v < 0:
+                               pitch = pitch + '\,' * v
+                       elif v > 0:
+                               pitch = pitch + '\'' * v
+
+                       body = '\\relative %s { %s }' %(pitch, body)
        
        if is_fragment:
                body = r"""\score { 
@@ -521,10 +572,12 @@ def compose_full_body (body, opts):
        optstring = re.sub ('\n', ' ', optstring)
        body = r"""
 %% Generated automatically by: lilypond-book.py
-%% options are %s  %%ughUGH not original options
+%% options are %s  
 \include "paper%d.ly"
-\paper  { linewidth = %f \pt; } 
+\paper  { linewidth = %f \pt } 
 """ % (optstring, music_size, l) + body
+
+       # ughUGH not original options
        return body
 
 def parse_options_string(s):
@@ -561,7 +614,10 @@ def scan_latex_preamble(chunks):
                        idx = idx + 1
                        continue
                m = get_re ('header').match(chunks[idx][1])
-               options = re.split (',[\n \t]*', m.group(1)[1:-1])
+               if m.group (1):
+                       options = re.split (',[\n \t]*', m.group(1)[1:-1])
+               else:
+                       options = []
                for o in options:
                        if o == 'landscape':
                                paperguru.m_landscape = 1
@@ -877,16 +933,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)
@@ -895,7 +941,42 @@ def system (cmd):
                error ('Error command exited with value %d\n' % st)
        return st
 
+
+def get_bbox (filename):
+       f = open (filename)
+       gr = []
+       while 1:
+               l =f.readline ()
+               m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', l)
+               if m:
+                       gr = map (string.atoi, m.groups ())
+                       break
+       
+       return gr
+
+def make_pixmap (name):
+       bbox = get_bbox (name + '.eps')
+       margin = 3
+       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=pgm  -dTextAlphaBits=4 -dGraphicsAlphaBits=4  -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %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 = []
        tex = []
        png = []
@@ -924,34 +1005,37 @@ def compile_all_files (chunks):
                                x = os.path.join (g_here_dir, x)
                        return ' -I %s' % x
 
-               incs =  map (incl_opt, include_path)
+               incs = map (incl_opt, include_path)
                lilyopts = string.join (incs, ' ' )
-               texfiles = string.join (tex, ' ')
                if do_deps:
                        lilyopts = lilyopts + ' --dependencies '
-                       #system ('touch %s' % texfiles)
+                       if g_outdir:
+                               lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
+               texfiles = string.join (tex, ' ')
                system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
 
                #
-               # Ugh, fixing up dependencies for outdir/ and .tex generation
+               # Ugh, fixing up dependencies for .tex generation
                #
                if do_deps:
                        depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
                        for i in depfiles:
-                               text=open (i).read ()
-                               text=re.sub ('\n([^:\n]*):', '\n' + g_outdir + '/' + foutn + ':', text)
-                               text=re.sub (' ([^ /\n]*).ly', ' ' + g_outdir + '/\\1.ly', text)
-                               open (i, 'w').write (text)
+                               f =open (i)
+                               text=f.read ()
+                               f.close ()
+                               text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', 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')
-               system (cmd)
-       if g_outdir:
-               os.chdir(d)
+               make_pixmap (g)
+               
+       os.chdir (d)
 
 
 def update_file (body, name):
@@ -1050,7 +1134,7 @@ Han-Wen Nienhuys <hanwen@cs.uu.nl>
 
 def write_deps (fn, target, chunks):
        global read_files
-       sys.stdout.write('writing `%s\'\n' % os.path.join(g_outdir, fn))
+       sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
        f = open (os.path.join(g_outdir, fn), 'w')
        f.write ('%s%s: ' % (g_dep_prefix, target))
        for d in read_files:
@@ -1063,6 +1147,10 @@ def write_deps (fn, target, chunks):
        for d in basenames:
                if g_outdir:
                        d=g_outdir + '/' + d
+               if g_dep_prefix:
+                       #if not os.isfile (d): # thinko?
+                       if not re.search ('/', d):
+                               d = g_dep_prefix + d
                f.write ('%s.tex ' %  d)
        f.write ('\n')
        #if len (basenames):
@@ -1096,17 +1184,35 @@ 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
 
 
+foutn=""
 def do_file(input_filename):
        global foutn
        file_settings = {}
@@ -1129,6 +1235,8 @@ def do_file(input_filename):
        scan_preamble(chunks)
        chunks = process_lilypond_blocks(my_outname, chunks)
 
+       foutn = os.path.join (g_outdir, my_outname + '.' + format)
+
        # Do It.
        if __main__.g_run_lilypond:
                compile_all_files (chunks)
@@ -1139,7 +1247,6 @@ def do_file(input_filename):
 
        x = 0
        chunks = completize_preamble (chunks)
-       foutn = os.path.join(g_outdir, my_outname + '.' + format)
        sys.stderr.write ("Writing `%s'\n" % foutn)
        fout = open (foutn, 'w')
        for c in chunks:
@@ -1208,6 +1315,7 @@ if g_outdir:
                error ("outdir is a file: %s" % g_outdir)
        if not os.path.exists(g_outdir):
                os.mkdir(g_outdir)
+setup_environment ()
 for input_filename in files:
        do_file(input_filename)