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 start(item): """Extract the start date from the 'issued' field If 'issued' is not a key in the bibliography file, return ``None``. If the month or day is not specified in the field, we set the value to the first month or day as appropriate. """ # Grab the issued field. issued = item.get("issued", None) if not issued: return issued # Now we need to figure out if we have a 'date-parts' or a list # of :class:`panflute.MetaMap`s`. In either case, we only worry # about the first element which is the start date. if isinstance(issued, list): field = lambda x: issued[0].get(x, 1) y, m, d = [int(field(x)) for x in ("year", "month", "day")] else: panflute.debug("start: In the 'date-parts' section!") panflute.debug("start: This will probably break...") parts = issued["date-parts"][0] y = parts[0] m = parts[1] if len(parts) > 1 else 1 d = parts[2] if len(parts) > 2 else 1 return datetime.date(y, m, d)
def action(self, elem, doc): if isinstance(elem, pf.RawBlock): if elem.text == r"\newpage": if (doc.format == "docx"): pf.debug("Page Break") elem = self.pagebreak # elif elem.text == r"\newsection": # if (doc.format == "docx"): # pf.debug("Section Break") # elem = self.sectionbreak # else: # elem = [] elif elem.text == r"\toc": if (doc.format == "docx"): pf.debug("Table of Contents") para = [ pf.Para(pf.Str("Table"), pf.Space(), pf.Str("of"), pf.Space(), pf.Str("Contents")) ] div = pf.Div(*para, attributes={"custom-style": "TOC Heading"}) elem = [div, self.toc] else: elem = [] return elem
def _get_prefix(position, odd=True): if position == "right": if odd: return "\\oddrighttip" return "\\evenrighttip" if position in ["left", ""]: if odd: return "\\oddlefttip" return "\\evenlefttip" if position == "inner": if odd: return "\\oddinnertip" return "\\eveninnertip" if position == "outer": if odd: return "\\oddoutertip" return "\\evenoutertip" debug( "[WARNING] pandoc-latex-tip: " + position + " is not a correct position; using left" ) if odd: return "\\oddlefttip" return "\\evenlefttip"
def action(self, elem, doc): if (doc.format == "docx"): # pf.debug(elem.__class__) if isinstance(elem, pf.BulletList): pf.debug("Bullet list found") bullets = [ doc.get_metadata("bullet-style.1", "Bullet List 1"), doc.get_metadata("bullet-style.2", "Bullet List 2"), doc.get_metadata("bullet-style.3", "Bullet List 3"), ] depth = self.get_depth(elem) # pf.debug(depth) converted = [] for se in elem.content: converted.extend([ pf.Div(e, attributes={"custom-style": bullets[depth]}) for e in se.content if not isinstance(e, pf.BulletList) ]) ret = [] for content in converted: while isinstance(content, pf.Div): content = content.content[0] ret.append(content.parent) # pf.debug(ret) return ret
def _get_prefix(position, odd=True): if position == "right": if odd: return "\\oddrighttip" return "\\evenrighttip" if position in ["left", ""]: if odd: return "\\oddlefttip" return "\\evenlefttip" if position == "inner": if odd: return "\\oddinnertip" return "\\eveninnertip" if position == "outer": if odd: return "\\oddoutertip" return "\\evenoutertip" debug( "[WARNING] pandoc-latex-tip: " + position + " is not a correct position; using left" ) if odd: return "\\oddlefttip" return "\\evenlefttip"
def parse_include(parsed, name, value): """ Extract include information from attributes. Arguments --------- parsed: A dictionnary of attributes name: attribute name value: attribute value """ if name == "include": try: with open(value, "r", encoding="utf-8") as stream: file_content = stream.readlines() parsed["content"] = file_content parsed["include"] = value except IOError: debug("[WARNING] pandoc-codeblock-include: " + value + " not found") except UnicodeDecodeError: debug("[WARNING] pandoc-codeblock-include: file " + value + " is not encoded in utf-8")
def action(self, elem, doc): if doc.format not in ["html", "html5"]: if isinstance(elem, pf.Image): fn = elem.url if fn.endswith(".svg"): counter = hashlib.sha1(fn.encode("utf-8")).hexdigest()[:8] if not os.path.exists(self.dir_to): pf.debug("mkdir") os.mkdir(self.dir_to) basename = "/".join([self.dir_to, str(counter)]) if doc.format in ["latex"]: format = "pdf" else: format = "png" linkto = os.path.abspath(".".join([basename, format])) elem.url = linkto command = "rsvg-convert {} -f {} -o {}".format( fn, format, linkto) sp.Popen(command, shell=True, stdin=sp.PIPE, stdout=sp.PIPE, stderr=sp.PIPE) pf.debug( "[inline] convert a svg file to {}".format(format))
def define_color(environment, definition, key_color, doc=None): """ Get the color Arguments --------- environment: The environment definition: The definition key_color: The color key doc: The pandoc document """ if key_color in definition: color = str(definition[key_color]).lower() if color in doc.x11colors: environment["color"] = color else: # color must be a valid x11 color # See https://www.w3.org/TR/css-color-3/#svg-color debug("[WARNING] pandoc-latex-admonition: " + color + " is not a valid x11 color; using " + environment["color"])
def define_linewidth(environment, definition, key_linewidth): """ Get the line width Arguments --------- environment: The environment definition: The definition key_linewidth: The linewidth key """ if key_linewidth in definition: try: linewidth = int(str(definition[key_linewidth])) if linewidth <= 0: debug("[WARNING] pandoc-latex-admonition: " + "linewidth must be a positivie integer; using " + str(environment["linewidth"])) else: environment["linewidth"] = linewidth except ValueError: debug( "[WARNING] pandoc-latex-admonition: linewidth is not a valid; using " + str(environment["linewidth"]))
def finalize(doc): for heading, content in doc.section_content.items(): try: output_section(doc, heading, content) except Exception as e: pf.debug(e) doc.content = []
def main(): with open("markdown.md", "r") as f: md = f.read() doc = pf.Doc(*pf.convert_text(md), format="docx") pf.debug("doc: {}".format(*doc.content)) ebl = ExtractBulletList() pf.run_filter(ebl.action, doc=doc)
def meta_format_link(category, definition, defined): """ Compute format link for a category. Arguments --------- category: definition: defined: """ if "format-link-classic" in definition: if isinstance(definition["format-link-classic"], MetaInlines): defined[category]["format-link-classic"] = definition[ "format-link-classic"].content # Detach from original parent defined[category]["format-link-classic"].parent = None else: debug( "[WARNING] pandoc-numbering: format-link-classic is not correct for category " + category) if "format-link-title" in definition: if isinstance(definition["format-link-title"], MetaInlines): defined[category]["format-link-title"] = definition[ "format-link-title"].content # Detach from original parent defined[category]["format-link-title"].parent = None else: debug( "[WARNING] pandoc-numbering: format-link-title is not correct for category " + category)
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 create_rule(elem): '''Create a rule HTML block.''' name, definition = pf.stringify(elem)[3:].split(':') substitutions = dict(name=name, definition=definition.strip()) rule_LaTeX = string.Template(RULE_TEMPLATE).substitute(substitutions) pf.debug(rule_LaTeX) return pf.RawBlock(rule_LaTeX, format='latex')
def get_correct_color(color): if color in x11colors(): return color if color: debug('[WARNING] pandoc-latex-color: ' + color + ' is not a correct X11 color; using black') return 'black' return False
def action(elem, doc): pf.debug("action()") if isinstance(elem, pf.Para): # pf.debug(elem) e = elem.walk(make_emph) # pf.debug(e) pf.debug(e.content)
def _create_images(doc, icons, size): # Generate the LaTeX image code images = [] for icon in icons: # Get the image from the App cache folder image_dir = os.path.join( doc.folder, icon["collection"], icon["version"], icon["variant"], icon["color"], ) image = os.path.join(image_dir, icon["extended-name"] + ".png") # Create the image if not existing in the cache try: if not os.path.isfile(image): # Create the image in the cache category = _category( icon["collection"], icon["version"], icon["variant"] ) doc.get_icon_font[category]["font"].export_icon( icon["extended-name"], 512, color=icon["color"], export_dir=image_dir, ) # Add the LaTeX image image = Image( url=image, attributes={"width": size + "pt", "height": size + "pt"} ) if icon["link"] == "": elem = image else: elem = Link(image, url=icon["link"]) images.append( convert_text( Plain(elem), input_format="panflute", output_format="latex" ) ) except TypeError: debug( "[WARNING] pandoc-latex-tip: icon name " + icon["name"] + " does not exist in variant " + icon["variant"] + " for collection " + icon["collection"] + "-" + icon["version"] ) except FileNotFoundError: debug("[WARNING] pandoc-latex-tip: error in generating image") return images
def get(key): '''parsing alignment''' key_lower = key.lower() if key_lower not in align_dict: panflute.debug( "pantable: alignment: invalid character {} found, replaced by the default 'd'." .format(key)) key_lower = 'd' return align_dict[key_lower]
def get(key): '''parsing alignment''' key_lower = key.lower() if key_lower not in LETTER_TO_ALIGN: panflute.debug( f"pantable: alignment: invalid character {key} found, replaced by the default 'd'." ) key_lower = 'd' return LETTER_TO_ALIGN[key_lower]
def action(elem, doc): if isinstance(elem, pf.TableRow): # Exclude table headers (which are not in a list) if elem.index is None: return if elem.next is None: pf.debug(elem) elem.walk(make_emph)
def finalize(doc): global sectionlist inc = 0 for (pos, name) in sectionlist: debug("adding : " + name + " at $pos") doc.content.insert( pos + inc, RawBlock("\n\\section*{" + name + "}\n", format="latex")) inc = inc + 1 debug('Ending "sections.py" ...')
def only_header(elem, doc): if type(elem) is pf.Header: txt = pf.stringify(elem).strip() if elem.level == 1: out = f'\n\n\n## {txt}\n\n' else: indent = ' ' * (elem.level - 2) indent = indent[:-1] + '*' out = f'{indent} {txt}\n' pf.debug(out)
def get_color(elem, doc): """ Get the color of display for an element. """ if "color" in elem.attributes: color = elem.attributes["color"] if color in doc.x11colors: return color debug(f"pandoc-beamer-arrow: color '{color}' is not correct") return ""
def action(self, elem, doc): if isinstance(elem, pf.Div) and "rmnote" in elem.classes: if doc.get_metadata("rmnote", False): pf.debug("remove <div class=\"rmnote\"> ~ </div>") ret = [] else: ret = elem # pf.debug(doc.get_metadata("rmnote", False)) return ret
def render_eps(self): self.eps_filename = ".".join([self.basename, "eps"]) command_stack = [ self.epsconvert, self.svg_filename, "--format=eps", "--output", self.eps_filename ] # pf.debug(" ".join(output)) if not os.path.exists(self.eps_filename): # renderPS.drawToFile(drawing, self.eps) pf.shell(" ".join(command_stack)) else: pf.debug("bypass conversion as output exists:", self.eps_filename)
def render_pdf(self): command_stack = [self.pdfconvert, self.svg_filename] self.pdf_filename = ".".join([str(self.basename), "pdf"]) if self.unix: command_stack.append("--format=pdf") command_stack.append("--output") command_stack.append(self.pdf_filename) # pf.debug(" ".join(output)) if not os.path.exists(self.pdf_filename): subprocess.call(" ".join(command_stack), shell=True) else: pf.debug("bypass conversion as output exists:", self.pdf_filename)
def parse_alignment(alignment_string, n_col): """ `alignment` string is parsed into pandoc format (AlignDefault, etc.). Cases are checked: - if not given, return None (let panflute handle it) - if wrong type - if too long - if invalid characters are given - if too short """ align_dict = { 'l': "AlignLeft", 'c': "AlignCenter", 'r': "AlignRight", 'd': "AlignDefault" } def get(key): '''parsing alignment''' key_lower = key.lower() if key_lower not in align_dict: panflute.debug( "pantable: alignment: invalid character {} found, replaced by the default 'd'." .format(key)) key_lower = 'd' return align_dict[key_lower] # alignment string can be None or empty; return None: set to default by # panflute if not alignment_string: return # test valid type if not isinstance(alignment_string, str): panflute.debug( "pantable: alignment should be a string. Set to default instead.") # return None: set to default by panflute return n = len(alignment_string) if n > n_col: alignment_string = alignment_string[:n_col] panflute.debug("pantable: alignment string is too long, truncated.") alignment = [get(key) for key in alignment_string] # fill up with default if too short if n < n_col: alignment += ["AlignDefault"] * (n_col - n) return alignment
def _get_size(size): try: int_value = int(size) if int_value > 0: size = str(int_value) else: debug( "[WARNING] pandoc-latex-tip: size must be greater than 0; using " + size ) except ValueError: debug("[WARNING] pandoc-latex-tip: size must be a number; using " + size) return size
def get_table_width(options): """ `table-width` set to `1.0` if invalid """ try: table_width = float( fractions.Fraction((options.get('table-width', 1.0)))) assert table_width > 0 except (ValueError, AssertionError, TypeError): table_width = 1.0 panflute.debug("pantable: invalid table-width") return table_width
def _get_size(size): try: int_value = int(size) if int_value > 0: size = str(int_value) else: debug( "[WARNING] pandoc-latex-tip: size must be greater than 0; using " + size ) except ValueError: debug("[WARNING] pandoc-latex-tip: size must be a number; using " + size) return size
def _add_icon(doc, icons, icon): if "name" not in icon: # Bad formed icon debug("[WARNING] pandoc-latex-tip: Bad formed icon") return # Lower the color lower_color = icon["color"].lower() # Convert the color to black if unexisting # pylint: disable=import-outside-toplevel from PIL import ImageColor # type: ignore if lower_color not in ImageColor.colormap: debug( "[WARNING] pandoc-latex-tip: " + lower_color + " is not a correct color name; using black" ) lower_color = "black" # Is the icon correct? try: category = _category(icon["collection"], icon["version"], icon["variant"]) if category in doc.get_icon_font: extended_name = doc.get_icon_font[category]["prefix"] + icon["name"] if extended_name in doc.get_icon_font[category]["font"].css_icons: icons.append( { "name": icon["name"], "extended-name": extended_name, "color": lower_color, "collection": icon["collection"], "version": icon["version"], "variant": icon["variant"], "link": icon["link"], } ) else: debug( "[WARNING] pandoc-latex-tip: " + icon["name"] + " is not a correct icon name" ) else: debug( "[WARNING] pandoc-latex-tip: " + icon["variant"] + " does not exist in version " + icon["version"] ) except FileNotFoundError: debug("[WARNING] pandoc-latex-tip: error in accessing to icons definition")
def _add_icon(doc, icons, icon): if "name" not in icon: # Bad formed icon debug("[WARNING] pandoc-latex-tip: Bad formed icon") return # Lower the color lower_color = icon["color"].lower() # Convert the color to black if unexisting from PIL import ImageColor if lower_color not in ImageColor.colormap: debug( "[WARNING] pandoc-latex-tip: " + lower_color + " is not a correct color name; using black" ) lower_color = "black" # Is the icon correct? try: category = _category(icon["collection"], icon["version"], icon["variant"]) if category in doc.get_icon_font: extended_name = doc.get_icon_font[category]["prefix"] + icon["name"] if extended_name in doc.get_icon_font[category]["font"].css_icons: icons.append( { "name": icon["name"], "extended-name": extended_name, "color": lower_color, "collection": icon["collection"], "version": icon["version"], "variant": icon["variant"], "link": icon["link"], } ) else: debug( "[WARNING] pandoc-latex-tip: " + icon["name"] + " is not a correct icon name" ) else: debug( "[WARNING] pandoc-latex-tip: " + icon["variant"] + " does not exist in version " + icon["version"] ) except FileNotFoundError: debug("[WARNING] pandoc-latex-tip: error in accessing to icons definition")
def prepare(doc): # Create file list fns = doc.get_metadata('files') pf.debug('-' * 64) pf.debug('Expanding pattern:', fns) fns = glob.glob(fns) pf.debug('Files:', fns) fns = [fn.replace('.md', '.html') for fn in fns] doc.fns = fns pf.debug('-' * 64) # Clear all contents except TOC doc.content.list = doc.content.list[:1]
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 _create_images(doc, icons, size): # Generate the LaTeX image code images = [] for icon in icons: # Get the apps dirs from pkg_resources import get_distribution import appdirs folder = appdirs.AppDirs( "pandoc_latex_tip", version=get_distribution("pandoc_latex_tip").version ).user_cache_dir # Get the image from the App cache folder image_dir = os.path.join( folder, icon["collection"], icon["version"], icon["variant"], icon["color"] ) image = os.path.join(image_dir, icon["extended-name"] + ".png") # Create the image if not existing in the cache try: if not os.path.isfile(image): # Create the image in the cache category = _category( icon["collection"], icon["version"], icon["variant"] ) doc.get_icon_font[category]["font"].export_icon( icon["extended-name"], 512, color=icon["color"], export_dir=image_dir, ) # Add the LaTeX image image = Image( url=image, attributes={"width": size + "pt", "height": size + "pt"} ) if icon["link"] == "": elem = image else: elem = Link(image, url=icon["link"]) images.append( convert_text( Plain(elem), input_format="panflute", output_format="latex" ) ) except TypeError: debug( "[WARNING] pandoc-latex-tip: icon name " + icon["name"] + " does not exist in variant " + icon["variant"] + " for collection " + icon["collection"] + "-" + icon["version"] ) except FileNotFoundError: debug("[WARNING] pandoc-latex-tip: error in generating image") return images