Skip to content

Commit 63203d6

Browse files
Merge pull request #17 from datalogics-cgreen/pdfcloud-5464-form-data
PDFCLOUD-5552 Add client methods to import and export PDF form data
2 parents 8b5fc49 + 67cc64e commit 63203d6

16 files changed

Lines changed: 2457 additions & 0 deletions

src/pdfrest/client.py

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,12 @@
9595
PdfCompressPayload,
9696
PdfDecryptPayload,
9797
PdfEncryptPayload,
98+
PdfExportFormDataPayload,
9899
PdfFlattenAnnotationsPayload,
99100
PdfFlattenFormsPayload,
100101
PdfFlattenLayersPayload,
101102
PdfFlattenTransparenciesPayload,
103+
PdfImportFormDataPayload,
102104
PdfInfoPayload,
103105
PdfLinearizePayload,
104106
PdfMergePayload,
@@ -127,6 +129,7 @@
127129
ALL_PDF_INFO_QUERIES,
128130
BmpColorModel,
129131
CompressionLevel,
132+
ExportDataFormat,
130133
ExtractTextGranularity,
131134
FlattenQuality,
132135
GifColorModel,
@@ -2846,6 +2849,68 @@ def convert_to_word(
28462849
timeout=timeout,
28472850
)
28482851

2852+
def import_form_data(
2853+
self,
2854+
file: PdfRestFile | Sequence[PdfRestFile],
2855+
data_file: PdfRestFile | Sequence[PdfRestFile],
2856+
*,
2857+
output: str | None = None,
2858+
extra_query: Query | None = None,
2859+
extra_headers: AnyMapping | None = None,
2860+
extra_body: Body | None = None,
2861+
timeout: TimeoutTypes | None = None,
2862+
) -> PdfRestFileBasedResponse:
2863+
"""Import form data from a data file into an existing PDF with form fields."""
2864+
2865+
payload: dict[str, Any] = {"files": file, "data_file": data_file}
2866+
if output is not None:
2867+
payload["output"] = output
2868+
2869+
return self._post_file_operation(
2870+
endpoint="/pdf-with-imported-form-data",
2871+
payload=payload,
2872+
payload_model=PdfImportFormDataPayload,
2873+
extra_query=extra_query,
2874+
extra_headers=extra_headers,
2875+
extra_body=extra_body,
2876+
timeout=timeout,
2877+
)
2878+
2879+
def export_form_data(
2880+
self,
2881+
file: PdfRestFile | Sequence[PdfRestFile],
2882+
*,
2883+
data_format: ExportDataFormat,
2884+
output: str | None = None,
2885+
extra_query: Query | None = None,
2886+
extra_headers: AnyMapping | None = None,
2887+
extra_body: Body | None = None,
2888+
timeout: TimeoutTypes | None = None,
2889+
) -> PdfRestFileBasedResponse:
2890+
"""Export form data from a PDF into an external data file.
2891+
2892+
`data_format` support depends on detected form type:
2893+
- AcroForm PDFs: `xfdf`, `fdf`, `xml`
2894+
- XFA PDFs: `xfd`, `xdp`, `xml`
2895+
"""
2896+
2897+
payload: dict[str, Any] = {
2898+
"files": file,
2899+
"data_format": data_format,
2900+
}
2901+
if output is not None:
2902+
payload["output"] = output
2903+
2904+
return self._post_file_operation(
2905+
endpoint="/exported-form-data",
2906+
payload=payload,
2907+
payload_model=PdfExportFormDataPayload,
2908+
extra_query=extra_query,
2909+
extra_headers=extra_headers,
2910+
extra_body=extra_body,
2911+
timeout=timeout,
2912+
)
2913+
28492914
def flatten_pdf_forms(
28502915
self,
28512916
file: PdfRestFile | Sequence[PdfRestFile],
@@ -4549,6 +4614,68 @@ async def convert_to_word(
45494614
timeout=timeout,
45504615
)
45514616

4617+
async def import_form_data(
4618+
self,
4619+
file: PdfRestFile | Sequence[PdfRestFile],
4620+
data_file: PdfRestFile | Sequence[PdfRestFile],
4621+
*,
4622+
output: str | None = None,
4623+
extra_query: Query | None = None,
4624+
extra_headers: AnyMapping | None = None,
4625+
extra_body: Body | None = None,
4626+
timeout: TimeoutTypes | None = None,
4627+
) -> PdfRestFileBasedResponse:
4628+
"""Asynchronously import form data from a data file into a PDF."""
4629+
4630+
payload: dict[str, Any] = {"files": file, "data_file": data_file}
4631+
if output is not None:
4632+
payload["output"] = output
4633+
4634+
return await self._post_file_operation(
4635+
endpoint="/pdf-with-imported-form-data",
4636+
payload=payload,
4637+
payload_model=PdfImportFormDataPayload,
4638+
extra_query=extra_query,
4639+
extra_headers=extra_headers,
4640+
extra_body=extra_body,
4641+
timeout=timeout,
4642+
)
4643+
4644+
async def export_form_data(
4645+
self,
4646+
file: PdfRestFile | Sequence[PdfRestFile],
4647+
*,
4648+
data_format: ExportDataFormat,
4649+
output: str | None = None,
4650+
extra_query: Query | None = None,
4651+
extra_headers: AnyMapping | None = None,
4652+
extra_body: Body | None = None,
4653+
timeout: TimeoutTypes | None = None,
4654+
) -> PdfRestFileBasedResponse:
4655+
"""Asynchronously export form data from a PDF into a data file.
4656+
4657+
`data_format` support depends on detected form type:
4658+
- AcroForm PDFs: `xfdf`, `fdf`, `xml`
4659+
- XFA PDFs: `xfd`, `xdp`, `xml`
4660+
"""
4661+
4662+
payload: dict[str, Any] = {
4663+
"files": file,
4664+
"data_format": data_format,
4665+
}
4666+
if output is not None:
4667+
payload["output"] = output
4668+
4669+
return await self._post_file_operation(
4670+
endpoint="/exported-form-data",
4671+
payload=payload,
4672+
payload_model=PdfExportFormDataPayload,
4673+
extra_query=extra_query,
4674+
extra_headers=extra_headers,
4675+
extra_body=extra_body,
4676+
timeout=timeout,
4677+
)
4678+
45524679
async def flatten_pdf_forms(
45534680
self,
45544681
file: PdfRestFile | Sequence[PdfRestFile],

src/pdfrest/models/_internal.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from pdfrest.types.public import PdfRedactionPreset
2424

2525
from ..types import (
26+
ExportDataFormat,
2627
HtmlPageOrientation,
2728
HtmlPageSize,
2829
HtmlWebLayout,
@@ -1318,6 +1319,80 @@ class PdfFlattenFormsPayload(BaseModel):
13181319
] = None
13191320

13201321

1322+
class PdfImportFormDataPayload(BaseModel):
1323+
"""Adapt caller options into a pdfRest-ready import-form-data request payload."""
1324+
1325+
files: Annotated[
1326+
list[PdfRestFile],
1327+
Field(
1328+
min_length=1,
1329+
max_length=1,
1330+
validation_alias=AliasChoices("file", "files"),
1331+
serialization_alias="id",
1332+
),
1333+
BeforeValidator(_ensure_list),
1334+
AfterValidator(
1335+
_allowed_mime_types("application/pdf", error_msg="Must be a PDF file")
1336+
),
1337+
PlainSerializer(_serialize_as_first_file_id),
1338+
]
1339+
data_file: Annotated[
1340+
list[PdfRestFile],
1341+
Field(
1342+
min_length=1,
1343+
max_length=1,
1344+
validation_alias=AliasChoices("data_file", "data_files"),
1345+
serialization_alias="data_file_id",
1346+
),
1347+
BeforeValidator(_ensure_list),
1348+
AfterValidator(
1349+
_allowed_mime_types(
1350+
"application/xml",
1351+
"text/xml",
1352+
"application/vnd.fdf",
1353+
"application/vnd.adobe.xfdf",
1354+
"application/vnd.adobe.xdp+xml",
1355+
"application/vnd.adobe.xfd+xml",
1356+
error_msg="Data file must be an XFDF, XDP, XFD, FDF, or XML file",
1357+
)
1358+
),
1359+
PlainSerializer(_serialize_as_first_file_id),
1360+
]
1361+
output: Annotated[
1362+
str | None,
1363+
Field(serialization_alias="output", min_length=1, default=None),
1364+
AfterValidator(_validate_output_prefix),
1365+
] = None
1366+
1367+
1368+
class PdfExportFormDataPayload(BaseModel):
1369+
"""Adapt caller options into a pdfRest-ready export-form-data request payload."""
1370+
1371+
files: Annotated[
1372+
list[PdfRestFile],
1373+
Field(
1374+
min_length=1,
1375+
max_length=1,
1376+
validation_alias=AliasChoices("file", "files"),
1377+
serialization_alias="id",
1378+
),
1379+
BeforeValidator(_ensure_list),
1380+
AfterValidator(
1381+
_allowed_mime_types("application/pdf", error_msg="Must be a PDF file")
1382+
),
1383+
PlainSerializer(_serialize_as_first_file_id),
1384+
]
1385+
data_format: Annotated[
1386+
ExportDataFormat,
1387+
Field(serialization_alias="data_format"),
1388+
]
1389+
output: Annotated[
1390+
str | None,
1391+
Field(serialization_alias="output", min_length=1, default=None),
1392+
AfterValidator(_validate_output_prefix),
1393+
] = None
1394+
1395+
13211396
class PdfCompressPayload(BaseModel):
13221397
"""Adapt caller options into a pdfRest-ready compress request payload."""
13231398

src/pdfrest/types/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
ALL_PDF_RESTRICTIONS,
77
BmpColorModel,
88
CompressionLevel,
9+
ExportDataFormat,
910
ExtractTextGranularity,
1011
FlattenQuality,
1112
GifColorModel,
@@ -48,6 +49,7 @@
4849
"ALL_PDF_RESTRICTIONS",
4950
"BmpColorModel",
5051
"CompressionLevel",
52+
"ExportDataFormat",
5153
"ExtractTextGranularity",
5254
"FlattenQuality",
5355
"GifColorModel",

src/pdfrest/types/public.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"ALL_PDF_RESTRICTIONS",
1919
"BmpColorModel",
2020
"CompressionLevel",
21+
"ExportDataFormat",
2122
"ExtractTextGranularity",
2223
"FlattenQuality",
2324
"GifColorModel",
@@ -167,6 +168,9 @@ class PdfMergeSource(TypedDict, total=False):
167168
JpegColorModel = Literal["rgb", "cmyk", "gray"]
168169
TiffColorModel = Literal["rgb", "rgba", "cmyk", "lab", "gray"]
169170
GraphicSmoothing = Literal["none", "all", "text", "line", "image"]
171+
# Server accepts all values here, but enforces form-type subsets at runtime:
172+
# AcroForm -> xfdf/fdf/xml, XFA -> xfd/xdp/xml.
173+
ExportDataFormat = Literal["fdf", "xfdf", "xml", "xdp", "xfd"]
170174

171175
SummaryFormat = Literal[
172176
"overview",

0 commit comments

Comments
 (0)