QScintilla/Exporters/ExporterRTF.py

changeset 0
de9c2efb9d02
child 12
1d8dd9706f46
equal deleted inserted replaced
-1:000000000000 0:de9c2efb9d02
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2007 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing an exporter for RTF.
8 """
9
10 # This code is a port of the C++ code found in SciTE 1.74
11 # Original code: Copyright 1998-2006 by Neil Hodgson <neilh@scintilla.org>
12
13 import time
14
15 from PyQt4.QtCore import *
16 from PyQt4.QtGui import *
17 from PyQt4.Qsci import QsciScintilla
18
19 from ExporterBase import ExporterBase
20
21 import Preferences
22
23 class ExporterRTF(ExporterBase):
24 """
25 Class implementing an exporter for RTF.
26 """
27 RTF_HEADEROPEN = "{\\rtf1\\ansi\\deff0\\deftab720"
28 RTF_HEADERCLOSE = "\n"
29 RTF_FONTDEFOPEN = "{\\fonttbl"
30 RTF_FONTDEF = "{\\f%d\\fnil\\fcharset%u %s;}"
31 RTF_FONTDEFCLOSE = "}"
32 RTF_COLORDEFOPEN = "{\\colortbl"
33 RTF_COLORDEF = "\\red%d\\green%d\\blue%d;"
34 RTF_COLORDEFCLOSE = "}"
35 RTF_INFOOPEN = "{\\info "
36 RTF_INFOCLOSE = "}"
37 RTF_COMMENT = "{\\comment Generated by eric4's RTF export filter.}"
38 RTF_CREATED = "{\creatim\yr%Y\mo%m\dy%d\hr%H\min%M\sec%S}" # to be used by strftime
39 RTF_BODYOPEN = ""
40 RTF_BODYCLOSE = "}"
41
42 RTF_SETFONTFACE = "\\f"
43 RTF_SETFONTSIZE = "\\fs"
44 RTF_SETCOLOR = "\\cf"
45 RTF_SETBACKGROUND = "\\highlight"
46 RTF_BOLD_ON = "\\b"
47 RTF_BOLD_OFF = "\\b0"
48 RTF_ITALIC_ON = "\\i"
49 RTF_ITALIC_OFF = "\\i0"
50
51 RTF_EOLN = "\\par\n"
52 RTF_TAB = "\\tab "
53
54 RTF_COLOR = "#000000"
55
56 def __init__(self, editor, parent = None):
57 """
58 Constructor
59
60 @param editor reference to the editor object (QScintilla.Editor.Editor)
61 @param parent parent object of the exporter (QObject)
62 """
63 ExporterBase.__init__(self, editor, parent)
64
65 def __GetRTFNextControl(self, pos, style):
66 """
67 Private method to extract the next RTF control word from style.
68
69 @param pos position to start search (integer)
70 @param style style definition to search in (string)
71 @return tuple of new start position and control word found
72 (integer, string)
73 """
74 # \f0\fs20\cf0\highlight0\b0\i0
75 if pos >= len(style):
76 return pos, ""
77
78 oldpos = pos
79 pos += 1 # implicit skip over leading '\'
80 while pos < len(style) and style[pos] != '\\':
81 pos += 1
82 return pos, style[oldpos:pos]
83
84 def __GetRTFStyleChange(self, last, current):
85 """
86 Private method to extract control words that are different between two styles.
87
88 @param last least recently used style (string)
89 @param current current style (string)
90 @return string containing the delta between these styles (string)
91 """
92 # \f0\fs20\cf0\highlight0\b0\i0
93 lastPos = 0
94 currentPos = 0
95 delta = ''
96 i = 0
97 while i < 6:
98 lastPos, lastControl = self.__GetRTFNextControl(lastPos, last)
99 currentPos, currentControl = self.__GetRTFNextControl(currentPos, current)
100 if lastControl != currentControl:
101 delta += currentControl
102 i += 1
103 if delta != '':
104 delta += ' '
105 return delta
106
107 def exportSource(self):
108 """
109 Public method performing the export.
110 """
111 filename = self._getFileName(self.trUtf8("RTF Files (*.rtf)"))
112 if not filename:
113 return
114
115 try:
116 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
117 QApplication.processEvents()
118
119 self.editor.recolor(0, -1)
120 lex = self.editor.getLexer()
121
122 tabSize = Preferences.getEditor("TabWidth")
123 if tabSize == 0:
124 tabSize = 4
125 wysiwyg = Preferences.getEditorExporter("RTF/WYSIWYG")
126 if wysiwyg:
127 if lex:
128 defaultFont = lex.font(QsciScintilla.STYLE_DEFAULT)
129 else:
130 defaultFont = Preferences.getEditorOtherFonts("DefaultFont")
131 else:
132 defaultFont = Preferences.getEditorExporter("RTF/Font")
133 fontface = defaultFont.family()
134 fontsize = QFontInfo(defaultFont).pointSize() << 1
135 if fontsize == 0:
136 fontsize = 10 << 1
137 characterset = QsciScintilla.SC_CHARSET_DEFAULT
138 tabs = Preferences.getEditorExporter("RTF/UseTabs")
139
140 if lex:
141 fgColour = lex.color(QsciScintilla.STYLE_DEFAULT)
142 bgColour = lex.paper(QsciScintilla.STYLE_DEFAULT)
143 else:
144 fgColour = self.editor.color()
145 bgColour = self.editor.paper()
146
147 try:
148 f = open(filename, "wb")
149
150 styles = {}
151 fonts = {}
152 colors = {}
153 lastStyle = ""
154
155 f.write(self.RTF_HEADEROPEN + self.RTF_FONTDEFOPEN)
156 fonts[0] = fontface
157 fontCount = 1
158 f.write(self.RTF_FONTDEF % (0, characterset, fontface))
159 colors[0] = fgColour
160 colors[1] = bgColour
161 colorCount = 2
162
163 if lex:
164 istyle = 0
165 while istyle <= QsciScintilla.STYLE_MAX:
166 if (istyle < QsciScintilla.STYLE_DEFAULT or \
167 istyle > QsciScintilla.STYLE_LASTPREDEFINED):
168 if lex.description(istyle):
169 font = lex.font(istyle)
170 if wysiwyg:
171 fontKey = None
172 for key, value in fonts.items():
173 if value.lower() == font.family().lower():
174 fontKey = key
175 break
176 if fontKey is None:
177 fonts[fontCount] = font.family()
178 f.write(self.RTF_FONTDEF % \
179 (fontCount, characterset, font.family()))
180 fontKey = fontCount
181 fontCount += 1
182 lastStyle = self.RTF_SETFONTFACE + "%d" % fontKey
183 else:
184 lastStyle = self.RTF_SETFONTFACE + "0"
185
186 if wysiwyg and QFontInfo(font).pointSize():
187 lastStyle += self.RTF_SETFONTSIZE + \
188 "%d" % (QFontInfo(font).pointSize() << 1)
189 else:
190 lastStyle += self.RTF_SETFONTSIZE + \
191 "%d" % fontsize
192
193 sColour = lex.color(istyle)
194 sColourKey = None
195 for key, value in colors.items():
196 if value == sColour:
197 sColourKey = key
198 break
199 if sColourKey is None:
200 colors[colorCount] = sColour
201 sColourKey = colorCount
202 colorCount += 1
203 lastStyle += self.RTF_SETCOLOR + "%d" % sColourKey
204
205 sColour = lex.paper(istyle)
206 sColourKey = None
207 for key, value in colors.items():
208 if value == sColour:
209 sColourKey = key
210 break
211 if sColourKey is None:
212 colors[colorCount] = sColour
213 sColourKey = colorCount
214 colorCount += 1
215 lastStyle += self.RTF_SETBACKGROUND + "%d" % sColourKey
216
217 if font.bold():
218 lastStyle += self.RTF_BOLD_ON
219 else:
220 lastStyle += self.RTF_BOLD_OFF
221 if font.italic():
222 lastStyle += self.RTF_ITALIC_ON
223 else:
224 lastStyle += self.RTF_ITALIC_OFF
225 styles[istyle] = lastStyle
226 else:
227 styles[istyle] = self.RTF_SETFONTFACE + "0" + \
228 self.RTF_SETFONTSIZE + \
229 "%d" % fontsize + \
230 self.RTF_SETCOLOR + "0" + \
231 self.RTF_SETBACKGROUND + "1" + \
232 self.RTF_BOLD_OFF + self.RTF_ITALIC_OFF
233
234 istyle += 1
235 else:
236 styles[0] = self.RTF_SETFONTFACE + "0" + \
237 self.RTF_SETFONTSIZE + "%d" % fontsize + \
238 self.RTF_SETCOLOR + "0" + \
239 self.RTF_SETBACKGROUND + "1" + \
240 self.RTF_BOLD_OFF + self.RTF_ITALIC_OFF
241
242 f.write(self.RTF_FONTDEFCLOSE + self.RTF_COLORDEFOPEN)
243 for value in colors.values():
244 f.write(self.RTF_COLORDEF % \
245 (value.red(), value.green(), value.blue()))
246 f.write(self.RTF_COLORDEFCLOSE)
247 f.write(self.RTF_INFOOPEN + self.RTF_COMMENT)
248 f.write(time.strftime(self.RTF_CREATED))
249 f.write(self.RTF_INFOCLOSE)
250 f.write(self.RTF_HEADERCLOSE + \
251 self.RTF_BODYOPEN + self.RTF_SETFONTFACE + "0" + \
252 self.RTF_SETFONTSIZE + "%d" % fontsize + \
253 self.RTF_SETCOLOR + "0 ")
254 lastStyle = self.RTF_SETFONTFACE + "0" + \
255 self.RTF_SETFONTSIZE + "%d" % fontsize + \
256 self.RTF_SETCOLOR + "0" + \
257 self.RTF_SETBACKGROUND + "1" + \
258 self.RTF_BOLD_OFF + self.RTF_ITALIC_OFF
259
260 lengthDoc = self.editor.length()
261 prevCR = False
262 column = 0
263 pos = 0
264 deltaStyle = ""
265 styleCurrent = -1
266 utf8 = self.editor.isUtf8()
267 utf8Ch = ""
268 utf8Len = 0
269
270 while pos < lengthDoc:
271 ch = self.editor.rawCharAt(pos)
272 style = self.editor.styleAt(pos)
273 if style != styleCurrent:
274 deltaStyle = self.__GetRTFStyleChange(lastStyle, styles[style])
275 if deltaStyle:
276 f.write(deltaStyle)
277 styleCurrent = style
278 lastStyle = styles[style]
279
280 if ch == '{':
281 f.write('\\{')
282 elif ch == '}':
283 f.write('\\}')
284 elif ch == '\\':
285 f.write('\\\\')
286 elif ch == '\t':
287 if tabs:
288 f.write(self.RTF_TAB)
289 else:
290 ts = tabSize - (column % tabSize)
291 f.write(' ' * ts)
292 column += ts - 1
293 elif ch == '\n':
294 if not prevCR:
295 f.write(self.RTF_EOLN)
296 column -= 1
297 elif ch == '\r':
298 f.write(self.RTF_EOLN)
299 column -= 1
300 else:
301 if ord(ch) > 0x7F and utf8:
302 utf8Ch += ch
303 if utf8Len == 0:
304 if (ord(utf8Ch[0]) & 0xF0) == 0xF0:
305 utf8Len = 4
306 elif (ord(utf8Ch[0]) & 0xE0) == 0xE0:
307 utf8Len = 3
308 elif (ord(utf8Ch[0]) & 0xC0) == 0xC0:
309 utf8Len = 2
310 column -= 1 # will be incremented again later
311 elif len(utf8Ch) == utf8Len:
312 ch = utf8Ch.decode('utf8')
313 if ord(ch) <= 0xff:
314 f.write("\\'%x" % ord(ch))
315 else:
316 f.write("\\u%d\\'%x" % (ord(ch), ord(ch) & 0xFF))
317 utf8Ch = ""
318 utf8Len = 0
319 else:
320 column -= 1 # will be incremented again later
321 else:
322 f.write(ch)
323
324 column += 1
325 prevCR = ch == '\r'
326 pos += 1
327
328 f.write(self.RTF_BODYCLOSE)
329 f.close()
330 except IOError, err:
331 QApplication.restoreOverrideCursor()
332 QMessageBox.critical(self.editor,
333 self.trUtf8("Export source"),
334 self.trUtf8(
335 """<p>The source could not be exported to <b>{0}</b>.</p>"""
336 """<p>Reason: {1}</p>""")\
337 .format(filename, unicode(err)),
338 QMessageBox.StandardButtons(\
339 QMessageBox.Ok))
340 finally:
341 QApplication.restoreOverrideCursor()

eric ide

mercurial