def simple_page(pdf_out, ascii_text, compress=False, extra_stream=False): # based on the minimal pdf file of # https://brendanzagaeski.appspot.com/0004.html from pyhanko.pdf_utils import generic, writer from pyhanko.pdf_utils.generic import pdf_name from pyhanko.pdf_utils.misc import get_courier resources = generic.DictionaryObject({ pdf_name('/Font'): generic.DictionaryObject({pdf_name('/F1'): get_courier()}) }) media_box = generic.ArrayObject(map(generic.NumberObject, (0, 0, 300, 144))) def stream_data(txt, y): return f'BT /F1 18 Tf 0 {y} Td ({txt}) Tj ET'.encode('ascii') stream = generic.StreamObject(stream_data=stream_data(ascii_text, 0)) if compress: stream.compress() if extra_stream: stream2 = generic.StreamObject( stream_data=stream_data(ascii_text, 100)) if compress: stream2.compress() contents = generic.ArrayObject( [pdf_out.add_object(stream), pdf_out.add_object(stream2)]) else: contents = pdf_out.add_object(stream) return writer.PageObject(contents=contents, media_box=media_box, resources=resources)
def test_append_sig_field_acro_update(): # test different configurations of the AcroForm w = PdfFileWriter() w.root['/AcroForm'] = generic.DictionaryObject( {pdf_name('/Fields'): generic.ArrayObject()}) w.insert_page(simple_page(w, 'Hello world')) out = BytesIO() w.write(out) out.seek(0) sp = fields.SigFieldSpec('InvisibleSig') w = IncrementalPdfFileWriter(out) fields.append_signature_field(w, sp) assert len(w.root['/AcroForm']['/Fields']) == 1 w = PdfFileWriter() # Technically, this is not standards-compliant, but our routine # shouldn't care w.root['/AcroForm'] = generic.DictionaryObject() w.insert_page(simple_page(w, 'Hello world')) out = BytesIO() w.write(out) out.seek(0) sp = fields.SigFieldSpec('InvisibleSig') w = IncrementalPdfFileWriter(out) with pytest.raises(ValueError): fields.append_signature_field(w, sp)
def test_write_embedded_string_objstream(): ffile = ttLib.TTFont(NOTO_SERIF_JP) ga = GlyphAccumulator(ffile) cid_hx, _ = ga.feed_string('テスト') assert cid_hx == '0637062a0639' w = IncrementalPdfFileWriter(BytesIO(MINIMAL_XREF)) obj_stream = w.prepare_object_stream() font_ref = ga.embed_subset(w, obj_stream=obj_stream) stream = generic.StreamObject( stream_data=f'BT /FEmb 18 Tf 0 100 Td <{cid_hx}> Tj ET'.encode( 'ascii')) stream_ref = w.add_object(stream) w.add_stream_to_page(0, stream_ref, resources=generic.DictionaryObject({ pdf_name('/Font'): generic.DictionaryObject( {pdf_name('/FEmb'): font_ref}) })) out = BytesIO() w.write(out) out.seek(0) r = PdfFileReader(out) page_obj = r.root['/Pages']['/Kids'][0].get_object() conts = page_obj['/Contents'] assert len(conts) == 2 assert stream_ref.idnum in (c.idnum for c in conts) assert font_ref.idnum in r.xrefs.in_obj_stream out.seek(0) # attempt to grab the font from the object stream font_ref.pdf = r font = font_ref.get_object() assert font['/Type'] == pdf_name('/Font')
def apply(self, dest_page: int, x: int, y: int): """ Apply a stamp to a particular page in the PDF writer attached to this :class:`.TextStamp` instance. :param dest_page: Index of the page to which the stamp is to be applied (starting at `0`). :param x: Horizontal position of the stamp's lower left corner on the page. :param y: Vertical position of the stamp's lower left corner on the page. :return: A reference to the affected page object, together with a ``(width, height)`` tuple describing the dimensions of the stamp. """ stamp_ref = self.register() resource_name = b'/Stamp' + hexlify(uuid.uuid4().bytes) stamp_paint = b'q 1 0 0 1 %g %g cm %s Do Q' % (rd(x), rd(y), resource_name) stamp_wrapper_stream = generic.StreamObject(stream_data=stamp_paint) resources = generic.DictionaryObject({ pdf_name('/XObject'): generic.DictionaryObject( {pdf_name(resource_name.decode('ascii')): stamp_ref}) }) wr = self.writer page_ref = wr.add_stream_to_page(dest_page, wr.add_object(stamp_wrapper_stream), resources) dims = (self.box.width, self.box.height) return page_ref, dims
def test_write_embedded_string(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL)) with open(NOTO_SERIF_JP, 'rb') as ffile: ga = GlyphAccumulator(w, ffile, font_size=10) # shape the string, just to register the glyphs as used ga.shape('テスト') # ... but we're not going to use the result # hardcoded CIDs cid_hx = '0637062a0639' stream = generic.StreamObject( stream_data=f'BT /FEmb 18 Tf 0 100 Td <{cid_hx}> Tj ET'.encode('ascii') ) stream_ref = w.add_object(stream) w.add_stream_to_page( 0, stream_ref, resources=generic.DictionaryObject({ pdf_name('/Font'): generic.DictionaryObject({ pdf_name('/FEmb'): ga.as_resource() }) }) ) out = BytesIO() w.write(out) out.seek(0) r = PdfFileReader(out) page_obj = r.root['/Pages']['/Kids'][0].get_object() conts = page_obj['/Contents'] assert len(conts) == 2 assert stream_ref.idnum in (c.idnum for c in conts)
def __init__(self, stream_xrefs=True, init_page_tree=True): # root object root = generic.DictionaryObject({ pdf_name("/Type"): pdf_name("/Catalog"), }) id1 = generic.ByteStringObject(os.urandom(16)) id2 = generic.ByteStringObject(os.urandom(16)) id_obj = generic.ArrayObject([id1, id2]) # info object info = generic.DictionaryObject({ pdf_name('/Producer'): pdf_string(VENDOR) }) super().__init__(root, info, id_obj, stream_xrefs=stream_xrefs) if init_page_tree: pages = generic.DictionaryObject({ pdf_name("/Type"): pdf_name("/Pages"), pdf_name("/Count"): generic.NumberObject(0), pdf_name("/Kids"): generic.ArrayObject(), }) root[pdf_name('/Pages')] = self.add_object(pages)
def apply(self, old: HistoricalResolver, new: HistoricalResolver)\ -> Iterable[ReferenceUpdate]: # TODO refactor these into less ad-hoc rules dss_path = RawPdfPath('/Root', '/DSS') old_dss, new_dss = yield from misc.map_with_return( compare_key_refs('/DSS', old, old.root, new.root), ReferenceUpdate.curry_ref(paths_checked=dss_path)) if new_dss is None: return if old_dss is None: old_dss = generic.DictionaryObject() nodict_err = "/DSS is not a dictionary" if not isinstance(old_dss, generic.DictionaryObject): raise misc.PdfReadError(nodict_err) # pragma: nocover if not isinstance(new_dss, generic.DictionaryObject): raise SuspiciousModification(nodict_err) dss_der_stream_keys = {'/Certs', '/CRLs', '/OCSPs'} dss_expected_keys = {'/Type', '/VRI'} | dss_der_stream_keys dss_keys = set(new_dss.keys()) if not (dss_keys <= dss_expected_keys): raise SuspiciousModification( f"Unexpected keys in DSS: {dss_keys - dss_expected_keys}.") yield from _validate_dss_substructure(old, new, old_dss, new_dss, dss_der_stream_keys, is_vri=False, path=RawPdfPath('/Root', '/DSS')) # check that the /VRI dictionary still contains all old keys, unchanged. vri_path = RawPdfPath('/Root', '/DSS', '/VRI') old_vri, new_vri = yield from misc.map_with_return( compare_key_refs( '/VRI', old, old_dss, new_dss, ), ReferenceUpdate.curry_ref(paths_checked=vri_path)) nodict_err = "/VRI is not a dictionary" if new_vri is not None: if not isinstance(new_vri, generic.DictionaryObject): raise SuspiciousModification(nodict_err) if old_vri is None: old_vri = generic.DictionaryObject() elif not isinstance(old_vri, generic.DictionaryObject): raise misc.PdfReadError(nodict_err) # pragma: nocover yield from DSSCompareRule._check_vri(old, new, old_vri, new_vri)
def test_code128_render(): writer = IncrementalPdfFileWriter(BytesIO(MINIMAL)) bb = barcodes.BarcodeBox("code128", "this is a test") xobj_ref = writer.add_object(bb.as_form_xobject()) stamp_wrapper_stream = generic.StreamObject( stream_data=b'q 1 0 0 1 50 50 cm /Barcode Do Q') resources = generic.DictionaryObject({ pdf_name('/XObject'): generic.DictionaryObject({pdf_name('/Barcode'): xobj_ref}) }) writer.add_stream_to_page(0, writer.add_object(stamp_wrapper_stream), resources)
def __init__(self, writer: BasePdfFileWriter, certs=None, ocsps=None, crls=None, vri_entries=None, backing_pdf_object=None): self.vri_entries = vri_entries if vri_entries is not None else {} self.certs = certs if certs is not None else {} self.ocsps = ocsps if ocsps is not None else [] self.crls = crls if crls is not None else [] self.writer = writer self.backing_pdf_object = ( backing_pdf_object if backing_pdf_object is not None else generic.DictionaryObject() ) ocsps_seen = {} for ocsp_ref in self.ocsps: ocsp_bytes = ocsp_ref.get_object().data ocsps_seen[ocsp_bytes] = ocsp_ref self._ocsps_seen = ocsps_seen crls_seen = {} for crl_ref in self.crls: crl_bytes = crl_ref.get_object().data crls_seen[crl_bytes] = crl_ref self._crls_seen = crls_seen self._modified = False
def __init__(self, field_name, *, box=None, include_on_page=None, combine_annotation=True, # this sets the "print" and "lock" bits annot_flags=0b10000100): if box is not None: rect = list(map(generic.FloatObject, box)) else: rect = [generic.FloatObject(0)] * 4 super().__init__({ # Signature field properties pdf_name('/FT'): pdf_name('/Sig'), pdf_name('/T'): pdf_string(field_name), }) if combine_annotation: annot_dict = self else: annot_dict = generic.DictionaryObject() # Annotation properties: bare minimum annot_dict['/Type'] = pdf_name('/Annot') annot_dict['/Subtype'] = pdf_name('/Widget') annot_dict['/F'] = generic.NumberObject(annot_flags) annot_dict['/Rect'] = generic.ArrayObject(rect) self.page_ref = include_on_page if include_on_page is not None: annot_dict['/P'] = include_on_page self.annot_dict = annot_dict
def test_double_sig_add_field(file_ix): w = IncrementalPdfFileWriter(BytesIO(DOUBLE_SIG_TESTDATA_FILES[file_ix])) out = signers.sign_pdf( w, signers.PdfSignatureMetadata( field_name='Sig1', certify=True, docmdp_permissions=fields.MDPPerm.FILL_FORMS), signer=FROM_CA, ) # create a new signature field after signing w = IncrementalPdfFileWriter(out) # throw in an /Info update for good measure dt = generic.pdf_date(datetime(2020, 10, 10, tzinfo=pytz.utc)) info = generic.DictionaryObject({pdf_name('/CreationDate'): dt}) w.set_info(info) out = signers.sign_pdf( w, signers.PdfSignatureMetadata(field_name='SigNew'), signer=FROM_CA, ) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' status = val_trusted(s, extd=True) assert status.modification_level == ModificationLevel.FORM_FILLING assert status.docmdp_ok s = r.embedded_signatures[1] assert s.field_name == 'SigNew' val_trusted(s)
def test_no_changes_policy(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD)) out = signers.sign_pdf( w, signers.PdfSignatureMetadata( field_name='Sig1', certify=True, docmdp_permissions=fields.MDPPerm.FILL_FORMS), signer=FROM_CA, ) w = IncrementalPdfFileWriter(out) # do an /Info update dt = generic.pdf_date(datetime(2020, 10, 10, tzinfo=pytz.utc)) info = generic.DictionaryObject({pdf_name('/CreationDate'): dt}) w.set_info(info) w.write_in_place() # check with normal diff policy r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' status = val_trusted(s, extd=True) assert status.modification_level == ModificationLevel.LTA_UPDATES assert status.docmdp_ok # now check with the ultra-strict no-op policy r = PdfFileReader(out) s = r.embedded_signatures[0] status = validate_pdf_signature(s, diff_policy=NO_CHANGES_DIFF_POLICY) assert isinstance(s.diff_result, SuspiciousModification) assert not status.docmdp_ok
def marked_content_property_list(self, txt) -> generic.DictionaryObject: result = generic.DictionaryObject({ pdf_name('/ActualText'): generic.TextStringObject(txt) }) if self.bcp47_lang_code is not None: result['/Lang'] = pdf_string(self.bcp47_lang_code) return result
def __init__(self, contents, media_box, resources=None): resources = resources or generic.DictionaryObject() if isinstance(contents, list): if not all(map(instance_test(generic.IndirectObject), contents)): raise PdfWriteError( 'Contents array must consist of indirect references' ) if not isinstance(contents, generic.ArrayObject): contents = generic.ArrayObject(contents) elif not isinstance(contents, generic.IndirectObject): raise PdfWriteError( 'Contents must be either an indirect reference or an array' ) if len(media_box) != 4: raise ValueError('Media box must consist of 4 coordinates.') super().__init__({ pdf_name('/Type'): pdf_name('/Page'), pdf_name('/MediaBox'): generic.ArrayObject( map(generic.NumberObject, media_box) ), pdf_name('/Resources'): resources, pdf_name('/Contents'): contents })
def test_double_sig_create_deep_field_post_sign(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD)) # create part of the structure already fields._insert_or_get_field_at( w, w.root['/AcroForm']['/Fields'], ('NewSigs', 'NewSig1'), field_obj=generic.DictionaryObject({pdf_name('/FT'): pdf_name('/Sig')})) out = signers.sign_pdf(w, signers.PdfSignatureMetadata( field_name='Sig1', certify=True, docmdp_permissions=fields.MDPPerm.FILL_FORMS), signer=FROM_CA, in_place=True) w = IncrementalPdfFileWriter(out) fqn = 'NewSigs.NewSig2' meta = signers.PdfSignatureMetadata(field_name=fqn) out = signers.sign_pdf(w, meta, signer=FROM_CA) r = PdfFileReader(out) s = r.embedded_signatures[0] assert s.field_name == 'Sig1' status = validate_pdf_signature( s, signer_validation_context=SIMPLE_V_CONTEXT()) # the /Kids array of NewSigs was modified, which we don't allow (right now) assert status.modification_level == ModificationLevel.OTHER
def init_xobject_dictionary(command_stream: bytes, box_width, box_height, resources: Optional[generic.DictionaryObject] = None) -> generic.StreamObject: """ Helper function to initialise form XObject dictionaries. .. note:: For utilities to handle image XObjects, see :mod:`.images`. :param command_stream: The XObject's raw appearance stream. :param box_width: The width of the XObject's bounding box. :param box_height: The height of the XObject's bounding box. :param resources: A resource dictionary to include with the form object. :return: A :class:`~.generic.StreamObject` representation of the form XObject. """ resources = resources or generic.DictionaryObject() return generic.StreamObject({ pdf_name('/BBox'): generic.ArrayObject(list( map(generic.FloatObject, (0.0, box_height, box_width, 0.0)) )), pdf_name('/Resources'): resources, pdf_name('/Type'): pdf_name('/XObject'), pdf_name('/Subtype'): pdf_name('/Form') }, stream_data=command_stream)
def apply(self, dest_page, x, y): page_ref, (w, h) = super().apply(dest_page, x, y) link_rect = (x, y, x + w, y + h) link_annot = generic.DictionaryObject({ pdf_name('/Type'): pdf_name('/Annot'), pdf_name('/Subtype'): pdf_name('/Link'), pdf_name('/Rect'): generic.ArrayObject(list( map(generic.FloatObject, link_rect) )), pdf_name('/A'): generic.DictionaryObject({ pdf_name('/S'): pdf_name('/URI'), pdf_name('/URI'): pdf_string(self.url) }) }) wr = self.writer wr.register_annotation(page_ref, wr.add_object(link_annot)) return page_ref, (w, h)
def test_code128_render(): writer = IncrementalPdfFileWriter(BytesIO(MINIMAL)) bb = barcodes.BarcodeBox("code128", "this is a test") xobj_ref = writer.add_object(bb.as_form_xobject()) stamp_wrapper_stream = generic.StreamObject( stream_data=b'q 1 0 0 1 50 50 cm /Barcode Do Q') resources = generic.DictionaryObject({ pdf_name('/XObject'): generic.DictionaryObject({pdf_name('/Barcode'): xobj_ref}) }) writer.add_stream_to_page(0, writer.add_object(stamp_wrapper_stream), resources) # TODO try to read back the code using some kind of barcode scanning # library, perhaps. compare_output(writer, f'{EXPECTED_OUTPUT_DIR}/code128-test.pdf')
def as_pdf_object(self): """ Render this :class:`.SigCertConstraints` object to a PDF dictionary. :return: A :class:`~.generic.DictionaryObject`. """ result = generic.DictionaryObject({ pdf_name('/Type'): pdf_name('/SVCert'), pdf_name('/Ff'): generic.NumberObject(self.flags.value), }) if self.subjects is not None: result[pdf_name('/Subject')] = generic.ArrayObject( generic.ByteStringObject(cert.dump()) for cert in self.subjects ) if self.subject_dn: # FIXME Adobe Reader seems to ignore this for some reason. # Should try to figure out what I'm doing wrong result[pdf_name('/SubjectDN')] = generic.ArrayObject([ generic.DictionaryObject({ pdf_name('/' + key): pdf_string(value) for key, value in x509_name_keyval_pairs( self.subject_dn, abbreviate_oids=True ) }) ]) if self.issuers is not None: result[pdf_name('/Issuer')] = generic.ArrayObject( generic.ByteStringObject(cert.dump()) for cert in self.issuers ) if self.info_url is not None: result[pdf_name('/URL')] = pdf_string(self.info_url) result[pdf_name('/URLType')] = self.url_type if self.key_usage is not None: result[pdf_name('/KeyUsage')] = generic.ArrayObject( pdf_string(ku.encode_to_sv_string()) for ku in self.key_usage ) return result
def test_trailer_update(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_ONE_FIELD)) dt = generic.pdf_date(datetime.datetime(2020, 10, 10, tzinfo=pytz.utc)) info = generic.DictionaryObject({pdf_name('/CreationDate'): dt}) w.trailer['/Info'] = w.add_object(info) out = BytesIO() w.write(out) r = PdfFileReader(out) assert r.trailer['/Info']['/CreationDate'] == dt
def docmdp_reference_dictionary(md_algorithm, permission_level: MDPPerm): # this is part of the /Reference entry of the signature object. return generic.DictionaryObject({ pdf_name('/Type'): pdf_name('/SigRef'), pdf_name('/TransformMethod'): pdf_name('/DocMDP'), pdf_name('/DigestMethod'): pdf_name('/' + md_algorithm.upper()), pdf_name('/TransformParams'): generic.DictionaryObject({ pdf_name('/Type'): pdf_name('/TransformParams'), pdf_name('/V'): pdf_name('/1.2'), pdf_name('/P'): generic.NumberObject(permission_level.value) }) })
def _check_vri(old, new, old_vri, new_vri): new_vri_hashes = set(new_vri.keys()) for key, old_vri_value in old_vri.items(): try: new_vri_dict = new_vri.raw_get(key) except KeyError: new_vri_dict = None if new_vri_dict != old_vri_value: # indirect or direct doesn't matter, they have to be the same raise SuspiciousModification( f"VRI key {key} was modified or deleted.") # check the newly added entries vri_der_stream_keys = {'/Cert', '/CRL', '/OCSP'} vri_expected_keys = {'/Type', '/TU', '/TS'} | vri_der_stream_keys for key in new_vri_hashes - old_vri.keys(): if not VRI_KEY_PATTERN.match(key): raise SuspiciousModification( f"VRI key {key} is not formatted correctly.") new_vri_dict = new_vri.raw_get(key) if isinstance(new_vri_dict, generic.IndirectObject) \ and old.is_ref_available(new_vri_dict.reference): yield ReferenceUpdate(new_vri_dict.reference) new_vri_dict = new_vri_dict.get_object() assert_not_stream(new_vri_dict) if not isinstance(new_vri_dict, generic.DictionaryObject): raise SuspiciousModification( "VRI entries should be dictionaries") new_vri_value_keys = new_vri_dict.keys() if not (new_vri_value_keys <= vri_expected_keys): raise SuspiciousModification( "Unexpected keys in VRI dictionary: " f"{new_vri_value_keys - vri_expected_keys}.") yield from _validate_dss_substructure(old, new, generic.DictionaryObject(), new_vri_dict, vri_der_stream_keys, is_vri=True, path=RawPdfPath( '/Root', '/DSS', '/VRI', key)) # /TS is also a DER stream try: ts_ref = new_vri_dict.get_value_as_reference('/TS', optional=True) if ts_ref is not None and old.is_ref_available(ts_ref): yield ReferenceUpdate(ts_ref) except misc.IndirectObjectExpected: pass
def test_sv_version(): fields.SigSeedValueSpec.from_pdf_object( generic.DictionaryObject({'/Ff': fields.SigSeedValFlags.V})) fields.SigSeedValueSpec.from_pdf_object( fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.V, sv_dict_version=fields.SeedValueDictVersion. PDF_2_0).as_pdf_object()) with pytest.raises(SigningError): fields.SigSeedValueSpec.from_pdf_object( fields.SigSeedValueSpec(flags=fields.SigSeedValFlags.V, sv_dict_version=4).as_pdf_object())
def _import_object(self, obj: generic.PdfObject, reference_map: dict, obj_stream) -> generic.PdfObject: # TODO check the spec for guidance on fonts. Do font identifiers have # to be globally unique? # TODO deal with container_ref if isinstance(obj, generic.DecryptedObjectProxy): obj = obj.decrypted if isinstance(obj, generic.IndirectObject): try: return reference_map[obj.reference] except KeyError: refd = obj.get_object() # Add a placeholder to reserve the reference value. # This ensures correct behaviour in recursive calls # with self-references. new_ido = self.allocate_placeholder() reference_map[obj.reference] = new_ido imported = self._import_object(refd, reference_map, obj_stream) # if the imported object is a bare reference and/or a stream # object, we can't put it into an object stream. if isinstance(imported, OBJSTREAM_FORBIDDEN): obj_stream = None # fill in the placeholder self.add_object( imported, obj_stream=obj_stream, idnum=new_ido.idnum ) return new_ido elif isinstance(obj, generic.DictionaryObject): raw_dict = { k: self._import_object(v, reference_map, obj_stream) for k, v in obj.items() } if isinstance(obj, generic.StreamObject): # In the vast majority of use cases, I'd expect the content # to be available in encoded form by default. # By initialising the stream object in this way, we avoid # a potentially costly decoding operation. return generic.StreamObject( raw_dict, encoded_data=obj.encoded_data ) else: return generic.DictionaryObject(raw_dict) elif isinstance(obj, generic.ArrayObject): return generic.ArrayObject( self._import_object(v, reference_map, obj_stream) for v in obj ) else: return obj
def as_pdf_object(self) -> generic.DictionaryObject: """ :return: A PDF dictionary representing this VRI entry. """ vri = generic.DictionaryObject({pdf_name('/Type'): pdf_name('/VRI')}) if self.ocsps: vri[pdf_name('/OCSP')] = generic.ArrayObject(self.ocsps) if self.crls: vri[pdf_name('/CRL')] = generic.ArrayObject(self.crls) vri[pdf_name('/Cert')] = generic.ArrayObject(self.certs) return vri
def as_resource(self): font_dict = generic.DictionaryObject({ pdf_name('/Type'): pdf_name('/Font'), pdf_name('/BaseFont'): pdf_name('/' + self.name), pdf_name('/Subtype'): pdf_name('/Type1'), pdf_name('/Encoding'): pdf_name('/WinAnsiEncoding') }) return font_dict
def test_write_embedded_string_objstream(): w = IncrementalPdfFileWriter(BytesIO(MINIMAL_XREF)) obj_stream = w.prepare_object_stream() with open(NOTO_SERIF_JP, 'rb') as ffile: ga = GlyphAccumulator(w, ffile, font_size=10, obj_stream=obj_stream) # shape the string, just to register the glyphs as used ga.shape('テスト') # ... but we're not going to use the result # hardcoded CIDs cid_hx = '0637062a0639' font_ref = ga.as_resource() stream = generic.StreamObject( stream_data=f'BT /FEmb 18 Tf 0 100 Td <{cid_hx}> Tj ET'.encode( 'ascii')) stream_ref = w.add_object(stream) w.add_stream_to_page(0, stream_ref, resources=generic.DictionaryObject({ pdf_name('/Font'): generic.DictionaryObject( {pdf_name('/FEmb'): font_ref}) })) out = BytesIO() w.write(out) out.seek(0) r = PdfFileReader(out) page_obj = r.root['/Pages']['/Kids'][0].get_object() conts = page_obj['/Contents'] assert len(conts) == 2 assert stream_ref.idnum in (c.idnum for c in conts) xref_sections = r.xrefs._xref_sections last = xref_sections[len(xref_sections) - 1] assert font_ref.idnum in last.xref_data.xrefs_in_objstm out.seek(0) # attempt to grab the font from the object stream font_ref.pdf = r font = font_ref.get_object() assert font['/Type'] == pdf_name('/Font')
def set_text_field_in_group(writer, ix, val): tf_parent = writer.root['/AcroForm']['/Fields'][1].get_object() tf = tf_parent['/Kids'][ix].get_object() appearance = RawContent(box=BoxConstraints(height=60, width=130), data=b'''q 0 0 1 rg BT /Ti 12 Tf (%s) Tj ET Q''' % val.encode('ascii')) tf['/V'] = generic.pdf_string(val) tf['/AP'] = generic.DictionaryObject({ generic.pdf_name('/N'): writer.add_object(appearance.as_form_xobject()) }) writer.update_container(tf)
def as_resource(self): # assume that self.font is the name of a PDF standard font # TODO enforce that font_dict = generic.DictionaryObject({ pdf_name('/Type'): pdf_name('/Font'), pdf_name('/BaseFont'): pdf_name('/' + self.name), pdf_name('/Subtype'): pdf_name('/Type1'), pdf_name('/Encoding'): pdf_name('/WinAnsiEncoding') }) return font_dict
def test_pubkey_encryption_dict_errors(): sh = PubKeySecurityHandler.build_from_certs([PUBKEY_TEST_DECRYPTER.cert]) original = sh.as_pdf_object() encrypt = generic.DictionaryObject(original) encrypt['/SubFilter'] = pdf_name('/asdflakdsjf') with pytest.raises(misc.PdfReadError): PubKeySecurityHandler.build(encrypt) encrypt = generic.DictionaryObject(original) encrypt['/Length'] = generic.NumberObject(13) with pytest.raises(misc.PdfError): PubKeySecurityHandler.build(encrypt) encrypt = generic.DictionaryObject(original) del encrypt['/CF']['/DefaultCryptFilter']['/CFM'] with pytest.raises(misc.PdfReadError): PubKeySecurityHandler.build(encrypt) encrypt = generic.DictionaryObject(original) encrypt['/CF']['/DefaultCryptFilter']['/CFM'] = pdf_name('/None') with pytest.raises(misc.PdfReadError): PubKeySecurityHandler.build(encrypt)