Skip to content

Commit 08bb5e9

Browse files
Add XFA to AcroForms
Assisted-by: Codex
1 parent 3a0645b commit 08bb5e9

4 files changed

Lines changed: 458 additions & 0 deletions

File tree

src/pdfrest/client.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
PdfToPdfxPayload,
9494
PdfToPowerpointPayload,
9595
PdfToWordPayload,
96+
PdfXfaToAcroformsPayload,
9697
PngPdfRestPayload,
9798
SummarizePdfTextPayload,
9899
TiffPdfRestPayload,
@@ -2473,6 +2474,32 @@ def convert_to_powerpoint(
24732474
timeout=timeout,
24742475
)
24752476

2477+
def convert_xfa_to_acroforms(
2478+
self,
2479+
file: PdfRestFile | Sequence[PdfRestFile],
2480+
*,
2481+
output: str | None = None,
2482+
extra_query: Query | None = None,
2483+
extra_headers: AnyMapping | None = None,
2484+
extra_body: Body | None = None,
2485+
timeout: TimeoutTypes | None = None,
2486+
) -> PdfRestFileBasedResponse:
2487+
"""Convert an XFA PDF to an AcroForm-enabled PDF."""
2488+
2489+
payload: dict[str, Any] = {"files": file}
2490+
if output is not None:
2491+
payload["output"] = output
2492+
2493+
return self._post_file_operation(
2494+
endpoint="/pdf-with-acroforms",
2495+
payload=payload,
2496+
payload_model=PdfXfaToAcroformsPayload,
2497+
extra_query=extra_query,
2498+
extra_headers=extra_headers,
2499+
extra_body=extra_body,
2500+
timeout=timeout,
2501+
)
2502+
24762503
def convert_to_word(
24772504
self,
24782505
file: PdfRestFile | Sequence[PdfRestFile],
@@ -3320,6 +3347,32 @@ async def convert_to_powerpoint(
33203347
timeout=timeout,
33213348
)
33223349

3350+
async def convert_xfa_to_acroforms(
3351+
self,
3352+
file: PdfRestFile | Sequence[PdfRestFile],
3353+
*,
3354+
output: str | None = None,
3355+
extra_query: Query | None = None,
3356+
extra_headers: AnyMapping | None = None,
3357+
extra_body: Body | None = None,
3358+
timeout: TimeoutTypes | None = None,
3359+
) -> PdfRestFileBasedResponse:
3360+
"""Asynchronously convert an XFA PDF to an AcroForm-enabled PDF."""
3361+
3362+
payload: dict[str, Any] = {"files": file}
3363+
if output is not None:
3364+
payload["output"] = output
3365+
3366+
return await self._post_file_operation(
3367+
endpoint="/pdf-with-acroforms",
3368+
payload=payload,
3369+
payload_model=PdfXfaToAcroformsPayload,
3370+
extra_query=extra_query,
3371+
extra_headers=extra_headers,
3372+
extra_body=extra_body,
3373+
timeout=timeout,
3374+
)
3375+
33233376
async def convert_to_word(
33243377
self,
33253378
file: PdfRestFile | Sequence[PdfRestFile],

src/pdfrest/models/_internal.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -841,6 +841,30 @@ class PdfFlattenFormsPayload(BaseModel):
841841
] = None
842842

843843

844+
class PdfXfaToAcroformsPayload(BaseModel):
845+
"""Adapt caller options into a pdfRest-ready XFA-to-AcroForms request payload."""
846+
847+
files: Annotated[
848+
list[PdfRestFile],
849+
Field(
850+
min_length=1,
851+
max_length=1,
852+
validation_alias=AliasChoices("file", "files"),
853+
serialization_alias="id",
854+
),
855+
BeforeValidator(_ensure_list),
856+
AfterValidator(
857+
_allowed_mime_types("application/pdf", error_msg="Must be a PDF file")
858+
),
859+
PlainSerializer(_serialize_as_first_file_id),
860+
]
861+
output: Annotated[
862+
str | None,
863+
Field(serialization_alias="output", min_length=1, default=None),
864+
AfterValidator(_validate_output_prefix),
865+
] = None
866+
867+
844868
class PdfLinearizePayload(BaseModel):
845869
"""Adapt caller options into a pdfRest-ready linearize PDF request payload."""
846870

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
from __future__ import annotations
2+
3+
import pytest
4+
5+
from pdfrest import AsyncPdfRestClient, PdfRestApiError, PdfRestClient
6+
from pdfrest.models import PdfRestFile
7+
8+
from ..resources import get_test_resource_path
9+
10+
11+
@pytest.fixture(scope="module")
12+
def uploaded_pdf_for_acroforms(
13+
pdfrest_api_key: str,
14+
pdfrest_live_base_url: str,
15+
) -> PdfRestFile:
16+
resource = get_test_resource_path("report.pdf")
17+
with PdfRestClient(
18+
api_key=pdfrest_api_key,
19+
base_url=pdfrest_live_base_url,
20+
) as client:
21+
return client.files.create_from_paths([resource])[0]
22+
23+
24+
@pytest.mark.parametrize(
25+
"output_name",
26+
[
27+
pytest.param(None, id="default-output"),
28+
pytest.param("live-acroforms", id="custom-output"),
29+
],
30+
)
31+
def test_live_convert_xfa_to_acroforms_success(
32+
pdfrest_api_key: str,
33+
pdfrest_live_base_url: str,
34+
uploaded_pdf_for_acroforms: PdfRestFile,
35+
output_name: str | None,
36+
) -> None:
37+
kwargs: dict[str, str] = {}
38+
if output_name is not None:
39+
kwargs["output"] = output_name
40+
41+
with PdfRestClient(
42+
api_key=pdfrest_api_key,
43+
base_url=pdfrest_live_base_url,
44+
) as client:
45+
response = client.convert_xfa_to_acroforms(uploaded_pdf_for_acroforms, **kwargs)
46+
47+
assert response.output_files
48+
output_file = response.output_file
49+
assert output_file.type == "application/pdf"
50+
assert str(response.input_id) == str(uploaded_pdf_for_acroforms.id)
51+
if output_name is not None:
52+
assert output_file.name.startswith(output_name)
53+
else:
54+
assert output_file.name.endswith(".pdf")
55+
56+
57+
def test_live_convert_xfa_to_acroforms_invalid_file_id(
58+
pdfrest_api_key: str,
59+
pdfrest_live_base_url: str,
60+
uploaded_pdf_for_acroforms: PdfRestFile,
61+
) -> None:
62+
with (
63+
PdfRestClient(
64+
api_key=pdfrest_api_key,
65+
base_url=pdfrest_live_base_url,
66+
) as client,
67+
pytest.raises(PdfRestApiError),
68+
):
69+
client.convert_xfa_to_acroforms(
70+
uploaded_pdf_for_acroforms,
71+
extra_body={"id": "00000000-0000-0000-0000-000000000000"},
72+
)
73+
74+
75+
@pytest.mark.asyncio
76+
async def test_live_async_convert_xfa_to_acroforms_success(
77+
pdfrest_api_key: str,
78+
pdfrest_live_base_url: str,
79+
uploaded_pdf_for_acroforms: PdfRestFile,
80+
) -> None:
81+
async with AsyncPdfRestClient(
82+
api_key=pdfrest_api_key,
83+
base_url=pdfrest_live_base_url,
84+
) as client:
85+
response = await client.convert_xfa_to_acroforms(
86+
uploaded_pdf_for_acroforms, output="async"
87+
)
88+
89+
assert response.output_files
90+
output_file = response.output_file
91+
assert output_file.name.startswith("async")
92+
assert output_file.type == "application/pdf"
93+
assert str(response.input_id) == str(uploaded_pdf_for_acroforms.id)
94+
95+
96+
@pytest.mark.asyncio
97+
async def test_live_async_convert_xfa_to_acroforms_invalid_file_id(
98+
pdfrest_api_key: str,
99+
pdfrest_live_base_url: str,
100+
uploaded_pdf_for_acroforms: PdfRestFile,
101+
) -> None:
102+
async with AsyncPdfRestClient(
103+
api_key=pdfrest_api_key,
104+
base_url=pdfrest_live_base_url,
105+
) as client:
106+
with pytest.raises(PdfRestApiError):
107+
await client.convert_xfa_to_acroforms(
108+
uploaded_pdf_for_acroforms,
109+
extra_body={"id": "ffffffff-ffff-ffff-ffff-ffffffffffff"},
110+
)

0 commit comments

Comments
 (0)