boazsegev / combine_pdf

A Pure ruby library to merge PDF files, number pages and maybe more...
MIT License
734 stars 156 forks source link

`fix_rotation` results in upside-down page #136

Closed dmkash closed 6 years ago

dmkash commented 6 years ago

Hi, there! I have a PDF that is in landscape orientation when viewing, but the parsed data has the orientation of :portrait. Calling .fix_rotation results in the right page dimensions and orientation, but the page is upside down. Is there any way for me to tell what the right page orientation is? I can call one of the #rotate_* methods to get the page the right way, but I don't see any way to tell that #fix_rotation will result in the page being upside-down.

Thank you!

boazsegev commented 6 years ago

Hi @dmkash ,

I finished writing a response and than I realized I might not have understood this issue correctly...

Do you mean to write that the PDF displays correctly before calling #fix_rotation and the #fix_rotation flips the PDF the wrong way?

If so, please send me an example PDF and I'll fix the issue.

Otherwise, please read my original response.

Leat me know. I'm waiting to read from you.

Kindly, Bo.

Original Response:

I'm sorry to say that CombinePDF wouldn't be able to "see" the intended orientation. I couldn't even imagine how to start coding a "smart" detection for such a feature...

CombinePDF doesn't implement PDF decoding for the page data streams (we add layers, but we don't decode existing layers), and I assume a "smart" detection algorithm would require this extra layer of data.

As a sidenote, the #orientation method does support a rotation flag (defaults to clockwise) and it automatically calls #fix_rotation.

B.

dmkash commented 6 years ago

Hi!! Thanks for both responses!!

I left out some helpful info (shame on me) and maybe this actually has nothing to do with combine_pdf itself.

We provide our users with a way to annotated PDF documents through our RoR product. At the end, I need to draw a marker on the PDF at the point where they annotated it. I have x/y coordinates for each annotation that I use for this purpose. I use prawn to create a new, transparent document with the same dimensions and orientation as the original PDF, draw the markers on it, then use combine_pdf to combine the two pages together.

For most PDFs this is fine. This particular PDF document, when parsed by combine_pdf, returns dimensions and orientation of :portrait, but a rotation of 90. This means that the page I create with prawn is actually in the wrong orientation and the markers get drawn sideways and in the wrong spot.

If I call #fix_rotation on the page before I use prawn to create my drawing page, then the markers are drawn perfectly. However, when I use combine_pdf to merge the original page and the new page together, the original page is now upside-down.

Any thoughts on what I can do to get the two to match up correctly? I tried using Prawn's #rotate method, passing in the value from the page's :Rotate key, but the coordinate system then seems to be all off.

boazsegev commented 6 years ago

Hi @dmkash ,

Thanks for the details.

You write that:

This particular PDF document, when parsed by combine_pdf, returns dimensions and orientation of :portrait, but a rotation of 90.

  1. I wonder, if the PDF is opened in a normal viewed before #fix_rotation is called, is it upside-down?

  2. What version of CombinePDF are you using? Does the issue persist on version 1.0.8?

  3. It sound like #fix_rotation somehow messed up the rotation. I wish to fix it, but I do need an example PDF to use for testing. Could you attach a PDF (or send me a PDF privately)?

Any thoughts on what I can do to get the two to match up correctly?

The only "hack" I can think of might add load on the server side (I'm not sure you want my idea, because it requires a round trip for the PDF data before the client adds any annotations)...

In essence, you "fix" the PDF before you show it's data on the client, allowing the client app to rotate the page as required. This will offload errors to be felt with by the client's human user at the cost of the data's roundtrip.

If you decide to save the temporary PDF object on the server (i.e., if you're using Websockets), you could show the client one PDF page at a time, to save on bandwidth.

Just a thought.

dmkash commented 6 years ago

I had thought about that, too -- calling #fix_rotation, saving the resulting PDF out, and then using that to draw on.

I think I may have a fix for the issue, though. I detect the rotation and manually adjust my new drawing page during creation. So if the orientation of the original page is :portrait, but the rotation value is 90, then I create my new page in :landscape orientation and use the height for the width and width for the height. This allows me to draw the annotations in the right spots.

Before combining the new page with the original page, I call #rotate_left on it. This rotates the new page to line up with the original page and voila! Everything is again right with the world.

I can totally see a problem, though, where the page is rotated a different way and I actually need to #rotate_right instead. In that case, would the :Rotate value on the original page be 270?

Unfortunately, the document is highly sensitive, and I'm unable to share it :(

Do you know of any way to generate PDF documents with the different rotation values? Thanks again for all of your help!!

dmkash commented 6 years ago

Even better!! I manually set the [:Rotation] value on the new page to match the original page, then call #fix_rotation on it. This gets the new page to line up with the original page, then they merge together wonderfully! Again, thank you so much for all of your help.

boazsegev commented 6 years ago

Hi @dmkash

I manually set the [:Rotation] value on the new page to match the original page, then call #fix_rotation on it. This gets the new page to line up with the original page, then they merge together wonderfully!

I'm super happy that you found a solution :-)

I'm wondering if the solution is more of a workaround? (your final PDF is still upside down, right?)

If so, I need to find a rotated PDF and leave the issue open until I solve it.

I can totally see a problem, though, where the page is rotated a different way and I actually need to #rotate_right instead. In that case, would the :Rotate value on the original page be 270?

I know it's a bit late for the answer to mean anything, but yes, the rotation value is direction specific.

dmkash commented 6 years ago

Hi, @boazsegev! Everything works very well. I don't need to call #fix_rotation on the original page, just on the new one to get it in the same orientation as the original page -- does that make sense? There's nothing upside-down or anything and it all lines up perfectly.

boazsegev commented 6 years ago

@dmkash

Oh, now I understand - that's a great approach! Nice :-)

boazsegev commented 6 years ago

Anyway, I just released a fix :-)

Sorry about the inconvenience. #fix_rotation will now work as expected (not that you need it anymore).