147 lines
4.9 KiB
Python
147 lines
4.9 KiB
Python
|
"""
|
||
|
pygments.formatters.rtf
|
||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
|
||
|
A formatter that generates RTF files.
|
||
|
|
||
|
:copyright: Copyright 2006-2022 by the Pygments team, see AUTHORS.
|
||
|
:license: BSD, see LICENSE for details.
|
||
|
"""
|
||
|
|
||
|
from pygments.formatter import Formatter
|
||
|
from pygments.util import get_int_opt, surrogatepair
|
||
|
|
||
|
|
||
|
__all__ = ['RtfFormatter']
|
||
|
|
||
|
|
||
|
class RtfFormatter(Formatter):
|
||
|
"""
|
||
|
Format tokens as RTF markup. This formatter automatically outputs full RTF
|
||
|
documents with color information and other useful stuff. Perfect for Copy and
|
||
|
Paste into Microsoft(R) Word(R) documents.
|
||
|
|
||
|
Please note that ``encoding`` and ``outencoding`` options are ignored.
|
||
|
The RTF format is ASCII natively, but handles unicode characters correctly
|
||
|
thanks to escape sequences.
|
||
|
|
||
|
.. versionadded:: 0.6
|
||
|
|
||
|
Additional options accepted:
|
||
|
|
||
|
`style`
|
||
|
The style to use, can be a string or a Style subclass (default:
|
||
|
``'default'``).
|
||
|
|
||
|
`fontface`
|
||
|
The used font family, for example ``Bitstream Vera Sans``. Defaults to
|
||
|
some generic font which is supposed to have fixed width.
|
||
|
|
||
|
`fontsize`
|
||
|
Size of the font used. Size is specified in half points. The
|
||
|
default is 24 half-points, giving a size 12 font.
|
||
|
|
||
|
.. versionadded:: 2.0
|
||
|
"""
|
||
|
name = 'RTF'
|
||
|
aliases = ['rtf']
|
||
|
filenames = ['*.rtf']
|
||
|
|
||
|
def __init__(self, **options):
|
||
|
r"""
|
||
|
Additional options accepted:
|
||
|
|
||
|
``fontface``
|
||
|
Name of the font used. Could for example be ``'Courier New'``
|
||
|
to further specify the default which is ``'\fmodern'``. The RTF
|
||
|
specification claims that ``\fmodern`` are "Fixed-pitch serif
|
||
|
and sans serif fonts". Hope every RTF implementation thinks
|
||
|
the same about modern...
|
||
|
|
||
|
"""
|
||
|
Formatter.__init__(self, **options)
|
||
|
self.fontface = options.get('fontface') or ''
|
||
|
self.fontsize = get_int_opt(options, 'fontsize', 0)
|
||
|
|
||
|
def _escape(self, text):
|
||
|
return text.replace('\\', '\\\\') \
|
||
|
.replace('{', '\\{') \
|
||
|
.replace('}', '\\}')
|
||
|
|
||
|
def _escape_text(self, text):
|
||
|
# empty strings, should give a small performance improvement
|
||
|
if not text:
|
||
|
return ''
|
||
|
|
||
|
# escape text
|
||
|
text = self._escape(text)
|
||
|
|
||
|
buf = []
|
||
|
for c in text:
|
||
|
cn = ord(c)
|
||
|
if cn < (2**7):
|
||
|
# ASCII character
|
||
|
buf.append(str(c))
|
||
|
elif (2**7) <= cn < (2**16):
|
||
|
# single unicode escape sequence
|
||
|
buf.append('{\\u%d}' % cn)
|
||
|
elif (2**16) <= cn:
|
||
|
# RTF limits unicode to 16 bits.
|
||
|
# Force surrogate pairs
|
||
|
buf.append('{\\u%d}{\\u%d}' % surrogatepair(cn))
|
||
|
|
||
|
return ''.join(buf).replace('\n', '\\par\n')
|
||
|
|
||
|
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 = {}
|
||
|
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 ')
|
||
|
if self.fontsize:
|
||
|
outfile.write('\\fs%d' % self.fontsize)
|
||
|
|
||
|
# highlight stream
|
||
|
for ttype, value in tokensource:
|
||
|
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']])
|
||
|
if style['color']:
|
||
|
buf.append('\\cf%d' % color_mapping[style['color']])
|
||
|
if style['bold']:
|
||
|
buf.append('\\b')
|
||
|
if style['italic']:
|
||
|
buf.append('\\i')
|
||
|
if style['underline']:
|
||
|
buf.append('\\ul')
|
||
|
if style['border']:
|
||
|
buf.append('\\chbrdr\\chcfpat%d' %
|
||
|
color_mapping[style['border']])
|
||
|
start = ''.join(buf)
|
||
|
if start:
|
||
|
outfile.write('{%s ' % start)
|
||
|
outfile.write(self._escape_text(value))
|
||
|
if start:
|
||
|
outfile.write('}')
|
||
|
|
||
|
outfile.write('}')
|