From a6f39fa33238c7dbee4ec2edabaabb435137a9d3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sun, 14 Oct 2007 22:07:20 +0200 Subject: Add SVG formatter. --- pygments/formatters/_mapping.py | 2 + pygments/formatters/svg.py | 153 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 pygments/formatters/svg.py (limited to 'pygments') 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 ```` element with explicit ``x`` and ``y`` + coordinates containing ```` elements with the individual token styles. + + *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 ```` elements' ``font-family`` attribute, + defaults to ``"monospace"``. + + `fontsize` + The value to give the ```` 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('\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: + if enc: + value = value.encode(enc) + 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 -- cgit v1.2.1