diff options
| author | Georg Brandl <georg@python.org> | 2007-10-14 22:07:20 +0200 |
|---|---|---|
| committer | Georg Brandl <georg@python.org> | 2007-10-14 22:07:20 +0200 |
| commit | a6f39fa33238c7dbee4ec2edabaabb435137a9d3 (patch) | |
| tree | be7b807a1b804d98689b706f643ea29b842bb9c3 /pygments | |
| parent | a1e5e3ab5bf3f0095e47e2c8032ea4b9ad606587 (diff) | |
| download | pygments-git-a6f39fa33238c7dbee4ec2edabaabb435137a9d3.tar.gz | |
Add SVG formatter.
Diffstat (limited to 'pygments')
| -rwxr-xr-x | pygments/formatters/_mapping.py | 2 | ||||
| -rw-r--r-- | pygments/formatters/svg.py | 153 |
2 files changed, 155 insertions, 0 deletions
diff --git a/pygments/formatters/_mapping.py b/pygments/formatters/_mapping.py index 1b13ded3..e2ed1a83 100755 --- a/pygments/formatters/_mapping.py +++ b/pygments/formatters/_mapping.py @@ -22,6 +22,7 @@ from pygments.formatters.latex import LatexFormatter from pygments.formatters.other import NullFormatter from pygments.formatters.other import RawTokenFormatter from pygments.formatters.rtf import RtfFormatter +from pygments.formatters.svg import SvgFormatter from pygments.formatters.terminal import TerminalFormatter from pygments.formatters.terminal256 import Terminal256Formatter @@ -32,6 +33,7 @@ FORMATTERS = { NullFormatter: ('Text only', ('text', 'null'), ('*.txt',), 'Output the text unchanged without any formatting.'), RawTokenFormatter: ('Raw tokens', ('raw', 'tokens'), ('*.raw',), 'Format tokens as a raw representation for storing token streams.'), RtfFormatter: ('RTF', ('rtf',), ('*.rtf',), 'Format tokens as RTF markup. This formatter automatically outputs full RTF documents with color information and other useful stuff. Perfect for Copy and Paste into Microsoft\xc2\xae Word\xc2\xae documents.'), + SvgFormatter: ('SVG', ('svg',), ('*.svg',), 'Format tokens as an SVG graphics file. This formatter is still experimental.'), Terminal256Formatter: ('Terminal256', ('terminal256', 'console256', '256'), (), 'Format tokens with ANSI color sequences, for output in a 256-color terminal or console. Like in `TerminalFormatter` color sequences are terminated at newlines, so that paging the output works correctly.'), TerminalFormatter: ('Terminal', ('terminal', 'console'), (), 'Format tokens with ANSI color sequences, for output in a text console. Color sequences are terminated at newlines, so that paging the output works correctly.') } diff --git a/pygments/formatters/svg.py b/pygments/formatters/svg.py new file mode 100644 index 00000000..cf801610 --- /dev/null +++ b/pygments/formatters/svg.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +""" + pygments.formatters.svg + ~~~~~~~~~~~~~~~~~~~~~~~ + + Formatter for SVG output. + + :copyright: 2007 by an unknown contributor. + :license: BSD, see LICENSE for more details. +""" +import StringIO + +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 ``<text>`` element with explicit ``x`` and ``y`` + coordinates containing ``<tspan>`` elements with the individual token styles. + + *New in Pygments 0.9.* + + Additional options accepted: + + `nowrap` + Don't wrap the SVG ``<text>`` elements in ``<svg><g>`` 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 ``<text>`` elements' ``font-family`` attribute, + defaults to ``"monospace"``. + + `fontsize` + The value to give the ``<text>`` elements' ``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 ``&160;``, 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(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 + enc = self.encoding + if not self.nowrap: + if enc: + outfile.write('<?xml version="1.0" encoding="%s"?>\n' % self.encoding) + else: + outfile.write('<?xml version="1.0"?>\n') + outfile.write('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" ' + '"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">\n') + outfile.write('<svg xmlns="http://www.w3.org/2000/svg">\n') + outfile.write('<g font-family="%s" font-size="%s">\n' % (self.fontfamily, + self.fontsize)) + outfile.write('<text x="%s" y="%s" xml:space="preserve">' % (x, y)) + for ttype, value in tokensource: + if enc: + value = value.encode(enc) + style = self._get_style(ttype) + tspan = style and '<tspan' + style + '>' or '' + tspanend = tspan and '</tspan>' 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('</text>\n<text x="%s" y="%s" ' + 'xml:space="preserve">' % (x, y)) + outfile.write(tspan + parts[-1] + tspanend) + outfile.write('</text>') + + if not self.nowrap: + outfile.write('</g></svg>\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 |
