Skip to content

Commit 9feb89a

Browse files
Add Flatten Layers methods
Assisted-by: Codex
1 parent 47a11f3 commit 9feb89a

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,
@@ -2794,6 +2795,32 @@ def flatten_annotations(
27942795
timeout=timeout,
27952796
)
27962797

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

3864+
async def flatten_layers(
3865+
self,
3866+
file: PdfRestFile | Sequence[PdfRestFile],
3867+
*,
3868+
output: str | None = None,
3869+
extra_query: Query | None = None,
3870+
extra_headers: AnyMapping | None = None,
3871+
extra_body: Body | None = None,
3872+
timeout: TimeoutTypes | None = None,
3873+
) -> PdfRestFileBasedResponse:
3874+
"""Asynchronously flatten all layers in a PDF."""
3875+
3876+
payload: dict[str, Any] = {"files": file}
3877+
if output is not None:
3878+
payload["output"] = output
3879+
3880+
return await self._post_file_operation(
3881+
endpoint="/flattened-layers-pdf",
3882+
payload=payload,
3883+
payload_model=PdfFlattenLayersPayload,
3884+
extra_query=extra_query,
3885+
extra_headers=extra_headers,
3886+
extra_body=extra_body,
3887+
timeout=timeout,
3888+
)
3889+
38373890
async def rasterize_pdf(
38383891
self,
38393892
file: PdfRestFile | Sequence[PdfRestFile],

src/pdfrest/models/_internal.py

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

10901090

1091+
class PdfFlattenLayersPayload(BaseModel):
1092+
"""Adapt caller options into a pdfRest-ready flatten-layers request payload."""
1093+
1094+
files: Annotated[
1095+
list[PdfRestFile],
1096+
Field(
1097+
min_length=1,
1098+
max_length=1,
1099+
validation_alias=AliasChoices("file", "files"),
1100+
serialization_alias="id",
1101+
),
1102+
BeforeValidator(_ensure_list),
1103+
AfterValidator(
1104+
_allowed_mime_types("application/pdf", error_msg="Must be a PDF file")
1105+
),
1106+
PlainSerializer(_serialize_as_first_file_id),
1107+
]
1108+
output: Annotated[
1109+
str | None,
1110+
Field(serialization_alias="output", min_length=1, default=None),
1111+
AfterValidator(_validate_output_prefix),
1112+
] = None
1113+
1114+
10911115
class BmpPdfRestPayload(BasePdfRestGraphicPayload[Literal["rgb", "gray"]]):
10921116
"""Adapt caller options into a pdfRest-ready BMP request payload."""
10931117

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)