Holger Frey
6 years ago
10 changed files with 543 additions and 0 deletions
@ -0,0 +1,63 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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