Skip to content

Commit 6793c3c

Browse files
Add Flatten Layers methods
Assisted-by: Codex
1 parent ce5e2de commit 6793c3c

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
@@ -84,6 +84,7 @@
8484
PdfCompressPayload,
8585
PdfFlattenAnnotationsPayload,
8686
PdfFlattenFormsPayload,
87+
PdfFlattenLayersPayload,
8788
PdfFlattenTransparenciesPayload,
8889
PdfInfoPayload,
8990
PdfLinearizePayload,
@@ -2796,6 +2797,32 @@ def flatten_annotations(
27962797
timeout=timeout,
27972798
)
27982799

2800+
def flatten_layers(
2801+
self,
2802+
file: PdfRestFile | Sequence[PdfRestFile],
2803+
*,
2804+
output: str | None = None,
2805+
extra_query: Query | None = None,
2806+
extra_headers: AnyMapping | None = None,
2807+
extra_body: Body | None = None,
2808+
timeout: TimeoutTypes | None = None,
2809+
) -> PdfRestFileBasedResponse:
2810+
"""Flatten all layers in a PDF into a single layer."""
2811+
2812+
payload: dict[str, Any] = {"files": file}
2813+
if output is not None:
2814+
payload["output"] = output
2815+
2816+
return self._post_file_operation(
2817+
endpoint="/flattened-layers-pdf",
2818+
payload=payload,
2819+
payload_model=PdfFlattenLayersPayload,
2820+
extra_query=extra_query,
2821+
extra_headers=extra_headers,
2822+
extra_body=extra_body,
2823+
timeout=timeout,
2824+
)
2825+
27992826
def rasterize_pdf(
28002827
self,
28012828
file: PdfRestFile | Sequence[PdfRestFile],
@@ -3838,6 +3865,32 @@ async def flatten_annotations(
38383865
timeout=timeout,
38393866
)
38403867

3868+
async def flatten_layers(
3869+
self,
3870+
file: PdfRestFile | Sequence[PdfRestFile],
3871+
*,
3872+
output: str | None = None,
3873+
extra_query: Query | None = None,
3874+
extra_headers: AnyMapping | None = None,
3875+
extra_body: Body | None = None,
3876+
timeout: TimeoutTypes | None = None,
3877+
) -> PdfRestFileBasedResponse:
3878+
"""Asynchronously flatten all layers in a PDF."""
3879+
3880+
payload: dict[str, Any] = {"files": file}
3881+
if output is not None:
3882+
payload["output"] = output
3883+
3884+
return await self._post_file_operation(
3885+
endpoint="/flattened-layers-pdf",
3886+
payload=payload,
3887+
payload_model=PdfFlattenLayersPayload,
3888+
extra_query=extra_query,
3889+
extra_headers=extra_headers,
3890+
extra_body=extra_body,
3891+
timeout=timeout,
3892+
)
3893+
38413894
async def rasterize_pdf(
38423895
self,
38433896
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 BmpPdfRestPayload(BasePdfRestGraphicPayload[Literal["rgb", "gray"]]):
11461170
"""Adapt caller options into a pdfRest-ready BMP 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)