Source code for parce.out.html
# -*- coding: utf-8 -*-
#
# This file is part of the parce Python package.
#
# Copyright © 2019-2020 by Wilbert Berendsen <info@wilbertberendsen.nl>
#
# This module is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This module is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
r"""
Formatter for HTML output.
This module contains two formatters that can create HTML output:
:class:`HtmlFormatter` and :class:`SimpleHtmlFormatter`. The first converts
format settings from a theme to inline CSS style attributes; the latter just
puts the class names in the HTML and you should link to a CSS file yourself.
Usage example::
>>> from parce import Cursor, Document, find, theme_by_name
>>> from parce.out.html import HtmlFormatter
>>> d = Document(find("xml"), '''<xml attr="value">text</xml>\n''')
>>> f = HtmlFormatter(theme_by_name('dark'))
>>> print(f.full_html(Cursor(d, 0, None)))
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<div class="parce">
<pre style="white-space: pre; background-color: #000000; color: #fffff0; fon
t-family: monospace; font-size: 12pt;"><<span style="color: #87cefa; font
-weight: bold;">xml</span> <span style="color: #1e90ff;">attr</span>=<span s
tyle="color: #cd5c5c;">"value"</span>>text</<span style="color: #87cef
a; font-weight: bold;">xml</span>>
</pre>
</div>
</body>
</html>
"""
import parce.formatter
FULL_HTML_TEMPLATE = """<!DOCTYPE html>
<html>
<head>
<meta charset="{charset}"/>
</head>
<body>
<div class="parce">
<pre style="white-space: pre; {baseformat}">{html}</pre>
</div>
</body>
</html>
"""
[docs]class HtmlMixin:
"""Helper class containing extra methods to generate HTML output."""
[docs] def full_html(self, cursor, charset="utf-8"):
"""Returns the selected text as a complete HTML document.
The charset is included in the HTML header, but the returned string is
still a Python string; you should take care of the encoding yourself
when writing the HTML to an output device.
"""
return FULL_HTML_TEMPLATE.format(
charset = charset,
baseformat = self.baseformat() or "",
html = self.html(cursor))
[docs]class HtmlFormatter(HtmlMixin, parce.formatter.Formatter):
"""A Formatter to output HTML."""
def __init__(self, theme=None, factory=None):
if factory is None:
factory = lambda tf: inline_css(tf) or None
super().__init__(theme, factory)
[docs] def html(self, cursor):
"""Return HTML output for the selected range of the cursor.
The text pieces that have some textformat are wrapped in ``<span
style="">...</span>`` tags with inline CSS attributes. The returned
HTML expects to be wrapped in a <pre> tag.
Example::
>>> from parce.out.html import HtmlFormatter
>>> from parce import Cursor, Document, find, theme_by_name
>>> d = Document(find('css'), "h1 { color: red; }")
>>> f = HtmlFormatter(theme_by_name())
>>> f.html(Cursor(d, 0, None))
'<span style="color: #00008b; font-weight: bold;">h1</span> <span sty
le="font-weight: bold;">{</span> <span style="color: #4169e1; font-we
ight: bold;">color</span>: <span style="color: #2e8b57;">red</span>;
<span style="font-weight: bold;">}</span>'
"""
span = '<span style="{}">{}</span>'.format
return "".join(
span(attrescape(fmt), escape(text)) if fmt else escape(text)
for text, fmt in self.format_document(cursor))
[docs]class SimpleHtmlFormatter(HtmlMixin, parce.formatter.SimpleFormatter):
"""A simple Formatter that produces HTML with classes.
This HTML should then be used with a css file to see the
highlighting.
"""
[docs] def html(self, cursor):
"""Return HTML output for the selected range of the cursor.
The text pieces that have a standard action are wrapped in ``<span
class="xxx">..</span>`` tags, where the class names correspond with
the standard actions.
The returned HTML expects to be wrapped in a <div> or <pre> with class
``"parce"``, so that the CSS rules match with the contents.
An example::
>>> from parce.out.html import SimpleHtmlFormatter
>>> from parce import Cursor, Document, find, theme_by_name
>>> d = Document(find('css'), "h2 { color: blue; }")
>>> f = SimpleHtmlFormatter()
>>> f.html(Cursor(d, 0, None))
'<span class="name tag">h2</span> <span class="delimiter bracket">{</
span> <span class="name property definition">color</span><span class=
"delimiter">:</span> <span class="literal color">blue</span><span cla
ss="delimiter">;</span> <span class="delimiter bracket">}</span>'
"""
span = '<span class="{}">{}</span>'.format
return "".join(
span(attrescape(fmt), escape(text)) if fmt else escape(text)
for text, fmt in self.format_document(cursor))
[docs]def escape(text):
r"""Escape &, < and > to use text in HTML."""
return text.replace('&', "&").replace('<', "<").replace('>', ">")
[docs]def attrescape(text):
r"""Escape &, <, > and ", to use text in HTML."""
return escape(text).replace('"', """)
[docs]def inline_css(textformat):
"""Convert a :class:`~.theme.TextFormat` to an inline CSS string.
The resulting string can be used in a Html ``style`` attribute.
"""
props = textformat.css_properties()
return " ".join("{}: {};".format(prop, value)
for prop, value in sorted(props.items()))