I would like to rotate PDF pages around the center (other than just multiples of 90°) in a PDF document and optionally scale them to fit into the original page.
Here on StackOverflow, I found a few similar questions, however, mostly about the outdated PyPDF2. And in the latest pypdf documentation, I could not find (or overlooked) a recipe to rotate pages around the center, e.g. for slightly tilted scanned documents, which require rotation of a few degrees.
I know that there is the Transformation Class
, but the standard rotation is around the lower left corner and documentation is not explaining in detail what the matrix elements actually are.
How to rotate a PDF page around the center and optionally scale it that it fits into the original page?
It took me some time to figure out how to rotate a page around the center and scale it to fit the original page. Although, it just requires a matrix with the correct elements at the right place and some trigonometric functions, it was not too obvious for me. Hence, I post the script I ended up with for my own memories and maybe it is helpful to others not having to re-invent the wheel.
After all, you can also achieve it somehow via a combination of rotate()
, translate()
, and scale()
.
If anybody has ideas to simplify or improve the following script, please let me know.
Script:
### rotate around center (and optional scale) pdf pages
from pypdf import PdfReader, PdfWriter, Transformation
from math import sin, cos, atan, sqrt, radians, pi
pdf_input = PdfReader("Test.pdf")
pdf_output = PdfWriter()
rotation_angle = 7.3 # in degrees
shrink_page = True
for page in pdf_input.pages:
x0 = (page.mediabox.right - page.mediabox.left)/2
y0 = (page.mediabox.top - page.mediabox.bottom)/2
a = radians(rotation_angle)
a0 = atan(max(x0,y0)/min(x0,y0))
s0 = min(x0,y0)/cos(a0 - abs((a-pi/2)%pi - pi/2))/sqrt(x0**2 + y0**2) if shrink_page else 1
rotate_center = Transformation((
s0*cos(a), s0*sin(a), -s0*sin(a), s0*cos(a),
s0*(-x0*cos(a)+y0*sin(a))+x0, s0*(-x0*sin(a)-y0*cos(a))+y0))
pdf_output.add_page(page).add_transformation(rotate_center)
pdf_output.write("Test_out.pdf")
### end of script
Alternatively, as @MartinThoma commented, instead of using the transformation matrix (or tuple) use translate()
, rotate()
and again translate()
and optionally scale()
, but in the right order with the proper numbers. This means, replace the line rotate_center = ...
with the following:
rotate_center = Transformation().translate(tx=-x0,ty=-y0).scale(sx=s0,sy=s0).rotate(rotation_angle).translate(tx=x0,ty=y0)
Result: (screenshot from Test_out.pdf
)