2

I found similar question that was answered Overlay two postscript files (command line approach)?, However, that solution does not work for me and my requirements are bit different (one of my files is multi-page file)

I have a PDF File (DrawingSheet.pdf) that is generated outside my control. I have a PostScript file (Table.ps) that I generate by converting XML file using XSLFO and Apache FOP. Table.ps contains one table.

The DrawingSheet.pdf has multiple pages. Table.ps has only one page. Both DrawingSheet.pdf and Table.ps has same paper size - A size, Landscape. I want to place that table from the Table.ps on top of the last page of the DrawingSheet.pdf. I don't want to "append" table page as additional page at the end of the DrawingSheet.pdf. I am using command-line GhostScript (v9.27).

Per the suggestion from that post, I wrote following Overlay.ps file:

/origshowpage /showpage load def
/showpage {} def
gsave
    (DrawingSheet.pdf) run
grestore
    (Table.ps) run
origshowpage

That does not work. What I get is the table page "appended" to the DrawingSheet.pdf rather than overlaying on top of the last page.

What am I doing wrong? How can I achieve what I want?

Edited-1:

If it helps to have same number of pages in both files to simplify logic, then I can generate Table.ps to have same number of pages as DrawingSheet.pdf file and have the table appear only on the last page of the Table.ps.

Edited-2:

Alternatively, I tried following approach (similar to adding image watermark to PDF file). Net result of this is that I as many blank pages as number of pages in DrawingSheet.pdf. Interestingly though, when I load that blank resulting PDF, I can see that the Acrobat Reader flashes the table across the screen, but when the entire file is loaded all I see in the end are blank pages.

Any help in solving this much appreciated.

<<
    /EndPage
    {
        2 eq
        {
            pop false
        }
        {
            gsave
                1 dict begin
                    /showpage {} def
                    (Table.ps) run
                end
            grestore
            pop true
        } ifelse
    } bind
>> setpagedevice

Edited-2 - Additional Notes: If I comment out gsave and grestore from above code, then I don't get blank pages but instead every page is now "replaced" (no overlay) with the table from Table.ps.

Edited-3 - Partial Success:

After coming across this post: Edit (every page of a) Postscript file manually, I tried that approach. I converted the DrawingSheet.pdf to DrawingSheet.ps, identified the last %%EndPageSetup line, and inserted following code after that line:

% BUNCH OF LINES NOT SHOWN %
%%BeginPageSetup
4 0 obj
<</Type/Page/MediaBox [0 0 792 612]
/Parent 3 0 R
/Resources<</ProcSet[/PDF]
/Font 10 0 R
>>
/Contents 5 0 R
/CropBox 
[0 0 792.0 612.0]
/BleedBox 
[0 0 792.0 612.0]
/TrimBox 
[0 0 792.0 612.0]
>>
endobj
%%EndPageSetup
%gsave                           % <== My Edits
    1 dict begin                 % <== My Edits
        /showpage {} def         % <== My Edits
        (Table.ps) run           % <== My Edits
    end                          % <== My Edits
%grestore                        % <== My Edits
5 0 obj
<</Length 30050>>stream
% BUNCH OF LINES NOT SHOWN %

With this edits (gsave and grestore commented out - if I uncomment them then this does not work), I get my table super imposed on the last page. Table appears fine, however the original last page from the pdf appears as flipped over the center on X axis after super imposing. The screenshot of the page is:

enter image description here

Text that you can read is from the Table.ps, while the text that is flipped is the original text from the last page of the DrawingSheet.pdf (of course that page in original pdf is not flipped).

Not sure what I am doing wrong to get that flipped image?

Edited-4:

Added additional notes to "Edited-2"

YogiWatcher
  • 165
  • 1
  • 8
  • Could you convert the pdf to ps and edit the DrawingSheet.ps to add the Table.ps then convert the ps back to pdf? – beginner6789 Mar 09 '21 at 23:33
  • I tried doing that manually. I used GhostScript to convert the `DrawingSheet.pdf` file into `DrawingSheet.ps` file. But, it seems that the converted PS file is has some binary data in it, and makes it very hard to determine where to insert `Table.ps`. If doing that manually is so hard, then it would be quite difficult to program this in automatic fashion. Each time this program would run, the PDF file would be different with different content, # of pages, etc. – YogiWatcher Mar 10 '21 at 04:16
  • The edited-2 section looks close, but the PS file could be just a single page. Maybe try adding `matrix defaultmatrix setmatrix` in there? And the logic seems a little off. You want the overlay in the *true* branch I think. And returning `false` will suppress the page, so I think you don't want to do that at all. – luser droog Mar 10 '21 at 22:19
  • @luserdroog: In Edited-2, I am adding overlay in the branch when reason code is 0 or 1. False is returned only when the reason code is 2 – YogiWatcher Mar 10 '21 at 23:03
  • The code in edited-3 looks lilke PDF and not PS. Are you "converting" by just changing the file extension? If so, it would be better to use a utility like ImageMagick's `convert` or `gs -sDEVICE=ps2write`. If you're running this result through GhostScript, it's probably taking advantage of quirks in the interpreter that interpret PDF using PS code. But this is not a great method because undocumented internals may change with newer versions of GS. If you still have an `xref` table at the end of the file, then it's PDF not PS. – luser droog Mar 11 '21 at 00:32
  • Re: edited-2. I see now, you're right. I forgot what the parameters were and thought the TOS was page number. It seems like you probably want to execute new code or not depending upon the page number. – luser droog Mar 11 '21 at 00:34
  • If edited-3 is actually PDF, then adding new code is going to change the binary offsets of the objects that follow and cause strange behavior (which could easily include the x-axis flip and many more bizarre things). If you add `-dPDFDEBUG -dPDFSTOPONERROR` it may help to figure out what's happening. – luser droog Mar 11 '21 at 00:39
  • @luserdroog: Code in Edited-3 is actually converted using GhostScript ps2write device. I will try `-dPDFDEBUG -dPDFSTOPONERROR` to see what's happening. RE: Edited-2: I actually want to superimpose table only on the last page of the pdf. However, that will be the next puzzle, if I could ever get the code from Edited-2 to work on all pages. – YogiWatcher Mar 11 '21 at 04:54

2 Answers2

1

I tried your endpage and finally got this approach to work. The EndPage puts the reason code 0 or 1 and the page count on the stack (unreliably for some pdfs) so an alternative is the /PageCount variable and I show both. These count the number of pages and are incremented at the end of the page.

You need to know the number of pages to put the overlay on the page before the page is finished. The "finalpage" variable can also be defined on the ghostscript command line instead of in the file. Run ghostscript with both your endpage.ps and DrawingSheet.ps files as arguments. More work would be needed to adapt this to a DrawingSheet.pdf except that should be possible.

See if this works after changing the finalpage number. The table.ps should be placed on top of the original last page before the showpage is executed.

%!
/finalpage 9 def
<<
/EndPage {
       2 ne {
       1 add finalpage eq {(found last page\n)print (Table.ps)run} if
       currentpagedevice /PageCount get ==
       true
       } if
     }
>> setpagedevice

EDIT: The Postscript Language Reference Manual 3 page 176 says this:

The PostScript interpreter maintains an implicit current page that accumulates the marks made by the painting operators. When a program begins, the current page is completely blank. As each painting operator executes, it places marks on the current page. Each new mark completely obscures any marks it may overlay. This method is known as a painting model: no matter what color a mark has—white, black, gray, or color—it is put onto the current page as if it were applied with opaque paint. Once the page has been completely composed, invoking the showpage operator renders the accumulated marks on the output media and then clears the page to white again.

beginner6789
  • 575
  • 3
  • 7
  • Your suggestion is basically same as my "Edited-2" code with additional logic to deal with the last page only. However, when I tried your code, it simply "replaces" (does not overlay/superimpose) the last page with the table and adds one additional blank page at the end. If I change `(Table.ps)run` from your code with `1 dict begin /showpage {} def (Table.ps)run end`, then it still "replaces" the last page with the table, but does not put the additional blank page at the end. Were you able to get your code to superimpose/overlay on top of the last page? – YogiWatcher Mar 12 '21 at 05:04
  • I tested the code with different postscript files and the overlay was added correctly. Your code added the overlay to every page and mine went on the last page only. I suspect your Table.ps has a showpage at the end that should be removed. The gsave/grestore shouldn't matter due to your Table.ps being just before the final showpage. The overlay will always be opaque where the painted area will cover the original page. Be sure your Table.ps does not draw a white box at the beginning of the code and is vector code not an image. Try some Simple.ps instead of Table.ps to compare the result. – beginner6789 Mar 12 '21 at 13:09
  • Perhaps this might explain: Although I generate the `Table.ps`, I don't write it line by line. I have XML data that I convert to Table.ps using XSLFO and Apache FOP. This might add some PostScript code to `Table.ps` that might be causing the behavior that I see. my `Table.ps` does not draw a while box, because the Edited-3 code kind of works. I will try with manually generated PostScript file. I will edit my original post to mention this. – YogiWatcher Mar 12 '21 at 13:35
  • I can confirm that a simple PostScript file manually generated (hand written) does in fact superimpose the page on top of the last page. I will have to play around with the `Table.ps` auto-generated by Apache FOP to see which PostScript code inside that file breaks it. It appears that the entire `Table.ps` page is treated as opaque page in "Edited-2" code, while the same `Table.ps` page works in "Edited-3" code (although net results are not quite what I expected). Do you know of any PostScript commands/operators that might cause such behavior? – YogiWatcher Mar 12 '21 at 16:38
  • The translate with scale operators can flip the page: 1 -1 scale – beginner6789 Mar 12 '21 at 18:24
  • Yes and thanks. But I was not wondering why the original page flipped but rather knowing/understanding why the code from "Edited-2" superimpose the table as full opaque page (that's why it appears that page is "replaced") as compared to code from "Edited-3" where it actually superimposes. Basically code "Edited-2" and "Edited-3" are identical except for the location and method of invoking it, and yet results are different. OR a simple handwritten PostScript file works correctly to superimpose the page, but the Apache FOP generated file does not. I would like to understand why. – YogiWatcher Mar 12 '21 at 18:50
1

After many trials and errors and of course suggestions/comments from fellow SO users like @beginner6789 and @luserdroog (my thanks to both of them), I finally got something that works for my case. I am going to summarize my experiments and findings for benefits of others.

For this discussion, let's say you want to superimpose a page from Overlay.* (.ps or .pdf) on top of one particular page from Base.* (.ps or .pdf). There does not appear to be one solution fit all scenarios. It depends on:

  • If the you have Overlay.ps or Overlay.pdf. Although converting them is quite easy.
  • If the you have Base.ps or Base.pdf. Although converting them is quite easy.
  • If the Overlay.ps or Base.ps is auto-generated from corresponding pdf file or if they are handwritten. I found that the handwritten PostScript files are much cleaner, precise, and optimized compared to auto-generated PostScript files.
  • Logic becomes more complex depending upon which page/s of Overlay file you want super imposed on top of which page/s of Base file. You may have to do some extra work to identify those pages.

Following are 3 solutions that I tried. To summarize: Solution-3 worked for me as described by the last row of the corresponding results table.


Solution-1:

Create following Merge.ps and run it through your GhostScript to generate PDF output:

1 dict begin
    /showpage {} def
    (Base.ps) run     % or Base.pdf
    (Overlay.ps) run  % or Overlay.pdf
end
showpage

Results:

Base File Overlay File Results/Notes
Base.pdf Overlay.ps (Handwritten) Overlay file is appended at the end of the Base file
Base.pdf Overlay.ps (Converted from PDF) Overlay file is appended at the end of the Base file
Base.pdf Overlay.pdf Overlay file and a blank page is appended at the end of the Base file
Base.ps (Converted from PDF) Overlay.ps (Handwritten) Single page PDF with all pages from Base file and Overlay file are superimposed on top of each other
Base.ps (Converted from PDF) Overlay.ps (Converted from PDF) Single page PDF with page from Overlay file only
Base.ps (Converted from PDF) Overlay.pdf Same as Overlay file with blank page appended at the end

Does not appear to be a suitable option if Base file is multi-page file.


Solution-2:

Create following Merge.ps and run it through your GhostScript along with Base file to generate PDF output:

<<
    /EndPage
    {
        2 eq
        {
            pop false
        }
        {
            1 dict begin
                /showpage {} def
                (Overlay.ps) run  % or Overlay.pdf
            end
            pop true
        } ifelse
    } bind
>> setpagedevice

Additional logic is required if you want Overlay on only certain pages of the Base file.

Results:

Base File Overlay File Results/Notes
Base.pdf Overlay.ps (Handwritten) Overlay file is superimposed on top of every page of the Base file
Base.pdf Overlay.ps (Converted from PDF) Each page from Base file is replaced by the page from Overlay file
Base.pdf Overlay.pdf GhostScript crash after stack-overflow (no pun intended)
Base.ps (Converted from PDF) Overlay.ps (Handwritten) Overlay file is superimposed on top of every page of the Base file
Base.ps (Converted from PDF) Overlay.ps (Converted from PDF) Each page from Base file is replaced by the page from Overlay file
Base.ps (Converted from PDF) Overlay.pdf GhostScript crash

Solution-3:

Edit the Base.ps file that was converted from Base.pdf. Find the entry %%EndPageSetup corresponding to the page/s that you want overlay, and insert following code after that. Run it through your GhostScript to generate PDF output. In the example below I am editing it for the third page:

% BUNCH OF CODE NOT SHOWN HERE %
%%Page: 3 3
%%PageBoundingBox: 0 0 792 612
%%BeginPageSetup
8 0 obj
<</Type/Page/MediaBox [0 0 792 612]
/Parent 3 0 R
/Resources<</ProcSet[/PDF]
>>
/Contents 9 0 R
/CropBox 
[0 0 792.0 612.0]
>>
endobj
%%EndPageSetup
    1 dict begin                    % <== My Edits
        /showpage {} def            % <== My Edits
        (Overlay.ps) run            % <== My Edits
        % [1 0 0 -1 0 612] concat   % <== My Edits. See Notes below.
    end                             % <== My Edits
9 0 obj
% BUNCH OF CODE NOT SHOWN HERE %

Results:

Base File Overlay File Results/Notes
Base.ps (Converted from PDF & Edited) Overlay.ps (Handwritten) Overlay page is superimposed on top of the third page of the Base file
Base.ps (Converted from PDF & Edited) Overlay.ps (Converted from PDF) Overlay page is superimposed on top of the third page of the Base file, but the third page of the base file appears as a X-axis mirror of the original third page
Base.ps (Converted from PDF & Edited) Overlay.pdf Four page output of the Base file with Overlay page inserted as the third page.
Base.ps (Converted from PDF & Edited) Overlay.ps (Converted from PDF with the transformation matrix concatenation uncommented from code above) Overlay page is superimposed on top of the third page of the Base file as I wanted. This scenario works for me

In my case:

  • I had no control over how the Base.pdf file was generated, but I could convert it to Base.ps using GhostScript.
  • I had partial control over Overlay file generation. I had XML data, which I converted to table format using XSLFO, and then converted to PDF and PostScript file using Apache FOP.
  • I really had no option of manually writing Base.ps or Overlay.ps.

Considering, results of all these trials and errors, at this time the Solution-3 (with transformation matrix concatenated after the run command, the last row from that results table) is working for me. I don't particularly like this option, because it involves editing the auto-generated PostScript file.

NOTE: For all those cases where I saw that the page from Base file is being replaced by Overlay page, I think the original page from the Base file is being printed outside the output page size hence it is clipped and does not show and appears as being replaced. Maybe proper manipulation of the transformation matrix would fix those cases. I am yet to experiment with that scenario but don't know how to verify.

YogiWatcher
  • 165
  • 1
  • 8