Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions src/pdfrest/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
PdfAddAttachmentPayload,
PdfAddImagePayload,
PdfAddTextPayload,
PdfBlankPayload,
PdfCompressPayload,
PdfDecryptPayload,
PdfEncryptPayload,
Expand Down Expand Up @@ -128,7 +129,9 @@
PdfAType,
PdfInfoQuery,
PdfMergeInput,
PdfPageOrientation,
PdfPageSelection,
PdfPageSize,
PdfRedactionInstruction,
PdfRestriction,
PdfRGBColor,
Expand Down Expand Up @@ -3071,6 +3074,39 @@ def add_attachment_to_pdf(
timeout=timeout,
)

def blank_pdf(
self,
*,
page_size: PdfPageSize = "letter",
page_count: int = 1,
page_orientation: PdfPageOrientation | None = None,
Comment thread
datalogics-kam marked this conversation as resolved.
output: str | None = None,
extra_query: Query | None = None,
extra_headers: AnyMapping | None = None,
extra_body: Body | None = None,
timeout: TimeoutTypes | None = None,
) -> PdfRestFileBasedResponse:
"""Create a blank PDF with configurable size, count, and orientation."""

payload: dict[str, Any] = {
"page_size": page_size,
"page_count": page_count,
}
if page_orientation is not None:
payload["page_orientation"] = page_orientation
if output is not None:
payload["output"] = output

return self._post_file_operation(
endpoint="/blank-pdf",
payload=payload,
payload_model=PdfBlankPayload,
extra_query=extra_query,
extra_headers=extra_headers,
extra_body=extra_body,
timeout=timeout,
)

def flatten_transparencies(
self,
file: PdfRestFile | Sequence[PdfRestFile],
Expand Down Expand Up @@ -4477,6 +4513,39 @@ async def add_attachment_to_pdf(
timeout=timeout,
)

async def blank_pdf(
self,
*,
page_size: PdfPageSize = "letter",
page_count: int = 1,
page_orientation: PdfPageOrientation | None = None,
output: str | None = None,
extra_query: Query | None = None,
extra_headers: AnyMapping | None = None,
extra_body: Body | None = None,
timeout: TimeoutTypes | None = None,
) -> PdfRestFileBasedResponse:
"""Asynchronously create a blank PDF with configurable size and count."""

payload: dict[str, Any] = {
"page_size": page_size,
"page_count": page_count,
}
if page_orientation is not None:
payload["page_orientation"] = page_orientation
if output is not None:
payload["output"] = output

return await self._post_file_operation(
endpoint="/blank-pdf",
payload=payload,
payload_model=PdfBlankPayload,
extra_query=extra_query,
extra_headers=extra_headers,
extra_body=extra_body,
timeout=timeout,
)

async def flatten_transparencies(
self,
file: PdfRestFile | Sequence[PdfRestFile],
Expand Down
95 changes: 92 additions & 3 deletions src/pdfrest/models/_internal.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
from __future__ import annotations

import re
from collections.abc import Callable, Sequence
from collections.abc import Callable, Mapping, Sequence
from pathlib import PurePath
from typing import Annotated, Any, Generic, Literal, TypeVar
from typing import Annotated, Any, Generic, Literal, TypeVar, cast

from langcodes import tag_is_valid
from pydantic import (
Expand All @@ -26,6 +26,8 @@
OcrLanguage,
PdfAType,
PdfInfoQuery,
PdfPageOrientation,
PdfPageSize,
PdfRestriction,
PdfXType,
SummaryFormat,
Expand Down Expand Up @@ -1365,6 +1367,89 @@ class PdfAddAttachmentPayload(BaseModel):
] = None


class PdfBlankPayload(BaseModel):
"""Adapt caller options into a pdfRest-ready blank PDF request payload."""

page_size: Annotated[
PdfPageSize | Literal["custom"],
Field(serialization_alias="page_size"),
]
page_count: Annotated[
int,
Field(serialization_alias="page_count", ge=1, le=1000),
Comment thread
datalogics-kam marked this conversation as resolved.
]
page_orientation: Annotated[
PdfPageOrientation | None,
Field(serialization_alias="page_orientation", default=None),
Comment thread
datalogics-kam marked this conversation as resolved.
] = None
custom_height: Annotated[
float | None,
Field(serialization_alias="custom_height", gt=0, default=None),
] = None
custom_width: Annotated[
float | None,
Field(serialization_alias="custom_width", gt=0, default=None),
] = None
output: Annotated[
str | None,
Field(serialization_alias="output", min_length=1, default=None),
AfterValidator(_validate_output_prefix),
] = None

@model_validator(mode="before")
@classmethod
def _normalize_custom_page_size(cls, data: Any) -> Any:
if not isinstance(data, Mapping):
return data

normalized_data: dict[str, Any] = dict(cast(Mapping[str, Any], data))
page_size = normalized_data.get("page_size")
if isinstance(page_size, Mapping):
custom_page_size = cast(Mapping[str, Any], page_size)
if (
"custom_height" not in custom_page_size
or "custom_width" not in custom_page_size
):
msg = (
"Custom page sizes must include both custom_height and "
"custom_width."
)
raise ValueError(msg)
normalized_data["page_size"] = "custom"
normalized_data["custom_height"] = custom_page_size["custom_height"]
normalized_data["custom_width"] = custom_page_size["custom_width"]

if (
normalized_data.get("page_size") is not None
and normalized_data.get("page_size") != "custom"
and normalized_data.get("page_orientation") is None
):
normalized_data["page_orientation"] = "portrait"

return normalized_data

@model_validator(mode="after")
def _validate_page_configuration(self) -> PdfBlankPayload:
Comment thread
datalogics-kam marked this conversation as resolved.
is_custom = self.page_size == "custom"
has_custom_height = self.custom_height is not None
has_custom_width = self.custom_width is not None
if is_custom:
if not (has_custom_height and has_custom_width):
msg = "custom_height and custom_width are required when page_size is 'custom'."
raise ValueError(msg)
if self.page_orientation is not None:
msg = "page_orientation must be omitted when page_size is 'custom'."
raise ValueError(msg)
else:
if self.page_orientation is None:
msg = "page_orientation is required when page_size is not 'custom'."
raise ValueError(msg)
if has_custom_height or has_custom_width:
msg = "custom_height and custom_width can only be provided when page_size is 'custom'."
raise ValueError(msg)
return self


class PdfRestrictPayload(BaseModel):
"""Adapt caller options into a pdfRest-ready restrict-PDF request payload."""

Expand Down Expand Up @@ -1603,7 +1688,11 @@ class PdfRestRawFileResponse(BaseModel):

input_id: Annotated[
list[PdfRestFileID],
Field(alias="inputId", description="The id of the input file"),
Field(
alias="inputId",
description="The id of the input file",
default_factory=list,
),
BeforeValidator(_ensure_list),
]
output_urls: Annotated[
Expand Down
1 change: 0 additions & 1 deletion src/pdfrest/models/public.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ class PdfRestFileBasedResponse(BaseModel):
list[PdfRestFileID],
Field(
description="The ids of the files that were input to the pdfRest operation",
min_length=1,
validation_alias=AliasChoices("input_id", "inputId"),
),
]
Expand Down
6 changes: 6 additions & 0 deletions src/pdfrest/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
PdfAddTextObject,
PdfAType,
PdfCmykColor,
PdfCustomPageSize,
PdfInfoQuery,
PdfMergeInput,
PdfMergeSource,
PdfPageOrientation,
PdfPageSelection,
PdfPageSize,
PdfRedactionInstruction,
PdfRedactionPreset,
PdfRedactionType,
Expand Down Expand Up @@ -48,10 +51,13 @@
"PdfAType",
"PdfAddTextObject",
"PdfCmykColor",
"PdfCustomPageSize",
"PdfInfoQuery",
"PdfMergeInput",
"PdfMergeSource",
"PdfPageOrientation",
"PdfPageSelection",
"PdfPageSize",
"PdfRGBColor",
"PdfRedactionInstruction",
"PdfRedactionPreset",
Expand Down
10 changes: 10 additions & 0 deletions src/pdfrest/types/public.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,13 @@
"PdfAType",
"PdfAddTextObject",
"PdfCmykColor",
"PdfCustomPageSize",
"PdfInfoQuery",
"PdfMergeInput",
"PdfMergeSource",
"PdfPageOrientation",
"PdfPageSelection",
"PdfPageSize",
"PdfRGBColor",
"PdfRedactionInstruction",
"PdfRedactionPreset",
Expand Down Expand Up @@ -126,6 +129,11 @@ class PdfAddTextObject(TypedDict, total=False):
is_right_to_left: bool


class PdfCustomPageSize(TypedDict):
custom_height: Required[float]
custom_width: Required[float]


PdfPageSelection = str | int | Sequence[str | int]


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