Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
rtf.py350 linesDownload Raw Back to formatters
1"""2    pygments.formatters.rtf3    ~~~~~~~~~~~~~~~~~~~~~~~4 5    A formatter that generates RTF files.6 7    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.8    :license: BSD, see LICENSE for details.9"""10 11from collections import OrderedDict12from pygments.formatter import Formatter13from pygments.style import _ansimap14from pygments.util import get_bool_opt, get_int_opt, get_list_opt, surrogatepair15 16 17__all__ = ['RtfFormatter']18 19 20class RtfFormatter(Formatter):21    """22    Format tokens as RTF markup. This formatter automatically outputs full RTF23    documents with color information and other useful stuff. Perfect for Copy and24    Paste into Microsoft(R) Word(R) documents.25 26    Please note that ``encoding`` and ``outencoding`` options are ignored.27    The RTF format is ASCII natively, but handles unicode characters correctly28    thanks to escape sequences.29 30    .. versionadded:: 0.631 32    Additional options accepted:33 34    `style`35        The style to use, can be a string or a Style subclass (default:36        ``'default'``).37 38    `fontface`39        The used font family, for example ``Bitstream Vera Sans``. Defaults to40        some generic font which is supposed to have fixed width.41 42    `fontsize`43        Size of the font used. Size is specified in half points. The44        default is 24 half-points, giving a size 12 font.45 46        .. versionadded:: 2.047 48    `linenos`49        Turn on line numbering (default: ``False``).50 51        .. versionadded:: 2.1852 53    `lineno_fontsize`54        Font size for line numbers. Size is specified in half points55        (default: `fontsize`). 56 57        .. versionadded:: 2.1858 59    `lineno_padding`60        Number of spaces between the (inline) line numbers and the61        source code (default: ``2``).62 63        .. versionadded:: 2.1864 65    `linenostart`66        The line number for the first line (default: ``1``).67 68        .. versionadded:: 2.1869 70    `linenostep`71        If set to a number n > 1, only every nth line number is printed.72 73        .. versionadded:: 2.1874 75    `lineno_color`76        Color for line numbers specified as a hex triplet, e.g. ``'5e5e5e'``. 77        Defaults to the style's line number color if it is a hex triplet, 78        otherwise ansi bright black.79 80        .. versionadded:: 2.1881 82    `hl_lines`83        Specify a list of lines to be highlighted, as line numbers separated by84        spaces, e.g. ``'3 7 8'``. The line numbers are relative to the input 85        (i.e. the first line is line 1) unless `hl_linenostart` is set.86 87        .. versionadded:: 2.1888 89    `hl_color`90        Color for highlighting the lines specified in `hl_lines`, specified as 91        a hex triplet (default: style's `highlight_color`).92 93        .. versionadded:: 2.1894 95    `hl_linenostart`96        If set to ``True`` line numbers in `hl_lines` are specified97        relative to `linenostart` (default ``False``).98 99        .. versionadded:: 2.18100    """101    name = 'RTF'102    aliases = ['rtf']103    filenames = ['*.rtf']104 105    def __init__(self, **options):106        r"""107        Additional options accepted:108 109        ``fontface``110            Name of the font used. Could for example be ``'Courier New'``111            to further specify the default which is ``'\fmodern'``. The RTF112            specification claims that ``\fmodern`` are "Fixed-pitch serif113            and sans serif fonts". Hope every RTF implementation thinks114            the same about modern...115 116        """117        Formatter.__init__(self, **options)118        self.fontface = options.get('fontface') or ''119        self.fontsize = get_int_opt(options, 'fontsize', 0)120        self.linenos = get_bool_opt(options, 'linenos', False)121        self.lineno_fontsize = get_int_opt(options, 'lineno_fontsize',122                                           self.fontsize)123        self.lineno_padding = get_int_opt(options, 'lineno_padding', 2)124        self.linenostart = abs(get_int_opt(options, 'linenostart', 1))125        self.linenostep = abs(get_int_opt(options, 'linenostep', 1))126        self.hl_linenostart = get_bool_opt(options, 'hl_linenostart', False)127 128        self.hl_color = options.get('hl_color', '')129        if not self.hl_color:130            self.hl_color = self.style.highlight_color131 132        self.hl_lines = []133        for lineno in get_list_opt(options, 'hl_lines', []):134            try:135                lineno = int(lineno)136                if self.hl_linenostart:137                    lineno = lineno - self.linenostart + 1138                self.hl_lines.append(lineno)139            except ValueError:140                pass141 142        self.lineno_color = options.get('lineno_color', '')143        if not self.lineno_color:144            if  self.style.line_number_color == 'inherit':145                # style color is the css value 'inherit'146                # default to ansi bright-black147                self.lineno_color = _ansimap['ansibrightblack']148            else:149                # style color is assumed to be a hex triplet as other150                # colors in pygments/style.py151                self.lineno_color = self.style.line_number_color152 153        self.color_mapping = self._create_color_mapping()154 155    def _escape(self, text):156        return text.replace('\\', '\\\\') \157                   .replace('{', '\\{') \158                   .replace('}', '\\}')159 160    def _escape_text(self, text):161        # empty strings, should give a small performance improvement162        if not text:163            return ''164 165        # escape text166        text = self._escape(text)167 168        buf = []169        for c in text:170            cn = ord(c)171            if cn < (2**7):172                # ASCII character173                buf.append(str(c))174            elif (2**7) <= cn < (2**16):175                # single unicode escape sequence176                buf.append('{\\u%d}' % cn)177            elif (2**16) <= cn:178                # RTF limits unicode to 16 bits.179                # Force surrogate pairs180                buf.append('{\\u%d}{\\u%d}' % surrogatepair(cn))181 182        return ''.join(buf).replace('\n', '\\par')183 184    @staticmethod185    def hex_to_rtf_color(hex_color):186        if hex_color[0] == "#":187            hex_color = hex_color[1:]188 189        return '\\red%d\\green%d\\blue%d;' % (190                        int(hex_color[0:2], 16),191                        int(hex_color[2:4], 16),192                        int(hex_color[4:6], 16)193                    )194 195    def _split_tokens_on_newlines(self, tokensource):196        """197        Split tokens containing newline characters into multiple token198        each representing a line of the input file. Needed for numbering199        lines of e.g. multiline comments.200        """201        for ttype, value in tokensource:202            if value == '\n':203                yield (ttype, value)204            elif "\n" in value:205                lines = value.split("\n")206                for line in lines[:-1]:207                    yield (ttype, line+"\n")208                if lines[-1]:209                    yield (ttype, lines[-1])210            else:211                yield (ttype, value)212 213    def _create_color_mapping(self):214        """215        Create a mapping of style hex colors to index/offset in216        the RTF color table.217        """218        color_mapping = OrderedDict()219        offset = 1220 221        if self.linenos:222            color_mapping[self.lineno_color] = offset223            offset += 1224 225        if self.hl_lines:226            color_mapping[self.hl_color] = offset227            offset += 1228 229        for _, style in self.style:230            for color in style['color'], style['bgcolor'], style['border']:231                if color and color not in color_mapping:232                    color_mapping[color] = offset233                    offset += 1234 235        return color_mapping236 237    @property238    def _lineno_template(self):239        if self.lineno_fontsize != self.fontsize:240            return '{{\\fs{} \\cf{} %s{}}}'.format(self.lineno_fontsize,241                          self.color_mapping[self.lineno_color],242                          " " * self.lineno_padding)243 244        return '{{\\cf{} %s{}}}'.format(self.color_mapping[self.lineno_color],245                      " " * self.lineno_padding)246 247    @property248    def _hl_open_str(self):249        return rf'{{\highlight{self.color_mapping[self.hl_color]} '250 251    @property252    def _rtf_header(self):253        lines = []254        # rtf 1.8 header255        lines.append('{\\rtf1\\ansi\\uc0\\deff0'256                     '{\\fonttbl{\\f0\\fmodern\\fprq1\\fcharset0%s;}}'257                     % (self.fontface and ' '258                        + self._escape(self.fontface) or ''))259 260        # color table261        lines.append('{\\colortbl;')262        for color, _ in self.color_mapping.items():263            lines.append(self.hex_to_rtf_color(color))264        lines.append('}')265 266        # font and fontsize267        lines.append('\\f0\\sa0')268        if self.fontsize:269            lines.append('\\fs%d' % self.fontsize)270 271        # ensure Libre Office Writer imports and renders consecutive272        # space characters the same width, needed for line numbering.273        # https://bugs.documentfoundation.org/show_bug.cgi?id=144050274        lines.append('\\dntblnsbdb')275 276        return lines277 278    def format_unencoded(self, tokensource, outfile):279        for line in self._rtf_header:280            outfile.write(line + "\n")281 282        tokensource = self._split_tokens_on_newlines(tokensource)283 284        # first pass of tokens to count lines, needed for line numbering285        if self.linenos:286            line_count = 0287            tokens = [] # for copying the token source generator288            for ttype, value in tokensource:289                tokens.append((ttype, value))290                if value.endswith("\n"):291                    line_count += 1292 293            # width of line number strings (for padding with spaces)294            linenos_width = len(str(line_count+self.linenostart-1))295 296            tokensource = tokens297 298        # highlight stream299        lineno = 1300        start_new_line = True301        for ttype, value in tokensource:302            if start_new_line and lineno in self.hl_lines:303                outfile.write(self._hl_open_str)304 305            if start_new_line and self.linenos:306                if (lineno-self.linenostart+1)%self.linenostep == 0:307                    current_lineno = lineno + self.linenostart - 1308                    lineno_str = str(current_lineno).rjust(linenos_width)309                else:310                    lineno_str = "".rjust(linenos_width)311                outfile.write(self._lineno_template % lineno_str)312 313            while not self.style.styles_token(ttype) and ttype.parent:314                ttype = ttype.parent315            style = self.style.style_for_token(ttype)316            buf = []317            if style['bgcolor']:318                buf.append('\\cb%d' % self.color_mapping[style['bgcolor']])319            if style['color']:320                buf.append('\\cf%d' % self.color_mapping[style['color']])321            if style['bold']:322                buf.append('\\b')323            if style['italic']:324                buf.append('\\i')325            if style['underline']:326                buf.append('\\ul')327            if style['border']:328                buf.append('\\chbrdr\\chcfpat%d' %329                           self.color_mapping[style['border']])330            start = ''.join(buf)331            if start:332                outfile.write(f'{{{start} ')333            outfile.write(self._escape_text(value))334            if start:335                outfile.write('}')336            start_new_line = False337 338            # complete line of input339            if value.endswith("\n"):340                # close line highlighting341                if lineno in self.hl_lines:342                    outfile.write('}')343                # newline in RTF file after closing }344                outfile.write("\n")345 346                start_new_line = True347                lineno += 1348 349        outfile.write('}\n')350 
codekingpro/portable-devtools · Team Ai