-texinfo_line_widths = {
- '@afourpaper': '160\\mm',
- '@afourwide': '6.5\\in',
- '@afourlatex': '150\\mm',
- '@smallbook': '5\\in',
- '@letterpaper': '6\\in',
-}
-
-def classic_lilypond_book_compatibility (key, value):
- if key == 'singleline' and value == None:
- return (RAGGED_RIGHT, None)
-
- m = re.search ('relative\s*([-0-9])', key)
- if m:
- return ('relative', m.group (1))
-
- m = re.match ('([0-9]+)pt', key)
- if m:
- return ('staffsize', m.group (1))
-
- if key == 'indent' or key == 'line-width':
- m = re.match ('([-.0-9]+)(cm|in|mm|pt|staffspace)', value)
- if m:
- f = float (m.group (1))
- return (key, '%f\\%s' % (f, m.group (2)))
-
- return (None, None)
-
-def find_file (name, raise_error=True):
- for i in global_options.include_path:
- full = os.path.join (i, name)
- if os.path.exists (full):
- return full
-
- if raise_error:
- error (_ ("file not found: %s") % name + '\n')
- exit (1)
- return ''
-
-def verbatim_html (s):
- return re.sub ('>', '>',
- re.sub ('<', '<',
- re.sub ('&', '&', s)))
-
-def split_options (option_string):
- if option_string:
- if global_options.format == HTML:
- options = re.findall('[\w\.-:]+(?:\s*=\s*(?:"[^"]*"|\'[^\']*\'|\S+))?',option_string)
- for i in range(len(options)):
- options[i] = re.sub('^([^=]+=\s*)(?P<q>["\'])(.*)(?P=q)','\g<1>\g<3>',options[i])
- return options
- else:
- return re.split (format_res[global_options.format]['option_sep'],
- option_string)
- return []
-
-def set_default_options (source):
- global default_ly_options
- if not default_ly_options.has_key (LINE_WIDTH):
- if global_options.format == LATEX:
- textwidth = get_latex_textwidth (source)
- default_ly_options[LINE_WIDTH] = \
- '''%.0f\\pt''' % textwidth
- elif global_options.format == TEXINFO:
- for (k, v) in texinfo_line_widths.items ():
- # FIXME: @layout is usually not in
- # chunk #0:
- #
- # \input texinfo @c -*-texinfo-*-
- #
- # Bluntly search first K items of
- # source.
- # s = chunks[0].replacement_text ()
- if re.search (k, source[:1024]):
- default_ly_options[LINE_WIDTH] = v
- break
-
-class Chunk:
- def replacement_text (self):
- return ''
-
- def filter_text (self):
- return self.replacement_text ()
-
- def ly_is_outdated (self):
- return 0
-
- def png_is_outdated (self):
- return 0
-
- def is_plain (self):
- return False
-
-class Substring (Chunk):
- def __init__ (self, source, start, end, line_number):
- self.source = source
- self.start = start
- self.end = end
- self.line_number = line_number
- self.override_text = None
-
- def is_plain (self):
- return True
-
- def replacement_text (self):
- if self.override_text:
- return self.override_text
- else:
- return self.source[self.start:self.end]
-
-class Snippet (Chunk):
- def __init__ (self, type, match, format, line_number):
- self.type = type
- self.match = match
- self.hash = 0
- self.option_dict = {}
- self.format = format
- self.line_number = line_number
-
- def replacement_text (self):
- return self.match.group ('match')
-
- def substring (self, s):
- return self.match.group (s)
-
- def __repr__ (self):
- return `self.__class__` + ' type = ' + self.type
-
-class Include_snippet (Snippet):
- def processed_filename (self):
- f = self.substring ('filename')
- return os.path.splitext (f)[0] + format2ext[global_options.format]
-
- def replacement_text (self):
- s = self.match.group ('match')
- f = self.substring ('filename')
-
- return re.sub (f, self.processed_filename (), s)
-
-class Lilypond_snippet (Snippet):
- def __init__ (self, type, match, format, line_number):
- Snippet.__init__ (self, type, match, format, line_number)
- os = match.group ('options')
- self.do_options (os, self.type)
-
- def verb_ly (self):
- return self.substring ('code')
-
- def ly (self):
- contents = self.substring ('code')
- return ('\\sourcefileline %d\n%s'
- % (self.line_number - 1, contents))
-
- def full_ly (self):
- s = self.ly ()
- if s:
- return self.compose_ly (s)
- return ''
-
- def do_options (self, option_string, type):
- self.option_dict = {}
-
- options = split_options (option_string)
-
- for i in options:
- if '=' in i:
- (key, value) = re.split ('\s*=\s*', i)
- self.option_dict[key] = value
- else:
- if i in no_options.keys ():
- if no_options[i] in self.option_dict.keys ():
- del self.option_dict[no_options[i]]
- else:
- self.option_dict[i] = None
-
- has_line_width = self.option_dict.has_key (LINE_WIDTH)
- no_line_width_value = 0
-
- # If LINE_WIDTH is used without parameter, set it to default.
- if has_line_width and self.option_dict[LINE_WIDTH] == None:
- no_line_width_value = 1
- del self.option_dict[LINE_WIDTH]
-
- for i in default_ly_options.keys ():
- if i not in self.option_dict.keys ():
- self.option_dict[i] = default_ly_options[i]
-
- if not has_line_width:
- if type == 'lilypond' or FRAGMENT in self.option_dict.keys ():
- self.option_dict[RAGGED_RIGHT] = None
-
- if type == 'lilypond':
- if LINE_WIDTH in self.option_dict.keys ():
- del self.option_dict[LINE_WIDTH]
- else:
- if RAGGED_RIGHT in self.option_dict.keys ():
- if LINE_WIDTH in self.option_dict.keys ():
- del self.option_dict[LINE_WIDTH]
-
- if QUOTE in self.option_dict.keys () or type == 'lilypond':
- if LINE_WIDTH in self.option_dict.keys ():
- del self.option_dict[LINE_WIDTH]
-
- if not INDENT in self.option_dict.keys ():
- self.option_dict[INDENT] = '0\\mm'
-
- # The QUOTE pattern from ly_options only emits the `line-width'
- # keyword.
- if has_line_width and QUOTE in self.option_dict.keys ():
- if no_line_width_value:
- del self.option_dict[LINE_WIDTH]
- else:
- del self.option_dict[QUOTE]
-
- def compose_ly (self, code):
- if FRAGMENT in self.option_dict.keys ():
- body = FRAGMENT_LY
- else:
- body = FULL_LY
-
- # Defaults.
- relative = 1
- override = {}
- # The original concept of the `exampleindent' option is broken.
- # It is not possible to get a sane value for @exampleindent at all
- # without processing the document itself. Saying
- #
- # @exampleindent 0
- # @example
- # ...
- # @end example
- # @exampleindent 5
- #
- # causes ugly results with the DVI backend of texinfo since the
- # default value for @exampleindent isn't 5em but 0.4in (or a smaller
- # value). Executing the above code changes the environment
- # indentation to an unknown value because we don't know the amount
- # of 1em in advance since it is font-dependent. Modifying
- # @exampleindent in the middle of a document is simply not
- # supported within texinfo.
- #
- # As a consequence, the only function of @exampleindent is now to
- # specify the amount of indentation for the `quote' option.
- #
- # To set @exampleindent locally to zero, we use the @format
- # environment for non-quoted snippets.
- override[EXAMPLEINDENT] = r'0.4\in'
- override[LINE_WIDTH] = texinfo_line_widths['@smallbook']
- override.update (default_ly_options)
-
- option_list = []
- for (key, value) in self.option_dict.items ():
- if value == None:
- option_list.append (key)
- else:
- option_list.append (key + '=' + value)
- option_string = ','.join (option_list)
-
- compose_dict = {}
- compose_types = [NOTES, PREAMBLE, LAYOUT, PAPER]
- for a in compose_types:
- compose_dict[a] = []
-
- for (key, value) in self.option_dict.items ():
- (c_key, c_value) = \
- classic_lilypond_book_compatibility (key, value)
- if c_key:
- if c_value:
- warning \
- (_ ("deprecated ly-option used: %s=%s" \
- % (key, value)))
- warning \
- (_ ("compatibility mode translation: %s=%s" \
- % (c_key, c_value)))
- else:
- warning \
- (_ ("deprecated ly-option used: %s" \
- % key))
- warning \
- (_ ("compatibility mode translation: %s" \
- % c_key))
-
- (key, value) = (c_key, c_value)
-
- if value:
- override[key] = value
- else:
- if not override.has_key (key):
- override[key] = None
-
- found = 0
- for type in compose_types:
- if ly_options[type].has_key (key):
- compose_dict[type].append (ly_options[type][key])
- found = 1
- break
-
- if not found and key not in simple_options:
- warning (_ ("ignoring unknown ly option: %s") % key)
-
- # URGS
- if RELATIVE in override.keys () and override[RELATIVE]:
- relative = int (override[RELATIVE])
-
- relative_quotes = ''
-
- # 1 = central C
- if relative < 0:
- relative_quotes += ',' * (- relative)
- elif relative > 0:
- relative_quotes += "'" * relative
-
- 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 = global_options.padding_mm
- font_dump_setting = ''
- if FONTLOAD in self.option_dict:
- font_dump_setting = '#(define-public force-eps-font-include #t)\n'
-
- d = globals().copy()
- d.update (locals())
- return (PREAMBLE_LY + body) % d
-
- def get_hash (self):
- if not self.hash:
- hash = md5.md5 (self.relevant_contents (self.full_ly ()))
-
- ## let's not create too long names.
- self.hash = hash.hexdigest ()[:10]
-
- return self.hash
-
- def basename (self):
- if FILENAME in self.option_dict:
- return self.option_dict[FILENAME]
- if global_options.use_hash:
- return 'lily-%s' % self.get_hash ()
- raise 'to be done'
-
- def write_ly (self):
- outf = open (self.basename () + '.ly', 'w')
- outf.write (self.full_ly ())
- open (self.basename () + '.txt', 'w').write ('image of music')
-
- def relevant_contents (self, ly):
- return re.sub (r'\\(version|sourcefileline|sourcefilename)[^\n]*\n', '', ly)
-
- def ly_is_outdated (self):
- base = self.basename ()
- ly_file = find_file (base + '.ly', raise_error=False)
- tex_file = find_file (base + '.tex', raise_error=False)
- systems_file = find_file (base + '-systems.tex', raise_error=False)
-
- if (os.path.exists (ly_file)
- and os.path.exists (systems_file)
- and os.stat (systems_file)[stat.ST_SIZE]
- and re.match ('% eof', open (systems_file).readlines ()[-1])
- and (global_options.use_hash or FILENAME in self.option_dict)
- and (self.relevant_contents (self.full_ly ())
- == self.relevant_contents (open (ly_file).read ()))):
- return None
-
- return self
-
- def png_is_outdated (self):
- base = self.basename ()
- eps_file = find_file (base + '.eps', raise_error=False)
- png_file = find_file (base + '.png', raise_error=False)
- if not self.ly_is_outdated () and global_options.format in (HTML, TEXINFO):
- if os.path.exists (eps_file):
- page_count = ps_page_count (eps_file)
- if page_count <= 1:
- return not os.path.exists (png_file)
- else:
- return not reduce (operator.or_,
- [find_file (base + '-page%d.png' % a, raise_error=False)
- for a in range (1, page_count + 1)])
- return True
-
- def texstr_is_outdated (self):
- if backend == 'ps':
- return 0
-
- base = self.basename ()
- return not (self.ly_is_outdated ()
- and find_file (base + '.texstr', raise_error=False))
-
- def filter_text (self):
- code = self.substring ('code')
- s = run_filter (code)
- d = {
- 'code': s,
- 'options': self.match.group ('options')
- }
- # TODO
- return output[self.format][FILTER] % d
-
- def replacement_text (self):
- func = Lilypond_snippet.__dict__['output_' + self.format]
- return func (self)
-
- def get_images (self):
- base = self.basename ()
- # URGUGHUGHUGUGH
- single = '%(base)s.png' % vars ()
- multiple = '%(base)s-page1.png' % vars ()
- images = (single,)
- if os.path.exists (multiple) \
- and (not os.path.exists (single) \
- or (os.stat (multiple)[stat.ST_MTIME] \
- > os.stat (single)[stat.ST_MTIME])):
- count = ps_page_count ('%(base)s.eps' % vars ())
- images = ['%s-page%d.png' % (base, a) for a in range (1, count+1)]
- images = tuple (images)
- return images
-
- def output_docbook (self):
- str = ''
- base = self.basename ()
- for image in self.get_images ():
- (base, ext) = os.path.splitext (image)
- str += output[DOCBOOK][OUTPUT] % vars ()
- str += self.output_print_filename (DOCBOOK)
- if (self.substring('inline') == 'inline'):
- str = '<inlinemediaobject>' + str + '</inlinemediaobject>'
- else:
- str = '<mediaobject>' + str + '</mediaobject>'
- if VERBATIM in self.option_dict:
- verb = verbatim_html (self.verb_ly ())
- str = output[DOCBOOK][VERBATIM] % vars () + str
- return str
-
- def output_html (self):
- str = ''
- base = self.basename ()
- if global_options.format == HTML:
- str += self.output_print_filename (HTML)
- if VERBATIM in self.option_dict:
- verb = verbatim_html (self.verb_ly ())
- str += output[HTML][VERBATIM] % vars ()
- if QUOTE in self.option_dict:
- str = output[HTML][QUOTE] % vars ()
-
- str += output[HTML][BEFORE] % vars ()
- for image in self.get_images ():
- (base, ext) = os.path.splitext (image)
- alt = self.option_dict[ALT]
- str += output[HTML][OUTPUT] % vars ()
- str += output[HTML][AFTER] % vars ()
- return str
-
- def output_info (self):
- str = ''
- for image in self.get_images ():
- (base, ext) = os.path.splitext (image)
-
- # URG, makeinfo implicitly prepends dot to extension.
- # Specifying no extension is most robust.
- ext = ''
- alt = self.option_dict[ALT]
- info_image_path = os.path.join (global_options.info_images_dir, base)
- str += output[TEXINFO][OUTPUTIMAGE] % vars ()
-
- base = self.basename ()
- str += output[global_options.format][OUTPUT] % vars ()
- return str
-
- def output_latex (self):
- str = ''
- base = self.basename ()
- if global_options.format == LATEX:
- str += self.output_print_filename (LATEX)
- if VERBATIM in self.option_dict:
- verb = self.verb_ly ()
- str += (output[LATEX][VERBATIM] % vars ())
-
- str += (output[LATEX][OUTPUT] % vars ())
-
- ## todo: maintain breaks
- if 0:
- breaks = self.ly ().count ("\n")
- str += "".ljust (breaks, "\n").replace ("\n","%\n")
-
- if QUOTE in self.option_dict:
- str = output[LATEX][QUOTE] % vars ()
- return str
-
- def output_print_filename (self, format):
- str = ''
- if PRINTFILENAME in self.option_dict:
- base = self.basename ()
- filename = os.path.basename (self.substring ('filename'))
- str = output[format][PRINTFILENAME] % vars ()
-
- return str
-
- def output_texinfo (self):
- str = self.output_print_filename (TEXINFO)
- base = self.basename ()
- if TEXIDOC in self.option_dict:
- texidoc = base + '.texidoc'
- if os.path.exists (texidoc):
- str += '@include %(texidoc)s\n\n' % vars ()
-
- substr = ''
- if VERBATIM in self.option_dict:
- verb = self.verb_ly ()
- if ADDVERSION in self.option_dict:
- verb = output[TEXINFO][ADDVERSION] % vars ()
- substr += output[TEXINFO][VERBATIM] % vars ()
- if not QUOTE in self.option_dict:
- substr = output[TEXINFO][NOQUOTE] % {'str':substr}
- substr += self.output_info ()
- if LILYQUOTE in self.option_dict:
- substr = output[TEXINFO][QUOTE] % {'str':substr}
- str += substr
-
-# str += ('@ifinfo\n' + self.output_info () + '\n@end ifinfo\n')
-# str += ('@tex\n' + self.output_latex () + '\n@end tex\n')
-# str += ('@html\n' + self.output_html () + '\n@end html\n')
-
- if QUOTE in self.option_dict:
- str = output[TEXINFO][QUOTE] % vars ()
-
- # need par after image
- str += '\n'
-
- return str
-
-re_begin_verbatim = re.compile (r'\s+%.*?begin verbatim.*\n*', re.M)
-re_end_verbatim = re.compile (r'\s+%.*?end verbatim.*$', re.M)
-
-class Lilypond_file_snippet (Lilypond_snippet):
- def __init__ (self, type, match, format, line_number):
- Lilypond_snippet.__init__ (self, type, match, format, line_number)
- self.contents = open (find_file (self.substring ('filename'))).read ()
-
- def verb_ly (self):
- s = self.contents
- s = re_begin_verbatim.split (s)[-1]
- s = re_end_verbatim.split (s)[0]
- return s
-
- def ly (self):
- name = self.substring ('filename')
- return ('\\sourcefilename \"%s\"\n\\sourcefileline 0\n%s'
- % (name, self.contents))
-
-
-snippet_type_to_class = {
- 'lilypond_file': Lilypond_file_snippet,
- 'lilypond_block': Lilypond_snippet,
- 'lilypond': Lilypond_snippet,
- 'include': Include_snippet,
-}