Back to index

moin  1.9.0~rc2
terminal256.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 """
00003     pygments.formatters.terminal256
00004     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
00005 
00006     Formatter for 256-color terminal output with ANSI sequences.
00007 
00008     RGB-to-XTERM color conversion routines adapted from xterm256-conv
00009     tool (http://frexx.de/xterm-256-notes/data/xterm256-conv2.tar.bz2)
00010     by Wolfgang Frisch.
00011 
00012     Formatter version 1.
00013 
00014     :copyright: Copyright 2006-2009 by the Pygments team, see AUTHORS.
00015     :license: BSD, see LICENSE for details.
00016 """
00017 
00018 # TODO:
00019 #  - Options to map style's bold/underline/italic/border attributes
00020 #    to some ANSI attrbutes (something like 'italic=underline')
00021 #  - An option to output "style RGB to xterm RGB/index" conversion table
00022 #  - An option to indicate that we are running in "reverse background"
00023 #    xterm. This means that default colors are white-on-black, not
00024 #    black-on-while, so colors like "white background" need to be converted
00025 #    to "white background, black foreground", etc...
00026 
00027 from pygments.formatter import Formatter
00028 
00029 
00030 __all__ = ['Terminal256Formatter']
00031 
00032 
00033 class EscapeSequence:
00034     def __init__(self, fg=None, bg=None, bold=False, underline=False):
00035         self.fg = fg
00036         self.bg = bg
00037         self.bold = bold
00038         self.underline = underline
00039 
00040     def escape(self, attrs):
00041         if len(attrs):
00042             return "\x1b[" + ";".join(attrs) + "m"
00043         return ""
00044 
00045     def color_string(self):
00046         attrs = []
00047         if self.fg is not None:
00048             attrs.extend(("38", "5", "%i" % self.fg))
00049         if self.bg is not None:
00050             attrs.extend(("48", "5", "%i" % self.bg))
00051         if self.bold:
00052             attrs.append("01")
00053         if self.underline:
00054             attrs.append("04")
00055         return self.escape(attrs)
00056 
00057     def reset_string(self):
00058         attrs = []
00059         if self.fg is not None:
00060             attrs.append("39")
00061         if self.bg is not None:
00062             attrs.append("49")
00063         if self.bold or self.underline:
00064             attrs.append("00")
00065         return self.escape(attrs)
00066 
00067 class Terminal256Formatter(Formatter):
00068     r"""
00069     Format tokens with ANSI color sequences, for output in a 256-color
00070     terminal or console. Like in `TerminalFormatter` color sequences
00071     are terminated at newlines, so that paging the output works correctly.
00072 
00073     The formatter takes colors from a style defined by the `style` option
00074     and converts them to nearest ANSI 256-color escape sequences. Bold and
00075     underline attributes from the style are preserved (and displayed).
00076 
00077     *New in Pygments 0.9.*
00078 
00079     Options accepted:
00080 
00081     `style`
00082         The style to use, can be a string or a Style subclass (default:
00083         ``'default'``).
00084     """
00085     name = 'Terminal256'
00086     aliases = ['terminal256', 'console256', '256']
00087     filenames = []
00088 
00089     def __init__(self, **options):
00090         Formatter.__init__(self, **options)
00091 
00092         self.xterm_colors = []
00093         self.best_match = {}
00094         self.style_string = {}
00095 
00096         self.usebold = 'nobold' not in options
00097         self.useunderline = 'nounderline' not in options
00098 
00099         self._build_color_table() # build an RGB-to-256 color conversion table
00100         self._setup_styles() # convert selected style's colors to term. colors
00101 
00102     def _build_color_table(self):
00103         # colors 0..15: 16 basic colors
00104 
00105         self.xterm_colors.append((0x00, 0x00, 0x00)) # 0
00106         self.xterm_colors.append((0xcd, 0x00, 0x00)) # 1
00107         self.xterm_colors.append((0x00, 0xcd, 0x00)) # 2
00108         self.xterm_colors.append((0xcd, 0xcd, 0x00)) # 3
00109         self.xterm_colors.append((0x00, 0x00, 0xee)) # 4
00110         self.xterm_colors.append((0xcd, 0x00, 0xcd)) # 5
00111         self.xterm_colors.append((0x00, 0xcd, 0xcd)) # 6
00112         self.xterm_colors.append((0xe5, 0xe5, 0xe5)) # 7
00113         self.xterm_colors.append((0x7f, 0x7f, 0x7f)) # 8
00114         self.xterm_colors.append((0xff, 0x00, 0x00)) # 9
00115         self.xterm_colors.append((0x00, 0xff, 0x00)) # 10
00116         self.xterm_colors.append((0xff, 0xff, 0x00)) # 11
00117         self.xterm_colors.append((0x5c, 0x5c, 0xff)) # 12
00118         self.xterm_colors.append((0xff, 0x00, 0xff)) # 13
00119         self.xterm_colors.append((0x00, 0xff, 0xff)) # 14
00120         self.xterm_colors.append((0xff, 0xff, 0xff)) # 15
00121 
00122         # colors 16..232: the 6x6x6 color cube
00123 
00124         valuerange = (0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff)
00125 
00126         for i in range(217):
00127             r = valuerange[(i // 36) % 6]
00128             g = valuerange[(i // 6) % 6]
00129             b = valuerange[i % 6]
00130             self.xterm_colors.append((r, g, b))
00131 
00132         # colors 233..253: grayscale
00133 
00134         for i in range(1, 22):
00135             v = 8 + i * 10
00136             self.xterm_colors.append((v, v, v))
00137 
00138     def _closest_color(self, r, g, b):
00139         distance = 257*257*3 # "infinity" (>distance from #000000 to #ffffff)
00140         match = 0
00141 
00142         for i in range(0, 254):
00143             values = self.xterm_colors[i]
00144 
00145             rd = r - values[0]
00146             gd = g - values[1]
00147             bd = b - values[2]
00148             d = rd*rd + gd*gd + bd*bd
00149 
00150             if d < distance:
00151                 match = i
00152                 distance = d
00153         return match
00154 
00155     def _color_index(self, color):
00156         index = self.best_match.get(color, None)
00157         if index is None:
00158             try:
00159                 rgb = int(str(color), 16)
00160             except ValueError:
00161                 rgb = 0
00162 
00163             r = (rgb >> 16) & 0xff
00164             g = (rgb >> 8) & 0xff
00165             b = rgb & 0xff
00166             index = self._closest_color(r, g, b)
00167             self.best_match[color] = index
00168         return index
00169 
00170     def _setup_styles(self):
00171         for ttype, ndef in self.style:
00172             escape = EscapeSequence()
00173             if ndef['color']:
00174                 escape.fg = self._color_index(ndef['color'])
00175             if ndef['bgcolor']:
00176                 escape.bg = self._color_index(ndef['bgcolor'])
00177             if self.usebold and ndef['bold']:
00178                 escape.bold = True
00179             if self.useunderline and ndef['underline']:
00180                 escape.underline = True
00181             self.style_string[str(ttype)] = (escape.color_string(),
00182                                              escape.reset_string())
00183 
00184     def format(self, tokensource, outfile):
00185         # hack: if the output is a terminal and has an encoding set,
00186         # use that to avoid unicode encode problems
00187         if not self.encoding and hasattr(outfile, "encoding") and \
00188            hasattr(outfile, "isatty") and outfile.isatty():
00189             self.encoding = outfile.encoding
00190         return Formatter.format(self, tokensource, outfile)
00191 
00192     def format_unencoded(self, tokensource, outfile):
00193         for ttype, value in tokensource:
00194             not_found = True
00195             while ttype and not_found:
00196                 try:
00197                     #outfile.write( "<" + str(ttype) + ">" )
00198                     on, off = self.style_string[str(ttype)]
00199 
00200                     # Like TerminalFormatter, add "reset colors" escape sequence
00201                     # on newline.
00202                     spl = value.split('\n')
00203                     for line in spl[:-1]:
00204                         if line:
00205                             outfile.write(on + line + off)
00206                         outfile.write('\n')
00207                     if spl[-1]:
00208                         outfile.write(on + spl[-1] + off)
00209 
00210                     not_found = False
00211                     #outfile.write( '#' + str(ttype) + '#' )
00212 
00213                 except KeyError:
00214                     #ottype = ttype
00215                     ttype = ttype[:-1]
00216                     #outfile.write( '!' + str(ottype) + '->' + str(ttype) + '!' )
00217 
00218             if not_found:
00219                 outfile.write(value)