def textvisual(self, no, udct, nsig, page): annotation = udct.get(b'signature', b'').decode('utf8') x1, y1, x2, y2 = udct.get(b'signaturebox', (0, 0, 0, 0)) annotation = FreeText( Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0), Appearance( fill=[0, 0, 0], stroke_width=1, wrap_text=True, font_size=udct.get(b'fontsize', 12), content=annotation, ), ) pdfa = annotation.as_pdf_object(identity(), page=None) pdfar = b'[%d %d %d %d]' % tuple(pdfa.Rect) pdfao = pdfa.AP.N visual, nav = self.makeannotation(pdfao, no+4) obj = [ self.makeobj(no + 3, b''' /Type /Annot /Subtype %s /AP <</N %d 0 R>> /BS <</S /S /Type /Border /W 0>> /C [] /Contents (%s) /DA (0 0 0 rg /%s 12 Tf) /Rect %s /F 704 /P %d 0 R /FT /Sig %s /T(Signature%d) /V %d 0 R ''' % ( b'/Widget' if udct.get(b'sigbutton', False) else b'/FreeText', no + 4, pdfa.Contents.encode('latin1'), pdfa.AP.N.Resources.Font.keys()[0].encode('latin1'), pdfar, page, b'/SM(TabletPOSinline)' if udct.get(b'sigbutton', False) else b'', nsig, nav + 1)), visual ] return b''.join(obj), nav
def test_pdf_object_backslash_escapes(self): x1, y1, x2, y2 = 10, 20, 100, 200 annotation = FreeText( Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0), Appearance( fill=[0.4, 0, 0], stroke_width=1, font_size=5, content='\\A \\\\B C', ), ) obj = annotation.as_pdf_object(identity(), page=None) assert obj.AP.N.stream == ( 'q BT 0.4 0 0 rg /{} 5 Tf ' '1 0 0 1 11 109 Tm (\\\\A \\\\\\\\B C) Tj ET Q' ).format(PDF_ANNOTATOR_FONT)
def test_pdf_object(self): x1, y1, x2, y2 = 10, 20, 100, 200 annotation = FreeText( Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0), Appearance( fill=[0.4, 0, 0], stroke_width=1, font_size=5, content='Hi', ), ) obj = annotation.as_pdf_object(identity(), page=None) assert obj.AP.N.stream == ( 'q BT 0.4 0 0 rg /{} 5 Tf ' '1 0 0 1 11 109 Tm (Hi) Tj ET Q').format(PDF_ANNOTATOR_FONT) assert obj.DA == '0.4 0 0 rg /{} 5 Tf'.format(PDF_ANNOTATOR_FONT) assert obj.Rect == [x1, y1, x2, y2] assert obj.AP.N.BBox == [x1, y1, x2, y2] assert obj.AP.N.Matrix == translate(-x1, -y1)
def test_make_composite_font(self): font = FreeText.make_composite_font_object(HELVETICA_PATH) keys = font.keys() expected_keys = ['/Type', '/Subtype', '/BaseFont', '/Encoding', '/DescendantFonts', '/ToUnicode'] assert all(key in keys for key in expected_keys) assert font['/Type'] == '/Font' assert font['/Subtype'] == '/Type0' assert len(font['/DescendantFonts']) == 1 descendant_font = font['/DescendantFonts'][0] assert descendant_font['/Type'] == '/Font' assert descendant_font['/Subtype'] == '/CIDFontType2' assert descendant_font['/BaseFont'] == font['/BaseFont']
def _add_explicit_text_annotation(self, a): x1, y1, x2, y2 = 110, 310, 200, 350 font_size = 4 content_stream = ContentStream([ Save(), BeginText(), FillColor(0, 0, 0), Font('MyFontyFont', font_size), ]) content_stream.extend( get_text_commands( x1, y1, x2, y2, text=(r'Twas brilling and the slithy toves \n' r'Did gyre and gimbel in the wabe \n' r'All mimsy were the borogroves \n' r'And the mome raths outgrabe \n'), font_size=font_size, wrap_text=True, align=constants.TEXT_ALIGN_LEFT, baseline=constants.TEXT_BASELINE_TOP, line_spacing=1.2, )) content_stream.extend([ EndText(), Restore(), ]) appearance = Appearance( appearance_stream=content_stream, fonts={'MyFontyFont': FreeText.make_font_object()}, ) a.add_annotation( 'square', location=Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0), appearance=appearance, )
def makepdf(self, pdfdata1, udct, zeros): parser = PDFParser(BytesIO(pdfdata1)) document = PDFDocument(parser, fallback=False) prev = document.find_xref(parser) info = document.xrefs[0].trailer['Info'].objid root = document.xrefs[0].trailer['Root'].objid size = 1 # calculate last object id, size is only xref size but not count of object in xref for ref in document.xrefs: if isinstance(ref, PDFXRefStream): no = max(ref.ranges, key=operator.itemgetter(1))[1] else: if len(ref.offsets) == 0: no = 0 else: no = max(ref.offsets.keys()) size = max(size, no) page = document.getobj( document.catalog['Pages'].objid)['Kids'][0].objid nsig, fields = self.getfields(root, document) annots = self.getannots(page, document) infodata = self.getdata(pdfdata1, info, prev, document) rootdata = self.getdata(pdfdata1, root, prev, document, ('AcroForm', )) pagedata = self.getdata(pdfdata1, page, prev, document, ('Annots', )) annotation = udct.get(b'signature', b'').decode('utf8') x1, y1, x2, y2 = udct.get(b'signaturebox', (0, 0, 0, 0)) annotation = FreeText( Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0), Appearance( fill=[0, 0, 0], stroke_width=1, wrap_text=True, font_size=12, content=annotation, ), ) pdfa = annotation.as_pdf_object(identity(), page=None) pdfar = b'[%d %d %d %d]' % tuple(pdfa.Rect) pdfas = pdfa.AP.N.stream.encode('latin1') no = size + 1 objs = [ self.makeobj(page, (b'/Annots[%s%d 0 R]' % (annots, no + 3) + pagedata)), self.makeobj(no + 0, infodata), self.makeobj(no + 1, (b'/AcroForm %d 0 R' % (no + 2)) + rootdata), self.makeobj( no + 2, b'/Fields[%s%d 0 R]/SigFlags %d' % (fields, no + 3, udct[b'sigflags'])), self.makeobj( no + 3, b''' /Type /Annot /Subtype /FreeText /AP <</N %d 0 R>> /BS <</S /S /Type /Border /W 0>> /C [] /Contents (%s) /DA (0 0 0 rg /%s 12 Tf) /Rect %s /F 704 /P %d 0 R /FT /Sig /T(Signature%d) /V %d 0 R ''' % (no + 4, pdfa.Contents.encode('latin1'), pdfa.AP.N.Resources.Font.keys()[0].encode('latin1'), pdfar, page, nsig, no + 5)), self.makeobj( no + 4, b''' /BBox %s /FormType 1 /Length %d /Matrix [1 0 0 1 0 0] /Resources <</Font <<%s <</BaseFont /Helvetica /Encoding /WinAnsiEncoding /Subtype /Type1 /Type /Font>>>> /ProcSet /PDF>> /Subtype /Form /Type /XObject ''' % ( pdfar, len(pdfas), pdfa.AP.N.Resources.Font.keys()[0].encode('latin1'), ), b'stream\n' + pdfas + b'\nendstream\n'), self.makeobj(no + 5, ( b'/ByteRange [0000000000 0000000000 0000000000 0000000000]/ContactInfo(%s)\ /Filter/Adobe.PPKLite/Location(%s)/M(D:%s)/Prop_Build<</App<</Name/>>>>/Reason(%s)/SubFilter/adbe.pkcs7.detached/Type/Sig\ /Contents <' % (udct[b'contact'], udct[b'location'], udct[b'signingdate'], udct[b'reason'])) + zeros + b'>'), ] pdfdata2 = b''.join(objs) xref = b'''\ xref\n\ %(page)d 1\n\ %(p0)010d 00000 n \n\ %(no)d 6\n\ %(n0)010d 00000 n \n\ %(n1)010d 00000 n \n\ %(n2)010d 00000 n \n\ %(n3)010d 00000 n \n\ %(n4)010d 00000 n \n\ %(n5)010d 00000 n \n\ ''' startxref = len(pdfdata1) dct = { b'page': page, b'no': no, b'startxref': startxref + len(pdfdata2), b'prev': prev, b'info': no + 0, b'root': no + 1, b'size': 6, b'p0': startxref + pdfdata2.find(b'\n%d 0 obj\n' % page) + 1, b'n0': startxref + pdfdata2.find(b'\n%d 0 obj\n' % (no + 0)) + 1, b'n1': startxref + pdfdata2.find(b'\n%d 0 obj\n' % (no + 1)) + 1, b'n2': startxref + pdfdata2.find(b'\n%d 0 obj\n' % (no + 2)) + 1, b'n3': startxref + pdfdata2.find(b'\n%d 0 obj\n' % (no + 3)) + 1, b'n4': startxref + pdfdata2.find(b'\n%d 0 obj\n' % (no + 4)) + 1, b'n5': startxref + pdfdata2.find(b'\n%d 0 obj\n' % (no + 5)) + 1, b'h1': hashlib.md5(pdfdata1).hexdigest().upper().encode('ascii'), b'h2': hashlib.md5(pdfdata2).hexdigest().upper().encode('ascii'), } trailer = b'''\ trailer <</ID [<%(h1)s><%(h2)s>]/Info %(info)d 0 R/Prev %(prev)d/Root %(root)d 0 R/Size %(size)d>>\n\ startxref\n\ %(startxref)d\n\ %%%%EOF\n\ ''' xref = xref % dct trailer = trailer % dct pdfdata2 = pdfdata2 + xref + trailer return pdfdata2
def makepdf(self, zeros): root = self.prev.Root size = int(self.prev.Size, 10) while len(self.objects) < size - 1: self.objects.append(None) page0 = self.copydict(root.Pages.Kids[0]) page0ref = PdfIndirect(root.Pages.Kids[0].indirect) obj10 = pdf.PdfDict() obj10ref = self.addObject(obj10) obj11 = pdf.PdfDict() obj11ref = self.addObject(obj11) obj12 = pdf.PdfDict() obj12ref = self.addObject(obj12) obj13 = pdf.PdfDict() obj13ref = self.addObject(obj13) obj14 = pdf.PdfDict() obj14ref = self.addObject(obj14) obj10.update({ pdf.PdfName("Type"): pdf.PdfName("TransformParams"), pdf.PdfName("P"): PdfNumber(2), pdf.PdfName("V"): pdf.PdfName("1.2"), }) obj11.update({ pdf.PdfName("Type"): pdf.PdfName("SigRef"), pdf.PdfName("TransformMethod"): pdf.PdfName("DocMDP"), pdf.PdfName("DigestMethod"): pdf.PdfName("SHA1"), pdf.PdfName("TransformParams"): obj10ref, }) obj12.update({ pdf.PdfName("Type"): pdf.PdfName("Sig"), pdf.PdfName("Filter"): pdf.PdfName("Adobe.PPKLite"), pdf.PdfName("SubFilter"): pdf.PdfName("adbe.pkcs7.detached"), pdf.PdfName("Name"): pdf.PdfString.from_unicode("Example User"), pdf.PdfName("Location"): pdf.PdfString.from_unicode("Los Angeles, CA"), pdf.PdfName("Reason"): pdf.PdfString.from_unicode("Testing"), pdf.PdfName("M"): pdf.PdfString.from_unicode("D:20200317214832+01'00'"), pdf.PdfName("Reference"): pdf.PdfArray([obj11ref]), pdf.PdfName("Contents"): PdfHexBytes(zeros), pdf.PdfName("ByteRange"): pdf.PdfArray( [PdfNumberB(0), PdfNumberB(0), PdfNumberB(0), PdfNumberB(0)]), }) obj13.update({ pdf.PdfName("FT"): pdf.PdfName("Sig"), pdf.PdfName("Type"): pdf.PdfName("Annot"), pdf.PdfName("Subtype"): pdf.PdfName("Widget"), pdf.PdfName("F"): PdfNumber(132), pdf.PdfName("T"): pdf.PdfString.from_unicode("Signature1"), pdf.PdfName("V"): obj12ref, pdf.PdfName("P"): page0ref, pdf.PdfName("Rect"): pdf.PdfArray([ PdfNumberFloat(0.0), PdfNumberFloat(0.0), PdfNumberFloat(0.0), PdfNumberFloat(0.0), ]), }) obj14.update({pdf.PdfName("DocMDP"): obj12ref}) obj15 = pdf.PdfDict() obj15.update({ pdf.PdfName("Fields"): pdf.PdfArray([obj13ref]), pdf.PdfName("SigFlags"): PdfNumber(3), }) obj15ref = self.addObject(obj15) if self.annotbutton: from pdf_annotate.annotations.image import Image from pdf_annotate.annotations.text import FreeText from pdf_annotate.config.appearance import Appearance from pdf_annotate.config.location import Location from pdf_annotate.util.geometry import identity annotation = "User signature text" x1, y1, x2, y2 = (470, 0, 570, 100) annotation = FreeText( Location(x1=x1, y1=y1, x2=x2, y2=y2, page=0), Appearance( fill=[0, 0, 0], stroke_width=1, wrap_text=True, font_size=12, content=annotation, ), ) pdfa = annotation.as_pdf_object(identity(), page=None) objapn = self.extend(pdfa[pdf.PdfName("AP")][pdf.PdfName("N")]) objapnref = self.addObject(objapn) for name in ( "BS", "C", "Contents", "DA", "Rect", # "Subtype", ): key = pdf.PdfName(name) v = pdfa[key] if isinstance(v, str): v = v.replace("/", "//") v = pdf.PdfString.from_unicode(v) elif isinstance(v, list): v = pdf.PdfArray(v) obj13.update({key: v}) objap = pdf.PdfDict() objap.update({pdf.PdfName("N"): objapnref}) obj13.update({ pdf.PdfName("SM"): pdf.PdfString.from_unicode("TabletPOSinline"), pdf.PdfName("AP"): objap, }) self.objects[root.Pages.Kids[0].indirect[0] - 1] = page0 annots = pdf.PdfArray([obj13ref]) # if False and pdf.PdfName("Annots") in page0: if pdf.PdfName("Annots") in page0: page0annots = page0[pdf.PdfName("Annots")] if isinstance(page0annots, PdfIndirect): annots.insert(0, page0annots) elif isinstance(page0annots, pdf.PdfArray): annots = page0annots annots.append(obj13ref) page0.update({pdf.PdfName("Annots"): annots}) croot = self.copydict(root) croot.update({ pdf.PdfName("Perms"): obj14ref, pdf.PdfName("AcroForm"): obj15ref }) self.objects[root.indirect[0] - 1] = croot try: ID = self.prev.ID[0] except: b = hashlib.md5(self.datau).digest() ID = pdf.PdfString.from_bytes(b, bytes_encoding="hex") b = repr(random.random()).encode() b = hashlib.md5(b).digest() self.trailer = pdf.PdfDict( Size=len(self.objects), Root=PdfIndirect(root.indirect), Info=PdfIndirect(self.prev.Info.indirect), Prev=self.startprev, ID=pdf.PdfArray( [ID, pdf.PdfString.from_bytes(b, bytes_encoding="hex")]), ) if self.prev.private.pdfdict.encrypt: self.trailer.Encrypt = PdfIndirect( self.prev.private.pdfdict.encrypt.indirect)