Ursprung
6720fbe301
Commit
6b80860a9b
3 geänderte Dateien mit 51 neuen und 12 gelöschten Zeilen
|
@ -29,7 +29,7 @@ Run `pdf-sign -h` or `pdf-create-empty -h` for details.
|
|||
* `python3.7` or later
|
||||
* Python module `tkinter` (only needed for interactive use)
|
||||
* `gs` (Ghostscript)
|
||||
* `pdftk`
|
||||
* `qpdf` or `pdftk` (at least one of them)
|
||||
* `pdfinfo`
|
||||
* Copy one or both tools to a directory in your `$PATH`.
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
#Dependencies: python3, gs
|
||||
# Dependencies:
|
||||
# - python3.7 or later
|
||||
# - gs (Ghostscript)
|
||||
|
||||
import argparse, os, re, subprocess, sys
|
||||
|
||||
|
|
57
pdf-sign
57
pdf-sign
|
@ -1,11 +1,21 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
#Dependencies: python3.7 or later with module tkinter, gs (Ghostscript), pdftk and pdfinfo.
|
||||
# Dependencies:
|
||||
# - python3.7 or later with module tkinter
|
||||
# - gs (Ghostscript)
|
||||
# - qpdf or pdftk (pdf-sign will use pdftk if qpdf cannot be found)
|
||||
# - pdfinfo.
|
||||
|
||||
import argparse, os, queue, re, subprocess, sys, tempfile, traceback, time
|
||||
|
||||
# Inspired by https://unix.stackexchange.com/a/141496
|
||||
def main(args):
|
||||
if not hasQpdf and not has("pdftk"):
|
||||
die("Needs either qpdf or pdftk installed")
|
||||
if not has("gs"):
|
||||
die("Needs Ghostscript installed")
|
||||
if not has("pdfinfo"):
|
||||
die("Needs pdfinfo installed")
|
||||
filePath=args.input
|
||||
if not isPdfFilename(filePath):
|
||||
die("Input file must end with .pdf (case insensitive)")
|
||||
|
@ -14,11 +24,16 @@ def main(args):
|
|||
# Maybe flatten (make forms non-editable) before signing
|
||||
if args.flatten:
|
||||
inputPDF=str(intmp('input.pdf'))
|
||||
subprocess.run([
|
||||
qpdfOrPdftk([
|
||||
'qpdf',
|
||||
'--flatten-annotations=all',
|
||||
'--generate-appearances',
|
||||
filePath, inputPDF,
|
||||
],[
|
||||
'pdftk', filePath,
|
||||
'output', inputPDF,
|
||||
'flatten'
|
||||
], check=True)
|
||||
])
|
||||
else:
|
||||
inputPDF=filePath
|
||||
# The chosen page
|
||||
|
@ -29,11 +44,13 @@ def main(args):
|
|||
@Cell
|
||||
def pagePDF():
|
||||
outFile=intmp('page.pdf')
|
||||
subprocess.run([
|
||||
qpdfOrPdftk([
|
||||
'qpdf', '--pages', '.', f'{pageNumber()}', '--',
|
||||
inputPDF, str(outFile)
|
||||
],[
|
||||
'pdftk', inputPDF,
|
||||
'cat', str(pageNumber()),
|
||||
'output', str(outFile)
|
||||
], check=True)
|
||||
'output', str(outFile)])
|
||||
return outFile
|
||||
pageSize=Cell(lambda: pdfGetSize(str(pagePDF())))
|
||||
# The chosen signature
|
||||
|
@ -71,11 +88,14 @@ def main(args):
|
|||
@Cell
|
||||
def signedPagePDF():
|
||||
outFile=intmp('signed-page.pdf')
|
||||
subprocess.run([
|
||||
qpdfOrPdftk([
|
||||
'qpdf', '--overlay', str(signaturePositionedPDF()), '--',
|
||||
str(pagePDF()), str(outFile),
|
||||
],[
|
||||
'pdftk', str(pagePDF()),
|
||||
'stamp', str(signaturePositionedPDF()),
|
||||
'output', str(outFile)
|
||||
], check=True)
|
||||
])
|
||||
return outFile
|
||||
# The signed page as PNG, for GUI use
|
||||
displayMaxSize=Cell((400, 800))
|
||||
|
@ -283,7 +303,15 @@ def main(args):
|
|||
else:
|
||||
assert args.existing=='overwrite'
|
||||
pnr=pageNumber()
|
||||
subprocess.run([
|
||||
qpdfOrPdftk([
|
||||
'qpdf', '--pages',
|
||||
*(['.', f'1-{pnr-1}'] if 1 < pnr else []),
|
||||
str(signedPagePDF()), '1',
|
||||
*(['.', f'{pnr+1}-z'] if pnr < pageCount else []),
|
||||
'--',
|
||||
inputPDF,
|
||||
signedFilePath,
|
||||
],[
|
||||
'pdftk',
|
||||
f'A={inputPDF}',
|
||||
f'B={signedPagePDF()}',
|
||||
|
@ -292,7 +320,7 @@ def main(args):
|
|||
'B',
|
||||
*([f'A{pnr+1}-end'] if pnr < pageCount else []),
|
||||
'output', signedFilePath,
|
||||
], check=True)
|
||||
])
|
||||
print(f'Signed document saved as {signedFilePath}')
|
||||
else:
|
||||
print(f'Aborted')
|
||||
|
@ -303,6 +331,15 @@ class Volatile():
|
|||
def __eq__(self, other): return self is other
|
||||
def __str__(self): return str(self._underlying)
|
||||
|
||||
def has(cmd):
|
||||
return subprocess.run(["which", cmd], check=False, capture_output=True).returncode == 0
|
||||
hasQpdf = has("qpdf")
|
||||
def qpdfOrPdftk(qpdfCmd, pdftkCmd):
|
||||
assert qpdfCmd[0] == "qpdf" and pdftkCmd[0] == "pdftk"
|
||||
cmd = qpdfCmd if hasQpdf else pdftkCmd
|
||||
subprocess.run(cmd, check=True)
|
||||
return True # Some lambdas above rely on this
|
||||
|
||||
def getSignatureDir():
|
||||
if 'PDF_SIGNATURE_DIR' in os.environ:
|
||||
sd=os.environ['PDF_SIGNATURE_DIR']
|
||||
|
|
Laden …
Tabelle hinzufügen
In neuem Issue referenzieren