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>
		
			
				
	
	
		
			65 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			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)
 |