|
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 TeX. |
|
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 os |
|
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 ExporterTEX(ExporterBase): |
|
24 """ |
|
25 Class implementing an exporter for TeX. |
|
26 """ |
|
27 CHARZ = ord('z') - ord('a') + 1 |
|
28 |
|
29 def __init__(self, editor, parent = None): |
|
30 """ |
|
31 Constructor |
|
32 |
|
33 @param editor reference to the editor object (QScintilla.Editor.Editor) |
|
34 @param parent parent object of the exporter (QObject) |
|
35 """ |
|
36 ExporterBase.__init__(self, editor, parent) |
|
37 |
|
38 def __getTexRGB(self, color): |
|
39 """ |
|
40 Private method to convert a color object to a TeX color string |
|
41 |
|
42 @param color color object to convert (QColor) |
|
43 @return TeX color string (string) |
|
44 """ |
|
45 # texcolor[rgb]{0,0.5,0}{....} |
|
46 rf = color.red() / 256.0 |
|
47 gf = color.green() / 256.0 |
|
48 bf = color.blue() / 256.0 |
|
49 |
|
50 # avoid breakage due to locale setting |
|
51 r = int(rf * 10 + 0.5) |
|
52 g = int(gf * 10 + 0.5) |
|
53 b = int(bf * 10 + 0.5) |
|
54 |
|
55 return "%d.%d, %d.%d, %d.%d" % (r / 10, r % 10, g / 10, g % 10, b / 10, b % 10) |
|
56 |
|
57 def __texStyle(self, style): |
|
58 """ |
|
59 Private method to calculate a style name string for a given style number. |
|
60 |
|
61 @param style style number (integer) |
|
62 @return style name string (string) |
|
63 """ |
|
64 buf = "" |
|
65 if style == 0: |
|
66 buf = "a" |
|
67 else: |
|
68 while style > 0: |
|
69 buf += chr(ord('a') + (style % self.CHARZ)) |
|
70 style /= self.CHARZ |
|
71 return buf |
|
72 |
|
73 def __defineTexStyle(self, font, color, paper, file, istyle): |
|
74 """ |
|
75 Private method to define a new TeX style. |
|
76 |
|
77 @param font the font to be used (QFont) |
|
78 @param color the foreground color to be used (QColor) |
|
79 @param paper the background color to be used (QColor) |
|
80 @param file reference to the open file to write to (file object) |
|
81 @param istyle style number (integer) |
|
82 """ |
|
83 closing_brackets = 3 |
|
84 file.write("\\newcommand{\\eric%s}[1]{\\noindent{\\ttfamily{" % \ |
|
85 self.__texStyle(istyle)) |
|
86 if font.italic(): |
|
87 file.write("\\textit{") |
|
88 closing_brackets += 1 |
|
89 if font.bold(): |
|
90 file.write("\\textbf{") |
|
91 closing_brackets += 1 |
|
92 if color != self.defaultColor: |
|
93 file.write("\\textcolor[rgb]{%s}{" % self.__getTexRGB(color)) |
|
94 closing_brackets += 1 |
|
95 if paper != self.defaultPaper: |
|
96 file.write("\\colorbox[rgb]{%s}{" % self.__getTexRGB(paper)) |
|
97 closing_brackets += 1 |
|
98 file.write("#1%s\n" % ('}' * closing_brackets)) |
|
99 |
|
100 def exportSource(self): |
|
101 """ |
|
102 Public method performing the export. |
|
103 """ |
|
104 filename = self._getFileName(self.trUtf8("TeX Files (*.tex)")) |
|
105 if not filename: |
|
106 return |
|
107 |
|
108 try: |
|
109 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
110 QApplication.processEvents() |
|
111 |
|
112 self.editor.recolor(0, -1) |
|
113 |
|
114 tabSize = Preferences.getEditor("TabWidth") |
|
115 if tabSize == 0: |
|
116 tabSize = 4 |
|
117 |
|
118 onlyStylesUsed = Preferences.getEditorExporter("TeX/OnlyStylesUsed") |
|
119 titleFullPath = Preferences.getEditorExporter("TeX/FullPathAsTitle") |
|
120 |
|
121 lex = self.editor.getLexer() |
|
122 self.defaultPaper = lex and \ |
|
123 lex.paper(QsciScintilla.STYLE_DEFAULT) or \ |
|
124 self.editor.paper().name() |
|
125 self.defaultColor = lex and \ |
|
126 lex.color(QsciScintilla.STYLE_DEFAULT) or \ |
|
127 self.editor.color().name() |
|
128 self.defaultFont = lex and \ |
|
129 lex.color(QsciScintilla.STYLE_DEFAULT) or \ |
|
130 Preferences.getEditorOtherFonts("DefaultFont") |
|
131 |
|
132 lengthDoc = self.editor.length() |
|
133 styleIsUsed = {} |
|
134 if onlyStylesUsed: |
|
135 for index in range(QsciScintilla.STYLE_MAX + 1): |
|
136 styleIsUsed[index] = False |
|
137 # check the used styles |
|
138 pos = 0 |
|
139 while pos < lengthDoc: |
|
140 styleIsUsed[self.editor.styleAt(pos) & 0x7F] = True |
|
141 pos += 1 |
|
142 else: |
|
143 for index in range(QsciScintilla.STYLE_MAX + 1): |
|
144 styleIsUsed[index] = True |
|
145 styleIsUsed[QsciScintilla.STYLE_DEFAULT] = True |
|
146 |
|
147 try: |
|
148 f = open(filename, "wb") |
|
149 |
|
150 f.write("\\documentclass[a4paper]{article}\n") |
|
151 f.write("\\usepackage[a4paper,margin=1.5cm]{geometry}\n") |
|
152 f.write("\\usepackage[T1]{fontenc}\n") |
|
153 f.write("\\usepackage{color}\n") |
|
154 f.write("\\usepackage{alltt}\n") |
|
155 f.write("\\usepackage{times}\n") |
|
156 if self.editor.isUtf8(): |
|
157 f.write("\\usepackage[utf8]{inputenc}\n") |
|
158 else: |
|
159 f.write("\\usepackage[latin1]{inputenc}\n") |
|
160 |
|
161 if lex: |
|
162 istyle = 0 |
|
163 while istyle <= QsciScintilla.STYLE_MAX: |
|
164 if (istyle <= QsciScintilla.STYLE_DEFAULT or \ |
|
165 istyle > QsciScintilla.STYLE_LASTPREDEFINED) and \ |
|
166 styleIsUsed[istyle]: |
|
167 if lex.description(istyle) or \ |
|
168 istyle == QsciScintilla.STYLE_DEFAULT: |
|
169 font = lex.font(istyle) |
|
170 colour = lex.color(istyle) |
|
171 paper = lex.paper(istyle) |
|
172 |
|
173 self.__defineTexStyle(font, colour, paper, f, istyle) |
|
174 istyle += 1 |
|
175 else: |
|
176 colour = self.editor.color() |
|
177 paper = self.editor.paper() |
|
178 font = Preferences.getEditorOtherFonts("DefaultFont") |
|
179 |
|
180 self.__defineTexStyle(font, colour, paper, f, 0) |
|
181 self.__defineTexStyle(font, colour, paper, f, |
|
182 QsciScintilla.STYLE_DEFAULT) |
|
183 |
|
184 f.write("\\begin{document}\n\n") |
|
185 if titleFullPath: |
|
186 title = self.editor.getFileName() |
|
187 else: |
|
188 title = os.path.basename(self.editor.getFileName()) |
|
189 f.write("Source File: %s\n\n\\noindent\n\\tiny{\n" % title) |
|
190 |
|
191 styleCurrent = self.editor.styleAt(0) |
|
192 f.write("\\eric%s{" % self.__texStyle(styleCurrent)) |
|
193 |
|
194 lineIdx = 0 |
|
195 pos = 0 |
|
196 |
|
197 while pos < lengthDoc: |
|
198 ch = self.editor.rawCharAt(pos) |
|
199 style = self.editor.styleAt(pos) |
|
200 if style != styleCurrent: |
|
201 # new style |
|
202 f.write("}\n\\eric%s{" % self.__texStyle(style)) |
|
203 styleCurrent = style |
|
204 |
|
205 if ch == '\t': |
|
206 ts = tabSize - (lineIdx % tabSize) |
|
207 lineIdx += ts - 1 |
|
208 f.write("\\hspace*{%dem}" % ts) |
|
209 elif ch == '\\': |
|
210 f.write("{\\textbackslash}") |
|
211 elif ch in ['>', '<', '@']: |
|
212 f.write("$%c$" % ch) |
|
213 elif ch in ['{', '}', '^', '_', '&', '$', '#', '%', '~']: |
|
214 f.write("\\%c" % ch) |
|
215 elif ch in ['\r', '\n']: |
|
216 lineIdx = -1 # because incremented below |
|
217 if ch == '\r' and self.editor.rawCharAt(pos + 1) == '\n': |
|
218 pos += 1 # skip the LF |
|
219 styleCurrent = self.editor.styleAt(pos + 1) |
|
220 f.write("} \\\\\n\\eric%s{" % self.__texStyle(styleCurrent)) |
|
221 elif ch == ' ': |
|
222 if self.editor.rawCharAt(pos + 1) == ' ': |
|
223 f.write("{\\hspace*{1em}}") |
|
224 else: |
|
225 f.write(' ') |
|
226 else: |
|
227 f.write(ch) |
|
228 lineIdx += 1 |
|
229 pos += 1 |
|
230 |
|
231 # close last empty style macros and document too |
|
232 f.write("}\n} %end tiny\n\n\\end{document}\n") |
|
233 f.close() |
|
234 except IOError, err: |
|
235 QApplication.restoreOverrideCursor() |
|
236 QMessageBox.critical(self.editor, |
|
237 self.trUtf8("Export source"), |
|
238 self.trUtf8(\ |
|
239 """<p>The source could not be exported to <b>{0}</b>.</p>""" |
|
240 """<p>Reason: {1}</p>""")\ |
|
241 .format(filename, unicode(err)), |
|
242 QMessageBox.StandardButtons(\ |
|
243 QMessageBox.Ok)) |
|
244 finally: |
|
245 QApplication.restoreOverrideCursor() |