Fix #6738: latex: literal_block does not support raw unicode characters

This commit is contained in:
Takeshi KOMIYA 2019-11-16 16:29:14 +09:00
parent ef09ea23fe
commit 660e746cf8
4 changed files with 53 additions and 7 deletions

View File

@ -28,7 +28,7 @@ from sphinx.ext import doctest
from sphinx.locale import __ from sphinx.locale import __
from sphinx.pygments_styles import SphinxStyle, NoneStyle from sphinx.pygments_styles import SphinxStyle, NoneStyle
from sphinx.util import logging from sphinx.util import logging
from sphinx.util.texescape import tex_hl_escape_map_new from sphinx.util.texescape import get_hlescape_func, tex_hl_escape_map_new
if False: if False:
# For type annotation # For type annotation
@ -68,9 +68,11 @@ class PygmentsBridge:
html_formatter = HtmlFormatter html_formatter = HtmlFormatter
latex_formatter = LatexFormatter latex_formatter = LatexFormatter
def __init__(self, dest='html', stylename='sphinx', trim_doctest_flags=None): def __init__(self, dest='html', stylename='sphinx', trim_doctest_flags=None,
# type: (str, str, bool) -> None latex_engine=None):
# type: (str, str, bool, str) -> None
self.dest = dest self.dest = dest
self.latex_engine = latex_engine
style = self.get_style(stylename) style = self.get_style(stylename)
self.formatter_args = {'style': style} # type: Dict[str, Any] self.formatter_args = {'style': style} # type: Dict[str, Any]
@ -192,7 +194,8 @@ class PygmentsBridge:
if self.dest == 'html': if self.dest == 'html':
return hlsource return hlsource
else: else:
return hlsource.translate(tex_hl_escape_map_new) escape = get_hlescape_func(self.latex_engine)
return escape(hlsource)
def get_stylesheet(self): def get_stylesheet(self):
# type: () -> str # type: () -> str

View File

@ -32,7 +32,6 @@ tex_replacements = [
('', r'\P{}'), ('', r'\P{}'),
('§', r'\S{}'), ('§', r'\S{}'),
('', r'\texteuro{}'), ('', r'\texteuro{}'),
('', r'\(\infty\)'),
('±', r'\(\pm\)'), ('±', r'\(\pm\)'),
('', r'\(\rightarrow\)'), ('', r'\(\rightarrow\)'),
('', r'\(\rightarrow\)'), ('', r'\(\rightarrow\)'),
@ -53,6 +52,8 @@ tex_replacements = [
# A map Unicode characters to LaTeX representation # A map Unicode characters to LaTeX representation
# (for LaTeX engines which don't support unicode) # (for LaTeX engines which don't support unicode)
unicode_tex_replacements = [ unicode_tex_replacements = [
# map special Unicode characters to TeX commands
('', r'\(\infty\)'),
# superscript # superscript
('', r'\(\sp{\text{0}}\)'), ('', r'\(\sp{\text{0}}\)'),
('¹', r'\(\sp{\text{1}}\)'), ('¹', r'\(\sp{\text{1}}\)'),
@ -80,7 +81,8 @@ unicode_tex_replacements = [
tex_escape_map = {} # type: Dict[int, str] tex_escape_map = {} # type: Dict[int, str]
tex_escape_map_without_unicode = {} # type: Dict[int, str] tex_escape_map_without_unicode = {} # type: Dict[int, str]
tex_replace_map = {} tex_replace_map = {}
tex_hl_escape_map_new = {} tex_hl_escape_map_new = {} # type: Dict[int, str]
tex_hl_escape_map_new_without_unicode = {} # type: Dict[int, str]
def get_escape_func(latex_engine: str) -> Callable[[str], str]: def get_escape_func(latex_engine: str) -> Callable[[str], str]:
@ -101,6 +103,24 @@ def escape_for_unicode_latex_engine(s: str) -> str:
return s.translate(tex_escape_map_without_unicode) return s.translate(tex_escape_map_without_unicode)
def get_hlescape_func(latex_engine: str) -> Callable[[str], str]:
"""Get hlescape() function for given latex_engine."""
if latex_engine in ('lualatex', 'xelatex'):
return hlescape_for_unicode_latex_engine
else:
return hlescape
def hlescape(s: str) -> str:
"""Escape text for LaTeX highlighter."""
return s.translate(tex_hl_escape_map_new)
def hlescape_for_unicode_latex_engine(s: str) -> str:
"""Escape text for unicode supporting LaTeX engine."""
return s.translate(tex_hl_escape_map_new_without_unicode)
def escape_abbr(text: str) -> str: def escape_abbr(text: str) -> str:
"""Adjust spacing after abbreviations. Works with @ letter or other.""" """Adjust spacing after abbreviations. Works with @ letter or other."""
return re.sub(r'\.(?=\s|$)', r'.\@{}', text) return re.sub(r'\.(?=\s|$)', r'.\@{}', text)
@ -120,3 +140,7 @@ def init() -> None:
if a in '[]{}\\': if a in '[]{}\\':
continue continue
tex_hl_escape_map_new[ord(a)] = b tex_hl_escape_map_new[ord(a)] = b
tex_hl_escape_map_new_without_unicode[ord(a)] = b
for a, b in unicode_tex_replacements:
tex_hl_escape_map_new[ord(a)] = b

View File

@ -653,7 +653,8 @@ class LaTeXTranslator(SphinxTranslator):
self.elements['classoptions'] += ',' + \ self.elements['classoptions'] += ',' + \
self.elements['extraclassoptions'] self.elements['extraclassoptions']
self.highlighter = highlighting.PygmentsBridge('latex', self.config.pygments_style) self.highlighter = highlighting.PygmentsBridge('latex', self.config.pygments_style,
latex_engine=self.config.latex_engine)
self.context = [] # type: List[Any] self.context = [] # type: List[Any]
self.descstack = [] # type: List[str] self.descstack = [] # type: List[str]
self.table = None # type: Table self.table = None # type: Table

View File

@ -312,6 +312,24 @@ def test_inline(get_verifier, type, rst, html_expected, latex_expected):
verifier(rst, html_expected, latex_expected) verifier(rst, html_expected, latex_expected)
@pytest.mark.sphinx(confoverrides={'latex_engine': 'xelatex'})
@pytest.mark.parametrize('type,rst,html_expected,latex_expected', [
(
# in verbatim code fragments
'verify',
'::\n\n\\∞${}',
None,
('\\begin{sphinxVerbatim}[commandchars=\\\\\\{\\}]\n'
'\\PYGZbs{}\\PYGZdl{}\\PYGZob{}\\PYGZcb{}\n'
'\\end{sphinxVerbatim}'),
),
])
def test_inline_for_unicode_latex_engine(get_verifier, type, rst,
html_expected, latex_expected):
verifier = get_verifier(type)
verifier(rst, html_expected, latex_expected)
def test_samp_role(parse): def test_samp_role(parse):
# no braces # no braces
text = ':samp:`a{b}c`' text = ':samp:`a{b}c`'