pypdf 1.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyPdf-1.4/CHANGELOG +64 -0
- pyPdf-1.4/PKG-INFO +29 -0
- pyPdf-1.4/README +38 -0
- pyPdf-1.4/pyPdf/__init__.py +2 -0
- pyPdf-1.4/pyPdf/filters.py +178 -0
- pyPdf-1.4/pyPdf/generic.py +426 -0
- pyPdf-1.4/pyPdf/pdf.py +683 -0
- pyPdf-1.4/pyPdf/utils.py +50 -0
- pyPdf-1.4/setup.py +38 -0
pyPdf-1.4/CHANGELOG
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
Version 1.4, 2006-01-27
|
|
2
|
+
-----------------------
|
|
3
|
+
|
|
4
|
+
- Add capability to merge pages from multiple PDF files into a single page
|
|
5
|
+
using the PageObject.mergePage function. See example code (README or web
|
|
6
|
+
site) for more information.
|
|
7
|
+
|
|
8
|
+
- Add ability to modify a page's MediaBox, CropBox, BleedBox, TrimBox, and
|
|
9
|
+
ArtBox properties through PageObject. See example code (README or web site)
|
|
10
|
+
for more information.
|
|
11
|
+
|
|
12
|
+
- Refactor pdf.py into multiple files: generic.py (contains objects like
|
|
13
|
+
NameObject, DictionaryObject), filters.py (contains filter code),
|
|
14
|
+
utils.py (various). This does not affect importing PdfFileReader
|
|
15
|
+
or PdfFileWriter.
|
|
16
|
+
|
|
17
|
+
- Add new decoding functions for standard PDF filters ASCIIHexDecode and
|
|
18
|
+
ASCII85Decode.
|
|
19
|
+
|
|
20
|
+
- Change url and download_url to refer to new pybrary.net web site.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
Version 1.3, 2006-01-23
|
|
24
|
+
-----------------------
|
|
25
|
+
|
|
26
|
+
- Fix new bug introduced in 1.2 where PDF files with \r line endings did not
|
|
27
|
+
work properly anymore. A new test suite developed with various PDF files
|
|
28
|
+
should prevent regression bugs from now on.
|
|
29
|
+
|
|
30
|
+
- Fix a bug where inheriting attributes from page nodes did not work.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
Version 1.2, 2006-01-23
|
|
34
|
+
-----------------------
|
|
35
|
+
|
|
36
|
+
- Improved support for files with CRLF-based line endings, fixing a common
|
|
37
|
+
reported problem stating "assertion error: assert line == "%%EOF"".
|
|
38
|
+
|
|
39
|
+
- Software author/maintainer is now officially a proud married person, which
|
|
40
|
+
is sure to result in better software... somehow.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
Version 1.1, 2006-01-18
|
|
44
|
+
-----------------------
|
|
45
|
+
|
|
46
|
+
- Add capability to rotate pages.
|
|
47
|
+
|
|
48
|
+
- Improved PDF reading support to properly manage inherited attributes from
|
|
49
|
+
/Type=/Pages nodes. This means that page groups that are rotated or have
|
|
50
|
+
different media boxes or whatever will now work properly.
|
|
51
|
+
|
|
52
|
+
- Added PDF 1.5 support. Namely cross-reference streams and object streams.
|
|
53
|
+
This release can mangle Adobe's PDFReference16.pdf successfully.
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
Version 1.0, 2006-01-17
|
|
57
|
+
-----------------------
|
|
58
|
+
|
|
59
|
+
- First distutils-capable true public release. Supports a wide variety of PDF
|
|
60
|
+
files that I found sitting around on my system.
|
|
61
|
+
|
|
62
|
+
- Does not support some PDF 1.5 features, such as object streams,
|
|
63
|
+
cross-reference streams.
|
|
64
|
+
|
pyPdf-1.4/PKG-INFO
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Metadata-Version: 1.0
|
|
2
|
+
Name: pyPdf
|
|
3
|
+
Version: 1.4
|
|
4
|
+
Summary: PDF toolkit
|
|
5
|
+
Home-page: http://pybrary.net/pyPdf/
|
|
6
|
+
Author: Mathieu Fenniak
|
|
7
|
+
Author-email: mfenniak@pobox.com
|
|
8
|
+
License: UNKNOWN
|
|
9
|
+
Download-URL: http://pybrary.net/pyPdf/pyPdf-1.4.tar.gz
|
|
10
|
+
Description:
|
|
11
|
+
A Pure-Python library built as a PDF toolkit. It is capable of:
|
|
12
|
+
|
|
13
|
+
- splitting documents page by page,
|
|
14
|
+
- merging documents page by page,
|
|
15
|
+
- cropping pages,
|
|
16
|
+
- merging multiple pages into a single page.
|
|
17
|
+
|
|
18
|
+
By being Pure-Python, it should run on any Python platform without any
|
|
19
|
+
dependencies on external libraries. It can also work entirely on StringIO
|
|
20
|
+
objects rather than file streams, allowing for PDF manipulation in memory.
|
|
21
|
+
It is therefore a useful tool for websites that manage or manipulate PDFs.
|
|
22
|
+
|
|
23
|
+
Platform: UNKNOWN
|
|
24
|
+
Classifier: Development Status :: 4 - Beta
|
|
25
|
+
Classifier: Intended Audience :: Developers
|
|
26
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
27
|
+
Classifier: Programming Language :: Python
|
|
28
|
+
Classifier: Operating System :: OS Independent
|
|
29
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
pyPdf-1.4/README
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Example:
|
|
2
|
+
|
|
3
|
+
from pyPdf import PdfFileWriter, PdfFileReader
|
|
4
|
+
|
|
5
|
+
output = PdfFileWriter()
|
|
6
|
+
input1 = PdfFileReader(file("document1.pdf", "rb"))
|
|
7
|
+
|
|
8
|
+
# add page 1 from input1 to output document, unchanged
|
|
9
|
+
output.addPage(input1.getPage(0))
|
|
10
|
+
|
|
11
|
+
# add page 2 from input1, but rotated clockwise 90 degrees
|
|
12
|
+
output.addPage(input1.getPage(1).rotateClockwise(90))
|
|
13
|
+
|
|
14
|
+
# add page 3 from input1, rotated the other way:
|
|
15
|
+
output.addPage(input1.getPage(2).rotateCounterClockwise(90))
|
|
16
|
+
# alt: output.addPage(input1.getPage(2).rotateClockwise(270))
|
|
17
|
+
|
|
18
|
+
# add page 4 from input1, but first add a watermark from another pdf:
|
|
19
|
+
page4 = input1.getPage(3)
|
|
20
|
+
watermark = PdfFileReader(file("watermark.pdf", "rb"))
|
|
21
|
+
page4.mergePage(watermark.getPage(0))
|
|
22
|
+
|
|
23
|
+
# add page 5 from input1, but crop it to half size:
|
|
24
|
+
page5 = input1.getPage(4)
|
|
25
|
+
page5.mediaBox.upperRight = (
|
|
26
|
+
page5.mediaBox.getUpperRight_x() / 2,
|
|
27
|
+
page5.mediaBox.getUpperRight_y() / 2
|
|
28
|
+
)
|
|
29
|
+
output.addPage(page5)
|
|
30
|
+
|
|
31
|
+
# print how many pages input1 has:
|
|
32
|
+
print "document1.pdf has %s pages." % input1.getNumPages())
|
|
33
|
+
|
|
34
|
+
# finally, write "output" to document-output.pdf
|
|
35
|
+
outputStream = file("document-output.pdf", "wb")
|
|
36
|
+
output.write(outputStream)
|
|
37
|
+
|
|
38
|
+
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# vim: sw=4:expandtab:foldmethod=marker
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2006, Mathieu Fenniak
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
|
7
|
+
# modification, are permitted provided that the following conditions are
|
|
8
|
+
# met:
|
|
9
|
+
#
|
|
10
|
+
# * Redistributions of source code must retain the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer.
|
|
12
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
# this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
# and/or other materials provided with the distribution.
|
|
15
|
+
# * The name of the author may not be used to endorse or promote products
|
|
16
|
+
# derived from this software without specific prior written permission.
|
|
17
|
+
#
|
|
18
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
19
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
20
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
21
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
22
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
23
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
24
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
25
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
26
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
27
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
28
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
Implementation of stream filters for PDF.
|
|
33
|
+
"""
|
|
34
|
+
__author__ = "Mathieu Fenniak"
|
|
35
|
+
__author_email__ = "mfenniak@pobox.com"
|
|
36
|
+
|
|
37
|
+
import zlib
|
|
38
|
+
from generic import NameObject
|
|
39
|
+
|
|
40
|
+
class FlateDecode(object):
|
|
41
|
+
def decode(data, decodeParms):
|
|
42
|
+
data = zlib.decompress(data)
|
|
43
|
+
predictor = 1
|
|
44
|
+
if decodeParms:
|
|
45
|
+
predictor = decodeParms.get("/Predictor", 1)
|
|
46
|
+
# predictor 1 == no predictor
|
|
47
|
+
if predictor != 1:
|
|
48
|
+
columns = decodeParms["/Columns"]
|
|
49
|
+
if predictor >= 10:
|
|
50
|
+
newdata = ""
|
|
51
|
+
# PNG prediction can vary from row to row
|
|
52
|
+
rowlength = columns + 1
|
|
53
|
+
assert len(data) % rowlength == 0
|
|
54
|
+
prev_rowdata = "\x00"*rowlength
|
|
55
|
+
for row in range(len(data) / rowlength):
|
|
56
|
+
rowdata = list(data[(row*rowlength):((row+1)*rowlength)])
|
|
57
|
+
filterByte = ord(rowdata[0])
|
|
58
|
+
if filterByte == 0:
|
|
59
|
+
pass
|
|
60
|
+
elif filterByte == 1:
|
|
61
|
+
for i in range(2, rowlength):
|
|
62
|
+
rowdata[i] = chr((ord(rowdata[i]) + ord(rowdata[i-1])) % 256)
|
|
63
|
+
elif filterByte == 2:
|
|
64
|
+
for i in range(1, rowlength):
|
|
65
|
+
rowdata[i] = chr((ord(rowdata[i]) + ord(prev_rowdata[i])) % 256)
|
|
66
|
+
else:
|
|
67
|
+
# unsupported PNG filter
|
|
68
|
+
assert False
|
|
69
|
+
prev_rowdata = rowdata
|
|
70
|
+
newdata += ''.join(rowdata[1:])
|
|
71
|
+
data = newdata
|
|
72
|
+
else:
|
|
73
|
+
# unsupported predictor
|
|
74
|
+
assert False
|
|
75
|
+
return data
|
|
76
|
+
decode = staticmethod(decode)
|
|
77
|
+
|
|
78
|
+
class ASCIIHexDecode(object):
|
|
79
|
+
def decode(data, decodeParms=None):
|
|
80
|
+
retval = ""
|
|
81
|
+
char = ""
|
|
82
|
+
x = 0
|
|
83
|
+
while True:
|
|
84
|
+
c = data[x]
|
|
85
|
+
if c == ">":
|
|
86
|
+
break
|
|
87
|
+
elif c.isspace():
|
|
88
|
+
x += 1
|
|
89
|
+
continue
|
|
90
|
+
char += c
|
|
91
|
+
if len(char) == 2:
|
|
92
|
+
retval += chr(int(char, base=16))
|
|
93
|
+
char = ""
|
|
94
|
+
x += 1
|
|
95
|
+
assert char == ""
|
|
96
|
+
return retval
|
|
97
|
+
decode = staticmethod(decode)
|
|
98
|
+
|
|
99
|
+
class ASCII85Decode(object):
|
|
100
|
+
def decode(data, decodeParms=None):
|
|
101
|
+
retval = ""
|
|
102
|
+
group = []
|
|
103
|
+
x = 0
|
|
104
|
+
hitEod = False
|
|
105
|
+
while not hitEod:
|
|
106
|
+
c = data[x]
|
|
107
|
+
if len(retval) == 0 and c == "<" and data[x+1] == "~":
|
|
108
|
+
x += 2
|
|
109
|
+
continue
|
|
110
|
+
elif c.isspace():
|
|
111
|
+
x += 1
|
|
112
|
+
continue
|
|
113
|
+
elif c == 'z':
|
|
114
|
+
assert len(group) == 0
|
|
115
|
+
retval += '\x00\x00\x00\x00'
|
|
116
|
+
continue
|
|
117
|
+
elif c == "~" and data[x+1] == ">":
|
|
118
|
+
if len(group) != 0:
|
|
119
|
+
# cannot have a final group of just 1 char
|
|
120
|
+
assert len(group) > 1
|
|
121
|
+
cnt = len(group) - 1
|
|
122
|
+
group += [ 85, 85, 85 ]
|
|
123
|
+
hitEod = cnt
|
|
124
|
+
else:
|
|
125
|
+
break
|
|
126
|
+
else:
|
|
127
|
+
c = ord(c) - 33
|
|
128
|
+
assert c >= 0 and c < 85
|
|
129
|
+
group += [ c ]
|
|
130
|
+
if len(group) >= 5:
|
|
131
|
+
b = group[0] * (85**4) + \
|
|
132
|
+
group[1] * (85**3) + \
|
|
133
|
+
group[2] * (85**2) + \
|
|
134
|
+
group[3] * 85 + \
|
|
135
|
+
group[4]
|
|
136
|
+
assert b < (2**32 - 1)
|
|
137
|
+
c4 = chr((b >> 0) % 256)
|
|
138
|
+
c3 = chr((b >> 8) % 256)
|
|
139
|
+
c2 = chr((b >> 16) % 256)
|
|
140
|
+
c1 = chr(b >> 24)
|
|
141
|
+
retval += (c1 + c2 + c3 + c4)
|
|
142
|
+
if hitEod:
|
|
143
|
+
retval = retval[:-4+hitEod]
|
|
144
|
+
group = []
|
|
145
|
+
x += 1
|
|
146
|
+
return retval
|
|
147
|
+
decode = staticmethod(decode)
|
|
148
|
+
|
|
149
|
+
def decodeStreamData(stream):
|
|
150
|
+
filters = stream.get("/Filter", ())
|
|
151
|
+
if len(filters) and not isinstance(filters[0], NameObject):
|
|
152
|
+
# we have a single filter instance
|
|
153
|
+
filters = (filters,)
|
|
154
|
+
data = stream['__streamdata__']
|
|
155
|
+
for filterType in filters:
|
|
156
|
+
if filterType == "/FlateDecode":
|
|
157
|
+
data = FlateDecode.decode(data, stream.get("/DecodeParms"))
|
|
158
|
+
elif filterType == "/ASCIIHexDecode":
|
|
159
|
+
data = ASCIIHexDecode.decode(data)
|
|
160
|
+
elif filterType == "/ASCII85Decode":
|
|
161
|
+
data = ASCII85Decode.decode(data)
|
|
162
|
+
else:
|
|
163
|
+
# unsupported filter
|
|
164
|
+
assert False
|
|
165
|
+
return data
|
|
166
|
+
|
|
167
|
+
if __name__ == "__main__":
|
|
168
|
+
assert "abc" == ASCIIHexDecode.decode('61\n626\n3>')
|
|
169
|
+
|
|
170
|
+
ascii85Test = """
|
|
171
|
+
<~9jqo^BlbD-BleB1DJ+*+F(f,q/0JhKF<GL>Cj@.4Gp$d7F!,L7@<6@)/0JDEF<G%<+EV:2F!,
|
|
172
|
+
O<DJ+*.@<*K0@<6L(Df-\\0Ec5e;DffZ(EZee.Bl.9pF"AGXBPCsi+DGm>@3BB/F*&OCAfu2/AKY
|
|
173
|
+
i(DIb:@FD,*)+C]U=@3BN#EcYf8ATD3s@q?d$AftVqCh[NqF<G:8+EV:.+Cf>-FD5W8ARlolDIa
|
|
174
|
+
l(DId<j@<?3r@:F%a+D58'ATD4$Bl@l3De:,-DJs`8ARoFb/0JMK@qB4^F!,R<AKZ&-DfTqBG%G
|
|
175
|
+
>uD.RTpAKYo'+CT/5+Cei#DII?(E,9)oF*2M7/c~>
|
|
176
|
+
"""
|
|
177
|
+
ascii85_originalText="Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure."
|
|
178
|
+
assert ASCII85Decode.decode(ascii85Test) == ascii85_originalText
|