Spaces:
Sleeping
Sleeping
# 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()) | |