|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2007 - 2019 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing an exporter for HTML. |
|
8 """ |
|
9 |
|
10 from __future__ import unicode_literals |
|
11 |
|
12 try: # Only for Py2 |
|
13 import StringIO as io # __IGNORE_EXCEPTION__ |
|
14 except (ImportError, NameError): |
|
15 import io # __IGNORE_WARNING__ |
|
16 |
|
17 # This code is a port of the C++ code found in SciTE 1.74 |
|
18 # Original code: Copyright 1998-2006 by Neil Hodgson <neilh@scintilla.org> |
|
19 |
|
20 import os |
|
21 import sys |
|
22 |
|
23 from PyQt5.QtCore import Qt |
|
24 from PyQt5.QtGui import QCursor, QFontInfo |
|
25 from PyQt5.QtWidgets import QApplication |
|
26 from PyQt5.Qsci import QsciScintilla |
|
27 |
|
28 from E5Gui import E5MessageBox |
|
29 |
|
30 from .ExporterBase import ExporterBase |
|
31 |
|
32 import Preferences |
|
33 import Utilities |
|
34 |
|
35 |
|
36 class HTMLGenerator(object): |
|
37 """ |
|
38 Class implementing an HTML generator for exporting source code. |
|
39 """ |
|
40 def __init__(self, editor): |
|
41 """ |
|
42 Constructor |
|
43 |
|
44 @param editor reference to the editor object (QScintilla.Editor.Editor) |
|
45 """ |
|
46 self.editor = editor |
|
47 |
|
48 def generate(self, tabSize=4, useTabs=False, wysiwyg=True, folding=False, |
|
49 onlyStylesUsed=False, titleFullPath=False): |
|
50 """ |
|
51 Public method to generate HTML for the source editor. |
|
52 |
|
53 @keyparam tabSize size of tabs (integer) |
|
54 @keyparam useTabs flag indicating the use of tab characters (boolean) |
|
55 @keyparam wysiwyg flag indicating colorization (boolean) |
|
56 @keyparam folding flag indicating usage of fold markers |
|
57 @keyparam onlyStylesUsed flag indicating to include only style |
|
58 definitions for styles used in the source (boolean) |
|
59 @keyparam titleFullPath flag indicating to include the full file path |
|
60 in the title tag (boolean) |
|
61 @return generated HTML text (string) |
|
62 """ |
|
63 self.editor.recolor(0, -1) |
|
64 |
|
65 lengthDoc = self.editor.length() |
|
66 styleIsUsed = {} |
|
67 if onlyStylesUsed: |
|
68 for index in range(QsciScintilla.STYLE_MAX + 1): |
|
69 styleIsUsed[index] = False |
|
70 # check the used styles |
|
71 pos = 0 |
|
72 while pos < lengthDoc: |
|
73 styleIsUsed[self.editor.styleAt(pos) & 0x7F] = True |
|
74 pos += 1 |
|
75 else: |
|
76 for index in range(QsciScintilla.STYLE_MAX + 1): |
|
77 styleIsUsed[index] = True |
|
78 styleIsUsed[QsciScintilla.STYLE_DEFAULT] = True |
|
79 |
|
80 html = \ |
|
81 '''<!DOCTYPE html PUBLIC "-//W3C//DTD''' \ |
|
82 ''' XHTML 1.0 Transitional//EN"\n''' \ |
|
83 ''' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">''' \ |
|
84 '''\n''' \ |
|
85 '''<html xmlns="http://www.w3.org/1999/xhtml">\n''' \ |
|
86 '''<head>\n''' |
|
87 if titleFullPath: |
|
88 html += '''<title>{0}</title>\n'''.format( |
|
89 self.editor.getFileName()) |
|
90 else: |
|
91 html += '''<title>{0}</title>\n'''.format( |
|
92 os.path.basename(self.editor.getFileName())) |
|
93 html += '''<meta name="Generator" content="eric6" />\n''' \ |
|
94 '''<meta http-equiv="Content-Type" ''' \ |
|
95 '''content="text/html; charset=utf-8" />\n''' |
|
96 if folding: |
|
97 html += \ |
|
98 '''<script language="JavaScript" type="text/javascript">\n''' \ |
|
99 '''<!--\n''' \ |
|
100 '''function symbol(id, sym) {\n''' \ |
|
101 ''' if (id.textContent == undefined) {\n''' \ |
|
102 ''' id.innerText = sym;\n''' \ |
|
103 ''' } else {\n''' \ |
|
104 ''' id.textContent = sym;\n''' \ |
|
105 ''' }\n''' \ |
|
106 '''}\n''' \ |
|
107 '''function toggle(id) {\n''' \ |
|
108 ''' var thislayer = document.getElementById('ln' + id);\n''' \ |
|
109 ''' id -= 1;\n''' \ |
|
110 ''' var togline = document.getElementById('hd' + id);\n''' \ |
|
111 ''' var togsym = document.getElementById('bt' + id);\n''' \ |
|
112 ''' if (thislayer.style.display == 'none') {\n''' \ |
|
113 ''' thislayer.style.display = 'block';\n''' \ |
|
114 ''' togline.style.textDecoration = 'none';\n''' \ |
|
115 ''' symbol(togsym, '- ');\n''' \ |
|
116 ''' } else {\n''' \ |
|
117 ''' thislayer.style.display = 'none';\n''' \ |
|
118 ''' togline.style.textDecoration = 'underline';\n''' \ |
|
119 ''' symbol(togsym, '+ ');\n''' \ |
|
120 ''' }\n''' \ |
|
121 '''}\n''' \ |
|
122 '''//-->\n''' \ |
|
123 '''</script>\n''' |
|
124 |
|
125 lex = self.editor.getLexer() |
|
126 if lex: |
|
127 bgColour = lex.paper(QsciScintilla.STYLE_DEFAULT).name() |
|
128 else: |
|
129 bgColour = self.editor.paper().name() |
|
130 |
|
131 html += '''<style type="text/css">\n''' |
|
132 if lex: |
|
133 istyle = 0 |
|
134 while istyle <= QsciScintilla.STYLE_MAX: |
|
135 if (istyle <= QsciScintilla.STYLE_DEFAULT or |
|
136 istyle > QsciScintilla.STYLE_LASTPREDEFINED) and \ |
|
137 styleIsUsed[istyle]: |
|
138 if lex.description(istyle) or \ |
|
139 istyle == QsciScintilla.STYLE_DEFAULT: |
|
140 font = lex.font(istyle) |
|
141 colour = lex.color(istyle) |
|
142 paper = lex.paper(istyle) |
|
143 if istyle == QsciScintilla.STYLE_DEFAULT: |
|
144 html += '''span {\n''' |
|
145 else: |
|
146 html += '''.S{0:d} {{\n'''.format(istyle) |
|
147 if font.italic(): |
|
148 html += ''' font-style: italic;\n''' |
|
149 if font.bold(): |
|
150 html += ''' font-weight: bold;\n''' |
|
151 if wysiwyg: |
|
152 html += ''' font-family: '{0}';\n'''.format( |
|
153 font.family()) |
|
154 html += ''' color: {0};\n'''.format(colour.name()) |
|
155 if istyle != QsciScintilla.STYLE_DEFAULT and \ |
|
156 bgColour != paper.name(): |
|
157 html += ''' background: {0};\n'''.format( |
|
158 paper.name()) |
|
159 html += ''' text-decoration: inherit;\n''' |
|
160 if wysiwyg: |
|
161 html += ''' font-size: {0:d}pt;\n'''.format( |
|
162 QFontInfo(font).pointSize()) |
|
163 html += '''}\n''' |
|
164 else: |
|
165 styleIsUsed[istyle] = False |
|
166 istyle += 1 |
|
167 else: |
|
168 colour = self.editor.color() |
|
169 paper = self.editor.paper() |
|
170 font = Preferences.getEditorOtherFonts("DefaultFont") |
|
171 html += '''.S0 {\n''' |
|
172 if font.italic(): |
|
173 html += ''' font-style: italic;\n''' |
|
174 if font.bold(): |
|
175 html += ''' font-weight: bold;\n''' |
|
176 if wysiwyg: |
|
177 html += ''' font-family: '{0}';\n'''.format(font.family()) |
|
178 html += ''' color: {0};\n'''.format(colour.name()) |
|
179 if bgColour != paper.name(): |
|
180 html += ''' background: {0};\n'''.format(paper.name()) |
|
181 html += ''' text-decoration: inherit;\n''' |
|
182 if wysiwyg: |
|
183 html += ''' font-size: {0:d}pt;\n'''.format( |
|
184 QFontInfo(font).pointSize()) |
|
185 html += '''}\n''' |
|
186 html += '''</style>\n''' |
|
187 html += '''</head>\n''' |
|
188 |
|
189 html += '''<body bgcolor="{0}">\n'''.format(bgColour) |
|
190 line = self.editor.lineAt(0) |
|
191 level = self.editor.foldLevelAt(line) - QsciScintilla.SC_FOLDLEVELBASE |
|
192 levelStack = [level] |
|
193 styleCurrent = self.editor.styleAt(0) |
|
194 inStyleSpan = False |
|
195 inFoldSpan = False |
|
196 # Global span for default attributes |
|
197 if wysiwyg: |
|
198 html += '''<span>''' |
|
199 else: |
|
200 html += '''<pre>''' |
|
201 |
|
202 if folding: |
|
203 if self.editor.foldFlagsAt(line) & \ |
|
204 QsciScintilla.SC_FOLDLEVELHEADERFLAG: |
|
205 html += '''<span id="hd{0:d}" onclick="toggle('{1:d}')">'''\ |
|
206 .format(line, line + 1) |
|
207 html += '''<span id="bt{0:d}">- </span>'''.format(line) |
|
208 inFoldSpan = True |
|
209 else: |
|
210 html += ''' ''' |
|
211 |
|
212 if styleIsUsed[styleCurrent]: |
|
213 html += '''<span class="S{0:0d}">'''.format(styleCurrent) |
|
214 inStyleSpan = True |
|
215 |
|
216 column = 0 |
|
217 pos = 0 |
|
218 utf8 = self.editor.isUtf8() |
|
219 utf8Ch = b"" |
|
220 utf8Len = 0 |
|
221 |
|
222 while pos < lengthDoc: |
|
223 ch = self.editor.byteAt(pos) |
|
224 style = self.editor.styleAt(pos) |
|
225 if style != styleCurrent: |
|
226 if inStyleSpan: |
|
227 html += '''</span>''' |
|
228 inStyleSpan = False |
|
229 if ch not in [b'\r', b'\n']: # no need of a span for the EOL |
|
230 if styleIsUsed[style]: |
|
231 html += '''<span class="S{0:d}">'''.format(style) |
|
232 inStyleSpan = True |
|
233 styleCurrent = style |
|
234 |
|
235 if ch == b' ': |
|
236 if wysiwyg: |
|
237 prevCh = b'' |
|
238 if column == 0: |
|
239 # at start of line, must put a |
|
240 # because regular space will be collapsed |
|
241 prevCh = b' ' |
|
242 while pos < lengthDoc and self.editor.byteAt(pos) == b' ': |
|
243 if prevCh != b' ': |
|
244 html += ' ' |
|
245 else: |
|
246 html += ''' ''' |
|
247 prevCh = self.editor.byteAt(pos) |
|
248 pos += 1 |
|
249 column += 1 |
|
250 pos -= 1 |
|
251 # the last incrementation will be done by the outer loop |
|
252 else: |
|
253 html += ' ' |
|
254 column += 1 |
|
255 elif ch == b'\t': |
|
256 ts = tabSize - (column % tabSize) |
|
257 if wysiwyg: |
|
258 html += ''' ''' * ts |
|
259 column += ts |
|
260 else: |
|
261 if useTabs: |
|
262 html += '\t' |
|
263 column += 1 |
|
264 else: |
|
265 html += ' ' * ts |
|
266 column += ts |
|
267 elif ch in [b'\r', b'\n']: |
|
268 if inStyleSpan: |
|
269 html += '''</span>''' |
|
270 inStyleSpan = False |
|
271 if inFoldSpan: |
|
272 html += '''</span>''' |
|
273 inFoldSpan = False |
|
274 if ch == b'\r' and self.editor.byteAt(pos + 1) == b'\n': |
|
275 pos += 1 # CR+LF line ending, skip the "extra" EOL char |
|
276 column = 0 |
|
277 if wysiwyg: |
|
278 html += '''<br />''' |
|
279 |
|
280 styleCurrent = self.editor.styleAt(pos + 1) |
|
281 if folding: |
|
282 line = self.editor.lineAt(pos + 1) |
|
283 newLevel = self.editor.foldLevelAt(line) |
|
284 |
|
285 if newLevel < level: |
|
286 while levelStack[-1] > newLevel: |
|
287 html += '''</span>''' |
|
288 levelStack.pop() |
|
289 html += '\n' # here to get clean code |
|
290 if newLevel > level: |
|
291 html += '''<span id="ln{0:d}">'''.format(line) |
|
292 levelStack.append(newLevel) |
|
293 if self.editor.foldFlagsAt(line) & \ |
|
294 QsciScintilla.SC_FOLDLEVELHEADERFLAG: |
|
295 html += \ |
|
296 '''<span id="hd{0:d}"''' \ |
|
297 ''' onclick="toggle('{1:d}')">''' \ |
|
298 .format(line, line + 1) |
|
299 html += '''<span id="bt{0:d}">- </span>'''.format(line) |
|
300 inFoldSpan = True |
|
301 else: |
|
302 html += ''' ''' |
|
303 level = newLevel |
|
304 else: |
|
305 html += '\n' |
|
306 |
|
307 if styleIsUsed[styleCurrent] and \ |
|
308 self.editor.byteAt(pos + 1) not in [b'\r', b'\n']: |
|
309 # We know it's the correct next style, |
|
310 # but no (empty) span for an empty line |
|
311 html += '''<span class="S{0:0d}">'''.format(styleCurrent) |
|
312 inStyleSpan = True |
|
313 else: |
|
314 if ch == b'<': |
|
315 html += '''<''' |
|
316 elif ch == b'>': |
|
317 html += '''>''' |
|
318 elif ch == b'&': |
|
319 html += '''&''' |
|
320 else: |
|
321 if ord(ch) > 127 and utf8: |
|
322 utf8Ch += ch |
|
323 if utf8Len == 0: |
|
324 if (utf8Ch[0] & 0xF0) == 0xF0: |
|
325 utf8Len = 4 |
|
326 elif (utf8Ch[0] & 0xE0) == 0xE0: |
|
327 utf8Len = 3 |
|
328 elif (utf8Ch[0] & 0xC0) == 0xC0: |
|
329 utf8Len = 2 |
|
330 column -= 1 # will be incremented again later |
|
331 elif len(utf8Ch) == utf8Len: |
|
332 ch = utf8Ch.decode('utf8') |
|
333 html += Utilities.html_encode(ch) |
|
334 utf8Ch = b"" |
|
335 utf8Len = 0 |
|
336 else: |
|
337 column -= 1 # will be incremented again later |
|
338 else: |
|
339 html += ch.decode() |
|
340 column += 1 |
|
341 |
|
342 pos += 1 |
|
343 |
|
344 if inStyleSpan: |
|
345 html += '''</span>''' |
|
346 |
|
347 if folding: |
|
348 while levelStack: |
|
349 html += '''</span>''' |
|
350 levelStack.pop() |
|
351 |
|
352 if wysiwyg: |
|
353 html += '''</span>''' |
|
354 else: |
|
355 html += '''</pre>''' |
|
356 |
|
357 html += '''</body>\n</html>\n''' |
|
358 |
|
359 return html |
|
360 |
|
361 |
|
362 class ExporterHTML(ExporterBase): |
|
363 """ |
|
364 Class implementing an exporter for HTML. |
|
365 """ |
|
366 def __init__(self, editor, parent=None): |
|
367 """ |
|
368 Constructor |
|
369 |
|
370 @param editor reference to the editor object (QScintilla.Editor.Editor) |
|
371 @param parent parent object of the exporter (QObject) |
|
372 """ |
|
373 ExporterBase.__init__(self, editor, parent) |
|
374 |
|
375 def exportSource(self): |
|
376 """ |
|
377 Public method performing the export. |
|
378 """ |
|
379 filename = self._getFileName(self.tr("HTML Files (*.html)")) |
|
380 if not filename: |
|
381 return |
|
382 |
|
383 try: |
|
384 QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) |
|
385 QApplication.processEvents() |
|
386 |
|
387 fn = self.editor.getFileName() |
|
388 if fn: |
|
389 extension = os.path.normcase(os.path.splitext(fn)[1][1:]) |
|
390 else: |
|
391 extension = "" |
|
392 |
|
393 if extension in \ |
|
394 Preferences.getEditor("PreviewMarkdownFileNameExtensions") or \ |
|
395 self.editor.getLanguage().lower() == "markdown": |
|
396 # export markdown to HTML |
|
397 html = self.__generateFromMarkdown() |
|
398 elif extension in \ |
|
399 Preferences.getEditor("PreviewRestFileNameExtensions") or \ |
|
400 self.editor.getLanguage().lower() == "restructuredtext": |
|
401 # export ReST to HTML |
|
402 html = self.__generateFromReSTDocutils() |
|
403 else: |
|
404 tabSize = self.editor.getEditorConfig("TabWidth") |
|
405 if tabSize == 0: |
|
406 tabSize = 4 |
|
407 wysiwyg = Preferences.getEditorExporter("HTML/WYSIWYG") |
|
408 folding = Preferences.getEditorExporter("HTML/Folding") |
|
409 onlyStylesUsed = Preferences.getEditorExporter( |
|
410 "HTML/OnlyStylesUsed") |
|
411 titleFullPath = Preferences.getEditorExporter( |
|
412 "HTML/FullPathAsTitle") |
|
413 tabs = Preferences.getEditorExporter("HTML/UseTabs") |
|
414 |
|
415 generator = HTMLGenerator(self.editor) |
|
416 html = generator.generate( |
|
417 tabSize=tabSize, |
|
418 useTabs=tabs, |
|
419 wysiwyg=wysiwyg, |
|
420 folding=folding, |
|
421 onlyStylesUsed=onlyStylesUsed, |
|
422 titleFullPath=titleFullPath |
|
423 ) |
|
424 |
|
425 if html: |
|
426 try: |
|
427 f = open(filename, "w", encoding="utf-8") |
|
428 f.write(html) |
|
429 f.close() |
|
430 except IOError as err: |
|
431 QApplication.restoreOverrideCursor() |
|
432 E5MessageBox.critical( |
|
433 self.editor, |
|
434 self.tr("Export source"), |
|
435 self.tr( |
|
436 """<p>The source could not be exported to""" |
|
437 """ <b>{0}</b>.</p><p>Reason: {1}</p>""") |
|
438 .format(filename, str(err))) |
|
439 else: |
|
440 QApplication.restoreOverrideCursor() |
|
441 E5MessageBox.critical( |
|
442 self.editor, |
|
443 self.tr("Export source"), |
|
444 self.tr( |
|
445 """<p>The source could not be exported to""" |
|
446 """ <b>{0}</b>.</p><p>Reason: No HTML code""" |
|
447 """ generated.</p>""") |
|
448 .format(filename)) |
|
449 finally: |
|
450 QApplication.restoreOverrideCursor() |
|
451 |
|
452 def __generateFromReSTDocutils(self): |
|
453 """ |
|
454 Private method to convert ReST text into HTML using 'docutils'. |
|
455 |
|
456 @return processed HTML (string) |
|
457 """ |
|
458 if 'sphinx' in sys.modules: |
|
459 # Make sure any Sphinx polution of docutils has been removed. |
|
460 unloadKeys = [k for k in sys.modules.keys() |
|
461 if k.startswith(('docutils', 'sphinx'))] |
|
462 for key in unloadKeys: |
|
463 sys.modules.pop(key) |
|
464 |
|
465 try: |
|
466 import docutils.core # __IGNORE_EXCEPTION__ |
|
467 except ImportError: |
|
468 E5MessageBox.critical( |
|
469 self.editor, |
|
470 self.tr("Export source"), |
|
471 self.tr( |
|
472 """<p>ReStructuredText export requires the""" |
|
473 """ <b>python-docutils</b> package.<br/>Install it with""" |
|
474 """ your package manager, 'pip install docutils' or see""" |
|
475 """ <a href="http://pypi.python.org/pypi/docutils">""" |
|
476 """this page.</a></p>""") |
|
477 ) |
|
478 return "" |
|
479 |
|
480 htmlFormat = Preferences.getEditor( |
|
481 "PreviewRestDocutilsHTMLFormat").lower() |
|
482 # redirect sys.stderr because we are not interested in it here |
|
483 origStderr = sys.stderr |
|
484 sys.stderr = io.StringIO() |
|
485 html = docutils.core.publish_string( |
|
486 self.editor.text(), writer_name=htmlFormat).decode("utf-8") |
|
487 sys.stderr = origStderr |
|
488 return html |
|
489 |
|
490 def __generateFromMarkdown(self): |
|
491 """ |
|
492 Private method to convert Markdown text into HTML. |
|
493 |
|
494 @return processed HTML |
|
495 @rtype str |
|
496 """ |
|
497 try: |
|
498 import markdown # __IGNORE_EXCEPTION__ |
|
499 except ImportError: |
|
500 E5MessageBox.critical( |
|
501 self.editor, |
|
502 self.tr("Export source"), |
|
503 self.tr( |
|
504 """<p>Markdown export requires the <b>python-markdown""" |
|
505 """</b> package.<br/>Install it with your package""" |
|
506 """ manager, 'pip install docutils' or see """ |
|
507 """<a href="http://pythonhosted.org/Markdown/install""" |
|
508 """.html"> installation instructions.</a></p>""") |
|
509 ) |
|
510 return "" |
|
511 |
|
512 try: |
|
513 import mdx_mathjax # __IGNORE_EXCEPTION__ __IGNORE_WARNING__ |
|
514 except ImportError: |
|
515 # mathjax doesn't require import statement if installed as |
|
516 # extension |
|
517 pass |
|
518 |
|
519 if Preferences.getEditor("PreviewMarkdownNLtoBR"): |
|
520 extensions = ['fenced_code', 'nl2br', 'extra'] |
|
521 else: |
|
522 extensions = ['fenced_code', 'extra'] |
|
523 |
|
524 # version 2.0 supports only extension names, not instances |
|
525 if markdown.version_info[0] > 2 or \ |
|
526 (markdown.version_info[0] == 2 and |
|
527 markdown.version_info[1] > 0): |
|
528 class _StrikeThroughExtension(markdown.Extension): |
|
529 """ |
|
530 Class is placed here, because it depends on imported markdown, |
|
531 and markdown import is lazy. |
|
532 |
|
533 (see http://achinghead.com/ |
|
534 python-markdown-adding-insert-delete.html this page for |
|
535 details) |
|
536 """ |
|
537 DEL_RE = r'(~~)(.*?)~~' |
|
538 |
|
539 def extendMarkdown(self, md, md_globals): |
|
540 # Create the del pattern |
|
541 del_tag = markdown.inlinepatterns.SimpleTagPattern( |
|
542 self.DEL_RE, 'del') |
|
543 # Insert del pattern into markdown parser |
|
544 md.inlinePatterns.add('del', del_tag, '>not_strong') |
|
545 |
|
546 extensions.append(_StrikeThroughExtension()) |
|
547 |
|
548 htmlFormat = Preferences.getEditor("PreviewMarkdownHTMLFormat").lower() |
|
549 try: |
|
550 body = markdown.markdown(self.editor.text(), |
|
551 extensions=extensions + ['mathjax'], |
|
552 output_format=htmlFormat) |
|
553 except (ImportError, ValueError): |
|
554 # markdown raises ValueError or ImportError, depends on version |
|
555 # It is not clear, how to distinguish missing mathjax from other |
|
556 # errors. So keep going without mathjax. |
|
557 body = markdown.markdown(self.editor.text(), |
|
558 extensions=extensions, |
|
559 output_format=htmlFormat) |
|
560 |
|
561 if htmlFormat == "xhtml1": |
|
562 head = \ |
|
563 '''<!DOCTYPE html PUBLIC "-//W3C//DTD''' \ |
|
564 ''' XHTML 1.0 Transitional//EN"\n''' \ |
|
565 ''' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional''' \ |
|
566 '''.dtd">\n''' \ |
|
567 '''<html xmlns="http://www.w3.org/1999/xhtml">\n''' |
|
568 elif htmlFormat == "html5": |
|
569 head = \ |
|
570 '''<!DOCTYPE html>\n''' \ |
|
571 '''<html lang="EN">\n''' |
|
572 else: |
|
573 head = '<html lang="EN">\n' |
|
574 head += '''<head>\n''' |
|
575 if Preferences.getEditorExporter("HTML/FullPathAsTitle"): |
|
576 head += '''<title>{0}</title>\n'''.format( |
|
577 self.editor.getFileName()) |
|
578 else: |
|
579 head += '''<title>{0}</title>\n'''.format( |
|
580 os.path.basename(self.editor.getFileName())) |
|
581 head += '''<meta name="Generator" content="eric6" />\n''' \ |
|
582 '''<meta http-equiv="Content-Type" ''' \ |
|
583 '''content="text/html; charset=utf-8" />\n''' \ |
|
584 '''</head>\n''' \ |
|
585 '''<body>\n''' |
|
586 |
|
587 foot = '''\n</body>\n</html>\n''' |
|
588 |
|
589 return head + body + foot |