All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pygments.formatters.svg.py Maven / Gradle / Ivy

There is a newer version: 1.9
Show newest version
# -*- coding: utf-8 -*-
"""
    pygments.formatters.svg
    ~~~~~~~~~~~~~~~~~~~~~~~

    Formatter for SVG output.

    :copyright: Copyright 2006-2012 by the Pygments team, see AUTHORS.
    :license: BSD, see LICENSE for details.
"""

from pygments.formatter import Formatter
from pygments.util import get_bool_opt, get_int_opt

__all__ = ['SvgFormatter']


def escape_html(text):
    """Escape &, <, > as well as single and double quotes for HTML."""
    return text.replace('&', '&').  \
                replace('<', '<').   \
                replace('>', '>').   \
                replace('"', '"'). \
                replace("'", ''')


class2style = {}

class SvgFormatter(Formatter):
    """
    Format tokens as an SVG graphics file.  This formatter is still experimental.
    Each line of code is a ```` element with explicit ``x`` and ``y``
    coordinates containing ```` elements with the individual token styles.

    By default, this formatter outputs a full SVG document including doctype
    declaration and the ```` root element.

    *New in Pygments 0.9.*

    Additional options accepted:

    `nowrap`
        Don't wrap the SVG ```` elements in ```` elements and
        don't add a XML declaration and a doctype.  If true, the `fontfamily`
        and `fontsize` options are ignored.  Defaults to ``False``.

    `fontfamily`
        The value to give the wrapping ```` element's ``font-family``
        attribute, defaults to ``"monospace"``.

    `fontsize`
        The value to give the wrapping ```` element's ``font-size``
        attribute, defaults to ``"14px"``.

    `xoffset`
        Starting offset in X direction, defaults to ``0``.

    `yoffset`
        Starting offset in Y direction, defaults to the font size if it is given
        in pixels, or ``20`` else.  (This is necessary since text coordinates
        refer to the text baseline, not the top edge.)

    `ystep`
        Offset to add to the Y coordinate for each subsequent line.  This should
        roughly be the text size plus 5.  It defaults to that value if the text
        size is given in pixels, or ``25`` else.

    `spacehack`
        Convert spaces in the source to `` ``, which are non-breaking
        spaces.  SVG provides the ``xml:space`` attribute to control how
        whitespace inside tags is handled, in theory, the ``preserve`` value
        could be used to keep all whitespace as-is.  However, many current SVG
        viewers don't obey that rule, so this option is provided as a workaround
        and defaults to ``True``.
    """
    name = 'SVG'
    aliases = ['svg']
    filenames = ['*.svg']

    def __init__(self, **options):
        # XXX outencoding
        Formatter.__init__(self, **options)
        self.nowrap = get_bool_opt(options, 'nowrap', False)
        self.fontfamily = options.get('fontfamily', 'monospace')
        self.fontsize = options.get('fontsize', '14px')
        self.xoffset = get_int_opt(options, 'xoffset', 0)
        fs = self.fontsize.strip()
        if fs.endswith('px'): fs = fs[:-2].strip()
        try:
            int_fs = int(fs)
        except:
            int_fs = 20
        self.yoffset = get_int_opt(options, 'yoffset', int_fs)
        self.ystep = get_int_opt(options, 'ystep', int_fs + 5)
        self.spacehack = get_bool_opt(options, 'spacehack', True)
        self._stylecache = {}

    def format_unencoded(self, tokensource, outfile):
        """
        Format ``tokensource``, an iterable of ``(tokentype, tokenstring)``
        tuples and write it into ``outfile``.

        For our implementation we put all lines in their own 'line group'.
        """
        x = self.xoffset
        y = self.yoffset
        if not self.nowrap:
            if self.encoding:
                outfile.write('\n' %
                              self.encoding)
            else:
                outfile.write('\n')
            outfile.write('\n')
            outfile.write('\n')
            outfile.write('\n' %
                          (self.fontfamily, self.fontsize))
        outfile.write('' % (x, y))
        for ttype, value in tokensource:
            style = self._get_style(ttype)
            tspan = style and '' or ''
            tspanend = tspan and '' or ''
            value = escape_html(value)
            if self.spacehack:
                value = value.expandtabs().replace(' ', ' ')
            parts = value.split('\n')
            for part in parts[:-1]:
                outfile.write(tspan + part + tspanend)
                y += self.ystep
                outfile.write('\n' % (x, y))
            outfile.write(tspan + parts[-1] + tspanend)
        outfile.write('')

        if not self.nowrap:
            outfile.write('\n')

    def _get_style(self, tokentype):
        if tokentype in self._stylecache:
            return self._stylecache[tokentype]
        otokentype = tokentype
        while not self.style.styles_token(tokentype):
            tokentype = tokentype.parent
        value = self.style.style_for_token(tokentype)
        result = ''
        if value['color']:
            result = ' fill="#' + value['color'] + '"'
        if value['bold']:
            result += ' font-weight="bold"'
        if value['italic']:
            result += ' font-style="italic"'
        self._stylecache[otokentype] = result
        return result




© 2015 - 2024 Weber Informatics LLC | Privacy Policy