ThirdParty/Pygments/pygments/formatters/svg.py

changeset 0
de9c2efb9d02
child 684
2f29a0b6e1c7
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2 """
3 pygments.formatters.svg
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
6 Formatter for SVG output.
7
8 :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS.
9 :license: BSD, see LICENSE for details.
10 """
11
12 from pygments.formatter import Formatter
13 from pygments.util import get_bool_opt, get_int_opt
14
15 __all__ = ['SvgFormatter']
16
17
18 def escape_html(text):
19 """Escape &, <, > as well as single and double quotes for HTML."""
20 return text.replace('&', '&amp;'). \
21 replace('<', '&lt;'). \
22 replace('>', '&gt;'). \
23 replace('"', '&quot;'). \
24 replace("'", '&#39;')
25
26
27 class2style = {}
28
29 class SvgFormatter(Formatter):
30 """
31 Format tokens as an SVG graphics file. This formatter is still experimental.
32 Each line of code is a ``<text>`` element with explicit ``x`` and ``y``
33 coordinates containing ``<tspan>`` elements with the individual token styles.
34
35 By default, this formatter outputs a full SVG document including doctype
36 declaration and the ``<svg>`` root element.
37
38 *New in Pygments 0.9.*
39
40 Additional options accepted:
41
42 `nowrap`
43 Don't wrap the SVG ``<text>`` elements in ``<svg><g>`` elements and
44 don't add a XML declaration and a doctype. If true, the `fontfamily`
45 and `fontsize` options are ignored. Defaults to ``False``.
46
47 `fontfamily`
48 The value to give the wrapping ``<g>`` element's ``font-family``
49 attribute, defaults to ``"monospace"``.
50
51 `fontsize`
52 The value to give the wrapping ``<g>`` element's ``font-size``
53 attribute, defaults to ``"14px"``.
54
55 `xoffset`
56 Starting offset in X direction, defaults to ``0``.
57
58 `yoffset`
59 Starting offset in Y direction, defaults to the font size if it is given
60 in pixels, or ``20`` else. (This is necessary since text coordinates
61 refer to the text baseline, not the top edge.)
62
63 `ystep`
64 Offset to add to the Y coordinate for each subsequent line. This should
65 roughly be the text size plus 5. It defaults to that value if the text
66 size is given in pixels, or ``25`` else.
67
68 `spacehack`
69 Convert spaces in the source to ``&#160;``, which are non-breaking
70 spaces. SVG provides the ``xml:space`` attribute to control how
71 whitespace inside tags is handled, in theory, the ``preserve`` value
72 could be used to keep all whitespace as-is. However, many current SVG
73 viewers don't obey that rule, so this option is provided as a workaround
74 and defaults to ``True``.
75 """
76 name = 'SVG'
77 aliases = ['svg']
78 filenames = ['*.svg']
79
80 def __init__(self, **options):
81 # XXX outencoding
82 Formatter.__init__(self, **options)
83 self.nowrap = get_bool_opt(options, 'nowrap', False)
84 self.fontfamily = options.get('fontfamily', 'monospace')
85 self.fontsize = options.get('fontsize', '14px')
86 self.xoffset = get_int_opt(options, 'xoffset', 0)
87 fs = self.fontsize.strip()
88 if fs.endswith('px'): fs = fs[:-2].strip()
89 try:
90 int_fs = int(fs)
91 except:
92 int_fs = 20
93 self.yoffset = get_int_opt(options, 'yoffset', int_fs)
94 self.ystep = get_int_opt(options, 'ystep', int_fs + 5)
95 self.spacehack = get_bool_opt(options, 'spacehack', True)
96 self._stylecache = {}
97
98 def format_unencoded(self, tokensource, outfile):
99 """
100 Format ``tokensource``, an iterable of ``(tokentype, tokenstring)``
101 tuples and write it into ``outfile``.
102
103 For our implementation we put all lines in their own 'line group'.
104 """
105 x = self.xoffset
106 y = self.yoffset
107 if not self.nowrap:
108 if self.encoding:
109 outfile.write('<?xml version="1.0" encoding="%s"?>\n' %
110 self.encoding)
111 else:
112 outfile.write('<?xml version="1.0"?>\n')
113 outfile.write('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" '
114 '"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/'
115 'svg10.dtd">\n')
116 outfile.write('<svg xmlns="http://www.w3.org/2000/svg">\n')
117 outfile.write('<g font-family="%s" font-size="%s">\n' %
118 (self.fontfamily, self.fontsize))
119 outfile.write('<text x="%s" y="%s" xml:space="preserve">' % (x, y))
120 for ttype, value in tokensource:
121 style = self._get_style(ttype)
122 tspan = style and '<tspan' + style + '>' or ''
123 tspanend = tspan and '</tspan>' or ''
124 value = escape_html(value)
125 if self.spacehack:
126 value = value.expandtabs().replace(' ', '&#160;')
127 parts = value.split('\n')
128 for part in parts[:-1]:
129 outfile.write(tspan + part + tspanend)
130 y += self.ystep
131 outfile.write('</text>\n<text x="%s" y="%s" '
132 'xml:space="preserve">' % (x, y))
133 outfile.write(tspan + parts[-1] + tspanend)
134 outfile.write('</text>')
135
136 if not self.nowrap:
137 outfile.write('</g></svg>\n')
138
139 def _get_style(self, tokentype):
140 if tokentype in self._stylecache:
141 return self._stylecache[tokentype]
142 otokentype = tokentype
143 while not self.style.styles_token(tokentype):
144 tokentype = tokentype.parent
145 value = self.style.style_for_token(tokentype)
146 result = ''
147 if value['color']:
148 result = ' fill="#' + value['color'] + '"'
149 if value['bold']:
150 result += ' font-weight="bold"'
151 if value['italic']:
152 result += ' font-style="italic"'
153 self._stylecache[otokentype] = result
154 return result

eric ide

mercurial