def filter_hatena_blockquote(elem, doc): if isinstance(elem, pf.BlockQuote): quotecomponents = [pf.RawInline('>>'), pf.RawInline('\n')] + list(elem.content[0].content) + [pf.RawInline('\n'), pf.RawInline('<<')] return pf.Plain(*quotecomponents) elif isinstance(elem, pf.Div) and 'epigraph' in elem.classes: epigraph_phrase = elem.content[0] epigraph_source = elem.content[1] return pf.Plain(*[pf.RawInline(f'>')] + list(epigraph_source.content) + [pf.RawInline('>'), pf.RawInline('\n')] + list(epigraph_phrase.content) + [pf.RawInline('\n'), pf.RawInline('<<')])
def finalize(doc): c1 = pf.TableCell(pf.Plain(pf.Str("Element"))) c2 = pf.TableCell(pf.Plain(pf.Str("Frequency"))) header = pf.TableRow(c1, c2) rows = [] for tag in doc.counter: c1 = pf.TableCell(pf.Plain(pf.Str(tag))) c2 = pf.TableCell(pf.Plain(pf.Str(str(doc.counter[tag])))) rows.append(pf.TableRow(c1, c2)) table = pf.Table(*rows, header=header) doc.content = [table] # bugbug?
def _wrap(opening, closing): if isinstance(elem, pf.Div): if elem.content and isinstance(elem.content[0], pf.Para): elem.content[0].content.insert(0, opening) else: elem.content.insert(0, pf.Plain(opening)) if elem.content and isinstance(elem.content[-1], pf.Para): elem.content[-1].content.append(closing) else: elem.content.append(pf.Plain(closing)) elif isinstance(elem, pf.Span): elem.content.insert(0, opening) elem.content.append(closing)
def finalize(doc): content = doc.content colour_boxes = [] count = 0 for t in tag_sequence: colour = outcome_colours[t] if doc.format in ('html', 'html5'): div = pf.Span( attributes={ 'style': f"width:4px;height:40px;background-color:{colour[1]};float:left" }) elif doc.format == 'latex': div = pf.RawInline( f"\\tcbox[tcbox width=forced center,boxrule=0mm,before=,after=,left=0mm,right=0mm,width=1mm,height=4em,arc=0mm,colframe={colour[1]},colback={colour[1]}]{{}}", format='latex') colour_boxes.append(div) colour_block = pf.Div(pf.Plain(*colour_boxes), attributes={'style': 'height:45px'}) barcodes.append((collect_tags, colour_block)) # Now insert them in reverse order (biggest insertion point first) so that # when we insert things into the document, stuff below doesn't get shifted # down. barcodes.reverse() for (spot, block) in barcodes: content.insert(spot, block)
def plain_to_table_cell(string): if isinstance(string, bool): if not string: string = "no" else: string = "yes" return panflute.TableCell(panflute.Plain(panflute.Str(string)))
def prepare(doc): date = doc.get_metadata('date') if date == 'today': doc.metadata['date'] = datetime.date.today().isoformat() doc.metadata['pagetitle'] = pf.convert_text( pf.Plain(*doc.metadata['title'].content), input_format='panflute', output_format='markdown') datadir = doc.get_metadata('datadir') with open(os.path.join(datadir, 'annex-f'), 'r') as f: stable_names.update(line.split() for line in f) def highlighting(output_format): return pf.convert_text( '`_`{.default}', output_format=output_format, extra_args=[ '--highlight-style', os.path.join(datadir, 'syntax', 'wg21.theme'), '--template', os.path.join(datadir, 'template', 'highlighting') ]) doc.metadata['highlighting-macros'] = pf.MetaBlocks( pf.RawBlock(highlighting('latex'), 'latex')) doc.metadata['highlighting-css'] = pf.MetaBlocks( pf.RawBlock(highlighting('html'), 'html'))
def action(e, doc): if not doc.format == 'latex': return None if isinstance(e, pf.Span) and 'textquote' in e.classes: cite = e.attributes.get('cite') if cite: cite = pf.convert_text(cite, extra_args=['--biblatex'], input_format='markdown', output_format='latex') text = pf.convert_text(pf.Plain(e), extra_args=['--biblatex'], input_format='panflute', output_format='latex') values = { 'lang': e.attributes.get('lang'), 'cite': cite, 'punct': e.attributes.get('punct'), 'text': text } tex = QUOTE.render(values) return pf.RawInline(tex, format='latex') else: return None
def handleHeaderBlockLevel(e, doc): """ :param e: :param doc: :return: """ global blocktag tag = blocktag blocktag = None before = None if tag: before = pf.RawBlock("\\end{" + tag + "}\n", "latex") if "endblock" in e.classes: return before for blocktype in BLOCKCLASSES: if blocktype in e.classes: logging.debug("BLOCKTYPE:" + blocktype) if not isinstance(e.content, pf.ListContainer): logging.debug("CONTENT:" + pf.stringify(e.content)) tag = TEX_BLOCKCLASSES_TAG[blocktype] elem = pf.Div() elem.content = [ pf.Plain(pf.RawInline("\n\\begin{" + tag + "}[", "latex"), e.content, pf.RawInline("]\n", "latex")) ] blocktag = tag if before: return [before, elem] return elem else: logging.debug("CONTENT: Listcontainer")
def list_to_elems(list_): for i in list_: if isinstance(i, str): for e in pf.convert_text(i, "rst"): yield e else: yield pf.Plain(pf.Str(str(i)))
def header(elem, doc): if not isinstance(elem, pf.Plain): return None return pf.Div(pf.Plain(pf.RawInline('\\centering', 'latex'), pf.Strong(*elem.content)), attributes={'style': 'text-align:center'})
def filter_hatena_toc(elem, doc): """ 目次を挿入する場合ははてな記法で自動生成するように置き換え """ if isinstance(elem, pf.RawBlock): if elem.format == 'latex' and elem.text == r'\tableofcontents{}': return pf.Plain(pf.RawInline('[:contents]'))
def build_header(elem): # We use a `pf.RawInline` here because setting the `align` # attribute on `pf.Div` does not work for some reason. header = pf.Plain(pf.RawInline('<div align="center">', 'html'), pf.Strong(*elem.content), pf.RawInline('</div>', 'html')) width = float( elem.attributes['width']) if 'width' in elem.attributes else 0 return header, width
def repl(match_obj): match = match_obj.group(1) if not match: # @@ return match_obj.group(0) if match.isspace(): # @ @ return match return pf.convert_text(pf.Plain(*pf.convert_text(match)[0].content), input_format='panflute', output_format=doc.format)
def action(elem, doc): if isinstance( elem, pf.RawInline) and elem.format == 'tex' and '\pnum{' in elem.text: repl = re.sub(r"\\pnum{([^}]+)}", "<small>(\\1)</small>", elem.text) return pf.RawInline(repl, 'html') if not isinstance(elem, pf.Div) and not isinstance(elem, pf.Span): return None color_name = None tag_name = None for cls in elem.classes: color_name = cls + 'color' if cls == 'add': tag_name = 'ins' elif cls == 'rm': tag_name = 'del' if tag_name is None: return None open_tag = pf.RawInline('<{}>'.format(tag_name), 'html') open_color = pf.RawInline('{{\\color{{{}}}'.format(color_name), 'tex') close_color = pf.RawInline('}', 'tex') close_tag = pf.RawInline('</{}>'.format(tag_name), 'html') color = doc.get_metadata(color_name) attributes = {} if color is None else {'style': 'color: #{}'.format(color)} if isinstance(elem, pf.Div): return pf.Div(pf.Plain(open_tag), pf.Plain(open_color), elem, pf.Plain(close_color), pf.Plain(close_tag), attributes=attributes) elif isinstance(elem, pf.Span): return pf.Span(open_tag, open_color, elem, close_color, close_tag, attributes=attributes)
def action(elem, doc): if isinstance(elem, pf.BulletList) and doc.format == 'latex': div = pf.Div() for item in elem.content.list: plain = item.content.list[0] key = plain.content.pop(0) cv_line_tex = "\cvitem{%s}{%s}{}" % (pf.stringify(key), pf.stringify(plain)) raw_inline = pf.RawInline(cv_line_tex, format='latex') div.content.extend([pf.Plain(raw_inline)]) return div
def figure(options, data, element, doc): # Get options fn = os.path.abspath(options['source']).replace('\\', '/') title = options.get('title', 'Untitled') notes = data label = options.get('label', os.path.splitext(os.path.basename(fn))[0]) if doc.format == 'latex': subs = {'fn': fn, 'label': label} subs['title'] = pf.convert_markdown(title, format='latex') subs['notes'] = pf.convert_markdown(notes, format='latex') backmatter = doc.get_metadata('format.backmatter', False) pagebreak = doc.get_metadata('format.media-pagebreak', False) w = options.get('width', 1.0) subs['width'] = w subs['innerwidth'] = options.get('innerwidth', w) / w subs['notesize'] = options.get('notesize', 'small') subs['pagebreak'] = '\\clearpage\n' if pagebreak else '' text = LATEX_TEMPLATE.safe_substitute(subs) ans = pf.RawBlock(text=text, format='latex') if backmatter: doc.backmatter.append(ans) msg = '\hyperref[fig:{}]{{[\Cref{{fig:{}}} Goes Here]}}' msg = msg.format(label, label) return pf.Plain(pf.Str(msg)) else: return ans else: title = pf.convert_markdown(title) assert len(title)==1, title title = (title[0]).items notes = pf.Div(*pf.convert_markdown(notes), classes=['note']) title_text = pf.stringify(title) img = pf.Image(*title, url=fn, title=title_text, identifier=label) ans = pf.Div(pf.Plain(img), pf.Plain(pf.LineBreak), notes, classes=['figure']) return ans
def list_to_elems(list_): for i in list_: if isinstance(i, str): for e in pf.convert_text( f""".. role:: raw-latex(raw) :format: latex {i}""", "rst", ): yield e else: yield pf.Plain(pf.Str(str(i)))
def parse_table_list(markdown, table_list, doc): """ read table in list and return panflute table format """ # make functions local to_table_row = pf.TableRow if markdown: to_table_cell = lambda x: pf.TableCell(*pf.convert_text(x)) else: to_table_cell = lambda x: pf.TableCell(pf.Plain(pf.Str(x))) return [ to_table_row(*[to_table_cell(x) for x in row]) for row in table_list ]
def sage(elem, doc): elemtype = type(elem) if elemtype in [pf.Math, pf.RawInline]: contents = replace_sagecommand(elem.text) if elemtype == pf.Math: return pf.Math(contents, format=elem.format) else: return pf.RawInline(contents, format=elem.format) if elemtype == pf.CodeBlock: isSageSilent = 'sagesilent' in elem.classes isSageBlock = 'sageblock' in elem.classes isSagePlot = 'sageplot' in elem.classes code = elem.text if isSageBlock or isSagePlot or isSageSilent: img_file = get_image_output_filename(code) sage_file = get_sage_filename(code) if isSagePlot: code = code.strip("\n") codelist = code.split("\n") plot_cmd = codelist.pop() code = "\n".join(codelist) m = re.search(r"sageplot\[(?P<first_name>.*)\]\((.*)\)", plot_cmd) if m == None: para, cmd = "", plot_cmd else: para, cmd = m.group(1), m.group(2) if len(para) > 0: para = ',' + para code += "\n(%s).save(\"%s\"%s)" % (cmd, img_file, para) out, err = run_sage(code) if isSageSilent: return pf.Plain(pf.RawInline("", "tex")) elif isSageBlock: sys.stderr.write('\n convert markdown \n') return pf.convert_text(out) else: return pf.Para( pf.Image(url=img_file, attributes=elem.attributes)) if 'latex' in elem.classes: out, err, img_file = run_tex(code) return pf.Para(pf.Image(url=img_file, attributes=elem.attributes))
def formatCell(text): if len(text) < 10 or text.isdigit(): return pf.Plain(pf.Str(text)) try: text = float(text) if text > 0 and text < .001: return pf.Plain(pf.Str('%.3E' % text)) return pf.Plain(pf.Str('%.3f' % text)) except (ValueError, TypeError): # transform links elems = [] lastend = None for m in re.finditer(r'\[(.+?)\]\((.+?)\)', text): if lastend is None: elems.append(pf.Str(text[:m.start()])) else: elems.append(pf.Str(text[lastend:m.start()])) elems.append(pf.Link(pf.Str(m.group(1)), url = m.group(2))) lastend = m.end() if elems: elems.append(pf.Str(text[lastend:])) return pf.Plain(*elems) return pf.Plain(pf.Str(text))
def process_internal_links(link, doc): # type: (Link, Doc) -> Element """extract links that point to internal items, e.g. [text](#label)""" if not isinstance(link, pf.Link): return None match = re.match(r'#(.+)$', link.url) if not match: return None return create_cite_span([match.group(1)], "markdown", False, prefix=dict(PREFIX_MAP_LATEX_R).get("cref"), alt=pf.stringify( pf.Plain(*list(link.content))).strip())
def fenced_action(options, data, element, doc): # We'll only run this for CodeBlock elements of class 'csv' title = options.get('title', 'Untitled Table') title = [pf.Str(title)] has_header = options.get('has-header', False) with io.StringIO(data) as f: reader = csv.reader(f) body = [] for row in reader: cells = [pf.TableCell(pf.Plain(pf.Str(x))) for x in row] body.append(pf.TableRow(*cells)) header = body.pop(0) if has_header else None table = pf.Table(*body, header=header, caption=title) return table
def create_cite_span(identifiers, rawformat, is_block, prefix="", alt=None): """create a cite element from an identifier """ citations = [pf.Citation(identifier) for identifier in identifiers] pmapping = dict(dict(PREFIX_MAP)[prefix]) classes = list(pmapping["classes"]) classes += [RAWSPAN_CLASS, CONVERTED_CITE_CLASS, ATTRIBUTE_CITE_CLASS] attributes = dict(pmapping["attributes"]) attributes["raw-format"] = rawformat if alt is not None: attributes["alt"] = str(alt) cite = Cite(citations=citations) span = pf.Span(cite, classes=classes, attributes=attributes) if is_block: return pf.Plain(span) else: return span
def action(e, doc): if isinstance(e, pf.Header) and 'unnumbered' in e.classes: if doc.format == 'latex': text = pf.convert_text( pf.Plain(*e.content), extra_args=[ '--biblatex', '--filter=tools/panflutist/reference_spans.py' ], input_format='panflute', output_format='latex') chp = doc.get_metadata('use-chapter', default=False) tex = CHAPTER.render(text=text) if chp else SECTION.render( text=text) return pf.RawBlock(tex, format='latex')
def repl(match_obj): match = match_obj.group(1) if not match: # @@ return match_obj.group(0) if match.isspace(): # @ @ return match if doc.format == 'latex': # Undo `escapeLaTeX` from https://github.com/jgm/skylighting match = match.replace('\\textbackslash{}', '\\') \ .replace('\\{', '{') \ .replace('\\}', '}') plain = pf.Plain(*pf.convert_text(match)[0].content) return pf.convert_text(plain.walk(divspan, doc), input_format='panflute', output_format=doc.format)
def block_wrap(elem, orig_elem): """Wraps an element in a block if necessary. If the original element was block panflute expects the return value to be also block. In many places we need to detect this and wrap an inline. :param elem: Element to be wrapped :type elem: :py:class:`panflute.base.Element` :param orig_elem: Original element :type orig_elem: :py:class:`panflute.base.Element` :rtype: :py:class:`panflute.base.Element` :returns: ``elem`` or ``elem`` wrapped in :py:class:`panflute.elements.Plain` """ if isinstance(orig_elem, pf.Block): return pf.Plain(elem) return elem
def fenced_html(options, data, element, doc): identifier = options.get('identifier', '') element.identifier = identifier caption = options.get('caption', '') caption = pf.convert_text(caption, extra_args=['--biblatex'], input_format='markdown', output_format='html') caption_span = pf.Plain( pf.Span(pf.RawInline(caption), classes=['fencedSourceCodeCaption'])) code_block = pf.CodeBlock(data, classes=element.classes, attributes=element.attributes) return pf.Div(code_block, caption_span, identifier=identifier, classes=['fencedSourceCode'])
def repl2(match): if match.isspace(): # @ @ return match result = convert.cache.get(match) if result is not None: return result if doc.format == 'latex': # Undo `escapeLaTeX` from https://github.com/jgm/skylighting match = match.replace('\\textbackslash{}', '\\') \ .replace('\\{', '{') \ .replace('\\}', '}') \ .replace('\\VerbBar{}', '|') \ .replace('\\_', '_') \ .replace('\\&', '&') \ .replace('\\%', '%') \ .replace('\\#', '#') \ .replace('\\textasciigrave{}', '`') \ .replace('\\textquotesingle{}', '\'') \ .replace('{-}', '-') \ .replace('\\textasciitilde{}', '~') \ .replace('\\^{}', '^') # Undo the workaround escaping. match = match.replace('\\textless{}', '<') \ .replace('\\textgreater{}', '>') elif doc.format == 'html': match = html.unescape(match) result = pf.convert_text( pf.Plain(*pf.convert_text(match)[0].content).walk( divspan, doc).walk(init_code_elems, doc), input_format='panflute', output_format=doc.format, extra_args=[ '--syntax-definition', os.path.join(datadir, 'syntax', 'isocpp.xml') ]) convert.cache[match] = result return result
def action(elem, doc): if isinstance(elem, pf.Doc): version = pkg_resources.get_distribution("panflute").version json_serializer = lambda elem: elem.to_json() raw = json.dumps(elem, default=json_serializer) raw = json.loads(raw) raw = json.dumps(raw, check_circular=False, indent=4, separators=(',', ': ')) disclaimer = pf.Para(pf.Emph(pf.Str('Note: sort order not preserved'))) elem.content = [ pf.Header(pf.Str('Python version:'), level=2), pf.Para(pf.Str(sys.version)), pf.Header(pf.Str('Panflute version:'), level=2), pf.Para(pf.Str(version)), pf.Header(pf.Str('sys.argv:'), level=2), pf.Plain(pf.Str(str(sys.argv))), pf.Header(pf.Str('JSON Input:'), level=2), disclaimer, pf.CodeBlock(raw) ]
def build_course_barcode(doc): content = doc.content colour_boxes = [] count = 0 for t in tag_sequence: colour = outcome_colours[t] if doc.format in ('html', 'html5'): div = pf.Span( attributes={ 'style': f"width:4px;height:40px;background-color:{colour[1]};float:left" }) elif doc.format == 'latex': div = pf.RawInline( f"\\tcbox[tcbox width=forced center,boxrule=0mm,before=,after=,left=0mm,right=0mm,width=1mm,height=4em,arc=0mm,colframe={colour[1]},colback={colour[1]}]{{}}", format='latex') colour_boxes.append(div) colour_block = pf.Div(pf.Plain(*colour_boxes), attributes={'style': 'height:45px'}) barcodes.append((collect_tags, colour_block))