aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Pygments/py3/pygments/formatters/rtf.py
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-05-20 07:58:40 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-05-20 08:05:00 +0300
commitbcd5bcc390793791d293d386b2ebefbe683fb4e1 (patch)
treec93e3b8c847237e7e7626f4a07f1b657bb34f04d /contrib/python/Pygments/py3/pygments/formatters/rtf.py
parent1a9f1508fe9c8c5927ffebf33197a6108e70501d (diff)
downloadydb-bcd5bcc390793791d293d386b2ebefbe683fb4e1.tar.gz
Intermediate changes
Diffstat (limited to 'contrib/python/Pygments/py3/pygments/formatters/rtf.py')
-rw-r--r--contrib/python/Pygments/py3/pygments/formatters/rtf.py253
1 files changed, 228 insertions, 25 deletions
diff --git a/contrib/python/Pygments/py3/pygments/formatters/rtf.py b/contrib/python/Pygments/py3/pygments/formatters/rtf.py
index d3a83fa603..7f8b7e4cd7 100644
--- a/contrib/python/Pygments/py3/pygments/formatters/rtf.py
+++ b/contrib/python/Pygments/py3/pygments/formatters/rtf.py
@@ -4,12 +4,14 @@
A formatter that generates RTF files.
- :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS.
+ :copyright: Copyright 2006-2024 by the Pygments team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
+from collections import OrderedDict
from pygments.formatter import Formatter
-from pygments.util import get_int_opt, surrogatepair
+from pygments.style import _ansimap
+from pygments.util import get_bool_opt, get_int_opt, get_list_opt, surrogatepair
__all__ = ['RtfFormatter']
@@ -42,6 +44,59 @@ class RtfFormatter(Formatter):
default is 24 half-points, giving a size 12 font.
.. versionadded:: 2.0
+
+ `linenos`
+ Turn on line numbering (default: ``False``).
+
+ .. versionadded:: 2.18
+
+ `lineno_fontsize`
+ Font size for line numbers. Size is specified in half points
+ (default: `fontsize`).
+
+ .. versionadded:: 2.18
+
+ `lineno_padding`
+ Number of spaces between the (inline) line numbers and the
+ source code (default: ``2``).
+
+ .. versionadded:: 2.18
+
+ `linenostart`
+ The line number for the first line (default: ``1``).
+
+ .. versionadded:: 2.18
+
+ `linenostep`
+ If set to a number n > 1, only every nth line number is printed.
+
+ .. versionadded:: 2.18
+
+ `lineno_color`
+ Color for line numbers specified as a hex triplet, e.g. ``'5e5e5e'``.
+ Defaults to the style's line number color if it is a hex triplet,
+ otherwise ansi bright black.
+
+ .. versionadded:: 2.18
+
+ `hl_lines`
+ Specify a list of lines to be highlighted, as line numbers separated by
+ spaces, e.g. ``'3 7 8'``. The line numbers are relative to the input
+ (i.e. the first line is line 1) unless `hl_linenostart` is set.
+
+ .. versionadded:: 2.18
+
+ `hl_color`
+ Color for highlighting the lines specified in `hl_lines`, specified as
+ a hex triplet (default: style's `highlight_color`).
+
+ .. versionadded:: 2.18
+
+ `hl_linenostart`
+ If set to ``True`` line numbers in `hl_lines` are specified
+ relative to `linenostart` (default ``False``).
+
+ .. versionadded:: 2.18
"""
name = 'RTF'
aliases = ['rtf']
@@ -62,6 +117,40 @@ class RtfFormatter(Formatter):
Formatter.__init__(self, **options)
self.fontface = options.get('fontface') or ''
self.fontsize = get_int_opt(options, 'fontsize', 0)
+ self.linenos = get_bool_opt(options, 'linenos', False)
+ self.lineno_fontsize = get_int_opt(options, 'lineno_fontsize',
+ self.fontsize)
+ self.lineno_padding = get_int_opt(options, 'lineno_padding', 2)
+ self.linenostart = abs(get_int_opt(options, 'linenostart', 1))
+ self.linenostep = abs(get_int_opt(options, 'linenostep', 1))
+ self.hl_linenostart = get_bool_opt(options, 'hl_linenostart', False)
+
+ self.hl_color = options.get('hl_color', '')
+ if not self.hl_color:
+ self.hl_color = self.style.highlight_color
+
+ self.hl_lines = []
+ for lineno in get_list_opt(options, 'hl_lines', []):
+ try:
+ lineno = int(lineno)
+ if self.hl_linenostart:
+ lineno = lineno - self.linenostart + 1
+ self.hl_lines.append(lineno)
+ except ValueError:
+ pass
+
+ self.lineno_color = options.get('lineno_color', '')
+ if not self.lineno_color:
+ if self.style.line_number_color == 'inherit':
+ # style color is the css value 'inherit'
+ # default to ansi bright-black
+ self.lineno_color = _ansimap['ansibrightblack']
+ else:
+ # style color is assumed to be a hex triplet as other
+ # colors in pygments/style.py
+ self.lineno_color = self.style.line_number_color
+
+ self.color_mapping = self._create_color_mapping()
def _escape(self, text):
return text.replace('\\', '\\\\') \
@@ -90,43 +179,145 @@ class RtfFormatter(Formatter):
# Force surrogate pairs
buf.append('{\\u%d}{\\u%d}' % surrogatepair(cn))
- return ''.join(buf).replace('\n', '\\par\n')
+ return ''.join(buf).replace('\n', '\\par')
- def format_unencoded(self, tokensource, outfile):
- # rtf 1.8 header
- outfile.write('{\\rtf1\\ansi\\uc0\\deff0'
- '{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0%s;}}'
- '{\\colortbl;' % (self.fontface and
- ' ' + self._escape(self.fontface) or
- ''))
-
- # convert colors and save them in a mapping to access them later.
- color_mapping = {}
+ @staticmethod
+ def hex_to_rtf_color(hex_color):
+ if hex_color[0] == "#":
+ hex_color = hex_color[1:]
+
+ return '\\red%d\\green%d\\blue%d;' % (
+ int(hex_color[0:2], 16),
+ int(hex_color[2:4], 16),
+ int(hex_color[4:6], 16)
+ )
+
+ def _split_tokens_on_newlines(self, tokensource):
+ """
+ Split tokens containing newline characters into multiple token
+ each representing a line of the input file. Needed for numbering
+ lines of e.g. multiline comments.
+ """
+ for ttype, value in tokensource:
+ if value == '\n':
+ yield (ttype, value)
+ elif "\n" in value:
+ lines = value.split("\n")
+ for line in lines[:-1]:
+ yield (ttype, line+"\n")
+ if lines[-1]:
+ yield (ttype, lines[-1])
+ else:
+ yield (ttype, value)
+
+ def _create_color_mapping(self):
+ """
+ Create a mapping of style hex colors to index/offset in
+ the RTF color table.
+ """
+ color_mapping = OrderedDict()
offset = 1
+
+ if self.linenos:
+ color_mapping[self.lineno_color] = offset
+ offset += 1
+
+ if self.hl_lines:
+ color_mapping[self.hl_color] = offset
+ offset += 1
+
for _, style in self.style:
for color in style['color'], style['bgcolor'], style['border']:
if color and color not in color_mapping:
color_mapping[color] = offset
- outfile.write('\\red%d\\green%d\\blue%d;' % (
- int(color[0:2], 16),
- int(color[2:4], 16),
- int(color[4:6], 16)
- ))
offset += 1
- outfile.write('}\\f0 ')
+
+ return color_mapping
+
+ @property
+ def _lineno_template(self):
+ if self.lineno_fontsize != self.fontsize:
+ return '{{\\fs{} \\cf{} %s{}}}'.format(self.lineno_fontsize,
+ self.color_mapping[self.lineno_color],
+ " " * self.lineno_padding)
+
+ return '{{\\cf{} %s{}}}'.format(self.color_mapping[self.lineno_color],
+ " " * self.lineno_padding)
+
+ @property
+ def _hl_open_str(self):
+ return rf'{{\highlight{self.color_mapping[self.hl_color]} '
+
+ @property
+ def _rtf_header(self):
+ lines = []
+ # rtf 1.8 header
+ lines.append('{\\rtf1\\ansi\\uc0\\deff0'
+ '{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0%s;}}'
+ % (self.fontface and ' '
+ + self._escape(self.fontface) or ''))
+
+ # color table
+ lines.append('{\\colortbl;')
+ for color, _ in self.color_mapping.items():
+ lines.append(self.hex_to_rtf_color(color))
+ lines.append('}')
+
+ # font and fontsize
+ lines.append('\\f0\\sa0')
if self.fontsize:
- outfile.write('\\fs%d' % self.fontsize)
+ lines.append('\\fs%d' % self.fontsize)
+
+ # ensure Libre Office Writer imports and renders consecutive
+ # space characters the same width, needed for line numbering.
+ # https://bugs.documentfoundation.org/show_bug.cgi?id=144050
+ lines.append('\\dntblnsbdb')
+
+ return lines
+
+ def format_unencoded(self, tokensource, outfile):
+ for line in self._rtf_header:
+ outfile.write(line + "\n")
+
+ tokensource = self._split_tokens_on_newlines(tokensource)
+
+ # first pass of tokens to count lines, needed for line numbering
+ if self.linenos:
+ line_count = 0
+ tokens = [] # for copying the token source generator
+ for ttype, value in tokensource:
+ tokens.append((ttype, value))
+ if value.endswith("\n"):
+ line_count += 1
+
+ # width of line number strings (for padding with spaces)
+ linenos_width = len(str(line_count+self.linenostart-1))
+
+ tokensource = tokens
# highlight stream
+ lineno = 1
+ start_new_line = True
for ttype, value in tokensource:
+ if start_new_line and lineno in self.hl_lines:
+ outfile.write(self._hl_open_str)
+
+ if start_new_line and self.linenos:
+ if (lineno-self.linenostart+1)%self.linenostep == 0:
+ current_lineno = lineno + self.linenostart - 1
+ lineno_str = str(current_lineno).rjust(linenos_width)
+ else:
+ lineno_str = "".rjust(linenos_width)
+ outfile.write(self._lineno_template % lineno_str)
+
while not self.style.styles_token(ttype) and ttype.parent:
ttype = ttype.parent
style = self.style.style_for_token(ttype)
buf = []
if style['bgcolor']:
- buf.append('\\cb%d' % color_mapping[style['bgcolor']])
+ buf.append('\\cb%d' % self.color_mapping[style['bgcolor']])
if style['color']:
- buf.append('\\cf%d' % color_mapping[style['color']])
+ buf.append('\\cf%d' % self.color_mapping[style['color']])
if style['bold']:
buf.append('\\b')
if style['italic']:
@@ -135,12 +326,24 @@ class RtfFormatter(Formatter):
buf.append('\\ul')
if style['border']:
buf.append('\\chbrdr\\chcfpat%d' %
- color_mapping[style['border']])
+ self.color_mapping[style['border']])
start = ''.join(buf)
if start:
- outfile.write('{%s ' % start)
+ outfile.write(f'{{{start} ')
outfile.write(self._escape_text(value))
if start:
outfile.write('}')
+ start_new_line = False
+
+ # complete line of input
+ if value.endswith("\n"):
+ # close line highlighting
+ if lineno in self.hl_lines:
+ outfile.write('}')
+ # newline in RTF file after closing }
+ outfile.write("\n")
+
+ start_new_line = True
+ lineno += 1
- outfile.write('}')
+ outfile.write('}\n')