Skip to content

Commit cf02a2c

Browse files
Add PNG conversion support to clients and tests
- Implemented `convert_to_png` method in both synchronous and asynchronous clients to allow PDF-to-PNG conversion. - Added `ConvertToGraphic` and `PdfRestFileBasedResponse` models to support conversion operations. - Introduced validation checks for parameters like page range, resolution, color model, and output prefix. - Created comprehensive test cases to ensure functionality and handle edge cases in both sync and async clients. Assisted-by: Codex
1 parent deb2e9f commit cf02a2c

2 files changed

Lines changed: 699 additions & 2 deletions

File tree

src/pdfrest/client.py

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,16 @@
2323
PdfRestConfigurationError,
2424
translate_httpx_error,
2525
)
26-
from .models import PdfRestErrorResponse, PdfRestFile, UpResponse
26+
from .models import (
27+
PdfRestErrorResponse,
28+
PdfRestFile,
29+
PdfRestFileBasedResponse,
30+
UpResponse,
31+
)
2732

2833
__all__ = ("AsyncPdfRestClient", "PdfRestClient")
2934

30-
from .models._internal import UploadURLs
35+
from .models._internal import ConvertToGraphic, PdfRestRawFileResponse, UploadURLs
3136

3237
DEFAULT_BASE_URL = "https://api.pdfrest.com"
3338
API_KEY_ENV_VAR = "PDFREST_API_KEY"
@@ -1024,6 +1029,56 @@ def up(
10241029
payload = self._send_request(request)
10251030
return UpResponse.model_validate(payload)
10261031

1032+
def convert_to_png(
1033+
self,
1034+
files: PdfRestFile | Sequence[PdfRestFile],
1035+
*,
1036+
output_prefix: str | None = None,
1037+
page_range: str | Sequence[str] | None = None,
1038+
resolution: int = 300,
1039+
color_model: Literal["rgb", "rgba", "gray"] = "rgb",
1040+
smoothing: Literal["none", "all", "text", "line", "image"]
1041+
| Sequence[Literal["none", "all", "text", "line", "image"]]
1042+
| None = None,
1043+
) -> PdfRestFileBasedResponse:
1044+
"""Convert one or more pdfRest files to PNG images."""
1045+
1046+
payload: dict[str, Any] = {
1047+
"files": files,
1048+
"resolution": resolution,
1049+
"color_model": color_model,
1050+
}
1051+
if output_prefix is not None:
1052+
payload["output_prefix"] = output_prefix
1053+
if page_range is not None:
1054+
payload["page_range"] = page_range
1055+
if smoothing is not None:
1056+
payload["smoothing"] = smoothing
1057+
1058+
conversion_options = ConvertToGraphic.model_validate(payload)
1059+
request = self.prepare_request(
1060+
"POST",
1061+
"/png",
1062+
json_body=conversion_options.model_dump(
1063+
mode="json", by_alias=True, exclude_none=True, exclude_defaults=True
1064+
),
1065+
)
1066+
raw_payload = self._send_request(request)
1067+
raw_response = PdfRestRawFileResponse.model_validate(raw_payload)
1068+
1069+
output_ids = raw_response.ids or []
1070+
output_files = [self.fetch_file_info(str(file_id)) for file_id in output_ids]
1071+
1072+
return PdfRestFileBasedResponse.model_validate(
1073+
{
1074+
"input_id": [str(file_id) for file_id in raw_response.input_id],
1075+
"output_file": [
1076+
file.model_dump(mode="json", by_alias=True) for file in output_files
1077+
],
1078+
"warning": raw_response.warning,
1079+
}
1080+
)
1081+
10271082

10281083
class AsyncPdfRestClient(_AsyncApiClient):
10291084
"""Asynchronous client for interacting with the pdfrest API."""
@@ -1081,3 +1136,59 @@ async def up(
10811136
)
10821137
payload = await self._send_request(request)
10831138
return UpResponse.model_validate(payload)
1139+
1140+
async def convert_to_png(
1141+
self,
1142+
files: PdfRestFile | Sequence[PdfRestFile],
1143+
*,
1144+
output_prefix: str | None = None,
1145+
page_range: str | Sequence[str] | None = None,
1146+
resolution: int = 300,
1147+
color_model: Literal["rgb", "rgba", "gray"] = "rgb",
1148+
smoothing: Literal["none", "all", "text", "line", "image"]
1149+
| Sequence[Literal["none", "all", "text", "line", "image"]]
1150+
| None = None,
1151+
) -> PdfRestFileBasedResponse:
1152+
"""Asynchronously convert one or more pdfRest files to PNG images."""
1153+
1154+
payload: dict[str, Any] = {
1155+
"files": files,
1156+
"resolution": resolution,
1157+
"color_model": color_model,
1158+
}
1159+
if output_prefix is not None:
1160+
payload["output_prefix"] = output_prefix
1161+
if page_range is not None:
1162+
payload["page_range"] = page_range
1163+
if smoothing is not None:
1164+
payload["smoothing"] = smoothing
1165+
1166+
conversion_options = ConvertToGraphic.model_validate(payload)
1167+
request = self.prepare_request(
1168+
"POST",
1169+
"/png",
1170+
json_body=conversion_options.model_dump(
1171+
mode="json", by_alias=True, exclude_none=True, exclude_unset=True
1172+
),
1173+
)
1174+
raw_payload = await self._send_request(request)
1175+
raw_response = PdfRestRawFileResponse.model_validate(raw_payload)
1176+
1177+
output_ids = raw_response.ids or []
1178+
output_files: list[PdfRestFile] = []
1179+
if output_ids:
1180+
output_files = list(
1181+
await asyncio.gather(
1182+
*(self.fetch_file_info(str(file_id)) for file_id in output_ids)
1183+
)
1184+
)
1185+
1186+
return PdfRestFileBasedResponse.model_validate(
1187+
{
1188+
"input_id": [str(file_id) for file_id in raw_response.input_id],
1189+
"output_file": [
1190+
file.model_dump(mode="json", by_alias=True) for file in output_files
1191+
],
1192+
"warning": raw_response.warning,
1193+
}
1194+
)

0 commit comments

Comments
 (0)