Brian S. Stephan 713632fe7a
unpin tox in requirements
for some reason bandit wasn't earlier catching the SubElement usage but
now it is, but it's harmless anyway so we'll just suppress it.

Signed-off-by: Brian S. Stephan <bss@incorporeal.org>
2023-12-30 11:12:59 -06:00

65 lines
2.3 KiB
Python

"""Create generic figures with captions.
SPDX-FileCopyrightText: © 2022 Brian S. Stephan <bss@incorporeal.org>
SPDX-License-Identifier: AGPL-3.0-or-later
"""
import re
from xml.etree.ElementTree import SubElement # nosec B405 - not parsing untrusted XML here
import markdown
class FigureExtension(markdown.Extension):
"""Wrap the markdown prepcoressor."""
def extendMarkdown(self, md):
"""Add FigureBlockProcessor to the Markdown instance."""
md.parser.blockprocessors.register(FigureBlockProcessor(md.parser), 'figure', 100)
class FigureBlockProcessor(markdown.blockprocessors.BlockProcessor):
"""Process figures."""
# |> thing to put in the figure
# |: optional caption for the figure
# optional whatever else, like maybe an attr_list
figure_regex = re.compile(r'^[ ]{0,3}\|>[ ]{0,3}(?P<content>[^\n]*)')
caption_regex = re.compile(r'^[ ]{0,3}\|:[ ]{0,3}(?P<caption>[^\n]*)')
def test(self, parent, block):
"""Determine if we should process this block."""
lines = block.split('\n')
return bool(self.figure_regex.search(lines[0]))
def run(self, parent, blocks):
"""Replace the top block with HTML."""
block = blocks.pop(0)
lines = block.split('\n')
# consume line and create a figure
figure_match = self.figure_regex.search(lines[0])
lines.pop(0)
content = figure_match.group('content')
figure = SubElement(parent, 'figure')
figure.text = content
if lines:
if caption_match := self.caption_regex.search(lines[0]):
# consume line and add the caption as a child of the figure
lines.pop(0)
caption = caption_match.group('caption')
figcaption = SubElement(figure, 'figcaption')
figcaption.text = caption
if lines:
# other lines are mysteries, might be attr_list, so re-append
# make sure there's a child to hang the rest (which is maybe an attr_list?) off of
# this is probably a bad hack
if not len(list(figure)):
SubElement(figure, 'span')
rest = '\n'.join(lines)
figure[-1].tail = f'\n{rest}'
def makeExtension(*args, **kwargs):
"""Provide the extension to the markdown extension loader."""
return FigureExtension(*args, **kwargs)