pdfwright 0.1.0__tar.gz
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.
- pdfwright-0.1.0/.gitignore +11 -0
- pdfwright-0.1.0/PKG-INFO +62 -0
- pdfwright-0.1.0/README.md +47 -0
- pdfwright-0.1.0/pdfwright/__init__.py +107 -0
- pdfwright-0.1.0/pyproject.toml +25 -0
pdfwright-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pdfwright
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python client for PDFwright — the developer-first PDF API. HTML, URLs, Markdown, and JSON templates in; pixel-perfect PDFs out.
|
|
5
|
+
Project-URL: Homepage, https://pdfwright.dev
|
|
6
|
+
Project-URL: Documentation, https://pdfwright.dev/docs
|
|
7
|
+
Author-email: PDFwright <hello@pdfwright.dev>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: html-to-pdf,invoice,markdown-to-pdf,pdf,pdf-api,pdf-generation
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.9
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# pdfwright
|
|
17
|
+
|
|
18
|
+
Official Python client for [PDFwright](https://pdfwright.dev) — the developer-first PDF API.
|
|
19
|
+
Zero dependencies (stdlib only), Python 3.9+.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pip install pdfwright
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
(Package and module are both `pdfwright`.)
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from pdfwright import PDFwright
|
|
29
|
+
|
|
30
|
+
client = PDFwright("pdfw_live_...") # free key at pdfwright.dev
|
|
31
|
+
|
|
32
|
+
# HTML → PDF
|
|
33
|
+
open("hello.pdf", "wb").write(client.html("<h1>Hello!</h1>", format="Letter"))
|
|
34
|
+
|
|
35
|
+
# JSON → invoice (totals & tax computed for you)
|
|
36
|
+
pdf = client.invoice({
|
|
37
|
+
"brand": {"name": "Acme Studio", "color": "#0f766e"},
|
|
38
|
+
"invoiceNumber": "INV-2043",
|
|
39
|
+
"currency": "USD",
|
|
40
|
+
"to": {"name": "Northwind Traders"},
|
|
41
|
+
"items": [{"description": "Design work", "quantity": 10, "unitPrice": 95}],
|
|
42
|
+
"taxRate": 8.5,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
# Markdown → styled PDF
|
|
46
|
+
client.markdown("# Q2 Report\n\nRevenue grew **12%**.", theme="serif")
|
|
47
|
+
|
|
48
|
+
# Check your quota
|
|
49
|
+
print(client.usage()) # {'plan': ..., 'used': ..., 'remaining': ...}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Get a free API key (50 PDFs/month) at [pdfwright.dev](https://pdfwright.dev/#get-key).
|
|
53
|
+
Full API reference: [pdfwright.dev/docs](https://pdfwright.dev/docs).
|
|
54
|
+
|
|
55
|
+
## Publishing (maintainer note)
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
cd clients/python
|
|
59
|
+
python -m pip install build twine
|
|
60
|
+
python -m build
|
|
61
|
+
python -m twine upload dist/* # requires a PyPI account/token
|
|
62
|
+
```
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# pdfwright
|
|
2
|
+
|
|
3
|
+
Official Python client for [PDFwright](https://pdfwright.dev) — the developer-first PDF API.
|
|
4
|
+
Zero dependencies (stdlib only), Python 3.9+.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
pip install pdfwright
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
(Package and module are both `pdfwright`.)
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
from pdfwright import PDFwright
|
|
14
|
+
|
|
15
|
+
client = PDFwright("pdfw_live_...") # free key at pdfwright.dev
|
|
16
|
+
|
|
17
|
+
# HTML → PDF
|
|
18
|
+
open("hello.pdf", "wb").write(client.html("<h1>Hello!</h1>", format="Letter"))
|
|
19
|
+
|
|
20
|
+
# JSON → invoice (totals & tax computed for you)
|
|
21
|
+
pdf = client.invoice({
|
|
22
|
+
"brand": {"name": "Acme Studio", "color": "#0f766e"},
|
|
23
|
+
"invoiceNumber": "INV-2043",
|
|
24
|
+
"currency": "USD",
|
|
25
|
+
"to": {"name": "Northwind Traders"},
|
|
26
|
+
"items": [{"description": "Design work", "quantity": 10, "unitPrice": 95}],
|
|
27
|
+
"taxRate": 8.5,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
# Markdown → styled PDF
|
|
31
|
+
client.markdown("# Q2 Report\n\nRevenue grew **12%**.", theme="serif")
|
|
32
|
+
|
|
33
|
+
# Check your quota
|
|
34
|
+
print(client.usage()) # {'plan': ..., 'used': ..., 'remaining': ...}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Get a free API key (50 PDFs/month) at [pdfwright.dev](https://pdfwright.dev/#get-key).
|
|
38
|
+
Full API reference: [pdfwright.dev/docs](https://pdfwright.dev/docs).
|
|
39
|
+
|
|
40
|
+
## Publishing (maintainer note)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd clients/python
|
|
44
|
+
python -m pip install build twine
|
|
45
|
+
python -m build
|
|
46
|
+
python -m twine upload dist/* # requires a PyPI account/token
|
|
47
|
+
```
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""Official Python client for PDFwright — https://pdfwright.dev/docs
|
|
2
|
+
|
|
3
|
+
Zero dependencies (stdlib urllib only), Python 3.9+.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import urllib.error
|
|
10
|
+
import urllib.request
|
|
11
|
+
from typing import Any, Optional
|
|
12
|
+
|
|
13
|
+
__all__ = ["PDFwright", "PDFwrightError"]
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class PDFwrightError(Exception):
|
|
18
|
+
"""Raised when the API returns an error response."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, message: str, status: Optional[int] = None, code: Optional[str] = None):
|
|
21
|
+
super().__init__(message)
|
|
22
|
+
self.status = status
|
|
23
|
+
self.code = code
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class PDFwright:
|
|
27
|
+
"""Client for the PDFwright rendering API.
|
|
28
|
+
|
|
29
|
+
>>> pdfwright = PDFwright("pdfw_live_...")
|
|
30
|
+
>>> pdf_bytes = pdfwright.html("<h1>Hello!</h1>")
|
|
31
|
+
>>> open("hello.pdf", "wb").write(pdf_bytes)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(self, api_key: str, base_url: str = "https://pdfwright.dev", timeout: float = 60.0):
|
|
35
|
+
if not api_key:
|
|
36
|
+
raise PDFwrightError("An API key is required (get one free at pdfwright.dev)")
|
|
37
|
+
self.api_key = api_key
|
|
38
|
+
self.base_url = base_url.rstrip("/")
|
|
39
|
+
self.timeout = timeout
|
|
40
|
+
|
|
41
|
+
# ── core ──────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
def render(self, **request: Any) -> bytes:
|
|
44
|
+
"""Render a PDF. Provide exactly one of: html, url, markdown, template (+data)."""
|
|
45
|
+
req = urllib.request.Request(
|
|
46
|
+
f"{self.base_url}/v1/pdf",
|
|
47
|
+
data=json.dumps(request).encode("utf-8"),
|
|
48
|
+
headers={
|
|
49
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
50
|
+
"Content-Type": "application/json",
|
|
51
|
+
},
|
|
52
|
+
method="POST",
|
|
53
|
+
)
|
|
54
|
+
try:
|
|
55
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as res:
|
|
56
|
+
return res.read()
|
|
57
|
+
except urllib.error.HTTPError as e:
|
|
58
|
+
try:
|
|
59
|
+
body = json.loads(e.read().decode("utf-8"))
|
|
60
|
+
except Exception:
|
|
61
|
+
body = {}
|
|
62
|
+
raise PDFwrightError(
|
|
63
|
+
body.get("message", f"Render failed ({e.code})"),
|
|
64
|
+
status=e.code,
|
|
65
|
+
code=body.get("error"),
|
|
66
|
+
) from None
|
|
67
|
+
|
|
68
|
+
# ── conveniences ──────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
def html(self, html: str, **options: Any) -> bytes:
|
|
71
|
+
return self.render(html=html, options=options or None)
|
|
72
|
+
|
|
73
|
+
def url(self, url: str, **options: Any) -> bytes:
|
|
74
|
+
return self.render(url=url, options=options or None)
|
|
75
|
+
|
|
76
|
+
def markdown(self, markdown: str, theme: str = "clean", **options: Any) -> bytes:
|
|
77
|
+
return self.render(markdown=markdown, theme=theme, options=options or None)
|
|
78
|
+
|
|
79
|
+
def invoice(self, data: dict, **options: Any) -> bytes:
|
|
80
|
+
return self.render(template="invoice", data=data, options=options or None)
|
|
81
|
+
|
|
82
|
+
def receipt(self, data: dict, **options: Any) -> bytes:
|
|
83
|
+
return self.render(template="receipt", data=data, options=options or None)
|
|
84
|
+
|
|
85
|
+
def quote(self, data: dict, **options: Any) -> bytes:
|
|
86
|
+
return self.render(template="quote", data=data, options=options or None)
|
|
87
|
+
|
|
88
|
+
def report(self, data: dict, **options: Any) -> bytes:
|
|
89
|
+
return self.render(template="report", data=data, options=options or None)
|
|
90
|
+
|
|
91
|
+
def certificate(self, data: dict, **options: Any) -> bytes:
|
|
92
|
+
return self.render(template="certificate", data=data, options=options or None)
|
|
93
|
+
|
|
94
|
+
def usage(self) -> dict:
|
|
95
|
+
"""Current plan/quota state."""
|
|
96
|
+
req = urllib.request.Request(
|
|
97
|
+
f"{self.base_url}/v1/usage",
|
|
98
|
+
headers={"Authorization": f"Bearer {self.api_key}"},
|
|
99
|
+
)
|
|
100
|
+
try:
|
|
101
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as res:
|
|
102
|
+
return json.loads(res.read().decode("utf-8"))
|
|
103
|
+
except urllib.error.HTTPError as e:
|
|
104
|
+
body = json.loads(e.read().decode("utf-8"))
|
|
105
|
+
raise PDFwrightError(
|
|
106
|
+
body.get("message", "usage lookup failed"), status=e.code, code=body.get("error")
|
|
107
|
+
) from None
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pdfwright"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python client for PDFwright — the developer-first PDF API. HTML, URLs, Markdown, and JSON templates in; pixel-perfect PDFs out."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
authors = [{ name = "PDFwright", email = "hello@pdfwright.dev" }]
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
keywords = ["pdf", "html-to-pdf", "pdf-generation", "invoice", "pdf-api", "markdown-to-pdf"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://pdfwright.dev"
|
|
22
|
+
Documentation = "https://pdfwright.dev/docs"
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["pdfwright"]
|