src/eric7/QScintilla/Exporters/ExporterPDF.py

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

eric ide

mercurial