--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/QScintilla/Exporters/ExporterHTML.py Mon Dec 28 16:03:33 2009 +0000 @@ -0,0 +1,364 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2007 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing an exporter for HTML. +""" + +# This code is a port of the C++ code found in SciTE 1.74 +# Original code: Copyright 1998-2006 by Neil Hodgson <neilh@scintilla.org> + +import os + +from PyQt4.QtCore import * +from PyQt4.QtGui import * +from PyQt4.Qsci import QsciScintilla + +from ExporterBase import ExporterBase + +import Preferences +import Utilities + +class ExporterHTML(ExporterBase): + """ + Class implementing an exporter for HTML. + """ + def __init__(self, editor, parent = None): + """ + Constructor + + @param editor reference to the editor object (QScintilla.Editor.Editor) + @param parent parent object of the exporter (QObject) + """ + ExporterBase.__init__(self, editor, parent) + + def exportSource(self): + """ + Public method performing the export. + """ + filename = self._getFileName(self.trUtf8("HTML Files (*.html)")) + if not filename: + return + + try: + QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + QApplication.processEvents() + + self.editor.recolor(0, -1) + + tabSize = Preferences.getEditor("TabWidth") + if tabSize == 0: + tabSize = 4 + wysiwyg = Preferences.getEditorExporter("HTML/WYSIWYG") + folding = Preferences.getEditorExporter("HTML/Folding") + onlyStylesUsed = Preferences.getEditorExporter("HTML/OnlyStylesUsed") + titleFullPath = Preferences.getEditorExporter("HTML/FullPathAsTitle") + tabs = Preferences.getEditorExporter("HTML/UseTabs") + + lengthDoc = self.editor.length() + styleIsUsed = {} + if onlyStylesUsed: + for index in range(QsciScintilla.STYLE_MAX + 1): + styleIsUsed[index] = False + # check the used styles + pos = 0 + while pos < lengthDoc: + styleIsUsed[self.editor.styleAt(pos) & 0x7F] = True + pos += 1 + else: + for index in range(QsciScintilla.STYLE_MAX + 1): + styleIsUsed[index] = True + styleIsUsed[QsciScintilla.STYLE_DEFAULT] = True + + try: + f = open(filename, "wb") + + f.write( + '''<!DOCTYPE html PUBLIC "-//W3C//DTD''' + ''' XHTML 1.0 Transitional//EN"\n''') + f.write( + ''' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n''') + f.write('''<html xmlns="http://www.w3.org/1999/xhtml">\n''') + f.write('''<head>\n''') + if titleFullPath: + f.write('''<title>%s</title>\n''' % self.editor.getFileName()) + else: + f.write('''<title>%s</title>\n''' % \ + os.path.basename(self.editor.getFileName())) + f.write('''<meta name="Generator" content="eric4" />\n''') + f.write('''<meta http-equiv="Content-Type" ''' + '''content="text/html; charset=utf-8" />\n''') + if folding: + f.write('''<script language="JavaScript" type="text/javascript">\n''' + '''<!--\n''' + '''function symbol(id, sym) {\n''' + ''' if (id.textContent == undefined) {\n''' + ''' id.innerText = sym;\n''' + ''' } else {\n''' + ''' id.textContent = sym;\n''' + ''' }\n''' + '''}\n''' + '''function toggle(id) {\n''' + ''' var thislayer = document.getElementById('ln' + id);\n''' + ''' id -= 1;\n''' + ''' var togline = document.getElementById('hd' + id);\n''' + ''' var togsym = document.getElementById('bt' + id);\n''' + ''' if (thislayer.style.display == 'none') {\n''' + ''' thislayer.style.display = 'block';\n''' + ''' togline.style.textDecoration = 'none';\n''' + ''' symbol(togsym, '- ');\n''' + ''' } else {\n''' + ''' thislayer.style.display = 'none';\n''' + ''' togline.style.textDecoration = 'underline';\n''' + ''' symbol(togsym, '+ ');\n''' + ''' }\n''' + '''}\n''' + '''//-->\n''' + '''</script>\n''' + ) + + lex = self.editor.getLexer() + if lex: + bgColour = lex.paper(QsciScintilla.STYLE_DEFAULT).name() + else: + bgColour = self.editor.paper().name() + + f.write('''<style type="text/css">\n''') + if lex: + istyle = 0 + while istyle <= QsciScintilla.STYLE_MAX: + if (istyle <= QsciScintilla.STYLE_DEFAULT or \ + istyle > QsciScintilla.STYLE_LASTPREDEFINED) and \ + styleIsUsed[istyle]: + if lex.description(istyle) or \ + istyle == QsciScintilla.STYLE_DEFAULT: + font = lex.font(istyle) + colour = lex.color(istyle) + paper = lex.paper(istyle) + if istyle == QsciScintilla.STYLE_DEFAULT: + f.write('''span {\n''') + else: + f.write('''.S%d {\n''' % istyle) + if font.italic(): + f.write(''' font-style: italic;\n''') + if font.bold(): + f.write(''' font-weight: bold;\n''') + if wysiwyg: + f.write(''' font-family: '%s';\n''' % \ + font.family()) + f.write(''' color: %s;\n''' % colour.name()) + if istyle != QsciScintilla.STYLE_DEFAULT and \ + bgColour != paper.name(): + f.write(''' background: %s;\n''' % paper.name()) + f.write(''' text-decoration: inherit;\n''') + if wysiwyg: + f.write(''' font-size: %dpt;\n''' % \ + QFontInfo(font).pointSize()) + f.write('''}\n''') + else: + styleIsUsed[istyle] = False + istyle += 1 + else: + colour = self.editor.color() + paper = self.editor.paper() + font = Preferences.getEditorOtherFonts("DefaultFont") + f.write('''.S0 {\n''') + if font.italic(): + f.write(''' font-style: italic;\n''') + if font.bold(): + f.write(''' font-weight: bold;\n''') + if wysiwyg: + f.write(''' font-family: '%s';\n''' % font.family()) + f.write(''' color: %s;\n''' % colour.name()) + if bgColour != paper.name(): + f.write(''' background: %s;\n''' % paper.name()) + f.write(''' text-decoration: inherit;\n''') + if wysiwyg: + f.write(''' font-size: %dpt;\n''' % \ + QFontInfo(font).pointSize()) + f.write('''}\n''') + f.write('''</style>\n''') + f.write('''</head>\n''') + + f.write('''<body bgcolor="%s">\n''' % bgColour) + line = self.editor.lineAt(0) + level = self.editor.foldLevelAt(line) - QsciScintilla.SC_FOLDLEVELBASE + levelStack = [level] + styleCurrent = self.editor.styleAt(0) + inStyleSpan = False + inFoldSpan = False + # Global span for default attributes + if wysiwyg: + f.write('''<span>''') + else: + f.write('''<pre>''') + + if folding: + if self.editor.foldFlagsAt(line) & \ + QsciScintilla.SC_FOLDLEVELHEADERFLAG: + f.write('''<span id="hd%d" onclick="toggle('%d')">''' % \ + (line, line + 1)) + f.write('''<span id="bt%d">- </span>''' % line) + inFoldSpan = True + else: + f.write(''' ''') + + if styleIsUsed[styleCurrent]: + f.write('''<span class="S%0d">''' % styleCurrent) + inStyleSpan = True + + column = 0 + pos = 0 + utf8 = self.editor.isUtf8() + utf8Ch = "" + utf8Len = 0 + + while pos < lengthDoc: + ch = self.editor.rawCharAt(pos) + style = self.editor.styleAt(pos) + if style != styleCurrent: + if inStyleSpan: + f.write('''</span>''') + inStyleSpan = False + if ch not in ['\r', '\n']: # no need of a span for the EOL + if styleIsUsed[style]: + f.write('''<span class="S%d">''' % style) + inStyleSpan = True + styleCurrent = style + + if ch == ' ': + if wysiwyg: + prevCh = '' + if column == 0: + # at start of line, must put a + # because regular space will be collapsed + prevCh = ' ' + while pos < lengthDoc and self.editor.rawCharAt(pos) == ' ': + if prevCh != ' ': + f.write(' ') + else: + f.write(''' ''') + prevCh = self.editor.rawCharAt(pos) + pos += 1 + column += 1 + pos -= 1 + # the last incrementation will be done by the outer loop + else: + f.write(' ') + column += 1 + elif ch == '\t': + ts = tabSize - (column % tabSize) + if wysiwyg: + f.write(''' ''' * ts) + column += ts + else: + if tabs: + f.write(ch) + column += 1 + else: + f.write(' ' * ts) + column += ts + elif ch in ['\r', '\n']: + if inStyleSpan: + f.write('''</span>''') + inStyleSpan = False + if inFoldSpan: + f.write('''</span>''') + inFoldSpan = False + if ch == '\r' and self.editor.rawCharAt(pos + 1) == '\n': + pos += 1 # CR+LF line ending, skip the "extra" EOL char + column = 0 + if wysiwyg: + f.write('''<br />''') + + styleCurrent = self.editor.styleAt(pos + 1) + if folding: + line = self.editor.lineAt(pos + 1) + newLevel = self.editor.foldLevelAt(line) + + if newLevel < level: + while levelStack[-1] > newLevel: + f.write('''</span>''') + levelStack.pop() + f.write('\n') # here to get clean code + if newLevel > level: + f.write('''<span id="ln%d">''' % line) + levelStack.append(newLevel) + if self.editor.foldFlagsAt(line) & \ + QsciScintilla.SC_FOLDLEVELHEADERFLAG: + f.write('''<span id="hd%d" onclick="toggle('%d')">''' % \ + (line, line + 1)) + f.write('''<span id="bt%d">- </span>''' % line) + inFoldSpan = True + else: + f.write(''' ''') + level = newLevel + else: + f.write('\n') + + if styleIsUsed[styleCurrent] and \ + self.editor.rawCharAt(pos + 1) not in ['\r', '\n']: + # We know it's the correct next style, + # but no (empty) span for an empty line + f.write('''<span class="S%0d">''' % styleCurrent) + inStyleSpan = True + else: + if ch == '<': + f.write('''<''') + elif ch == '>': + f.write('''>''') + elif ch == '&': + f.write('''&''') + else: + if ord(ch) > 127 and utf8: + utf8Ch += ch + if utf8Len == 0: + if (ord(utf8Ch[0]) & 0xF0) == 0xF0: + utf8Len = 4 + elif (ord(utf8Ch[0]) & 0xE0) == 0xE0: + utf8Len = 3 + elif (ord(utf8Ch[0]) & 0xC0) == 0xC0: + utf8Len = 2 + column -= 1 # will be incremented again later + elif len(utf8Ch) == utf8Len: + ch = utf8Ch.decode('utf8') + f.write(Utilities.html_encode(ch)) + utf8Ch = "" + utf8Len = 0 + else: + column -= 1 # will be incremented again later + else: + f.write(ch) + column += 1 + + pos += 1 + + if inStyleSpan: + f.write('''</span>''') + + if folding: + while levelStack: + f.write('''</span>''') + levelStack.pop() + + if wysiwyg: + f.write('''</span>''') + else: + f.write('''</pre>''') + + f.write('''</body>\n</html>\n''') + f.close() + except IOError, err: + QApplication.restoreOverrideCursor() + QMessageBox.critical(self.editor, + self.trUtf8("Export source"), + self.trUtf8(\ + """<p>The source could not be exported to <b>{0}</b>.</p>""" + """<p>Reason: {1}</p>""")\ + .format(filename, unicode(err)), + QMessageBox.StandardButtons(\ + QMessageBox.Ok)) + finally: + QApplication.restoreOverrideCursor()