28 PDF_MARGIN_DEFAULT = 72 # 1.0" |
28 PDF_MARGIN_DEFAULT = 72 # 1.0" |
29 PDF_ENCODING = "WinAnsiEncoding" |
29 PDF_ENCODING = "WinAnsiEncoding" |
30 |
30 |
31 PDFfontNames = [ |
31 PDFfontNames = [ |
32 "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique", |
32 "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique", |
33 "Helvetica", "Helvetica-Bold", "Helvetica-Oblique", "Helvetica-BoldOblique", |
33 "Helvetica", "Helvetica-Bold", "Helvetica-Oblique", |
34 "Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic" |
34 "Helvetica-BoldOblique", "Times-Roman", "Times-Bold", "Times-Italic", |
|
35 "Times-BoldItalic" |
35 ] |
36 ] |
36 PDFfontAscenders = [629, 718, 699] |
37 PDFfontAscenders = [629, 718, 699] |
37 PDFfontDescenders = [157, 207, 217] |
38 PDFfontDescenders = [157, 207, 217] |
38 PDFfontWidths = [600, 0, 0] |
39 PDFfontWidths = [600, 0, 0] |
39 |
40 |
56 self.font = 0 |
57 self.font = 0 |
57 |
58 |
58 |
59 |
59 class PDFObjectTracker(object): |
60 class PDFObjectTracker(object): |
60 """ |
61 """ |
61 Class to conveniently handle the tracking of PDF objects |
62 Class to conveniently handle the tracking of PDF objects so that the |
62 so that the cross-reference table can be built (PDF1.4Ref(p39)) |
63 cross-reference table can be built (PDF1.4Ref(p39)). |
|
64 |
63 All writes to the file are passed through a PDFObjectTracker object. |
65 All writes to the file are passed through a PDFObjectTracker object. |
64 """ |
66 """ |
65 def __init__(self, file): |
67 def __init__(self, file): |
66 """ |
68 """ |
67 Constructor |
69 Constructor |
155 |
157 |
156 def fontToPoints(self, thousandths): |
158 def fontToPoints(self, thousandths): |
157 """ |
159 """ |
158 Public method to convert the font size to points. |
160 Public method to convert the font size to points. |
159 |
161 |
|
162 @param thousandths font size (integer) |
160 @return point size of the font (integer) |
163 @return point size of the font (integer) |
161 """ |
164 """ |
162 return self.fontSize * thousandths / 1000.0 |
165 return self.fontSize * thousandths / 1000.0 |
163 |
166 |
164 def setStyle(self, style_): |
167 def setStyle(self, style_): |
172 if style_ == -1: |
175 if style_ == -1: |
173 styleNext = self.styleCurrent |
176 styleNext = self.styleCurrent |
174 |
177 |
175 buf = "" |
178 buf = "" |
176 if styleNext != self.styleCurrent or style_ == -1: |
179 if styleNext != self.styleCurrent or style_ == -1: |
177 if self.style[self.styleCurrent].font != self.style[styleNext].font or \ |
180 if self.style[self.styleCurrent].font != \ |
178 style_ == -1: |
181 self.style[styleNext].font or style_ == -1: |
179 buf += "/F{0:d} {1:d} Tf ".format(self.style[styleNext].font + 1, |
182 buf += "/F{0:d} {1:d} Tf ".format( |
180 self.fontSize) |
183 self.style[styleNext].font + 1, self.fontSize) |
181 if self.style[self.styleCurrent].fore != self.style[styleNext].fore or \ |
184 if self.style[self.styleCurrent].fore != \ |
182 style_ == -1: |
185 self.style[styleNext].fore or style_ == -1: |
183 buf += "{0}rg ".format(self.style[styleNext].fore) |
186 buf += "{0}rg ".format(self.style[styleNext].fore) |
184 return buf |
187 return buf |
185 |
188 |
186 def startPDF(self): |
189 def startPDF(self): |
187 """ |
190 """ |
211 # build objects for font resources; note that font objects are |
214 # build objects for font resources; note that font objects are |
212 # *expected* to start from index 1 since they are the first objects |
215 # *expected* to start from index 1 since they are the first objects |
213 # to be inserted (PDF1.4Ref(p317)) |
216 # to be inserted (PDF1.4Ref(p317)) |
214 for i in range(4): |
217 for i in range(4): |
215 buffer = \ |
218 buffer = \ |
216 "<</Type/Font/Subtype/Type1/Name/F{0:d}/BaseFont/{1}/Encoding/{2}>>\n"\ |
219 "<</Type/Font/Subtype/Type1/Name/F{0:d}/BaseFont/{1}/" \ |
217 .format(i + 1, PDFfontNames[self.fontSet * 4 + i], PDF_ENCODING) |
220 "Encoding/{2}>>\n".format( |
|
221 i + 1, PDFfontNames[self.fontSet * 4 + i], PDF_ENCODING) |
218 self.oT.add(buffer) |
222 self.oT.add(buffer) |
219 |
223 |
220 self.pageContentStart = self.oT.index |
224 self.pageContentStart = self.oT.index |
221 |
225 |
222 def endPDF(self): |
226 def endPDF(self): |
227 # flush buffers |
231 # flush buffers |
228 self.endPage() |
232 self.endPage() |
229 |
233 |
230 # refer to all used or unused fonts for simplicity |
234 # refer to all used or unused fonts for simplicity |
231 resourceRef = self.oT.add( |
235 resourceRef = self.oT.add( |
232 "<</ProcSet[/PDF/Text]\n/Font<</F1 1 0 R/F2 2 0 R/F3 3 0 R/F4 4 0 R>> >>\n") |
236 "<</ProcSet[/PDF/Text]\n/Font<</F1 1 0 R/F2 2 0 R/F3 3 0 R/" |
|
237 "F4 4 0 R>> >>\n") |
233 |
238 |
234 # create all the page objects (PDF1.4Ref(p88)) |
239 # create all the page objects (PDF1.4Ref(p88)) |
235 # forward reference pages object; calculate its object number |
240 # forward reference pages object; calculate its object number |
236 pageObjectStart = self.oT.index |
241 pageObjectStart = self.oT.index |
237 pagesRef = pageObjectStart + self.pageCount |
242 pagesRef = pageObjectStart + self.pageCount |
257 |
262 |
258 # append the cross reference table (PDF1.4Ref(p64)) |
263 # append the cross reference table (PDF1.4Ref(p64)) |
259 xref = self.oT.xref() |
264 xref = self.oT.xref() |
260 |
265 |
261 # end the file with the trailer (PDF1.4Ref(p67)) |
266 # end the file with the trailer (PDF1.4Ref(p67)) |
262 buffer = "trailer\n<< /Size {0:d} /Root {1:d} 0 R\n>>\nstartxref\n{2:d}\n%%EOF\n"\ |
267 buffer = \ |
263 .format(self.oT.index, catalogRef, xref) |
268 "trailer\n<< /Size {0:d} /Root {1:d} 0 R\n>>\nstartxref\n{2:d}\n" \ |
|
269 "%%EOF\n".format(self.oT.index, catalogRef, xref) |
264 self.oT.write(buffer) |
270 self.oT.write(buffer) |
265 |
271 |
266 def add(self, ch, style_): |
272 def add(self, ch, style_): |
267 """ |
273 """ |
268 Public method to add a character to the page. |
274 Public method to add a character to the page. |
423 tabSize = Preferences.getEditor("TabWidth") |
429 tabSize = Preferences.getEditor("TabWidth") |
424 if tabSize == 0: |
430 if tabSize == 0: |
425 tabSize = 4 |
431 tabSize = 4 |
426 |
432 |
427 # get magnification value to add to default screen font size |
433 # get magnification value to add to default screen font size |
428 self.pr.fontSize = Preferences.getEditorExporter("PDF/Magnification") |
434 self.pr.fontSize = Preferences.getEditorExporter( |
|
435 "PDF/Magnification") |
429 |
436 |
430 # set font family according to face name |
437 # set font family according to face name |
431 fontName = Preferences.getEditorExporter("PDF/Font") |
438 fontName = Preferences.getEditorExporter("PDF/Font") |
432 self.pr.fontSet = PDF_FONT_DEFAULT |
439 self.pr.fontSet = PDF_FONT_DEFAULT |
433 if fontName == "Courier": |
440 if fontName == "Courier": |
519 else: |
526 else: |
520 self.pr.fontSize = PDF_FONTSIZE_DEFAULT |
527 self.pr.fontSize = PDF_FONTSIZE_DEFAULT |
521 |
528 |
522 try: |
529 try: |
523 # save file in win ansi using cp1250 |
530 # save file in win ansi using cp1250 |
524 f = open(filename, "w", encoding="cp1250", errors="backslashreplace") |
531 f = open(filename, "w", encoding="cp1250", |
|
532 errors="backslashreplace") |
525 |
533 |
526 # initialise PDF rendering |
534 # initialise PDF rendering |
527 ot = PDFObjectTracker(f) |
535 ot = PDFObjectTracker(f) |
528 self.pr.oT = ot |
536 self.pr.oT = ot |
529 self.pr.startPDF() |
537 self.pr.startPDF() |
548 # expand tabs |
556 # expand tabs |
549 ts = tabSize - (column % tabSize) |
557 ts = tabSize - (column % tabSize) |
550 column += ts |
558 column += ts |
551 self.pr.add(' ' * ts, style) |
559 self.pr.add(' ' * ts, style) |
552 elif ch == b'\r' or ch == b'\n': |
560 elif ch == b'\r' or ch == b'\n': |
553 if ch == b'\r' and self.editor.byteAt(pos + 1) == b'\n': |
561 if ch == b'\r' and \ |
|
562 self.editor.byteAt(pos + 1) == b'\n': |
554 pos += 1 |
563 pos += 1 |
555 # close and begin a newline... |
564 # close and begin a newline... |
556 self.pr.nextLine() |
565 self.pr.nextLine() |
557 column = 0 |
566 column = 0 |
558 else: |
567 else: |
564 utf8Len = 4 |
573 utf8Len = 4 |
565 elif (utf8Ch[0] & 0xE0) == 0xE0: |
574 elif (utf8Ch[0] & 0xE0) == 0xE0: |
566 utf8Len = 3 |
575 utf8Len = 3 |
567 elif (utf8Ch[0] & 0xC0) == 0xC0: |
576 elif (utf8Ch[0] & 0xC0) == 0xC0: |
568 utf8Len = 2 |
577 utf8Len = 2 |
569 column -= 1 # will be incremented again later |
578 column -= 1 # will be incremented |
|
579 # again later |
570 elif len(utf8Ch) == utf8Len: |
580 elif len(utf8Ch) == utf8Len: |
571 ch = utf8Ch.decode('utf8') |
581 ch = utf8Ch.decode('utf8') |
572 self.pr.add(ch, style) |
582 self.pr.add(ch, style) |
573 utf8Ch = b"" |
583 utf8Ch = b"" |
574 utf8Len = 0 |
584 utf8Len = 0 |
575 else: |
585 else: |
576 column -= 1 # will be incremented again later |
586 column -= 1 # will be incremented |
|
587 # again later |
577 else: |
588 else: |
578 self.pr.add(ch.decode(), style) |
589 self.pr.add(ch.decode(), style) |
579 column += 1 |
590 column += 1 |
580 |
591 |
581 pos += 1 |
592 pos += 1 |
583 # write required stuff and close the PDF file |
594 # write required stuff and close the PDF file |
584 self.pr.endPDF() |
595 self.pr.endPDF() |
585 f.close() |
596 f.close() |
586 except IOError as err: |
597 except IOError as err: |
587 QApplication.restoreOverrideCursor() |
598 QApplication.restoreOverrideCursor() |
588 E5MessageBox.critical(self.editor, |
599 E5MessageBox.critical( |
|
600 self.editor, |
589 self.trUtf8("Export source"), |
601 self.trUtf8("Export source"), |
590 self.trUtf8( |
602 self.trUtf8( |
591 """<p>The source could not be exported to <b>{0}</b>.</p>""" |
603 """<p>The source could not be exported to""" |
592 """<p>Reason: {1}</p>""")\ |
604 """ <b>{0}</b>.</p><p>Reason: {1}</p>""")\ |
593 .format(filename, str(err))) |
605 .format(filename, str(err))) |
594 finally: |
606 finally: |
595 QApplication.restoreOverrideCursor() |
607 QApplication.restoreOverrideCursor() |