]> git.donarmstrong.com Git - lilypond.git/blobdiff - python/book_snippets.py
Lilypond-book: Improve comments
[lilypond.git] / python / book_snippets.py
index a19a5796d7e330eb74ab87053791963b6d40450d..cbf09a7a28f59bcf47cb8103883c2d511bedc510 100644 (file)
@@ -38,14 +38,11 @@ EXAMPLEINDENT = 'exampleindent'
 FILENAME = 'filename'
 FILTER = 'filter'
 FRAGMENT = 'fragment'
-LANG = 'lang'    ## TODO: This is handled nowhere!
 LAYOUT = 'layout'
-LILYQUOTE = 'lilyquote'
 LINE_WIDTH = 'line-width'
 NOFRAGMENT = 'nofragment'
 NOGETTEXT = 'nogettext'
 NOINDENT = 'noindent'
-NOQUOTE = 'noquote'
 INDENT = 'indent'
 NORAGGED_RIGHT = 'noragged-right'
 NOTES = 'body'
@@ -53,6 +50,7 @@ NOTIME = 'notime'
 OUTPUT = 'output'
 OUTPUTIMAGE = 'outputimage'
 PAPER = 'paper'
+PAPERSIZE = 'papersize'
 PREAMBLE = 'preamble'
 PRINTFILENAME = 'printfilename'
 QUOTE = 'quote'
@@ -67,7 +65,6 @@ VERSION = 'lilypondversion'
 
 # NOTIME and NOGETTEXT have no opposite so they aren't part of this
 # dictionary.
-# NOQUOTE is used internally only.
 no_options = {
     NOFRAGMENT: FRAGMENT,
     NOINDENT: INDENT,
@@ -91,7 +88,6 @@ simple_options = [
     PRINTFILENAME,
     DOCTITLE,
     TEXIDOC,
-    LANG,
     VERBATIM,
     FILENAME,
     ALT,
@@ -112,10 +108,10 @@ snippet_options = {
 
     ##
     PAPER: {
+        PAPERSIZE: r'''#(set-paper-size "%(papersize)s")''',
         INDENT: r'''indent = %(indent)s''',
         LINE_WIDTH: r'''line-width = %(line-width)s''',
         QUOTE: r'''line-width = %(line-width)s - 2.0 * %(exampleindent)s''',
-        LILYQUOTE: r'''line-width = %(line-width)s - 2.0 * %(exampleindent)s''',
         RAGGED_RIGHT: r'''ragged-right = ##t''',
         NORAGGED_RIGHT: r'''ragged-right = ##f''',
     },
@@ -143,24 +139,9 @@ snippet_options = {
 
 
 
-FRAGMENT_LY = r'''
-%(notes_string)s
-{
-
-
-%% ****************************************************************
-%% ly snippet contents follows:
-%% ****************************************************************
-%(code)s
-
-
-%% ****************************************************************
-%% end ly snippet
-%% ****************************************************************
-}
-'''
-
 def classic_lilypond_book_compatibility (key, value):
+    if key == 'lilyquote':
+        return (QUOTE, value)
     if key == 'singleline' and value == None:
         return (RAGGED_RIGHT, None)
 
@@ -181,6 +162,8 @@ def classic_lilypond_book_compatibility (key, value):
     return (None, None)
 
 
+# TODO: Remove the 1mm additional padding in the line-width, once lilypond
+#       creates tighter cropped images!
 PREAMBLE_LY = '''%%%% Generated by %(program_name)s
 %%%% Options: [%(option_string)s]
 \\include "lilypond-book-preamble.ly"
@@ -194,13 +177,16 @@ PREAMBLE_LY = '''%%%% Generated by %(program_name)s
 
 \paper {
   %(paper_string)s
-  force-assignment = #""
-  line-width = #(- line-width (* mm  %(padding_mm)f))
+  %% offset the left padding, also add 1mm as lilypond creates cropped
+  %% images with a little space on the right
+  line-width = #(- line-width (* mm  %(padding_mm)f) (* mm 1))
 }
 
 \layout {
   %(layout_string)s
 }
+
+%(safe_mode_string)s
 '''
 
 
@@ -218,9 +204,22 @@ FULL_LY = '''
 %% ****************************************************************
 '''
 
+FRAGMENT_LY = r'''
+%(notes_string)s
+{
+
 
+%% ****************************************************************
+%% ly snippet contents follows:
+%% ****************************************************************
+%(code)s
 
 
+%% ****************************************************************
+%% end ly snippet
+%% ****************************************************************
+}
+'''
 
 
 
@@ -396,13 +395,13 @@ class LilypondSnippet (Snippet):
                     self.option_dict[option] = None
 
 
-        # If LINE_WIDTH is used without parameter, set it to default.
+        # Store if we have an explicit line-width given
         has_line_width = self.option_dict.has_key (LINE_WIDTH)
         if has_line_width and self.option_dict[LINE_WIDTH] == None:
             has_line_width = False
             del self.option_dict[LINE_WIDTH]
 
-        # TODO: Can't we do that more efficiently (built-in python func?)
+        # Use default options (i.e. auto-detected line-width, etc)
         for k in self.formatter.default_snippet_options:
             if k not in self.option_dict:
                 self.option_dict[k] = self.formatter.default_snippet_options[k]
@@ -413,22 +412,6 @@ class LilypondSnippet (Snippet):
         if has_relative and not self.option_dict.has_key (FRAGMENT):
             self.option_dict[FRAGMENT] = None
 
-        if not has_line_width:
-            if type == 'lilypond' or FRAGMENT in self.option_dict:
-                self.option_dict[RAGGED_RIGHT] = None
-
-            if type == 'lilypond':
-                if LINE_WIDTH in self.option_dict:
-                    del self.option_dict[LINE_WIDTH]
-            else:
-                if RAGGED_RIGHT in self.option_dict:
-                    if LINE_WIDTH in self.option_dict:
-                        del self.option_dict[LINE_WIDTH]
-
-            if QUOTE in self.option_dict or type == 'lilypond':
-                if LINE_WIDTH in self.option_dict:
-                    del self.option_dict[LINE_WIDTH]
-
         if not INDENT in self.option_dict:
             self.option_dict[INDENT] = '0\\mm'
 
@@ -439,8 +422,7 @@ class LilypondSnippet (Snippet):
         # all settings before writing them in the \paper block.
         if not LINE_WIDTH in self.option_dict:
             if not QUOTE in self.option_dict:
-                if not LILYQUOTE in self.option_dict:
-                    self.option_dict[LINE_WIDTH] = "#(- paper-width \
+                self.option_dict[LINE_WIDTH] = "#(- paper-width \
 left-margin-default right-margin-default)"
 
     def get_option_list (self):
@@ -456,10 +438,6 @@ left-margin-default right-margin-default)"
         return self.option_list
 
     def compose_ly (self, code):
-        if FRAGMENT in self.option_dict:
-            body = FRAGMENT_LY
-        else:
-            body = FULL_LY
 
         # Defaults.
         relative = 1
@@ -551,15 +529,29 @@ left-margin-default right-margin-default)"
         elif relative > 0:
             relative_quotes += "'" * relative
 
+        # put paper-size first, if it exists
+        for i,elem in enumerate(compose_dict[PAPER]):
+            if elem.startswith("#(set-paper-size"):
+                compose_dict[PAPER].insert(0, compose_dict[PAPER].pop(i))
+                break
+
         paper_string = '\n  '.join (compose_dict[PAPER]) % override
         layout_string = '\n  '.join (compose_dict[LAYOUT]) % override
         notes_string = '\n  '.join (compose_dict[NOTES]) % vars ()
         preamble_string = '\n  '.join (compose_dict[PREAMBLE]) % override
         padding_mm = self.global_options.padding_mm
+        if self.global_options.safe_mode:
+            safe_mode_string = "#(ly:set-option 'safe #t)"
+        else:
+            safe_mode_string = ""
 
         d = globals().copy()
         d.update (locals())
         d.update (self.global_options.information)
+        if FRAGMENT in self.option_dict:
+            body = FRAGMENT_LY
+        else:
+            body = FULL_LY
         return (PREAMBLE_LY + body) % d
 
     def get_checksum (self):
@@ -645,6 +637,11 @@ printing diff against existing file." % filename)
                 os.makedirs (dst_path)
             os.link (src, dst)
 
+    def additional_files_to_consider (self, base, full):
+        return []
+    def additional_files_required (self, base, full):
+        return []
+
 
     def all_output_files (self, output_dir, output_dir_files):
         """Return all files generated in lily_output_dir, a set.
@@ -708,6 +705,8 @@ printing diff against existing file." % filename)
             if 'ddump-signature' in self.global_options.process_cmd:
                 consider_file (systemfile + '.signature')
 
+        map (consider_file, self.additional_files_to_consider (base, full))
+        map (require_file, self.additional_files_required (base, full))
 
         return (result, missing)
 
@@ -719,7 +718,7 @@ printing diff against existing file." % filename)
         """Pass input through cmd, and return the result."""
 
         if self.global_options.verbose:
-            progress (_ ("Opening filter `%s'\n") % cmd)
+            progress (_ ("Running through filter `%s'\n") % cmd)
 
         # TODO: Use Popen once we resolve the problem with msvcrt in Windows:
         (stdin, stdout, stderr) = os.popen3 (cmd)
@@ -732,16 +731,16 @@ printing diff against existing file." % filename)
             status = 0
             output = stdout.read ()
             status = stdout.close ()
-            error = stderr.read ()
+            err = stderr.read ()
 
         if not status:
             status = 0
         signal = 0x0f & status
-        if status or (not output and error):
+        if status or (not output and err):
             exit_status = status >> 8
             ly.error (_ ("`%s' failed (%d)") % (cmd, exit_status))
             ly.error (_ ("The error log is as follows:"))
-            ly.stderr_write (error)
+            ly.stderr_write (err)
             ly.stderr_write (stderr.read ())
             exit (status)
 
@@ -794,7 +793,9 @@ re_end_verbatim = re.compile (r'\s+%.*?end verbatim.*$', re.M)
 class LilypondFileSnippet (LilypondSnippet):
     def __init__ (self, type, match, formatter, line_number, global_options):
         LilypondSnippet.__init__ (self, type, match, formatter, line_number, global_options)
-        self.contents = file (BookBase.find_file (self.substring ('filename'), global_options.include_path)).read ()
+        self.filename = self.substring ('filename')
+        self.ext = os.path.splitext (os.path.basename (self.filename))[1]
+        self.contents = file (BookBase.find_file (self.filename, global_options.include_path)).read ()
 
     def get_snippet_code (self):
         return self.contents;
@@ -810,18 +811,113 @@ class LilypondFileSnippet (LilypondSnippet):
         return s
 
     def ly (self):
-        name = self.substring ('filename')
+        name = self.filename
         return ('\\sourcefilename \"%s\"\n\\sourcefileline 0\n%s'
                 % (name, self.contents))
 
     def final_basename (self):
         if self.global_options.use_source_file_names:
-            base = os.path.splitext (os.path.basename (self.substring ('filename')))[0]
+            base = os.path.splitext (os.path.basename (self.filename))[0]
             return base
         else:
             return self.basename ()
 
 
+class MusicXMLFileSnippet (LilypondFileSnippet):
+    def __init__ (self, type, match, formatter, line_number, global_options):
+        LilypondFileSnippet.__init__ (self, type, match, formatter, line_number, global_options)
+        self.compressed = False
+        self.converted_ly = None
+        self.musicxml_options_dict = {
+           'verbose': '--verbose',
+           'lxml': '--lxml',
+           'compressed': '--compressed',
+           'relative': '--relative',
+           'absolute': '--absolute',
+           'no-articulation-directions': '--no-articulation-directions',
+           'no-rest-positions': '--no-rest-positions',
+           'no-page-layout': '--no-page-layout',
+           'no-beaming': '--no-beaming',
+           'language': '--language',
+        }
+
+    def snippet_options (self):
+        return self.musicxml_options_dict.keys ()
+
+    def convert_from_musicxml (self):
+        name = self.filename
+        xml2ly_option_list = []
+        for (key, value) in self.option_dict.items ():
+            cmd_key = self.musicxml_options_dict.get (key, None)
+            if cmd_key == None:
+                continue
+            if value == None:
+                xml2ly_option_list.append (cmd_key)
+            else:
+                xml2ly_option_list.append (cmd_key + '=' + value)
+        if ('.mxl' in name) and ('--compressed' not in xml2ly_option_list):
+            xml2ly_option_list.append ('--compressed')
+            self.compressed = True
+        opts = " ".join (xml2ly_option_list)
+        progress (_ ("Converting MusicXML file `%s'...\n") % self.filename)
+
+        ly_code = self.filter_pipe (self.contents, 'musicxml2ly %s --out=- - ' % opts)
+        return ly_code
+
+    def ly (self):
+        if self.converted_ly == None:
+            self.converted_ly = self.convert_from_musicxml ()
+        name = self.filename
+        return ('\\sourcefilename \"%s\"\n\\sourcefileline 0\n%s'
+                % (name, self.converted_ly))
+
+    def additional_files_required (self, base, full):
+        result = [];
+        if self.compressed:
+            result.append (base + '.mxl')
+        else:
+            result.append (base + '.xml')
+        return result
+
+    def write_ly (self):
+        base = self.basename ()
+        path = os.path.join (self.global_options.lily_output_dir, base)
+        directory = os.path.split(path)[0]
+        if not os.path.isdir (directory):
+            os.makedirs (directory)
+
+        # First write the XML to a file (so we can link it!)
+        if self.compressed:
+            xmlfilename = path + '.mxl'
+        else:
+            xmlfilename = path + '.xml'
+        if os.path.exists (xmlfilename):
+            diff_against_existing = self.filter_pipe (self.contents, 'diff -u %s - ' % xmlfilename)
+            if diff_against_existing:
+                warning (_ ("%s: duplicate filename but different contents of orginal file,\n\
+printing diff against existing file.") % xmlfilename)
+                ly.stderr_write (diff_against_existing)
+        else:
+            out = file (xmlfilename, 'w')
+            out.write (self.contents)
+            out.close ()
+
+        # also write the converted lilypond
+        filename = path + '.ly'
+        if os.path.exists (filename):
+            diff_against_existing = self.filter_pipe (self.full_ly (), 'diff -u %s -' % filename)
+            if diff_against_existing:
+                warning (_ ("%s: duplicate filename but different contents of converted lilypond file,\n\
+printing diff against existing file.") % filename)
+                ly.stderr_write (diff_against_existing)
+        else:
+            out = file (filename, 'w')
+            out.write (self.full_ly ())
+            out.close ()
+            file (path + '.txt', 'w').write ('image of music')
+
+
+
 class LilyPondVersionString (Snippet):
     """A string that does not require extra memory."""
     def __init__ (self, type, match, formatter, line_number, global_options):
@@ -837,4 +933,5 @@ snippet_type_to_class = {
     'lilypond': LilypondSnippet,
     'include': IncludeSnippet,
     'lilypondversion': LilyPondVersionString,
+    'musicxml_file': MusicXMLFileSnippet,
 }