src/eric7/QScintilla/Exporters/ExporterTEX.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
24 24
25 class ExporterTEX(ExporterBase): 25 class ExporterTEX(ExporterBase):
26 """ 26 """
27 Class implementing an exporter for TeX. 27 Class implementing an exporter for TeX.
28 """ 28 """
29
29 CHARZ = 26 30 CHARZ = 26
30 31
31 def __init__(self, editor, parent=None): 32 def __init__(self, editor, parent=None):
32 """ 33 """
33 Constructor 34 Constructor
34 35
35 @param editor reference to the editor object (QScintilla.Editor.Editor) 36 @param editor reference to the editor object (QScintilla.Editor.Editor)
36 @param parent parent object of the exporter (QObject) 37 @param parent parent object of the exporter (QObject)
37 """ 38 """
38 ExporterBase.__init__(self, editor, parent) 39 ExporterBase.__init__(self, editor, parent)
39 40
40 def __getTexRGB(self, color): 41 def __getTexRGB(self, color):
41 """ 42 """
42 Private method to convert a color object to a TeX color string. 43 Private method to convert a color object to a TeX color string.
43 44
44 @param color color object to convert (QColor) 45 @param color color object to convert (QColor)
45 @return TeX color string (string) 46 @return TeX color string (string)
46 """ 47 """
47 # texcolor[rgb]{0,0.5,0}{....} 48 # texcolor[rgb]{0,0.5,0}{....}
48 rf = color.red() / 256.0 49 rf = color.red() / 256.0
49 gf = color.green() / 256.0 50 gf = color.green() / 256.0
50 bf = color.blue() / 256.0 51 bf = color.blue() / 256.0
51 52
52 # avoid breakage due to locale setting 53 # avoid breakage due to locale setting
53 r = int(rf * 10 + 0.5) 54 r = int(rf * 10 + 0.5)
54 g = int(gf * 10 + 0.5) 55 g = int(gf * 10 + 0.5)
55 b = int(bf * 10 + 0.5) 56 b = int(bf * 10 + 0.5)
56 57
57 return "{0:d}.{1:d}, {2:d}.{3:d}, {4:d}.{5:d}".format( 58 return "{0:d}.{1:d}, {2:d}.{3:d}, {4:d}.{5:d}".format(
58 r // 10, r % 10, g // 10, g % 10, b // 10, b % 10) 59 r // 10, r % 10, g // 10, g % 10, b // 10, b % 10
59 60 )
61
60 def __texStyle(self, style): 62 def __texStyle(self, style):
61 """ 63 """
62 Private method to calculate a style name string for a given style 64 Private method to calculate a style name string for a given style
63 number. 65 number.
64 66
65 @param style style number (integer) 67 @param style style number (integer)
66 @return style name string (string) 68 @return style name string (string)
67 """ 69 """
68 buf = "" 70 buf = ""
69 if style >= 0: 71 if style >= 0:
70 start = ord('a') 72 start = ord("a")
71 else: 73 else:
72 start = ord('A') 74 start = ord("A")
73 style = abs(style) 75 style = abs(style)
74 76
75 if style == 0: 77 if style == 0:
76 buf = "a" 78 buf = "a"
77 else: 79 else:
78 while style > 0: 80 while style > 0:
79 buf += chr(start + (style % self.CHARZ)) 81 buf += chr(start + (style % self.CHARZ))
80 style //= self.CHARZ 82 style //= self.CHARZ
81 return buf 83 return buf
82 84
83 def __defineTexStyle(self, font, color, paper, file, istyle): 85 def __defineTexStyle(self, font, color, paper, file, istyle):
84 """ 86 """
85 Private method to define a new TeX style. 87 Private method to define a new TeX style.
86 88
87 @param font the font to be used (QFont) 89 @param font the font to be used (QFont)
88 @param color the foreground color to be used (QColor) 90 @param color the foreground color to be used (QColor)
89 @param paper the background color to be used (QColor) 91 @param paper the background color to be used (QColor)
90 @param file reference to the open file to write to (file object) 92 @param file reference to the open file to write to (file object)
91 @param istyle style number (integer) 93 @param istyle style number (integer)
92 """ 94 """
93 closing_brackets = 3 95 closing_brackets = 3
94 file.write( 96 file.write(
95 "\\newcommand{{\\eric{0}}}[1]{{\\noindent{{\\ttfamily{{".format( 97 "\\newcommand{{\\eric{0}}}[1]{{\\noindent{{\\ttfamily{{".format(
96 self.__texStyle(istyle))) 98 self.__texStyle(istyle)
99 )
100 )
97 if font.italic(): 101 if font.italic():
98 file.write("\\textit{") 102 file.write("\\textit{")
99 closing_brackets += 1 103 closing_brackets += 1
100 if font.bold(): 104 if font.bold():
101 file.write("\\textbf{") 105 file.write("\\textbf{")
102 closing_brackets += 1 106 closing_brackets += 1
103 if color != self.defaultColor: 107 if color != self.defaultColor:
104 file.write( 108 file.write("\\textcolor[rgb]{{{0}}}{{".format(self.__getTexRGB(color)))
105 "\\textcolor[rgb]{{{0}}}{{".format(self.__getTexRGB(color)))
106 closing_brackets += 1 109 closing_brackets += 1
107 if paper != self.defaultPaper: 110 if paper != self.defaultPaper:
108 file.write( 111 file.write("\\colorbox[rgb]{{{0}}}{{".format(self.__getTexRGB(paper)))
109 "\\colorbox[rgb]{{{0}}}{{".format(self.__getTexRGB(paper))) 112 closing_brackets += 1
110 closing_brackets += 1 113 file.write("#1{0}\n".format("}" * closing_brackets))
111 file.write("#1{0}\n".format('}' * closing_brackets)) 114
112
113 def exportSource(self): 115 def exportSource(self):
114 """ 116 """
115 Public method performing the export. 117 Public method performing the export.
116 """ 118 """
117 filename = self._getFileName(self.tr("TeX Files (*.tex)")) 119 filename = self._getFileName(self.tr("TeX Files (*.tex)"))
118 if not filename: 120 if not filename:
119 return 121 return
120 122
121 self.editor.recolor(0, -1) 123 self.editor.recolor(0, -1)
122 124
123 tabSize = self.editor.getEditorConfig("TabWidth") 125 tabSize = self.editor.getEditorConfig("TabWidth")
124 if tabSize == 0: 126 if tabSize == 0:
125 tabSize = 4 127 tabSize = 4
126 128
127 onlyStylesUsed = Preferences.getEditorExporter( 129 onlyStylesUsed = Preferences.getEditorExporter("TeX/OnlyStylesUsed")
128 "TeX/OnlyStylesUsed") 130 titleFullPath = Preferences.getEditorExporter("TeX/FullPathAsTitle")
129 titleFullPath = Preferences.getEditorExporter( 131
130 "TeX/FullPathAsTitle")
131
132 lex = self.editor.getLexer() 132 lex = self.editor.getLexer()
133 self.defaultPaper = ( 133 self.defaultPaper = (
134 lex and 134 lex and lex.paper(QsciScintilla.STYLE_DEFAULT) or self.editor.paper().name()
135 lex.paper(QsciScintilla.STYLE_DEFAULT) or
136 self.editor.paper().name()
137 ) 135 )
138 self.defaultColor = ( 136 self.defaultColor = (
139 lex and 137 lex and lex.color(QsciScintilla.STYLE_DEFAULT) or self.editor.color().name()
140 lex.color(QsciScintilla.STYLE_DEFAULT) or
141 self.editor.color().name()
142 ) 138 )
143 self.defaultFont = ( 139 self.defaultFont = (
144 lex and 140 lex
145 lex.color(QsciScintilla.STYLE_DEFAULT) or 141 and lex.color(QsciScintilla.STYLE_DEFAULT)
146 Preferences.getEditorOtherFonts("DefaultFont") 142 or Preferences.getEditorOtherFonts("DefaultFont")
147 ) 143 )
148 144
149 lengthDoc = self.editor.length() 145 lengthDoc = self.editor.length()
150 styleIsUsed = {} 146 styleIsUsed = {}
151 if onlyStylesUsed: 147 if onlyStylesUsed:
152 for index in range(QsciScintilla.STYLE_MAX + 1): 148 for index in range(QsciScintilla.STYLE_MAX + 1):
153 styleIsUsed[index] = False 149 styleIsUsed[index] = False
158 pos += 1 154 pos += 1
159 else: 155 else:
160 for index in range(QsciScintilla.STYLE_MAX + 1): 156 for index in range(QsciScintilla.STYLE_MAX + 1):
161 styleIsUsed[index] = True 157 styleIsUsed[index] = True
162 styleIsUsed[QsciScintilla.STYLE_DEFAULT] = True 158 styleIsUsed[QsciScintilla.STYLE_DEFAULT] = True
163 159
164 with EricOverrideCursor(), open(filename, "w", encoding="utf-8") as f: 160 with EricOverrideCursor(), open(filename, "w", encoding="utf-8") as f:
165 try: 161 try:
166 f.write("\\documentclass[a4paper]{article}\n") 162 f.write("\\documentclass[a4paper]{article}\n")
167 f.write("\\usepackage[a4paper,margin=1.5cm]{geometry}\n") 163 f.write("\\usepackage[a4paper,margin=1.5cm]{geometry}\n")
168 f.write("\\usepackage[T1]{fontenc}\n") 164 f.write("\\usepackage[T1]{fontenc}\n")
171 f.write("\\usepackage{times}\n") 167 f.write("\\usepackage{times}\n")
172 if self.editor.isUtf8(): 168 if self.editor.isUtf8():
173 f.write("\\usepackage[utf8]{inputenc}\n") 169 f.write("\\usepackage[utf8]{inputenc}\n")
174 else: 170 else:
175 f.write("\\usepackage[latin1]{inputenc}\n") 171 f.write("\\usepackage[latin1]{inputenc}\n")
176 172
177 if lex: 173 if lex:
178 istyle = 0 174 istyle = 0
179 while istyle <= QsciScintilla.STYLE_MAX: 175 while istyle <= QsciScintilla.STYLE_MAX:
180 if ( 176 if (
181 (istyle <= QsciScintilla.STYLE_DEFAULT or 177 (
182 istyle > QsciScintilla.STYLE_LASTPREDEFINED) and 178 istyle <= QsciScintilla.STYLE_DEFAULT
183 styleIsUsed[istyle] and 179 or istyle > QsciScintilla.STYLE_LASTPREDEFINED
184 (lex.description(istyle) or 180 )
185 istyle == QsciScintilla.STYLE_DEFAULT) 181 and styleIsUsed[istyle]
182 and (
183 lex.description(istyle)
184 or istyle == QsciScintilla.STYLE_DEFAULT
185 )
186 ): 186 ):
187 font = lex.font(istyle) 187 font = lex.font(istyle)
188 colour = lex.color(istyle) 188 colour = lex.color(istyle)
189 paper = lex.paper(istyle) 189 paper = lex.paper(istyle)
190 190
191 self.__defineTexStyle( 191 self.__defineTexStyle(font, colour, paper, f, istyle)
192 font, colour, paper, f, istyle)
193 # get substyles 192 # get substyles
194 subs_start, subs_count = ( 193 subs_start, subs_count = self.editor.getSubStyleRange(
195 self.editor.getSubStyleRange(istyle) 194 istyle
196 ) 195 )
197 for subs_idx in range(subs_count): 196 for subs_idx in range(subs_count):
198 font = lex.font(subs_start + subs_idx) 197 font = lex.font(subs_start + subs_idx)
199 colour = lex.color( 198 colour = lex.color(subs_start + subs_idx)
200 subs_start + subs_idx) 199 paper = lex.paper(subs_start + subs_idx)
201 paper = lex.paper( 200
202 subs_start + subs_idx)
203
204 self.__defineTexStyle( 201 self.__defineTexStyle(
205 font, colour, paper, f, 202 font, colour, paper, f, subs_idx - subs_start
206 subs_idx - subs_start) 203 )
207 204
208 istyle += 1 205 istyle += 1
209 else: 206 else:
210 colour = self.editor.color() 207 colour = self.editor.color()
211 paper = self.editor.paper() 208 paper = self.editor.paper()
212 font = Preferences.getEditorOtherFonts("DefaultFont") 209 font = Preferences.getEditorOtherFonts("DefaultFont")
213 210
214 self.__defineTexStyle(font, colour, paper, f, 0) 211 self.__defineTexStyle(font, colour, paper, f, 0)
215 self.__defineTexStyle(font, colour, paper, f, 212 self.__defineTexStyle(
216 QsciScintilla.STYLE_DEFAULT) 213 font, colour, paper, f, QsciScintilla.STYLE_DEFAULT
217 214 )
215
218 f.write("\\begin{document}\n\n") 216 f.write("\\begin{document}\n\n")
219 title = ( 217 title = (
220 self.editor.getFileName() 218 self.editor.getFileName()
221 if titleFullPath else 219 if titleFullPath
222 os.path.basename(self.editor.getFileName()) 220 else os.path.basename(self.editor.getFileName())
223 ) 221 )
224 f.write( 222 f.write(
225 "Source File: {0}\n\n\\noindent\n\\tiny{{\n" 223 "Source File: {0}\n\n\\noindent\n\\tiny{{\n".format(
226 .format(title.replace('_', '\\_'))) 224 title.replace("_", "\\_")
227 225 )
226 )
227
228 styleCurrent = self.editor.styleAt(0) 228 styleCurrent = self.editor.styleAt(0)
229 f.write("\\eric{0}{{" 229 f.write("\\eric{0}{{".format(self.__texStyle(styleCurrent)))
230 .format(self.__texStyle(styleCurrent))) 230
231
232 lineIdx = 0 231 lineIdx = 0
233 pos = 0 232 pos = 0
234 utf8 = self.editor.isUtf8() 233 utf8 = self.editor.isUtf8()
235 utf8Ch = b"" 234 utf8Ch = b""
236 utf8Len = 0 235 utf8Len = 0
237 236
238 while pos < lengthDoc: 237 while pos < lengthDoc:
239 ch = self.editor.byteAt(pos) 238 ch = self.editor.byteAt(pos)
240 style = self.editor.styleAt(pos) 239 style = self.editor.styleAt(pos)
241 if style != styleCurrent: 240 if style != styleCurrent:
242 # new style 241 # new style
243 f.write( 242 f.write("}}\n\\eric{0}{{".format(self.__texStyle(style)))
244 "}}\n\\eric{0}{{".format(
245 self.__texStyle(style)))
246 styleCurrent = style 243 styleCurrent = style
247 244
248 if ch == b'\t': 245 if ch == b"\t":
249 ts = tabSize - (lineIdx % tabSize) 246 ts = tabSize - (lineIdx % tabSize)
250 lineIdx += ts - 1 247 lineIdx += ts - 1
251 f.write("\\hspace*{{{0:d}em}}".format(ts)) 248 f.write("\\hspace*{{{0:d}em}}".format(ts))
252 elif ch == b'\\': 249 elif ch == b"\\":
253 f.write("{\\textbackslash}") 250 f.write("{\\textbackslash}")
254 elif ch in [b'>', b'<', b'@']: 251 elif ch in [b">", b"<", b"@"]:
255 f.write("${0}$".format(ch.decode())) 252 f.write("${0}$".format(ch.decode()))
256 elif ch in [b'{', b'}', b'^', b'_', b'&', b'$', b'#', 253 elif ch in [b"{", b"}", b"^", b"_", b"&", b"$", b"#", b"%", b"~"]:
257 b'%', b'~']:
258 f.write("\\{0}".format(ch.decode())) 254 f.write("\\{0}".format(ch.decode()))
259 elif ch in [b'\r', b'\n']: 255 elif ch in [b"\r", b"\n"]:
260 lineIdx = -1 # because incremented below 256 lineIdx = -1 # because incremented below
261 if ( 257 if ch == b"\r" and self.editor.byteAt(pos + 1) == b"\n":
262 ch == b'\r' and 258 pos += 1 # skip the LF
263 self.editor.byteAt(pos + 1) == b'\n'
264 ):
265 pos += 1 # skip the LF
266 styleCurrent = self.editor.styleAt(pos + 1) 259 styleCurrent = self.editor.styleAt(pos + 1)
267 f.write("}} \\\\\n\\eric{0}{{".format( 260 f.write(
268 self.__texStyle(styleCurrent))) 261 "}} \\\\\n\\eric{0}{{".format(self.__texStyle(styleCurrent))
269 elif ch == b' ': 262 )
270 if self.editor.byteAt(pos + 1) == b' ': 263 elif ch == b" ":
264 if self.editor.byteAt(pos + 1) == b" ":
271 f.write("{\\hspace*{1em}}") 265 f.write("{\\hspace*{1em}}")
272 else: 266 else:
273 f.write(' ') 267 f.write(" ")
274 else: 268 else:
275 if ord(ch) > 127 and utf8: 269 if ord(ch) > 127 and utf8:
276 utf8Ch += ch 270 utf8Ch += ch
277 if utf8Len == 0: 271 if utf8Len == 0:
278 if (utf8Ch[0] & 0xF0) == 0xF0: 272 if (utf8Ch[0] & 0xF0) == 0xF0:
280 elif (utf8Ch[0] & 0xE0) == 0xE0: 274 elif (utf8Ch[0] & 0xE0) == 0xE0:
281 utf8Len = 3 275 utf8Len = 3
282 elif (utf8Ch[0] & 0xC0) == 0xC0: 276 elif (utf8Ch[0] & 0xC0) == 0xC0:
283 utf8Len = 2 277 utf8Len = 2
284 elif len(utf8Ch) == utf8Len: 278 elif len(utf8Ch) == utf8Len:
285 ch = utf8Ch.decode('utf8') 279 ch = utf8Ch.decode("utf8")
286 f.write(ch) 280 f.write(ch)
287 utf8Ch = b"" 281 utf8Ch = b""
288 utf8Len = 0 282 utf8Len = 0
289 else: 283 else:
290 f.write(ch.decode()) 284 f.write(ch.decode())
291 lineIdx += 1 285 lineIdx += 1
292 pos += 1 286 pos += 1
293 287
294 # close last empty style macros and document too 288 # close last empty style macros and document too
295 f.write("}\n} %end tiny\n\n\\end{document}\n") 289 f.write("}\n} %end tiny\n\n\\end{document}\n")
296 except OSError as err: 290 except OSError as err:
297 EricMessageBox.critical( 291 EricMessageBox.critical(
298 self.editor, 292 self.editor,
299 self.tr("Export source"), 293 self.tr("Export source"),
300 self.tr( 294 self.tr(
301 """<p>The source could not be exported to""" 295 """<p>The source could not be exported to"""
302 """ <b>{0}</b>.</p><p>Reason: {1}</p>""") 296 """ <b>{0}</b>.</p><p>Reason: {1}</p>"""
303 .format(filename, str(err))) 297 ).format(filename, str(err)),
298 )

eric ide

mercurial