blob: 205e73f6e57f846b016e182d5c3808b584c1b172 [file] [log] [blame]
# -*- coding: utf-8 -*-
#
# Copyright (C) 2004-2009 Edgewall Software
# Copyright (C) 2004 Daniel Lundin <daniel@edgewall.com>
# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at http://trac.edgewall.org/wiki/TracLicense.
#
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
#
# Author: Daniel Lundin <daniel@edgewall.com>
from genshi.core import Markup
from trac.config import Option, ListOption
from trac.core import *
from trac.mimeview.api import IHTMLPreviewRenderer, Mimeview
from trac.util import NaivePopen
from trac.util.html import Deuglifier
__all__ = ['EnscriptRenderer']
types = {
'application/xhtml+xml': ('html', 2),
'application/postscript': ('postscript', 2),
'application/x-csh': ('csh', 2),
'application/x-javascript': ('javascript', 2),
'application/x-troff': ('nroff', 2),
'text/html': ('html', 2),
'text/x-ada': ('ada', 2),
'text/x-asm': ('asm', 2),
'text/x-awk': ('awk', 2),
'text/x-c++src': ('cpp', 2),
'text/x-c++hdr': ('cpp', 2),
'text/x-chdr': ('c', 2),
'text/x-csh': ('csh', 2),
'text/x-csrc': ('c', 2),
'text/x-diff': ('diffu', 2), # Assume unified diff (works otherwise)
'text/x-eiffel': ('eiffel', 2),
'text/x-elisp': ('elisp', 2),
'text/x-fortran': ('fortran', 2),
'text/x-haskell': ('haskell', 2),
'text/x-idl': ('idl', 2),
'text/x-inf': ('inf', 2),
'text/x-java': ('java', 2),
'text/x-javascript': ('javascript', 2),
'text/x-ksh': ('ksh', 2),
'text/x-lua': ('lua', 2),
'text/x-m4': ('m4', 2),
'text/x-makefile': ('makefile', 2),
'text/x-mail': ('mail', 2),
'text/x-matlab': ('matlab', 2),
'text/x-objc': ('objc', 2),
'text/x-pascal': ('pascal', 2),
'text/x-perl': ('perl', 2),
'text/x-pyrex': ('pyrex', 2),
'text/x-python': ('python', 2),
'text/x-rfc': ('rfc', 2),
'text/x-ruby': ('ruby', 2),
'text/x-sh': ('sh', 2),
'text/x-scheme': ('scheme', 2),
'text/x-sql': ('sql', 2),
'text/x-tcl': ('tcl', 2),
'text/x-tex': ('tex', 2),
'text/x-vba': ('vba', 2),
'text/x-verilog': ('verilog', 2),
'text/x-vhdl': ('vhdl', 2),
'model/vrml': ('vrml', 2),
'application/x-sh': ('sh', 2),
'text/x-zsh': ('zsh', 2),
'text/vnd.wap.wmlscript': ('wmlscript', 2),
}
class EnscriptDeuglifier(Deuglifier):
@classmethod
def rules(cls):
return [
r'(?P<comment><FONT COLOR="#B22222">)',
r'(?P<keyword><FONT COLOR="#5F9EA0">)',
r'(?P<type><FONT COLOR="#228B22">)',
r'(?P<string><FONT COLOR="#BC8F8F">)',
r'(?P<func><FONT COLOR="#0000FF">)',
r'(?P<prep><FONT COLOR="#B8860B">)',
r'(?P<lang><FONT COLOR="#A020F0">)',
r'(?P<var><FONT COLOR="#DA70D6">)',
r'(?P<font><FONT.*?>)',
r'(?P<endfont></FONT>)'
]
class EnscriptRenderer(Component):
"""Syntax highlighter using GNU Enscript."""
implements(IHTMLPreviewRenderer)
expand_tabs = True
returns_source = True
path = Option('mimeviewer', 'enscript_path', 'enscript',
"""Path to the Enscript executable.""")
enscript_modes = ListOption('mimeviewer', 'enscript_modes',
'text/x-dylan:dylan:4', doc=
"""List of additional MIME types known by Enscript.
For each, a tuple `mimetype:mode:quality` has to be
specified, where `mimetype` is the MIME type,
`mode` is the corresponding Enscript mode to be used
for the conversion and `quality` is the quality ratio
associated to this conversion.
That can also be used to override the default
quality ratio used by the Enscript render, which is 2
(''since 0.10'').""")
def __init__(self):
self._types = None
# IHTMLPreviewRenderer methods
def get_quality_ratio(self, mimetype):
# Extend default MIME type to mode mappings with configured ones
if not self._types:
self._types = {}
self._types.update(types)
self._types.update(
Mimeview(self.env).configured_modes_mapping('enscript'))
return self._types.get(mimetype, (None, 0))[1]
def render(self, context, mimetype, content, filename=None, rev=None):
cmdline = self.path
mimetype = mimetype.split(';', 1)[0] # strip off charset
mode = self._types[mimetype][0]
cmdline += ' --color -h -q --language=html -p - -E%s' % mode
self.log.debug("Enscript command line: %s" % cmdline)
np = NaivePopen(cmdline, content.encode('utf-8'), capturestderr=1)
if np.errorlevel or np.err:
self.env.disable_component(self)
err = "Running enscript failed with (%s, %s), disabling " \
"EnscriptRenderer (command: '%s')" \
% (np.errorlevel, np.err.strip(), cmdline)
raise Exception(err)
odata = np.out
# Strip header and footer
i = odata.find('<PRE>')
beg = i > 0 and i + 6
i = odata.rfind('</PRE>')
end = i if i > 0 else len(odata)
odata = EnscriptDeuglifier().format(odata[beg:end].decode('utf-8'))
return [Markup(line) for line in odata.splitlines()]