720 lines
31 KiB
Diff
720 lines
31 KiB
Diff
diff -urN '-F^f' pyPdf-1.12/pyPdf.orig/generic.py pyPdf-1.12/pyPdf/generic.py
|
|
--- pyPdf-1.12/pyPdf.orig/generic.py 2008-08-12 03:04:17.000000000 +0200
|
|
+++ pyPdf-1.12/pyPdf/generic.py 2010-10-13 18:29:57.000000000 +0200
|
|
@@ -206,14 +206,18 @@ from utils import readNonWhitespace, RC4
|
|
def __new__(cls, value="0", context=None):
|
|
return decimal.Decimal.__new__(cls, str(value), context)
|
|
def __repr__(self):
|
|
- return str(self)
|
|
+ if self == self.to_integral():
|
|
+ return str(self.quantize(decimal.Decimal(1)))
|
|
+ else:
|
|
+ # XXX: this adds useless extraneous zeros.
|
|
+ return "%.5f" % self
|
|
def writeToStream(self, stream, encryption_key):
|
|
- stream.write(str(self))
|
|
+ stream.write(repr(self))
|
|
|
|
|
|
class NumberObject(int, PdfObject):
|
|
def __init__(self, value):
|
|
- int.__init__(self, value)
|
|
+ int.__init__(value)
|
|
|
|
def writeToStream(self, stream, encryption_key):
|
|
stream.write(repr(self))
|
|
@@ -299,7 +303,7 @@ from utils import readNonWhitespace, RC4
|
|
elif tok == "t":
|
|
tok = "\t"
|
|
elif tok == "b":
|
|
- tok == "\b"
|
|
+ tok = "\b"
|
|
elif tok == "f":
|
|
tok = "\f"
|
|
elif tok == "(":
|
|
@@ -309,7 +313,17 @@ from utils import readNonWhitespace, RC4
|
|
elif tok == "\\":
|
|
tok = "\\"
|
|
elif tok.isdigit():
|
|
- tok += stream.read(2)
|
|
+ # "The number ddd may consist of one, two, or three
|
|
+ # octal digits; high-order overflow shall be ignored.
|
|
+ # Three octal digits shall be used, with leading zeros
|
|
+ # as needed, if the next character of the string is also
|
|
+ # a digit." (PDF reference 7.3.4.2, p 16)
|
|
+ for i in range(2):
|
|
+ ntok = stream.read(1)
|
|
+ if ntok.isdigit():
|
|
+ tok += ntok
|
|
+ else:
|
|
+ break
|
|
tok = chr(int(tok, base=8))
|
|
elif tok in "\n\r":
|
|
# This case is hit when a backslash followed by a line
|
|
@@ -403,7 +417,7 @@ from utils import readNonWhitespace, RC4
|
|
delimiterCharacters = "(", ")", "<", ">", "[", "]", "{", "}", "/", "%"
|
|
|
|
def __init__(self, data):
|
|
- str.__init__(self, data)
|
|
+ str.__init__(data)
|
|
|
|
def writeToStream(self, stream, encryption_key):
|
|
stream.write(self)
|
|
@@ -707,6 +721,12 @@ from utils import readNonWhitespace, RC4
|
|
def setUpperRight(self, value):
|
|
self[2], self[3] = [self.ensureIsNumber(x) for x in value]
|
|
|
|
+ def getWidth(self):
|
|
+ return self.getUpperRight_x() - self.getLowerLeft_x()
|
|
+
|
|
+ def getHeight(self):
|
|
+ return self.getUpperRight_y() - self.getLowerLeft_x()
|
|
+
|
|
lowerLeft = property(getLowerLeft, setLowerLeft, None, None)
|
|
lowerRight = property(getLowerRight, setLowerRight, None, None)
|
|
upperLeft = property(getUpperLeft, setUpperLeft, None, None)
|
|
diff -urN '-F^f' pyPdf-1.12/pyPdf.orig/pdf.py pyPdf-1.12/pyPdf/pdf.py
|
|
--- pyPdf-1.12/pyPdf.orig/pdf.py 2008-09-02 23:19:48.000000000 +0200
|
|
+++ pyPdf-1.12/pyPdf/pdf.py 2010-10-13 18:29:50.000000000 +0200
|
|
@@ -1,3 +1,5 @@
|
|
+# -*- coding: utf-8 -*-
|
|
+#
|
|
# vim: sw=4:expandtab:foldmethod=marker
|
|
#
|
|
# Copyright (c) 2006, Mathieu Fenniak
|
|
@@ -38,7 +40,9 @@
|
|
__author__ = "Mathieu Fenniak"
|
|
__author_email__ = "biziqe@mathieu.fenniak.net"
|
|
|
|
+import math
|
|
import struct
|
|
+from sys import version_info
|
|
try:
|
|
from cStringIO import StringIO
|
|
except ImportError:
|
|
@@ -49,7 +53,14 @@
|
|
import warnings
|
|
from generic import *
|
|
from utils import readNonWhitespace, readUntilWhitespace, ConvertFunctionsToVirtualList
|
|
-from sets import ImmutableSet
|
|
+
|
|
+if version_info < ( 2, 4 ):
|
|
+ from sets import ImmutableSet as frozenset
|
|
+
|
|
+if version_info < ( 2, 5 ):
|
|
+ from md5 import md5
|
|
+else:
|
|
+ from hashlib import md5
|
|
|
|
##
|
|
# This class supports writing PDF files out, given pages produced by another
|
|
@@ -93,6 +104,21 @@ from sets import ImmutableSet
|
|
return self._objects[ido.idnum - 1]
|
|
|
|
##
|
|
+ # Common method for inserting or adding a page to this PDF file.
|
|
+ #
|
|
+ # @param page The page to add to the document. This argument should be
|
|
+ # an instance of {@link #PageObject PageObject}.
|
|
+ # @param action The function which will insert the page in the dictionnary.
|
|
+ # Takes: page list, page to add.
|
|
+ def _addPage(self, page, action):
|
|
+ assert page["/Type"] == "/Page"
|
|
+ page[NameObject("/Parent")] = self._pages
|
|
+ page = self._addObject(page)
|
|
+ pages = self.getObject(self._pages)
|
|
+ action(pages["/Kids"], page)
|
|
+ pages[NameObject("/Count")] = NumberObject(pages["/Count"] + 1)
|
|
+
|
|
+ ##
|
|
# Adds a page to this PDF file. The page is usually acquired from a
|
|
# {@link #PdfFileReader PdfFileReader} instance.
|
|
# <p>
|
|
@@ -101,12 +127,64 @@ from sets import ImmutableSet
|
|
# @param page The page to add to the document. This argument should be
|
|
# an instance of {@link #PageObject PageObject}.
|
|
def addPage(self, page):
|
|
- assert page["/Type"] == "/Page"
|
|
- page[NameObject("/Parent")] = self._pages
|
|
- page = self._addObject(page)
|
|
+ self._addPage(page, list.append)
|
|
+
|
|
+ ##
|
|
+ # Insert a page in this PDF file. The page is usually acquired from a
|
|
+ # {@link #PdfFileReader PdfFileReader} instance.
|
|
+ #
|
|
+ # @param page The page to add to the document. This argument should be
|
|
+ # an instance of {@link #PageObject PageObject}.
|
|
+ # @param index Position at which the page will be inserted.
|
|
+ def insertPage(self, page, index=0):
|
|
+ self._addPage(page, lambda l, p: l.insert(index, p))
|
|
+
|
|
+ ##
|
|
+ # Retrieves a page by number from this PDF file.
|
|
+ # @return Returns a {@link #PageObject PageObject} instance.
|
|
+ def getPage(self, pageNumber):
|
|
pages = self.getObject(self._pages)
|
|
- pages["/Kids"].append(page)
|
|
- pages[NameObject("/Count")] = NumberObject(pages["/Count"] + 1)
|
|
+ # XXX: crude hack
|
|
+ return pages["/Kids"][pageNumber].getObject()
|
|
+
|
|
+ ##
|
|
+ # Return the number of pages.
|
|
+ # @return The number of pages.
|
|
+ def getNumPages(self):
|
|
+ pages = self.getObject(self._pages)
|
|
+ return int(pages[NameObject("/Count")])
|
|
+
|
|
+ ##
|
|
+ # Append a blank page to this PDF file and returns it. If no page size
|
|
+ # is specified, use the size of the last page; throw
|
|
+ # PageSizeNotDefinedError if it doesn't exist.
|
|
+ # @param width The width of the new page expressed in default user
|
|
+ # space units.
|
|
+ # @param height The height of the new page expressed in default user
|
|
+ # space units.
|
|
+ def addBlankPage(self, width=None, height=None):
|
|
+ page = PageObject.createBlankPage(self, width, height)
|
|
+ self.addPage(page)
|
|
+ return page
|
|
+
|
|
+ ##
|
|
+ # Insert a blank page to this PDF file and returns it. If no page size
|
|
+ # is specified, use the size of the page in the given index; throw
|
|
+ # PageSizeNotDefinedError if it doesn't exist.
|
|
+ # @param width The width of the new page expressed in default user
|
|
+ # space units.
|
|
+ # @param height The height of the new page expressed in default user
|
|
+ # space units.
|
|
+ # @param index Position to add the page.
|
|
+ def insertBlankPage(self, width=None, height=None, index=0):
|
|
+ if width is None or height is None and \
|
|
+ (self.getNumPages() - 1) >= index:
|
|
+ oldpage = self.getPage(index)
|
|
+ width = oldpage.mediaBox.getWidth()
|
|
+ height = oldpage.mediaBox.getHeight()
|
|
+ page = PageObject.createBlankPage(self, width, height)
|
|
+ self.insertPage(page, index)
|
|
+ return page
|
|
|
|
##
|
|
# Encrypt this PDF file with the PDF Standard encryption handler.
|
|
@@ -119,7 +197,7 @@ from sets import ImmutableSet
|
|
# encryption. When false, 40bit encryption will be used. By default, this
|
|
# flag is on.
|
|
def encrypt(self, user_pwd, owner_pwd = None, use_128bit = True):
|
|
- import md5, time, random
|
|
+ import time, random
|
|
if owner_pwd == None:
|
|
owner_pwd = user_pwd
|
|
if use_128bit:
|
|
@@ -133,8 +211,8 @@ from sets import ImmutableSet
|
|
# permit everything:
|
|
P = -1
|
|
O = ByteStringObject(_alg33(owner_pwd, user_pwd, rev, keylen))
|
|
- ID_1 = md5.new(repr(time.time())).digest()
|
|
- ID_2 = md5.new(repr(random.random())).digest()
|
|
+ ID_1 = md5(repr(time.time())).digest()
|
|
+ ID_2 = md5(repr(random.random())).digest()
|
|
self._ID = ArrayObject((ByteStringObject(ID_1), ByteStringObject(ID_2)))
|
|
if rev == 2:
|
|
U, key = _alg34(user_pwd, O, P, ID_1)
|
|
@@ -160,9 +238,28 @@ from sets import ImmutableSet
|
|
# @param stream An object to write the file to. The object must support
|
|
# the write method, and the tell method, similar to a file object.
|
|
def write(self, stream):
|
|
- import struct, md5
|
|
+ import struct
|
|
|
|
externalReferenceMap = {}
|
|
+
|
|
+ # PDF objects sometimes have circular references to their /Page objects
|
|
+ # inside their object tree (for example, annotations). Those will be
|
|
+ # indirect references to objects that we've recreated in this PDF. To
|
|
+ # address this problem, PageObject's store their original object
|
|
+ # reference number, and we add it to the external reference map before
|
|
+ # we sweep for indirect references. This forces self-page-referencing
|
|
+ # trees to reference the correct new object location, rather than
|
|
+ # copying in a new copy of the page object.
|
|
+ for objIndex in xrange(len(self._objects)):
|
|
+ obj = self._objects[objIndex]
|
|
+ if isinstance(obj, PageObject) and obj.indirectRef != None:
|
|
+ data = obj.indirectRef
|
|
+ if not externalReferenceMap.has_key(data.pdf):
|
|
+ externalReferenceMap[data.pdf] = {}
|
|
+ if not externalReferenceMap[data.pdf].has_key(data.generation):
|
|
+ externalReferenceMap[data.pdf][data.generation] = {}
|
|
+ externalReferenceMap[data.pdf][data.generation][data.idnum] = IndirectObject(objIndex + 1, 0, self)
|
|
+
|
|
self.stack = []
|
|
self._sweepIndirectReferences(externalReferenceMap, self._root)
|
|
del self.stack
|
|
@@ -181,7 +278,7 @@ from sets import ImmutableSet
|
|
pack2 = struct.pack("<i", 0)[:2]
|
|
key = self._encrypt_key + pack1 + pack2
|
|
assert len(key) == (len(self._encrypt_key) + 5)
|
|
- md5_hash = md5.new(key).digest()
|
|
+ md5_hash = md5(key).digest()
|
|
key = md5_hash[:min(16, len(self._encrypt_key) + 5)]
|
|
obj.writeToStream(stream, key)
|
|
stream.write("\nendobj\n")
|
|
@@ -487,7 +584,7 @@ from sets import ImmutableSet
|
|
pages = property(lambda self: ConvertFunctionsToVirtualList(self.getNumPages, self.getPage),
|
|
None, None)
|
|
|
|
- def _flatten(self, pages=None, inherit=None):
|
|
+ def _flatten(self, pages=None, inherit=None, indirectRef=None):
|
|
inheritablePageAttributes = (
|
|
NameObject("/Resources"), NameObject("/MediaBox"),
|
|
NameObject("/CropBox"), NameObject("/Rotate")
|
|
@@ -504,14 +601,17 @@ from sets import ImmutableSet
|
|
if pages.has_key(attr):
|
|
inherit[attr] = pages[attr]
|
|
for page in pages["/Kids"]:
|
|
- self._flatten(page.getObject(), inherit)
|
|
+ addt = {}
|
|
+ if isinstance(page, IndirectObject):
|
|
+ addt["indirectRef"] = page
|
|
+ self._flatten(page.getObject(), inherit, **addt)
|
|
elif t == "/Page":
|
|
for attr,value in inherit.items():
|
|
# if the page has it's own value, it does not inherit the
|
|
# parent's value:
|
|
if not pages.has_key(attr):
|
|
pages[attr] = value
|
|
- pageObj = PageObject(self)
|
|
+ pageObj = PageObject(self, indirectRef)
|
|
pageObj.update(pages)
|
|
self.flattenedPages.append(pageObj)
|
|
|
|
@@ -554,12 +654,12 @@ from sets import ImmutableSet
|
|
if not hasattr(self, '_decryption_key'):
|
|
raise Exception, "file has not been decrypted"
|
|
# otherwise, decrypt here...
|
|
- import struct, md5
|
|
+ import struct
|
|
pack1 = struct.pack("<i", indirectReference.idnum)[:3]
|
|
pack2 = struct.pack("<i", indirectReference.generation)[:2]
|
|
key = self._decryption_key + pack1 + pack2
|
|
assert len(key) == (len(self._decryption_key) + 5)
|
|
- md5_hash = md5.new(key).digest()
|
|
+ md5_hash = md5(key).digest()
|
|
key = md5_hash[:min(16, len(self._decryption_key) + 5)]
|
|
retval = self._decryptObject(retval, key)
|
|
|
|
@@ -890,11 +990,46 @@ from sets import ImmutableSet
|
|
##
|
|
# This class represents a single page within a PDF file. Typically this object
|
|
# will be created by accessing the {@link #PdfFileReader.getPage getPage}
|
|
-# function of the {@link #PdfFileReader PdfFileReader} class.
|
|
+# function of the {@link #PdfFileReader PdfFileReader} class, but it is
|
|
+# also possible to create an empty page with the createBlankPage static
|
|
+# method.
|
|
+# @param pdf PDF file the page belongs to (optional, defaults to None).
|
|
class PageObject(DictionaryObject):
|
|
- def __init__(self, pdf):
|
|
+ def __init__(self, pdf=None, indirectRef=None):
|
|
DictionaryObject.__init__(self)
|
|
self.pdf = pdf
|
|
+ # Stores the original indirect reference to this object in its source PDF
|
|
+ self.indirectRef = indirectRef
|
|
+
|
|
+ ##
|
|
+ # Returns a new blank page.
|
|
+ # If width or height is None, try to get the page size from the
|
|
+ # last page of pdf. If pdf is None or contains no page, a
|
|
+ # PageSizeNotDefinedError is raised.
|
|
+ # @param pdf PDF file the page belongs to
|
|
+ # @param width The width of the new page expressed in default user
|
|
+ # space units.
|
|
+ # @param height The height of the new page expressed in default user
|
|
+ # space units.
|
|
+ def createBlankPage(pdf=None, width=None, height=None):
|
|
+ page = PageObject(pdf)
|
|
+
|
|
+ # Creates a new page (cf PDF Reference 7.7.3.3)
|
|
+ page.__setitem__(NameObject('/Type'), NameObject('/Page'))
|
|
+ page.__setitem__(NameObject('/Parent'), NullObject())
|
|
+ page.__setitem__(NameObject('/Resources'), DictionaryObject())
|
|
+ if width is None or height is None:
|
|
+ if pdf is not None and pdf.getNumPages() > 0:
|
|
+ lastpage = pdf.getPage(pdf.getNumPages() - 1)
|
|
+ width = lastpage.mediaBox.getWidth()
|
|
+ height = lastpage.mediaBox.getHeight()
|
|
+ else:
|
|
+ raise utils.PageSizeNotDefinedError()
|
|
+ page.__setitem__(NameObject('/MediaBox'),
|
|
+ RectangleObject([0, 0, width, height]))
|
|
+
|
|
+ return page
|
|
+ createBlankPage = staticmethod(createBlankPage)
|
|
|
|
##
|
|
# Rotates a page clockwise by increments of 90 degrees.
|
|
@@ -931,7 +1066,7 @@ from sets import ImmutableSet
|
|
renameRes[key] = newname
|
|
newRes[newname] = page2Res[key]
|
|
elif not newRes.has_key(key):
|
|
- newRes[key] = page2Res[key]
|
|
+ newRes[key] = page2Res.raw_get(key)
|
|
return newRes, renameRes
|
|
_mergeResources = staticmethod(_mergeResources)
|
|
|
|
@@ -957,6 +1092,26 @@ from sets import ImmutableSet
|
|
return stream
|
|
_pushPopGS = staticmethod(_pushPopGS)
|
|
|
|
+ def _addTransformationMatrix(contents, pdf, ctm):
|
|
+ # adds transformation matrix at the beginning of the given
|
|
+ # contents stream.
|
|
+ a, b, c, d, e, f = ctm
|
|
+ contents = ContentStream(contents, pdf)
|
|
+ contents.operations.insert(0, [[FloatObject(a), FloatObject(b),
|
|
+ FloatObject(c), FloatObject(d), FloatObject(e),
|
|
+ FloatObject(f)], " cm"])
|
|
+ return contents
|
|
+ _addTransformationMatrix = staticmethod(_addTransformationMatrix)
|
|
+
|
|
+ ##
|
|
+ # Returns the /Contents object, or None if it doesn't exist.
|
|
+ # /Contents is optionnal, as described in PDF Reference 7.7.3.3
|
|
+ def getContents(self):
|
|
+ if self.has_key("/Contents"):
|
|
+ return self["/Contents"].getObject()
|
|
+ else:
|
|
+ return None
|
|
+
|
|
##
|
|
# Merges the content streams of two pages into one. Resource references
|
|
# (i.e. fonts) are maintained from both pages. The mediabox/cropbox/etc
|
|
@@ -968,7 +1123,23 @@ from sets import ImmutableSet
|
|
# @param page2 An instance of {@link #PageObject PageObject} to be merged
|
|
# into this one.
|
|
def mergePage(self, page2):
|
|
+ self._mergePage(page2)
|
|
|
|
+ ##
|
|
+ # Actually merges the content streams of two pages into one. Resource
|
|
+ # references (i.e. fonts) are maintained from both pages. The
|
|
+ # mediabox/cropbox/etc of this page are not altered. The parameter page's
|
|
+ # content stream will be added to the end of this page's content stream,
|
|
+ # meaning that it will be drawn after, or "on top" of this page.
|
|
+ #
|
|
+ # @param page2 An instance of {@link #PageObject PageObject} to be merged
|
|
+ # into this one.
|
|
+ # @param page2transformation A fuction which applies a transformation to
|
|
+ # the content stream of page2. Takes: page2
|
|
+ # contents stream. Must return: new contents
|
|
+ # stream. If omitted, the content stream will
|
|
+ # not be modified.
|
|
+ def _mergePage(self, page2, page2transformation=None):
|
|
# First we work on merging the resource dictionaries. This allows us
|
|
# to find out what symbols in the content streams we might need to
|
|
# rename.
|
|
@@ -978,7 +1149,7 @@ from sets import ImmutableSet
|
|
originalResources = self["/Resources"].getObject()
|
|
page2Resources = page2["/Resources"].getObject()
|
|
|
|
- for res in "/ExtGState", "/Font", "/XObject", "/ColorSpace", "/Pattern", "/Shading":
|
|
+ for res in "/ExtGState", "/Font", "/XObject", "/ColorSpace", "/Pattern", "/Shading", "/Properties":
|
|
new, newrename = PageObject._mergeResources(originalResources, page2Resources, res)
|
|
if new:
|
|
newResources[NameObject(res)] = new
|
|
@@ -986,25 +1157,199 @@ from sets import ImmutableSet
|
|
|
|
# Combine /ProcSet sets.
|
|
newResources[NameObject("/ProcSet")] = ArrayObject(
|
|
- ImmutableSet(originalResources.get("/ProcSet", ArrayObject()).getObject()).union(
|
|
- ImmutableSet(page2Resources.get("/ProcSet", ArrayObject()).getObject())
|
|
+ frozenset(originalResources.get("/ProcSet", ArrayObject()).getObject()).union(
|
|
+ frozenset(page2Resources.get("/ProcSet", ArrayObject()).getObject())
|
|
)
|
|
)
|
|
|
|
newContentArray = ArrayObject()
|
|
|
|
- originalContent = self["/Contents"].getObject()
|
|
- newContentArray.append(PageObject._pushPopGS(originalContent, self.pdf))
|
|
-
|
|
- page2Content = page2['/Contents'].getObject()
|
|
- page2Content = PageObject._contentStreamRename(page2Content, rename, self.pdf)
|
|
- page2Content = PageObject._pushPopGS(page2Content, self.pdf)
|
|
- newContentArray.append(page2Content)
|
|
+ originalContent = self.getContents()
|
|
+ if originalContent is not None:
|
|
+ newContentArray.append(PageObject._pushPopGS(
|
|
+ originalContent, self.pdf))
|
|
+
|
|
+ page2Content = page2.getContents()
|
|
+ if page2Content is not None:
|
|
+ if page2transformation is not None:
|
|
+ page2Content = page2transformation(page2Content)
|
|
+ page2Content = PageObject._contentStreamRename(
|
|
+ page2Content, rename, self.pdf)
|
|
+ page2Content = PageObject._pushPopGS(page2Content, self.pdf)
|
|
+ newContentArray.append(page2Content)
|
|
|
|
self[NameObject('/Contents')] = ContentStream(newContentArray, self.pdf)
|
|
self[NameObject('/Resources')] = newResources
|
|
|
|
##
|
|
+ # This is similar to mergePage, but a transformation matrix is
|
|
+ # applied to the merged stream.
|
|
+ #
|
|
+ # @param page2 An instance of {@link #PageObject PageObject} to be merged.
|
|
+ # @param ctm A 6 elements tuple containing the operands of the
|
|
+ # transformation matrix
|
|
+ def mergeTransformedPage(self, page2, ctm):
|
|
+ self._mergePage(page2, lambda page2Content:
|
|
+ PageObject._addTransformationMatrix(page2Content, page2.pdf, ctm))
|
|
+
|
|
+ ##
|
|
+ # This is similar to mergePage, but the stream to be merged is scaled
|
|
+ # by appling a transformation matrix.
|
|
+ #
|
|
+ # @param page2 An instance of {@link #PageObject PageObject} to be merged.
|
|
+ # @param factor The scaling factor
|
|
+ def mergeScaledPage(self, page2, factor):
|
|
+ # CTM to scale : [ sx 0 0 sy 0 0 ]
|
|
+ return self.mergeTransformedPage(page2, [factor, 0,
|
|
+ 0, factor,
|
|
+ 0, 0])
|
|
+
|
|
+ ##
|
|
+ # This is similar to mergePage, but the stream to be merged is rotated
|
|
+ # by appling a transformation matrix.
|
|
+ #
|
|
+ # @param page2 An instance of {@link #PageObject PageObject} to be merged.
|
|
+ # @param rotation The angle of the rotation, in degrees
|
|
+ def mergeRotatedPage(self, page2, rotation):
|
|
+ rotation = math.radians(rotation)
|
|
+ return self.mergeTransformedPage(page2,
|
|
+ [math.cos(rotation), math.sin(rotation),
|
|
+ -math.sin(rotation), math.cos(rotation),
|
|
+ 0, 0])
|
|
+
|
|
+ ##
|
|
+ # This is similar to mergePage, but the stream to be merged is translated
|
|
+ # by appling a transformation matrix.
|
|
+ #
|
|
+ # @param page2 An instance of {@link #PageObject PageObject} to be merged.
|
|
+ # @param tx The translation on X axis
|
|
+ # @param tx The translation on Y axis
|
|
+ def mergeTranslatedPage(self, page2, tx, ty):
|
|
+ return self.mergeTransformedPage(page2, [1, 0,
|
|
+ 0, 1,
|
|
+ tx, ty])
|
|
+
|
|
+ ##
|
|
+ # This is similar to mergePage, but the stream to be merged is rotated
|
|
+ # and scaled by appling a transformation matrix.
|
|
+ #
|
|
+ # @param page2 An instance of {@link #PageObject PageObject} to be merged.
|
|
+ # @param rotation The angle of the rotation, in degrees
|
|
+ # @param factor The scaling factor
|
|
+ def mergeRotatedScaledPage(self, page2, rotation, scale):
|
|
+ rotation = math.radians(rotation)
|
|
+ rotating = [[math.cos(rotation), math.sin(rotation),0],
|
|
+ [-math.sin(rotation),math.cos(rotation), 0],
|
|
+ [0, 0, 1]]
|
|
+ scaling = [[scale,0, 0],
|
|
+ [0, scale,0],
|
|
+ [0, 0, 1]]
|
|
+ ctm = utils.matrixMultiply(rotating, scaling)
|
|
+
|
|
+ return self.mergeTransformedPage(page2,
|
|
+ [ctm[0][0], ctm[0][1],
|
|
+ ctm[1][0], ctm[1][1],
|
|
+ ctm[2][0], ctm[2][1]])
|
|
+
|
|
+ ##
|
|
+ # This is similar to mergePage, but the stream to be merged is translated
|
|
+ # and scaled by appling a transformation matrix.
|
|
+ #
|
|
+ # @param page2 An instance of {@link #PageObject PageObject} to be merged.
|
|
+ # @param scale The scaling factor
|
|
+ # @param tx The translation on X axis
|
|
+ # @param tx The translation on Y axis
|
|
+ def mergeScaledTranslatedPage(self, page2, scale, tx, ty):
|
|
+ translation = [[1, 0, 0],
|
|
+ [0, 1, 0],
|
|
+ [tx,ty,1]]
|
|
+ scaling = [[scale,0, 0],
|
|
+ [0, scale,0],
|
|
+ [0, 0, 1]]
|
|
+ ctm = utils.matrixMultiply(scaling, translation)
|
|
+
|
|
+ return self.mergeTransformedPage(page2, [ctm[0][0], ctm[0][1],
|
|
+ ctm[1][0], ctm[1][1],
|
|
+ ctm[2][0], ctm[2][1]])
|
|
+
|
|
+ ##
|
|
+ # This is similar to mergePage, but the stream to be merged is translated,
|
|
+ # rotated and scaled by appling a transformation matrix.
|
|
+ #
|
|
+ # @param page2 An instance of {@link #PageObject PageObject} to be merged.
|
|
+ # @param tx The translation on X axis
|
|
+ # @param ty The translation on Y axis
|
|
+ # @param rotation The angle of the rotation, in degrees
|
|
+ # @param scale The scaling factor
|
|
+ def mergeRotatedScaledTranslatedPage(self, page2, rotation, scale, tx, ty):
|
|
+ translation = [[1, 0, 0],
|
|
+ [0, 1, 0],
|
|
+ [tx,ty,1]]
|
|
+ rotation = math.radians(rotation)
|
|
+ rotating = [[math.cos(rotation), math.sin(rotation),0],
|
|
+ [-math.sin(rotation),math.cos(rotation), 0],
|
|
+ [0, 0, 1]]
|
|
+ scaling = [[scale,0, 0],
|
|
+ [0, scale,0],
|
|
+ [0, 0, 1]]
|
|
+ ctm = utils.matrixMultiply(rotating, scaling)
|
|
+ ctm = utils.matrixMultiply(ctm, translation)
|
|
+
|
|
+ return self.mergeTransformedPage(page2, [ctm[0][0], ctm[0][1],
|
|
+ ctm[1][0], ctm[1][1],
|
|
+ ctm[2][0], ctm[2][1]])
|
|
+
|
|
+ ##
|
|
+ # Applys a transformation matrix the page.
|
|
+ #
|
|
+ # @param ctm A 6 elements tuple containing the operands of the
|
|
+ # transformation matrix
|
|
+ def addTransformation(self, ctm):
|
|
+ originalContent = self.getContents()
|
|
+ if originalContent is not None:
|
|
+ newContent = PageObject._addTransformationMatrix(
|
|
+ originalContent, self.pdf, ctm)
|
|
+ newContent = PageObject._pushPopGS(newContent, self.pdf)
|
|
+ self[NameObject('/Contents')] = newContent
|
|
+
|
|
+ ##
|
|
+ # Scales a page by the given factors by appling a transformation
|
|
+ # matrix to its content and updating the page size.
|
|
+ #
|
|
+ # @param sx The scaling factor on horizontal axis
|
|
+ # @param sy The scaling factor on vertical axis
|
|
+ def scale(self, sx, sy):
|
|
+ self.addTransformation([sx, 0,
|
|
+ 0, sy,
|
|
+ 0, 0])
|
|
+ self.mediaBox = RectangleObject([
|
|
+ float(self.mediaBox.getLowerLeft_x()) * sx,
|
|
+ float(self.mediaBox.getLowerLeft_y()) * sy,
|
|
+ float(self.mediaBox.getUpperRight_x()) * sx,
|
|
+ float(self.mediaBox.getUpperRight_y()) * sy])
|
|
+
|
|
+ ##
|
|
+ # Scales a page by the given factor by appling a transformation
|
|
+ # matrix to its content and updating the page size.
|
|
+ #
|
|
+ # @param factor The scaling factor
|
|
+ def scaleBy(self, factor):
|
|
+ self.scale(factor, factor)
|
|
+
|
|
+ ##
|
|
+ # Scales a page to the specified dimentions by appling a
|
|
+ # transformation matrix to its content and updating the page size.
|
|
+ #
|
|
+ # @param width The new width
|
|
+ # @param height The new heigth
|
|
+ def scaleTo(self, width, height):
|
|
+ sx = width / (self.mediaBox.getUpperRight_x() -
|
|
+ self.mediaBox.getLowerLeft_x ())
|
|
+ sy = height / (self.mediaBox.getUpperRight_y() -
|
|
+ self.mediaBox.getLowerLeft_x ())
|
|
+ self.scale(sx, sy)
|
|
+
|
|
+ ##
|
|
# Compresses the size of this page by joining all content streams and
|
|
# applying a FlateDecode filter.
|
|
# <p>
|
|
@@ -1012,10 +1357,11 @@ from sets import ImmutableSet
|
|
# However, it is possible that this function will perform no action if
|
|
# content stream compression becomes "automatic" for some reason.
|
|
def compressContentStreams(self):
|
|
- content = self["/Contents"].getObject()
|
|
- if not isinstance(content, ContentStream):
|
|
- content = ContentStream(content, self.pdf)
|
|
- self[NameObject("/Contents")] = content.flateEncode()
|
|
+ content = self.getContents()
|
|
+ if content is not None:
|
|
+ if not isinstance(content, ContentStream):
|
|
+ content = ContentStream(content, self.pdf)
|
|
+ self[NameObject("/Contents")] = content.flateEncode()
|
|
|
|
##
|
|
# Locate all text drawing commands, in the order they are provided in the
|
|
@@ -1369,8 +1715,8 @@ from sets import ImmutableSet
|
|
password = (password + _encryption_padding)[:32]
|
|
# 2. Initialize the MD5 hash function and pass the result of step 1 as
|
|
# input to this function.
|
|
- import md5, struct
|
|
- m = md5.new(password)
|
|
+ import struct
|
|
+ m = md5(password)
|
|
# 3. Pass the value of the encryption dictionary's /O entry to the MD5 hash
|
|
# function.
|
|
m.update(owner_entry)
|
|
@@ -1394,7 +1740,7 @@ from sets import ImmutableSet
|
|
# /Length entry.
|
|
if rev >= 3:
|
|
for i in range(50):
|
|
- md5_hash = md5.new(md5_hash[:keylen]).digest()
|
|
+ md5_hash = md5(md5_hash[:keylen]).digest()
|
|
# 9. Set the encryption key to the first n bytes of the output from the
|
|
# final MD5 hash, where n is always 5 for revision 2 but, for revision 3 or
|
|
# greater, depends on the value of the encryption dictionary's /Length
|
|
@@ -1436,14 +1782,13 @@ from sets import ImmutableSet
|
|
password = (password + _encryption_padding)[:32]
|
|
# 2. Initialize the MD5 hash function and pass the result of step 1 as
|
|
# input to this function.
|
|
- import md5
|
|
- m = md5.new(password)
|
|
+ m = md5(password)
|
|
# 3. (Revision 3 or greater) Do the following 50 times: Take the output
|
|
# from the previous MD5 hash and pass it as input into a new MD5 hash.
|
|
md5_hash = m.digest()
|
|
if rev >= 3:
|
|
for i in range(50):
|
|
- md5_hash = md5.new(md5_hash).digest()
|
|
+ md5_hash = md5(md5_hash).digest()
|
|
# 4. Create an RC4 encryption key using the first n bytes of the output
|
|
# from the final MD5 hash, where n is always 5 for revision 2 but, for
|
|
# revision 3 or greater, depends on the value of the encryption
|
|
@@ -1473,8 +1818,7 @@ from sets import ImmutableSet
|
|
key = _alg32(password, rev, keylen, owner_entry, p_entry, id1_entry)
|
|
# 2. Initialize the MD5 hash function and pass the 32-byte padding string
|
|
# shown in step 1 of Algorithm 3.2 as input to this function.
|
|
- import md5
|
|
- m = md5.new()
|
|
+ m = md5()
|
|
m.update(_encryption_padding)
|
|
# 3. Pass the first element of the file's file identifier array (the value
|
|
# of the ID entry in the document's trailer dictionary; see Table 3.13 on
|
|
diff -urN '-F^f' pyPdf-1.12/pyPdf.orig/utils.py pyPdf-1.12/pyPdf/utils.py
|
|
--- pyPdf-1.12/pyPdf.orig/utils.py 2007-10-05 04:37:14.000000000 +0200
|
|
+++ pyPdf-1.12/pyPdf/utils.py 2010-10-13 18:27:58.000000000 +0200
|
|
@@ -99,7 +99,19 @@
|
|
retval += chr(ord(plaintext[x]) ^ t)
|
|
return retval
|
|
|
|
-class PdfReadError(Exception):
|
|
+def matrixMultiply(a, b):
|
|
+ return [[sum([float(i)*float(j)
|
|
+ for i, j in zip(row, col)]
|
|
+ ) for col in zip(*b)]
|
|
+ for row in a]
|
|
+
|
|
+class PyPdfError(Exception):
|
|
+ pass
|
|
+
|
|
+class PdfReadError(PyPdfError):
|
|
+ pass
|
|
+
|
|
+class PageSizeNotDefinedError(PyPdfError):
|
|
pass
|
|
|
|
if __name__ == "__main__":
|