Skip to content

Commit fab2536

Browse files
Add Flatten Layers methods
Assisted-by: Codex
1 parent 3d1c459 commit fab2536

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
@@ -87,6 +87,7 @@
8787
PdfEncryptPayload,
8888
PdfFlattenAnnotationsPayload,
8989
PdfFlattenFormsPayload,
90+
PdfFlattenLayersPayload,
9091
PdfFlattenTransparenciesPayload,
9192
PdfInfoPayload,
9293
PdfLinearizePayload,
@@ -3052,6 +3053,32 @@ def flatten_annotations(
30523053
timeout=timeout,
30533054
)
30543055

3056+
def flatten_layers(
3057+
self,
3058+
file: PdfRestFile | Sequence[PdfRestFile],
3059+
*,
3060+
output: str | None = None,
3061+
extra_query: Query | None = None,
3062+
extra_headers: AnyMapping | None = None,
3063+
extra_body: Body | None = None,
3064+
timeout: TimeoutTypes | None = None,
3065+
) -> PdfRestFileBasedResponse:
3066+
"""Flatten all layers in a PDF into a single layer."""
3067+
3068+
payload: dict[str, Any] = {"files": file}
3069+
if output is not None:
3070+
payload["output"] = output
3071+
3072+
return self._post_file_operation(
3073+
endpoint="/flattened-layers-pdf",
3074+
payload=payload,
3075+
payload_model=PdfFlattenLayersPayload,
3076+
extra_query=extra_query,
3077+
extra_headers=extra_headers,
3078+
extra_body=extra_body,
3079+
timeout=timeout,
3080+
)
3081+
30553082
def rasterize_pdf(
30563083
self,
30573084
file: PdfRestFile | Sequence[PdfRestFile],
@@ -4339,6 +4366,32 @@ async def flatten_annotations(
43394366
timeout=timeout,
43404367
)
43414368

4369+
async def flatten_layers(
4370+
self,
4371+
file: PdfRestFile | Sequence[PdfRestFile],
4372+
*,
4373+
output: str | None = None,
4374+
extra_query: Query | None = None,
4375+
extra_headers: AnyMapping | None = None,
4376+
extra_body: Body | None = None,
4377+
timeout: TimeoutTypes | None = None,
4378+
) -> PdfRestFileBasedResponse:
4379+
"""Asynchronously flatten all layers in a PDF."""
4380+
4381+
payload: dict[str, Any] = {"files": file}
4382+
if output is not None:
4383+
payload["output"] = output
4384+
4385+
return await self._post_file_operation(
4386+
endpoint="/flattened-layers-pdf",
4387+
payload=payload,
4388+
payload_model=PdfFlattenLayersPayload,
4389+
extra_query=extra_query,
4390+
extra_headers=extra_headers,
4391+
extra_body=extra_body,
4392+
timeout=timeout,
4393+
)
4394+
43424395
async def rasterize_pdf(
43434396
self,
43444397
file: PdfRestFile | Sequence[PdfRestFile],

src/pdfrest/models/_internal.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,30 @@ class PdfFlattenAnnotationsPayload(BaseModel):
11421142
] = None
11431143

11441144

1145+
class PdfFlattenLayersPayload(BaseModel):
1146+
"""Adapt caller options into a pdfRest-ready flatten-layers request payload."""
1147+
1148+
files: Annotated[
1149+
list[PdfRestFile],
1150+
Field(
1151+
min_length=1,
1152+
max_length=1,
1153+
validation_alias=AliasChoices("file", "files"),
1154+
serialization_alias="id",
1155+
),
1156+
BeforeValidator(_ensure_list),
1157+
AfterValidator(
1158+
_allowed_mime_types("application/pdf", error_msg="Must be a PDF file")
1159+
),
1160+
PlainSerializer(_serialize_as_first_file_id),
1161+
]
1162+
output: Annotated[
1163+
str | None,
1164+
Field(serialization_alias="output", min_length=1, default=None),
1165+
AfterValidator(_validate_output_prefix),
1166+
] = None
1167+
1168+
11451169
class PdfRestrictPayload(BaseModel):
11461170
"""Adapt caller options into a pdfRest-ready restrict-PDF request payload."""
11471171

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
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_layers(
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("flatten-layers", id="custom-output"),
29+
],
30+
)
31+
def test_live_flatten_layers_success(
32+
pdfrest_api_key: str,
33+
pdfrest_live_base_url: str,
34+
uploaded_pdf_for_layers: 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.flatten_layers(uploaded_pdf_for_layers, **kwargs)
46+
47+
assert response.output_files
48+
output_file = response.output_file
49+
assert output_file.type == "application/pdf"
50+
assert output_file.size > 0
51+
assert response.warning is None
52+
assert str(response.input_id) == str(uploaded_pdf_for_layers.id)
53+
if output_name is not None:
54+
assert output_file.name.startswith(output_name)
55+
else:
56+
assert output_file.name.endswith(".pdf")
57+
58+
59+
@pytest.mark.asyncio
60+
async def test_live_async_flatten_layers_success(
61+
pdfrest_api_key: str,
62+
pdfrest_live_base_url: str,
63+
uploaded_pdf_for_layers: PdfRestFile,
64+
) -> None:
65+
async with AsyncPdfRestClient(
66+
api_key=pdfrest_api_key,
67+
base_url=pdfrest_live_base_url,
68+
) as client:
69+
response = await client.flatten_layers(uploaded_pdf_for_layers, output="async")
70+
71+
assert response.output_files
72+
output_file = response.output_file
73+
assert output_file.name.startswith("async")
74+
assert output_file.type == "application/pdf"
75+
assert output_file.size > 0
76+
assert response.warning is None
77+
assert str(response.input_id) == str(uploaded_pdf_for_layers.id)
78+
79+
80+
def test_live_flatten_layers_invalid_file_id(
81+
pdfrest_api_key: str,
82+
pdfrest_live_base_url: str,
83+
uploaded_pdf_for_layers: PdfRestFile,
84+
) -> None:
85+
with (
86+
PdfRestClient(
87+
api_key=pdfrest_api_key,
88+
base_url=pdfrest_live_base_url,
89+
) as client,
90+
pytest.raises(PdfRestApiError, match=r"(?i)(id|file)"),
91+
):
92+
client.flatten_layers(
93+
uploaded_pdf_for_layers,
94+
extra_body={"id": "00000000-0000-0000-0000-000000000000"},
95+
)
96+
97+
98+
@pytest.mark.asyncio
99+
async def test_live_async_flatten_layers_invalid_file_id(
100+
pdfrest_api_key: str,
101+
pdfrest_live_base_url: str,
102+
uploaded_pdf_for_layers: PdfRestFile,
103+
) -> None:
104+
async with AsyncPdfRestClient(
105+
api_key=pdfrest_api_key,
106+
base_url=pdfrest_live_base_url,
107+
) as client:
108+
with pytest.raises(PdfRestApiError, match=r"(?i)(id|file)"):
109+
await client.flatten_layers(
110+
uploaded_pdf_for_layers,
111+
extra_body={"id": "ffffffff-ffff-ffff-ffff-ffffffffffff"},
112+
)

0 commit comments

Comments
 (0)