Search code examples
pythonpdfcolorspypdf

Change color of rectangle link contourn created with PyPDF2


I want to change the color of the rectangle contourn created with addLink command of PyPDF2 from black to blue but I can't find documentation about it and I couldn't find any solution yet.

from PyPDF2 import PdfFileWriter
from pathlib import Path


output = PdfFileWriter()
output.addBlankPage(width=72, height=72) #page 1 with index 0
output.addBlankPage(width=72, height=72) #page 2 with index 1
output.addLink(0, 1, [10, 10, 50, 30], border=[0, 0, 1])

with Path("link_example.pdf").open(mode="wb") as output_file:
    output.write(output_file)

The only information I got is the self-explanation of the comments: "border: if provided, an array describing border-drawing properties. See the PDF spec for details. No border will be drawn if this argument is omitted."


Solution

  • After a lot of research, I figured out that it is not possible to change the color of the contourn using the original PyPDF2 code. For this reason I've modified it a little bit in order to allow an additional parameter for color in RGB.

    I've changed the function addLink of file pdf.py that is inside ..PythonXX/Lib/PyPDF2 to include the parameter color

    def addLink(self, pagenum, pagedest, rect, border=None, color=None, fit='/Fit', *args):
        """
        Add an internal link from a rectangular area to the specified page.
    
        :param int pagenum: index of the page on which to place the link.
        :param int pagedest: index of the page to which the link should go.
        :param rect: :class:`RectangleObject<PyPDF2.generic.RectangleObject>` or array of four
            integers specifying the clickable rectangular area
            ``[xLL, yLL, xUR, yUR]``, or string in the form ``"[ xLL yLL xUR yUR ]"``.
        :param border: if provided, an array describing border-drawing
            properties. See the PDF spec for details. No border will be
            drawn if this argument is omitted.
        :param color: if provided, an array describing border-color
            RGB. See the PDF spec for details. Black if this argument is omitted.
        :param str fit: Page fit or 'zoom' option (see below). Additional arguments may need
            to be supplied. Passing ``None`` will be read as a null value for that coordinate.
    
        Valid zoom arguments (see Table 8.2 of the PDF 1.7 reference for details):
             /Fit       No additional arguments
             /XYZ       [left] [top] [zoomFactor]
             /FitH      [top]
             /FitV      [left]
             /FitR      [left] [bottom] [right] [top]
             /FitB      No additional arguments
             /FitBH     [top]
             /FitBV     [left]
        """
    
        pageLink = self.getObject(self._pages)['/Kids'][pagenum]
        pageDest = self.getObject(self._pages)['/Kids'][pagedest]  # TODO: switch for external link
        pageRef = self.getObject(pageLink)
    
        if border is not None:
            borderArr = [NameObject(n) for n in border[:3]]
            if len(border) == 4:
                dashPattern = ArrayObject([NameObject(n) for n in border[3]])
                borderArr.append(dashPattern)
        else:
            borderArr = [NumberObject(0)] * 3
    
        if color is not None:
            colorArr = [NameObject(n) for n in color[:3]]
        else:
            colorArr = [NumberObject(0)] * 3
    
        if isString(rect):
            rect = NameObject(rect)
        elif isinstance(rect, RectangleObject):
            pass
        else:
            rect = RectangleObject(rect)
    
        zoomArgs = []
        for a in args:
            if a is not None:
                zoomArgs.append(NumberObject(a))
            else:
                zoomArgs.append(NullObject())
        dest = Destination(NameObject("/LinkName"), pageDest, NameObject(fit),
                           *zoomArgs)  # TODO: create a better name for the link
        destArray = dest.getDestArray()
    
        lnk = DictionaryObject()
        lnk.update({
            NameObject('/Type'): NameObject('/Annot'),
            NameObject('/Subtype'): NameObject('/Link'),
            NameObject('/P'): pageLink,
            NameObject('/Rect'): rect,
            NameObject('/Border'): ArrayObject(borderArr),
            NameObject('/C'): ArrayObject(colorArr),
            NameObject('/Dest'): destArray
        })
        lnkRef = self._addObject(lnk)
    
        if "/Annots" in pageRef:
            pageRef['/Annots'].append(lnkRef)
        else:
            pageRef[NameObject('/Annots')] = ArrayObject([lnkRef])
    
    _valid_layouts = ['/NoLayout', '/SinglePage', '/OneColumn', '/TwoColumnLeft', '/TwoColumnRight', '/TwoPageLeft',
                      '/TwoPageRight']
    

    And now I can get a blue contourn just including the new parameter in the function:

    from PyPDF2 import PdfFileWriter
    from pathlib import Path
    
    
    output = PdfFileWriter()
    output.addBlankPage(width=72, height=72) #page 1 with index 0
    output.addBlankPage(width=72, height=72) #page 2 with index 1
    output.addLink(0, 1, [10, 10, 50, 30], border=[0, 0, 1], color=[0, 0, 1])
    
    with Path("link_example.pdf").open(mode="wb") as output_file:
        output.write(output_file)