k-l-lambda's picture
added node-addon-lilypond
f65fe85
# book_latex.py
# -*- coding: utf-8 -*-
#
# This file is part of LilyPond, the GNU music typesetter.
#
# Copyright (C) 2010 Reinhold Kainhofer <[email protected]>,
# 2020--2020 Han-Wen Nienhuys <[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 os
import re
import subprocess
import sys
import tempfile
import book_base
import book_snippets
import lilylib as ly
progress = ly.progress
warning = ly.warning
error = ly.error
debug = ly.debug_output
# 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
Latex_snippet_res = {
'include':
r'''(?smx)
^[^%\n]*?
(?P<match>
\\input\s*{
(?P<filename>\S+?)
})''',
'lilypond':
r'''(?smx)
^[^%\n]*?
(?P<match>
\\lilypond\s*(
\[
\s*(?P<options>.*?)\s*
\])?\s*{
(?P<code>.*?)
})''',
'lilypond_block':
r'''(?smx)
^[^%\n]*?
(?P<match>
\\begin\s*(?P<env>{lilypond}\s*)?(
\[
\s*(?P<options>.*?)\s*
\])?(?(env)|\s*{lilypond})
(?P<code>.*?)
^[^%\n]*?
\\end\s*{lilypond})''',
'lilypond_file':
r'''(?smx)
^[^%\n]*?
(?P<match>
\\lilypondfile\s*(
\[
\s*(?P<options>.*?)\s*
\])?\s*\{
(?P<filename>\S+?)
})''',
'musicxml_file':
r'''(?smx)
^[^%\n]*?
(?P<match>
\\musicxmlfile\s*(
\[
\s*(?P<options>.*?)\s*
\])?\s*\{
(?P<filename>\S+?)
})''',
'singleline_comment':
r'''(?mx)
^.*?
(?P<match>
(?P<code>
%.*$\n+))''',
'verb':
r'''(?mx)
^[^%\n]*?
(?P<match>
(?P<code>
\\verb(?P<del>.)
.*?
(?P=del)))''',
'verbatim':
r'''(?msx)
^[^%\n]*?
(?P<match>
(?P<code>
\\begin\s*{verbatim}
.*?
\\end\s*{verbatim}))''',
'lilypondversion':
r'''(?smx)
(?P<match>
\\lilypondversion)[^a-zA-Z]''',
}
Latex_output = {
book_snippets.FILTER: r'''\begin{lilypond}[%(options)s]
%(code)s
\end{lilypond}''',
book_snippets.OUTPUT: r'''{%%
\parindent 0pt
\noindent
\ifx\preLilyPondExample \undefined
\else
\expandafter\preLilyPondExample
\fi
\def\lilypondbook{}%%
\input{%(base)s-systems.tex}
\ifx\postLilyPondExample \undefined
\else
\expandafter\postLilyPondExample
\fi
}''',
book_snippets.PRINTFILENAME: r'''\texttt{%(filename)s}
\linebreak
''',
book_snippets.QUOTE: r'''\begin{quote}
%(str)s
\end{quote}''',
book_snippets.VERBATIM: r'''\noindent
\begin{verbatim}%(verb)s\end{verbatim}
''',
book_snippets.VERSION: r'''%(program_version)s''',
}
###
# Retrieve dimensions from LaTeX
LATEX_INSPECTION_DOCUMENT = r'''
\nonstopmode
%(preamble)s
\begin{document}
\typeout{textwidth=\the\textwidth}
\typeout{columnsep=\the\columnsep}
\makeatletter\if@twocolumn\typeout{columns=2}\fi\makeatother
\end{document}
'''
# Do we need anything else besides `textwidth'?
def get_latex_textwidth(source, global_options):
# default value
textwidth = 550.0
m = re.search(r'''(?P<preamble>\\begin\s*{document})''', source)
if m is None:
warning(_("cannot find \\begin{document} in LaTeX document"))
return textwidth
preamble = source[:m.start(0)]
latex_document = LATEX_INSPECTION_DOCUMENT % {'preamble': preamble}
(handle, tmpfile) = tempfile.mkstemp('.tex')
tmpfileroot = os.path.splitext(tmpfile)[0]
tmpfileroot = os.path.split(tmpfileroot)[1]
auxfile = tmpfileroot + '.aux'
logfile = tmpfileroot + '.log'
tmp_handle = os.fdopen(handle, 'w')
tmp_handle.write(latex_document)
tmp_handle.close()
progress(_("Running `%s' on file `%s' to detect default page settings.\n")
% (global_options.latex_program, tmpfile))
cmd = '%s %s' % (global_options.latex_program, tmpfile)
debug("Executing: %s\n" % cmd)
run_env = os.environ.copy()
run_env['LC_ALL'] = 'C'
run_env['TEXINPUTS'] = '%s:%s' % \
(global_options.input_dir, run_env.get('TEXINPUTS', ""))
# 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
oldtexinputs = os.environ.get('TEXINPUTS')
os.environ['TEXINPUTS'] = run_env['TEXINPUTS']
returncode = os.system(cmd)
if oldtexinputs:
os.environ['TEXINPUTS'] = oldtexinputs
else:
del os.environ['TEXINPUTS']
parameter_string = open(output_filename, encoding="utf8").read()
if returncode != 0:
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:
warning(_("Unable to auto-detect default settings:\n%s")
% error_string)
os.unlink(tmpfile)
if os.path.exists(auxfile):
os.unlink(auxfile)
if os.path.exists(logfile):
parameter_string = open(logfile, encoding="utf8").read()
os.unlink(logfile)
columns = 0
m = re.search('columns=([0-9.]+)', parameter_string)
if m:
columns = int(m.group(1))
columnsep = 0
m = re.search('columnsep=([0-9.]+)pt', parameter_string)
if m:
columnsep = float(m.group(1))
m = re.search('textwidth=([0-9.]+)pt', parameter_string)
if m:
textwidth = float(m.group(1))
else:
warning(_("cannot detect textwidth from LaTeX"))
return textwidth
debug('Detected values:')
debug(' columns = %s' % columns)
debug(' columnsep = %s' % columnsep)
debug(' textwidth = %s' % textwidth)
if m and columns:
textwidth = (textwidth - columnsep) / columns
debug('Adjusted value:')
debug(' textwidth = %s' % textwidth)
return textwidth
def modify_preamble(chunk):
s = chunk.replacement_text()
if (re.search(r"\\begin *{document}", s)
and not re.search("{graphic[sx]", s)):
s = re.sub(r"\\begin{document}",
r"\\usepackage{graphics}" + '\n'
+ r"\\begin{document}",
s)
chunk.override_text = s
class BookLatexOutputFormat (book_base.BookOutputFormat):
def __init__(self):
book_base.BookOutputFormat.__init__(self)
self.format = "latex"
self.default_extension = ".tex"
self.snippet_res = Latex_snippet_res
self.output = Latex_output
self.handled_extensions = ['.latex', '.lytex', '.tex']
self.image_formats = "ps"
self.snippet_option_separator = r'\s*,\s*'
def process_options(self, global_options):
self.process_options_pdfnotdefault(global_options)
def get_line_width(self, source):
textwidth = get_latex_textwidth(source, self.global_options)
return '%.0f\\pt' % textwidth
def input_fullname(self, input_filename):
# Use kpsewhich if available, otherwise fall back to the default:
try:
trial = subprocess.run(['kpsewhich', input_filename],
check=True, stdout=subprocess.PIPE).stdout
if trial:
return trial
except subprocess.CalledProcessError:
pass
return book_base.BookOutputFormat.input_fullname(self, input_filename)
def process_chunks(self, chunks):
for c in chunks:
if (c.is_plain() and
re.search(r"\\begin *{document}", c.replacement_text())):
modify_preamble(c)
break
return chunks
def snippet_output(self, basename, snippet):
s = ''
rep = snippet.get_replacements()
rep['base'] = basename.replace('\\', '/')
rep['filename'] = os.path.basename(snippet.filename).replace('\\', '/')
rep['ext'] = snippet.ext
if book_snippets.PRINTFILENAME in snippet.option_dict:
s += self.output[book_snippets.PRINTFILENAME] % rep
if book_snippets.VERBATIM in snippet.option_dict:
rep['verb'] = snippet.verb_ly()
s += self.output[book_snippets.VERBATIM] % rep
s += self.output[book_snippets.OUTPUT] % rep
# todo: maintain breaks
if 0:
breaks = snippet.ly().count("\n")
s += "".ljust(breaks, "\n").replace("\n", "%\n")
if book_snippets.QUOTE in snippet.option_dict:
s = self.output[book_snippets.QUOTE] % {'str': s}
return s
book_base.register_format(BookLatexOutputFormat())