Example #1
0
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)
Example #2
0
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)
Example #3
0
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')
Example #4
0
    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
Example #5
0
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)
Example #6
0
    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)
Example #8
0
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)
Example #9
0
    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
Example #10
0
    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
Example #11
0
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)
Example #12
0
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
Example #13
0
 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
Example #14
0
    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
        })
Example #15
0
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
Example #16
0
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)
Example #17
0
 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)
Example #18
0
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')
Example #19
0
    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
Example #20
0
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
Example #21
0
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
Example #23
0
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())
Example #24
0
    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
Example #25
0
 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
Example #26
0
 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
Example #27
0
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')
Example #28
0
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)
Example #29
0
 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
Example #30
0
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)