astrapdf 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,69 @@
1
+ Metadata-Version: 2.4
2
+ Name: astrapdf
3
+ Version: 0.1.0
4
+ Summary: A private, local-first PDF toolkit and browser studio
5
+ Author: AstraPDF contributors
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: local,merge,ocr,pdf,pdf-editor,private
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Environment :: Web Environment
11
+ Classifier: Framework :: FastAPI
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Multimedia :: Graphics :: Editors
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: cryptography<47,>=43
23
+ Requires-Dist: fastapi<1,>=0.115
24
+ Requires-Dist: pillow<13,>=10.0
25
+ Requires-Dist: pypdf<7,>=5.0
26
+ Requires-Dist: pypdfium2<6,>=4.30
27
+ Requires-Dist: pytesseract<1,>=0.3.13
28
+ Requires-Dist: python-multipart<1,>=0.0.12
29
+ Requires-Dist: reportlab<5,>=4.2
30
+ Requires-Dist: uvicorn[standard]<1,>=0.30
31
+ Provides-Extra: dev
32
+ Requires-Dist: build<2,>=1.2; extra == 'dev'
33
+ Requires-Dist: twine<7,>=5; extra == 'dev'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # AstraPDF
37
+
38
+ A local-first PDF toolkit with a Python API and browser-based desktop studio. PDF files remain on the machine running the library; the web UI binds to localhost by default.
39
+
40
+ ## Install and launch
41
+
42
+ ```bash
43
+ python -m pip install astrapdf
44
+ astrapdf
45
+ ```
46
+
47
+ Open the local address printed by the command. AstraPDF asks the operating system for an available port by default, so it can run even when another app is using a common port. To choose a specific port: `astrapdf --port 9000`. Do not expose this development server directly to the internet. The default listener is loopback only.
48
+
49
+ ## Python API
50
+
51
+ ```python
52
+ from pathlib import Path
53
+ from pdfstudio import PDFStudio
54
+
55
+ studio = PDFStudio()
56
+ merged = studio.merge(["cover.pdf", "report.pdf", "photo.png"])
57
+ Path("combined.pdf").write_bytes(merged)
58
+
59
+ pages = studio.extract("combined.pdf", [0, 2, 3])
60
+ Path("selection.pdf").write_bytes(pages)
61
+ ```
62
+
63
+ Inputs can be paths, bytes, or binary file objects. Document methods return PDF bytes unless documented otherwise; all page indices in the library are zero-based.
64
+
65
+ See the feature table below for implemented operations and boundaries. Redaction annotations and whiteout overlays are visual covers; they do not remove underlying PDF text. Permanent redaction is intentionally not offered.
66
+
67
+ ## License and PDF compatibility
68
+
69
+ The Python package is MIT licensed. PDFium is distributed in the pypdfium2 wheels under its upstream BSD-style license; see the installed dependency's third-party notices. OCR also requires the Tesseract executable to be installed separately; `pip` installs AstraPDF's Python dependencies, but cannot install that operating-system program. Supported behavior follows the file format and versions of the bundled libraries. Digital signing, general damaged-PDF repair, XFA forms, and secure redaction are not included.
@@ -0,0 +1,13 @@
1
+ pdfstudio/__init__.py,sha256=W-KMl5mLZTRZmKnYCsAC0x7V8UtTqXUavWenLNiEWcc,158
2
+ pdfstudio/cli.py,sha256=mnIaD8CzsAEdsD2MtQoH9tvWyPU9qso-t3Es7NUUhG8,1789
3
+ pdfstudio/core.py,sha256=FwyHpxhN2g00tVGziGT2M_03IkgS55iZp-O3XmIT5kw,30164
4
+ pdfstudio/webapp.py,sha256=_4lCB2wWVWs6MkgfV9oUvTFtv8wSeGF0qnSeLMx6qQA,19425
5
+ pdfstudio/web/app.js,sha256=SgkcWIhzQNs1pTurvstjrP0UexpfHL9sj8iRSqOmKI0,33222
6
+ pdfstudio/web/index.html,sha256=Ya73fo030L9OGVYF0ivju9VFFhTGFlj-Hasnd256uDg,7986
7
+ pdfstudio/web/style.css,sha256=3SV6EKOJ-sQWZKdjAaz5OT95xLysLl9xwtNTtlBcQz0,9494
8
+ pdfstudio/web/viewer-fixes.css,sha256=FGlFPwjN1UAhf0jQ3egHS7bvnUB2O3pnCp7LHYR2DTU,1418
9
+ astrapdf-0.1.0.dist-info/METADATA,sha256=4z5LB8MjZLInPWb2AMFjlQJDwsPqLEoPtsrfy0cSWy4,3141
10
+ astrapdf-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
11
+ astrapdf-0.1.0.dist-info/entry_points.txt,sha256=pR-tBLmX4BofQSGnHnJ5Tq2Uo7bTTW1oGcHqlYnYnJw,48
12
+ astrapdf-0.1.0.dist-info/licenses/LICENSE,sha256=8WgnI7DORBqkX6BwuUO1vbVpHyVUXsyN3Ks0Vljo50o,1086
13
+ astrapdf-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ astrapdf = pdfstudio.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PDF Studio Local contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pdfstudio/__init__.py ADDED
@@ -0,0 +1,6 @@
1
+ """Local-first PDF tools and browser studio."""
2
+
3
+ from .core import PDFStudio, PDFStudioError
4
+
5
+ __all__ = ["PDFStudio", "PDFStudioError"]
6
+ __version__ = "0.1.0"
pdfstudio/cli.py ADDED
@@ -0,0 +1,48 @@
1
+ """Command line entry point for `astrapdf`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import socket
7
+ import webbrowser
8
+
9
+
10
+ def main() -> None:
11
+ parser = argparse.ArgumentParser(prog="astrapdf", description="Run the private AstraPDF web app")
12
+ parser.add_argument("--host", default="127.0.0.1", help="Listen address (default: 127.0.0.1; avoid public interfaces)")
13
+ parser.add_argument("--port", type=int, default=0, help="Listen port (default: choose an available port)")
14
+ parser.add_argument("--no-browser", action="store_true", help="Print the address without opening a browser")
15
+ args = parser.parse_args()
16
+ if args.host not in {"127.0.0.1", "localhost", "::1"}:
17
+ parser.error("Only loopback addresses are supported; keep the local document server private.")
18
+ import uvicorn
19
+
20
+ family = socket.AF_INET6 if args.host == "::1" else socket.AF_INET
21
+ listener = socket.socket(family, socket.SOCK_STREAM)
22
+ try:
23
+ listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
24
+ listener.bind((args.host, args.port))
25
+ listener.listen(2048)
26
+ listener.setblocking(False)
27
+ port = listener.getsockname()[1]
28
+ display_host = f"[{args.host}]" if family == socket.AF_INET6 else args.host
29
+ url = f"http://{display_host}:{port}"
30
+
31
+ config = uvicorn.Config(
32
+ "pdfstudio.webapp:app",
33
+ host=args.host,
34
+ port=port,
35
+ log_level="warning",
36
+ access_log=False,
37
+ )
38
+ server = uvicorn.Server(config)
39
+ if not args.no_browser:
40
+ webbrowser.open(url)
41
+ print(f"AstraPDF is running at {url} (Ctrl-C to stop).")
42
+ server.run(sockets=[listener])
43
+ finally:
44
+ listener.close()
45
+
46
+
47
+ if __name__ == "__main__":
48
+ main()