def break_newpara(elem, doc): if type(elem) == pf.Para: splitParas = [] singleParaElems = [] needs_to_be_split = False for segment in elem.content: if isinstance(segment, pf.LineBreak): needs_to_be_split = True if needs_to_be_split: for segment in elem.content: if isinstance(segment, pf.LineBreak): splitParas.append(pf.Para(*singleParaElems)) singleParaElems = [] else: singleParaElems.append(segment) splitParas.append(pf.Para(*singleParaElems)) # pf.debug("Before--------------------") # pf.debug(elem.parent.content) # pf.debug("Before--------------------") for i, item in enumerate(splitParas, elem.index + 1): elem.parent.content.insert(i, item) # pf.debug("Afer--------------------") # pf.debug(elem.parent.content) # pf.debug("Afer--------------------") #Delete the original Para return [] else: # pf.debug("no softbreak or linebreak found") # pf.debug(elem) return elem
def action(elem, doc): """The panflute filter main method, called once per element.""" global shift, workaround_level_overflow, workaround_level_underflow if isinstance(elem, pf.Header): level_old = elem.level level_new = level_old + shift if level_new > MAX_LEVEL: eprint( "After shifting header levels by %d, '%s' would be on level %d, " "which is above the max level %d." % (shift, elem.identifier, level_new, MAX_LEVEL)) if workaround_level_overflow: eprint( "Thus we convert it to an emphazised text paragraph instead." ) if level_new == (MAX_LEVEL + 1): elem = pf.Para(pf.Strong(*elem.content)) else: elem = pf.Para(pf.Emph(*elem.content)) else: raise OverflowError() elif level_new < MIN_LEVEL: eprint( "After shifting header levels by %d, '%s' would be on level %d, " "which is below the min level %d." % (shift, elem.identifier, level_new, MIN_LEVEL)) if workaround_level_underflow: eprint("Thus we leave it at the min level.") else: raise OverflowError() else: elem.level = level_new return elem
def action(elem, doc): if isinstance(elem, panflute.CodeBlock) and 'graphviz' in elem.classes: svg = check_output(GRAPHVIZ_COMMAND, input=elem.text.encode('utf8')) b64data = b64encode(svg).decode('utf8') url = 'data:image/svg+xml;base64,{}'.format(b64data) return panflute.Para(panflute.Image(url=url)) elif isinstance(elem, panflute.CodeBlock) and 'uml' in elem.classes: svg = check_output(PLANTUML_COMMAND, input=elem.text.encode('utf8')) b64data = b64encode(svg).decode('utf8') url = 'data:image/svg+xml;base64,{}'.format(b64data) return panflute.Para(panflute.Image(url=url))
def test_merge_root_multiple_nodes(self): doc = common.MockDoc('html') filename = os.path.join('linguafilter', 'test_lexicon.csv') attributes = {'file': filename, 'merge_root': 'foo'} div = pf.Div(pf.Para(pf.Str('{field1}')), pf.Para(pf.Str('{field2}')), attributes=attributes, classes=['lexicon']) with self.assertRaisesRegexp( Exception, 'if merge_root is specified, there can be only one node under the lexicon div' ): lexicon.parse(div, doc)
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 fenced_action(options, data, element, doc): modalid = options.get('id', 'modal1') title = options.get('title') closebtn = options.get('closebtn', True) size = options.get('size', 'default') size2class = { 'default': None, 'small' : 'modal-sm', 'sm' : 'modal-sm', 'large' : 'modal-lg', 'lg' : 'modal-lg', 'xlarge' : 'modal-xl', 'xl' : 'modal-xl', } components = [] if title: modal_header1 = pf.Header(pf.Str(title), classes=['modal-title'], level=5, identifier=modalid + 'Title') modal_header2 = pf.Div( pf.Div(pf.Para(pf.Str('x')), attributes = {'aria-hidden': "true"}), classes = ['close', 'button'], attributes = { 'type': 'button', 'data-dismiss': 'modal', 'aria-label': 'Close' }) components.append(pf.Div(modal_header1, modal_header2, classes = ['modal-header'])) components.append(pf.Div(*data, classes = ['modal-body'])) if closebtn: components.append(pf.Div( pf.Div(pf.Para(pf.Str('Close')), classes = ['button', 'btn', 'btn-secondary'], attributes = { 'type': 'button', 'data-dismiss': 'modal', }), classes = ['modal-footer'] )) modal_content = pf.Div(*components, classes = ['modal-content']) mainclasses = ['modal-dialog', 'modal-dialog-centered', 'modal-dialog-scrollable'] sizeclass = size2class.get(size) if sizeclass: mainclasses.append(sizeclass) model_dialog = pf.Div(modal_content, classes = mainclasses, attributes = {'role': 'document'}) return pf.Div(model_dialog, classes = ['modal', 'fade'], identifier = modalid, attributes = { 'tabindex' : '-1', 'role' : 'dialog', 'aria-labelledby': modalid + 'Title', 'aria-hidden' : "true" })
def test_remove_empty_paragraphs(self): """It should remove empty paras in document""" doc = pf.Doc( pf.Para(pf.Str("Foo"), pf.Space(), pf.Str("Bar")), pf.Para(), pf.Para(pf.Str("Bar"), pf.Space(), pf.Str("Baz")), ) remove_empty_paragraphs(doc) self.assertEqual(len(doc.content), 2) para1 = doc.content[0] self.assertEqual(para1.content[0].text, "Foo") self.assertEqual(para1.content[2].text, "Bar") para2 = doc.content[1] self.assertEqual(para2.content[0].text, "Bar") self.assertEqual(para2.content[2].text, "Baz")
def test_to_emph_simple(self): ast = deepcopy(self.simple) ast.walk(self.to_type) res = convert_text(ast, input_format="panflute", output_format="native") ref = pf.Para(self.ElementType(pf.Str("a")), pf.Space, self.ElementType(pf.Str("b"))) ref_native = convert_text(ref, input_format="panflute", output_format="native") assert res == ref_native
def test_unknown_raw_inline_command(self): """filter() handles unknown RawInline command""" elem_unkown_cmd = pf.RawInline(r"\ThisCommandDoesNotExist", format="latex") para = pf.Para(elem_unkown_cmd) ret = self._filter_elem([para], elem_unkown_cmd) self.assertIsNone(ret)
def test_rawinline(self): """filter() handles RawInline element""" glqq = pf.RawInline(r"\glqq", format="latex") elem = pf.Para(glqq) ret = self._filter_elem([elem], glqq) self.assertIsInstance(ret, pf.Str) self.assertEqual(ret.text, r"„")
def test_cancel_emph(self): ast = deepcopy(self.ast_double_type) ast.walk(self.cancel_repeated_type) res = convert_text(ast, input_format="panflute", output_format="native") ref = pf.Para(pf.Str("a"), pf.Space, self.ElementType(pf.Str("b"))) ref_native = convert_text(ref, input_format="panflute", output_format="native") assert res == ref_native
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 test_merge_emph2(self): ast = deepcopy(self.ast2) ast.walk(self.merge_consecutive_type) res = convert_text(ast, input_format="panflute", output_format="native") ref = pf.Para(self.ElementType(pf.Str("a"), pf.Space, pf.Str("b"))) ref_native = convert_text(ref, input_format="panflute", output_format="native") assert res == ref_native
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 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 div2env(elem, doc, debug=False): if type(elem) == panflute.Div and doc.format in ["latex", "beamer"]: attr = getattr(elem, "attributes", None) if not attr: return env = attr.get("data-environment", "").split(",") if not env[0]: # This is not marked as an environment return # Convert the positional arguments to the proper \LaTeX format args = attr.get("data-environment-args", "").split(",") args = "{{{0}}}".format("}{".join(args)) if args[0] else "" # Enclose the keyword arguments in '[...]' opt = attr.get("data-environment-keyword", "") opt = "[{0}]".format(opt) if opt else "" begin = panflute.RawInline( "\\begin{{{0}}}{1}{2}%\n".format(env[0], opt, args), format="latex" ) end = panflute.RawInline( "%\n\\end{{{0}}}".format(env[0]), format="latex" ) if debug: panflute.debug("begin = '{0!s}'".format(begin)) panflute.debug("end = '{0!s}'".format(end)) if not getattr(elem.content[0], "content", False): begin = panflute.RawBlock(begin.text, format="latex") end = panflute.RawBlock(end.text, format="latex") elem = panflute.Div(begin, *elem.content, end) else: elem.content[0] = panflute.Para(begin, *elem.content[0].content) elem.content[-1] = panflute.Para(*elem.content[-1].content, end) if debug: panflute.debug("content = '{0!s}'".format(elem.content)) return elem return
def setUp(self): self.simple = pf.Para(pf.Str("a"), pf.Space, pf.Str("b")) self.ast = pf.Para(self.ElementType(pf.Str("a")), self.ElementType(pf.Str("b"))) self.ast2 = pf.Para(self.ElementType(pf.Str("a")), pf.Space, self.ElementType(pf.Str("b"))) self.ast_double_type = pf.Para( self.ElementType(self.ElementType(pf.Str("a"))), pf.Space, self.ElementType(pf.Str("b")) ) self.combined = pf.Para( self.ElementType(pf.Str("a")), self.ElementType(pf.Str("b")), pf.Space, pf.Str("c"), pf.Str("d"), ) self.to_type = partial(to_type, ElementType=self.ElementType) self.cancel_repeated_type = partial(cancel_repeated_type, ElementType=self.ElementType) self.merge_consecutive_type = partial(merge_consecutive_type, ElementType=self.ElementType)
def test_uxid_para(self): annot = pf.Div( identifier="{}-foo".format(SITE_UXID_PREFIX), classes=(SITE_UXID_PREFIX, ), ) para = pf.Para(pf.Str("bar")) identifier = extract_identifier([annot, para]) self.assertEqual(identifier, "foo")
def finalize(doc): reader_options = pf.load_reader_options() definitions = [] for k, v in reader_options.items(): term = [pf.Str(k)] definition = pf.Definition(pf.Para(pf.Str(repr(v)))) definitions.append(pf.DefinitionItem(term, [definition])) doc.content.append(pf.DefinitionList(*definitions))
def finalize(doc): try: filepath = doc.get_metadata('filepath') label = 'Link' except: filepath = '' label = 'No link' doc.content.insert(0, pf.Para(pf.Link(pf.Str(label), url=filepath))) return doc
def test_handle_mugraphicssolo_inline(self): """MUGraphicsSolo command (inline)""" content = r"\MUGraphicsSolo{foo.jpg}{width=0.3\linewidth}{}" doc = pf.Doc(pf.Para(pf.RawInline(content, format="latex"))) elem = doc.content[0].content[0] # this sets up elem.parent elem_args = ["foo.jpg", r"width=0.3\linewidth", ""] ret = self.commands.handle_mugraphicssolo(elem_args, elem) self.assertIsInstance(ret, pf.Image) self.assertEqual(ret.url, "foo.jpg")
def prepare(doc): """The panflute filter init method.""" global doc_path, id_prefix doc_path = get_arg(doc, 'll_doc_path') id_prefix = linearize_link_path(doc_path) # Add reference for the whole file at the top if id_prefix != '': # empty here, because the id_prefix will be added later in action() doc.content.insert(0, pf.Para(pf.RawInline('<a name=""/>')))
def break_plain(plain): """ Break a Plain element with SoftBreaks into a list of Para elements. """ is_break = lambda el: isinstance(el, pf.SoftBreak) content = list(plain.content) # group sequences of non-breaks together as paragraphs and throw out breaks return [pf.Para(*list(g)) for k, g in groupby(content, is_break) if not k]
def test_all_emph_together(self): ast = deepcopy(self.combined) ast.walk(self.to_type) ast.walk(self.cancel_repeated_type) ast.walk(self.merge_consecutive_type) res = convert_text(ast, input_format="panflute", output_format="native") ref = pf.Para(pf.Str("a"), pf.Str("b"), pf.Space, self.ElementType(pf.Str("c"), pf.Str("d"))) ref_native = convert_text(ref, input_format="panflute", output_format="native") assert res == ref_native
def action(elem, doc): if isinstance(elem, pf.Div) and "Question" in elem.classes: question = elem parent = question.parent header = pf.Div(pf.Para(pf.Str("Question {0}".format(doc.latest_question)))) doc.latest_question += 1 parent.content[question.index] = header header.classes.append("QNumber") header.content.append(question) return header
def test_all(): x=pf.Para(pf.Str("a")) y=pf.Para(pf.Str("b")) c1=pf.TableCell(x) c2=pf.TableCell(y) row=pf.TableRow(c1,c2) t1 = pf.Table(row) t2 = pf.Table(row, header=row) print(t1.header) print(t2.header) with io.StringIO() as f: pf.dump(pf.Doc(t1), f) print(f.getvalue()) with io.StringIO() as f: pf.dump(pf.Doc(t2), f) print(f.getvalue())
def graphviz_filter(elem, doc): if isinstance(elem, pf.CodeBlock) and 'graphviz' in elem.classes: code = elem.text graph = pygraphviz.AGraph(string=code) title = graph.graph_attr.pop('label', '') graph.layout() path = gen_randpath() graph.draw(path, prog='dot') para = pf.Para(pf.Image(pf.Str(title), title='fig:', url=path)) return para
def finalize(doc: pf.Doc): #raise Exception("input file %s header_level %s" % (doc.input_file, doc.header_level)) header = pf.Header(pf.Str(doc.meta_title), level=doc.header_level) doc.content.insert(0, header) doc.content.insert(1, pf.Para(pf.Str(doc.description))) del doc.header_level del doc.input_file del doc.meta_title del doc.description del doc.images_path del doc.out_meta
def action(elem, doc): if isinstance(elem, pf.Image): raw_html = re.search( r'<figure>.*?</figure>|<img.*?>', pf.convert_text(pf.Para(elem), input_format='panflute', output_format='html', standalone=True, extra_args=['--self-contained']), re.DOTALL).group(0).replace('\n', '').replace('\r', '') return pf.RawInline(raw_html, format='html')
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) ]