]> jfr.im git - dlqueue.git/blob - venv/lib/python3.11/site-packages/pip/_vendor/pygments/formatters/svg.py
init: venv aand flask
[dlqueue.git] / venv / lib / python3.11 / site-packages / pip / _vendor / pygments / formatters / svg.py
1 """
2 pygments.formatters.svg
3 ~~~~~~~~~~~~~~~~~~~~~~~
4
5 Formatter for SVG output.
6
7 :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
8 :license: BSD, see LICENSE for details.
9 """
10
11 from pip._vendor.pygments.formatter import Formatter
12 from pip._vendor.pygments.token import Comment
13 from pip._vendor.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 .. versionadded:: 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 `linenos`
56 If ``True``, add line numbers (default: ``False``).
57
58 `linenostart`
59 The line number for the first line (default: ``1``).
60
61 `linenostep`
62 If set to a number n > 1, only every nth line number is printed.
63
64 `linenowidth`
65 Maximum width devoted to line numbers (default: ``3*ystep``, sufficient
66 for up to 4-digit line numbers. Increase width for longer code blocks).
67
68 `xoffset`
69 Starting offset in X direction, defaults to ``0``.
70
71 `yoffset`
72 Starting offset in Y direction, defaults to the font size if it is given
73 in pixels, or ``20`` else. (This is necessary since text coordinates
74 refer to the text baseline, not the top edge.)
75
76 `ystep`
77 Offset to add to the Y coordinate for each subsequent line. This should
78 roughly be the text size plus 5. It defaults to that value if the text
79 size is given in pixels, or ``25`` else.
80
81 `spacehack`
82 Convert spaces in the source to ``&#160;``, which are non-breaking
83 spaces. SVG provides the ``xml:space`` attribute to control how
84 whitespace inside tags is handled, in theory, the ``preserve`` value
85 could be used to keep all whitespace as-is. However, many current SVG
86 viewers don't obey that rule, so this option is provided as a workaround
87 and defaults to ``True``.
88 """
89 name = 'SVG'
90 aliases = ['svg']
91 filenames = ['*.svg']
92
93 def __init__(self, **options):
94 Formatter.__init__(self, **options)
95 self.nowrap = get_bool_opt(options, 'nowrap', False)
96 self.fontfamily = options.get('fontfamily', 'monospace')
97 self.fontsize = options.get('fontsize', '14px')
98 self.xoffset = get_int_opt(options, 'xoffset', 0)
99 fs = self.fontsize.strip()
100 if fs.endswith('px'): fs = fs[:-2].strip()
101 try:
102 int_fs = int(fs)
103 except:
104 int_fs = 20
105 self.yoffset = get_int_opt(options, 'yoffset', int_fs)
106 self.ystep = get_int_opt(options, 'ystep', int_fs + 5)
107 self.spacehack = get_bool_opt(options, 'spacehack', True)
108 self.linenos = get_bool_opt(options,'linenos',False)
109 self.linenostart = get_int_opt(options,'linenostart',1)
110 self.linenostep = get_int_opt(options,'linenostep',1)
111 self.linenowidth = get_int_opt(options,'linenowidth', 3*self.ystep)
112 self._stylecache = {}
113
114 def format_unencoded(self, tokensource, outfile):
115 """
116 Format ``tokensource``, an iterable of ``(tokentype, tokenstring)``
117 tuples and write it into ``outfile``.
118
119 For our implementation we put all lines in their own 'line group'.
120 """
121 x = self.xoffset
122 y = self.yoffset
123 if not self.nowrap:
124 if self.encoding:
125 outfile.write('<?xml version="1.0" encoding="%s"?>\n' %
126 self.encoding)
127 else:
128 outfile.write('<?xml version="1.0"?>\n')
129 outfile.write('<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" '
130 '"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/'
131 'svg10.dtd">\n')
132 outfile.write('<svg xmlns="http://www.w3.org/2000/svg">\n')
133 outfile.write('<g font-family="%s" font-size="%s">\n' %
134 (self.fontfamily, self.fontsize))
135
136 counter = self.linenostart
137 counter_step = self.linenostep
138 counter_style = self._get_style(Comment)
139 line_x = x
140
141 if self.linenos:
142 if counter % counter_step == 0:
143 outfile.write('<text x="%s" y="%s" %s text-anchor="end">%s</text>' %
144 (x+self.linenowidth,y,counter_style,counter))
145 line_x += self.linenowidth + self.ystep
146 counter += 1
147
148 outfile.write('<text x="%s" y="%s" xml:space="preserve">' % (line_x, y))
149 for ttype, value in tokensource:
150 style = self._get_style(ttype)
151 tspan = style and '<tspan' + style + '>' or ''
152 tspanend = tspan and '</tspan>' or ''
153 value = escape_html(value)
154 if self.spacehack:
155 value = value.expandtabs().replace(' ', '&#160;')
156 parts = value.split('\n')
157 for part in parts[:-1]:
158 outfile.write(tspan + part + tspanend)
159 y += self.ystep
160 outfile.write('</text>\n')
161 if self.linenos and counter % counter_step == 0:
162 outfile.write('<text x="%s" y="%s" text-anchor="end" %s>%s</text>' %
163 (x+self.linenowidth,y,counter_style,counter))
164
165 counter += 1
166 outfile.write('<text x="%s" y="%s" ' 'xml:space="preserve">' % (line_x,y))
167 outfile.write(tspan + parts[-1] + tspanend)
168 outfile.write('</text>')
169
170 if not self.nowrap:
171 outfile.write('</g></svg>\n')
172
173 def _get_style(self, tokentype):
174 if tokentype in self._stylecache:
175 return self._stylecache[tokentype]
176 otokentype = tokentype
177 while not self.style.styles_token(tokentype):
178 tokentype = tokentype.parent
179 value = self.style.style_for_token(tokentype)
180 result = ''
181 if value['color']:
182 result = ' fill="#' + value['color'] + '"'
183 if value['bold']:
184 result += ' font-weight="bold"'
185 if value['italic']:
186 result += ' font-style="italic"'
187 self._stylecache[otokentype] = result
188 return result