src/eric7/QScintilla/Exporters/ExporterTEX.py

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

eric ide

mercurial