WIP issue #5
Dieser Commit ist enthalten in:
Ursprung
6720fbe301
Commit
71b1a84970
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
|
* `python3.7` or later
|
||||||
* Python module `tkinter` (only needed for interactive use)
|
* Python module `tkinter` (only needed for interactive use)
|
||||||
* `gs` (Ghostscript)
|
* `gs` (Ghostscript)
|
||||||
* `pdftk`
|
* `qpdf` or `pdftk` (at least one of them)
|
||||||
* `pdfinfo`
|
* `pdfinfo`
|
||||||
* Copy one or both tools to a directory in your `$PATH`.
|
* Copy one or both tools to a directory in your `$PATH`.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
#Dependencies: python3, gs
|
# Dependencies:
|
||||||
|
# - python3.7 or later
|
||||||
|
# - gs (Ghostscript)
|
||||||
|
|
||||||
import argparse, os, re, subprocess, sys
|
import argparse, os, re, subprocess, sys
|
||||||
|
|
||||||
|
|
57
pdf-sign
57
pdf-sign
|
@ -1,11 +1,21 @@
|
||||||
#!/usr/bin/env python3
|
#!/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
|
import argparse, os, queue, re, subprocess, sys, tempfile, traceback, time
|
||||||
|
|
||||||
# Inspired by https://unix.stackexchange.com/a/141496
|
# Inspired by https://unix.stackexchange.com/a/141496
|
||||||
def main(args):
|
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
|
filePath=args.input
|
||||||
if not isPdfFilename(filePath):
|
if not isPdfFilename(filePath):
|
||||||
die("Input file must end with .pdf (case insensitive)")
|
die("Input file must end with .pdf (case insensitive)")
|
||||||
|
@ -14,11 +24,16 @@ def main(args):
|
||||||
# Maybe flatten (make forms non-editable) before signing
|
# Maybe flatten (make forms non-editable) before signing
|
||||||
if args.flatten:
|
if args.flatten:
|
||||||
inputPDF=str(intmp('input.pdf'))
|
inputPDF=str(intmp('input.pdf'))
|
||||||
subprocess.run([
|
qpdfOrPdftk([
|
||||||
|
'qpdf',
|
||||||
|
'--flatten-annotations=all',
|
||||||
|
'--generate-appearances',
|
||||||
|
filePath, inputPDF,
|
||||||
|
],[
|
||||||
'pdftk', filePath,
|
'pdftk', filePath,
|
||||||
'output', inputPDF,
|
'output', inputPDF,
|
||||||
'flatten'
|
'flatten'
|
||||||
], check=True)
|
])
|
||||||
else:
|
else:
|
||||||
inputPDF=filePath
|
inputPDF=filePath
|
||||||
# The chosen page
|
# The chosen page
|
||||||
|
@ -29,11 +44,13 @@ def main(args):
|
||||||
@Cell
|
@Cell
|
||||||
def pagePDF():
|
def pagePDF():
|
||||||
outFile=intmp('page.pdf')
|
outFile=intmp('page.pdf')
|
||||||
subprocess.run([
|
qpdfOrPdftk([
|
||||||
|
'qpdf', '--pages', '.', f'1-{pageNumber()}', '--',
|
||||||
|
inputPDF, str(outFile)
|
||||||
|
],[
|
||||||
'pdftk', inputPDF,
|
'pdftk', inputPDF,
|
||||||
'cat', str(pageNumber()),
|
'cat', str(pageNumber()),
|
||||||
'output', str(outFile)
|
'output', str(outFile)])
|
||||||
], check=True)
|
|
||||||
return outFile
|
return outFile
|
||||||
pageSize=Cell(lambda: pdfGetSize(str(pagePDF())))
|
pageSize=Cell(lambda: pdfGetSize(str(pagePDF())))
|
||||||
# The chosen signature
|
# The chosen signature
|
||||||
|
@ -71,11 +88,14 @@ def main(args):
|
||||||
@Cell
|
@Cell
|
||||||
def signedPagePDF():
|
def signedPagePDF():
|
||||||
outFile=intmp('signed-page.pdf')
|
outFile=intmp('signed-page.pdf')
|
||||||
subprocess.run([
|
qpdfOrPdftk([
|
||||||
|
'qpdf', '--overlay', str(signaturePositionedPDF()), '--',
|
||||||
|
str(pagePDF()), str(outFile),
|
||||||
|
],[
|
||||||
'pdftk', str(pagePDF()),
|
'pdftk', str(pagePDF()),
|
||||||
'stamp', str(signaturePositionedPDF()),
|
'stamp', str(signaturePositionedPDF()),
|
||||||
'output', str(outFile)
|
'output', str(outFile)
|
||||||
], check=True)
|
])
|
||||||
return outFile
|
return outFile
|
||||||
# The signed page as PNG, for GUI use
|
# The signed page as PNG, for GUI use
|
||||||
displayMaxSize=Cell((400, 800))
|
displayMaxSize=Cell((400, 800))
|
||||||
|
@ -283,7 +303,15 @@ def main(args):
|
||||||
else:
|
else:
|
||||||
assert args.existing=='overwrite'
|
assert args.existing=='overwrite'
|
||||||
pnr=pageNumber()
|
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',
|
'pdftk',
|
||||||
f'A={inputPDF}',
|
f'A={inputPDF}',
|
||||||
f'B={signedPagePDF()}',
|
f'B={signedPagePDF()}',
|
||||||
|
@ -292,7 +320,7 @@ def main(args):
|
||||||
'B',
|
'B',
|
||||||
*([f'A{pnr+1}-end'] if pnr < pageCount else []),
|
*([f'A{pnr+1}-end'] if pnr < pageCount else []),
|
||||||
'output', signedFilePath,
|
'output', signedFilePath,
|
||||||
], check=True)
|
])
|
||||||
print(f'Signed document saved as {signedFilePath}')
|
print(f'Signed document saved as {signedFilePath}')
|
||||||
else:
|
else:
|
||||||
print(f'Aborted')
|
print(f'Aborted')
|
||||||
|
@ -303,6 +331,15 @@ class Volatile():
|
||||||
def __eq__(self, other): return self is other
|
def __eq__(self, other): return self is other
|
||||||
def __str__(self): return str(self._underlying)
|
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():
|
def getSignatureDir():
|
||||||
if 'PDF_SIGNATURE_DIR' in os.environ:
|
if 'PDF_SIGNATURE_DIR' in os.environ:
|
||||||
sd=os.environ['PDF_SIGNATURE_DIR']
|
sd=os.environ['PDF_SIGNATURE_DIR']
|
||||||
|
|
Laden …
Tabelle hinzufügen
In neuem Issue referenzieren