Source code for ipypublish.filters_pandoc.format_raw_spans

""" a panflute filter to format Span element
representations of RawInline elements

The :py:mod:`ipypublish.filters_pandoc.prepare_raw` filter should be run
first to access the functionality below:

"""
import itertools

# from textwrap import fill as textwrap

from panflute import Element, Doc, Span  # noqa: F401
import panflute as pf

from ipypublish.filters_pandoc.definitions import (
    CONVERTED_OTHER_CLASS,
    CONVERTED_DIRECTIVE_CLASS,
    IPUB_META_ROUTE,
)


[docs]def process_raw_spans(container, doc): # type: (Span, Doc) -> Element if not isinstance(container, (pf.Span, pf.Div)): return None hide_raw = doc.get_metadata(IPUB_META_ROUTE + ".hide_raw", False) if CONVERTED_OTHER_CLASS in container.classes and isinstance(container, pf.Span): if doc.format == "rst" and container.attributes["format"] == "latex": if container.attributes["tag"] in ["todo"]: return pf.Str( "\n\n.. {}:: {}\n\n".format( container.attributes["tag"], container.attributes["content"] ) ) if container.attributes["tag"] == "ensuremath": return pf.RawInline( ":math:`{}`".format(container.attributes["content"]), format="rst" ) return pf.RawInline( container.attributes.get("original"), format=container.attributes["format"] ) if CONVERTED_DIRECTIVE_CLASS in container.classes and isinstance(container, pf.Div): # convert the directive head, which will be e.g. # Para(Str(..) Space Str(toctree::) SoftBreak Str(:maxdepth:) Space Str(2) SoftBreak Str(:numbered:)) # noqa # we need to spilt on the soft breaks, # place them on a new line and re-indent them if doc.format in ("rst"): # split into lines by soft breaks header_lines = [ list(y) for x, y in itertools.groupby( container.content[0].content, lambda z: isinstance(z, pf.SoftBreak) ) if not x ] # wrap each line in a Para and convert block with pandoc head_doc = pf.Doc(*[pf.Para(*l) for l in header_lines]) head_doc.api_version = doc.api_version head_str = pf.convert_text( head_doc, input_format="panflute", output_format=doc.format ) # remove blank lines and indent head_str = head_str.replace("\n\n", "\n ") + "\n\n" head_block = pf.RawBlock(head_str, format=doc.format) if len(container.content) == 1: return head_block # split into lines by soft breaks, we use indicators to tell # us where to indent in the converted text body_blocks = [] for block in container.content[1:]: new_elements = [pf.RawInline("%^*", format=doc.format)] for el in block.content: if isinstance(el, pf.SoftBreak): new_elements.append(pf.RawInline("?&@", format=doc.format)) else: new_elements.append(el) block.content = new_elements body_blocks.append(block) # convert body content with pandoc body_doc = pf.Doc(*body_blocks) body_doc.api_version = doc.api_version body_str = pf.convert_text( body_doc, input_format="panflute", output_format=doc.format ) # raise ValueError(body_blocks) body_str = body_str.replace("%^*", " ").replace("?&@", "\n ") # ensure all lines are indented correctly # (doesn't occur by default?) body_str = ( "\n".join( [ " " + l.lstrip() if l.strip() else l for l in body_str.splitlines() ] ) + "\n\n" ) body_block = pf.RawBlock(body_str, format=doc.format) return [head_block, body_block] elif ( doc.format in ("html", "html5") and container.attributes["format"] == "rst" ): if hide_raw: return [] head_para = pf.Para( *[ pf.RawInline("<br>" + "&nbsp" * 4) if isinstance(c, pf.SoftBreak) else c for c in container.content[0].content ] ) head_str = pf.convert_text( head_para, input_format="panflute", output_format=doc.format ) if len(container.content) > 1: body_doc = pf.Doc(*container.content[1:]) body_doc.api_version = doc.api_version body_str = pf.convert_text( body_doc, input_format="panflute", output_format=doc.format ) body_str = ( '<p></p><div style="margin-left: 20px">' "{0}</div>" ).format(body_str) else: body_str = "" return pf.RawBlock( '<div {0} style="background-color:rgba(10, 225, 10, .2)">' "{1}{2}" "</div>".format( container.attributes.get("directive", ""), head_str, body_str ), format="html", ) elif doc.format in ("tex", "latex") and container.attributes["format"] == "rst": if hide_raw: return [] directive = container.attributes.get("directive", "") inline = container.attributes.get("inline", "") # TODO handle directive with options and/or inline body # e.g. .. figure:: path/to/figure # :centre: box_open = ( "\\begin{{mdframed}}" "[frametitle={{{0}}},frametitlerule=true]".format(directive) ) if inline: box_open += "\n\\mdfsubtitle{{{0}}}".format(inline) box_close = "\\end{mdframed}" if len(container.content) == 1: return pf.RawBlock(box_open + box_close, format="tex") else: return ( [pf.RawBlock(box_open, format="tex")] + list(container.content[1:]) + [pf.RawBlock(box_close, format="tex")] ) return pf.RawBlock( pf.stringify(pf.Doc(*container.content)), format=container.attributes["format"], ) if CONVERTED_OTHER_CLASS in container.classes and isinstance(container, pf.Div): return pf.RawBlock( pf.stringify(pf.Doc(*container.content)), format=container.attributes["format"], )
# now unused # def split_soft_breaks(container, # indent=4, fmt="rst", indent_first=False, # pre_content="", post_content="", # pre_chunk="", post_chunk="", # linebreak="\n", raw_indent=None): # """rst conversion doesn't recognise soft breaks as new lines, # so add them manually and return a list containing the new elements # """ # content = [] # if pre_content: # content.append(pf.RawBlock(pre_content, fmt)) # chunks = [list(y) for x, y in itertools.groupby( # container.content, # lambda z: isinstance(z, pf.SoftBreak)) if not x] # for i, chunk in enumerate(chunks): # if i > 0 or indent_first: # if raw_indent is not None: # chunk = [pf.RawInline(raw_indent, fmt)] * indent + chunk # else: # chunk = [pf.Space()] * indent + chunk # if pre_chunk: # content.append(pf.RawBlock(pre_chunk, fmt)) # content.append(pf.Plain(*chunk)) # content.append(pf.RawBlock(linebreak, fmt)) # if post_chunk: # content.append(pf.RawBlock(post_chunk, fmt)) # # if isinstance(container, pf.Para): # # content.append(pf.RawBlock(linebreak, fmt)) # if post_content: # content.append(pf.RawBlock(post_content, fmt)) # return content
[docs]def process_code_latex(code, doc): # type: (pf.CodeBlock, Doc) -> Element if doc.format not in ("tex", "latex"): return None if not isinstance(code, pf.CodeBlock): return None # TODO line wrapping return [ pf.RawBlock("\\begin{mdframed}", format=doc.format), code, pf.RawBlock("\\end{mdframed}", format=doc.format), ]
[docs]def prepare(doc): # type: (Doc) -> None pass
[docs]def finalize(doc): # type: (Doc) -> None pass
[docs]def main(doc=None): # type: (Doc) -> None """ """ return pf.run_filters( [process_raw_spans, process_code_latex], prepare, finalize, doc=doc )
if __name__ == "__main__": main()