1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Copyright (C) 2017, James R. Barlow (https://github.com/jbarlow83/)
"""Provide classes to stand in for PDF objects
The purpose of these is to provide nice-looking classes to allow explicit
construction of PDF objects and more pythonic idioms and facilitate discovery
by documentation generators.
It's also a place to narrow the scope of input types to those more easily
converted to C++.
In reality all of these return objects of class pikepdf.Object or rather
QPDFObjectHandle which is a generic type.
"""
from . import _qpdf
from ._qpdf import Object, ObjectType
# pylint: disable=unused-import
from ._qpdf import Operator
class _ObjectMeta(type):
"""Supports instance checking"""
def __instancecheck__(cls, instance):
if type(instance) != Object:
return False
return cls.object_type == instance._type_code
class _NameObjectMeta(_ObjectMeta):
"""Supports usage pikepdf.Name.Whatever -> Name('/Whatever')"""
def __getattr__(self, attr):
return Name('/' + attr)
def __setattr__(self, name, value):
raise TypeError("Attributes may not be set on pikepdf.Name")
def __getitem__(self, item):
if item.startswith('/'):
item = item[1:]
raise TypeError(
(
"pikepdf.Name is not subscriptable. You probably meant:\n"
" pikepdf.Name.{}\n"
"or\n"
" pikepdf.Name('/{}')\n"
).format(item, item)
)
class Name(metaclass=_NameObjectMeta):
"""Constructs a PDF Name object
Names can be constructed with two notations:
1. ``Name.Resources``
2. ``Name('/Resources')``
The two are semantically equivalent. The former is preferred for names
that are normally expected to be in a PDF. The latter is preferred for
dynamic names and attributes.
"""
object_type = ObjectType.name
def __new__(cls, name):
# QPDF_Name::unparse ensures that names are always saved in a UTF-8
# compatible way, so we only need to guard the input.
if isinstance(name, bytes):
raise TypeError("Name should be str")
return _qpdf._new_name(name)
class String(metaclass=_ObjectMeta):
"""Constructs a PDF String object"""
object_type = ObjectType.string
def __new__(cls, s):
"""
Args:
s (str or bytes): The string to use. String will be encoded for
PDF, bytes will be constructed without encoding.
Returns:
pikepdf.Object
"""
if isinstance(s, bytes):
return _qpdf._new_string(s)
return _qpdf._new_string_utf8(s)
class Array(metaclass=_ObjectMeta):
"""Constructs a PDF Array object"""
object_type = ObjectType.array
def __new__(cls, a=None):
"""
Args:
a (iterable): A list of objects. All objects must be either
`pikepdf.Object` or convertible to `pikepdf.Object`.
Returns:
pikepdf.Object
"""
if isinstance(a, (str, bytes)):
raise TypeError('Strings cannot be converted to arrays of chars')
if a is None:
a = []
return _qpdf._new_array(a)
class Dictionary(metaclass=_ObjectMeta):
"""Constructs a PDF Dictionary object"""
object_type = ObjectType.dictionary
def __new__(cls, d=None, **kwargs):
"""
Constructs a PDF Dictionary from either a Python ``dict`` or keyword
arguments.
These two examples are equivalent:
.. code-block:: python
pikepdf.Dictionary({'/NameOne': 1, '/NameTwo': 'Two'})
pikepdf.Dictionary(NameOne=1, NameTwo='Two')
In either case, the keys must be strings, and the strings
correspond to the desired Names in the PDF Dictionary. The values
must all be convertible to `pikepdf.Object`.
Returns:
pikepdf.Object
"""
if kwargs and d is not None:
raise ValueError('Unsupported parameters')
if kwargs:
# Add leading slash
# Allows Dictionary(MediaBox=(0,0,1,1), Type=Name('/Page')...
return _qpdf._new_dictionary(
{('/' + k) : v for k, v in kwargs.items()})
if not d:
d = {}
return _qpdf._new_dictionary(d)
class Stream(metaclass=_ObjectMeta):
"""Constructs a PDF Stream object"""
object_type = ObjectType.stream
def __new__(cls, owner, obj):
"""
Args:
owner (pikepdf.Pdf): The Pdf to which this stream shall be attached.
obj (bytes or list): If ``bytes``, the data bytes for the stream.
If ``list``, a list of ``(operands, operator)`` tuples such
as returned by :func:`pikepdf.parse_content_stream`.
Returns:
pikepdf.Object
"""
return _qpdf._new_stream(owner, obj)
|