]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/lilypond-book.py
patch::: 1.3.132.jcn1
[lilypond.git] / scripts / lilypond-book.py
index 9adb9c3e61c68aabd8007747380c713573ffacd0..e395ca7d48c87d21d06f8246748cb9467c6039b2 100644 (file)
@@ -62,12 +62,13 @@ no_match = 'a\ba'
 
 default_music_fontsize = 16
 default_text_fontsize = 12
+paperguru = None
 
-
+# this code is ugly. It should be cleaned
 class LatexPaper:
        def __init__(self):
                self.m_paperdef =  {
-                       # the dimentions are from geometry.sty
+                       # the dimensions are from geometry.sty
                        'a0paper': (mm2pt(841), mm2pt(1189)),
                        'a1paper': (mm2pt(595), mm2pt(841)),
                        'a2paper': (mm2pt(420), mm2pt(595)),
@@ -102,9 +103,9 @@ class LatexPaper:
        def set_geo_option(self, name, value):
                if name == 'body' or name == 'text':
                        if type(value) == type(""):
-                               self._set_dimen('m_geo_textwidth', value)
+                               self.m_geo_textwidth =  value
                        else:
-                               self._set_dimen('m_geo_textwidth', value[0])
+                               self.m_geo_textwidth =  value[0]
                        self.__body = 1
                elif name == 'portrait':
                        self.m_geo_landscape = 0
@@ -112,10 +113,10 @@ class LatexPaper:
                        if self.m_geo_includemp == None:
                                self.m_geo_includemp = 1
                elif name == 'marginparwidth' or name == 'marginpar':
-                       self._set_dimen('m_geo_x_marginparwidth', value)
+                       self.m_geo_x_marginparwidth =  value
                        self.m_geo_includemp = 1
                elif name == 'marginparsep':
-                       self._set_dimen('m_geo_x_marginparsep', value)
+                       self.m_geo_x_marginparsep =  value
                        self.m_geo_includemp = 1
                elif name == 'scale':
                        if type(value) == type(""):
@@ -125,61 +126,60 @@ class LatexPaper:
                elif name == 'hscale':
                        self.m_geo_width = self.get_paperwidth() * float(value)
                elif name == 'left' or name == 'lmargin':
-                       self._set_dimen('m_geo_lmargin', value)
+                       self.m_geo_lmargin =  value
                elif name == 'right' or name == 'rmargin':
-                       self._set_dimen('m_geo_rmargin', value)
+                       self.m_geo_rmargin =  value
                elif name == 'hdivide' or name == 'divide':
                        if value[0] not in ('*', ''):
-                               self._set_dimen('m_geo_lmargin', value[0])
+                               self.m_geo_lmargin =  value[0]
                        if value[1] not in ('*', ''):
-                               self._set_dimen('m_geo_width', value[1])
+                               self.m_geo_width =  value[1]
                        if value[2] not in ('*', ''):
-                               self._set_dimen('m_geo_rmargin', value[2])
+                               self.m_geo_rmargin =  value[2]
                elif name == 'hmargin':
                        if type(value) == type(""):
-                               self._set_dimen('m_geo_lmargin', value)
-                               self._set_dimen('m_geo_rmargin', value)
+                               self.m_geo_lmargin =  value
+                               self.m_geo_rmargin =  value
                        else:
-                               self._set_dimen('m_geo_lmargin', value[0])
-                               self._set_dimen('m_geo_rmargin', value[1])
+                               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(""):
-                               self._set_dimen('m_geo_lmargin', value)
-                               self._set_dimen('m_geo_rmargin', value)
+                               self.m_geo_lmargin =  value
+                               self.m_geo_rmargin =  value
                        else:
-                               self._set_dimen('m_geo_lmargin', value[0])
-                               self._set_dimen('m_geo_rmargin', value[0])
+                               self.m_geo_lmargin =  value[0]
+                               self.m_geo_rmargin =  value[0]
                elif name == 'total':
                        if type(value) == type(""):
-                               self._set_dimen('m_geo_width', value)
+                               self.m_geo_width =  value
                        else:
-                               self._set_dimen('m_geo_width', value[0])
+                               self.m_geo_width =  value[0]
                elif name == 'width' or name == 'totalwidth':
-                       self._set_dimen('m_geo_width', value)
+                       self.m_geo_width =  value
                elif name == 'paper' or name == 'papername':
                        self.m_papersize = value
                elif name[-5:] == 'paper':
                        self.m_papersize = name
                else:
                        self._set_dimen('m_geo_'+name, value)
-       def _set_dimen(self, name, value):
-               if type(value) == type("") and value[-2:] == 'pt':
-                       self.__dict__[name] = float(value[:-2])
-               elif type(value) == type("") and value[-2:] == 'mm':
-                       self.__dict__[name] = mm2pt(float(value[:-2]))
-               elif type(value) == type("") and value[-2:] == 'cm':
-                       self.__dict__[name] = 10 * mm2pt(float(value[:-2]))
-               elif type(value) == type("") and value[-2:] == 'in':
-                       self.__dict__[name] = in2pt(float(value[:-2]))
+       def __setattr__(self, name, value):
+               if type(value) == type("") and \
+                  dimension_conversion_dict.has_key (value[-2:]):
+                       f = dimension_conversion_dict[dim]
+                       self.__dict__[name] = f(float(value[:-2]))
                else:
                        self.__dict__[name] = value
-       def display(self):
-               print "LatexPaper:\n-----------"
+                       
+       def __str__(self):
+               s =  "LatexPaper:\n-----------"
                for v in self.__dict__.keys():
                        if v[:2] == 'm_':
-                               print v, self.__dict__[v]
-               print "-----------"
+                               s = s +  str (v) + ' ' + str (self.__dict__[v])
+               s = s +  "-----------"
+               return s
+       
        def get_linewidth(self):
                w = self._calc_linewidth()
                if self.m_num_cols == 2:
@@ -204,38 +204,39 @@ class LatexPaper:
                                mp = mp + self.m_geo_x_marginparwidth
                        else:
                                mp = mp + self.m_geo_marginparwidth[self.m_fontsize]
-               if self.__body:#ugh test if this is necessary
+
+               #ugh test if this is necessary                          
+               if self.__body:
                        mp = 0
-               def tNone(a, b, c):
-                       return a == None, b == None, c == None
+
                if not self.m_use_geometry:
                        return latex_linewidths[self.m_papersize][self.m_fontsize]
                else:
-                       if tNone(self.m_geo_lmargin, self.m_geo_width,
-                               self.m_geo_rmargin) == (1, 1, 1):
+                       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
                                w = self.get_paperwidth() * 0.8
                                return w - mp
-                       elif tNone(self.m_geo_lmargin, self.m_geo_width,
-                                self.m_geo_rmargin) == (0, 1, 1):
+                       elif geo_opts == (0, 1, 1):
                                 if self.m_geo_textwidth:
                                        return self.m_geo_textwidth
                                 return self.f1(self.m_geo_lmargin, mp)
-                       elif tNone(self.m_geo_lmargin, self.m_geo_width,
-                                self.m_geo_rmargin) == (1, 1, 0):
+                       elif geo_opts == (1, 1, 0):
                                 if self.m_geo_textwidth:
                                        return self.m_geo_textwidth
                                 return self.f1(self.m_geo_rmargin, mp)
-                       elif tNone(self.m_geo_lmargin, self.m_geo_width,
-                               self.m_geo_rmargin) \
+                       elif geo_opts \
                                        in ((0, 0, 1), (1, 0, 0), (1, 0, 1)):
                                if self.m_geo_textwidth:
                                        return self.m_geo_textwidth
                                return self.m_geo_width - mp
-                       elif tNone(self.m_geo_lmargin, self.m_geo_width,
-                               self.m_geo_rmargin) in ((0, 1, 0), (0, 0, 0)):
-                               w = self.get_paperwidth() - self.m_geo_lmargin - self.m_geo_rmargin - mp
+                       elif geo_opts in ((0, 1, 0), (0, 0, 0)):
+                               w = self.get_paperwidth() \
+                                 - self.m_geo_lmargin - self.m_geo_rmargin - mp
                                if w < 0:
                                        w = 0
                                return w
@@ -263,10 +264,22 @@ def mm2pt(x):
        return x * 2.8452756
 def in2pt(x):
        return x * 72.26999
-def em2pt(x, fontsize):
+def em2pt(x, fontsize = 10):
        return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
-def ex2pt(x, fontsize):
+def ex2pt(x, fontsize = 10):
        return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
+
+def pt2pt(x):
+       return x
+
+dimension_conversion_dict ={
+       'mm': mm2pt,
+       'in': in2pt,
+       'em': em2pt,
+       'ex': ex2pt,
+       'pt': pt2pt
+       }
+
        
 # latex linewidths:
 # indices are no. of columns, papersize,  fontsize
@@ -315,8 +328,11 @@ output_dict= {
       %s
     }
   >
-\end{lilypond}""", 
-               'output-lilypond':r"""\begin[%s]{lilypond}
+\end{lilypond}""",
+               'output-filename' : r'''
+
+\verb+%s+:''',
+               'output-lilypond': r"""\begin[%s]{lilypond}
 %s
 \end{lilypond}""",
                'output-verbatim': "\\begin{verbatim}%s\\end{verbatim}",
@@ -331,6 +347,9 @@ output_dict= {
 %s
 @end lilypond 
 """,
+               'output-filename' : r'''
+
+@file{%s}:''',   
                  'output-lilypond-fragment': """@lilypond[%s]
 \context Staff\context Voice{ %s }
 @end lilypond """,
@@ -347,7 +366,8 @@ output_dict= {
 
 # should also support fragment in
                  
-                 'output-all': r"""@tex
+                 'output-all': r"""
+@tex
 \catcode`\@=12
 \input lilyponddefs
 \def\EndLilyPondOutput{}
@@ -388,7 +408,9 @@ re_dict = {
                  'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
                  'numcols': r"(?P<code>\\(?P<num>one|two)column)",
                  },
-       
+
+
+       # 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,
@@ -485,7 +507,7 @@ def compose_full_body (body, opts):
        if 'singleline' in opts:
                l = -1.0;
        else:
-               l = paperguru.get_linewidth()
+               l = __main__.paperguru.get_linewidth()
        
        if 'relative' in opts:#ugh only when is_fragment
                body = '\\relative c { %s }' % body
@@ -500,7 +522,8 @@ def compose_full_body (body, opts):
        optstring = string.join (opts, ' ')
        optstring = re.sub ('\n', ' ', optstring)
        body = r"""
-%% Generated by lilypond-book.py; options are %s  %%ughUGH not original options
+%% Generated automatically by: lilypond-book.py
+%% options are %s  %%ughUGH not original options
 \include "paper%d.ly"
 \paper  { linewidth = %f \pt; } 
 """ % (optstring, music_size, l) + body
@@ -527,7 +550,8 @@ def parse_options_string(s):
                        s = s[m.end():]
                        d[m.group(1)] = 1
                        continue
-               print "trøbbel:%s:" % s
+               
+               error ("format of option string invalid (was `%')" % s)
        return d
 
 def scan_latex_preamble(chunks):
@@ -566,16 +590,12 @@ def scan_latex_preamble(chunks):
 
 def scan_texi_preamble (chunks):
        # this is not bulletproof..., it checks the first 10 chunks
-       idx = 0
-       while 1:
-               if chunks[idx][0] == 'input':
+       for c in chunks[:10]: 
+               if c[0] == 'input':
                        for s in ('afourpaper', 'afourwide', 'letterpaper',
                                  'afourlatex', 'smallbook'):
-                               if string.find(chunks[idx][1], "@%s" % s) != -1:
+                               if string.find(c[1], "@%s" % s) != -1:
                                        paperguru.m_papersize = s
-               idx = idx + 1
-               if idx == 10 or idx == len(chunks):
-                       break
 
 def scan_preamble (chunks):
        if __main__.format == 'texi':
@@ -618,7 +638,12 @@ def completize_preamble (chunks):
 
 read_files = []
 def find_file (name):
+       """
+       Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
+       """
+       
        f = None
+       nm = ''
        for a in include_path:
                try:
                        nm = os.path.join (a, name)
@@ -628,10 +653,11 @@ def find_file (name):
                except IOError:
                        pass
        if f:
-               return f.read ()
+               sys.stderr.write ("Reading `%s'\n" % nm)
+               return (f.read (), nm)
        else:
                error ("File not found `%s'\n" % name)
-               return ''
+               return ('', '')
 
 def do_ignore(match_object):
        return [('ignore', match_object.group('code'))]
@@ -662,12 +688,22 @@ def make_lilypond(m):
                        (options, m.group('code')))]
 
 def make_lilypond_file(m):
+       """
+
+       Find @lilypondfile{bla.ly} occurences and substitute bla.ly
+       into a @lilypond .. @end lilypond block.
+       
+       """
+       
        if m.group('options'):
                options = m.group('options')
        else:
                options = ''
+       (content, nm) = find_file(m.group('filename'))
+       options = "filename=%s," % nm + options
+
        return [('input', get_output('output-lilypond') %
-                       (options, find_file(m.group('filename'))))]
+                       (options, content))]
 
 def make_lilypond_block(m):
        if m.group('options'):
@@ -708,26 +744,40 @@ def chop_chunks(chunks, re_name, func, use_match=0):
             newchunks.append(c)
     return newchunks
 
-def read_doc_file (filename):
-       """Read the input file, find verbatim chunks and do \input and \include
-       """
-       str = ''
-       str = find_file(filename)
-
+def determine_format (str):
        if __main__.format == '':
+               
                latex =  re.search ('\\\\document', str[:200])
                texinfo =  re.search ('@node|@setfilename', str[:200])
-               if (texinfo and latex) or not (texinfo or latex):
+
+               f = ''
+               g = None
+               
+               if texinfo and latex == None:
+                       f = 'texi'
+               elif latex and texinfo == None: 
+                       f = 'latex'
+               else:
                        error("error: can't determine format, please specify")
-               if texinfo:
-                       __main__.format = 'texi'
+               __main__.format = f
+
+       if __main__.paperguru == None:
+               if __main__.format == 'texi':
+                       g = TexiPaper()
                else:
-                       __main__.format = 'latex'
-       if __main__.format == 'texi':
-               __main__.paperguru = TexiPaper()
-       else:
-               __main__.paperguru = LatexPaper()
+                       g = LatexPaper()
+                       
+               __main__.paperguru = g
+
+
+def read_doc_file (filename):
+       """Read the input file, find verbatim chunks and do \input and \include
+       """
+       (str, path) = find_file(filename)
+       determine_format (str)
+       
        chunks = [('input', str)]
+       
        # we have to check for verbatim before doing include,
        # because we don't want to include files that are mentioned
        # inside a verbatim environment
@@ -755,7 +805,7 @@ def schedule_lilypond_block (chunk):
        (type, body, opts) = chunk
        assert type == 'lilypond'
        file_body = compose_full_body (body, opts)
-       basename = `abs(hash (file_body))`
+       basename = 'lily-' + `abs(hash (file_body))`
        for o in opts:
                m = re.search ('filename="(.*?)"', o)
                if m:
@@ -774,7 +824,7 @@ def schedule_lilypond_block (chunk):
                needed_filetypes.append('png')
        if 'eps' in opts and not ('eps' in needed_filetypes):
                needed_filetypes.append('eps')
-       outname = os.path.join(g_outdir, basename)
+       pathbase = os.path.join (g_outdir, basename)
        def f(base, ext1, ext2):
                a = os.path.isfile(base + ext2)
                if (os.path.isfile(base + ext1) and
@@ -784,13 +834,22 @@ def schedule_lilypond_block (chunk):
                                not os.path.isfile(base + ext2):
                        return 1
        todo = []
-       if 'tex' in needed_filetypes and f(outname, '.ly', '.tex'):
+       if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
                todo.append('tex')
-       if 'eps' in needed_filetypes and f(outname, '.tex', '.eps'):
+       if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
                todo.append('eps')
-       if 'png' in needed_filetypes and f(outname, '.eps', '.png'):
+       if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
                todo.append('png')
        newbody = ''
+
+       if 'printfilename' in opts:
+               for o in opts:
+                       m= re.match ("filename=(.*)", o)
+                       if m:
+                               newbody = newbody + get_output ("output-filename") % m.group(1)
+                               break
+               
+       
        if 'verbatim' in opts:
                newbody = output_verbatim (body)
 
@@ -804,8 +863,8 @@ def schedule_lilypond_block (chunk):
                else:
                        s = 'output-tex'
        else: # format == 'texi'
-               s = 'output-all'
-       newbody = newbody + get_output(s) % {'fn': basename }
+               s = 'output-all'
+       newbody = newbody + get_output (s) % {'fn': basename }
        return ('lilypond', newbody, opts, todo, basename)
 
 def process_lilypond_blocks(outname, chunks):#ugh rename
@@ -839,6 +898,7 @@ def system (cmd):
        return st
 
 def compile_all_files (chunks):
+       global foutn
        eps = []
        tex = []
        png = []
@@ -867,10 +927,25 @@ 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, ' ' )
+               if do_deps:
+                       lilyopts = lilyopts + ' --dependencies '
+                       if g_outdir:
+                               lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
                texfiles = string.join (tex, ' ')
-               system ('lilypond %s %s' % (lilyopts, texfiles))
+               system ('lilypond --header=texidoc %s %s' % (lilyopts, texfiles))
+
+               #
+               # 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' + foutn + ':', text)
+                               open (i, 'w').write (text)
+
        for e in eps:
                system(r"tex '\nonstopmode \input %s'" % e)
                system(r"dvips -E -o %s %s" % (e + '.eps', e))
@@ -878,8 +953,7 @@ def compile_all_files (chunks):
                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)
+       os.chdir (d)
 
 
 def update_file (body, name):
@@ -976,15 +1050,34 @@ Han-Wen Nienhuys <hanwen@cs.uu.nl>
        sys.exit (0)
 
 
-def write_deps (fn, target):
-       sys.stdout.write('writing `%s\'\n' % os.path.join(g_outdir, fn))
+def write_deps (fn, target, chunks):
+       global read_files
+       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 __main__.read_files:
+       for d in read_files:
                f.write ('%s ' %  d)
+       basenames=[]
+        for c in chunks:
+               if c[0] == 'lilypond':
+                       (type, body, opts, todo, basename) = c;
+                       basenames.append (basename)
+       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):
+       #       for d in basenames:
+       #               f.write ('%s.ly ' %  d)
+       #       f.write (' : %s' % target)
        f.write ('\n')
        f.close ()
-       __main__.read_files = []
+       read_files = []
 
 def identify():
        sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
@@ -996,7 +1089,33 @@ Distributed under terms of the GNU General Public License. It comes with
 NO WARRANTY.
 """)
 
+
+def check_texidoc (chunks):
+       n = []
+        for c in chunks:
+               if c[0] == 'lilypond':
+                       (type, body, opts, todo, basename) = c;
+                       pathbase = os.path.join (g_outdir, basename)
+                       if os.path.isfile (pathbase + '.texidoc'):
+                               body = '\n@include %s.texidoc\n' % basename + body
+                               c = (type, body, opts, todo, basename)
+               n.append (c)
+       return n
+
+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:
+                       newchunks.append (c)
+       return newchunks
+
+
+foutn=""
 def do_file(input_filename):
+       global foutn
        file_settings = {}
        if outname:
                my_outname = outname
@@ -1016,21 +1135,19 @@ def do_file(input_filename):
        #sys.exit()
        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)
-               newchunks = []
-               # finishing touch.
-               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))
-                       else:
-                               newchunks.append (c)
-               chunks = newchunks
+               chunks = fix_epswidth (chunks)
+
+       if __main__.format == 'texi':
+               chunks = check_texidoc (chunks)
+
        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:
@@ -1039,7 +1156,7 @@ def do_file(input_filename):
        # should chmod -w
 
        if do_deps:
-               write_deps (my_depname, foutn)
+               write_deps (my_depname, foutn, chunks)
 
 
 outname = ''