parent
6ba1e15b0b
commit
cbcc1fff14
@ -0,0 +1,7 @@ |
||||
image: python:latest |
||||
|
||||
test: |
||||
stage: test |
||||
script: |
||||
- pip3 install pytest |
||||
- python3 -m pytest -v |
@ -0,0 +1,3 @@ |
||||
# backupill |
||||
|
||||
Backupill (Backup Pill), generates barcoded PDF to backup text files on paper. Designed to backup ASCII-armored GnuPG and SSH key files and ciphertext. |
@ -0,0 +1,52 @@ |
||||
#!/usr/bin/python3 |
||||
|
||||
""" |
||||
Copyright (C) 2021- beucismis |
||||
This program is free software: you can redistribute it and/or modify |
||||
it under the terms of the GNU General Public License as published by |
||||
the Free Software Foundation, either version 3 of the License, or |
||||
(at your option) any later version. |
||||
|
||||
This program is distributed in the hope that it will be useful, |
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
GNU General Public License for more details. |
||||
|
||||
You should have received a copy of the GNU General Public License |
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. |
||||
""" |
||||
|
||||
import os |
||||
import click |
||||
from .backuper import Pill |
||||
|
||||
|
||||
__version__ = "0.1.0" |
||||
__license__ = "GPL-3.0" |
||||
__author__ = "Adil Gurbuz" |
||||
__contact__ = "beucismis@tutamail.com" |
||||
__source__ = "https://github.com/beucismis/backupill" |
||||
__description__ = "Generates barcoded PDF to backup text files on paper." |
||||
|
||||
|
||||
@click.version_option(version=__version__) |
||||
@click.option("-f", "--file", help="ASC file path.") |
||||
@click.command(context_settings=dict(help_option_names=["-h", "--help"])) |
||||
def main(file): |
||||
"""Generates barcoded PDF to backup text files on paper. |
||||
https://github.com/beucismis/backupill |
||||
""" |
||||
|
||||
if file: |
||||
if not os.path.isfile(file): |
||||
click.echo("File '{}' not found!".format(file)) |
||||
exit() |
||||
|
||||
Pill(asc_file=file).generate_backup() |
||||
click.echo("Done!") |
||||
else: |
||||
click.echo("Usage: packupill -f FILENAME.asc") |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
main() |
@ -0,0 +1,110 @@ |
||||
import os |
||||
|
||||
# import re |
||||
import qrcode |
||||
|
||||
# import hashlib |
||||
import logging |
||||
from pyx import * |
||||
|
||||
# import subprocess |
||||
from tempfile import mkstemp |
||||
from datetime import datetime |
||||
|
||||
|
||||
TEXT_X_OFFSET = 0.6 |
||||
TEXT_Y_OFFSET = 8.2 |
||||
PLAINTEXT_MAXLINECHARS = 73 |
||||
|
||||
QRCODE_HEIGHT = 8 |
||||
QRCODE_PER_PAGE = 6 |
||||
QRCODE_MAX_BYTE = 140 |
||||
QRCODE_X_POS = [1.5, 11, 1.5, 11, 1.5, 11] |
||||
QRCODE_Y_POS = [18.7, 18.7, 10, 10, 1.2, 1.2] |
||||
|
||||
PF_STR = "A4" |
||||
PF_OBJ = document.paperformat.A4 |
||||
|
||||
# suppressing all the warnings |
||||
for name in logging.Logger.manager.loggerDict.keys(): |
||||
logging.getLogger(name).setLevel(logging.CRITICAL) |
||||
|
||||
|
||||
class Pill: |
||||
def __init__(self, asc_file): |
||||
self.ASC_FILE = asc_file |
||||
|
||||
self.pageno = 0 |
||||
self.pageid = 0 |
||||
self.code_blocks = [] |
||||
|
||||
self._init_pdf() |
||||
|
||||
with open(asc_file) as file: |
||||
self.ASCDATA = file.read() |
||||
|
||||
def _init_pdf(self): |
||||
unit.set(defaultunit="cm") |
||||
self.pdf = document.document() |
||||
|
||||
def _generate_barcode(self, chunkdata): |
||||
qr = qrcode.QRCode( |
||||
version=1, |
||||
border=4, |
||||
box_size=10, |
||||
error_correction=qrcode.constants.ERROR_CORRECT_L, |
||||
) |
||||
qr.add_data(chunkdata) |
||||
qr.make(fit=True) |
||||
|
||||
im = qr.make_image(fill_color="black", back_color="white") |
||||
|
||||
return im |
||||
|
||||
def _finish_page(self, pdf, canvas, pageno): |
||||
canvas.text(10, 0.6, "Page {}".format(pageno + 1)) |
||||
pdf.append(document.page(canvas, paperformat=PF_OBJ, fittosize=0, centered=0)) |
||||
|
||||
def generate_backup(self): |
||||
chunkdata = "^1 " |
||||
c = canvas.canvas() |
||||
|
||||
for char in list(self.ASCDATA): |
||||
if len(chunkdata) + 1 > QRCODE_MAX_BYTE: |
||||
self.code_blocks.append(self._generate_barcode(chunkdata)) |
||||
chunkdata = "^" + str(len(self.code_blocks) + 1) + " " |
||||
chunkdata += char |
||||
|
||||
self.code_blocks.append(self._generate_barcode(chunkdata)) |
||||
|
||||
for bc in range(len(self.code_blocks)): |
||||
if self.pageid >= QRCODE_PER_PAGE: |
||||
self._finish_page(self.pdf, c, self.pageno) |
||||
c = canvas.canvas() |
||||
self.pageno += 1 |
||||
self.pageid = 0 |
||||
|
||||
c.text( |
||||
QRCODE_X_POS[self.pageid] + TEXT_X_OFFSET, |
||||
QRCODE_Y_POS[self.pageid] + TEXT_Y_OFFSET, |
||||
"{} ({}/{})".format( |
||||
text.escapestring(self.ASC_FILE), bc + 1, len(self.code_blocks) |
||||
), |
||||
) |
||||
|
||||
c.insert( |
||||
bitmap.bitmap( |
||||
QRCODE_X_POS[self.pageid], |
||||
QRCODE_Y_POS[self.pageid], |
||||
self.code_blocks[bc], |
||||
height=QRCODE_HEIGHT, |
||||
) |
||||
) |
||||
|
||||
self.pageid += 1 |
||||
|
||||
self._finish_page(self.pdf, c, self.pageno) |
||||
|
||||
fd, temp_barcode_path = mkstemp(".pdf", "qr_", ".") |
||||
self.pdf.writetofile(temp_barcode_path) |
||||
os.rename(temp_barcode_path.split(os.sep)[-1], self.ASC_FILE + ".pdf") |
@ -0,0 +1,3 @@ |
||||
# backupill-gui |
||||
|
||||
|
@ -0,0 +1,4 @@ |
||||
twine |
||||
black |
||||
pytest |
||||
virtualenv |
@ -0,0 +1,4 @@ |
||||
pyx |
||||
click |
||||
qrcode |
||||
pillow |
@ -0,0 +1,34 @@ |
||||
import sys |
||||
import backupill as bp |
||||
from setuptools import setup |
||||
|
||||
|
||||
if sys.version_info < (3, 5): |
||||
raise RuntimeError("backupill requires Python 3.5 or later") |
||||
|
||||
with open("README.md") as f: |
||||
long_description = f.read() |
||||
|
||||
|
||||
setup( |
||||
name="backupill", |
||||
packages=["backupill"], |
||||
version=bp.__version__, |
||||
description=bp.__description__, |
||||
long_description=long_description, |
||||
long_description_content_type="text/markdown", |
||||
url=bp.__source__, |
||||
author=bp.__author__, |
||||
author_email=bp.__contact__, |
||||
license=bp.__license__, |
||||
classifiers=[], |
||||
platforms=["Linux"], |
||||
python_requires=">=3.5", |
||||
install_requires=["pyx", "click", "qrcode"], |
||||
keywords=[], |
||||
entry_points={ |
||||
"console_scripts": [ |
||||
"backupill = backupill:main", |
||||
], |
||||
}, |
||||
) |
@ -0,0 +1,52 @@ |
||||
-----BEGIN PGP PUBLIC KEY BLOCK----- |
||||
|
||||
mQINBGB0fWIBEAC7p3xVDdWdQsLM4kt8laiY3XBynlShQh0OA7ppZPC793OgaQy1 |
||||
7ao62IqZmZR8bph7e9obrAKwWqdR7wtvePVIa18GNQTBjZOB8D10HHesowPWBAGM |
||||
rB25jxyuZ2CmolWIzGVy9oURs09kqO5QiXxHNhRu5hiwb1PtpkIslXqKjCohgAMu |
||||
khdnutN8mWaE1aSKrr0fa76MVCF49B2eFhg5YydrwLM/z/1tpFFVatgiPVqOgEE5 |
||||
fwsdfcABhWakqIAbBrjw3IAt7RDmqAh75qaW+17yHVP/mVNG/cNp+hBtnm4SM2sI |
||||
maIV5gkKe2BfxlEAir2QqTcoAyzWs8mNiO5XodBVwRSEKE02Qwyt62uIcBVh7QZ4 |
||||
Q0qJoqTVvATVfAOMG76yUjqROAI73ZkV3LQHoCjozXRe63v5YK+iPZ7cACuhHSqj |
||||
/ZCQvKse8UNZiZ14fzNizKaGrHQ/0RxzNY6TrxD88B5UJwy+4lRxYHm1PwcGYkoM |
||||
K0VBhL02BVE7tPe8XIizcweWlnYb/ko3Z+KgNhQlUp3Ky8kEBXBYR+F5Xj+wreCF |
||||
zGmSQAajkajv4kKu5rdJaBN3/WZhgfptIeuqkAQQUQo/eRaPt2TDmStYXo5r+dm8 |
||||
1TIvDjUHrc9TLlCDTQQzkj3NrjWUrrMYdZADWnGKcBzihoSYIvBbOZmqGQARAQAB |
||||
tCFOYW1lIFN1cm5hbWUgPG1lQG5hbWVzdXJuYW1lLm9yZz6JAlQEEwEKAD4WIQSn |
||||
qd+GR9yJUihnsapJIpwOg6fLNgUCYHR9YgIbAwUJAAFRgAULCQgHAgYVCgkICwIE |
||||
FgIDAQIeAQIXgAAKCRBJIpwOg6fLNnftD/9LZ80Dr9Z9XueKpAy+Bh9pYs1NWXvp |
||||
AyOc9snezlTKml7a9uEGpRYqD5vsWWBqX9IXj44vD1M+JaQtvY8Qs9/0c4+NXucS |
||||
/2ED81YSMgEKkIz+/asnDFpwoKJJZBy/SPCUeYLMlNWqlt3H+ZouN/o8g718cMI1 |
||||
3Jpg60HwV3Iadub0zF415Kk2+yDRxcTske+M3oTJlUO4RNuXKYnUWP+yw5MAfLGg |
||||
lxNn8mZZ8P6pFLqmjdssFs4cofs1kAdzaRXJ6Ip7EvfQuTTwL570nfaVxrqj3jvt |
||||
dOFmQmxTkrkYsZvv4neT+hnMfhge1d2PULBxknkKe0EZosj3G5+bGPxLqQlbFgXf |
||||
XPFb6bZn1EFvmBZlgRXvNvV2QmgshbJjoTdLpjKLkb9K9SahGDWhd3xdWll1e3z5 |
||||
DuzWDPQUWmasi680nwxfRRe8A7lcKQACR3akiVzS97hpAGQjXLE7D6/mzRt29zj6 |
||||
9eGputnyMNk/24CKTRYNTBseLaZoqeJIunfCpxSyZuG7/TICHQAdwFAIfW1pwOYA |
||||
otVBSm75L9RuhxtjD0z47Kc24nOV5L2OXCdtUuaw6to0VSxjXc/99rxc+Q6XgEZd |
||||
sZIYtfKNOCrgTv0v1zfq4+8ewFvKwUZE+MRhHMrfKE8IXEfTeD1XppbpVcL22hL2 |
||||
MAtK3iYmwsI40LkCDQRgdH1iARAAsKqQ9aZfSGME7RQWYh6h767v0Oi1H2D1EbLU |
||||
tuonY8mmF8r4hf7JkqexLJMzJJfGVOOS9bek2Z8x0g0IgOF2qTjPGsgQRX6tyItF |
||||
GXuJJbVZ9Gz+ByVzIQ9QcKlTYUjMK7wZvFKTM7G2whRV+L47jLCpYDY4Xjmy960P |
||||
DEb9gguhSLNvaEy1MClOnCPTafuVZ0TjvndSDRKYL/22UAKvv5h6bhZReoA4mtcb |
||||
7vmI9QJ2AWkDWtYVUfquIIOmPcBTnlyii/9a7NdgTl7bE5qmgOlsHRhD8iScDl+T |
||||
Vzp13PKaIxV+lgctfdDdLnsS5a8CWp99SwwnJ5oj4GFOuk+U4oPFXTmu3RCQBBHf |
||||
o1cFq4We2RMnrx4yEEjZntcXueZT3tuTcAxakFWWZzk2flacaPLvuOAAW895kw1m |
||||
uF0aWy/c88ofJBTMXOsIUUGLSYxLRfiyLjp6P/IPS3azCWoKtNTioeYrjAEHuTc0 |
||||
PjsKXn/d9DC3HRiC6CMUY6y386y91MRRXxqGLn9A05dK61zAWwquc/B322obRwHk |
||||
ZfrEd5B5K0ZRpCwTtjPLDLJ/6P9AAURwaXgFBmOf90EkZZJ5BIssxeTlIoc10qwi |
||||
KRb8pKcvDJ3sgLClGVNaXmQiqbx5f8ZZ0ArXt4mNMq+688Ssnh1k9VSMHdbDhI4u |
||||
uK5Y310AEQEAAYkCPAQYAQoAJhYhBKep34ZH3IlSKGexqkkinA6Dp8s2BQJgdH1i |
||||
AhsMBQkAAVGAAAoJEEkinA6Dp8s2BKsQAKrRbgx+jLvU7q4MoKHKyPOCnS0s3Qp9 |
||||
eoNSEb8VD95yZr/lmIctcQoGqYNL9hbWgOK78+xpUu9lDM+NMcqrPBQ4xI4PsaD1 |
||||
VzO80p5ac4dQOcTsjvhbnN/v7TNpzShc6lTJpvbfYoqAMK4tg6zrpJkDdnJS+Pnt |
||||
64Vt5N8qzjMOowoT825ihYqHqtG0GeasVJ/hfRaEUwJKGhsIlGdDigBqiKXaBGQI |
||||
jH/f9NNFRDudy/rOTayT2M6RYUFdAAieX4D8jbuRIZJRddlVQBgx3WZNvt8jeE50 |
||||
7EqVpGUPO5xFWU9BTLAb2B97Ng5CT4rpn4BGRMRIGnJJu3Y1chZGLgQ09zlLp0JK |
||||
ItX9X7RgJ8HSRfu4Nyq8iyK7BldXPV+66mvwiYmbkJ546AeHDy+Bh4kykF7TOl7N |
||||
z/+yu1WPEEOSV+SMV/xmxc4/KKX3cxgHsMxMxWy5P6byzw328kW8wtufUEdRmdoW |
||||
l/8rMZbtlCMHgjYzqYxLjK+IGi8lurp7Yr0sLpl1mlBKN4EjelT4fKcbe+Z/NJm9 |
||||
Y+U+P79s3pGGcJOfvZPXGDq8TB6ow571149Kg9pRLtNCB7tkk12UKkZ4piRvCYS0 |
||||
ZEeIqKq0s061gdnqC0J4fozh+XmAS4h90kUG1G1Zyuo4wDqVz4mJ6ditkD2OtIMR |
||||
dRYbPoN2pT4P |
||||
=XYxH |
||||
-----END PGP PUBLIC KEY BLOCK----- |
Binary file not shown.
@ -0,0 +1,11 @@ |
||||
import os |
||||
import pytest |
||||
from backupill import Pill |
||||
|
||||
|
||||
this_dir, this_filename = os.path.split(__file__) |
||||
|
||||
|
||||
def test_generate_backup(): |
||||
pill = Pill(asc_file=os.path.join(this_dir, "test.asc")) |
||||
pill.generate_backup() |
Loading…
Reference in new issue