def meta_format_caption(category, definition, defined): """ Compute format caption for a category. Arguments --------- category: definition: defined: """ if "format-caption-classic" in definition: if isinstance(definition["format-caption-classic"], MetaInlines): defined[category]["format-caption-classic"] = stringify( definition["format-caption-classic"]) else: debug( "[WARNING] pandoc-numbering: format-caption-classic is not correct for category " + category) if "format-caption-title" in definition: if isinstance(definition["format-caption-title"], MetaInlines): defined[category]["format-caption-title"] = stringify( definition["format-caption-title"]) else: debug( "[WARNING] pandoc-numbering: format-caption-title is not correct for category " + category)
def h_html_header(e, doc): if not isinstance(e, pf.Header) or doc.format != "html": return None if doc.get_metadata("indexpage", False): return None if doc.chapternum == "99": return None logstring(f"--Evaluating header {e} level {e.level} vs {doc.currentlevel}", doc) if e.level > 1 and not pf.stringify(e): return None if e.level > len(doc.currentplace): doc.currentplace += ["0"] * (e.level - len(doc.currentplace)) if e.level > 1: doc.currentplace = doc.currentplace[:e.level] t = int(doc.currentplace[e.level - 1]) doc.currentplace[e.level - 1] = str(t + 1) href = "" if e.level == 1 else labelref(e, doc) if href in doc.labels and doc.labels[href]["number"]: place = doc.labels[href]["number"].split(".") else: place = doc.currentplace place[0] = doc.chapternum # title = doc.get_metadata("title","---") if e.level==1 else pf.stringify(e) title = pf.stringify(e) if not title: return e addtocline(doc.toc, place=place, title=title, file=doc.get_metadata("filename", "") + ".html", href=href) return e
def match(elem: panflute.Element) -> Optional[Match[str]]: """Pattern match for a key in the element We can convert a given element to a string and match it against the known format for an acronym key. This method manages converting an element to a string, while appropriately stripping unnecessary punctuation, and passing the result to the :func:`re.match`. Parameters ---------- elem: :class:`panflute.Element` The document element to inspect for an acronym key. Returns ------- Match: The result of the :func:`re.match` call with the string version of the element. """ if isinstance(elem, panflute.Str): content = panflute.stringify(elem) if isinstance(elem.parent, panflute.Quoted): content = re.sub("['\"]", "", content) elif isinstance(elem, panflute.Span): content = panflute.stringify(elem.content[0]) \ if len(elem.content) == 1 else "" else: content = "" return Key.PATTERN.match(content)
def fix_appendix(elem, doc): """Looks for headings titled 'Bibliography' or 'Abbreviations', adds a page break before them and puts them at the end of the documents. :param elem: :param doc: """ if isinstance(elem, pf.Header) and pf.stringify(elem) in ( "Bibliography", "Abbreviations", ): list_ = (doc.abbreviations if pf.stringify(elem) == "Abbreviations" else doc.bibliography) elem.classes.append("page-break-before") list_.append(elem) next_ = elem.next while True: next = next_ if (isinstance(next, pf.Header) and next.level == elem.level) or next is None: break else: next_ = next.next list_.append(next) next.parent.content.pop(next.index) return []
def h_html_header(e,doc): if not isinstance(e,pf.Header) or doc.format != "html": return None if doc.get_metadata("indexpage",False): return None if doc.chapternum=="99": return None logstring(f"--Evaluating header {e} level {e.level} vs {doc.currentlevel}",doc) if e.level>1 and not pf.stringify(e): return None if e.level> len(doc.currentplace): doc.currentplace += ["0"]*(e.level-len(doc.currentplace)) if e.level>1: doc.currentplace = doc.currentplace[:e.level] t = int(doc.currentplace[e.level-1]) doc.currentplace[e.level-1] = str(t+1) href = "" if e.level==1 else labelref(e,doc) if href in doc.labels and doc.labels[href]["number"]: place = doc.labels[href]["number"].split(".") else: place = doc.currentplace place[0] = doc.chapternum # title = doc.get_metadata("title","---") if e.level==1 else pf.stringify(e) title = pf.stringify(e) if not title: return e addtocline(doc.toc,place=place, title=title,file=doc.get_metadata("filename","")+".html",href=href) return e
def render_citations(elem, doc, string=False): if isinstance(elem, pf.Cite): if doc.format == "latex" and not doc.get_metadata( "doit_citeproc_for_latex", True ): latex_commands = [] latex_command = "\\autocite{{{ids}}}" if hasattr(elem, "latex_command") and elem.latex_command: for command in elem.latex_command: head = "" if command.startswith("\\") else "\\cite" latex_command = "{head}{command}{{{{{{ids}}}}}}".format( head=head, command=command ) latex_commands.append(latex_command) else: latex_commands.append(latex_command) citations = ",".join([c.id for c in elem.citations]) raw = "".join(lc.format(ids=citations) for lc in latex_commands) if string: return raw else: return pf.RawInline(raw, format="latex") else: if hasattr(elem, "latex_command") and "author" in elem.latex_command: names = [] amount_citations = len(elem.citations) for i in range(1, amount_citations + 1): citation = elem.citations[i - 1] citation = doc.bibliography.get(citation.id, False) if citation: names_list = citation.get( "author", citation.get("editor", False) ) if names_list: names.extend(utils.format_names(names_list)) if not i == amount_citations: names.extend([pf.Str(", "), pf.Space]) if names: if elem.next: if pf.stringify(names[-1]).endswith(".") and pf.stringify( elem.next ).startswith("."): names[-1] = pf.Str(pf.stringify(names[-1])[:-1]) return pf.Span(*names) return pf.Cite(citations=elem.citations)
def action(elem, doc): if isinstance(elem, pf.ListItem) and elem.content and isinstance( elem.content[0], pf.Plain): plain = elem.content[0] stringy = pf.stringify(elem) matches = tag_pattern.findall(stringy) identifier = None outcome = None # strip the tags from the element before we proceed any further for tag in matches: elem = elem.replace_keyword(tag, pf.Space()) # strip the trailing whitespace (this is equivalent to a trim operation) while type(plain.content[-1]) in (pf.Space, pf.SoftBreak): del plain.content[-1] # gather all of the tags/IDs on the learning objective into something # that we can write out to an attribute on the span. outcomes = [] identifiers = [] courses = [] for tag in matches: if "&" in tag: # this is not a tag, it's a course reference courses.append(tag[1:-1]) elif "#" in tag: identifiers.append(tag[tag.index("#") + 1:-1]) outcomes.append(tag[:tag.index("#")] + ":") else: outcomes.append(tag) outcomes = " ".join(outcomes) courses = " ".join(courses) if len(identifiers) == 1: identifier = identifiers[0] elif len(identifiers) > 1: pf.debug(f"Found multiple IDs: {identifiers}") identifier = identifiers[0] else: identifier = "" # Take the existing contents of the `pf.Plain` that's the first child # of the `pf.ListItem` and embed that into a `pf.Span`. Then make the # `pf.Span` the only child of the `pf.Plain`. if matches: span = pf.Span(*plain.content, identifier=identifier, attributes={ "outcomes": outcomes, "course": pf.stringify(doc.metadata["title"]) }) if courses: span.attributes["used-in"] = courses plain.content.clear() plain.content.append(span) return elem
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 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) elif isinstance(elem, Image): elem.url = './' + elem.url
def action(self, elem, doc): if isinstance(elem, pf.Header): if elem.level == 1: day_text = elem.content[0].text try: self.current_day = datetime.datetime.strptime( day_text, "%Y-%m-%d" ).date() except ValueError: if "NOWARN" not in pf.stringify(elem): self.warnings.append( f"Ignoring header for day {pf.stringify(elem)}" ) self.current_day = None if elem.level == 2: period_text = elem.content[0].text try: begin_time, end_time = map( lambda d: datetime.datetime.strptime(d, "%H:%M").time(), period_text.split("-"), ) begin = datetime.datetime.combine(self.current_day, begin_time) end = datetime.datetime.combine(self.current_day, end_time) self.period = Period(begin=begin, end=end) self.periods.append(self.period) except ValueError: if "NOWARN" not in pf.stringify(elem): self.warnings.append( f"Ignoring header for period {pf.stringify(elem)}" ) self.period = None if elem.level == 3: # first, partition on '/'s def is_slash(e): return isinstance(e, pf.Str) and e.text == "/" parts = [ list(content) for group, content in itertools.groupby(elem.content, is_slash) if not group ] def elem_to_text(e): if isinstance(e, pf.Space): return " " return e.text def elems_to_text(e): return " ".join(map(elem_to_text, e)).strip() task_hierarchy = list(map(elems_to_text, parts)) self.period.task_hierarchies.append(task_hierarchy) else: if self.period and isinstance(elem, pf.ListItem): self.period.items.append(elem)
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 action(elem, doc): if isinstance(elem, pf.Header) and elem.level == 1: if doc.current_heading and len(doc.section_content) > 10: output_section(doc) doc.section_content = [] doc.current_heading = pf.stringify(elem) doc.seq = str(float(doc.seq) + float(doc.seq_inc)) doc.current_heading = pf.stringify(elem) else: doc.current_heading = pf.stringify(elem) doc.section_content = [] else: doc.section_content.append(pf.stringify(elem))
def action(elem, doc): global collect_objectives if isinstance(elem, pf.Header) and pf.stringify(elem) == "Learning objectives": collect_objectives = True # we're explicitly expecting learning objectives to be `pf.Span` that # appear after the "Learning objectives" header. if isinstance(elem, pf.Span) and collect_objectives: # We're interested in collecting the `pf.ListItem` so that we can # stuff them into a `pf.OrderedList` later. When we write them out # in the collection at the end, we **do not** want them to have any # of the metadata, so make a deep copy of the `pf.ListItem` and its # children, then strip any metadata from that copy listitem = pfp.copy_listitem(elem.ancestor(2)) def delete_span_meta(elem, doc): if isinstance(elem, pf.Span) and "outcomes" in elem.attributes: elem.identifier = "" del elem.attributes["outcomes"] listitem.walk(delete_span_meta) # Learning objectives that have an ID should also have an "outcomes" # attribute, we'll use that to put the learning objective into a dict outcomes = elem.attributes["outcomes"].split() for outcome in outcomes: objectives[outcome].append(listitem)
def parse_citations(elem, doc): if isinstance(elem, pf.Link) and "table_note" not in elem.classes: text = pf.stringify(elem) if link_is_cite.search(text): return to_cite(text, doc) elif ( hasattr(elem, "text") and len(elem.text) > 0 and not isinstance(elem.parent, pf.Link) and not isinstance(elem, (pf.Code, pf.CodeBlock)) ): text = elem.text if str_is_cite.search(text): content = [] for s, c in utils.re_split(str_is_cite, text): content.append(s) if c: content.append(to_cite(c, doc)) return pf.Span(*content)
def action(elem, doc): if isinstance(elem, pf.Table): table = elem.caption.list[0].text headers = list(map(pf.stringify, elem.header.content)) headers_sql = ",".join(headers) placeholders_sql = ",".join(["?"] * len(headers)) doc.sql.execute(f"create table {table} ({headers_sql})") for row in elem.content: row_values = [pf.stringify(cell) for cell in row.content] doc.sql.execute( f"insert into {table}({headers_sql}) values ({placeholders_sql})", row_values, ) return [] if isinstance(elem, pf.CodeBlock): if elem.classes != ["pandocsql"]: return table = doc.sql.execute(elem.text) headers = [col[0] for col in table.description] rows = list(table) def c(t): return pf.TableCell(pf.Para(pf.Str(str(t)))) def r(cs): return pf.TableRow(*list(map(c, cs))) return [pf.Table(*list(map(r, rows)), header=r(headers))]
def is_include_line(elem): # value: return 0 for false, 1 for include file, 2 for include header value = 0 config = {} name = None if not hasattr(elem, "_content"): value = 0 elif (len(elem.content) not in [3, 4]) \ or (not isinstance(elem.content[0], pf.Str)) \ or (elem.content[0].text not in ['!include', '$include', '!include-header', '$include-header']) \ or (not isinstance(elem.content[-2], pf.Space)) \ or (len(elem.content) == 4 and not isinstance(elem.content[1], pf.Code)): value = 0 else: if elem.content[0].text in ['!include', '$include']: # include file value = 1 else: # include header value = 2 # filename fn = elem.content[-1] if isinstance(fn, pf.Quoted): # Convert list to args of Para name = pf.stringify(pf.Para(*fn.content), newlines=False) else: name = fn.text # config if len(elem.content) == 4: config = parseConfig(elem.content[1].text) return value, name, config
def gl_latex(e, doc): label = pf.stringify(e).lower() name = e.attributes.get('name') description = e.attributes.get('description') if label and name and description: values = { 'label': label, 'name': name, 'description': pf.convert_text(description, extra_args=['--biblatex'], input_format='markdown', output_format='latex'), 'text': e.attributes.get('text'), 'plural': e.attributes.get('plural'), 'uppercase': 'up' in e.classes } doc.glsentries[label] = values tex = USE_TERM.render(label=label, plural='pl' in e.classes, uppercase='up' in e.classes) return pf.RawInline(tex, format='latex')
def action(elem, doc): if isinstance(elem, panflute.Header): if elem.level == 1: doc.metadata["title"] = panflute.stringify(elem) return [] else: elem.level -= 1
def render_links(elem, doc): if isinstance(elem, pf.Link) and doc.format == "latex": url = elem.url ref = _get_ref(url, doc) head = "\\myref{{{ref}}}" tail = "" if ref: if elem.content: head = "\\myref{{{ref}}}[" tail = "]" return [ pf.RawInline(head.format(ref=ref), format="latex"), *elem.content, pf.RawInline(tail, format="latex"), ] alt_url = pf.stringify(elem).strip() ref = _get_ref(alt_url, doc) if ref: return [ pf.RawInline(head.format(ref=ref), format="latex"), pf.RawInline(tail, format="latex"), ] logger.debug(url)
def action(elem, doc): if isinstance(elem, panflute.Header): if elem.level == 1: text = panflute.stringify(elem) rainbow_header = '<h1 id="' + text + '">' + rainbowify( text) + '</h1>' return (panflute.RawBlock(rainbow_header))
def test_handle_mtest_section_title(self): """MTest""" doc = pf.Doc(metadata={"lang": "en"}) elem_content = r""" \begin{MTest}{Abschlusstest Kapitel \arabic{section}} Foo bar \end{MXContent}""" doc.content.extend([pf.RawBlock(elem_content, format="latex")]) elem = doc.content[0] # this sets up elem.parent ret = self.environments.handle_mtest( "Foo bar", [r"Abschlusstest Kapitel \arabic{section}"], elem) self.assertEqual(len(ret), 2) header = ret[0] self.assertIsInstance(header, pf.Header) self.assertEqual(pf.stringify(header), "Abschlusstest") para = ret[1] self.assertIsInstance(para.content[0], pf.Str) self.assertEqual(para.content[0].text, "Foo") self.assertIsInstance(para.content[1], pf.Space) self.assertIsInstance(para.content[2], pf.Str) self.assertEqual(para.content[2].text, "bar")
def action(elem, doc): if isinstance(elem, pf.Para) and is_include_line(elem): raw_path = pf.stringify(elem, newlines=False).split(maxsplit=1)[1].strip() if raw_path[0:2] == '@/': raw_path = './' + raw_path[2:] else: raise ValueError(f'[code import] {raw_path} should begin with @/') rawPathRegexp = r'^(.+(?:\.([a-z]+)))(?:#([\w-]+))?(?: ?({\d+(?:[,-]\d+)?}))?$' search = re.search(rawPathRegexp, raw_path) if search is None: raise ValueError(f'[code import] invalid parameter {raw_path}') (filepath, extension, region_name, meta) = search.groups() if not path.isfile(filepath): raise ValueError(f'[code import] file not found: {filepath}') basename = path.basename(filepath).split('.') extension = basename[-1] subextension = '' if len(basename) > 2: subextension = basename[-2] with open(filepath) as f: raw = f.read() region = extract_region(raw, region_name, filepath) return pf.CodeBlock(dedent(region), '', [get_code_type(extension, subextension)])
def h_latex_cite(e, doc): """Handle cite. Just feed it to a cite command and hope for the best. This is pretty hacky and should be changed.""" if not isinstance(e, pf.Cite): return None logstring(f"I am {e} and my parent is {e.parent}", doc) if isinstance(e.parent, pf.Citation): return [] s = pf.stringify(e).replace("[", "").replace("]", "").replace("@", "").strip() if s[-1] == ",": s = s[:-1] if doc.format == "latex": tex = f"\\cite{{{s}}}" return pf.RawInline(tex, format="latex") if doc.format == "html": html = "" for a in s.split(','): a = a.strip() if a in doc.bibentries: D = doc.bibentries[a] authors, title, year = D["author"], D["title"], D["year"] authors = bibtexparser.customization.getnames([ i.strip() for i in authors.replace('\n', ' ').split(" and ") ]) author = ", ".join([a.split(',')[0] for a in authors]) T_encode = latex_accents.LaTexAccents_to_UTF8() author = T_encode.decode_Tex_Accents(author) title = T_encode.decode_Tex_Accents(title) q = f'{author} {title}'.replace(' ', '+') html += rf' (<a href="https://scholar.google.com/scholar?hl=en&q={q}" target="_blank">{author}, {year}</a>) ' if not html: html = s return pf.RawInline(html, format="html")
def wrapfig(elem, doc): attrs = elem.attributes caption = ''.join(pf.stringify(e) for e in elem.content) # caption = elem.content # pf.debug(caption) target = elem.url fmt = "latex" # Strip tag size = attrs.get("width") if "lineheight" in attrs: lineheight = attrs.lineheight latex_begin = r"\begin{wrapfigure}[" + lineheight + "]{l}{" + size + "}" else: latex_begin = r"\begin{wrapfigure}{l}{" + size + "}" if len(caption) > 0: latex_fig = r"\centering\includegraphics{" + target + "}\caption{" latex_end = r"}\end{wrapfigure}" return pf.RawInline(latex_begin + latex_fig + caption + latex_end, fmt) # return list(pf.RawInline(latex_begin + latex_fig), caption, pf.RawInline(latex_end)) else: latex_fig = r"\centering\includegraphics{" + target[0] + "}" latex_end = r"\end{wrapfigure}" return pf.RawInline(latex_begin + latex_fig + latex_end, fmt)
def match_example_header(element): if not isinstance(element, panflute.Header): return None try: return example.search(panflute.stringify(element)).group(0) except Exception: return None
def match_rule_id(element): if not isinstance(element, panflute.Header): return None try: return rule_id.search(panflute.stringify(element)).group(1) except Exception: return None
def create_example(number, elem): '''Create an example LaTeX block.''' examples = [] # For each example in the list, extract each part of the example. # Then substitute the parts into the example template. for example in elem.content: content = [line.strip() for line in pf.stringify(example).split(',')] parts = {} parts['target'] = content[0] # Format the gloss in small caps parts['gloss'] = ' '.join( ['\\textsc{' + x + '}' for x in content[1].split(' ')]) parts['native'] = content[2] example_LaTeX = string.Template(EXAMPLE_TEMPLATE).substitute(parts) examples.append(example_LaTeX) # Substitute examples into the block div. block_settings = {'examples': '\n'.join(examples)} example_block = string.Template(EXAMPLE_BLOCK_TEMPLATE) return pf.RawBlock(example_block.substitute(block_settings), format='latex')
def test_forced_first_use() -> None: """Check the two usages work as expected Based on the rules, after the first use that counts, the default for subsequent uses should be the short version. """ keys = [] for type in ("long", ""): key = Key() key.value = value key.type = type key.plural = False key.capitalize = False key.count = True keys.append(key) text = meta + "- " + "\n- ".join(str(k) for k in keys) doc = panflute.convert_text(text, standalone=True) acronyms = { k: panflute.stringify(doc.metadata["acronyms"]["mwe"][k]) for k in doc.metadata["acronyms"]["mwe"].content } result = panflute.convert_text(text, output_format="markdown", extra_args=["-F", "pandoc-acro"]) lines = (line for line in result.splitlines()) assert "- " + acronyms["long"] == next(lines).rstrip() assert "- " + acronyms["short"] == next(lines).rstrip()
def test_plain() -> None: """Check the results of the starred plain text output""" text, keys = generate() doc = panflute.convert_text(text, standalone=True) acronyms = { k: panflute.stringify(doc.metadata["acronyms"]["mwe"][k]) for k in doc.metadata["acronyms"]["mwe"].content } result = panflute.convert_text(text, output_format="markdown", extra_args=["-F", "pandoc-acro"]) lines = (line for line in result.splitlines()) first = True for key in keys: if key.type == "long": expected = acronyms["long"] + ("s" if key.plural else "") elif key.type == "short": expected = acronyms["short"] + ("s" if key.plural else "") elif key.type == "full": expected = acronyms["long"] + ("s" if key.plural else "") \ + " (" + acronyms["short"] + ")" else: if first: expected = acronyms["long"] + ("s" if key.plural else "") \ + " (" + acronyms["short"] + ")" else: expected = acronyms["short"] + ("s" if key.plural else "") if key.count: first = False head, *tail = (s for s in expected) expected = (head.upper() if key.capitalize else head) + "".join(tail) assert "- " + expected == next(lines).rstrip()
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 test_quotes_129(): #pf https://github.com/sergiocorreia/panflute/issues/129 text = [pf.Str("Some"), pf.Space, pf.Str("quoted text")] quoted_text = pf.Quoted(*text) para = pf.Para(quoted_text) output = pf.stringify(para, False) assert output == '"Some quoted text"'
def h_latex_cite(e,doc): """Handle cite. Just feed it to a cite command and hope for the best. This is pretty hacky and should be changed.""" if not isinstance(e,pf.Cite): return None logstring(f"I am {e} and my parent is {e.parent}", doc) if isinstance(e.parent,pf.Citation): return [] s = pf.stringify(e).replace("[","").replace("]","").replace("@","").strip() if s[-1]==",": s = s[:-1] if doc.format=="latex": tex = f"\\cite{{{s}}}" return pf.RawInline(tex,format="latex") if doc.format=="html": html = "" for a in s.split(','): a = a.strip() if a in doc.bibentries: D = doc.bibentries[a] authors,title,year = D["author"],D["title"],D["year"] authors = bibtexparser.customization.getnames([i.strip() for i in authors.replace('\n', ' ').split(" and ")]) author = ", ".join([a.split(',')[0] for a in authors]) T_encode = latex_accents.LaTexAccents_to_UTF8() author = T_encode.decode_Tex_Accents(author) title = T_encode.decode_Tex_Accents(title) q = f'{author} {title}'.replace(' ','+') html += rf' (<a href="https://scholar.google.com/scholar?hl=en&q={q}" target="_blank">{author}, {year}</a>) ' if not html: html = s return pf.RawInline(html, format="html")
def elem2cite(elem, doc): """Safely extract a Cite element and convert to RawInline""" if isinstance(elem, pf.Cite): citation = elem.content[0] tex = pf.RawInline(str2cite(pf.stringify(citation)), format="latex") # pf.debug(tex) return tex
def h_add_search(e,doc): """Add text to search index""" if isinstance(e,pf.Header): doc.searchtext += " "+pf.stringify(e) return None if isinstance(e,pf.Div): t = e.attributes.get("title","") doc.searchtext += " " + t return None
def action(elem, doc): if isinstance(elem, pf.Link) and elem.url.startswith('wiki://'): title = pf.stringify(elem).strip() baseurl = 'https://en.wikipedia.org/w/api.php' query = {'format': 'json', 'action': 'query', 'prop': 'extracts', 'explaintext': '', 'titles': title} r = requests.get(baseurl, params=query) data = r.json() extract = list(data['query']['pages'].values())[0]['extract'] extract = extract.split('.', maxsplit=1)[0] return pf.RawInline(extract)
def action(e, doc): if isinstance(e, pf.Link) and e.url == 'acro': acronym = pf.stringify(e) definition = e.title # Only update dictionary if definition is not empty if definition: doc.acronyms[acronym] = definition if doc.format == 'latex': tex = '\gls{{}}'.format(acronym) tex = TEMPLATE_GLS.safe_substitute(acronym=acronym) return pf.RawInline(tex, format='latex')
def getgitlink(elem_in, elem_out, linkstr=''): for i in elem_in.content: istr = pf.stringify(i) if linkstr or istr[:2] == '[[': linkstr += istr # is the link complete? if linkstr[-2:] == ']]': link = linkstr[2:-2].split('|') elem_out.content.append(pf.Link(pf.Str(link[0]), url=link[-1] + '.html')) linkstr = '' else: elem_out.content.append(i) return linkstr
def h_link_ref(e,doc): """Change links of class .ref to latex labels""" if not isinstance(e,pf.Link): return None if not e.classes: return None label = "".join(pf.stringify(a) for a in e.content) if label[0]=="#": label = label[1:] if not label: label="???" if doc.format == 'latex': if 'ref' in e.classes: return pf.RawInline(f"\\cref{{{label}}}", format = "latex") if 'eqref' in e.classes: return pf.RawInline(f"\\eqref{{{label}}}", format = "latex") if doc.format =='html': name, link = getlabel(label,doc) if 'ref' in e.classes or 'eqref' in e.classes: return pf.RawInline(fr"<a href='{link}'>{name}</a>",format='html') return e
def inner_test_stringify(input_fn, output_fn): output_txt_benchmark = './tests/temp_benchmark.txt' output_txt_panflute = './tests/temp_panflute.txt' print('Testing stringify()') with open(input_fn, encoding='utf-8') as f: doc = pf.load(f) ans = pf.stringify(doc) #print(repr(ans).encode('utf-8')) with open(output_txt_panflute, encoding='utf-8', mode='w') as f: f.write(ans) with open(input_fn, encoding='utf-8') as f: doc = json.load(f) ans = pandocfilters.stringify(doc) with open(output_txt_benchmark, encoding='utf-8', mode='w') as f: f.write(ans)
def pnum(): num = pf.stringify(elem) if not num.replace('.', '').isdigit(): raise SyntaxError('`pnum` must be integrals with optional `.` separators.') if '.' in num: num = '({})'.format(num) if doc.format == 'latex': return pf.RawInline('\\pnum{{{}}}'.format(num), 'latex') if doc.format == 'html': return pf.RawInline( '<div class="marginalizedparent">' \ '<a class="marginalized">{}</a>' \ '</div>'.format(num), 'html') return pf.Superscript(pf.Str(num))
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 tonytable(table, doc): """ Tony Tables: CodeBlocks are the first-class entities that get added to the table. The last (if any) header leading upto a CodeBlock is the header that gets attached to the table cell with the CodeBlock. Each CodeBlock entry is pushed onto the current row. Horizontal rule is used to move to the next row. # Example ::: tonytable ### Before ```cpp std::visit([&](auto&& x) { strm << "got auto: " << x; }, v); ``` ### After ```cpp inspect (v) { <auto> x: strm << "got auto: " << x; } ``` --- ```cpp std::visit([&](auto&& x) { using X = std::remove_cvref_t<decltype(x)>; if constexpr (C1<X>()) { strm << "got C1: " << x; } else if constexpr (C2<X>()) { strm << "got C2: " << x; } }, v); ``` ```cpp inspect (v) { <C1> c1: strm << "got C1: " << c1; <C2> c2: strm << "got C2: " << c2; } ``` ::: # Generates +------------------------------------------------+-------------------------------------------------+ | __Before__ | __After__ | +------------------------------------------------+-------------------------------------------------+ | ```cpp | ```cpp | | std::visit([&](auto&& x) { | inspect (v) { | | strm << "got auto: " << x; | <auto> x: strm << "got auto: " << x; | | }, v); | } | | | ``` | +------------------------------------------------+-------------------------------------------------+ | std::visit([&](auto&& x) { | ```cpp | | using X = std::remove_cvref_t<decltype(x)>; | inspect (v) { | | if constexpr (C1<X>()) { | <C1> c1: strm << "got C1: " << c1; | | strm << "got C1: " << x; | <C2> c2: strm << "got C2: " << c2; | | } else if constexpr (C2<X>()) { | } | | strm << "got C2: " << x; | ``` | | } | | | }, v); | | +------------------------------------------------+-------------------------------------------------+ """ 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 build_code(elem, format): if (format != 'gfm'): return elem lang = ' lang="{}"'.format(elem.classes[0]) if elem.classes else '' code = html.escape(elem.text) return pf.RawBlock('\n\n<pre{lang}>\n{code}\n</pre>'.format(lang=lang, code=code)) def build_row(elems): return pf.TableRow(*[pf.TableCell(elem) for elem in elems]) if not isinstance(table, pf.Div) or 'tonytable' not in table.classes: return None rows = [] kwargs = {} headers = [] widths = [] examples = [] header = pf.Null() width = 0 table.content.append(pf.HorizontalRule()) for elem in table.content: if isinstance(elem, pf.Header): header, width = build_header(elem) elif isinstance(elem, pf.CodeBlock): headers.append(header) widths.append(width) header = pf.Null() width = 0 examples.append(build_code(elem, doc.format)) elif isinstance(elem, pf.HorizontalRule) and examples: if not all(isinstance(header, pf.Null) for header in headers): rows.append(build_row(headers)) if 'width' not in kwargs: kwargs['width'] = widths rows.append(build_row(examples)) headers = [] widths = [] examples = [] else: pf.debug("[Warning] The following is ignored by a Tony Table:", pf.stringify(elem)) return pf.Table(*rows, **kwargs)
def get_filename(elem): fn = pf.stringify(elem, newlines=False).split(maxsplit=1)[1] if not os.path.splitext(fn)[1]: fn += '.md' return fn
def h_paragraph(e,doc): """Make lines starting with bold become paragraphs.""" if not isinstance(e,pf.Strong) or doc.format != 'latex': return None if isinstance(e.parent , pf.Para) and e.parent.parent == doc and e.parent.content.index(e)==0: return pf.RawInline(r"\paragraph{"+pf.stringify(e)+"}",format='latex') return None
f.write('\n') print(' - Done!') print(' - Comparing...') with open(input_fn, encoding='utf-8') as f: input_data = f.read() with open(output_fn, encoding='utf-8') as f: output_data = f.read() print(' - Are both files the same?') print(' - Length:', len(input_data) == len(output_data), len(input_data), len(output_data)) print(' - Content:', input_data == output_data) assert input_data == output_data print('Testing stringify()') with open(input_fn, encoding='utf-8') as f: doc = pf.load(f) ans = pf.stringify(doc) #print(repr(ans).encode('utf-8')) with open(output_txt_panflute, encoding='utf-8', mode='w') as f: f.write(ans) import pandocfilters, json with open(input_fn, encoding='utf-8') as f: doc = json.load(f) ans = pandocfilters.stringify(doc) with open(output_txt_benchmark, encoding='utf-8', mode='w') as f: f.write(ans)
def h_emph(e,doc): if isinstance(e,pf.Emph) or isinstance(e,pf.Strong): doc.searchtext += " " + pf.stringify(e) return None
def action(elem, doc): if isinstance(elem, pf.Superscript) and doc.format == 'markdown': text = '<sup>' + pf.stringify(elem) + '</sup>' return pf.RawInline(text)
def labelref(e,doc): if e.identifier: return e.identifier regex = re.compile(r'[^a-zA-Z\-]') t = regex.sub('', pf.stringify(e).replace(' ','-'))[:25] if t: return t return "temp"