|
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 PDF. |
|
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 from PyQt6.QtGui import QFontInfo |
|
14 from PyQt6.Qsci import QsciScintilla |
|
15 |
|
16 from EricWidgets import EricMessageBox |
|
17 from EricGui.EricOverrideCursor import EricOverrideCursor |
|
18 |
|
19 from .ExporterBase import ExporterBase |
|
20 |
|
21 import Preferences |
|
22 |
|
23 PDF_FONT_DEFAULT = 1 # Helvetica |
|
24 PDF_FONTSIZE_DEFAULT = 10 |
|
25 PDF_SPACING_DEFAULT = 1.2 |
|
26 PDF_MARGIN_DEFAULT = 72 # 1.0" |
|
27 PDF_ENCODING = "WinAnsiEncoding" |
|
28 |
|
29 PDFfontNames = [ |
|
30 "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique", |
|
31 "Helvetica", "Helvetica-Bold", "Helvetica-Oblique", |
|
32 "Helvetica-BoldOblique", "Times-Roman", "Times-Bold", "Times-Italic", |
|
33 "Times-BoldItalic" |
|
34 ] |
|
35 PDFfontAscenders = [629, 718, 699] |
|
36 PDFfontDescenders = [157, 207, 217] |
|
37 PDFfontWidths = [600, 0, 0] |
|
38 |
|
39 PDFpageSizes = { |
|
40 #- name : (height, width) |
|
41 "Letter": (792, 612), |
|
42 "A4": (842, 595), |
|
43 } |
|
44 |
|
45 |
|
46 class PDFStyle: |
|
47 """ |
|
48 Simple class to store the values of a PDF style. |
|
49 """ |
|
50 def __init__(self): |
|
51 """ |
|
52 Constructor |
|
53 """ |
|
54 self.fore = "" |
|
55 self.font = 0 |
|
56 |
|
57 |
|
58 class PDFObjectTracker: |
|
59 """ |
|
60 Class to conveniently handle the tracking of PDF objects so that the |
|
61 cross-reference table can be built (PDF1.4Ref(p39)). |
|
62 |
|
63 All writes to the file are passed through a PDFObjectTracker object. |
|
64 """ |
|
65 def __init__(self, file): |
|
66 """ |
|
67 Constructor |
|
68 |
|
69 @param file file object open for writing (file) |
|
70 """ |
|
71 self.file = file |
|
72 self.offsetList = [] |
|
73 self.index = 1 |
|
74 |
|
75 def write(self, objectData): |
|
76 """ |
|
77 Public method to write the data to the file. |
|
78 |
|
79 @param objectData data to be written (integer or string) |
|
80 """ |
|
81 if isinstance(objectData, int): |
|
82 self.file.write("{0:d}".format(objectData)) |
|
83 else: |
|
84 self.file.write(objectData) |
|
85 |
|
86 def add(self, objectData): |
|
87 """ |
|
88 Public method to add a new object. |
|
89 |
|
90 @param objectData data to be added (integer or string) |
|
91 @return object number assigned to the supplied data (integer) |
|
92 """ |
|
93 self.offsetList.append(self.file.tell()) |
|
94 self.write(self.index) |
|
95 self.write(" 0 obj\n") |
|
96 self.write(objectData) |
|
97 self.write("endobj\n") |
|
98 ind = self.index |
|
99 self.index += 1 |
|
100 return ind |
|
101 |
|
102 def xref(self): |
|
103 """ |
|
104 Public method to build the xref table. |
|
105 |
|
106 @return file offset of the xref table (integer) |
|
107 """ |
|
108 xrefStart = self.file.tell() |
|
109 self.write("xref\n0 ") |
|
110 self.write(self.index) |
|
111 # a xref entry *must* be 20 bytes long (PDF1.4Ref(p64)) |
|
112 # so extra space added; also the first entry is special |
|
113 self.write("\n0000000000 65535 f \n") |
|
114 ind = 0 |
|
115 while ind < len(self.offsetList): |
|
116 self.write("{0:010d} 00000 n \n".format(self.offsetList[ind])) |
|
117 ind += 1 |
|
118 return xrefStart |
|
119 |
|
120 |
|
121 class PDFRender: |
|
122 """ |
|
123 Class to manage line and page rendering. |
|
124 |
|
125 Apart from startPDF, endPDF everything goes in via add() and nextLine() |
|
126 so that line formatting and pagination can be done properly. |
|
127 """ |
|
128 def __init__(self): |
|
129 """ |
|
130 Constructor |
|
131 """ |
|
132 self.pageStarted = False |
|
133 self.firstLine = False |
|
134 self.pageCount = 0 |
|
135 self.pageData = "" |
|
136 self.style = {} |
|
137 self.segStyle = "" |
|
138 self.segment = "" |
|
139 self.pageMargins = { |
|
140 "left": 72, |
|
141 "right": 72, |
|
142 "top": 72, |
|
143 "bottom": 72, |
|
144 } |
|
145 self.fontSize = 0 |
|
146 self.fontSet = 0 |
|
147 self.leading = 0.0 |
|
148 self.pageWidth = 0 |
|
149 self.pageHeight = 0 |
|
150 self.pageContentStart = 0 |
|
151 self.xPos = 0.0 |
|
152 self.yPos = 0.0 |
|
153 self.justWhiteSpace = False |
|
154 self.oT = None |
|
155 |
|
156 def fontToPoints(self, thousandths): |
|
157 """ |
|
158 Public method to convert the font size to points. |
|
159 |
|
160 @param thousandths font size (integer) |
|
161 @return point size of the font (integer) |
|
162 """ |
|
163 return self.fontSize * thousandths / 1000.0 |
|
164 |
|
165 def setStyle(self, style_): |
|
166 """ |
|
167 Public method to set a style. |
|
168 |
|
169 @param style_ style to be set (integer) |
|
170 @return the PDF string to set the given style (string) |
|
171 """ |
|
172 styleNext = style_ |
|
173 if style_ == -1: |
|
174 styleNext = self.styleCurrent |
|
175 |
|
176 buf = "" |
|
177 if styleNext != self.styleCurrent or style_ == -1: |
|
178 if ( |
|
179 (self.style[self.styleCurrent].font != |
|
180 self.style[styleNext].font) or |
|
181 style_ == -1 |
|
182 ): |
|
183 buf += "/F{0:d} {1:d} Tf ".format( |
|
184 self.style[styleNext].font + 1, self.fontSize) |
|
185 if ( |
|
186 (self.style[self.styleCurrent].fore != |
|
187 self.style[styleNext].fore) or |
|
188 style_ == -1 |
|
189 ): |
|
190 buf += "{0}rg ".format(self.style[styleNext].fore) |
|
191 return buf |
|
192 |
|
193 def startPDF(self): |
|
194 """ |
|
195 Public method to start the PDF document. |
|
196 """ |
|
197 if self.fontSize <= 0: |
|
198 self.fontSize = PDF_FONTSIZE_DEFAULT |
|
199 |
|
200 # leading is the term for distance between lines |
|
201 self.leading = self.fontSize * PDF_SPACING_DEFAULT |
|
202 |
|
203 # sanity check for page size and margins |
|
204 pageWidthMin = ( |
|
205 int(self.leading) + |
|
206 self.pageMargins["left"] + |
|
207 self.pageMargins["right"] |
|
208 ) |
|
209 if self.pageWidth < pageWidthMin: |
|
210 self.pageWidth = pageWidthMin |
|
211 pageHeightMin = ( |
|
212 int(self.leading) + |
|
213 self.pageMargins["top"] + |
|
214 self.pageMargins["bottom"] |
|
215 ) |
|
216 if self.pageHeight < pageHeightMin: |
|
217 self.pageHeight = pageHeightMin |
|
218 |
|
219 # start to write PDF file here (PDF1.4Ref(p63)) |
|
220 # ASCII>127 characters to indicate binary-possible stream |
|
221 self.oT.write("%PDF-1.3\n%�쏢\n") |
|
222 self.styleCurrent = QsciScintilla.STYLE_DEFAULT |
|
223 |
|
224 # build objects for font resources; note that font objects are |
|
225 # *expected* to start from index 1 since they are the first objects |
|
226 # to be inserted (PDF1.4Ref(p317)) |
|
227 for i in range(4): |
|
228 buffer = ( |
|
229 "<</Type/Font/Subtype/Type1/Name/F{0:d}/BaseFont/{1}/" |
|
230 "Encoding/{2}>>\n" |
|
231 ).format( |
|
232 i + 1, PDFfontNames[self.fontSet * 4 + i], PDF_ENCODING |
|
233 ) |
|
234 self.oT.add(buffer) |
|
235 |
|
236 self.pageContentStart = self.oT.index |
|
237 |
|
238 def endPDF(self): |
|
239 """ |
|
240 Public method to end the PDF document. |
|
241 """ |
|
242 if self.pageStarted: |
|
243 # flush buffers |
|
244 self.endPage() |
|
245 |
|
246 # refer to all used or unused fonts for simplicity |
|
247 resourceRef = self.oT.add( |
|
248 "<</ProcSet[/PDF/Text]\n/Font<</F1 1 0 R/F2 2 0 R/F3 3 0 R/" |
|
249 "F4 4 0 R>> >>\n") |
|
250 |
|
251 # create all the page objects (PDF1.4Ref(p88)) |
|
252 # forward reference pages object; calculate its object number |
|
253 pageObjectStart = self.oT.index |
|
254 pagesRef = pageObjectStart + self.pageCount |
|
255 for i in range(self.pageCount): |
|
256 buffer = ( |
|
257 "<</Type/Page/Parent {0:d} 0 R\n" |
|
258 "/MediaBox[ 0 0 {1:d} {2:d}]\n" |
|
259 "/Contents {3:d} 0 R\n" |
|
260 "/Resources {4:d} 0 R\n>>\n" |
|
261 ).format( |
|
262 pagesRef, self.pageWidth, self.pageHeight, |
|
263 self.pageContentStart + i, resourceRef |
|
264 ) |
|
265 self.oT.add(buffer) |
|
266 |
|
267 # create page tree object (PDF1.4Ref(p86)) |
|
268 self.pageData = "<</Type/Pages/Kids[\n" |
|
269 for i in range(self.pageCount): |
|
270 self.pageData += "{0:d} 0 R\n".format(pageObjectStart + i) |
|
271 self.pageData += "]/Count {0:d}\n>>\n".format(self.pageCount) |
|
272 self.oT.add(self.pageData) |
|
273 |
|
274 # create catalog object (PDF1.4Ref(p83)) |
|
275 buffer = "<</Type/Catalog/Pages {0:d} 0 R >>\n".format(pagesRef) |
|
276 catalogRef = self.oT.add(buffer) |
|
277 |
|
278 # append the cross reference table (PDF1.4Ref(p64)) |
|
279 xref = self.oT.xref() |
|
280 |
|
281 # end the file with the trailer (PDF1.4Ref(p67)) |
|
282 buffer = ( |
|
283 "trailer\n<< /Size {0:d} /Root {1:d} 0 R\n>>\nstartxref\n{2:d}\n" |
|
284 "%%EOF\n" |
|
285 ).format(self.oT.index, catalogRef, xref) |
|
286 self.oT.write(buffer) |
|
287 |
|
288 def add(self, ch, style_): |
|
289 """ |
|
290 Public method to add a character to the page. |
|
291 |
|
292 @param ch character to add (string) |
|
293 @param style_ number of the style of the character (integer) |
|
294 """ |
|
295 if not self.pageStarted: |
|
296 self.startPage() |
|
297 |
|
298 # get glyph width (TODO future non-monospace handling) |
|
299 glyphWidth = self.fontToPoints(PDFfontWidths[self.fontSet]) |
|
300 self.xPos += glyphWidth |
|
301 |
|
302 # if cannot fit into a line, flush, wrap to next line |
|
303 if self.xPos > self.pageWidth - self.pageMargins["right"]: |
|
304 self.nextLine() |
|
305 self.xPos += glyphWidth |
|
306 |
|
307 # if different style, then change to style |
|
308 if style_ != self.styleCurrent: |
|
309 self.flushSegment() |
|
310 # output code (if needed) for new style |
|
311 self.segStyle = self.setStyle(style_) |
|
312 self.stylePrev = self.styleCurrent |
|
313 self.styleCurrent = style_ |
|
314 |
|
315 # escape these characters |
|
316 if ch in (')', '(', '\\'): |
|
317 self.segment += '\\' |
|
318 if ch != ' ': |
|
319 self.justWhiteSpace = False |
|
320 self.segment += ch # add to segment data |
|
321 |
|
322 def flushSegment(self): |
|
323 """ |
|
324 Public method to flush a segment of data. |
|
325 """ |
|
326 if len(self.segment) > 0: |
|
327 if self.justWhiteSpace: # optimise |
|
328 self.styleCurrent = self.stylePrev |
|
329 else: |
|
330 self.pageData += self.segStyle |
|
331 self.pageData += "({0})Tj\n".format(self.segment) |
|
332 self.segment = "" |
|
333 self.segStyle = "" |
|
334 self.justWhiteSpace = True |
|
335 |
|
336 def startPage(self): |
|
337 """ |
|
338 Public method to start a new page. |
|
339 """ |
|
340 self.pageStarted = True |
|
341 self.firstLine = True |
|
342 self.pageCount += 1 |
|
343 fontAscender = self.fontToPoints(PDFfontAscenders[self.fontSet]) |
|
344 self.yPos = self.pageHeight - self.pageMargins["top"] - fontAscender |
|
345 |
|
346 # start a new page |
|
347 buffer = "BT 1 0 0 1 {0:d} {1:d} Tm\n".format( |
|
348 self.pageMargins["left"], int(self.yPos)) |
|
349 |
|
350 # force setting of initial font, colour |
|
351 self.segStyle = self.setStyle(-1) |
|
352 buffer += self.segStyle |
|
353 self.pageData = buffer |
|
354 self.xPos = self.pageMargins["left"] |
|
355 self.segment = "" |
|
356 self.flushSegment() |
|
357 |
|
358 def endPage(self): |
|
359 """ |
|
360 Public method to end a page. |
|
361 """ |
|
362 self.pageStarted = False |
|
363 self.flushSegment() |
|
364 |
|
365 # build actual text object; +3 is for "ET\n" |
|
366 # PDF1.4Ref(p38) EOL marker preceding endstream not counted |
|
367 textObj = "<</Length {0:d}>>\nstream\n{1}ET\nendstream\n".format( |
|
368 len(self.pageData) - 1 + 3, self.pageData) |
|
369 self.oT.add(textObj) |
|
370 |
|
371 def nextLine(self): |
|
372 """ |
|
373 Public method to start a new line. |
|
374 """ |
|
375 if not self.pageStarted: |
|
376 self.startPage() |
|
377 |
|
378 self.xPos = self.pageMargins["left"] |
|
379 self.flushSegment() |
|
380 |
|
381 # PDF follows cartesian coords, subtract -> down |
|
382 self.yPos -= self.leading |
|
383 fontDescender = self.fontToPoints(PDFfontDescenders[self.fontSet]) |
|
384 if self.yPos < self.pageMargins["bottom"] + fontDescender: |
|
385 self.endPage() |
|
386 self.startPage() |
|
387 return |
|
388 |
|
389 if self.firstLine: |
|
390 # avoid breakage due to locale setting |
|
391 f = int(self.leading * 10 + 0.5) |
|
392 buffer = "0 -{0:d}.{1:d} TD\n".format(f // 10, f % 10) |
|
393 self.firstLine = False |
|
394 else: |
|
395 buffer = "T*\n" |
|
396 self.pageData += buffer |
|
397 |
|
398 |
|
399 class ExporterPDF(ExporterBase): |
|
400 """ |
|
401 Class implementing an exporter for PDF. |
|
402 """ |
|
403 def __init__(self, editor, parent=None): |
|
404 """ |
|
405 Constructor |
|
406 |
|
407 @param editor reference to the editor object (QScintilla.Editor.Editor) |
|
408 @param parent parent object of the exporter (QObject) |
|
409 """ |
|
410 ExporterBase.__init__(self, editor, parent) |
|
411 |
|
412 def __getPDFRGB(self, color): |
|
413 """ |
|
414 Private method to convert a color object to the correct PDF color. |
|
415 |
|
416 @param color color object to convert (QColor) |
|
417 @return PDF color description (string) |
|
418 """ |
|
419 pdfColor = "" |
|
420 for component in [color.red(), color.green(), color.blue()]: |
|
421 c = (component * 1000 + 127) // 255 |
|
422 if c in (0, 1000): |
|
423 pdfColor += "{0:d} ".format(c // 1000) |
|
424 else: |
|
425 pdfColor += "0.{0:03d} ".format(c) |
|
426 return pdfColor |
|
427 |
|
428 def exportSource(self): |
|
429 """ |
|
430 Public method performing the export. |
|
431 """ |
|
432 self.pr = PDFRender() |
|
433 |
|
434 filename = self._getFileName(self.tr("PDF Files (*.pdf)")) |
|
435 if not filename: |
|
436 return |
|
437 |
|
438 self.editor.recolor(0, -1) |
|
439 lex = self.editor.getLexer() |
|
440 |
|
441 tabSize = self.editor.getEditorConfig("TabWidth") |
|
442 if tabSize == 0: |
|
443 tabSize = 4 |
|
444 |
|
445 # get magnification value to add to default screen font size |
|
446 self.pr.fontSize = Preferences.getEditorExporter( |
|
447 "PDF/Magnification") |
|
448 |
|
449 # set font family according to face name |
|
450 fontName = Preferences.getEditorExporter("PDF/Font") |
|
451 self.pr.fontSet = PDF_FONT_DEFAULT |
|
452 if fontName == "Courier": |
|
453 self.pr.fontSet = 0 |
|
454 elif fontName == "Helvetica": |
|
455 self.pr.fontSet = 1 |
|
456 elif fontName == "Times": |
|
457 self.pr.fontSet = 2 |
|
458 |
|
459 # page size: height, width, |
|
460 pageSize = Preferences.getEditorExporter("PDF/PageSize") |
|
461 try: |
|
462 pageDimensions = PDFpageSizes[pageSize] |
|
463 except KeyError: |
|
464 pageDimensions = PDFpageSizes["A4"] |
|
465 self.pr.pageHeight = pageDimensions[0] |
|
466 self.pr.pageWidth = pageDimensions[1] |
|
467 |
|
468 # page margins: left, right, top, bottom |
|
469 # < 0 to use PDF default values |
|
470 val = Preferences.getEditorExporter("PDF/MarginLeft") |
|
471 if val < 0: |
|
472 self.pr.pageMargins["left"] = PDF_MARGIN_DEFAULT |
|
473 else: |
|
474 self.pr.pageMargins["left"] = val |
|
475 val = Preferences.getEditorExporter("PDF/MarginRight") |
|
476 if val < 0: |
|
477 self.pr.pageMargins["right"] = PDF_MARGIN_DEFAULT |
|
478 else: |
|
479 self.pr.pageMargins["right"] = val |
|
480 val = Preferences.getEditorExporter("PDF/MarginTop") |
|
481 if val < 0: |
|
482 self.pr.pageMargins["top"] = PDF_MARGIN_DEFAULT |
|
483 else: |
|
484 self.pr.pageMargins["top"] = val |
|
485 val = Preferences.getEditorExporter("PDF/MarginBottom") |
|
486 if val < 0: |
|
487 self.pr.pageMargins["bottom"] = PDF_MARGIN_DEFAULT |
|
488 else: |
|
489 self.pr.pageMargins["bottom"] = val |
|
490 |
|
491 # collect all styles available for that 'language' |
|
492 # or the default style if no language is available... |
|
493 if lex: |
|
494 istyle = 0 |
|
495 while istyle <= QsciScintilla.STYLE_MAX: |
|
496 if (istyle <= QsciScintilla.STYLE_DEFAULT or |
|
497 istyle > QsciScintilla.STYLE_LASTPREDEFINED): |
|
498 if ( |
|
499 lex.description(istyle) or |
|
500 istyle == QsciScintilla.STYLE_DEFAULT |
|
501 ): |
|
502 style = PDFStyle() |
|
503 |
|
504 font = lex.font(istyle) |
|
505 if font.italic(): |
|
506 style.font |= 2 |
|
507 if font.bold(): |
|
508 style.font |= 1 |
|
509 |
|
510 colour = lex.color(istyle) |
|
511 style.fore = self.__getPDFRGB(colour) |
|
512 self.pr.style[istyle] = style |
|
513 |
|
514 # get substyles |
|
515 subs_start, subs_count = self.editor.getSubStyleRange( |
|
516 istyle) |
|
517 for subs_idx in range(subs_count): |
|
518 style = PDFStyle() |
|
519 font = lex.font(subs_start + subs_idx) |
|
520 if font.italic(): |
|
521 style.font |= 2 |
|
522 if font.bold(): |
|
523 style.font |= 1 |
|
524 |
|
525 colour = lex.color(subs_start + subs_idx) |
|
526 style.fore = self.__getPDFRGB(colour) |
|
527 # styleAt returns negative numbers for substyles |
|
528 self.pr.style[subs_idx - subs_start] = style |
|
529 |
|
530 # grab font size from default style |
|
531 if istyle == QsciScintilla.STYLE_DEFAULT: |
|
532 fontSize = QFontInfo(font).pointSize() |
|
533 if fontSize > 0: |
|
534 self.pr.fontSize += fontSize |
|
535 else: |
|
536 self.pr.fontSize = PDF_FONTSIZE_DEFAULT |
|
537 |
|
538 istyle += 1 |
|
539 else: |
|
540 style = PDFStyle() |
|
541 |
|
542 font = Preferences.getEditorOtherFonts("DefaultFont") |
|
543 if font.italic(): |
|
544 style.font |= 2 |
|
545 if font.bold(): |
|
546 style.font |= 1 |
|
547 |
|
548 colour = self.editor.color() |
|
549 style.fore = self.__getPDFRGB(colour) |
|
550 self.pr.style[0] = style |
|
551 self.pr.style[QsciScintilla.STYLE_DEFAULT] = style |
|
552 |
|
553 fontSize = QFontInfo(font).pointSize() |
|
554 if fontSize > 0: |
|
555 self.pr.fontSize += fontSize |
|
556 else: |
|
557 self.pr.fontSize = PDF_FONTSIZE_DEFAULT |
|
558 |
|
559 with EricOverrideCursor(), open(filename, "w", encoding="cp1250", |
|
560 errors="backslashreplace") as f: |
|
561 # save file in win ansi using cp1250 |
|
562 try: |
|
563 # initialise PDF rendering |
|
564 ot = PDFObjectTracker(f) |
|
565 self.pr.oT = ot |
|
566 self.pr.startPDF() |
|
567 |
|
568 # do here all the writing |
|
569 lengthDoc = self.editor.length() |
|
570 |
|
571 if lengthDoc == 0: |
|
572 self.pr.nextLine() # enable zero length docs |
|
573 else: |
|
574 pos = 0 |
|
575 column = 0 |
|
576 utf8 = self.editor.isUtf8() |
|
577 utf8Ch = b"" |
|
578 utf8Len = 0 |
|
579 |
|
580 while pos < lengthDoc: |
|
581 ch = self.editor.byteAt(pos) |
|
582 style = self.editor.styleAt(pos) |
|
583 |
|
584 if ch == b'\t': |
|
585 # expand tabs |
|
586 ts = tabSize - (column % tabSize) |
|
587 column += ts |
|
588 self.pr.add(' ' * ts, style) |
|
589 elif ch in (b'\r', b'\n'): |
|
590 if ( |
|
591 ch == b'\r' and |
|
592 self.editor.byteAt(pos + 1) == b'\n' |
|
593 ): |
|
594 pos += 1 |
|
595 # close and begin a newline... |
|
596 self.pr.nextLine() |
|
597 column = 0 |
|
598 else: |
|
599 # write the character normally... |
|
600 if ord(ch) > 127 and utf8: |
|
601 utf8Ch += ch |
|
602 if utf8Len == 0: |
|
603 if (utf8Ch[0] & 0xF0) == 0xF0: |
|
604 utf8Len = 4 |
|
605 elif (utf8Ch[0] & 0xE0) == 0xE0: |
|
606 utf8Len = 3 |
|
607 elif (utf8Ch[0] & 0xC0) == 0xC0: |
|
608 utf8Len = 2 |
|
609 column -= 1 |
|
610 # will be incremented again later |
|
611 elif len(utf8Ch) == utf8Len: |
|
612 ch = utf8Ch.decode('utf8') |
|
613 self.pr.add(ch, style) |
|
614 utf8Ch = b"" |
|
615 utf8Len = 0 |
|
616 else: |
|
617 column -= 1 |
|
618 # will be incremented again later |
|
619 else: |
|
620 self.pr.add(ch.decode(), style) |
|
621 column += 1 |
|
622 |
|
623 pos += 1 |
|
624 |
|
625 # write required stuff and close the PDF file |
|
626 self.pr.endPDF() |
|
627 except OSError as err: |
|
628 EricMessageBox.critical( |
|
629 self.editor, |
|
630 self.tr("Export source"), |
|
631 self.tr( |
|
632 """<p>The source could not be exported to""" |
|
633 """ <b>{0}</b>.</p><p>Reason: {1}</p>""") |
|
634 .format(filename, str(err))) |