f3dcbce9b9fa2904fc361ef09139aeec3568685e
[SubU] /
1 """
2     pygments.formatters.groff
3     ~~~~~~~~~~~~~~~~~~~~~~~~~
4
5     Formatter for groff output.
6
7     :copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
8     :license: BSD, see LICENSE for details.
9 """
10
11 import math
12 from pip._vendor.pygments.formatter import Formatter
13 from pip._vendor.pygments.util import get_bool_opt, get_int_opt
14
15 __all__ = ['GroffFormatter']
16
17
18 class GroffFormatter(Formatter):
19     """
20     Format tokens with groff escapes to change their color and font style.
21
22     .. versionadded:: 2.11
23
24     Additional options accepted:
25
26     `style`
27         The style to use, can be a string or a Style subclass (default:
28         ``'default'``).
29
30     `monospaced`
31         If set to true, monospace font will be used (default: ``true``).
32
33     `linenos`
34         If set to true, print the line numbers (default: ``false``).
35
36     `wrap`
37         Wrap lines to the specified number of characters. Disabled if set to 0
38         (default: ``0``).
39     """
40
41     name = 'groff'
42     aliases = ['groff','troff','roff']
43     filenames = []
44
45     def __init__(self, **options):
46         Formatter.__init__(self, **options)
47
48         self.monospaced = get_bool_opt(options, 'monospaced', True)
49         self.linenos = get_bool_opt(options, 'linenos', False)
50         self._lineno = 0
51         self.wrap = get_int_opt(options, 'wrap', 0)
52         self._linelen = 0
53
54         self.styles = {}
55         self._make_styles()
56
57
58     def _make_styles(self):
59         regular = '\\f[CR]' if self.monospaced else '\\f[R]'
60         bold = '\\f[CB]' if self.monospaced else '\\f[B]'
61         italic = '\\f[CI]' if self.monospaced else '\\f[I]'
62
63         for ttype, ndef in self.style:
64             start = end = ''
65             if ndef['color']:
66                 start += '\\m[%s]' % ndef['color']
67                 end = '\\m[]' + end
68             if ndef['bold']:
69                 start += bold
70                 end = regular + end
71             if ndef['italic']:
72                 start += italic
73                 end = regular + end
74             if ndef['bgcolor']:
75                 start += '\\M[%s]' % ndef['bgcolor']
76                 end = '\\M[]' + end
77
78             self.styles[ttype] = start, end
79
80
81     def _define_colors(self, outfile):
82         colors = set()
83         for _, ndef in self.style:
84             if ndef['color'] is not None:
85                 colors.add(ndef['color'])
86
87         for color in colors:
88             outfile.write('.defcolor ' + color + ' rgb #' + color + '\n')
89
90
91     def _write_lineno(self, outfile):
92         self._lineno += 1
93         outfile.write("%s% 4d " % (self._lineno != 1 and '\n' or '', self._lineno))
94
95
96     def _wrap_line(self, line):
97         length = len(line.rstrip('\n'))
98         space = '     ' if self.linenos else ''
99         newline = ''
100
101         if length > self.wrap:
102             for i in range(0, math.floor(length / self.wrap)):
103                 chunk = line[i*self.wrap:i*self.wrap+self.wrap]
104                 newline += (chunk + '\n' + space)
105             remainder = length % self.wrap
106             if remainder > 0:
107                 newline += line[-remainder-1:]
108                 self._linelen = remainder
109         elif self._linelen + length > self.wrap:
110             newline = ('\n' + space) + line
111             self._linelen = length
112         else:
113             newline = line
114             self._linelen += length
115
116         return newline
117
118
119     def _escape_chars(self, text):
120         text = text.replace('\\', '\\[u005C]'). \
121                     replace('.', '\\[char46]'). \
122                     replace('\'', '\\[u0027]'). \
123                     replace('`', '\\[u0060]'). \
124                     replace('~', '\\[u007E]')
125         copy = text
126
127         for char in copy:
128             if len(char) != len(char.encode()):
129                 uni = char.encode('unicode_escape') \
130                     .decode()[1:] \
131                     .replace('x', 'u00') \
132                     .upper()
133                 text = text.replace(char, '\\[u' + uni[1:] + ']')
134
135         return text
136
137
138     def format_unencoded(self, tokensource, outfile):
139         self._define_colors(outfile)
140
141         outfile.write('.nf\n\\f[CR]\n')
142
143         if self.linenos:
144             self._write_lineno(outfile)
145
146         for ttype, value in tokensource:
147             while ttype not in self.styles:
148                 ttype = ttype.parent
149             start, end = self.styles[ttype]
150
151             for line in value.splitlines(True):
152                 if self.wrap > 0:
153                     line = self._wrap_line(line)
154
155                 if start and end:
156                     text = self._escape_chars(line.rstrip('\n'))
157                     if text != '':
158                         outfile.write(''.join((start, text, end)))
159                 else:
160                     outfile.write(self._escape_chars(line.rstrip('\n')))
161
162                 if line.endswith('\n'):
163                     if self.linenos:
164                         self._write_lineno(outfile)
165                         self._linelen = 0
166                     else:
167                         outfile.write('\n')
168                         self._linelen = 0
169
170         outfile.write('\n.fi')