0

I need to join multiple pdf files into a single page pdf. I need to stitch together some PDFs that I generate. If I am stitching horizontally, all of the PDFs are the same height but not the same width. When I am stitching vertically, all of the PDFs are the same width but not the same height.

I found lots of answers saying to do this with pdfjam/pdfnup. I tried that, but it seems that is only for when each item to be joined is the same dimension, or else I just can't get it to work right.

I would really prefer to do this in ghostscript, since the rest of my PDF manipulation is already done in ghostscript. I haven't found a solution for that though.

As an example, the items I am stitching together look pretty much like this:

┌---------------------------------------┐
┬-┬----------------┬-┬----------------┬-┬
| |                | |                | |
| |                | |                | |
| |                | |                | |
| |                | |                | |
| |                | |                | |
| |                | |                | |
| |                | |                | |
┴-┴----------------┴-┴----------------┴-┴
└---------------------------------------┘

I am first trying to join the 5 boxes that form the center section horizontally. Then I need to join that with the boxes above and below it.

Brandon
  • 1,735
  • 2
  • 22
  • 37
  • Do you need to do this to a lot of files? It's actually a fairly trivial exercise of automated within Acrobat Pro. It's a good solution for 10s of files but not hundreds. Is that an option for you? – joelgeraci Apr 17 '19 at 18:37
  • It is a process I have to do repeatedly lots of times from the command line. – Brandon Apr 19 '19 at 04:43

1 Answers1

1

My answer here contains a PostScript program which will work only with Ghostscript and only with versions up to 9.26 but does imposition.

That program does a load of stuff you won't want, it assumes you are trying to fit the 2 input pages onto a specific size of media, and scales and rotates the pages to make the best fit. You won't want to do any of that.

Essentially you want to move the current point to the start of each page, draw the page content, then move the current point to the start of the next page, draw the content of that page, and so on.

In broad terms, this:

%%
%% First we open the PDF file
%%
File dup (r) file runpdfbegin pop
process_trailer_attrs

opens the PDF file then this:

  pdfgetpage                                             %% get the page from the PDF file, stack: -save- -save- loop -dict-
  dup /Page exch store                                   %% save a copy of the page dict inside itself
  pdfshowpage_init                                       %% initialise the page
  draw_page_content                                      %% se above, draws the graphical objects, stack -save- -save- loop

draws a page (pdfgetpage takes a number, starting from 0, for the page to draw from the PDF file).

This:

PageXTx PageYTx translate                                %% Move to draw page 2

Moves the current point.

So that's all the operations you need, putting them together is up to you. You will need to work out the size of the total final 'page', and start Ghostscript with that set as the media size (use -dDEVICEWIDTHPOINTS and -dDEVICEHEIGHTPOINTS) then you'll need to work out the position on that page of each of the 'sub pages' (bear in mind that the origin is at bottom left for PDF) move to that location, draw the page, repeat for each page.

Finally you will need to execute:

showpage

so that the final drawn page is flushed, and then ideally:

//runpdfend exec                                             %% End the PDF file

So its possible, but as you can see, non-trivial in the current implementation. Again let me point out that this will NOT work with versions of Ghostscript greater than 9.26.

KenS
  • 30,202
  • 3
  • 34
  • 51
  • This is awesome, thanks! I don’t know how to run postscript programs past the -c switch one liners, but I’m sure some googling will help me with that. This is going to make the job much simpler because all 5 of the narrow strips are just white space strips. Being able to move the cursor and draw the next file this is exactly the type of answer I was looking for. I will give this a shot today, thanks again! – Brandon Apr 04 '19 at 14:55
  • 1
    Obviously you'll want to write your modifed program to file and then run the file just like 2-up.ps does, so soemthing like : gs -sFile= 2-up.ps – KenS Apr 04 '19 at 16:16
  • Ya, sorry about that. I was on my phone and hadn't been to your other link yet. It looks like this is definitely the answer, I'm just trying to make sense of the PostScript now. Thanks again! – Brandon Apr 04 '19 at 19:45
  • I tried to put together a proof of concept and failed miserably. My POC and the output I am getting is here: https://gist.github.com/brandoncc/8c01c4bcf123f0321e9f1b9a6b23e226 If you have a second, would you mind looking and telling me if the problem jumps out at you? If not, I understand. Thanks! – Brandon Apr 04 '19 at 20:23
  • It has become obvious to me that `/draw_page_content` is actually a function definition that I cut out. I wish I any postscript at all, because this looks like exactly what I need, I just have no idea how to put the building blocks together :-/ – Brandon Apr 05 '19 at 00:54
  • I went through a postscript tutorial and can now understand the script. I didn't realize I threw out all of the good important guts! Now I'm just trying to figure out why my `translate` call isn't doing anything. As soon as I get that working, this should function perfectly. – Brandon Apr 05 '19 at 03:16
  • Okay, I finally figured it all out and it is working perfectly. THANK YOU! – Brandon Apr 05 '19 at 05:18
  • 1
    Well done! I'm glad its working for you. I'm afraid your additional comments were during the night for me, so I only just saw them. – KenS Apr 05 '19 at 07:16
  • No problem at all, I really appreciate your answer here! – Brandon Apr 05 '19 at 18:31
  • Hi Ken, have you written or come across a newer implementation by any chance? I'm having trouble running 9.2x on my new Mac (arm64) so it would be nice if I could use a newer version of Ghostscript. I figure it is worth a shot to ask, thanks :-) – Brandon Nov 20 '21 at 20:06
  • No but, assuming yo are asking about Imposition, Ghostscript (from 9.55.0) does imposition natively. If all you want is the program written above, it should work with all versions, as far as I know. The soon-to-be-default new PDF interpreter won't work with either the imposition or the program above, but the new PDF interpreter can be run from PostScript and has functionality which should enable a similar program to be created (more simply if anything). – KenS Nov 21 '21 at 16:06
  • Thanks for the response. If I'm understanding you correctly, imposition stopped working after 9.26, but works again after 9.55? And the program from above should work fine on on 9.55? This is the postscript file I am using, from your original answer: https://pastebin.com/nZcWk6Dr – Brandon Nov 22 '21 at 17:32
  • No.... Imposition as done by the program liked to in my answer 'probably' still works. Imposition is now, however, available (in 9.55.0) natively in GS instead of by an external PostScript program. So even if it doesn't work you can get at least the imposition functionality. If you want to use that program then it may not work, because of security model changes. The new PDF interpreter (-dNEWPDF=true in G 9.55.0 or better) has a much better defined means for scripting the PDF interpreter from PostScript, the program should be possible to make work with the new interpreter. – KenS Nov 22 '21 at 19:09
  • Okay, thanks again for your help. I think I'm stuck on 9.26 for the foreseeable future then, but at least I have more of an understanding of a path forward (starting with figuring out what imposition is and how to use the new pdf interpreter!) – Brandon Nov 22 '21 at 20:42
  • I guess the security model changes must be the reason I keep getting "Last OS error: Permission denied" when I run that program. Makes sense now! – Brandon Nov 24 '21 at 00:55
  • Try -dNOSAFER, it might work at the cost of opening up the security holes. – KenS Nov 24 '21 at 08:51
  • I will try that, thank you. I generate the PDF's myself, so that probably means opening the security hole is not as big of a deal. Unless of course, the security hole could be exploited via an image that is uploaded from a user and included in the PDF. – Brandon Dec 22 '21 at 00:33
  • I'm sorry to ask this again, but I'm a little confused. You originally said "My answer here contains a PostScript program which will work only with Ghostscript and only with versions up to 9.26 but does imposition." -- do you remember why you said 9.26? Is it because you thought imposition was going away? – Brandon Dec 22 '21 at 00:39
  • I don't recall exactly but... The program meddles with the internals of the interpreter, calling bits of it that are not intended to be used publicly (in fact **none** of that interpreter was intended to be public). It's possible that I knew something had changed in a later version and I do know that the code which will be shipped in the next release is utterly different and that program absolutely will not work with the March 2021 release. – KenS Dec 22 '21 at 08:11
  • This code seems to run fine on Ghostscript 10.01.1, which surprises me since it isn't supposed to work after March 2021. Did something change that allows it to work after all? Is there a newer version that I should be using? I'm afraid to upgrade production because of your warning but upgrading is a necessity. – Brandon Jun 15 '23 at 02:58
  • Oops, I was mistaken. You are right that the script doesn't work anymore :-) Now to find a new solution! – Brandon Jun 15 '23 at 03:40
  • There is documentation on scripting the new PDF interpreter here: https://ghostscript.readthedocs.io/en/latest/Language.html#scripting-the-pdf-interpreter. Also there is a new device that does N=up imposition which might work for you (the scripting definitely will but is harder). See https://ghostscript.readthedocs.io/en/latest/Use.html#snupcontrol-nup-option-string – KenS Jun 15 '23 at 19:15