k-l-lambda's picture
added node-addon-lilypond
f65fe85
# book_texinfo.py
# -*- coding: utf-8 -*-
#
# This file is part of LilyPond, the GNU music typesetter.
#
# Copyright (C) 2010--2020 Reinhold Kainhofer <[email protected]>
#
# LilyPond is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# LilyPond is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with LilyPond. If not, see <http://www.gnu.org/licenses/>.
import codecs
import copy
import os
import re
import subprocess
import sys
import tempfile
import book_base
import book_snippets
import lilylib as ly
# Recognize special sequences in the input.
#
# (?P<name>regex) -- Assign result of REGEX to NAME.
# *? -- Match non-greedily.
# (?!...) -- Match if `...' doesn't match next (without consuming
# the string).
#
# (?m) -- Multiline regex: Make ^ and $ match at each line.
# (?s) -- Make the dot match all characters including newline.
# (?x) -- Ignore whitespace in patterns.
# See book_base.BookOutputFormat for possible keys
# 'multiline_comment', 'verbatim', 'lilypond_block', 'singleline_comment',
# 'lilypond_file', 'include', 'lilypond', 'lilypondversion'
TexInfo_snippet_res = {
'include': r'''(?mx)
^(?P<match>
@include\s+
(?P<filename>\S+))''',
'lilypond': r'''(?smx)
^[^\n]*?(?!@c\s+)[^\n]*?
(?P<match>
@lilypond\s*(
\[
\s*(?P<options>.*?)\s*
\])?\s*{
(?P<code>.*?)
})''',
'lilypond_block': r'''(?msx)
^(?P<match>
@lilypond\s*(
\[
\s*(?P<options>.*?)\s*
\])?\s+?
^(?P<code>.*?)
^@end\s+lilypond)\s''',
'lilypond_file': r'''(?mx)
^(?P<match>
@lilypondfile\s*(
\[
\s*(?P<options>.*?)\s*
\])?\s*{
(?P<filename>\S+)
})''',
'multiline_comment': r'''(?smx)
^(?P<match>
(?P<code>
@ignore\s
.*?
@end\s+ignore))\s''',
'musicxml_file': r'''(?mx)
^(?P<match>
@musicxmlfile\s*(
\[
\s*(?P<options>.*?)\s*
\])?\s*{
(?P<filename>\S+)
})''',
'singleline_comment': r'''(?mx)
^.*
(?P<match>
(?P<code>
@c([ \t][^\n]*|)\n))''',
# Don't do this: It interferes with @code{@{}.
# 'verb': r'''(?P<code>@code{.*?})''',
'verbatim': r'''(?sx)
(?P<match>
(?P<code>
@example
\s.*?
@end\s+example\s))''',
'lilypondversion': r'''(?mx)
[^@](?P<match>
@lilypondversion)[^a-zA-Z]''',
}
TexInfo_output = {
book_snippets.ADDVERSION: r'''@example
\version @w{"@version{}"}
@end example
''',
book_snippets.FILTER: r'''@lilypond[%(options)s]
%(code)s@end lilypond''',
book_snippets.OUTPUT: r'''
@iftex
@include %(base)s-systems.texi
@end iftex
''',
book_snippets.OUTPUTIMAGE: r'''@noindent
@ifinfo
@image{%(info_image_path)s,,,%(alt)s,}
@end ifinfo
@html
<p>
<a href="%(base)s%(ext)s">
<img align="middle"
border="0"
src="%(image)s"
alt="%(alt)s">
</a>
</p>
@end html
''',
book_snippets.PRINTFILENAME: '''
@html
<a href="%(base)s%(ext)s">
@end html
@file{%(filename)s}
@html
</a>
@end html
''',
book_snippets.QUOTE: r'''@quotation
%(str)s@end quotation
''',
book_snippets.VERBATIM: r'''@format
@exampleindent 0
%(version)s@verbatim
%(verb)s@end verbatim
@end format
''',
book_snippets.VERSION: r'''%(program_version)s''',
}
texinfo_line_widths = {
'@afourpaper': '160\\mm',
'@afourwide': '6.5\\in',
'@afourlatex': '150\\mm',
'@smallbook': '5\\in',
'@letterpaper': '6\\in',
}
###
# Retrieve dimensions from texinfo
TEXINFO_INSPECTION_DOCUMENT = r'''
\input texinfo
@setfilename Texinfo_width_test
@settitle Texinfo width test
%(preamble)s
@message{Global: textwidth=@the@hsize,exampleindent=@the@lispnarrowing}
dummy
@bye
'''
def get_texinfo_width_indent(source, global_options):
# TODO: Check for end of header command "@c %**end of header"
# only use material before that comment ?
# extract all relevant papter settings from the input:
pagesize = None
texinfo_paper_size_regexp = r'''(@(?:afourpaper|afourwide|afourlatex|afivepaper|smallbook|letterpaper))'''
m = re.search(texinfo_paper_size_regexp, source)
if m:
pagesize = m.group(1)
relevant_settings_regexp = r'''(@(?:fonttextsize|pagesizes|cropmarks|exampleindent).*)\n'''
m = re.findall(relevant_settings_regexp, source)
if pagesize:
m.insert(0, pagesize)
# all relevant options to insert into the test document:
preamble = "\n".join(m)
texinfo_document = TEXINFO_INSPECTION_DOCUMENT % {'preamble': preamble}
(handle, tmpfile) = tempfile.mkstemp('.texi')
outfile = os.path.splitext(tmpfile)[0] + '.pdf'
tmp_handle = open(handle, 'w', encoding='utf8')
tmp_handle.write(texinfo_document)
tmp_handle.close()
# Work around a texi2pdf bug: if LANG=C is not given, a broken regexp is
# used to detect relative/absolute paths, so the absolute path is not
# detected as such and this command fails:
ly.progress(
_("Running texi2pdf on file %s to detect default page settings.\n") % tmpfile)
# execute the command and pipe stdout to the parameter_string:
cmd = '%s --batch -c -o %s %s' % (
global_options.texinfo_program, outfile, tmpfile)
ly.debug_output("Executing: %s\n" % cmd)
run_env = os.environ.copy()
run_env['LC_ALL'] = 'C'
# unknown why this is necessary
universal_newlines = True
if sys.platform == 'mingw32':
universal_newlines = False
# use os.system to avoid weird sleep() problems on
# GUB's python 2.4.2 on mingw
# make file to write to
output_dir = tempfile.mkdtemp()
output_filename = os.path.join(output_dir, 'output.txt')
# call command
cmd += " > %s" % output_filename
returncode = os.system(cmd)
parameter_string = open(output_filename, encoding="utf8").read()
if returncode != 0:
ly.warning(_("Unable to auto-detect default settings:\n"))
# clean up
os.remove(output_filename)
os.rmdir(output_dir)
else:
proc = subprocess.Popen(cmd,
env=run_env,
universal_newlines=universal_newlines,
shell=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(parameter_string, error_string) = proc.communicate()
if proc.returncode != 0:
ly.warning(_("Unable to auto-detect default settings:\n%s")
% error_string)
os.unlink(tmpfile)
if os.path.exists(outfile):
os.unlink(outfile)
# Find textwidth and exampleindent and format it as \\mm or \\in
# Use defaults if they cannot be extracted
textwidth = 0
m = re.search('textwidth=([0-9.]+)pt', parameter_string)
if m:
val = float(m.group(1))/72.27
if pagesize and pagesize.startswith("@afour"):
textwidth = "%g\\mm" % round(val*25.4, 3)
else:
textwidth = "%g\\in" % round(val, 3)
else:
textwidth = texinfo_line_widths.get(pagesize, "6\\in")
exampleindent = 0
m = re.search('exampleindent=([0-9.]+)pt', parameter_string)
if m:
val = float(m.group(1))/72.27
if pagesize and pagesize.startswith("@afour"):
exampleindent = "%g\\mm" % round(val*25.4, 3)
else:
exampleindent = "%g\\in" % round(val, 3)
else:
exampleindent = "0.4\\in"
retval = {book_snippets.LINE_WIDTH: textwidth,
book_snippets.EXAMPLEINDENT: exampleindent}
ly.debug_output("Auto-detected values are: %s\n" % retval)
return retval
texinfo_lang_re = re.compile('(?m)^@documentlanguage (.*?)( |$)')
class BookTexinfoOutputFormat (book_base.BookOutputFormat):
def __init__(self):
book_base.BookOutputFormat.__init__(self)
self.format = "texinfo"
self.default_extension = ".texi"
self.snippet_res = TexInfo_snippet_res
self.output = TexInfo_output
self.handled_extensions = ['.itely', '.tely', '.texi', '.texinfo']
self.snippet_option_separator = r'\s*,\s*'
def can_handle_format(self, format):
return (book_base.BookOutputFormat.can_handle_format(self, format) or
(format in ['texi-html', 'texi']))
def process_options(self, global_options):
self.process_options_pdfnotdefault(global_options)
def get_document_language(self, source):
m = texinfo_lang_re.search(source)
if m and not m.group(1).startswith('en'):
return m.group(1)
else:
return ''
def init_default_snippet_options(self, source):
texinfo_defaults = get_texinfo_width_indent(
source, self.global_options)
self.default_snippet_options.update(texinfo_defaults)
book_base.BookOutputFormat.init_default_snippet_options(self, source)
def adjust_snippet_command(self, cmd):
if '--formats' not in cmd:
return cmd + ' --formats=png '
else:
return cmd
def output_info(self, basename, snippet):
s = ''
rep = snippet.get_replacements()
rep['base'] = basename
rep['filename'] = os.path.basename(snippet.filename)
rep['ext'] = snippet.ext
for image in snippet.get_images():
rep1 = copy.copy(rep)
rep1['base'] = os.path.splitext(image)[0]
rep1['image'] = image
rep1['alt'] = snippet.option_dict[book_snippets.ALT]
rep1['info_image_path'] = os.path.join(
self.global_options.info_images_dir, rep1['base'])
s += self.output[book_snippets.OUTPUTIMAGE] % rep1
s += self.output[book_snippets.OUTPUT] % rep
return s
def snippet_output(self, basename, snippet):
def find(fn):
p = os.path.join(self.global_options.output_dir, fn)
if os.path.exists(p):
return p
return ''
s = ''
base = basename
if book_snippets.DOCTITLE in snippet.option_dict:
doctitle = base + '.doctitle'
translated_doctitle = doctitle + self.document_language
for t in [translated_doctitle, doctitle]:
fullpath = find(t)
if fullpath:
s += '\n@lydoctitle %s\n\n' % codecs.open(
fullpath, 'r', 'utf-8').read().replace(",", "@comma{}")
break
if book_snippets.TEXIDOC in snippet.option_dict:
texidoc = base + '.texidoc'
translated_texidoc = texidoc + self.document_language
for t in [translated_texidoc, texidoc]:
fullpath = find(t)
if fullpath:
s += '@include %s\n\n' % t
break
s += self.output_print_filename(basename, snippet)
substr = ''
rep = snippet.get_replacements()
if book_snippets.VERBATIM in snippet.option_dict:
rep['version'] = ''
if book_snippets.ADDVERSION in snippet.option_dict:
rep['version'] = self.output[book_snippets.ADDVERSION]
rep['verb'] = snippet.verb_ly()
substr = self.output[book_snippets.VERBATIM] % rep
substr += self.output_info(basename, snippet)
if book_snippets.QUOTE in snippet.option_dict:
substr = self.output[book_snippets.QUOTE] % {'str': substr}
s += substr
# need par after image
s += '\n'
return s
def required_files(self, snippet, base, full, required_files):
return self.required_files_png(snippet, base, full, required_files)
book_base.register_format(BookTexinfoOutputFormat())