Holger Frey
6 years ago
10 changed files with 543 additions and 0 deletions
@ -0,0 +1,63 @@ |
|||||||
|
.PHONY: clean clean-test clean-pyc clean-build help |
||||||
|
.DEFAULT_GOAL := help |
||||||
|
|
||||||
|
define BROWSER_PYSCRIPT |
||||||
|
import os, webbrowser, sys |
||||||
|
|
||||||
|
try: |
||||||
|
from urllib import pathname2url |
||||||
|
except: |
||||||
|
from urllib.request import pathname2url |
||||||
|
|
||||||
|
webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) |
||||||
|
endef |
||||||
|
export BROWSER_PYSCRIPT |
||||||
|
|
||||||
|
define PRINT_HELP_PYSCRIPT |
||||||
|
import re, sys |
||||||
|
|
||||||
|
for line in sys.stdin: |
||||||
|
match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) |
||||||
|
if match: |
||||||
|
target, help = match.groups() |
||||||
|
print("%-20s %s" % (target, help)) |
||||||
|
endef |
||||||
|
export PRINT_HELP_PYSCRIPT |
||||||
|
|
||||||
|
BROWSER := python -c "$$BROWSER_PYSCRIPT" |
||||||
|
|
||||||
|
help: |
||||||
|
@python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) |
||||||
|
|
||||||
|
clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts
|
||||||
|
|
||||||
|
clean-build: ## remove build artifacts
|
||||||
|
rm -fr build/ |
||||||
|
rm -fr dist/ |
||||||
|
rm -fr .eggs/ |
||||||
|
find . -name '*.egg-info' -exec rm -fr {} + |
||||||
|
find . -name '*.egg' -exec rm -f {} + |
||||||
|
|
||||||
|
clean-pyc: ## remove Python file artifacts
|
||||||
|
find . -name '*.pyc' -exec rm -f {} + |
||||||
|
find . -name '*.pyo' -exec rm -f {} + |
||||||
|
find . -name '*~' -exec rm -f {} + |
||||||
|
find . -name '__pycache__' -exec rm -fr {} + |
||||||
|
|
||||||
|
clean-test: ## remove test and coverage artifacts
|
||||||
|
rm -fr .tox/ |
||||||
|
rm -f .coverage |
||||||
|
rm -fr htmlcov/ |
||||||
|
|
||||||
|
lint: ## check style with flake8
|
||||||
|
black pdftools tests |
||||||
|
flake8 pdftools tests |
||||||
|
|
||||||
|
test: ## run tests quickly with the default Python
|
||||||
|
py.test -x --disable-warnings |
||||||
|
|
||||||
|
coverage: ## check code coverage with the default Python
|
||||||
|
coverage run --source pdftools -m pytest |
||||||
|
coverage report -m |
||||||
|
coverage html |
||||||
|
$(BROWSER) htmlcov/index.html |
@ -0,0 +1,89 @@ |
|||||||
|
import click |
||||||
|
|
||||||
|
from reportlab.lib.units import mm |
||||||
|
|
||||||
|
from . import nameplate |
||||||
|
from . import ruler |
||||||
|
|
||||||
|
|
||||||
|
@click.command() |
||||||
|
@click.argument( |
||||||
|
"attendees", |
||||||
|
type=click.Path( |
||||||
|
exists=True, |
||||||
|
file_okay=True, |
||||||
|
dir_okay=False, |
||||||
|
writable=False, |
||||||
|
readable=True, |
||||||
|
), |
||||||
|
) |
||||||
|
@click.argument( |
||||||
|
"logo", |
||||||
|
type=click.Path( |
||||||
|
exists=True, |
||||||
|
file_okay=True, |
||||||
|
dir_okay=False, |
||||||
|
writable=False, |
||||||
|
readable=True, |
||||||
|
), |
||||||
|
required=False, |
||||||
|
) |
||||||
|
@click.option( |
||||||
|
"-s", |
||||||
|
"--size", |
||||||
|
type=click.INT, |
||||||
|
required=False, |
||||||
|
default=10, |
||||||
|
show_default="10 mm", |
||||||
|
) |
||||||
|
@click.option( |
||||||
|
"-y", |
||||||
|
"--adjust_y", |
||||||
|
type=click.INT, |
||||||
|
required=False, |
||||||
|
default=0, |
||||||
|
show_default=" 0 mm", |
||||||
|
) |
||||||
|
@click.option( |
||||||
|
"-x", |
||||||
|
"--adjust_x", |
||||||
|
type=click.INT, |
||||||
|
required=False, |
||||||
|
default=0, |
||||||
|
show_default=" 0 mm", |
||||||
|
) |
||||||
|
def nameplates(attendees, logo, size, adjust_x, adjust_y): |
||||||
|
""" creates a pdf with name plate cards |
||||||
|
|
||||||
|
The attendees file should be a tab separated text file with no headers. Put |
||||||
|
the company name in the first column, the given name in the second and the |
||||||
|
last name in the third column, e.g: |
||||||
|
|
||||||
|
Hochimin Enterprizes \\t Jane \\t Doe \\n |
||||||
|
|
||||||
|
|
||||||
|
The positioning of the optional logo file can be controlled with the |
||||||
|
size and adjustment options. |
||||||
|
""" |
||||||
|
if logo is not None: |
||||||
|
logo = nameplate.Logo(logo, size * mm, adjust_x=adjust_x, adjust_y=adjust_y) |
||||||
|
npc = nameplate.NamePlateCards(logo) |
||||||
|
npc.generate(attendees, show=True) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@click.command() |
||||||
|
@click.argument( |
||||||
|
"pdf", |
||||||
|
type=click.Path( |
||||||
|
exists=True, |
||||||
|
file_okay=True, |
||||||
|
dir_okay=False, |
||||||
|
writable=False, |
||||||
|
readable=True, |
||||||
|
), |
||||||
|
) |
||||||
|
def addruler(pdf): |
||||||
|
""" adds a blue ruler overlay to a pdf file """ |
||||||
|
ruler.ruler_overlay(pdf) |
@ -0,0 +1,101 @@ |
|||||||
|
import pdfrw |
||||||
|
import subprocess |
||||||
|
import itertools |
||||||
|
import tempfile |
||||||
|
|
||||||
|
from pathlib import Path |
||||||
|
from reportlab.pdfgen import canvas |
||||||
|
from reportlab.lib.units import mm |
||||||
|
from reportlab.lib.pagesizes import A4 |
||||||
|
|
||||||
|
|
||||||
|
def parse_text_file(text_file, ignore_first_line=True): |
||||||
|
with open(text_file) as fh: |
||||||
|
if ignore_first_line: |
||||||
|
next(fh) |
||||||
|
lines = [line.strip() for line in fh] |
||||||
|
|
||||||
|
parts = [line.split("\t") for line in lines if line] |
||||||
|
return [tuple(map(str.strip, p)) for p in parts] |
||||||
|
|
||||||
|
|
||||||
|
def fill(original_form, draw_function, values, output_file="filled.pdf"): |
||||||
|
p = Path(output_file) |
||||||
|
if p.exists(): |
||||||
|
p.unlink() |
||||||
|
|
||||||
|
with tempfile.TemporaryFile() as overlay_fh: |
||||||
|
with tempfile.TemporaryFile() as multipage_fh: |
||||||
|
|
||||||
|
# first create the pages with the values at the right positions |
||||||
|
# this must be done first, because we need to know the number of |
||||||
|
# pages |
||||||
|
c = canvas.Canvas(overlay_fh, pagesize=A4) |
||||||
|
for items in values: |
||||||
|
draw_function(c, items) |
||||||
|
c.showPage() |
||||||
|
c.save() |
||||||
|
overlay_fh.seek(0) |
||||||
|
|
||||||
|
olay = pdfrw.PdfReader(overlay_fh) |
||||||
|
|
||||||
|
# create a temporary pdf that has as many pages as the filled in |
||||||
|
# values |
||||||
|
original_form = pdfrw.PdfReader(original_form) |
||||||
|
writer = pdfrw.PdfWriter() |
||||||
|
for i in range(len(olay.pages)): |
||||||
|
writer.addpages(original_form.pages) |
||||||
|
writer.write(multipage_fh) |
||||||
|
multipage_fh.seek(0) |
||||||
|
|
||||||
|
# merge the overlay and the multipage form |
||||||
|
form = pdfrw.PdfReader(multipage_fh) |
||||||
|
for form_page, overlay_page in zip(form.pages, olay.pages): |
||||||
|
merge_obj = pdfrw.PageMerge() |
||||||
|
filled = merge_obj.add(overlay_page)[0] |
||||||
|
pdfrw.PageMerge(form_page).add(filled).render() |
||||||
|
|
||||||
|
# write the combined file to the output |
||||||
|
pdfrw.PdfWriter().write(output_file, form) |
||||||
|
subprocess.run(["open", output_file]) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=="__main__": |
||||||
|
|
||||||
|
attend = parse_text_file("teilnehmertabelle.txt") |
||||||
|
|
||||||
|
def create_overlay(c, item): |
||||||
|
company, addr, name = item |
||||||
|
c.setFont("Helvetica", 11) |
||||||
|
c.drawString( 75*mm, 234*mm, "Universität Freiburg, IMTEK, CPI") |
||||||
|
c.drawString(118*mm, 225*mm, "20126 N") |
||||||
|
c.drawString( 58*mm, 216*mm, "VDK-Bestimmung in Jungbier") |
||||||
|
|
||||||
|
if company.startswith("Wissenschaftsförderung"): |
||||||
|
c.setFont("Helvetica", 10) |
||||||
|
c.drawString( 35*mm, 204*mm, f"{company}, {addr}") |
||||||
|
if company.startswith("Wissenschaftsförderung"): |
||||||
|
c.setFont("Helvetica", 11) |
||||||
|
c.drawString( 35*mm, 191*mm, f"19.02.2019") |
||||||
|
c.drawString(113*mm, 191*mm, f"10:30 Uhr") |
||||||
|
c.drawString(150*mm, 191*mm, f"16:00 Uhr") |
||||||
|
|
||||||
|
c.drawString( 25.8*mm, 177.75*mm, f"X") |
||||||
|
|
||||||
|
c.drawString( 35*mm, 169*mm, name) |
||||||
|
c.drawString( 35*mm, 156*mm, "Teilname Kickoff-Meeting") |
||||||
|
|
||||||
|
c.drawString( 37.15*mm, 134.35*mm, f"X") |
||||||
|
c.drawString( 54*mm, 134*mm, "1") |
||||||
|
|
||||||
|
c.drawString( 24*mm, 82*mm, "Freiburg, den 19.02.2019") |
||||||
|
|
||||||
|
c.setFont("Helvetica-Bold", 12) |
||||||
|
c.drawString( 117*mm, 100*mm, "1.000") |
||||||
|
|
||||||
|
#fill("form.pdf", create_overlay, attend) |
||||||
|
ruler_overlay("form.pdf") |
After Width: | Height: | Size: 49 KiB |
@ -0,0 +1,141 @@ |
|||||||
|
import subprocess |
||||||
|
import itertools |
||||||
|
|
||||||
|
from pathlib import Path |
||||||
|
from PIL import Image |
||||||
|
from reportlab.pdfgen import canvas |
||||||
|
from reportlab.lib.pagesizes import A4 |
||||||
|
from reportlab.lib.units import mm |
||||||
|
|
||||||
|
|
||||||
|
class Logo: |
||||||
|
def __init__(self, path, target_height, adjust_x=0, adjust_y=0): |
||||||
|
self.path = path |
||||||
|
self.height = target_height |
||||||
|
self.width = self._scale_width(target_height) |
||||||
|
self.adjust_x = adjust_x |
||||||
|
self.adjust_y = adjust_y |
||||||
|
self.size = {"width": self.width, "height": self.height} |
||||||
|
|
||||||
|
def _scale_width(self, height): |
||||||
|
img = Image.open(self.path) |
||||||
|
img_w2h = img.width / img.height |
||||||
|
return height * img_w2h |
||||||
|
|
||||||
|
|
||||||
|
_tmp_pos = [ |
||||||
|
# upper card |
||||||
|
39 + 114, |
||||||
|
# lower card |
||||||
|
39, |
||||||
|
] |
||||||
|
y_positions = itertools.cycle([rp * mm for rp in _tmp_pos]) |
||||||
|
y_height = 50 * mm |
||||||
|
y_padding = 5 * mm |
||||||
|
|
||||||
|
x_pos = 55 * mm |
||||||
|
x_width = 100 * mm |
||||||
|
x_padding = 10 * mm |
||||||
|
|
||||||
|
false_and_true = itertools.cycle([False, True]) |
||||||
|
|
||||||
|
imtek_logo_path = Path(__file__).parent / "logo_imtek.png" |
||||||
|
|
||||||
|
class NamePlateCards: |
||||||
|
def __init__( |
||||||
|
self, |
||||||
|
partner_logo=None, |
||||||
|
partner_color=None, |
||||||
|
name_size=24, |
||||||
|
company_size=12, |
||||||
|
debug=False, |
||||||
|
): |
||||||
|
self.imtek_logo = Logo(imtek_logo_path, 10 * mm) |
||||||
|
self.imtek_color = self._color_from_rgb(23, 17, 117) |
||||||
|
self.partner_logo = partner_logo |
||||||
|
if partner_color is None: |
||||||
|
self.partner_color = self._guess_partner_color() |
||||||
|
else: |
||||||
|
self.partner_color = self._color_from_rgb(partner_color) |
||||||
|
self.name_size = name_size |
||||||
|
self.company_size = company_size |
||||||
|
self.debug = debug |
||||||
|
self._canvas = None |
||||||
|
|
||||||
|
def generate(self, attendees_file, output_file="output.pdf", show=True): |
||||||
|
attendees = self.parse_attendees(attendees_file) |
||||||
|
for page_break, entry in zip(false_and_true, attendees): |
||||||
|
self.draw_card(*entry) |
||||||
|
if page_break: |
||||||
|
self.canvas.showPage() |
||||||
|
self.canvas.save() |
||||||
|
if show: |
||||||
|
subprocess.run(["open", output_file]) |
||||||
|
|
||||||
|
@property |
||||||
|
def canvas(self): |
||||||
|
if self._canvas is None: |
||||||
|
self._canvas = canvas.Canvas("output.pdf", pagesize=A4) |
||||||
|
return self._canvas |
||||||
|
|
||||||
|
def _color_from_rgb(self, *args): |
||||||
|
return tuple(x / 255 for x in args) |
||||||
|
|
||||||
|
def _guess_partner_color(self): |
||||||
|
if self.partner_logo is None: |
||||||
|
return (0, 0, 0) |
||||||
|
img = Image.open(self.partner_logo.path) |
||||||
|
colors = sorted(img.getcolors(img.width * img.height), reverse=True) |
||||||
|
if colors is None: |
||||||
|
return (0, 0, 0) |
||||||
|
else: |
||||||
|
return self._color_from_rgb(*colors[0][1]) |
||||||
|
|
||||||
|
def parse_attendees(self, attendees_file): |
||||||
|
with open(attendees_file, "r") as fh: |
||||||
|
lines = [l.strip() for l in fh] |
||||||
|
splited = [line.split("\t") for line in lines if line] |
||||||
|
return [(c.strip(), f.strip(), l.strip()) for c, f, l in splited] |
||||||
|
|
||||||
|
def box_helper(self): |
||||||
|
if self.debug: |
||||||
|
self.canvas.setStrokeColorRGB(1, 0, 0) |
||||||
|
self.canvas.rect(0, 0, x_width, y_height) |
||||||
|
|
||||||
|
def draw_card(self, company, first_name, last_name): |
||||||
|
self.canvas.saveState() |
||||||
|
y_pos = next(y_positions) |
||||||
|
self.canvas.translate(x_pos, y_pos) |
||||||
|
self.box_helper() |
||||||
|
self.draw_field(company, first_name, last_name) |
||||||
|
# next translate is relative to the first |
||||||
|
self.canvas.translate(x_width, y_height * 2) |
||||||
|
self.canvas.rotate(180) |
||||||
|
self.box_helper() |
||||||
|
self.draw_field(company, first_name, last_name) |
||||||
|
self.canvas.restoreState() |
||||||
|
|
||||||
|
def draw_field(self, company, first_name, last_name): |
||||||
|
self.draw_logo(self.imtek_logo, "left") |
||||||
|
if self.partner_logo is not None: |
||||||
|
self.draw_logo(self.partner_logo, "right") |
||||||
|
self.canvas.setFillColorRGB(*self.imtek_color) |
||||||
|
self.canvas.setFont("Helvetica-Bold", self.name_size) |
||||||
|
self.canvas.drawString(x_padding, y_padding + 18 * mm, first_name) |
||||||
|
self.canvas.drawString(x_padding, y_padding + 8 * mm, last_name) |
||||||
|
self.canvas.setFillColorRGB(*self.partner_color) |
||||||
|
self.canvas.setFont("Helvetica-Bold", self.company_size) |
||||||
|
self.canvas.drawString(x_padding, y_padding, company) |
||||||
|
|
||||||
|
def draw_logo(self, img, position="left"): |
||||||
|
if position == "left": |
||||||
|
x_left = x_padding |
||||||
|
else: |
||||||
|
x_left = x_width - x_padding - img.width |
||||||
|
self.canvas.drawImage( |
||||||
|
img.path, |
||||||
|
x_left + img.adjust_x, |
||||||
|
y_height - y_padding - img.height + img.adjust_y, |
||||||
|
**img.size, |
||||||
|
mask="auto" |
||||||
|
) |
@ -0,0 +1,26 @@ |
|||||||
|
from pathlib import Path |
||||||
|
from reportlab.lib.units import mm |
||||||
|
|
||||||
|
from . import formfill |
||||||
|
|
||||||
|
|
||||||
|
def ruler_overlay(original_form): |
||||||
|
|
||||||
|
def ruler(c, item): |
||||||
|
c.setStrokeColorRGB(0, 0, 1) |
||||||
|
c.setFillColorRGB(0, 0, 1) |
||||||
|
c.setLineWidth(0.5) |
||||||
|
c.setFont("Helvetica", 8) |
||||||
|
x_grid = [x*mm for x in range(10, 210, 10)] |
||||||
|
y_grid = [y*mm for y in range(10, 297, 10)] |
||||||
|
c.grid(x_grid, y_grid) |
||||||
|
|
||||||
|
for x in range(10, 210, 10): |
||||||
|
c.drawString((x-2)*mm, 5*mm, str(x)) |
||||||
|
for y in range(10, 297, 10): |
||||||
|
c.drawString(4*mm, (y-0.5)*mm, str(y)) |
||||||
|
|
||||||
|
ofp = Path(original_form) |
||||||
|
out_path = ofp.parent / f"{ofp.stem}_ruler.pdf" |
||||||
|
|
||||||
|
formfill.fill(original_form, ruler, [1], output_file=out_path) |
@ -0,0 +1,44 @@ |
|||||||
|
[[package]] |
||||||
|
category = "main" |
||||||
|
description = "Composable command line interface toolkit" |
||||||
|
name = "click" |
||||||
|
optional = false |
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" |
||||||
|
version = "7.0" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
category = "main" |
||||||
|
description = "PDF file reader/writer library" |
||||||
|
name = "pdfrw" |
||||||
|
optional = false |
||||||
|
python-versions = "*" |
||||||
|
version = "0.4" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
category = "main" |
||||||
|
description = "Python Imaging Library (Fork)" |
||||||
|
name = "pillow" |
||||||
|
optional = false |
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" |
||||||
|
version = "5.4.1" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
category = "main" |
||||||
|
description = "The Reportlab Toolkit" |
||||||
|
name = "reportlab" |
||||||
|
optional = false |
||||||
|
python-versions = "*" |
||||||
|
version = "3.5.13" |
||||||
|
|
||||||
|
[package.dependencies] |
||||||
|
pillow = ">=4.0.0" |
||||||
|
|
||||||
|
[metadata] |
||||||
|
content-hash = "02a2ad8ff93804f7fa328d882f2dae2863ffa491831651d312cc368638333ddc" |
||||||
|
python-versions = "^3.7" |
||||||
|
|
||||||
|
[metadata.hashes] |
||||||
|
click = ["2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13", "5b94b49521f6456670fdb30cd82a4eca9412788a93fa6dd6df72c94d5a8ff2d7"] |
||||||
|
pdfrw = ["0dc0494a0e6561b268542b28ede2280387c2728114f117d3bb5d8e4787b93ef4", "758289edaa3b672e9a1a67504be73c18ec668d4e5b9d5ac9cbc0dc753d8d196b"] |
||||||
|
pillow = ["01a501be4ae05fd714d269cb9c9f145518e58e73faa3f140ddb67fae0c2607b1", "051de330a06c99d6f84bcf582960487835bcae3fc99365185dc2d4f65a390c0e", "07c35919f983c2c593498edcc126ad3a94154184899297cc9d27a6587672cbaa", "0ae5289948c5e0a16574750021bd8be921c27d4e3527800dc9c2c1d2abc81bf7", "0b1efce03619cdbf8bcc61cfae81fcda59249a469f31c6735ea59badd4a6f58a", "0cf0208500df8d0c3cad6383cd98a2d038b0678fd4f777a8f7e442c5faeee81d", "163136e09bd1d6c6c6026b0a662976e86c58b932b964f255ff384ecc8c3cefa3", "18e912a6ccddf28defa196bd2021fe33600cbe5da1aa2f2e2c6df15f720b73d1", "24ec3dea52339a610d34401d2d53d0fb3c7fd08e34b20c95d2ad3973193591f1", "267f8e4c0a1d7e36e97c6a604f5b03ef58e2b81c1becb4fccecddcb37e063cc7", "3273a28734175feebbe4d0a4cde04d4ed20f620b9b506d26f44379d3c72304e1", "39fbd5d62167197318a0371b2a9c699ce261b6800bb493eadde2ba30d868fe8c", "4132c78200372045bb348fcad8d52518c8f5cfc077b1089949381ee4a61f1c6d", "4baab2d2da57b0d9d544a2ce0f461374dd90ccbcf723fe46689aff906d43a964", "4c678e23006798fc8b6f4cef2eaad267d53ff4c1779bd1af8725cc11b72a63f3", "4d4bc2e6bb6861103ea4655d6b6f67af8e5336e7216e20fff3e18ffa95d7a055", "505738076350a337c1740a31646e1de09a164c62c07db3b996abdc0f9d2e50cf", "5233664eadfa342c639b9b9977190d64ad7aca4edc51a966394d7e08e7f38a9f", "52e2e56fc3706d8791761a157115dc8391319720ad60cc32992350fda74b6be2", "5337ac3280312aa065ed0a8ec1e4b6142e9f15c31baed36b5cd964745853243f", "5ccd97e0f01f42b7e35907272f0f8ad2c3660a482d799a0c564c7d50e83604d4", "5d95cb9f6cced2628f3e4de7e795e98b2659dfcc7176ab4a01a8b48c2c2f488f", "634209852cc06c0c1243cc74f8fdc8f7444d866221de51125f7b696d775ec5ca", "75d1f20bd8072eff92c5f457c266a61619a02d03ece56544195c56d41a1a0522", "7eda4c737637af74bac4b23aa82ea6fbb19002552be85f0b89bc27e3a762d239", "801ddaa69659b36abf4694fed5aa9f61d1ecf2daaa6c92541bbbbb775d97b9fe", "825aa6d222ce2c2b90d34a0ea31914e141a85edefc07e17342f1d2fdf121c07c", "87fe838f9dac0597f05f2605c0700b1926f9390c95df6af45d83141e0c514bd9", "9c215442ff8249d41ff58700e91ef61d74f47dfd431a50253e1a1ca9436b0697", "a3d90022f2202bbb14da991f26ca7a30b7e4c62bf0f8bf9825603b22d7e87494", "a631fd36a9823638fe700d9225f9698fb59d049c942d322d4c09544dc2115356", "a6523a23a205be0fe664b6b8747a5c86d55da960d9586db039eec9f5c269c0e6", "a756ecf9f4b9b3ed49a680a649af45a8767ad038de39e6c030919c2f443eb000", "ac036b6a6bac7010c58e643d78c234c2f7dc8bb7e591bd8bc3555cf4b1527c28", "b117287a5bdc81f1bac891187275ec7e829e961b8032c9e5ff38b70fd036c78f", "ba04f57d1715ca5ff74bb7f8a818bf929a204b3b3c2c2826d1e1cc3b1c13398c", "ba6ef2bd62671c7fb9cdb3277414e87a5cd38b86721039ada1464f7452ad30b2", "c8939dba1a37960a502b1a030a4465c46dd2c2bca7adf05fa3af6bea594e720e", "cd878195166723f30865e05d87cbaf9421614501a4bd48792c5ed28f90fd36ca", "cee815cc62d136e96cf76771b9d3eb58e0777ec18ea50de5cfcede8a7c429aa8", "d1722b7aa4b40cf93ac3c80d3edd48bf93b9208241d166a14ad8e7a20ee1d4f3", "d7c1c06246b05529f9984435fc4fa5a545ea26606e7f450bdbe00c153f5aeaad", "db418635ea20528f247203bf131b40636f77c8209a045b89fa3badb89e1fcea0", "e1555d4fda1db8005de72acf2ded1af660febad09b4708430091159e8ae1963e", "e9c8066249c040efdda84793a2a669076f92a301ceabe69202446abb4c5c5ef9", "e9f13711780c981d6eadd6042af40e172548c54b06266a1aabda7de192db0838", "f0e3288b92ca5dbb1649bd00e80ef652a72b657dc94989fa9c348253d179054b", "f227d7e574d050ff3996049e086e1f18c7bd2d067ef24131e50a1d3fe5831fbc", "f62b1aeb5c2ced8babd4fbba9c74cbef9de309f5ed106184b12d9778a3971f15", "f71ff657e63a9b24cac254bb8c9bd3c89c7a1b5e00ee4b3997ca1c18100dac28", "fc9a12aad714af36cf3ad0275a96a733526571e52710319855628f476dcb144e"] |
||||||
|
reportlab = ["069f684cd0aaa518a27dc9124aed29cee8998e21ddf19604e53214ec8462bdd7", "09b68ec01d86b4b120456b3f3202570ec96f57624e3a4fc36f3829323391daa4", "0c32be9a406172c29ea20ff55a709ccac1e7fb09f15aba67cb7b455fd1d3dbe0", "233196cf25e97cfe7c452524ea29d9a4909f1cb66599299233be1efaaaa7a7a3", "2b5e4533f3e5b962835a5ce44467e66d1ecc822761d1b508077b5087a06be338", "2e860bcdace5a558356802a92ae8658d7e5fdaa00ded82e83a3f2987c562cb66", "3546029e63a9a9dc24ee38959eb417678c2425b96cd27b31e09e216dafc94666", "4452b93f9c73b6b70311e7d69082d64da81b38e91bfb4766397630092e6da6fd", "528c74a1c6527d1859c2c7a64a94a1cba485b00175162ea23699ae58a1e94939", "6116e750f98018febc08dfee6df20446cf954adbcfa378d2c703d56c8864aff3", "6b2b3580c647d75ef129172cb3da648cdb24566987b0b59c5ebb80ab770748d6", "727b5f2bed08552d143fc99649b1863c773729f580a416844f9d9967bb0a1ae8", "74c24a3ec0a3d4f8acb13a07192f45bdb54a1cc3c2286241677e7e8bcd5011fa", "98ccd2f8b4f8636db05f3f14db0b471ad6bb4b66ae0dc9052c4822b3bd5d6a7d", "a5905aa567946bc938b489a7249c7890c3fd3c9b7b5680dece5bc551c2ddbe0d", "acbb7f676b8586b770719e9683eda951fdb38eb7970d46fcbf3cdda88d912a64", "b5e30f865add48cf880f1c363eb505b97f2f7baaa88c155f87a335a76515a3e5", "be2a7c33a2c28bbd3f453ffe4f0e5200b88c803a097f4cf52d69c6b53fad7a8f", "c356bb600f59ac64955813d6497a08bfd5d0c451cb5829b61e3913d0ac084e26", "c7ec4ae2393beab584921b1287a04e94fd98c28315e348362d89b85f4b464546", "d476edc831bb3e9ebd04d1403abaf3ea57b3e4c2276c91a54fdfb6efbd3f9d97", "db059e1a0691c872784062421ec51848539eb4f5210142682e61059a5ca7cc55", "dd423a6753509ab14a0ac1b5be39d219c8f8d3781cce3deb4f45eda31969b5e8", "ed9b7c0d71ce6fe2b31c6cde530ad8238632b876a5d599218739bda142a77f7c", "f0a2465af4006f97b05e1f1546d67d3a3213d414894bf28be7f87f550a7f4a55", "f20bfe26e57e8e1f575a9e0325be04dd3562db9f247ffdd73b5d4df6dec53bc2", "f3463f2cb40a1b515ac0133ba859eca58f53b56760da9abb27ed684c565f853c", "facc3c9748ab1525fb8401a1223bce4f24f0d6aa1a9db86c55db75777ccf40f9"] |
@ -0,0 +1,36 @@ |
|||||||
|
[tool.poetry] |
||||||
|
name = "pdftools" |
||||||
|
version = "0.1.0" |
||||||
|
description = "Custom tools for pdf related stuff" |
||||||
|
authors = ["Holger Frey <frey@imtek.de>"] |
||||||
|
license = "Beerware" |
||||||
|
|
||||||
|
[tool.poetry.dependencies] |
||||||
|
python = "^3.7" |
||||||
|
reportlab = "^3.5" |
||||||
|
pdfrw = "^0.4.0" |
||||||
|
click = "^7.0" |
||||||
|
|
||||||
|
[tool.poetry.dev-dependencies] |
||||||
|
|
||||||
|
[build-system] |
||||||
|
requires = ["poetry>=0.12"] |
||||||
|
build-backend = "poetry.masonry.api" |
||||||
|
|
||||||
|
[tool.poetry.scripts] |
||||||
|
nameplates = 'pdftools.cli:nameplates' |
||||||
|
addruler = 'pdftools.cli:addruler' |
||||||
|
|
||||||
|
[tool.black] |
||||||
|
line-length = 79 |
||||||
|
py37 = true |
||||||
|
include = '\.pyi?$' |
||||||
|
exclude = ''' |
||||||
|
/( |
||||||
|
\.git |
||||||
|
| \.tox |
||||||
|
| \.venv |
||||||
|
| build |
||||||
|
| dist |
||||||
|
)/ |
||||||
|
''' |
@ -0,0 +1,43 @@ |
|||||||
|
PDF helpers for the work at IMTEK |
||||||
|
================================= |
||||||
|
|
||||||
|
This package provides some cli commands: |
||||||
|
|
||||||
|
- `nameplates` generate a pdf with nameplates |
||||||
|
- `addruler` adds a blue ruler to a pdf |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
nameplates |
||||||
|
---------- |
||||||
|
|
||||||
|
Usage: nameplates [OPTIONS] ATTENDEES [LOGO] |
||||||
|
|
||||||
|
creates a pdf with name plate cards |
||||||
|
|
||||||
|
The attendees file should be a tab separated text file with no headers. |
||||||
|
Put the company name in the first column, the given name in the second and |
||||||
|
the last name in the third column, e.g: |
||||||
|
|
||||||
|
Hochimin Enterprizes \t Jane \t Doe \n |
||||||
|
|
||||||
|
The positioning of the optional logo file can be controlled with the size |
||||||
|
and adjustment options. |
||||||
|
|
||||||
|
Options: |
||||||
|
-s, --size INTEGER [default: (10 mm)] |
||||||
|
-y, --adjust_y INTEGER [default: ( 0 mm)] |
||||||
|
-x, --adjust_x INTEGER [default: ( 0 mm)] |
||||||
|
--help Show this message and exit. |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
addruler |
||||||
|
-------- |
||||||
|
|
||||||
|
Usage: addruler [OPTIONS] PDF |
||||||
|
|
||||||
|
adds a blue ruler overlay to a pdf file |
||||||
|
|
||||||
|
Options: |
||||||
|
--help Show this message and exit. |
Loading…
Reference in new issue