Ursprung
3eed74e1ef
Commit
c61747cf7a
3 geänderte Dateien mit 271 neuen und 12 gelöschten Zeilen
14
README.md
14
README.md
|
@ -22,7 +22,7 @@ The recommended way is:
|
||||||
You can now use the `pdf-sign` tool interactively (or non-interactively) to sign PDF files.
|
You can now use the `pdf-sign` tool interactively (or non-interactively) to sign PDF files.
|
||||||
The GUI is self documented and allows both keyboard-only and pointer-only operation.
|
The GUI is self documented and allows both keyboard-only and pointer-only operation.
|
||||||
|
|
||||||
Run `pdf-sign -h` or `pdf-create-empty -h` for details.
|
Run `pdf-sign -h`, `pdf-create-empty -h` or `pdf-from-text -h` for details.
|
||||||
|
|
||||||
**Installation**
|
**Installation**
|
||||||
|
|
||||||
|
@ -41,9 +41,19 @@ apt-get update
|
||||||
apt-get install -y coreutils git python3 python3-tk ghostscript pdftk poppler-utils
|
apt-get install -y coreutils git python3 python3-tk ghostscript pdftk poppler-utils
|
||||||
git clone https://github.com/svenssonaxel/pdf-sign.git
|
git clone https://github.com/svenssonaxel/pdf-sign.git
|
||||||
cd pdf-sign
|
cd pdf-sign
|
||||||
cp pdf-sign pdf-create-empty /usr/local/bin/
|
cp pdf-sign pdf-create-empty pdf-from-text /usr/local/bin/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Related use cases
|
||||||
|
|
||||||
|
* You can add the date or other pieces of text using the `--text` CLI option or `Signature -> Custom text` menu option.
|
||||||
|
* You can convert SVG stamps/marks and add them to your signature directory. Example:
|
||||||
|
```
|
||||||
|
curl -LO https://www.svgrepo.com/download/438371/checkmark-round.svg
|
||||||
|
sudo apt-get install librsvg2-bin
|
||||||
|
rsvg-convert -f pdf -o ~/.pdf_signatures/check.pdf checkmark-round.svg
|
||||||
|
```
|
||||||
|
|
||||||
## Why
|
## Why
|
||||||
|
|
||||||
There appears to be a lack of applications that run on Linux and allow for attaching free-hand signatures to PDF files in a good way.
|
There appears to be a lack of applications that run on Linux and allow for attaching free-hand signatures to PDF files in a good way.
|
||||||
|
|
98
pdf-from-text
Ausführbare Datei
98
pdf-from-text
Ausführbare Datei
|
@ -0,0 +1,98 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
#Dependencies: python3.7 or later and gs (Ghostscript)
|
||||||
|
|
||||||
|
import argparse, os, re, subprocess, sys, tempfile
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Create a correctly cropped PDF from one line of text. Only Latin-1 character set is supported.")
|
||||||
|
parser.add_argument("-t", "--text", required=True, help="Text to be converted to PDF.")
|
||||||
|
parser.add_argument("-o", "--output", required=True, help="Output PDF file.")
|
||||||
|
parser.add_argument("-s", "--size", type=int, default=12, help="Font size in points (default 12).")
|
||||||
|
parser.add_argument("-m", "--margin", type=int, default=1, help="Margin in points (default 1).")
|
||||||
|
args = parser.parse_args()
|
||||||
|
text_to_pdf(args.text, args.output, args.size, args.margin)
|
||||||
|
|
||||||
|
# Keep this function in sync with pdf-sign
|
||||||
|
def text_to_pdf(text, output, size, margin):
|
||||||
|
# Validate text
|
||||||
|
text_chars=set(map(ord, text))
|
||||||
|
latin1_chars=set([*range(0x20, 0x7f), *range(0xa0, 0x100)])
|
||||||
|
if not text_chars.issubset(latin1_chars):
|
||||||
|
die(f"Error: Only non-control latin-1 characters are supported. Unsupported characters: {', '.join(map(hex, text_chars - latin1_chars))}")
|
||||||
|
text.encode('latin-1').decode('latin-1') # Assertion. E.i., an exception here indicates a bug.
|
||||||
|
with tempfile.TemporaryDirectory() as tempdir:
|
||||||
|
# Write postscript file
|
||||||
|
ps_file=os.path.join(tempdir, "file.ps")
|
||||||
|
text_len=len(text)
|
||||||
|
w=text_len * size + margin * 2
|
||||||
|
h=size * 3 + margin * 2
|
||||||
|
x=size + margin
|
||||||
|
y=size + margin
|
||||||
|
ps_text = (text
|
||||||
|
.replace("\\", "\\\\")
|
||||||
|
.replace("(", "\\(")
|
||||||
|
.replace(")", "\\)")
|
||||||
|
)
|
||||||
|
def write_ps():
|
||||||
|
with open(ps_file, "w", encoding="latin-1") as f:
|
||||||
|
f.write('\n'.join([
|
||||||
|
"%!PS-Adobe-3.0",
|
||||||
|
f"%%BoundingBox: 0 0 {w} {h}",
|
||||||
|
"%%Pages: 1",
|
||||||
|
"%%EndComments",
|
||||||
|
"%%Page: 1 1",
|
||||||
|
f"/DejaVuSansMono findfont {size} scalefont setfont",
|
||||||
|
f"{x} {y} moveto",
|
||||||
|
f"({ps_text}) show",
|
||||||
|
"showpage"]))
|
||||||
|
# Write postscript file with too big bounding box
|
||||||
|
write_ps()
|
||||||
|
# Get correct bounding box
|
||||||
|
bbox_result = subprocess.run([
|
||||||
|
"gs", '-dBATCH', '-dNOPAUSE', '-dSAFER', '-dQUIET',
|
||||||
|
"-sDEVICE=bbox",
|
||||||
|
ps_file
|
||||||
|
], stderr=subprocess.PIPE, text=True, check=True)
|
||||||
|
bbox = m(r'.*%%HiResBoundingBox: (\d+(\.\d+)?) (\d+(\.\d+)?) (\d+(\.\d+)?) (\d+(\.\d+)?)\n.*', bbox_result.stderr)
|
||||||
|
if not bbox:
|
||||||
|
die("Error: Unable to extract bounding box.")
|
||||||
|
# Adjust variables for bounding box
|
||||||
|
llx, lly, urx, ury = float(bbox[1]), float(bbox[3]), float(bbox[5]), float(bbox[7])
|
||||||
|
llx, lly, urx, ury = llx - margin, lly - margin, urx + margin, ury + margin
|
||||||
|
w=urx - llx
|
||||||
|
h=ury - lly
|
||||||
|
x-=llx
|
||||||
|
y-=lly
|
||||||
|
# Write postscript file with correct bounding box
|
||||||
|
write_ps()
|
||||||
|
# Convert to PDF
|
||||||
|
gs_cmd = [
|
||||||
|
"gs", '-dBATCH', '-dNOPAUSE', '-dSAFER', '-dQUIET',
|
||||||
|
"-o", output,
|
||||||
|
"-sDEVICE=pdfwrite",
|
||||||
|
f"-dDEVICEWIDTHPOINTS={w}",
|
||||||
|
f"-dDEVICEHEIGHTPOINTS={h}",
|
||||||
|
"-dFIXEDMEDIA",
|
||||||
|
"-c", "[ /PAGES pdfmark",
|
||||||
|
"-f", ps_file
|
||||||
|
]
|
||||||
|
subprocess.run(gs_cmd, check=True, stdout=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
def m(pattern, string):
|
||||||
|
match = re.match(pattern, string, re.DOTALL)
|
||||||
|
if not match:
|
||||||
|
return None
|
||||||
|
if(match.group(0) != string):
|
||||||
|
die(f'Pattern /${pattern}/ matched only part of string')
|
||||||
|
ret = []
|
||||||
|
for index in range((match.lastindex or 0)+1):
|
||||||
|
ret.append(match.group(index))
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def die(reason):
|
||||||
|
print(reason, file=sys.stderr)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
171
pdf-sign
171
pdf-sign
|
@ -57,15 +57,48 @@ def main(args):
|
||||||
return outFile
|
return outFile
|
||||||
pageSize=Cell(lambda: pdfGetSize(str(pagePDF())))
|
pageSize=Cell(lambda: pdfGetSize(str(pagePDF())))
|
||||||
# The chosen signature
|
# The chosen signature
|
||||||
if not args.signature and args.batch:
|
if args.batch:
|
||||||
die('In batch mode, signature must be specified.')
|
if not args.signature and not args.text:
|
||||||
|
die('In batch mode, --signature or --text must be specified.')
|
||||||
|
if args.text and len(args.text) > 1:
|
||||||
|
die('In batch mode, --text must be given only once.')
|
||||||
|
if args.signature and args.text:
|
||||||
|
die('--signature and --text cannot be specified together.')
|
||||||
|
if args.text:
|
||||||
|
assert(len(args.text) > 0)
|
||||||
|
latin1_chars=set([*range(0x20, 0x7f), *range(0xa0, 0x100)])
|
||||||
|
for text in args.text:
|
||||||
|
text_chars=set(map(ord, text))
|
||||||
|
if not text_chars.issubset(latin1_chars):
|
||||||
|
die("Error: Only non-control latin-1 characters are supported in --text." +
|
||||||
|
" Unsupported characters: " + ', '.join(map(hex, text_chars - latin1_chars)))
|
||||||
signatureDir=getSignatureDir()
|
signatureDir=getSignatureDir()
|
||||||
signatures=[*filter(isPdfFilename, os.listdir(signatureDir))] if not args.signature else [None]
|
signatures=[('file', x) for x in filter(isPdfFilename, os.listdir(signatureDir))] if not args.signature else [None]
|
||||||
if not signatures:
|
if not signatures and not args.text:
|
||||||
die(f'No .pdf files found in {signatureDir}')
|
die(f'Could not find anything usable as signature, since no .pdf files found in {signatureDir} and no --text option given.')
|
||||||
signatures.sort()
|
signatures.sort()
|
||||||
|
if args.text:
|
||||||
|
signatures=[('text', x) for x in args.text] + signatures
|
||||||
signatureIndex=Cell(0)
|
signatureIndex=Cell(0)
|
||||||
signaturePath=Cell(lambda: args.signature if args.signature else os.path.join(signatureDir, signatures[signatureIndex()]))
|
customText=Cell(('text', time.strftime('%Y-%m-%d')))
|
||||||
|
@Cell
|
||||||
|
def signaturePath():
|
||||||
|
if args.signature:
|
||||||
|
return args.signature
|
||||||
|
(signType, content)=signatures[signatureIndex()]
|
||||||
|
if signType=='cell':
|
||||||
|
(signType, content)=content()
|
||||||
|
if signType=='file':
|
||||||
|
return os.path.join(signatureDir, content)
|
||||||
|
assert signType=='text'
|
||||||
|
cache = signaturePath._cache
|
||||||
|
if content in cache:
|
||||||
|
return cache[content]
|
||||||
|
fileName=os.path.join(tempdir, f"text{len(cache)}.pdf")
|
||||||
|
text_to_pdf(content, fileName, 12, 1)
|
||||||
|
cache[content]=fileName
|
||||||
|
return fileName
|
||||||
|
signaturePath._cache={}
|
||||||
signatureSize=Cell(lambda: pdfGetSize(signaturePath()))
|
signatureSize=Cell(lambda: pdfGetSize(signaturePath()))
|
||||||
signaturePositionX=Cell(args.x_coordinate)
|
signaturePositionX=Cell(args.x_coordinate)
|
||||||
signaturePositionY=Cell(args.y_coordinate)
|
signaturePositionY=Cell(args.y_coordinate)
|
||||||
|
@ -173,9 +206,11 @@ def main(args):
|
||||||
if gui:
|
if gui:
|
||||||
try:
|
try:
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
from tkinter import simpledialog
|
||||||
except ModuleNotFoundError:
|
except ModuleNotFoundError:
|
||||||
die('Cannot find Python module `tkinter`, which is needed for interactive use.')
|
die('Cannot find Python module `tkinter`, which is needed for interactive use.')
|
||||||
doSign=False
|
doSign=False
|
||||||
|
customTextIndex = len(signatures)
|
||||||
# Commands
|
# Commands
|
||||||
def uf(fun):
|
def uf(fun):
|
||||||
def ret():
|
def ret():
|
||||||
|
@ -208,6 +243,39 @@ def main(args):
|
||||||
if i<len(signatures):
|
if i<len(signatures):
|
||||||
signatureIndex(i)
|
signatureIndex(i)
|
||||||
update()
|
update()
|
||||||
|
def cmd_customText():
|
||||||
|
if args.signature:
|
||||||
|
return
|
||||||
|
# Get text from user
|
||||||
|
text = simpledialog.askstring(
|
||||||
|
"Custom Text",
|
||||||
|
"Input the text you want to stamp this PDF file with",
|
||||||
|
initialvalue=customText()[1])
|
||||||
|
if text == None:
|
||||||
|
return
|
||||||
|
# Validate text
|
||||||
|
text_chars=set(map(ord, text))
|
||||||
|
latin1_chars=set([*range(0x20, 0x7f), *range(0xa0, 0x100)])
|
||||||
|
if not text_chars.issubset(latin1_chars):
|
||||||
|
simpledialog.messagebox.showerror(
|
||||||
|
parent=root,
|
||||||
|
title="Invalid text",
|
||||||
|
message=f"Only non-control latin-1 characters are supported. Unsupported characters: {', '.join(map(hex, text_chars - latin1_chars))}"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if not (1 <= len(text) and len(text) <= 100):
|
||||||
|
simpledialog.messagebox.showerror(
|
||||||
|
parent=root,
|
||||||
|
title="Invalid text",
|
||||||
|
message="Text must be between 1 and 100 characters long."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
# Set text
|
||||||
|
customText(('text', text))
|
||||||
|
signatureIndex(customTextIndex)
|
||||||
|
label='Custom text: ' + (text if len(text) <= 20 else (text[:17] + '...'))
|
||||||
|
placemenu.entryconfig(len(signatures) + 2, label=label)
|
||||||
|
update()
|
||||||
def cmd_abort():
|
def cmd_abort():
|
||||||
root.destroy()
|
root.destroy()
|
||||||
def cmd_sign():
|
def cmd_sign():
|
||||||
|
@ -245,8 +313,22 @@ def main(args):
|
||||||
if root.signatureControlVar.get() != signatureIndex():
|
if root.signatureControlVar.get() != signatureIndex():
|
||||||
signatureIndex(root.signatureControlVar.get())
|
signatureIndex(root.signatureControlVar.get())
|
||||||
update()
|
update()
|
||||||
for index, filename in enumerate(signatures):
|
for index, (_, signature) in enumerate(signatures):
|
||||||
placemenu.add_radiobutton(value=index, label=f'{index+1}: {filename}', underline=0 if index<9 else None, variable=root.signatureControlVar, accelerator=(str(index+1) if index<9 else None), command=updateFromSignatureRadio)
|
placemenu.add_radiobutton(
|
||||||
|
value=index,
|
||||||
|
label=f'{index+1}: {signature}',
|
||||||
|
underline=0 if index<9 else None,
|
||||||
|
variable=root.signatureControlVar,
|
||||||
|
accelerator=(str(index+1) if index<9 else None),
|
||||||
|
command=updateFromSignatureRadio)
|
||||||
|
signatures.append(('cell', customText))
|
||||||
|
placemenu.add_radiobutton(
|
||||||
|
value=customTextIndex,
|
||||||
|
label='Custom text: ' + customText()[1],
|
||||||
|
underline=7,
|
||||||
|
variable=root.signatureControlVar,
|
||||||
|
accelerator='T',
|
||||||
|
command=cmd_customText)
|
||||||
placemenu.add_separator()
|
placemenu.add_separator()
|
||||||
placemenu.add_command(label='Enlarge signature', underline=0, accelerator='+', command=cmd_enlargeSignature)
|
placemenu.add_command(label='Enlarge signature', underline=0, accelerator='+', command=cmd_enlargeSignature)
|
||||||
placemenu.add_command(label='Shrink signature', underline=0, accelerator='-', command=cmd_shrinkSignature)
|
placemenu.add_command(label='Shrink signature', underline=0, accelerator='-', command=cmd_shrinkSignature)
|
||||||
|
@ -274,6 +356,8 @@ def main(args):
|
||||||
'q': cmd_abort,
|
'q': cmd_abort,
|
||||||
'S': cmd_sign,
|
'S': cmd_sign,
|
||||||
's': cmd_sign,
|
's': cmd_sign,
|
||||||
|
'T': cmd_customText,
|
||||||
|
't': cmd_customText,
|
||||||
'space': cmd_sign,
|
'space': cmd_sign,
|
||||||
}
|
}
|
||||||
def onkey(event):
|
def onkey(event):
|
||||||
|
@ -492,6 +576,72 @@ class Cell():
|
||||||
for d in self._dependents:
|
for d in self._dependents:
|
||||||
d._dirty()
|
d._dirty()
|
||||||
|
|
||||||
|
# Keep this function in sync with pdf-from-text
|
||||||
|
def text_to_pdf(text, output, size, margin):
|
||||||
|
# Validate text
|
||||||
|
text_chars=set(map(ord, text))
|
||||||
|
latin1_chars=set([*range(0x20, 0x7f), *range(0xa0, 0x100)])
|
||||||
|
if not text_chars.issubset(latin1_chars):
|
||||||
|
die(f"Error: Only non-control latin-1 characters are supported. Unsupported characters: {', '.join(map(hex, text_chars - latin1_chars))}")
|
||||||
|
text.encode('latin-1').decode('latin-1') # Assertion. E.i., an exception here indicates a bug.
|
||||||
|
with tempfile.TemporaryDirectory() as tempdir:
|
||||||
|
# Write postscript file
|
||||||
|
ps_file=os.path.join(tempdir, "file.ps")
|
||||||
|
text_len=len(text)
|
||||||
|
w=text_len * size + margin * 2
|
||||||
|
h=size * 3 + margin * 2
|
||||||
|
x=size + margin
|
||||||
|
y=size + margin
|
||||||
|
ps_text = (text
|
||||||
|
.replace("\\", "\\\\")
|
||||||
|
.replace("(", "\\(")
|
||||||
|
.replace(")", "\\)")
|
||||||
|
)
|
||||||
|
def write_ps():
|
||||||
|
with open(ps_file, "w", encoding="latin-1") as f:
|
||||||
|
f.write('\n'.join([
|
||||||
|
"%!PS-Adobe-3.0",
|
||||||
|
f"%%BoundingBox: 0 0 {w} {h}",
|
||||||
|
"%%Pages: 1",
|
||||||
|
"%%EndComments",
|
||||||
|
"%%Page: 1 1",
|
||||||
|
f"/DejaVuSansMono findfont {size} scalefont setfont",
|
||||||
|
f"{x} {y} moveto",
|
||||||
|
f"({ps_text}) show",
|
||||||
|
"showpage"]))
|
||||||
|
# Write postscript file with too big bounding box
|
||||||
|
write_ps()
|
||||||
|
# Get correct bounding box
|
||||||
|
bbox_result = subprocess.run([
|
||||||
|
"gs", '-dBATCH', '-dNOPAUSE', '-dSAFER', '-dQUIET',
|
||||||
|
"-sDEVICE=bbox",
|
||||||
|
ps_file
|
||||||
|
], stderr=subprocess.PIPE, text=True, check=True)
|
||||||
|
bbox = m(r'.*%%HiResBoundingBox: (\d+(\.\d+)?) (\d+(\.\d+)?) (\d+(\.\d+)?) (\d+(\.\d+)?)\n.*', bbox_result.stderr)
|
||||||
|
if not bbox:
|
||||||
|
die("Error: Unable to extract bounding box.")
|
||||||
|
# Adjust variables for bounding box
|
||||||
|
llx, lly, urx, ury = float(bbox[1]), float(bbox[3]), float(bbox[5]), float(bbox[7])
|
||||||
|
llx, lly, urx, ury = llx - margin, lly - margin, urx + margin, ury + margin
|
||||||
|
w=urx - llx
|
||||||
|
h=ury - lly
|
||||||
|
x-=llx
|
||||||
|
y-=lly
|
||||||
|
# Write postscript file with correct bounding box
|
||||||
|
write_ps()
|
||||||
|
# Convert to PDF
|
||||||
|
gs_cmd = [
|
||||||
|
"gs", '-dBATCH', '-dNOPAUSE', '-dSAFER', '-dQUIET',
|
||||||
|
"-o", output,
|
||||||
|
"-sDEVICE=pdfwrite",
|
||||||
|
f"-dDEVICEWIDTHPOINTS={w}",
|
||||||
|
f"-dDEVICEHEIGHTPOINTS={h}",
|
||||||
|
"-dFIXEDMEDIA",
|
||||||
|
"-c", "[ /PAGES pdfmark",
|
||||||
|
"-f", ps_file
|
||||||
|
]
|
||||||
|
subprocess.run(gs_cmd, check=True, stdout=subprocess.DEVNULL)
|
||||||
|
|
||||||
def tkthrottle(frequency, root):
|
def tkthrottle(frequency, root):
|
||||||
wait=1/frequency
|
wait=1/frequency
|
||||||
now=lambda: time.time()
|
now=lambda: time.time()
|
||||||
|
@ -568,7 +718,7 @@ def getSignatureHelp():
|
||||||
(path, helptxt) = getSignatureDirAndHelp()
|
(path, helptxt) = getSignatureDirAndHelp()
|
||||||
ret="""
|
ret="""
|
||||||
Path to file used as signature.
|
Path to file used as signature.
|
||||||
Required in batch mode.
|
Required in batch mode unless -t is given.
|
||||||
In GUI mode, the user can choose among PDF files in the signature directory.
|
In GUI mode, the user can choose among PDF files in the signature directory.
|
||||||
"""
|
"""
|
||||||
if path:
|
if path:
|
||||||
|
@ -584,10 +734,11 @@ parser.add_argument('-s', '--signature', type=str, help=getSignatureHelp())
|
||||||
parser.add_argument('-x', '--x-coordinate', type=float, default=0.5, help='Horizontal coordinate of signature center, in page width units. (default: 0.5)')
|
parser.add_argument('-x', '--x-coordinate', type=float, default=0.5, help='Horizontal coordinate of signature center, in page width units. (default: 0.5)')
|
||||||
parser.add_argument('-y', '--y-coordinate', type=float, default=0.75, help='Vertical coordinate of signature center, in page height units. (default: 0.75)')
|
parser.add_argument('-y', '--y-coordinate', type=float, default=0.75, help='Vertical coordinate of signature center, in page height units. (default: 0.75)')
|
||||||
parser.add_argument('-W', '--width', type=float, default=0.28, help='Width of box to fit signature to, in page width units. (default: 0.28)')
|
parser.add_argument('-W', '--width', type=float, default=0.28, help='Width of box to fit signature to, in page width units. (default: 0.28)')
|
||||||
parser.add_argument('-H', '--height', type=float, default=0.28, help='Height of box to fit signature to, in page height units. (default: 0.28)')
|
parser.add_argument('-H', '--height', type=float, default=0.11, help='Height of box to fit signature to, in page height units. (default: 0.11)')
|
||||||
parser.add_argument('-o', '--output', type=str, help='Output file. (default: Add ".signed" before the extension)')
|
parser.add_argument('-o', '--output', type=str, help='Output file. (default: Add ".signed" before the extension)')
|
||||||
parser.add_argument('-b', '--batch', action=argparse.BooleanOptionalAction, default=False, help='Batch mode: do not show GUI. (default: False)')
|
parser.add_argument('-b', '--batch', action=argparse.BooleanOptionalAction, default=False, help='Batch mode: do not show GUI. (default: False)')
|
||||||
parser.add_argument('-f', '--flatten', action=argparse.BooleanOptionalAction, default=True, help='Flatten before signing, preventing subsequent changes in PDF forms. (default: True)')
|
parser.add_argument('-f', '--flatten', action=argparse.BooleanOptionalAction, default=True, help='Flatten before signing, preventing subsequent changes in PDF forms. (default: True)')
|
||||||
parser.add_argument('-e', '--existing', type=str, choices=['backup', 'overwrite', 'fail'], default='backup', help='What to do if output file exists. (default: backup)')
|
parser.add_argument('-e', '--existing', type=str, choices=['backup', 'overwrite', 'fail'], default='backup', help='What to do if output file exists. (default: backup)')
|
||||||
|
parser.add_argument('-t', '--text', type=str, action='append', help='In GUI mode, a text option to be added to the list of signatures (can be repeated). In batch mode only one can be given, and will be used instead of --signature.')
|
||||||
|
|
||||||
main(parser.parse_args(sys.argv[1:] or ['-h']))
|
main(parser.parse_args(sys.argv[1:] or ['-h']))
|
||||||
|
|
Laden …
Tabelle hinzufügen
In neuem Issue referenzieren