Esempio n. 1
0
 def _nonnormative(name):
     _wrap(
         pf.Span(pf.Str('[ '), pf.Emph(pf.Str('{}:'.format(name.title()))),
                 pf.Space),
         pf.Span(pf.Str(' — '),
                 pf.Emph(pf.Str('end {}'.format(name.lower()))),
                 pf.Str(' ]')))
Esempio n. 2
0
def format_names(names):
    first = pf.Emph(pf.Str(split_name(names[0])[0]))

    if len(names) == 0:
        return [first]
    elif len(names) == 1:
        second = pf.Emph(pf.Str(split_name(names[0])[0]))

        return [first, pf.Str("and"), second]
    elif len(names) > 1:
        return [first, pf.Str("et al.")]
Esempio n. 3
0
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
Esempio n. 4
0
def action(elem, doc):
    if isinstance(elem, pf.RawInline):
        if elem.format == 'html':
            if elem.text.startswith('<!--') and elem.text.endswith('-->'):
                comment = elem.text.replace('<!--',
                                            'question: ').replace('-->', '?')
                token = [pf.Emph(pf.Inline(text=comment))]
                elem = token
Esempio n. 5
0
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)
        ]
Esempio n. 6
0
    def test_to_inline(self):
        """It should convert different elements correctly to inline"""

        content1 = pf.Para(pf.Strong(pf.Str("just some text")))
        transformed1 = content1.content[0]

        content2 = pf.Div(
            pf.Para(pf.Strong(pf.Str("again")), pf.Space,
                    pf.Emph(pf.Str("normal"))))

        content3 = pf.Div(
            pf.Para(
                pf.Span(pf.Str("foo"), classes=["1st-span-class"]),
                pf.Span(
                    pf.Strong(pf.Str("Unhandled"), pf.Space,
                              pf.Str("command:")),
                    classes=["2nd-span-class"],
                ),
            ),
            pf.CodeBlock(r"\MLFunctionQuestion{10}{sin(x)}{5}{x}{5}{DS2}"),
            classes=["div-class"],
        )

        self.assertEqual(to_inline(content1), transformed1)

        # test if nested inlining works
        il_content2 = to_inline(content2)
        self.assertIsInstance(il_content2.content[0], pf.Strong)
        self.assertEqual(il_content2.content[0].content[0].text, "again")
        self.assertIsInstance(il_content2.content[2], pf.Emph)

        # test if class conservation works and advanced nesting
        il_content3 = to_inline(content3)
        self.assertEqual(len(il_content3.content), 2)
        self.assertEqual(len(il_content3.content[0].content), 2)
        self.assertEqual(il_content3.classes, ["div-class"])
        self.assertEqual(il_content3.content[0].content[0].classes,
                         ["1st-span-class"])
        self.assertEqual(il_content3.content[0].content[1].classes,
                         ["2nd-span-class"])
Esempio n. 7
0
def comment(elem, doc):
    text = pf.stringify(elem).strip()
    is_relevant = isinstance(elem, pf.Str) and text.startswith("::comment-")

    if is_relevant and text.endswith("-begin"):
        doc.ignore += 1

        # comment text wont be displayed

        if doc.show_comments and doc.ignore == 1:
            logger.debug("Seems like show_comments is set to true?")

            return pf.Emph(pf.Str("Comment:"))

        return []

    if doc.ignore > 0:
        if is_relevant and text.endswith("-end"):
            doc.ignore -= 1
        elif doc.show_comments:
            return None

        return []
Esempio n. 8
0
def test_all():
    md = 'Some *markdown* **text** ~xyz~'
    c_md = pf.convert_text(md)
    b_md = [
        pf.Para(pf.Str("Some"), pf.Space, pf.Emph(pf.Str("markdown")),
                pf.Space, pf.Strong(pf.Str("text")), pf.Space,
                pf.Subscript(pf.Str("xyz")))
    ]

    print("Benchmark MD:")
    print(b_md)
    print("Converted MD:")
    print(c_md)
    assert repr(c_md) == repr(b_md)

    with io.StringIO() as f:
        doc = pf.Doc(*c_md)
        pf.dump(doc, f)
        c_md_dump = f.getvalue()

    with io.StringIO() as f:
        doc = pf.Doc(*b_md)
        pf.dump(doc, f)
        b_md_dump = f.getvalue()

    assert c_md_dump == b_md_dump

    # ----------------------
    print()

    tex = r'Some $x^y$ or $x_n = \sqrt{a + b}$ \textit{a}'
    c_tex = pf.convert_text(tex)
    b_tex = [
        pf.Para(pf.Str("Some"), pf.Space, pf.Math("x^y", format='InlineMath'),
                pf.Space, pf.Str("or"), pf.Space,
                pf.Math(r"x_n = \sqrt{a + b}", format='InlineMath'), pf.Space,
                pf.RawInline(r"\textit{a}", format='tex'))
    ]

    print("Benchmark TEX:")
    print(b_tex)
    print("Converted TEX:")
    print(c_tex)
    assert repr(c_tex) == repr(b_tex)

    with io.StringIO() as f:
        doc = pf.Doc(*c_tex)
        pf.dump(doc, f)
        c_tex_dump = f.getvalue()

    with io.StringIO() as f:
        doc = pf.Doc(*b_tex)
        pf.dump(doc, f)
        b_tex_dump = f.getvalue()

    assert c_tex_dump == b_tex_dump

    print("\nBack and forth conversions... md->json->md")
    md = 'Some *markdown* **text** ~xyz~'
    print("[MD]", md)
    md2json = pf.convert_text(md,
                              input_format='markdown',
                              output_format='json')
    print("[JSON]", md2json)
    md2json2md = pf.convert_text(md2json,
                                 input_format='json',
                                 output_format='markdown')
    print("[MD]", md2json2md)
    assert md == md2json2md

    print("\nBack and forth conversions... md->panflute->md")
    md = 'Some *markdown* **text** ~xyz~'
    print("[MD]", md)
    md2panflute = pf.convert_text(md,
                                  input_format='markdown',
                                  output_format='panflute')
    print("[PANFLUTE]", md2panflute)
    md2panflute2md = pf.convert_text(md2panflute,
                                     input_format='panflute',
                                     output_format='markdown')
    print("[MD]", md2panflute2md)
    assert md == md2panflute2md

    print("\nBack and forth conversions... md->panflute(standalone)->md")
    md = 'Some *markdown* **text** ~xyz~'
    print("[MD]", md)
    md2panflute = pf.convert_text(md,
                                  input_format='markdown',
                                  output_format='panflute',
                                  standalone=True)
    print("[PANFLUTE]", md2panflute)
    md2panflute2md = pf.convert_text(md2panflute,
                                     input_format='panflute',
                                     output_format='markdown')
    print("[MD]", md2panflute2md)
    assert md == md2panflute2md

    print(
        "\nBack and forth conversions... md table -> json(standalone) -> md table"
    )
    md = """lorem

  --- ---
  x   y
  --- ---

ipsum"""
    print("[MD]", repr(md))
    md2json = pf.convert_text(md,
                              input_format='markdown',
                              output_format='json',
                              standalone=True)
    print("[json]", md2json)
    md2json2md = pf.convert_text(md2json,
                                 input_format='json',
                                 output_format='markdown')
    print("[MD]", repr(md2json2md))
    assert md == md2json2md

    print(
        "\nBack and forth conversions... md table -> panflute(standalone) -> md table"
    )
    print("[MD]", repr(md))
    md2panflute = pf.convert_text(md,
                                  input_format='markdown',
                                  output_format='panflute',
                                  standalone=True)
    print("[PANFLUTE]", md2panflute)
    md2panflute2md = pf.convert_text(md2panflute,
                                     input_format='panflute',
                                     output_format='markdown')
    print("[MD]", repr(md2panflute2md))
    assert md == md2panflute2md

    print(
        "\nBack and forth conversions... gfm table (empty) -> json(standalone) -> gfm table (empty)"
    )
    md = """lorem

| x | y |
| - | - |

ipsum"""
    print("[MD]", repr(md))
    md2json = pf.convert_text(md,
                              input_format='gfm',
                              output_format='json',
                              standalone=True)
    print("[json]", md2json)
    md2json2md = pf.convert_text(md2json,
                                 input_format='json',
                                 output_format='gfm')
    print("[MD]", repr(md2json2md))
    assert md == md2json2md

    print(
        "\nBack and forth conversions... gfm table (empty) -> panflute(standalone) -> gfm table (empty)"
    )
    print("[MD]", repr(md))
    md2panflute = pf.convert_text(md,
                                  input_format='gfm',
                                  output_format='panflute',
                                  standalone=True)
    print("[PANFLUTE]", md2panflute)
    md2panflute2md = pf.convert_text(md2panflute,
                                     input_format='panflute',
                                     output_format='gfm')
    print("[MD]", repr(md2panflute2md))
    assert md == md2panflute2md
Esempio n. 9
0
def make_emph(elem, doc):
    if isinstance(elem, pf.Str):
        return pf.Emph(elem)
Esempio n. 10
0
    def convert(self):
        doc = panflute.Doc(
            api_version=(1, 17, 5),
            metadata={
                'pagetitle': self.title,
            },
        )

        doc.content.append(panflute.Header(panflute.Str(self.title)))

        lists = {}
        tables = {}
        table_rows = {}
        table_cells = {}

        for chunk in self._attr_chunks():
            self.logger.debug(chunk)
            container = panflute.Para()
            cdiv = panflute.Div(container)

            # Handle lists
            if 'list_class' in chunk[0]['attrs']:
                lc = chunk[0]['attrs']['list_class']
                check_state = None
                if lc in ['checked', 'unchecked']:
                    check_state = lc
                    lc = 'checklist'
                ld = chunk[0]['attrs']['list_depth']

                # prune any lists that are lower than us, they're finished
                for i in list(lists.keys()):
                    if i > ld:
                        lists.pop(i)

                # non-homogenous list types can be immediately adjacent without
                # ending up merged
                if ld in lists and lists[ld]['class'] != lc:
                    lists.pop(ld)

                # checklists are a special case, they can't contain other lists
                if lc != 'checklist' and lists and lists[1][
                        'class'] == 'checklist':
                    lists = {}

                # make sure any intermediate lists were created, including
                # the top level because boxnotes
                for i in range(1, ld + 1):
                    if i not in lists:
                        lists[i] = self._list(lc, i)
                        if i != ld:
                            lists[i]['pf'].content.append(panflute.ListItem())
                        lp = lists[i]['pf']
                        if lc == 'checklist':
                            lp = panflute.Div(lp, classes=['checklist'])
                        if i == 1:
                            doc.content.append(lp)
                        else:
                            lists[i - 1]['pf'].content[-1].content.append(lp)

                # set the container for the other subchunks
                container = panflute.Plain()
                cdiv.content = [container]
                cdiv.classes.append(lc)
                if check_state:
                    cdiv.classes.append(check_state)
                lists[ld]['pf'].content.append(panflute.ListItem(cdiv))

                if check_state == 'checked':
                    container.content.append(panflute.Str(CHECKED))
                elif check_state == 'unchecked':
                    container.content.append(panflute.Str(UNCHECKED))

            elif 'table_id' in chunk[-1]['attrs']:
                table_id = chunk[-1]['attrs']['table_id']
                row_id = chunk[-1]['attrs']['table_row']
                cell_id = row_id + chunk[-1]['attrs']['table_col']

                if table_id not in tables:
                    # There's some magic in the constructor for panflute tables
                    # that isn't exposed in any other way, so we can't create
                    # the table until we've finished populating the rows.
                    # Instead, use a placeholder div to locate it within the
                    # document.
                    tables[table_id] = {
                        'div': panflute.Div(),
                        'rows': [],
                    }
                    doc.content.append(tables[table_id]['div'])
                if row_id not in table_rows:
                    table_rows[row_id] = panflute.TableRow()
                    tables[table_id]['rows'].append(table_rows[row_id])
                if cell_id not in table_cells:
                    cdiv = panflute.Div(panflute.Plain())
                    table_cells[cell_id] = panflute.TableCell(cdiv)
                    table_rows[row_id].content.append(table_cells[cell_id])
                container = table_cells[cell_id].content[0].content[0]

            else:
                lists = {}
                doc.content.append(cdiv)

            if 'align' in chunk[0]['attrs']:
                cdiv.attributes['style'] = 'text-align: ' + chunk[0]['attrs'][
                    'align'] + ';'

            for subchunk in chunk:
                if subchunk['newlines'] > 1:
                    # we've had an extra linebreak, no more adding on to lists
                    lists = {}

                # don't do anything with markers
                if subchunk['text'] == '*' and 'lmkr' in subchunk['attrs']:
                    continue

                scont = container
                if 'href' in subchunk['attrs']:
                    scont = panflute.Link(url=subchunk['attrs']['href'])
                    container.content.append(scont)

                if 'image' in subchunk['attrs']:
                    scont.content.append(
                        panflute.Image(
                            url=self._image(subchunk['attrs']['author'],
                                            subchunk['attrs']['image'])))
                    continue

                span = panflute.Span()
                lines = subchunk['text'].splitlines()
                while lines:
                    subtext = lines.pop(0)
                    span.content.append(panflute.Str(subtext))
                    if lines:
                        span.content.append(panflute.LineBreak())

                if 'font' in subchunk['attrs']:
                    color = subchunk['attrs']['font'].get('color', '000000')
                    size = subchunk['attrs']['font'].get('size', 'medium')
                    span.classes.append('font-size-' + size)
                    span.classes.append('font-color-' + color)

                    # I don't actually know what the possible colors are and I
                    # don't feel like finding out, so just inject it as an
                    # inline style.
                    if color != '000000':
                        span.attributes['style'] = 'color: #' + color + ';'

                if subchunk['attrs'].get('underline'):
                    span.classes.append('underline')
                if subchunk['attrs'].get('bold'):
                    span = panflute.Strong(span)
                if subchunk['attrs'].get('italic'):
                    span = panflute.Emph(span)
                if subchunk['attrs'].get('strikethrough'):
                    span = panflute.Strikeout(span)
                scont.content.append(span)

        # Actually create the tables
        for x in tables:
            tables[x]['div'].content.append(panflute.Table(*tables[x]['rows']))

        with io.StringIO() as f:
            panflute.dump(doc, f)
            return f.getvalue()
Esempio n. 11
0
def action(elem, doc):  # noqa
    """Processes pf.CodeBlocks.

    For details and a specification of how each command should behave,
    check the example files (especially the md and pdf)!

    Args:
        elem: The element to process.
        doc:  The document.

    Returns:
        A changed element or None.
    """
    if isinstance(elem, pf.CodeBlock):
        doc.listings_counter += 1
        elems = [elem] if 'hide' not in elem.classes else []

        if 'file' in elem.attributes:
            elem.text = read_file(elem.attributes['file'])
            filename = trimpath(elem.attributes)
            prefix = pf.Emph(pf.Str('File:'))

        if 'exec' in elem.classes:
            if 'interactive' in elem.classes or elem.text[:4] == '>>> ':
                elem.text = execute_interactive_code(elem, doc)
            else:
                result = execute_code_block(elem, doc)

                if 'hideimports' in elem.classes:
                    elem.text = remove_import_statements(elem.text)

                if 'plt' in elem.attributes or 'plt' in elem.classes:
                    doc.plot_found = True
                    result = maybe_center_plot(result)
                    block = pf.RawBlock(result, format='latex')
                else:
                    block = pf.CodeBlock(result, classes=['changelog'])

                elems += [pf.Para(pf.Emph(pf.Str('Output:'))), block]

        if 'lines' in elem.attributes:
            elem.text = filter_lines(elem.text, elem.attributes['lines'])

        label = elem.attributes.get('label', f'cl:{doc.listings_counter}')

        if 'caption' in elem.attributes.keys():
            doc.caption_found = True
            cap = pf.convert_text(elem.attributes['caption'], output_format='latex')  # noqa
            if 'shortcaption' in elem.attributes.keys():
                shortcap = pf.convert_text(elem.attributes['shortcaption'], output_format='latex')  # noqa
            else:
                shortcap = cap
            if 'file' in elem.attributes.keys():
                cap += pf.convert_text(f'&nbsp;(`{filename}`)', output_format='latex')  # noqa

            elems = make_codelisting(elems, cap, label, shortcaption=shortcap,
                                     above='capbelow' not in elem.classes)
        elif 'caption' in elem.classes:
            doc.caption_found = True
            cap = ''
            if 'file' in elem.attributes.keys():
                cap = pf.convert_text(f'`{filename}`', output_format='latex')
            elems = make_codelisting(elems, cap, label,
                                     above='capbelow' not in elem.classes)
        else:
            if 'file' in elem.attributes.keys():
                elems.insert(0, pf.Para(prefix, pf.Space,
                                        pf.Code(filename)))

        return elems
Esempio n. 12
0
 def handle_modsemph(self, cmd_args, elem):
     r"""Handle \modsemph command."""
     return pf.Emph(*parse_fragment(
         cmd_args[0], elem.doc.metadata["lang"].text)[0].content)