Skip to content

Commit 5fc736f

Browse files
Merge pull request #14 from datalogics-cgreen/pdfcloud-5464-blank-pdf
PDFCLOUD-5549 Add Blank PDF client methods
2 parents 6387bd1 + 5eca382 commit 5fc736f

7 files changed

Lines changed: 1133 additions & 4 deletions

File tree

src/pdfrest/client.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
PdfAddAttachmentPayload,
8686
PdfAddImagePayload,
8787
PdfAddTextPayload,
88+
PdfBlankPayload,
8889
PdfCompressPayload,
8990
PdfDecryptPayload,
9091
PdfEncryptPayload,
@@ -128,7 +129,9 @@
128129
PdfAType,
129130
PdfInfoQuery,
130131
PdfMergeInput,
132+
PdfPageOrientation,
131133
PdfPageSelection,
134+
PdfPageSize,
132135
PdfRedactionInstruction,
133136
PdfRestriction,
134137
PdfRGBColor,
@@ -3071,6 +3074,39 @@ def add_attachment_to_pdf(
30713074
timeout=timeout,
30723075
)
30733076

3077+
def blank_pdf(
3078+
self,
3079+
*,
3080+
page_size: PdfPageSize = "letter",
3081+
page_count: int = 1,
3082+
page_orientation: PdfPageOrientation | None = None,
3083+
output: str | None = None,
3084+
extra_query: Query | None = None,
3085+
extra_headers: AnyMapping | None = None,
3086+
extra_body: Body | None = None,
3087+
timeout: TimeoutTypes | None = None,
3088+
) -> PdfRestFileBasedResponse:
3089+
"""Create a blank PDF with configurable size, count, and orientation."""
3090+
3091+
payload: dict[str, Any] = {
3092+
"page_size": page_size,
3093+
"page_count": page_count,
3094+
}
3095+
if page_orientation is not None:
3096+
payload["page_orientation"] = page_orientation
3097+
if output is not None:
3098+
payload["output"] = output
3099+
3100+
return self._post_file_operation(
3101+
endpoint="/blank-pdf",
3102+
payload=payload,
3103+
payload_model=PdfBlankPayload,
3104+
extra_query=extra_query,
3105+
extra_headers=extra_headers,
3106+
extra_body=extra_body,
3107+
timeout=timeout,
3108+
)
3109+
30743110
def flatten_transparencies(
30753111
self,
30763112
file: PdfRestFile | Sequence[PdfRestFile],
@@ -4477,6 +4513,39 @@ async def add_attachment_to_pdf(
44774513
timeout=timeout,
44784514
)
44794515

4516+
async def blank_pdf(
4517+
self,
4518+
*,
4519+
page_size: PdfPageSize = "letter",
4520+
page_count: int = 1,
4521+
page_orientation: PdfPageOrientation | None = None,
4522+
output: str | None = None,
4523+
extra_query: Query | None = None,
4524+
extra_headers: AnyMapping | None = None,
4525+
extra_body: Body | None = None,
4526+
timeout: TimeoutTypes | None = None,
4527+
) -> PdfRestFileBasedResponse:
4528+
"""Asynchronously create a blank PDF with configurable size and count."""
4529+
4530+
payload: dict[str, Any] = {
4531+
"page_size": page_size,
4532+
"page_count": page_count,
4533+
}
4534+
if page_orientation is not None:
4535+
payload["page_orientation"] = page_orientation
4536+
if output is not None:
4537+
payload["output"] = output
4538+
4539+
return await self._post_file_operation(
4540+
endpoint="/blank-pdf",
4541+
payload=payload,
4542+
payload_model=PdfBlankPayload,
4543+
extra_query=extra_query,
4544+
extra_headers=extra_headers,
4545+
extra_body=extra_body,
4546+
timeout=timeout,
4547+
)
4548+
44804549
async def flatten_transparencies(
44814550
self,
44824551
file: PdfRestFile | Sequence[PdfRestFile],

src/pdfrest/models/_internal.py

Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from __future__ import annotations
22

33
import re
4-
from collections.abc import Callable, Sequence
4+
from collections.abc import Callable, Mapping, Sequence
55
from pathlib import PurePath
6-
from typing import Annotated, Any, Generic, Literal, TypeVar
6+
from typing import Annotated, Any, Generic, Literal, TypeVar, cast
77

88
from langcodes import tag_is_valid
99
from pydantic import (
@@ -26,6 +26,8 @@
2626
OcrLanguage,
2727
PdfAType,
2828
PdfInfoQuery,
29+
PdfPageOrientation,
30+
PdfPageSize,
2931
PdfRestriction,
3032
PdfXType,
3133
SummaryFormat,
@@ -1365,6 +1367,89 @@ class PdfAddAttachmentPayload(BaseModel):
13651367
] = None
13661368

13671369

1370+
class PdfBlankPayload(BaseModel):
1371+
"""Adapt caller options into a pdfRest-ready blank PDF request payload."""
1372+
1373+
page_size: Annotated[
1374+
PdfPageSize | Literal["custom"],
1375+
Field(serialization_alias="page_size"),
1376+
]
1377+
page_count: Annotated[
1378+
int,
1379+
Field(serialization_alias="page_count", ge=1, le=1000),
1380+
]
1381+
page_orientation: Annotated[
1382+
PdfPageOrientation | None,
1383+
Field(serialization_alias="page_orientation", default=None),
1384+
] = None
1385+
custom_height: Annotated[
1386+
float | None,
1387+
Field(serialization_alias="custom_height", gt=0, default=None),
1388+
] = None
1389+
custom_width: Annotated[
1390+
float | None,
1391+
Field(serialization_alias="custom_width", gt=0, default=None),
1392+
] = None
1393+
output: Annotated[
1394+
str | None,
1395+
Field(serialization_alias="output", min_length=1, default=None),
1396+
AfterValidator(_validate_output_prefix),
1397+
] = None
1398+
1399+
@model_validator(mode="before")
1400+
@classmethod
1401+
def _normalize_custom_page_size(cls, data: Any) -> Any:
1402+
if not isinstance(data, Mapping):
1403+
return data
1404+
1405+
normalized_data: dict[str, Any] = dict(cast(Mapping[str, Any], data))
1406+
page_size = normalized_data.get("page_size")
1407+
if isinstance(page_size, Mapping):
1408+
custom_page_size = cast(Mapping[str, Any], page_size)
1409+
if (
1410+
"custom_height" not in custom_page_size
1411+
or "custom_width" not in custom_page_size
1412+
):
1413+
msg = (
1414+
"Custom page sizes must include both custom_height and "
1415+
"custom_width."
1416+
)
1417+
raise ValueError(msg)
1418+
normalized_data["page_size"] = "custom"
1419+
normalized_data["custom_height"] = custom_page_size["custom_height"]
1420+
normalized_data["custom_width"] = custom_page_size["custom_width"]
1421+
1422+
if (
1423+
normalized_data.get("page_size") is not None
1424+
and normalized_data.get("page_size") != "custom"
1425+
and normalized_data.get("page_orientation") is None
1426+
):
1427+
normalized_data["page_orientation"] = "portrait"
1428+
1429+
return normalized_data
1430+
1431+
@model_validator(mode="after")
1432+
def _validate_page_configuration(self) -> PdfBlankPayload:
1433+
is_custom = self.page_size == "custom"
1434+
has_custom_height = self.custom_height is not None
1435+
has_custom_width = self.custom_width is not None
1436+
if is_custom:
1437+
if not (has_custom_height and has_custom_width):
1438+
msg = "custom_height and custom_width are required when page_size is 'custom'."
1439+
raise ValueError(msg)
1440+
if self.page_orientation is not None:
1441+
msg = "page_orientation must be omitted when page_size is 'custom'."
1442+
raise ValueError(msg)
1443+
else:
1444+
if self.page_orientation is None:
1445+
msg = "page_orientation is required when page_size is not 'custom'."
1446+
raise ValueError(msg)
1447+
if has_custom_height or has_custom_width:
1448+
msg = "custom_height and custom_width can only be provided when page_size is 'custom'."
1449+
raise ValueError(msg)
1450+
return self
1451+
1452+
13681453
class PdfRestrictPayload(BaseModel):
13691454
"""Adapt caller options into a pdfRest-ready restrict-PDF request payload."""
13701455

@@ -1603,7 +1688,11 @@ class PdfRestRawFileResponse(BaseModel):
16031688

16041689
input_id: Annotated[
16051690
list[PdfRestFileID],
1606-
Field(alias="inputId", description="The id of the input file"),
1691+
Field(
1692+
alias="inputId",
1693+
description="The id of the input file",
1694+
default_factory=list,
1695+
),
16071696
BeforeValidator(_ensure_list),
16081697
]
16091698
output_urls: Annotated[

src/pdfrest/models/public.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,6 @@ class PdfRestFileBasedResponse(BaseModel):
269269
list[PdfRestFileID],
270270
Field(
271271
description="The ids of the files that were input to the pdfRest operation",
272-
min_length=1,
273272
validation_alias=AliasChoices("input_id", "inputId"),
274273
),
275274
]

src/pdfrest/types/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@
1515
PdfAddTextObject,
1616
PdfAType,
1717
PdfCmykColor,
18+
PdfCustomPageSize,
1819
PdfInfoQuery,
1920
PdfMergeInput,
2021
PdfMergeSource,
22+
PdfPageOrientation,
2123
PdfPageSelection,
24+
PdfPageSize,
2225
PdfRedactionInstruction,
2326
PdfRedactionPreset,
2427
PdfRedactionType,
@@ -48,10 +51,13 @@
4851
"PdfAType",
4952
"PdfAddTextObject",
5053
"PdfCmykColor",
54+
"PdfCustomPageSize",
5155
"PdfInfoQuery",
5256
"PdfMergeInput",
5357
"PdfMergeSource",
58+
"PdfPageOrientation",
5459
"PdfPageSelection",
60+
"PdfPageSize",
5561
"PdfRGBColor",
5662
"PdfRedactionInstruction",
5763
"PdfRedactionPreset",

src/pdfrest/types/public.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,13 @@
2727
"PdfAType",
2828
"PdfAddTextObject",
2929
"PdfCmykColor",
30+
"PdfCustomPageSize",
3031
"PdfInfoQuery",
3132
"PdfMergeInput",
3233
"PdfMergeSource",
34+
"PdfPageOrientation",
3335
"PdfPageSelection",
36+
"PdfPageSize",
3437
"PdfRGBColor",
3538
"PdfRedactionInstruction",
3639
"PdfRedactionPreset",
@@ -126,6 +129,11 @@ class PdfAddTextObject(TypedDict, total=False):
126129
is_right_to_left: bool
127130

128131

132+
class PdfCustomPageSize(TypedDict):
133+
custom_height: Required[float]
134+
custom_width: Required[float]
135+
136+
129137
PdfPageSelection = str | int | Sequence[str | int]
130138

131139

@@ -197,3 +205,5 @@ class PdfMergeSource(TypedDict, total=False):
197205
ALL_PDF_RESTRICTIONS: tuple[PdfRestriction, ...] = cast(
198206
tuple[PdfRestriction, ...], get_args(PdfRestriction)
199207
)
208+
PdfPageSize = Literal["letter", "legal", "ledger", "A3", "A4", "A5"] | PdfCustomPageSize
209+
PdfPageOrientation = Literal["portrait", "landscape"]

0 commit comments

Comments
 (0)