Spaces:
Sleeping
Sleeping
""" | |
pygments.formatters.svg | |
~~~~~~~~~~~~~~~~~~~~~~~ | |
Formatter for SVG output. | |
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS. | |
:license: BSD, see LICENSE for details. | |
""" | |
from pip._vendor.pygments.formatter import Formatter | |
from pip._vendor.pygments.token import Comment | |
from pip._vendor.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. | |
By default, this formatter outputs a full SVG document including doctype | |
declaration and the ``<svg>`` root element. | |
.. versionadded:: 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 wrapping ``<g>`` element's ``font-family`` | |
attribute, defaults to ``"monospace"``. | |
`fontsize` | |
The value to give the wrapping ``<g>`` element's ``font-size`` | |
attribute, defaults to ``"14px"``. | |
`linenos` | |
If ``True``, add line numbers (default: ``False``). | |
`linenostart` | |
The line number for the first line (default: ``1``). | |
`linenostep` | |
If set to a number n > 1, only every nth line number is printed. | |
`linenowidth` | |
Maximum width devoted to line numbers (default: ``3*ystep``, sufficient | |
for up to 4-digit line numbers. Increase width for longer code blocks). | |
`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): | |
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.linenos = get_bool_opt(options,'linenos',False) | |
self.linenostart = get_int_opt(options,'linenostart',1) | |
self.linenostep = get_int_opt(options,'linenostep',1) | |
self.linenowidth = get_int_opt(options,'linenowidth', 3*self.ystep) | |
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('<?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)) | |
counter = self.linenostart | |
counter_step = self.linenostep | |
counter_style = self._get_style(Comment) | |
line_x = x | |
if self.linenos: | |
if counter % counter_step == 0: | |
outfile.write('<text x="%s" y="%s" %s text-anchor="end">%s</text>' % | |
(x+self.linenowidth,y,counter_style,counter)) | |
line_x += self.linenowidth + self.ystep | |
counter += 1 | |
outfile.write('<text x="%s" y="%s" xml:space="preserve">' % (line_x, y)) | |
for ttype, value in tokensource: | |
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') | |
if self.linenos and counter % counter_step == 0: | |
outfile.write('<text x="%s" y="%s" text-anchor="end" %s>%s</text>' % | |
(x+self.linenowidth,y,counter_style,counter)) | |
counter += 1 | |
outfile.write('<text x="%s" y="%s" ' 'xml:space="preserve">' % (line_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 | |