[reportlab-users] Acroform text widget alignment

300 views
Skip to first unread message

Chris Else

unread,
Oct 8, 2020, 6:02:31 AM10/8/20
to reportlab-users
Hi,

I'm trying to create a PDF containing an interactive form, where the Textfield widgets have centered text.

I can achieve what I want by modifying the ReportLab generated PDF, but it's not ideal and a bit brutal.

import fitz
import re

pdf = fitz.open("original.pdf")

for w in pdf[0].widgets():
    # Not 100% sure this is the best way to add /Q 1
    contents = re.sub("^  /", "  /Q 1\n  /", pdf.xrefObject(w.xref), 1, re.MULTILINE)
    pdf.updateObject(w.xref, contents)

pdf.need_appearances(True)
pdf.save("fixed.pdf")  

In terms of PDF specification, I'm trying to add "/Q 1" to the widget's /Annot dictionary.

In the ReportLab source code, src/reportlab/pdfbase/pdfform.py there is a definition for TextFieldPattern.

Does anyone have any suggestions on how I may override this?
Or the possibility to add an alignment option to the textfield method?

Thank you.

Chris Else









Robin Becker

unread,
Oct 8, 2020, 8:32:21 AM10/8/20
to reportlab-users
......

it would help to know what you are using in ReportLab toolkit to produce your form.

There are two form systems present in pdfbase; the older one is pdfform.py and the later one is acroform. The code in
pdfform uses a pattern style interpolation to create pdf objects directly. It's quite hard to use and not well
understood or maintained.

The acroform code is more recent and attempts to make form building more object oriented. You access canvas.acroForm and
the base object is created automatically. The various widgets are created using methods on the canvas.acroForm object eg

def textfield(self,
value='',
fillColor=None,
borderColor=None,
textColor=None,
borderWidth=1,
borderStyle='solid',
width=120,
height=36,
x=0,
y=0,
tooltip=None,
name=None,
annotationFlags='print',
fieldFlags='',
forceBorder=False,
relative=False,
maxlen=100,
fontName=None,
fontSize=None,
dashLen=3,
):

is the method called to add a textfield. The above ends up adding an annotation to the canvas. It will have a name
NUMBER<d> where <d> is canvas._annotationCount - 1. The annotation gets added to the canvas._doc object and it should be
visible as canvas._doc.id2Object['NUMBER<d>'].

If you are using the older technology then you will need to hack the definitions of the patterns which is hard.

I looked in vain for the /Q usage and failed. I'm using pdf_reference_1-7.pdf do you have another reference; perhaps you
can point me at it.
--
Robin Becker
_______________________________________________
reportlab-users mailing list
reportl...@lists2.reportlab.com
https://pairlist2.pair.net/mailman/listinfo/reportlab-users

Robin Becker

unread,
Oct 8, 2020, 9:23:30 AM10/8/20
to reportlab-users
I messed up the explanation of how to access the annotations via canvas.acroForm this exampl shows how

if __name__=='__main__':
from reportlab.pdfgen.canvas import Canvas
canv = Canvas('aaa.pdf')
tf = canv.acroForm.textfield()
print(canv._doc.idToObject[canv._annotationrefs[-1].name].dict)

....... for me this produces
python tmp/tf.py
{'FT': '/Tx', 'P': <reportlab.pdfbase.pdfdoc.PDFObjectReference object at 0x7f675bec6f40>, 'V':
<reportlab.pdfbase.pdfdoc.PDFString object at 0x7f675bec6e50>, 'DV': <reportlab.pdfbase.pdfdoc.PDFString object at
0x7f675bec6e50>, 'Rect': <reportlab.pdfbase.pdfdoc.PDFArray object at 0x7f675bed81f0>, 'AP':
<reportlab.pdfbase.pdfdoc.PDFDictionary object at 0x7f675bed8250>, 'Subtype': '/Widget', 'Type': '/Annot', 'F': 4, 'Ff':
0, 'DA': <reportlab.pdfbase.pdfdoc.PDFString object at 0x7f675bed82b0>, 'MaxLen': 100, 'T':
<reportlab.pdfbase.pdfdoc.PDFString object at 0x7f675bed8310>, 'BS': <reportlab.pdfbase.pdfdoc.PDFDictionary object at
0x7f675bed83d0>, 'MK': <reportlab.pdfbase.pdfdoc.PDFDictionary object at 0x7f675bed8490>}

so you should be able to do


canv._doc.idToObject[canv._annotationrefs[-1].name][Q] = 1

Robin Becker

unread,
Oct 9, 2020, 7:16:25 AM10/9/20
to For users of Reportlab open source software
On 09/10/2020 10:20, Chris Else wrote:
> Hi Robin,
>
> Sorry but I'm stuck again. Using your code, I was unable to set the
> NeedAppearances.
>
>>>> if __name__=='__main__':
> ... from reportlab.pdfgen.canvas import Canvas
> ... canv = Canvas('aaa.pdf')
> ... tf = canv.acroForm.textfield()
> ... canv.acroForm['NeedAppearances'] = 'true'
> ... canv.save()
> ...
> Traceback (most recent call last):
> File "<stdin>", line 5, in <module>
> TypeError: 'AcroForm' object does not support item assignment
>
> Any ideas?
>
my stupidity the AcroForm only becomes a pdfdictionary at final assembly time. I patched the acroform file

diff -r 45bd9edde3c8 src/reportlab/pdfbase/acroform.py
--- a/src/reportlab/pdfbase/acroform.py Fri Oct 02 16:37:46 2020 +0100
+++ b/src/reportlab/pdfbase/acroform.py Fri Oct 09 12:08:37 2020 +0100
@@ -158,6 +158,7 @@
self._refMap = {}
self._pdfdocenc = {}
self.sigFlags = None
+ self.extras = {}

@property
def canv(self):
@@ -179,6 +180,7 @@
F = [self.fontRef(f) for f in FK]
d['DA'] = PDFString('/%s 0 Tf 0 g' % FK[0])
d['DR'] = PDFFromString('<< /Encoding\n<<\n/RLAFencoding\n%s\n>>\n%s\n>>' % (self.encRefStr,'\n'.join(F)))
+ d.update(self.extras)
r = PDFDictionary(d).format(doc)
return r

and then you can use

canv.acroForm.extras['NeedAppearances'] = 'true'

and the extras get rolled into the final dictionary.

I'll add this into the next micro release.

> Thank you
>
> Chris Else
..........
Reply all
Reply to author
Forward
0 new messages