6

I'm trying to add a text comment (not a note) to a pdf file. I create a date.ps file which contains the text comment:

%! 
/Arial findfont 
30 scalefont 
setfont 
newpath 
10 720 moveto 
(PAID on 5.1.2013) show 
showpage

and I launch the shell command with $i=name of the pdf file to tag:

gs \
    -q \
    -dNOPAUSE \
    -dSAFER \
    -dBATCH \
    -sOutputFile="$RFP/$DOMAINE/$NEWNAME" \
    -sDEVICE=pdfwrite \
    -sPAPERSIZE=a4 \
    date.ps \
    $i

This works, but it creates a new 1st page - empty - with just the text "PAID on 5.1.2013" alone.

I do not find the trick to overlay the text on the 1st page of the original pdf.

hoijui
  • 3,615
  • 2
  • 33
  • 41

4 Answers4

8

You can do this directly to the PDF using the free cpdf command line tools:

For example,

cpdf \
    -add-text "PAID on 5.1.2013" \
    -topleft 100 \
    -font "Helvetica" \
    -font-size 30 \
    in.pdf \
    -o out.pdf
hoijui
  • 3,615
  • 2
  • 33
  • 41
johnwhitington
  • 2,308
  • 1
  • 16
  • 18
  • 1
    I use a synology NAS, and I have no such command. I may install a kit named xpdf 3.02-1, do u think it can help ? – Georges Guinchard Sep 12 '13 at 16:30
  • 1
    I try it under windows, this is very usefull and works fine. THe problem is that there is no compiled version for the NAS I use, and my knowledge to compile is too poor ! – Georges Guinchard Sep 13 '13 at 09:43
  • 1
    The nix package manager has a compiled version of it, you can install nix with `sh <(curl -L https://nixos.org/nix/install) --daemon` (should work in Linux, MacOS and Windows WSL) and then install cpdf with `nix-env -iA nixpkgs.ocamlPackages.cpdf`. – tobiasBora Oct 15 '20 at 20:34
  • @johnwhitington thanks, thanks, THANKS! This tool is simply awesome and damn easy to use! you saved my day! – Ma3x Sep 21 '22 at 13:25
5

Since cpdf has a strange license for commercial use, I tried to find an alternative. Here is one (you need to install enscript, ps2pdf and (pdftk or qpdf)). The idea is just to use enscript to create a .ps from a text, then you convert this .ps into a .pdf using ps2pdf, and then you stack it on top of the original pdf with pdftk or qpdf...).

pdtfk version:

echo "I will be stamped on top of the page" | \
    enscript -B -f Courier-Bold16 -o- | \
    ps2pdf - | \
    pdftk input.pdf stamp - output output.pdf

qpdf version:

If you want the text to repeat on all pages:

tmpfile=$(mktemp) && echo "I will be stamped on top of the page" | \
    enscript -B -f Courier-Bold16 -o- | \
    ps2pdf - "$tmpfile" && qpdf input.pdf --overlay "$tmpfile" --repeat=1-z -- output.pdf

if you just want to put it on the first page:

tmpfile=$(mktemp) && echo "I will be stamped on top of the page" | \
    enscript -B -f Courier-Bold16 -o- | \
    ps2pdf - "$tmpfile" && qpdf input.pdf --overlay "$tmpfile" -- output.pdf

See the enscript documentation for more options on how to format the text.

NB: mktemp is just used to create a temporary file to provide a one-liner solution, since qpdf does not accept input from stdin. Remove the tmpfile with rm "$tmpfile" after the command is done.

slhck
  • 36,575
  • 28
  • 148
  • 201
tobiasBora
  • 1,542
  • 14
  • 23
  • nice idea! Can I center multiline text with enscript? – xeruf Apr 12 '23 at 07:47
  • @xeruf I don’t have much time to try, but you might want to try to use the escape symbol `c`, which might be used like this `^@c{your text}` (not sure about the syntax). Cf https://linux.die.net/man/1/enscript To enable escape, you also want to `-e` option. – tobiasBora Apr 12 '23 at 07:53
2

Because your PostScript executed a showpage it ejects the first page after marking it, so the remaining content is therefore on the 2nd and subsequent pages. If you don;t execute showpage then the marks you make will be on the first page, and the first PDF page will be drawn 'on top' of it.

More complex code can use BeginPage and EndPage to draw over and under the page contents, and to do so on specified pages, among other things.

[added later]

Try this:

%!
<< 
/EndPage 
{
  0 eq
  {
    0 eq
    {
      /Arialabold findfont 22 scalefont setfont newpath 250 820 moveto 1 0 0 setrgbcolor (PAYE PAR CCP LE $DATEPMT) show
    } if
    true
  }
  {
    pop false
  } ifelse
} >> setpagedevice

Works for me.

KenS
  • 30,202
  • 3
  • 34
  • 51
  • Do'nt work, I get the same result with ps file:%! /Arial findfont 30 scalefont setfont newpath 10 720 moveto (PAYE PAR UBS LE 16.09.2013) show – Georges Guinchard Sep 13 '13 at 08:14
  • 1
    because its *under* the PDF page, which does an implicit erasepage and fills with white... – KenS Sep 13 '13 at 15:25
  • maybe you may help me more, could you contact me per e-mail. Of course, if you propose me a solution, I pay for the time you spend. – Georges Guinchard Sep 16 '13 at 15:58
  • 2
    "<> setpagedevice" You'll need to fill in your complete marking operation in the '...' section. EndPage is called after every page, this code tests the reason code to see if its 'showpage' (that is, 0) and if it is tests the page count to see if its 1, if it is it runs the code. – KenS Sep 17 '13 at 07:36
  • Many thanks, it is almost what I'm looking for.
    but when I use for example a pdf of 2 pages, I get 1 page empty, the 1nd correspond to the 1st of the pdf, the 3rd is empty and the 4th sorrespond to the 2nd WITH MY TEXT. Here is the complete content of my ps file : << /EndPage {0 eq{1 eq{/Arial findfont 20 scalefont setfont newpath 20 720 moveto (My 123 Text) show} if}{pop} ifelse true} >> setpagedevice. The shell script to use gs is : cd /volume1/DATA/A_PAYER gs -q -dNOPAUSE -dSAFER -dBATCH -sOutputFile=./OUT.pdf -sDEVICE=pdfwrite -sPAPERSIZE=a4 datex.ps ./IN.pdf
    – Georges Guinchard Sep 17 '13 at 08:54
  • The '1 eq' should of course have been '0 eq', since you want the text to appear on the first page (ie when 0 pages have already been printed...) I can't thinkof any reason why you would be getting multiple pages emitted though. – KenS Sep 17 '13 at 13:14
  • Yes, the text appears on the right page. But there is an empty page before every page, then 2 empty pages at the end. I have checkd with various pdf's (scans, documents created witg pdf creator, etc) – Georges Guinchard Sep 17 '13 at 14:10
  • Finally, I can leave with the 2nd line in my script : gs -sDEVICE=pdfwrite -dNOPAUSE -dQUIET -dBATCH -dFirstPage=2 -sOutputFile=output.pdf OUT.pdf So is the 1st (and empty) page deleted. The further empty pages are not very elegant, but I have no idea how to suppress blank pages. I have see that there is a bug (http://bugs.ghostscript.com/show_bug.cgi?id=691915), maybe that it comes from that. – Georges Guinchard Sep 17 '13 at 15:07
  • That bug is only relevant if you have an output filename of the form -sOutputFile=out%d.pdf, ie you are producing one file per page, and it only produces one extra (empty) file. I can't investigate further without seeing the file in question. – KenS Sep 18 '13 at 07:22
  • no, the output file is a shell variable that is defined above in the script. Would you I send you an example ? where ? Maybe that it is the version (8.71) of ghostscript, I have taken the ipkg available by synology, my knowledge in compiling dos not allow me to take a newer version of gs :( – Georges Guinchard Sep 18 '13 at 09:31
  • 8.71 is old, I would recommend getting the latest version (9.10). There's a built executable available for download for Linux I believe, so you don't need to compile GS unless you are running on something other than an x86 architecture. Rather than send me an example, why not put it on a public location where anyone interested can have a look. – KenS Sep 18 '13 at 12:01
  • Finally, I have compiled the gs GPL Ghostscript 9.10 (2013-08-30) and I have the same blank pages problem. You may consukt original pdf, overlayed pdf, and shell script (with example of the .ps file used to http://www.myupload.dk/handleupload/76e90zWEtmQiM. Thanks for help – Georges Guinchard Sep 19 '13 at 12:24
  • Edited answer for clarity, please see above – KenS Sep 19 '13 at 20:51
  • 1
    Thanks a lot KenS, it works with the version 9.10, but not with 8.71. If you take contact with me per e-mail, I'll offer you something for your precious help. – Georges Guinchard Sep 20 '13 at 13:49
  • Thanks for the offer, but really no need, thanks are more than enough. I'm pleased to hear its working OK for you. – KenS Sep 20 '13 at 18:50
1

PDFtk Ghostscript and cPDF are not "Free for commercial usage".

Where as qPDF is FOSS and can do the task using a helper script, as it currently has no inbuilt editing ability.

enter image description here

Here is a Windows Command script for above where command was

Watermark "PAID on 5.1.2013" qpdf-manual.pdf

Watermark.cmd (proof of concept for you to edit as desired)

@echo off &rem cls & Title qPDF Watermark/Overlay with solid text
REM For PDF/A-1 & compatability with older PDFs, this file is intentionally solid color only
Rem Answer to https://stackoverflow.com/questions/18769314/add-text-on-1st-page-of-a-pdf-file
::ÿþ
Rem Variables For Letter use "Page=612 792" for A4 use "Page=594 842"
REM 1 = full color For RGB blue say "Color=0 0 1 rg" For cmyk black change to say "Color=0 0 0 1 k"
set "Page=612 792"
set "Color=1 0 0 rg"
set "Indent=72"
set "Elevation=710"
set "Font=Helvetica"
set "emSize=30"
set "Range=1"

if "%~1"=="" echo Usage "%0" "Quoted Text" "filename.pdf" &pause & exit /b
if /i "%~x2" neq ".pdf" echo Usage "%0" "Quoted Text" "filename.pdf" (pdf only)&pause & exit /b
if not exist "%~2" echo Usage "%0" "Quoted Text" "filename.pdf" (PDF not found)&pause & exit /b

REM for this usage setlocal is surplus to requirement but included if needed later
rem setlocal enableExtensions enableDelayedExpansion

echo/>TempOverlay.tmp
for /f "tokens=2 delims=:."  %%c in ('ChCp') do @set "OLD_ChCp=%%~c"
if not defined OLD_ChCp set "OLD_ChCp=850"
ChCp 65001 >nul
echo/

for %%e in (
%%PDF-1.7
1 0 obj ^<^</Type/Catalog/Pages 2 0 R^>^>endobj
2 0 obj ^<^</Type/Pages/MediaBox [0 0 %Page%]/Count 1/Kids[3 0 R]^>^>endobj
3 0 obj ^<^</Type/Page/Parent 2 0 R/Resources^<^</Font^<^</F1 4 0 R^>^>^>^>/Contents 5 0 R^>^>endobj
4 0 obj ^<^</Type/Font/Subtype/Type1/BaseFont/%Font%^>^>endobj
5 0 obj ^<^</Length 72^>^>stream %Color% BT %Indent% %Elevation% TD /F1 %emSize% Tf(%~1
) do @echo %%e >>TempOverlay.tmp

@echo off
rem deliberate firebreak to allow for other additions

for %%e in (
^)Tj ET
endstream
endobj

xref
0 6
0000000000 65535 f
0000000013 00000 n
0000000071 00000 n
0000000166 00000 n
0000000281 00000 n
0000000351 00000 n
trailer^<^</Size 6/Root 1 0 R^>^>
startxref 475
%%%%EOF
) do @echo %%e >>TempOverlay.tmp

REM tidy-up temporary file
REM we expect errors from bad file above so here send error output 2 to nul
qpdf TempOverlay.tmp TempOverlay.pdf 2>nul
qpdf "%~2" --overlay TempOverlay.pdf --to=%Range% -- "%~dpn2-overlaid.pdf"

ChCp %OLD_ChCp% >nul
set OLD_ChCp=
endlocal
REM exit
K J
  • 8,045
  • 3
  • 14
  • 36