pdf-lib
pdf-lib copied to clipboard
Issue with Jpeg's Orientation Exif when embedding in pdf
What were you trying to do?
I am trying to create a pdf file from multiple jpeg files.
How did you attempt to do it?
We allow the user to upload jpeg, png, pdf files in our application. They can then select those files to "join" them, which creates a pdf. This works fine, except when the jpeg has an Orientation exif, it is not taken into account by the pdf-lib, and they see the jpeg as being rotated when they create the pdf.
const newPdf = await PDFDocument.create()
const appendNextCheckedDocument = async (newPdf, documentToAppend, previewURL) => {
const documentBytes = await (await fetch(previewURL)).arrayBuffer()
if (documentToAppend.content_type.match('image/png') || documentToAppend.content_type.match('image/jpeg')) {
const image = await (documentToAppend.content_type.match('image/png')
? newPdf.embedPng(documentBytes)
: newPdf.embedJpg(documentBytes))
const page = newPdf.addPage([image.width, image.height])
page.drawImage(image, { x: 0, y: 0, width: image.width, height: image.height })
}
}
This works well with files that to not have an orientation exif.
What actually happened?
What happened is that the generated pdf contains image that look like they are rotated (they are not rotated, it is just that the Orientation exif is not taken into account, and they look rotated to the user).
You can find a user report here, unfortunately it is in French: https://community.doctolib.com/t5/logiciel-m%C3%A9dical/bug-import-des-photos/m-p/72835
You can see that the user imports pictures taken from his phone on the left side of the screenshot (so, with orientation exif since they took it in portrait mode), but when the pdf is created (right side of the screenshot) the pictures seem rotated.
What did you expect to happen?
I would expect the orientation exif to be taken into account, so that the jpeg is rotated accordingly when appending it to a pdf file. Or, if I had an option that I could pass as a parameter, I could extract the exif orientation metadata and pass it to embedJpg
.
How can we reproduce the issue?
You can use embedJpg
with a file that has an orientation exif metadata. To get that you can take a picture with your phone and orient the screen while doing it, or you can use some examples here: https://github.com/recurser/exif-orientation-examples
Version
1.17.1
What environment are you running pdf-lib in?
React Native
Checklist
- [X] My report includes a Short, Self Contained, Correct (Compilable) Example.
- [X] I have attached all PDFs, images, and other files needed to run my SSCCE.
Additional Notes
Thank you!
I have the same issue. I've tried to embed an image which was rotated using windows media rotate option. The embed returns wrong values for width/ height, but also the image is displayed as before rotation.
Until a native solution is implemented, you can download the image and use imagemagick's auto-orient before embedding into the PDF.
Until a native solution is implemented, you can download the image and use imagemagick's auto-orient before embedding into the PDF.
Can we ask this for our customers? :)
convert -auto-orient -strip original.jpg transformed.jpg
Then you can use the transformed.jpg
in the script, no need to ask the customer! ;)
convert? this looks like a CLI execution. I'm using the library on the client side
Oh, I see, I thought you were in back-end mode.
Alright, let me try to find something for you in the front-end side.
I already made some investigations, not that easy :) Thanks though
Didn't try but can you check if a package like this would do the job for you? https://github.com/onurzorluer/exif-auto-rotate
hmm, looking nice, I'll give a try
Oh even that one: https://jsfiddle.net/orotemo/obvna6qn/1/
You don't even need a 3rd package, apparently.
Oh even that one: https://jsfiddle.net/orotemo/obvna6qn/1/
You don't even need a 3rd package, apparently.
This one is working only in the browser, it's using css transform on the image, lol
That's a reference for reading the image, you can use it to detect the rotation and then use the canvas API to rotate it.
I'm not giving you the copy/paste code, just the right path! :)
it's not working that easly, I've tried all possible solutions, even with canvas api, still seeking for something else. but anyway, I would still be interested in having a solution from the library itself.
How did you try?
If you can read the image EXIF, get the rotation nad then use the canvas API to render the image, rotate it and convert it back to a base64 string, you should be able to embed it.
found solution. it solved my cases. this thread and discussion helped me to find correct way. thank you guys https://github.com/Hopding/pdf-lib/issues/1329#issuecomment-1285534542
There is another solution that doesn't require adding any additional libraries. You can get just the image's EXIF orientation from the array buffer using this snippet:
https://stackoverflow.com/a/32490603
Although, I would remove the check to skip images that are not JPEG because modern PNGs can also contain EXIF metadata. Then determine the rotation for each of the 8 possible orientations. But before you draw the image and rotate you need to correct for mirrored images using scale
and translate
:
if (orientationCorrection.mirrored === 'x') {
// scale - flips the image horizontally but keeps it in the same quadrant
// translate - moves the coordinate system origin to the bottom right and flips the direction of the x-axis
page.pushOperators(pushGraphicsState(), scale(-1, 1), translate(-pageWidth, 0));
} else if (orientationCorrection.mirrored === 'y') {
// scale - flips the image vertically but keeps it in the same quadrant
// translate - moves the coordinate system origin to the top left and flips the direction of the y-axis
page.pushOperators(pushGraphicsState(), scale(1, -1), translate(0, -pageHeight));
}
...
page.drawImage(...);
orientationCorrection.mirrored && page.pushOperators(popGraphicsState());
After you translate and rotate, your image will be in a new quadrant so you'll have to shift it back to where you want it.