{"id":16412451,"url":"https://github.com/sfneal/pdfconduit","last_synced_at":"2025-12-13T16:24:12.543Z","repository":{"id":49432549,"uuid":"141729178","full_name":"sfneal/pdfconduit","owner":"sfneal","description":"Prepare documents for distribution","archived":false,"fork":false,"pushed_at":"2025-04-21T07:07:53.000Z","size":239350,"stargazers_count":26,"open_issues_count":13,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-25T21:54:07.422Z","etag":null,"topics":["encryption","pdf","pdf-document-processor","pdf-generation","pdfkit","pdfrw","pypdf2","python","watermark"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sfneal.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-07-20T15:32:53.000Z","updated_at":"2025-04-03T17:57:54.000Z","dependencies_parsed_at":"2024-08-28T19:44:47.652Z","dependency_job_id":"832eb358-9eac-4ebe-bd37-870ae25672a6","html_url":"https://github.com/sfneal/pdfconduit","commit_stats":{"total_commits":1154,"total_committers":2,"mean_commits":577.0,"dds":"0.0034662045060658286","last_synced_commit":"55934803efd91b6b456985be7df93c03d24747c7"},"previous_names":["mrstephenneal/pdfconduit"],"tags_count":53,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfneal%2Fpdfconduit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfneal%2Fpdfconduit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfneal%2Fpdfconduit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sfneal%2Fpdfconduit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sfneal","download_url":"https://codeload.github.com/sfneal/pdfconduit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252975037,"owners_count":21834363,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["encryption","pdf","pdf-document-processor","pdf-generation","pdfkit","pdfrw","pypdf2","python","watermark"],"created_at":"2024-10-11T06:48:58.626Z","updated_at":"2025-10-18T19:59:21.708Z","avatar_url":"https://github.com/sfneal.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PDF Conduit\n\n[![Build Status](https://travis-ci.com/sfneal/pdfconduit.svg?branch=master)](https://travis-ci.com/sfneal/pdfconduit)\n[![PyPi version](https://img.shields.io/pypi/v/pdfconduit)](https://pypi.org/project/pdfconduit)\n[![PyPi Python support](https://img.shields.io/pypi/pyversions/pdfconduit)](https://pypi.org/project/pdfconduit)\n[![PyPi downloads per month](https://img.shields.io/pypi/dm/pdfconduit)](https://pypi.org/project/pdfconduit)\n[![PyPi license](https://img.shields.io/pypi/l/pdfconduit)](https://pypi.org/project/pdfconduit)\n\nPrepare documents for distribution.\n\nA Pure-Python library built as a PDF toolkit.\n\n*Features*:\n\n* **Watermark**: Dynamically generate watermarks and add watermark to existing document\n* **Label**: Overlay text labels such as filename or date to documents \n* **Encrypt**: Password protect and restrict permissions to print only\n* **Rotate**: Rotate by increments of 90 degrees\n* **Upscale**: Scale PDF size\n* **Merge**: Concatenate multiple documents into one file\n* **Slice**: Extract page ranges from documents\n* **Flatten**: Flatten PDF pages and remove layers\n* **Convert**: Convert an image file to a PDF or convert a PDF to an image\n* Extract Text and Images\n* Retrieve document metadata and information\n\n## Getting Started\n\nThese instructions will get you a copy of the project up and running on your local machine for development and testing purposes. See deployment for notes on how to deploy the project on a live system.\n\n### Prerequisites\n\nIn order to use this application you will need to have a Python 3 interpreter installed on your machine.  A limited functionality executable application has been developed for Windows 10 to bypass Python as a system dependency.\n\nUpgrade to the latest version of pip.\n\n```bash\npip install --upgrade pip\n```\n\n### Installation\n\nInstall the latest version from the PyPi distribution.  Run pip install pdfconduit on the command line of your interpreter (virtual environment not required but recommended).\n\nPyPi install\n\n```bash\npip install pdfconduit\n```\nPyPi update (no cache dir to force install of newest version)\n\n```bash\npip install --no-cache-dir --upgrade pdfconduit\n```\nPyPi install (with GUI)\n\n```bash\npip install pdfconduit-gui\n```\n\n### Project Structure\n\n```bash\npdf\n├── conduit\n│   ├── __init__.py\n│   ├── _version.py\n│   ├── encrypt.py\n│   ├── flatten.py\n│   ├── merge.py\n│   ├── rotate.py\n│   ├── slice.py\n│   ├── upscale.py\n│   ├── utils\n│   │   ├── __init__.py\n│   │   ├── extract.py\n│   │   ├── info.py\n│   │   ├── lib\n│   │   │   └── font\n│   │   │       └── Vera.ttf\n│   │   ├── lib.py\n│   │   ├── path.py\n│   │   ├── receipt.py\n│   │   ├── samples.py\n│   │   ├── view.py\n│   │   └── write.py\n│   └── watermark\n│       ├── __init__.py\n│       ├── add.py\n│       ├── canvas\n│       │   ├── __init__.py\n│       │   ├── constructor.py\n│       │   └── objects.py\n│       ├── draw\n│       │   ├── __init__.py\n│       │   ├── image.py\n│       │   └── pdf.py\n│       ├── label.py\n│       └── watermark.py\n└── gui\n    ├── __init__.py\n    ├── _version.py\n    ├── gui.py\n    └── lib\n        ├── icon\n        │   ├── lock.ico\n        │   └── stamp.ico\n        └── img\n            ├── Standard\\ (no\\ blocks).png\n            ├── Standard.png\n            ├── Wide\\ (no\\ blocks).png\n            └── Wide.png\npdfconduit\n├── __init__.py\n```\n\n\n## Purpose\n\npdfconduit was developed to streamline the redundant process of creating watermarks, overlaying them on PDF files and adding security parameters before distribution to clients.\n\n#### Process \"as is\"\n \n1. Photoshop\n\t* Open watermark PSD template\n\t* Modify text (address, town, state)\n\t* Save file to PNG\n2. Acrobat (watermark)\n\t* Open source PDF file\n\t* Find PNG file and add as a watermark\n\t* Save new file with '_watermarked' suffix\n3. Acrobat (security)\n\t* Open watermarked PDF file\n\t* Add user and owner password protection\n\t* Restrict permissions to 'Print Only'\n\n#### Process \"automated\"\n\n1. Run pdfwatermark GUI\n2. Select source PDF file\n3. Input text (address, town, state)\n4. Select watermark and encryption parameters\n\nBy removing the steps of launching Photoshop and Acrobat to perform a number of tasks process efficiency is dramatically increaded.\n\n## High Level APIs\n\nOutlined below are basic uses of the main classes and functions of the pdfconduit python package.\n\n* GUI.watermark() - GUI for setting source file and watermark parameters\n\t* Launch GUI window to set source file and watermark settings\n\t* Dependent on PySimpleGUI library and TKinter back-end\n\t* Return inputs to caller\n* Watermark() - Wrapper class that manages inputs and file structures\n\t* Creates watermark file\n\t* Merges watermark file and source document file\n\t* Saves new watermark and removes temp files\n* WatermarkDraw() - Dynamically generates a watermark using CanvasObjects\n\t* Set text, image, font, opacity and location parameters by creating CanvasStr and CavnasImg objects\n\t* Draw to letter sized canvas\n\t* Add rotation to canvas for rotated watermarked\n\t* Merges watermark template and dynamically drawn canvas or image to create watermark\n\t* Write watermark pdf file to temp folder and returns path\n* WatermarkAdd() - Merges source PDF file with the watermark generated by WatermarkDraw\n\t* Checks if source PDF file is verically or horizontally oriented\n\t* Calls upscale() to upscale PDF to fit letter size (8.5 x 11)\n\t* Checks if watermark orientation is the same as source pdf file's\n\t\t* Calls rotate() function to rotate watermark by increments of 90 degrees if needed\n\t* Merges source PDF file and watermark file to create new PDF object\n* rotate() - Rotate PDF by increments of 90 degrees\n* upscale() - Upscales PDF to fit letter size\n* Encrypt() - Encrypt a PDF document to add passwords and permissions\n* Merge() - Concatenate multiple PDF documents into one PDF\n* slicer() - Save range of pages in PDF document to a new PDF file\n* Flatten() - Convert each page of a PDF document to a flattened image\n\n## Usage * Watermark\nGenerate watermark, add watermark to file and encrypt file\n#### Using module imports.\n\n```python\nfrom pdfconduit import Watermark\n\n# Set document and watermark params\npdf = 'mypdfdoc.pdf'\naddress = '2000 Main Street'\ntown = 'Boston'\nstate = 'MA'\n\n# Initialize with PDF document\nw = Watermark(pdf)\n\n# Generate watermark file\nw.draw(text1=address, text2=town + ', ' + state, include_copyright=True, rotate=30, opacity=0.08\n\n# Add watermark file to PDF document\nw.add()\n\u003e\u003e \u003e mypdfdoc_watermarked.pdf\n\n# Encypt PDF document\nw.encrypt(user_pw='foo', owner_pw='baz')\n\u003e\u003e \u003e mypdfdoc_secured.pdf\n\n# Remove temp files and save receipt to disk\nw.cleanup()\n```\n\n#### Using GUI.\n```python\nfrom pdfconduit import GUI\nGUI.watermark()\n```\n![GUI Screenshot](https://i.imgur.com/9pMvzJj.png)\n\n#### Optional Parameters - Watermark Settings\n###### Logo Images\n* References the logo images within the pdfconduit/watermark/lib/img directory\n* Can be replaced with any png \n\n```python\nWatermark.draw(image='Wide.png')\n```\n\n###### File Compression\n* Handles compressing of PDF object components of the watermark file\n* When objects are automatically compressed this parameter may have no effect\n\n```python\nWatermark.draw(compress=0)  # Uncompressed\nWatermark.draw(compress=1)  # Compressed\n``` \n\n###### Watermark Flattening\n| | Layered | Flattened\n| --- | --- | --- |\n| | *Finer parameter tuning with more options* | *Watermark harder to remove by merging img layers* |\n| **Construction** | * Creates a CanvasStr object for each text layer \u003cbr\u003e * Create CanvasImg object for watermark logo image file | * Draw each text layer to PIL image file \u003cbr\u003e * Draw PIL image with text to PIL image with logo to create one image file |\n| **CanvasObjects** | Initiate CanvasObjects() and use CanvasObjects().add() to add each string and image | Initiate CanvasObjects() and use CanvasObjects().add() to one CanvasImg instance |\n| **Draw** | Iterate CanvasObjects and draw each to canvas | Draw CanvasImg to canvas |\n| **Save** | Save canvas with text objects to layered PDF document | Save canvas with single image layer |\n\n```python\nWatermark.draw(flatten=False)  # Layered\nWatermark.draw(flatten=True)  # Flattened\n``` \n\n###### Watermark Placement\n* Place Watermark on top of or below existing PDF document\n* Overlay placement is necessary for watermarking images\n* Underneath placement is often cleaner for watermarking text heavy PDF documents\n\n```python\nWatermark.add(underneath=False)  # Overlay\nWatermark.add(underneath=True)  # Underneath\n```\n\n###### Opacity\n* Opacity of watermark logo image and watermark text\n* Adjustable from 1% to 20%\n* Opacity parameter must of type float\n\n```python\nWatermark.draw(opacity=0.09)  # Set opacity to 9%\n```\n\n## Usage - Encrypt\nEncrypt a PDF file to add passwords and restrict permissions.\n#### Using module imports.\n```python\nfrom pdfconduit import protect\n\n# Required parameters\npdf = 'mypdfdoc.pdf'\nuser_pw = 'baz'  # Password to open and view PDF\nowner_pw = 'foo'  # Password to change security settings\n\n# Optional parameters\nencrypt_128 = True  # Encrypt using 128 bits (40 bits when False)\nrestrict_permission = True  # Restrict permissions to print only (all allowed when false)\n\n# Encrypt PDF document\nencrypted = encrypt(pdf, user_pw, owner_pw, encrypt_128, restrict_permission)\n\u003e\u003e\u003e mypdfdoc_secured.pdf\n```\n\n## Usage - Merge\nMerge multiple PDF files into one concatenated PDF file.\n#### Using module imports.\n```python\nfrom pdfconduit import Merge\n\n# List of PDF paths\npdfs = ['doc1.pdf', 'doc2.pdf', 'doc3.pdf']\n\n# Merge PDF files\nmerged = Merge(pdfs)\n\u003e\u003e\u003e merged.pdf\n```\nor\n\n```python\nfrom pdfconduit import Merge\n\n# List of PDF paths\npdfs = ['doc1.pdf', 'doc2.pdf', 'doc3.pdf']\n\n# Specify output file name\noutput = 'combined doc'\n\n# Merge PDF files\nmerged = Merge(pdfs, output_name=output)\n\u003e\u003e\u003e combined doc.pdf\n```\n\n## Usage - Rotate\nRotate a PDF document by increments of 90 degrees.\n#### Using module imports.\n```python\nfrom pdfconduit import rotate\n\npdf = 'mypdfdoc.pdf'  # PDF to-be rotated\nrotate = 90  # Degress of rotation (clockwise)\n\n# Rotate PDF file\nrotated = rotate(pdf, rotate)\n\u003e\u003e\u003e mypdfdoc_rotated.pdf\n```\n\n## Usage - Slice\nSlice a PDF document to extract a range of page.\n#### Using module imports.\n```python\nfrom pdfconduit import slicer\n\n# Parameters\npdf = 'mypdfdoc.pdf'\nfirst_page = 4\nlast_page = 17\n\n# Slice PDF file\nsliced = slicer(pdf, first_page, last_page)\n\u003e\u003e\u003e mypdfdoc_sliced.pdf\n```\n\n## Usage - Label\nAdd a text label to the bottom left corner of each page of PDF file.\n#### Using module imports.\n```python\nfrom pdfconduit import Label\n\n# Parameters\npdf = 'mypdfdoc.pdf'\nlabel = 'Document updated 7/10/18'\n\n# Label PDF file\nlabeled = Label(pdf, label)\n\u003e\u003e\u003e mypdfdoc_labeled.pdf\n```\n[Original](https://i.imgur.com/4plXGHN.png)\n\n[Labeled](https://i.imgur.com/UvEMNxy.png)\n\n## Functionality\n### Watermark()\n```python\nWatermark(document, remove_temps=True, open_file=True, tempdir=mkdtemp(), receipt=None, use_receipt=True)\n```\nParameters | Type | Description\n--- | --- | ---\ndocument | `str ` | PDF document full path\nremove_temps | `bool` | Remove temporary files after completion\nopen_file | `bool` | Open file after completion\ntempdir | `str or function` | Temporary directory for file writing\nreceipt | `cls` | Use existing Receipt object if already initiated\nuse_receipt | `bool` | Print receipt information to console and write to file\n\n### Watermark().draw()\n```python\ndef draw(self, text1, text2=None, copyright=True, image=default_image, rotate=30,\n\t\t opacity=0.08, compress=0, flatten=False, add=False):\n```\nParameters | Type | Description\n--- | --- | ---\ntext1 | `str ` | Text line 1\ntext2 | `str ` | Text line 2\ncopyright | `bool` | Draw copyright and year to canvas\nimage | `str` | Logo image to be used as base watermark\nrotate | `int` | Degrees to rotate canvas by\nopacity | `float` | Watermark opacity``\ncompress | `bool` | Compress watermark contents (not entire PDF)\nflatten | `bool` | Draw watermark with multiple layers or a single flattened layer\nadd | `bool` | Add watermark to original document``\n\n**Return**: Watermark file full path\n\n### Watermark().add()\n```python\ndef add(self, document=None, watermark=None, underneath=False, output=None, suffix='watermarked'):\n```\nParameters | Type | Description\n--- | --- | ---\ndocument | `str ` | PDF document full path\nwatermark | `str ` | Watermark PDF full path\nunderneath | `bool` | Place watermark either under or over existing PDF document\noutput | `str` | Output file path\nsuffix | `str ` | Suffix to append to existing PDF document file name\n\n**Return**: Watermarked PDF document full path\n\n### Watermark().encrypt()\n```python\ndef encrypt(self, user_pw='', owner_pw=None, encrypt_128=True, restrict_permission=True):\n```\nParameters | Type | Description\n--- | --- | ---\nuser_pw | `str ` | User password required to open and view PDF document\nowner_pw | `str ` | Owner password required to alter security settings and permissions\nencrypt_128 | `bool` | Encrypt PDF document using 128 bit keys\nrestrict_permission | `str` | Restrict permissions to print only\n\n**Return**: Encrypted PDF document full path\n\n## Challenges\n\n* A number of PDF libraries exist I was unable to find one with the functionality I was looking for.\n* Simple add watermark functionality wasn't enough, I needed the ability to adjust each watermark without opening another application.\n* PDF files can only be rotated by 90 degree increments so slanted text was achieved by drawing to a rotated canvas object\n\n## Built With\n\n* [PyPDF3](https://github.com/mrstephenneal/PyPDF3) - A utility to read and write PDFs with Python forked from [PyPDF2](https://github.com/mstamy2/PyPDF2)\n* [pdfrw](https://github.com/pmaupin/pdfrw) - pdfrw is a pure Python library that reads and writes PDFs.\n* [PyMuPDF](https://github.com/rk700/PyMuPDF) - A lightweight PDF and XPS viewer\n* [Pillow](https://python-pillow.org/) - The friendly PIL fork (Python Imaging Library)\n* [PySimpleGUI](https://github.com/MikeTheWatchGuy/PySimpleGUI) - A simple yet powerful GUI built on top of tkinter.\n* [reportlab](https://bitbucket.org/rptlab/reportlab) - Allows rapid creation of rich PDF documents, and also creation of charts in a variety of bitmap and vector formats.\n* [looptools](https://github.com/mrstephenneal/looptools) - Logging output, timing processes and counting iterations.\n* [tqdm](https://github.com/tqdm/tqdm) - A fast, extensible progress bar for Python\n\n## Contributing\n\nPlease read [CONTRIBUTING.md](https://github.com/mrstephenneal/pdfconduit/contributing.md) for details on our code conduct, and the process for submitting pull requests to us.\n\n## Versioning\n\nWe use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/mrstephenneal/pdfconduit).\n\n## Authors\n\n* **Stephen Neal** *Initial work* [pdfconduit](https://github.com/mrstephenneal/pdfconduit)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsfneal%2Fpdfconduit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsfneal%2Fpdfconduit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsfneal%2Fpdfconduit/lists"}