mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
Enable automatic formatting for `sphinx/ext/imgmath.py
`
This commit is contained in:
parent
f592962b8d
commit
da5a67d5a4
@ -415,7 +415,6 @@ exclude = [
|
|||||||
"sphinx/domains/python/_object.py",
|
"sphinx/domains/python/_object.py",
|
||||||
"sphinx/domains/rst.py",
|
"sphinx/domains/rst.py",
|
||||||
"sphinx/domains/std/__init__.py",
|
"sphinx/domains/std/__init__.py",
|
||||||
"sphinx/ext/imgmath.py",
|
|
||||||
"sphinx/ext/inheritance_diagram.py",
|
"sphinx/ext/inheritance_diagram.py",
|
||||||
"sphinx/ext/linkcode.py",
|
"sphinx/ext/linkcode.py",
|
||||||
"sphinx/ext/mathjax.py",
|
"sphinx/ext/mathjax.py",
|
||||||
|
@ -29,7 +29,6 @@ from sphinx.util.png import read_png_depth, write_png_depth
|
|||||||
from sphinx.util.template import LaTeXRenderer
|
from sphinx.util.template import LaTeXRenderer
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
|
||||||
from docutils.nodes import Element
|
from docutils.nodes import Element
|
||||||
|
|
||||||
from sphinx.application import Sphinx
|
from sphinx.application import Sphinx
|
||||||
@ -47,7 +46,7 @@ class MathExtError(SphinxError):
|
|||||||
category = 'Math extension error'
|
category = 'Math extension error'
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, msg: str, stderr: str | None = None, stdout: str | None = None,
|
self, msg: str, stderr: str | None = None, stdout: str | None = None
|
||||||
) -> None:
|
) -> None:
|
||||||
if stderr:
|
if stderr:
|
||||||
msg += '\n[stderr]\n' + stderr
|
msg += '\n[stderr]\n' + stderr
|
||||||
@ -68,9 +67,8 @@ depthsvgcomment_re = re.compile(r'<!-- DEPTH=(-?\d+) -->')
|
|||||||
|
|
||||||
|
|
||||||
def read_svg_depth(filename: str) -> int | None:
|
def read_svg_depth(filename: str) -> int | None:
|
||||||
"""Read the depth from comment at last line of SVG file
|
"""Read the depth from comment at last line of SVG file"""
|
||||||
"""
|
with open(filename, encoding='utf-8') as f:
|
||||||
with open(filename, encoding="utf-8") as f:
|
|
||||||
for line in f: # NoQA: B007
|
for line in f: # NoQA: B007
|
||||||
pass
|
pass
|
||||||
# Only last line is checked
|
# Only last line is checked
|
||||||
@ -81,16 +79,17 @@ def read_svg_depth(filename: str) -> int | None:
|
|||||||
|
|
||||||
|
|
||||||
def write_svg_depth(filename: str, depth: int) -> None:
|
def write_svg_depth(filename: str, depth: int) -> None:
|
||||||
"""Write the depth to SVG file as a comment at end of file
|
"""Write the depth to SVG file as a comment at end of file"""
|
||||||
"""
|
with open(filename, 'a', encoding='utf-8') as f:
|
||||||
with open(filename, 'a', encoding="utf-8") as f:
|
|
||||||
f.write('\n<!-- DEPTH=%s -->' % depth)
|
f.write('\n<!-- DEPTH=%s -->' % depth)
|
||||||
|
|
||||||
|
|
||||||
def generate_latex_macro(image_format: str,
|
def generate_latex_macro(
|
||||||
math: str,
|
image_format: str,
|
||||||
config: Config,
|
math: str,
|
||||||
confdir: str | os.PathLike[str] = '') -> str:
|
config: Config,
|
||||||
|
confdir: str | os.PathLike[str] = '',
|
||||||
|
) -> str:
|
||||||
"""Generate LaTeX macro."""
|
"""Generate LaTeX macro."""
|
||||||
variables = {
|
variables = {
|
||||||
'fontsize': config.imgmath_font_size,
|
'fontsize': config.imgmath_font_size,
|
||||||
@ -109,7 +108,9 @@ def generate_latex_macro(image_format: str,
|
|||||||
|
|
||||||
for template_dir in config.templates_path:
|
for template_dir in config.templates_path:
|
||||||
for template_suffix in ('.jinja', '_t'):
|
for template_suffix in ('.jinja', '_t'):
|
||||||
template = os.path.join(confdir, template_dir, template_name + template_suffix)
|
template = os.path.join(
|
||||||
|
confdir, template_dir, template_name + template_suffix
|
||||||
|
)
|
||||||
if os.path.exists(template):
|
if os.path.exists(template):
|
||||||
return LaTeXRenderer().render(template, variables)
|
return LaTeXRenderer().render(template, variables)
|
||||||
|
|
||||||
@ -149,16 +150,21 @@ def compile_math(latex: str, builder: Builder) -> str:
|
|||||||
command.append('math.tex')
|
command.append('math.tex')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(command, capture_output=True, cwd=tempdir, check=True,
|
subprocess.run(
|
||||||
encoding='ascii')
|
command, capture_output=True, cwd=tempdir, check=True, encoding='ascii'
|
||||||
|
)
|
||||||
if imgmath_latex_name in {'xelatex', 'tectonic'}:
|
if imgmath_latex_name in {'xelatex', 'tectonic'}:
|
||||||
return os.path.join(tempdir, 'math.xdv')
|
return os.path.join(tempdir, 'math.xdv')
|
||||||
else:
|
else:
|
||||||
return os.path.join(tempdir, 'math.dvi')
|
return os.path.join(tempdir, 'math.dvi')
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
logger.warning(__('LaTeX command %r cannot be run (needed for math '
|
logger.warning(
|
||||||
'display), check the imgmath_latex setting'),
|
__(
|
||||||
builder.config.imgmath_latex)
|
'LaTeX command %r cannot be run (needed for math '
|
||||||
|
'display), check the imgmath_latex setting'
|
||||||
|
),
|
||||||
|
builder.config.imgmath_latex,
|
||||||
|
)
|
||||||
raise InvokeError from exc
|
raise InvokeError from exc
|
||||||
except CalledProcessError as exc:
|
except CalledProcessError as exc:
|
||||||
msg = 'latex exited with error'
|
msg = 'latex exited with error'
|
||||||
@ -171,12 +177,19 @@ def convert_dvi_to_image(command: list[str], name: str) -> tuple[str, str]:
|
|||||||
ret = subprocess.run(command, capture_output=True, check=True, encoding='ascii')
|
ret = subprocess.run(command, capture_output=True, check=True, encoding='ascii')
|
||||||
return ret.stdout, ret.stderr
|
return ret.stdout, ret.stderr
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
logger.warning(__('%s command %r cannot be run (needed for math '
|
logger.warning(
|
||||||
'display), check the imgmath_%s setting'),
|
__(
|
||||||
name, command[0], name)
|
'%s command %r cannot be run (needed for math '
|
||||||
|
'display), check the imgmath_%s setting'
|
||||||
|
),
|
||||||
|
name,
|
||||||
|
command[0],
|
||||||
|
name,
|
||||||
|
)
|
||||||
raise InvokeError from exc
|
raise InvokeError from exc
|
||||||
except CalledProcessError as exc:
|
except CalledProcessError as exc:
|
||||||
raise MathExtError('%s exited with error' % name, exc.stderr, exc.stdout) from exc
|
msg = f'{name} exited with error'
|
||||||
|
raise MathExtError(msg, exc.stderr, exc.stdout) from exc
|
||||||
|
|
||||||
|
|
||||||
def convert_dvi_to_png(dvipath: str, builder: Builder, out_path: str) -> int | None:
|
def convert_dvi_to_png(dvipath: str, builder: Builder, out_path: str) -> int | None:
|
||||||
@ -245,13 +258,16 @@ def render_math(
|
|||||||
unsupported_format_msg = 'imgmath_image_format must be either "png" or "svg"'
|
unsupported_format_msg = 'imgmath_image_format must be either "png" or "svg"'
|
||||||
raise MathExtError(unsupported_format_msg)
|
raise MathExtError(unsupported_format_msg)
|
||||||
|
|
||||||
latex = generate_latex_macro(image_format,
|
latex = generate_latex_macro(
|
||||||
math,
|
image_format, math, self.builder.config, self.builder.confdir
|
||||||
self.builder.config,
|
)
|
||||||
self.builder.confdir)
|
|
||||||
|
|
||||||
filename = f"{sha1(latex.encode(), usedforsecurity=False).hexdigest()}.{image_format}"
|
filename = (
|
||||||
generated_path = os.path.join(self.builder.outdir, self.builder.imagedir, 'math', filename)
|
f'{sha1(latex.encode(), usedforsecurity=False).hexdigest()}.{image_format}'
|
||||||
|
)
|
||||||
|
generated_path = os.path.join(
|
||||||
|
self.builder.outdir, self.builder.imagedir, 'math', filename
|
||||||
|
)
|
||||||
ensuredir(os.path.dirname(generated_path))
|
ensuredir(os.path.dirname(generated_path))
|
||||||
if os.path.isfile(generated_path):
|
if os.path.isfile(generated_path):
|
||||||
if image_format == 'png':
|
if image_format == 'png':
|
||||||
@ -261,8 +277,9 @@ def render_math(
|
|||||||
return generated_path, depth
|
return generated_path, depth
|
||||||
|
|
||||||
# if latex or dvipng (dvisvgm) has failed once, don't bother to try again
|
# if latex or dvipng (dvisvgm) has failed once, don't bother to try again
|
||||||
if hasattr(self.builder, '_imgmath_warned_latex') or \
|
latex_failed = hasattr(self.builder, '_imgmath_warned_latex')
|
||||||
hasattr(self.builder, '_imgmath_warned_image_translator'):
|
trans_failed = hasattr(self.builder, '_imgmath_warned_image_translator')
|
||||||
|
if latex_failed or trans_failed:
|
||||||
return None, None
|
return None, None
|
||||||
|
|
||||||
# .tex -> .dvi
|
# .tex -> .dvi
|
||||||
@ -286,7 +303,7 @@ def render_math(
|
|||||||
|
|
||||||
|
|
||||||
def render_maths_to_base64(image_format: str, generated_path: str) -> str:
|
def render_maths_to_base64(image_format: str, generated_path: str) -> str:
|
||||||
with open(generated_path, "rb") as f:
|
with open(generated_path, 'rb') as f:
|
||||||
encoded = base64.b64encode(f.read()).decode(encoding='utf-8')
|
encoded = base64.b64encode(f.read()).decode(encoding='utf-8')
|
||||||
if image_format == 'png':
|
if image_format == 'png':
|
||||||
return f'data:image/png;base64,{encoded}'
|
return f'data:image/png;base64,{encoded}'
|
||||||
@ -308,12 +325,14 @@ def clean_up_files(app: Sphinx, exc: Exception) -> None:
|
|||||||
# in embed mode, the images are still generated in the math output dir
|
# in embed mode, the images are still generated in the math output dir
|
||||||
# to be shared across workers, but are not useful to the final document
|
# to be shared across workers, but are not useful to the final document
|
||||||
with contextlib.suppress(Exception):
|
with contextlib.suppress(Exception):
|
||||||
shutil.rmtree(os.path.join(app.builder.outdir, app.builder.imagedir, 'math'))
|
shutil.rmtree(
|
||||||
|
os.path.join(app.builder.outdir, app.builder.imagedir, 'math')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_tooltip(self: HTML5Translator, node: Element) -> str:
|
def get_tooltip(self: HTML5Translator, node: Element) -> str:
|
||||||
if self.builder.config.imgmath_add_tooltips:
|
if self.builder.config.imgmath_add_tooltips:
|
||||||
return ' alt="%s"' % self.encode(node.astext()).strip()
|
return f' alt="{self.encode(node.astext()).strip()}"'
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
@ -322,16 +341,18 @@ def html_visit_math(self: HTML5Translator, node: nodes.math) -> None:
|
|||||||
rendered_path, depth = render_math(self, '$' + node.astext() + '$')
|
rendered_path, depth = render_math(self, '$' + node.astext() + '$')
|
||||||
except MathExtError as exc:
|
except MathExtError as exc:
|
||||||
msg = str(exc)
|
msg = str(exc)
|
||||||
sm = nodes.system_message(msg, type='WARNING', level=2,
|
sm = nodes.system_message(
|
||||||
backrefs=[], source=node.astext())
|
msg, type='WARNING', level=2, backrefs=[], source=node.astext()
|
||||||
|
)
|
||||||
sm.walkabout(self)
|
sm.walkabout(self)
|
||||||
logger.warning(__('display latex %r: %s'), node.astext(), msg)
|
logger.warning(__('display latex %r: %s'), node.astext(), msg)
|
||||||
raise nodes.SkipNode from exc
|
raise nodes.SkipNode from exc
|
||||||
|
|
||||||
if rendered_path is None:
|
if rendered_path is None:
|
||||||
# something failed -- use text-only as a bad substitute
|
# something failed -- use text-only as a bad substitute
|
||||||
self.body.append('<span class="math">%s</span>' %
|
self.body.append(
|
||||||
self.encode(node.astext()).strip())
|
f'<span class="math">{self.encode(node.astext()).strip()}</span>'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if self.builder.config.imgmath_embed:
|
if self.builder.config.imgmath_embed:
|
||||||
image_format = self.builder.config.imgmath_image_format.lower()
|
image_format = self.builder.config.imgmath_image_format.lower()
|
||||||
@ -340,10 +361,10 @@ def html_visit_math(self: HTML5Translator, node: nodes.math) -> None:
|
|||||||
bname = os.path.basename(rendered_path)
|
bname = os.path.basename(rendered_path)
|
||||||
relative_path = os.path.join(self.builder.imgpath, 'math', bname)
|
relative_path = os.path.join(self.builder.imgpath, 'math', bname)
|
||||||
img_src = relative_path.replace(os.path.sep, '/')
|
img_src = relative_path.replace(os.path.sep, '/')
|
||||||
c = f'<img class="math" src="{img_src}"' + get_tooltip(self, node)
|
align = f' style="vertical-align: {-depth:d}px"' if depth is not None else ''
|
||||||
if depth is not None:
|
self.body.append(
|
||||||
c += f' style="vertical-align: {-depth:d}px"'
|
f'<img class="math" src="{img_src}"{get_tooltip(self, node)}{align}/>'
|
||||||
self.body.append(c + '/>')
|
)
|
||||||
raise nodes.SkipNode
|
raise nodes.SkipNode
|
||||||
|
|
||||||
|
|
||||||
@ -356,8 +377,9 @@ def html_visit_displaymath(self: HTML5Translator, node: nodes.math_block) -> Non
|
|||||||
rendered_path, depth = render_math(self, latex)
|
rendered_path, depth = render_math(self, latex)
|
||||||
except MathExtError as exc:
|
except MathExtError as exc:
|
||||||
msg = str(exc)
|
msg = str(exc)
|
||||||
sm = nodes.system_message(msg, type='WARNING', level=2,
|
sm = nodes.system_message(
|
||||||
backrefs=[], source=node.astext())
|
msg, type='WARNING', level=2, backrefs=[], source=node.astext()
|
||||||
|
)
|
||||||
sm.walkabout(self)
|
sm.walkabout(self)
|
||||||
logger.warning(__('inline latex %r: %s'), node.astext(), msg)
|
logger.warning(__('inline latex %r: %s'), node.astext(), msg)
|
||||||
raise nodes.SkipNode from exc
|
raise nodes.SkipNode from exc
|
||||||
@ -371,8 +393,9 @@ def html_visit_displaymath(self: HTML5Translator, node: nodes.math_block) -> Non
|
|||||||
|
|
||||||
if rendered_path is None:
|
if rendered_path is None:
|
||||||
# something failed -- use text-only as a bad substitute
|
# something failed -- use text-only as a bad substitute
|
||||||
self.body.append('<span class="math">%s</span></p>\n</div>' %
|
self.body.append(
|
||||||
self.encode(node.astext()).strip())
|
f'<span class="math">{self.encode(node.astext()).strip()}</span></p>\n</div>'
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
if self.builder.config.imgmath_embed:
|
if self.builder.config.imgmath_embed:
|
||||||
image_format = self.builder.config.imgmath_image_format.lower()
|
image_format = self.builder.config.imgmath_image_format.lower()
|
||||||
@ -381,24 +404,27 @@ def html_visit_displaymath(self: HTML5Translator, node: nodes.math_block) -> Non
|
|||||||
bname = os.path.basename(rendered_path)
|
bname = os.path.basename(rendered_path)
|
||||||
relative_path = os.path.join(self.builder.imgpath, 'math', bname)
|
relative_path = os.path.join(self.builder.imgpath, 'math', bname)
|
||||||
img_src = relative_path.replace(os.path.sep, '/')
|
img_src = relative_path.replace(os.path.sep, '/')
|
||||||
self.body.append(f'<img src="{img_src}"' + get_tooltip(self, node) +
|
self.body.append(f'<img src="{img_src}"{get_tooltip(self, node)}/></p>\n</div>')
|
||||||
'/></p>\n</div>')
|
|
||||||
raise nodes.SkipNode
|
raise nodes.SkipNode
|
||||||
|
|
||||||
|
|
||||||
def setup(app: Sphinx) -> ExtensionMetadata:
|
def setup(app: Sphinx) -> ExtensionMetadata:
|
||||||
app.add_html_math_renderer('imgmath',
|
app.add_html_math_renderer(
|
||||||
(html_visit_math, None),
|
'imgmath',
|
||||||
(html_visit_displaymath, None))
|
inline_renderers=(html_visit_math, None),
|
||||||
|
block_renderers=(html_visit_displaymath, None),
|
||||||
|
)
|
||||||
|
|
||||||
app.add_config_value('imgmath_image_format', 'png', 'html')
|
app.add_config_value('imgmath_image_format', 'png', 'html')
|
||||||
app.add_config_value('imgmath_dvipng', 'dvipng', 'html')
|
app.add_config_value('imgmath_dvipng', 'dvipng', 'html')
|
||||||
app.add_config_value('imgmath_dvisvgm', 'dvisvgm', 'html')
|
app.add_config_value('imgmath_dvisvgm', 'dvisvgm', 'html')
|
||||||
app.add_config_value('imgmath_latex', 'latex', 'html')
|
app.add_config_value('imgmath_latex', 'latex', 'html')
|
||||||
app.add_config_value('imgmath_use_preview', False, 'html')
|
app.add_config_value('imgmath_use_preview', False, 'html')
|
||||||
app.add_config_value('imgmath_dvipng_args',
|
app.add_config_value(
|
||||||
['-gamma', '1.5', '-D', '110', '-bg', 'Transparent'],
|
'imgmath_dvipng_args',
|
||||||
'html')
|
['-gamma', '1.5', '-D', '110', '-bg', 'Transparent'],
|
||||||
|
'html',
|
||||||
|
)
|
||||||
app.add_config_value('imgmath_dvisvgm_args', ['--no-fonts'], 'html')
|
app.add_config_value('imgmath_dvisvgm_args', ['--no-fonts'], 'html')
|
||||||
app.add_config_value('imgmath_latex_args', [], 'html')
|
app.add_config_value('imgmath_latex_args', [], 'html')
|
||||||
app.add_config_value('imgmath_latex_preamble', '', 'html')
|
app.add_config_value('imgmath_latex_preamble', '', 'html')
|
||||||
|
Loading…
Reference in New Issue
Block a user