Search code examples
ghostscriptpostscriptpoppler

Edit (every page of a) Postscript file manually


I want to (manually) insert additional postscript commands in a postscript file that was converted from a PDF file via ghostscript’s pdf2ps. For the purpose of testing I created a PDF file from the following file using pdflatex:

\documentclass[a4paper]{article}
\begin{document}
Mostly empty.
\end{document}

In the converted postscript file I make the following edit:

...
%%Page: 1 1
%%PageBoundingBox: 0 0 595 841
%%BeginPageSetup
4 0 obj
<</Type/Page/MediaBox [0 0 595.28 841.89]
/Parent 3 0 R
/Resources<</ProcSet[/PDF]
/Font 8 0 R
>>
/Contents 5 0 R
>>
endobj
%%EndPageSetup
% BEGIN MANUAL EDIT
0 setgray 0 0 moveto 595 841 lineto stroke
% END MANUAL EDIT
5 0 obj
<</Length 257>>stream
q 0.1 0 0 0.1 0 0 cm
0 G
0 g
q
10 0 0 10 0 0 cm BT
/R6 9.9626 Tf
1 0 0 1 139.746 706.129 Tm
[(M)-0.699638(os)-0.399443(t)-0.900585(l)-0.798886(y)-333.819(e)-0.400668(m)-0.300195(p)-0.599165(t)26.0974(y)83.192(.)-0.800112]TJ
154.421 -615.691 Td
(1)Tj
ET
Q
Q

endstream
endobj
pagesave restore
%%PageTrailer
%%Trailer
end
%%EOF

Instead of producing a diagonal line the postscipt/PDF file remains (seemingly) unchanged. However, if I alter the page dimensions from A4 to letter size the line is shown:

%%Page: 1 1
%%PageBoundingBox: 0 0 612 792
%%BeginPageSetup
4 0 obj
<</Type/Page/MediaBox [0 0 612 792]
...

I am obviously missing something here (which is not surprising given my rudimentary knowledge of postscript). My question is: How can I make the line appear while keeping the page dimensions unchanged?

P.S.: A comment I stumbled across mentioned that pdftops (from poppler-utils) is in some sense superior to pdf2ps. Indeed, inserting commands into the converted postscript file immediately before the showpage command (which is not there at all when using pdf2ps) worked fine. So I have probably already found a solution to my problem. However, I would like to learn what the page dimensions have to do with it when using pdf2ps.


Solution

Thanks to KenS’s advice and referring to his answer to this question I was able to achieve the desired effect by adding an EndPage procedure to the postscript file:

<<
/EndPage
{
  exch pop 2 lt
  {
    gsave
    0 0 translate
    0 setgray 0 0 moveto 596 842 lineto stroke
    grestore
    true
  }{false} ifelse
} bind
>> setpagedevice

(This assumes the page size is a4.)


Solution

  • PostScript is a write-only language :-)

    Seriously, its a programming language. In order to understand what's going on, you need to understand the program, which in the case of the output from Ghostscript's ps2write device, is distinctly non-trivial.

    The syntax is basically PDF, with a prolog program which interprets it in PostScript terms.

    The program will use showpage, it does it when the procedure EndStream is executed, which is (basically) when the endobj keyword is encoutnered in a page stream. You'll see that it looks like:

    ET
    Q
    Q
    Q
    
    endstream
    endobj
    %%Page: 2 2
    

    You could place anything you like between the endstream and the endobj, but you need to be aware that the graphics state at that point is determined by whatever operations have already taken place. This can include scaling, oration, skeing, flipping the vertical axis, etc. So simply inserting some PostScript into there is unlikely to work. You could do an initgraphics which would at least reset the graphics state to a known setup.

    As a test I ran Ghostscript'sd ps2write device like this:

    gs -sDEVICE=pdfwrite -o out.ps -c "showpage" -f

    which produces a PostScript program where the (effective) content is:

    %%EndResource
    %%EndProlog
    %%Page: 1 1
    %%PageBoundingBox: 0 0 595 842
    %%BeginPageSetup
    4 0 obj
    <</Type/Page/MediaBox [0 0 595 842]
    /Parent 3 0 R
    /Resources<</ProcSet[/PDF]
    >>
    /Contents 5 0 R
    >>
    endobj
    %%EndPageSetup
    5 0 obj
    <</Length 23>>stream
    q 0.1 0 0 0.1 0 0 cm
    Q
    
    endstream
    endobj
    %%Trailer
    end
    %%EOF
    

    I then modified this more or less as you suggested:

    %%EndPageSetup
    0 setgray 0 0 moveto 595 842 lineto stroke
    5 0 obj
    <</Length 23>>stream
    q 0.1 0 0 0.1 0 0 cm
    Q
    
    endstream
    endobj
    %%Trailer
    

    For me this produced the expected stroke from bottom left to top right. Obviously without the PostScript file you originally produced I can't tell you why your experience is different. (no I'm not in a position to run latex to produce such a thing, and even if I did I have no way to know which version of Ghostscript and the other tools you used).

    My guess would be that 'something' in your PDF file overwrote the entire page, its not entirely uncommon.