Andrew Zolotukhin
Andrew Zolotukhin

Reputation: 109

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.

Upvotes: 1

Views: 950

Answers (2)

KenS
KenS

Reputation: 31139

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.

Upvotes: 1

Andrew Zolotukhin
Andrew Zolotukhin

Reputation: 109

I'm posting here a solution which support clipping of pasted PDF file in case if somebody will need it in future. I've added additional parameters to draw_page_content to support that. I appeared that "dict beginpage setmatrix" command somehow overrides clipping path and it should be set only after that command. I'm not an expert in PostScript, but at least this solution is working. Thanks again KenS for your help.

%!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 -
   /clipx exch def /clipy exch def /clip_width exch def /clip_height exch def /should_clip exch def
   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

  % set clipping here
  should_clip 0 gt
  {
    newpath clipx clipy moveto clipx clipy clip_height add lineto clipx clip_width add clipy clip_height add lineto clipx clip_width add clipy lineto closepath clip
  }
  {
  }
  ifelse

  /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
} bind def

% And now, draw the page from teh first PDF file
(/opt/files/main.pdf) (r) file runpdfbegin
save
1 pdfgetpage
dup /Page exch store 0 1125 1125 0 0 draw_page_content
restore                                      
runpdfend

gsave

% and then the page from the next PDF file
(/opt/files/placement1.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
120 120 translate

% and rotate it
25 rotate

% adjust the size of the output
0.7 0.7 scale

%let's skew file
[1 0 1.3 1 0 0] concat

% draw file and clip it
1 100 100 0 0 draw_page_content

restore                                      
runpdfend
grestore

showpage

Upvotes: 0

Related Questions