pdfpdf-generationghostscript

Is there is a way to overlay one rotated PDF document over another one?


I have SVG documents which I convert to PDF via ghostscript in the end of my workflow. Now I have to add a new feature. I need to replace some element in the SVG with PDF content. It's not just overlay, it could be rotated and scaled proportionally, here is an image for example:

1"example"

My question is are there any way to do it with ghostscript?

At the moment I just insert it as rasterized image by SVG image elements but I'll need it to be vector for printing.

NOTE: I'll need to take only first page from the inserted PDF.

I know there are a pdftk which can overlay one PDF file over another, but I can't scale and rotate overlayed PDF document with it.


Solution

  • I'm afraid that, due to recent changes in the Ghostscript PDF interpreter to address security vulnerabilities, this code will only work with versions up to 9.26. In future the PDF interpreter will be altered and there will be better ways to achieve this, but I'm afraid that's a long-term goal. For now, to use this code, you'll need to stick with an older version.

    The PostScript program which does this is as follows:

    %!PS
    
    %%
    %% This code is copied from pdf_main.ps, pdfshowpage_finish
    %% sadly that routine always calls showpage, and we want that
    %% to be under our control, so we have to duplicate the code
    %% here. Not only that but it uses GS extensions which aren't
    %% available outside of startup, so some things it simply can't
    %% replicate. As a result some of the error handling is less
    %% good.
    %%
    %% I plan to extend the PDF interpreter with two new
    %% routines, pdfnoshowpage_finish and then have both
    %% that and pdfshowpage_finish call pdfoptionalshowpage_finish
    %% which will take a boolean determining whether to actually
    %% call the showpage. At that time we'll alter this code.
    %%
    /draw_page_content {    % <pagedict> pdfshowpage_finish -
       save /PDFSave exch store
       /PDFdictstackcount countdictstack store
       /PDFexecstackcount count 2 sub store
       (before exec) VMDEBUG
    
       % set up color space substitution (this must be inside the page save)
       pdfshowpage_setcspacesub
    
            % Display the actual page contents.
       8 dict begin
       /BXlevel 0 def
       /BMClevel 0 def
       /OFFlevels 0 dict def
       /BGDefault currentblackgeneration def
       /UCRDefault currentundercolorremoval def
            %****** DOESN'T HANDLE COLOR TRANSFER YET ******
       /TRDefault currenttransfer def
      matrix currentmatrix 
      2 dict
      dictbeginpage setmatrix
      /DefaultQstate qstate store
    
      count 1 sub /pdfemptycount exch store
            % If the page uses any transparency features, show it within
            % a transparency group.
      dup pageusestransparency dup /PDFusingtransparency exch def {
        % Show the page within a PDF 1.4 device filter.
        0 .pushpdf14devicefilter {
          /DefaultQstate qstate store       % device has changed -- reset DefaultQstate
          % If the page has a Group, enclose contents in transparency group.
          % (Adobe Tech Note 5407, sec 9.2)
          dup /Group knownoget {
            1 index /CropBox pget {
              /CropBox exch
            } {
              1 index get_media_box pop /MediaBox exch
            } ifelse
            oforce_elems normrect_elems fix_empty_rect_elems 4 array astore .beginformgroup 
            showpagecontents
            .endtransparencygroup
          } {
            showpagecontents
          } ifelse
        } stopped {
          % abort the transparency device 
          .abortpdf14devicefilter
          /DefaultQstate qstate store   % device has changed -- reset DefaultQstate
          stop
        } if .poppdf14devicefilter
        /DefaultQstate qstate store % device has changed -- reset DefaultQstate
      } {
        showpagecontents
      } ifelse
      .free_page_resources
      % todo: mixing drawing ops outside the device filter could cause
      % problems, for example with the pnga device.
    
      end           % scratch dict
      % Some PDF files don't have matching q/Q (gsave/grestore) so we need
      % to clean up any left over dicts from the dictstack
    
      PDFdictstackcount //false
      { countdictstack 2 index le { exit } if
        currentdict /n known not or
        end
      } loop 
    
      pop
      count PDFexecstackcount sub { pop } repeat
      Repaired      % pass Repaired state around the restore
      PDFSave restore
      currentglobal pdfdict gcheck .setglobal
      .setglobal
      /Repaired exch def
    } def
    
    % And now, draw the page from the first PDF file
    (d:/temp/SO/target.pdf) (r) file runpdfbegin
    save
    1 pdfgetpage
    dup /Page exch store                                         draw_page_content
    restore                                      
    runpdfend
    
    % and then the page from the second PDF file
    (d:/temp/SO/source.pdf) (r) file runpdfbegin
    save
    1 pdfgetpage
    dup /Page exch store                                        
    
    % Before drawing the second page, adjust the CTM so
    % that the bottom left corner of the page is co-incident
    % with the bottom left of the area we want to draw the
    % page in
    75 575 translate
    
    % adjust the size of the output
    0.11 0.11 scale
    
    % and rotate it
    -46 rotate
    
    draw_page_content
    restore                                      
    runpdfend
    
    showpage
    

    Save that in a file, lets call it test.ps, and adjust the paths in the PostScript program suitably. Then run Ghostscript with: gs -sDEVICE=pdfwrite -sOutputFile=out.pdf test.ps and you'll get a PDF file something like what I think you want. Obviously you'll also need to change the numbers for translate/scale/rotate.