| # -*- 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()] |