src/eric7/QScintilla/Exporters/ExporterPDF.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
diff -r e9e7eca7efee -r bf71ee032bb4 src/eric7/QScintilla/Exporters/ExporterPDF.py
--- a/src/eric7/QScintilla/Exporters/ExporterPDF.py	Wed Jul 13 11:16:20 2022 +0200
+++ b/src/eric7/QScintilla/Exporters/ExporterPDF.py	Wed Jul 13 14:55:47 2022 +0200
@@ -20,24 +20,32 @@
 
 import Preferences
 
-PDF_FONT_DEFAULT = 1    # Helvetica
+PDF_FONT_DEFAULT = 1  # Helvetica
 PDF_FONTSIZE_DEFAULT = 10
 PDF_SPACING_DEFAULT = 1.2
 PDF_MARGIN_DEFAULT = 72  # 1.0"
 PDF_ENCODING = "WinAnsiEncoding"
 
 PDFfontNames = [
-    "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique",
-    "Helvetica", "Helvetica-Bold", "Helvetica-Oblique",
-    "Helvetica-BoldOblique", "Times-Roman", "Times-Bold", "Times-Italic",
-    "Times-BoldItalic"
+    "Courier",
+    "Courier-Bold",
+    "Courier-Oblique",
+    "Courier-BoldOblique",
+    "Helvetica",
+    "Helvetica-Bold",
+    "Helvetica-Oblique",
+    "Helvetica-BoldOblique",
+    "Times-Roman",
+    "Times-Bold",
+    "Times-Italic",
+    "Times-BoldItalic",
 ]
 PDFfontAscenders = [629, 718, 699]
 PDFfontDescenders = [157, 207, 217]
 PDFfontWidths = [600, 0, 0]
 
 PDFpageSizes = {
-    #- name   : (height, width)
+    # - name   : (height, width)
     "Letter": (792, 612),
     "A4": (842, 595),
 }
@@ -47,6 +55,7 @@
     """
     Simple class to store the values of a PDF style.
     """
+
     def __init__(self):
         """
         Constructor
@@ -59,34 +68,35 @@
     """
     Class to conveniently handle the tracking of PDF objects so that the
     cross-reference table can be built (PDF1.4Ref(p39)).
-    
+
     All writes to the file are passed through a PDFObjectTracker object.
     """
+
     def __init__(self, file):
         """
         Constructor
-        
+
         @param file file object open for writing (file)
         """
         self.file = file
         self.offsetList = []
         self.index = 1
-        
+
     def write(self, objectData):
         """
         Public method to write the data to the file.
-        
+
         @param objectData data to be written (integer or string)
         """
         if isinstance(objectData, int):
             self.file.write("{0:d}".format(objectData))
         else:
             self.file.write(objectData)
-        
+
     def add(self, objectData):
         """
         Public method to add a new object.
-        
+
         @param objectData data to be added (integer or string)
         @return object number assigned to the supplied data (integer)
         """
@@ -98,11 +108,11 @@
         ind = self.index
         self.index += 1
         return ind
-        
+
     def xref(self):
         """
         Public method to build the xref table.
-        
+
         @return file offset of the xref table (integer)
         """
         xrefStart = self.file.tell()
@@ -121,10 +131,11 @@
 class PDFRender:
     """
     Class to manage line and page rendering.
-    
+
     Apart from startPDF, endPDF everything goes in via add() and nextLine()
     so that line formatting and pagination can be done properly.
     """
+
     def __init__(self):
         """
         Constructor
@@ -152,75 +163,68 @@
         self.yPos = 0.0
         self.justWhiteSpace = False
         self.oT = None
-        
+
     def fontToPoints(self, thousandths):
         """
         Public method to convert the font size to points.
-        
+
         @param thousandths font size (integer)
         @return point size of the font (integer)
         """
         return self.fontSize * thousandths / 1000.0
-        
+
     def setStyle(self, style_):
         """
         Public method to set a style.
-        
+
         @param style_ style to be set (integer)
         @return the PDF string to set the given style (string)
         """
         styleNext = style_
         if style_ == -1:
             styleNext = self.styleCurrent
-        
+
         buf = ""
         if styleNext != self.styleCurrent or style_ == -1:
             if (
-                (self.style[self.styleCurrent].font !=
-                 self.style[styleNext].font) or
-                style_ == -1
-            ):
+                self.style[self.styleCurrent].font != self.style[styleNext].font
+            ) or style_ == -1:
                 buf += "/F{0:d} {1:d} Tf ".format(
-                    self.style[styleNext].font + 1, self.fontSize)
+                    self.style[styleNext].font + 1, self.fontSize
+                )
             if (
-                (self.style[self.styleCurrent].fore !=
-                 self.style[styleNext].fore) or
-                style_ == -1
-            ):
+                self.style[self.styleCurrent].fore != self.style[styleNext].fore
+            ) or style_ == -1:
                 buf += "{0}rg ".format(self.style[styleNext].fore)
         return buf
-        
+
     def startPDF(self):
         """
         Public method to start the PDF document.
         """
         if self.fontSize <= 0:
             self.fontSize = PDF_FONTSIZE_DEFAULT
-        
+
         # leading is the term for distance between lines
         self.leading = self.fontSize * PDF_SPACING_DEFAULT
-        
+
         # sanity check for page size and margins
         pageWidthMin = (
-            int(self.leading) +
-            self.pageMargins["left"] +
-            self.pageMargins["right"]
+            int(self.leading) + self.pageMargins["left"] + self.pageMargins["right"]
         )
         if self.pageWidth < pageWidthMin:
             self.pageWidth = pageWidthMin
         pageHeightMin = (
-            int(self.leading) +
-            self.pageMargins["top"] +
-            self.pageMargins["bottom"]
+            int(self.leading) + self.pageMargins["top"] + self.pageMargins["bottom"]
         )
         if self.pageHeight < pageHeightMin:
             self.pageHeight = pageHeightMin
-        
+
         # start to write PDF file here (PDF1.4Ref(p63))
         # ASCII>127 characters to indicate binary-possible stream
         self.oT.write("%PDF-1.3\n%�쏢\n")
         self.styleCurrent = QsciScintilla.STYLE_DEFAULT
-        
+
         # build objects for font resources; note that font objects are
         # *expected* to start from index 1 since they are the first objects
         # to be inserted (PDF1.4Ref(p317))
@@ -228,13 +232,11 @@
             buffer = (
                 "<</Type/Font/Subtype/Type1/Name/F{0:d}/BaseFont/{1}/"
                 "Encoding/{2}>>\n"
-            ).format(
-                i + 1, PDFfontNames[self.fontSet * 4 + i], PDF_ENCODING
-            )
+            ).format(i + 1, PDFfontNames[self.fontSet * 4 + i], PDF_ENCODING)
             self.oT.add(buffer)
-        
+
         self.pageContentStart = self.oT.index
-        
+
     def endPDF(self):
         """
         Public method to end the PDF document.
@@ -242,12 +244,13 @@
         if self.pageStarted:
             # flush buffers
             self.endPage()
-        
+
         # refer to all used or unused fonts for simplicity
         resourceRef = self.oT.add(
             "<</ProcSet[/PDF/Text]\n/Font<</F1 1 0 R/F2 2 0 R/F3 3 0 R/"
-            "F4 4 0 R>> >>\n")
-        
+            "F4 4 0 R>> >>\n"
+        )
+
         # create all the page objects (PDF1.4Ref(p88))
         # forward reference pages object; calculate its object number
         pageObjectStart = self.oT.index
@@ -259,51 +262,53 @@
                 "/Contents {3:d} 0 R\n"
                 "/Resources {4:d} 0 R\n>>\n"
             ).format(
-                pagesRef, self.pageWidth, self.pageHeight,
-                self.pageContentStart + i, resourceRef
+                pagesRef,
+                self.pageWidth,
+                self.pageHeight,
+                self.pageContentStart + i,
+                resourceRef,
             )
             self.oT.add(buffer)
-        
+
         # create page tree object (PDF1.4Ref(p86))
         self.pageData = "<</Type/Pages/Kids[\n"
         for i in range(self.pageCount):
             self.pageData += "{0:d} 0 R\n".format(pageObjectStart + i)
         self.pageData += "]/Count {0:d}\n>>\n".format(self.pageCount)
         self.oT.add(self.pageData)
-        
+
         # create catalog object (PDF1.4Ref(p83))
         buffer = "<</Type/Catalog/Pages {0:d} 0 R >>\n".format(pagesRef)
         catalogRef = self.oT.add(buffer)
-        
+
         # append the cross reference table (PDF1.4Ref(p64))
         xref = self.oT.xref()
-        
+
         # end the file with the trailer (PDF1.4Ref(p67))
         buffer = (
-            "trailer\n<< /Size {0:d} /Root {1:d} 0 R\n>>\nstartxref\n{2:d}\n"
-            "%%EOF\n"
+            "trailer\n<< /Size {0:d} /Root {1:d} 0 R\n>>\nstartxref\n{2:d}\n" "%%EOF\n"
         ).format(self.oT.index, catalogRef, xref)
         self.oT.write(buffer)
-        
+
     def add(self, ch, style_):
         """
         Public method to add a character to the page.
-        
+
         @param ch character to add (string)
         @param style_ number of the style of the character (integer)
         """
         if not self.pageStarted:
             self.startPage()
-        
+
         # get glyph width (TODO future non-monospace handling)
         glyphWidth = self.fontToPoints(PDFfontWidths[self.fontSet])
         self.xPos += glyphWidth
-        
+
         # if cannot fit into a line, flush, wrap to next line
         if self.xPos > self.pageWidth - self.pageMargins["right"]:
             self.nextLine()
             self.xPos += glyphWidth
-        
+
         # if different style, then change to style
         if style_ != self.styleCurrent:
             self.flushSegment()
@@ -311,20 +316,20 @@
             self.segStyle = self.setStyle(style_)
             self.stylePrev = self.styleCurrent
             self.styleCurrent = style_
-        
+
         # escape these characters
-        if ch in (')', '(', '\\'):
-            self.segment += '\\'
-        if ch != ' ':
+        if ch in (")", "(", "\\"):
+            self.segment += "\\"
+        if ch != " ":
             self.justWhiteSpace = False
         self.segment += ch  # add to segment data
-        
+
     def flushSegment(self):
         """
         Public method to flush a segment of data.
         """
         if len(self.segment) > 0:
-            if self.justWhiteSpace:     # optimise
+            if self.justWhiteSpace:  # optimise
                 self.styleCurrent = self.stylePrev
             else:
                 self.pageData += self.segStyle
@@ -332,7 +337,7 @@
             self.segment = ""
             self.segStyle = ""
             self.justWhiteSpace = True
-        
+
     def startPage(self):
         """
         Public method to start a new page.
@@ -342,11 +347,12 @@
         self.pageCount += 1
         fontAscender = self.fontToPoints(PDFfontAscenders[self.fontSet])
         self.yPos = self.pageHeight - self.pageMargins["top"] - fontAscender
-        
+
         # start a new page
         buffer = "BT 1 0 0 1 {0:d} {1:d} Tm\n".format(
-            self.pageMargins["left"], int(self.yPos))
-        
+            self.pageMargins["left"], int(self.yPos)
+        )
+
         # force setting of initial font, colour
         self.segStyle = self.setStyle(-1)
         buffer += self.segStyle
@@ -354,30 +360,31 @@
         self.xPos = self.pageMargins["left"]
         self.segment = ""
         self.flushSegment()
-        
+
     def endPage(self):
         """
         Public method to end a page.
         """
         self.pageStarted = False
         self.flushSegment()
-        
+
         # build actual text object; +3 is for "ET\n"
         # PDF1.4Ref(p38) EOL marker preceding endstream not counted
         textObj = "<</Length {0:d}>>\nstream\n{1}ET\nendstream\n".format(
-                  len(self.pageData) - 1 + 3, self.pageData)
+            len(self.pageData) - 1 + 3, self.pageData
+        )
         self.oT.add(textObj)
-        
+
     def nextLine(self):
         """
         Public method to start a new line.
         """
         if not self.pageStarted:
             self.startPage()
-        
+
         self.xPos = self.pageMargins["left"]
         self.flushSegment()
-        
+
         # PDF follows cartesian coords, subtract -> down
         self.yPos -= self.leading
         fontDescender = self.fontToPoints(PDFfontDescenders[self.fontSet])
@@ -385,7 +392,7 @@
             self.endPage()
             self.startPage()
             return
-        
+
         if self.firstLine:
             # avoid breakage due to locale setting
             f = int(self.leading * 10 + 0.5)
@@ -400,19 +407,20 @@
     """
     Class implementing an exporter for PDF.
     """
+
     def __init__(self, editor, parent=None):
         """
         Constructor
-        
+
         @param editor reference to the editor object (QScintilla.Editor.Editor)
         @param parent parent object of the exporter (QObject)
         """
         ExporterBase.__init__(self, editor, parent)
-    
+
     def __getPDFRGB(self, color):
         """
         Private method to convert a color object to the correct PDF color.
-        
+
         @param color color object to convert (QColor)
         @return PDF color description (string)
         """
@@ -424,28 +432,27 @@
             else:
                 pdfColor += "0.{0:03d} ".format(c)
         return pdfColor
-        
+
     def exportSource(self):
         """
         Public method performing the export.
         """
         self.pr = PDFRender()
-        
+
         filename = self._getFileName(self.tr("PDF Files (*.pdf)"))
         if not filename:
             return
-        
+
         self.editor.recolor(0, -1)
         lex = self.editor.getLexer()
-        
+
         tabSize = self.editor.getEditorConfig("TabWidth")
         if tabSize == 0:
             tabSize = 4
-        
+
         # get magnification value to add to default screen font size
-        self.pr.fontSize = Preferences.getEditorExporter(
-            "PDF/Magnification")
-        
+        self.pr.fontSize = Preferences.getEditorExporter("PDF/Magnification")
+
         # set font family according to face name
         fontName = Preferences.getEditorExporter("PDF/Font")
         self.pr.fontSet = PDF_FONT_DEFAULT
@@ -455,7 +462,7 @@
             self.pr.fontSet = 1
         elif fontName == "Times":
             self.pr.fontSet = 2
-        
+
         # page size: height, width,
         pageSize = Preferences.getEditorExporter("PDF/PageSize")
         try:
@@ -464,7 +471,7 @@
             pageDimensions = PDFpageSizes["A4"]
         self.pr.pageHeight = pageDimensions[0]
         self.pr.pageWidth = pageDimensions[1]
-        
+
         # page margins: left, right, top, bottom
         # < 0 to use PDF default values
         val = Preferences.getEditorExporter("PDF/MarginLeft")
@@ -487,33 +494,31 @@
             self.pr.pageMargins["bottom"] = PDF_MARGIN_DEFAULT
         else:
             self.pr.pageMargins["bottom"] = val
-        
+
         # collect all styles available for that 'language'
         # or the default style if no language is available...
         if lex:
             istyle = 0
             while istyle <= QsciScintilla.STYLE_MAX:
-                if (istyle <= QsciScintilla.STYLE_DEFAULT or
-                        istyle > QsciScintilla.STYLE_LASTPREDEFINED):
-                    if (
-                        lex.description(istyle) or
-                        istyle == QsciScintilla.STYLE_DEFAULT
-                    ):
+                if (
+                    istyle <= QsciScintilla.STYLE_DEFAULT
+                    or istyle > QsciScintilla.STYLE_LASTPREDEFINED
+                ):
+                    if lex.description(istyle) or istyle == QsciScintilla.STYLE_DEFAULT:
                         style = PDFStyle()
-                        
+
                         font = lex.font(istyle)
                         if font.italic():
                             style.font |= 2
                         if font.bold():
                             style.font |= 1
-                        
+
                         colour = lex.color(istyle)
                         style.fore = self.__getPDFRGB(colour)
                         self.pr.style[istyle] = style
-                    
+
                         # get substyles
-                        subs_start, subs_count = self.editor.getSubStyleRange(
-                            istyle)
+                        subs_start, subs_count = self.editor.getSubStyleRange(istyle)
                         for subs_idx in range(subs_count):
                             style = PDFStyle()
                             font = lex.font(subs_start + subs_idx)
@@ -521,7 +526,7 @@
                                 style.font |= 2
                             if font.bold():
                                 style.font |= 1
-                            
+
                             colour = lex.color(subs_start + subs_idx)
                             style.fore = self.__getPDFRGB(colour)
                             # styleAt returns negative numbers for substyles
@@ -534,40 +539,41 @@
                             self.pr.fontSize += fontSize
                         else:
                             self.pr.fontSize = PDF_FONTSIZE_DEFAULT
-                    
+
                 istyle += 1
         else:
             style = PDFStyle()
-            
+
             font = Preferences.getEditorOtherFonts("DefaultFont")
             if font.italic():
                 style.font |= 2
             if font.bold():
                 style.font |= 1
-            
+
             colour = self.editor.color()
             style.fore = self.__getPDFRGB(colour)
             self.pr.style[0] = style
             self.pr.style[QsciScintilla.STYLE_DEFAULT] = style
-            
+
             fontSize = QFontInfo(font).pointSize()
             if fontSize > 0:
                 self.pr.fontSize += fontSize
             else:
                 self.pr.fontSize = PDF_FONTSIZE_DEFAULT
-        
-        with EricOverrideCursor(), open(filename, "w", encoding="cp1250",
-                                        errors="backslashreplace") as f:
+
+        with EricOverrideCursor(), open(
+            filename, "w", encoding="cp1250", errors="backslashreplace"
+        ) as f:
             # save file in win ansi using cp1250
             try:
                 # initialise PDF rendering
                 ot = PDFObjectTracker(f)
                 self.pr.oT = ot
                 self.pr.startPDF()
-                
+
                 # do here all the writing
                 lengthDoc = self.editor.length()
-                
+
                 if lengthDoc == 0:
                     self.pr.nextLine()  # enable zero length docs
                 else:
@@ -576,21 +582,18 @@
                     utf8 = self.editor.isUtf8()
                     utf8Ch = b""
                     utf8Len = 0
-                    
+
                     while pos < lengthDoc:
                         ch = self.editor.byteAt(pos)
                         style = self.editor.styleAt(pos)
-                        
-                        if ch == b'\t':
+
+                        if ch == b"\t":
                             # expand tabs
                             ts = tabSize - (column % tabSize)
                             column += ts
-                            self.pr.add(' ' * ts, style)
-                        elif ch in (b'\r', b'\n'):
-                            if (
-                                ch == b'\r' and
-                                self.editor.byteAt(pos + 1) == b'\n'
-                            ):
+                            self.pr.add(" " * ts, style)
+                        elif ch in (b"\r", b"\n"):
+                            if ch == b"\r" and self.editor.byteAt(pos + 1) == b"\n":
                                 pos += 1
                             # close and begin a newline...
                             self.pr.nextLine()
@@ -609,7 +612,7 @@
                                     column -= 1
                                     # will be incremented again later
                                 elif len(utf8Ch) == utf8Len:
-                                    ch = utf8Ch.decode('utf8')
+                                    ch = utf8Ch.decode("utf8")
                                     self.pr.add(ch, style)
                                     utf8Ch = b""
                                     utf8Len = 0
@@ -619,9 +622,9 @@
                             else:
                                 self.pr.add(ch.decode(), style)
                             column += 1
-                        
+
                         pos += 1
-                
+
                 # write required stuff and close the PDF file
                 self.pr.endPDF()
             except OSError as err:
@@ -630,5 +633,6 @@
                     self.tr("Export source"),
                     self.tr(
                         """<p>The source could not be exported to"""
-                        """ <b>{0}</b>.</p><p>Reason: {1}</p>""")
-                    .format(filename, str(err)))
+                        """ <b>{0}</b>.</p><p>Reason: {1}</p>"""
+                    ).format(filename, str(err)),
+                )

eric ide

mercurial