def doc_filter(elem, doc): if type(elem) == Image: # Link to the raw user link instead of relative # That way the wiki and the site will have valid links automagically new_url = replace_suffix(elem.url, eps_ext, '.png') if not os.path.isfile(new_url): raise ValueError('{} Not found'.format(new_url)) elem.url = base_raw_url + new_url return elem if isinstance(elem, Math): # Raw inline mathlinks so jekyll renders them content = elem.text escaped = "$$ {} $$".format(content) return RawInline(escaped) if isinstance(elem, Link): # Transform all Links into a tags # Reason being is github and jekyll are weird # About leaving html as is and markdown as parsing # So we change everything to avoid ambiguity # There is a script injection possibility here so be careful url = elem.url title = str(elem.title) if title == "": title = elem.url link = '<a href="{}">{}</a>'.format(url, title) return RawInline(link)
def change_base_url(elem, doc): if type(elem) == Image: # Get the number of chars for the alt tag alt_length = len(elem._content) # No alt means no compile # Accessibility by default if alt_length == 0: raise NoAltTagException(elem.url) # Otherwise link to the raw user link instead of relative # That way the wiki and the site will have valid links automagically elem.url = base_raw_url + elem.url return elem if isinstance(elem, Math): # Raw inline mathlinks so jekyll renders them content = elem.text escaped = "$$ {} $$".format(content) return RawInline(escaped) if isinstance(elem, Link): # Transform all Links into a tags # Reason being is github and jekyll are weird # About leaving html as is and markdown as parsing # So we change everything to avoid ambiguity # There is a script injection possibility here so be careful url = elem.url title = str(elem.title) if title == "": title = elem.url link = '<a href="{}">{}</a>'.format(url, title) return RawInline(link)
def html_img_to_image(elem, doc): """ Apply the translations, see module doc """ # We are only interested in image elements if not isinstance(elem, Image): return if not len(elem.content): return RawInline(etree.tostring(E.img(src=elem.url, **elem.attributes), encoding='utf-8', xml_declaration=False, pretty_print=False).decode('utf-8'), format='html') html = E( "figure", E.img(CLASS("figure"), src=elem.url, **elem.attributes), E("figcaption", CLASS("caption"), stringify(elem)), ) if hasattr(elem.content[0], 'identifier'): html.set('id', elem.content[0].identifier) return RawInline(etree.tostring(html, encoding='utf-8', xml_declaration=False, pretty_print=True).decode('utf-8'), format='html')
def _finalize(doc): # Add header-includes if necessary if "header-includes" not in doc.metadata: doc.metadata["header-includes"] = MetaList() # Convert header-includes to MetaList if necessary elif not isinstance(doc.metadata["header-includes"], MetaList): doc.metadata["header-includes"] = MetaList( doc.metadata["header-includes"]) doc.metadata["header-includes"].append( MetaInlines(RawInline("\\usepackage{graphicx,grffile}", "tex"))) doc.metadata["header-includes"].append( MetaInlines(RawInline("\\usepackage{marginnote}", "tex"))) doc.metadata["header-includes"].append( MetaInlines(RawInline("\\usepackage{etoolbox}", "tex"))) doc.metadata["header-includes"].append( MetaInlines( RawInline("\\usepackage{changepage}\n\\strictpagecheck", "tex"))) doc.metadata["header-includes"].append( MetaInlines( RawInline( r""" \makeatletter% \@ifpackagelater{marginnote}{2018/04/12}% {% \newcommand{\oddinnertip}{\reversemarginpar}% \newcommand{\eveninnertip}{\reversemarginpar}% \newcommand{\oddoutertip}{\normalmarginpar}% \newcommand{\evenoutertip}{\normalmarginpar}% \newcommand{\oddlefttip}{\reversemarginpar}% \newcommand{\evenlefttip}{\normalmarginpar}% \newcommand{\oddrighttip}{\normalmarginpar}% \newcommand{\evenrighttip}{\reversemarginpar}% }% {% \if@twoside% \newcommand{\oddinnertip}{\reversemarginpar}% \newcommand{\eveninnertip}{\reversemarginpar}% \newcommand{\oddoutertip}{\normalmarginpar}% \newcommand{\evenoutertip}{\normalmarginpar}% \newcommand{\oddlefttip}{\reversemarginpar}% \newcommand{\evenlefttip}{\normalmarginpar}% \newcommand{\oddrighttip}{\reversemarginpar}% \newcommand{\evenrighttip}{\normalmarginpar}% \else% \newcommand{\oddinnertip}{\reversemarginpar}% \newcommand{\eveninnertip}{\reversemarginpar}% \newcommand{\oddoutertip}{\normalmarginpar}% \newcommand{\evenoutertip}{\normalmarginpar}% \newcommand{\oddlefttip}{\reversemarginpar}% \newcommand{\evenlefttip}{\reversemarginpar}% \newcommand{\oddrighttip}{\normalmarginpar}% \newcommand{\evenrighttip}{\normalmarginpar}% \fi% }% \makeatother% """, "tex", )))
def append(child, _doc): nonlocal child_elems nonlocal after_paragraph if isinstance(child, Inline): if after_paragraph: child_elems += [SoftBreak(), RawInline(">")] * 2 + [ Space() ] child_elems.append(child) if isinstance(child, SoftBreak): child_elems += [RawInline(">"), Space()] after_paragraph = isinstance(child, Para)
def finalize(doc): """ Finalize the pandoc document Arguments --------- doc: The pandoc document """ # load footnote or footnotehyper package if doc.format == "latex": doc.metadata["tables"] = MetaBool(True) # Add header-includes if necessary if "header-includes" not in doc.metadata: doc.metadata["header-includes"] = MetaList() # Convert header-includes to MetaList if necessary elif not isinstance(doc.metadata["header-includes"], MetaList): doc.metadata["header-includes"] = MetaList( doc.metadata["header-includes"]) # Add usefull LaTexPackage doc.metadata["header-includes"].append( MetaInlines(RawInline("\\usepackage{xcolor}", "tex"))) # Define x11 colors tex = [] for name, color in doc.x11colors.items(): tex.append("\\definecolor{" + name.lower() + "}{HTML}{" + color + "}") doc.metadata["header-includes"].append( MetaInlines(RawInline("\n".join(tex), "tex"))) doc.metadata["header-includes"].append( MetaInlines(RawInline("\\usepackage[most]{tcolorbox}", "tex"))) doc.metadata["header-includes"].append( MetaInlines( RawInline( r""" \usepackage{ifthen} \provideboolean{admonitiontwoside} \makeatletter% \if@twoside% \setboolean{admonitiontwoside}{true} \else% \setboolean{admonitiontwoside}{false} \fi% \makeatother% """, "tex", ))) # Define specific environments for environment in doc.defined + doc.added: doc.metadata["header-includes"].append( MetaInlines(RawInline(new_environment(doc, environment), "tex")))
def spaces(elem, doc): """ Add LaTeX spaces when needed. """ # Is it in the right format and is it a Space? if doc.format in ["latex", "beamer"] and isinstance(elem, Space): if isinstance(elem.prev, Str) and elem.prev.text in ["«", "“", "‹"]: return RawInline("\\thinspace{}", "tex") if isinstance(elem.next, Str): if elem.next.text == ":": return RawInline("~", "tex") if elem.next.text in [";", "?", "!", "»", "”", "›"]: return RawInline("\\thinspace{}", "tex") return None
def moreblocks(e, doc): # print(doc.format) if doc.format == "latex" or doc.format == "beamer": if isinstance(e, Header): # debug(e) if e.level == 4: tag = "" if e.identifier: label = '\\label{' + removeUmlaute(e.identifier) + '}' else: label = '' tag = '' for key in blockmap: if key in e.classes: tag = blockmap[key] if tag == '': if 'endblock' in e.classes: return Plain() # RawBlock('\\relax', format='latex')? else: return left = RawInline('\\begin{' + tag + '}[', format='latex') right = RawInline(']' + label + '\n', format='latex') n = Plain() n.content = [left] + list(e.content) + [right] i = 1 length = len(doc.content) # debug(doc.content[e.index+i]) while not (isinstance(doc.content[e.index + i], Header)): i += 1 if (e.index + i) >= length: break ret = RawBlock(endTag(tag), format='latex') if (e.index + i) >= length: # We need to add it at the end of the list! doc.content.append(ret) else: doc.content.insert(e.index + i, ret) return n else: return else: return else: return
def finalize(doc): # Add header-includes if necessary if "header-includes" not in doc.metadata: doc.metadata["header-includes"] = MetaList() # Convert header-includes to MetaList if necessary elif not isinstance(doc.metadata["header-includes"], MetaList): doc.metadata["header-includes"] = MetaList( doc.metadata["header-includes"] ) # noqa: E501 # Add usefull LaTex definition doc.metadata["header-includes"].append( MetaInlines( RawInline( "\n".join( [ "\\def\\pandocchangemargin#1#2{\\list{}{\\rightmargin#2\\leftmargin#1}\\item[]}", # noqa: E501 "\\let\\endpandocchangemargin=\\endlist", "", ] ), "tex", ) ) )
def header_img_to_latex(elem, doc): """ Convert Header -> LaTeX RawInline """ if isinstance(elem, Header): modified = False # Will contain the elements without the Images, replaced by LaTeX RawInlines new_content = [] stringified = stringify( elem).strip() # before we include the latex includegraphics for item in elem.content: if isinstance(item, Image): modified = True convert_px_to_in(96, item) new_content.append( RawInline('\\raisebox{-0.2\\height}{' + generate_includegraphics(item) + '}\\enspace', format='tex')) else: new_content.append(item) if modified: elem.content = new_content return generate_latex_header(elem, stringified)
def _add_latex(elem, latex): if bool(latex): # Is it a Span or a Code? if isinstance(elem, (Span, Code)): return [elem, RawInline(latex, "tex")] # It is a CodeBlock: create a minipage to ensure the # _tip to be on the same page as the codeblock if isinstance(elem, CodeBlock): return [ RawBlock("\\begin{minipage}{\\textwidth}" + latex, "tex"), elem, RawBlock("\\end{minipage}", "tex"), ] # It is a Div: try to insert an inline raw before the first inline element inserted = [False] def insert(element, _): if ( not inserted[0] and isinstance(element, Inline) and not isinstance(element.parent, Inline) ): inserted[0] = True return [RawInline(latex, "tex"), element] return None elem.walk(insert) if not inserted[0]: return [RawBlock("\\needspace{5em}", "tex"), RawBlock(latex, "tex"), elem] return [RawBlock("\\needspace{5em}", "tex"), elem] return None
def add_latex(elem, color, bgcolor): # Is it a Span? if isinstance(elem, Span): if bgcolor: elem.content.insert(0, RawInline(bgcolor + '\\hl{', 'tex')) elem.content.append(RawInline('}', 'tex')) elem.content.insert(0, RawInline(color, 'tex')) # Is it a Div? elif isinstance(elem, Div): inlines = {'first': None, 'last': None} def find_inlines(elem, _): if isinstance(elem, Inline): inlines['last'] = elem if inlines['first'] is None: inlines['first'] = elem elem.walk(find_inlines, None) if inlines['first'] is not None: if bgcolor: inlines['first'].parent.content.insert( 0, RawInline('{' + color + bgcolor + '\\hl{', 'tex') ) inlines['last'].parent.content.append(RawInline('}}', 'tex')) else: inlines['first'].parent.content.insert( 0, RawInline('{' + color, 'tex') ) inlines['last'].parent.content.append(RawInline('}', 'tex'))
def add_latex(elem, color, bgcolor): # Is it a Span? if isinstance(elem, Span): if bgcolor: elem.content.insert(0, RawInline(bgcolor + '\\hl{', 'tex')) elem.content.append(RawInline('}', 'tex')) elem.content.insert(0, RawInline(color, 'tex')) # Is it a Div? elif isinstance(elem, Div): if bgcolor: elem.content.insert( 0, RawBlock('{' + color + bgcolor + '\\hl{', 'tex')) elem.content.append(RawBlock('}', 'tex')) else: elem.content.insert(0, RawBlock('{' + color, 'tex')) elem.content.append(RawBlock('}', 'tex'))
def action(elem, doc): if doc.get_metadata('title') is None: # No title -> Beancount Options Reference if isinstance(elem, Para): # Convert all paragraphs to code blocks text = stringify(elem) if not text.startswith('option'): text = ' ' + text return CodeBlock(text) # Skip everything else return if isinstance(elem, BlockQuote): if isinstance(elem.parent, ListItem): # Don't use blockquotes in lists assert len(elem.content) == 1 return elem.content[0] elif any(isinstance(item, CodeBlock) for item in elem.content): # Remove blockquotes around code blocks return [item for item in elem.content] elif len(elem.content) == 1: # Convert blockquotes to code blocks code = '' for item in elem.content[0].content: if isinstance(item, Link): # Don't convert links to code break elif isinstance(item, Str): code += item.text elif isinstance(item, Space): code += ' ' elif isinstance(item, LineBreak): code += '\n' else: code += stringify(item) else: return CodeBlock(code) elif isinstance(elem, Header): # There must be only one level 1 header if elem.identifier != 'title': elem.level += 1 # Add explicit anchor elem.content.append(RawInline(f'<a id="{elem.identifier}"></a>')) elif isinstance(elem, Link): if elem.url == stringify(elem): # Displayed as url, skip pass else: resolved = resolve_url(elem.url) if resolved: elem.url = resolved elif isinstance(elem, CodeBlock): # Remove unnecessary leading tabs from code blocks elem.text = re.sub(r'^\t', '', elem.text, flags=re.MULTILINE)
def add_latex(elem, latex): # Is it a Span? if isinstance(elem, Span): elem.content.insert(0, RawInline(latex, 'tex')) # Is it a Div? elif isinstance(elem, Div): elem.content.insert(0, RawBlock('{' + latex, 'tex')) elem.content.append(RawBlock('}', 'tex'))
def insert(element, _): if ( not inserted[0] and isinstance(element, Inline) and not isinstance(element.parent, Inline) ): inserted[0] = True return [RawInline(latex, "tex"), element] return None
def finalize(doc): # Add header-includes if necessary if 'header-includes' not in doc.metadata: doc.metadata['header-includes'] = MetaList() # Convert header-includes to MetaList if necessary elif not isinstance(doc.metadata['header-includes'], MetaList): doc.metadata['header-includes'] = MetaList(doc.metadata['header-includes']) # Add usefull LaTexPackage doc.metadata['header-includes'].append(MetaInlines(RawInline('\\usepackage{xcolor}', 'tex')))
def finalize(doc): """ Finalize the pandoc document Arguments --------- doc: The pandoc document """ # Add header-includes if necessary if "header-includes" not in doc.metadata: doc.metadata["header-includes"] = MetaList() # Convert header-includes to MetaList if necessary elif not isinstance(doc.metadata["header-includes"], MetaList): doc.metadata["header-includes"] = MetaList( doc.metadata["header-includes"]) # Add usefull LaTexPackage doc.metadata["header-includes"].append( MetaInlines(RawInline("\\usepackage{tikz}", "tex"))) doc.metadata["header-includes"].append( MetaInlines( RawInline("\\tikzstyle{every picture}+=[remember picture]", "tex"))) doc.metadata["header-includes"].append( MetaInlines(RawInline("\\usetikzlibrary{positioning}", "tex"))) doc.metadata["header-includes"].append( MetaInlines( RawInline( "\\tikzset{onslide/.code args={<#1>#2}{\\only<#1>{\\pgfkeysalso{#2}}}}", "tex", ))) # Define x11 colors tex = [] for name, color in doc.x11colors.items(): tex.append("\\definecolor{" + name.lower() + "}{HTML}{" + color + "}") doc.metadata["header-includes"].append( MetaInlines(RawInline("\n".join(tex), "tex")))
def replace_page_number(where, tag): """ Replace page number in where. Arguments --------- where: where to replace tag: replace %p by tag """ where.walk( partial(replacing, search="%p", replace=[RawInline("\\pageref{" + tag + "}", "tex")]))
def add_latex(elem, latex): """ Add LaTeX code to elem. """ # Is it a Span? if isinstance(elem, Span): elem.content.insert(0, RawInline(latex, "tex")) # Is it a Div? elif isinstance(elem, Div): elem.content.insert(0, RawBlock("{" + latex, "tex")) elem.content.append(RawBlock("}", "tex")) # Is it a Code? elif isinstance(elem, Code): return [RawInline("{" + latex, "tex"), elem, RawInline("}", "tex")] # Is it a CodeBlock? elif isinstance(elem, CodeBlock): return [RawBlock("{" + latex, "tex"), elem, RawBlock("}", "tex")] return None
def alerts_to_text(elem, _): """ Apply the translations, see module doc """ if not isinstance(elem, Para): return if not isinstance(elem.content[0], Str): return if not isinstance(elem.content[-1], Str): return match1 = re.match(r':::(info|warning|danger|success)', elem.content[0].text) match2 = re.match(r':::', elem.content[-1].text) if match1 and match2: return Para( RawInline('\\begin{infobox' + match1.group(1).title() + '}', format='latex'), *elem.content[2:-2], RawInline('\\end{infobox' + match1.group(1).title() + '}', format='latex'))
def image(elem, doc): """ Transform image element. Arguments --------- elem: current element doc: pandoc document """ if doc.format == "beamer" and isinstance(elem, Image): classes = frozenset(elem.classes) # Loop on all multigraphics definition for definition in doc.defined: # Are the classes correct? if classes >= definition["classes"]: graphics = [] if ("height" in elem.attributes or "width" in elem.attributes or "height" in definition or "width" in definition): graphics.append("height=%s" % str( elem.attributes.get( "height", definition.get("height", "\\textheight")))) graphics.append("width=%s" % str( elem.attributes.get( "width", definition.get("width", "\\textwidth")))) options = [] if "start" in elem.attributes: options.append("start=%d" % int(elem.attributes["start"])) if "end" in elem.attributes: options.append("end=%d" % int(elem.attributes["end"])) options.append( "format=%s" % str(elem.attributes.get("format", definition["format"]))) return RawInline( "\\multiinclude[graphics={%s},%s]{%s}" % (",".join(graphics), ",".join(options), elem.url), "tex", ) return None
def finalize(doc): # Add header-includes if necessary if 'header-includes' not in doc.metadata: doc.metadata['header-includes'] = MetaList() # Convert header-includes to MetaList if necessary elif not isinstance(doc.metadata['header-includes'], MetaList): doc.metadata['header-includes'] = MetaList( doc.metadata['header-includes'] ) # Add usefull LaTexPackage doc.metadata['header-includes'].append(MetaInlines(RawInline( '\\usepackage{xcolor}', 'tex' ))) doc.metadata['header-includes'].append(MetaInlines(RawInline( '\\usepackage{soulutf8,color}', 'tex' ))) doc.metadata['header-includes'].append(MetaInlines(RawInline( '\\soulregister\\cite7', 'tex' ))) doc.metadata['header-includes'].append(MetaInlines(RawInline( '\\soulregister\\ref7', 'tex' ))) doc.metadata['header-includes'].append(MetaInlines(RawInline( '\\soulregister\\pageref7', 'tex' ))) for color, value in x11colors().items(): doc.metadata['header-includes'].append(MetaInlines(RawInline( '\\definecolor{' + color + '}{HTML}{' + value + '}', 'tex' ))) if doc.format == 'beamer': special_beamer = [ '\\makeatletter', '\\let\\HL\\hl', '\\renewcommand\\hl{%', '\\let\\set@color\\beamerorig@set@color', '\\let\\reset@color\\beamerorig@reset@color', '\\HL}', '\\makeatother' ] for line in special_beamer: doc.metadata['header-includes'].append(MetaInlines(RawInline( line, 'tex' )))
def note(element, doc): if (isinstance(element, Note) and doc.format == "beamer" and not environment["localfootnotes"]): return RawInline( "".join([ "\\footnote<.->[frame]{", convert_text( element.content, input_format="panflute", output_format="latex", ), "}", ]), "tex", )
def add_caption(elem, doc): """ """ if not isinstance(elem, Image): return if not isinstance(elem.parent.next, BlockQuote): return elem.title = 'fig:' info = elem.parent.next.content[0] elem.content = info.content if doc.format == 'latex' and hasattr(info.content[0], 'identifier'): elem.content.insert( 0, RawInline(r'\label{' + info.content[0].identifier + r'}', format='tex')) elem.parent.container.remove(elem.parent.next) return elem
def finalize(doc): """ Finalize the document Arguments --------- doc: The pandoc document """ # Add header-includes if necessary if "header-includes" not in doc.metadata: doc.metadata["header-includes"] = MetaList() # Convert header-includes to MetaList if necessary elif not isinstance(doc.metadata["header-includes"], MetaList): doc.metadata["header-includes"] = MetaList( doc.metadata["header-includes"]) doc.metadata["header-includes"].append( MetaInlines(RawInline("\\usepackage{xmpmulti}", "tex")))
def stripped_quote(elem, _doc): if isinstance(elem, BlockQuote): child_elems = [RawInline(">"), Space()] after_paragraph = False def append(child, _doc): nonlocal child_elems nonlocal after_paragraph if isinstance(child, Inline): if after_paragraph: child_elems += [SoftBreak(), RawInline(">")] * 2 + [ Space() ] child_elems.append(child) if isinstance(child, SoftBreak): child_elems += [RawInline(">"), Space()] after_paragraph = isinstance(child, Para) elem.walk(append) return Para(*child_elems)
def links_to_footnotes(elem, doc): """ Will shift a header level from the filter-header-shift metadata value (which must exist) """ if doc.format != 'latex': return if isinstance(elem, Link): if elem.url.startswith('#'): return if elem.url.startswith('mailto:'): return if elem.url == stringify(elem): return return [ elem, Note( Para(RawInline(stringify(elem), format='tex'), Str(':'), Space(), Link(Str(elem.url), title=elem.title, url=elem.url))) ]
def ghm_to_md(elem, doc): if isinstance(elem, Image): return [] if isinstance(elem, Link) and len(elem.content) == 0: return [] if isinstance(elem, Header): if elem.level > 1: elem.level -= 1 else: return [] if isinstance(elem, Code): return RawInline("`" + elem.text + "'") if isinstance(elem, Str): if elem.text == '\\': return [] if isinstance(elem, Link): elem.parent.content.insert(elem.index + 1, Span(*elem.content)) return []
def latex(code): """LaTeX inline""" return RawInline(code, format='latex')