Skip to content
Closed
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
1 change: 1 addition & 0 deletions docs/api-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ formats.
profile. See the
[custom job options example](https://github.com/pdfrest/pdfrest-python/blob/main/examples/convert_postscript_to_pdf/convert_postscript_with_job_options_example.py).
- Out of PDF:
[convert_pdf_to_postscript][pdfrest.PdfRestClient.convert_pdf_to_postscript],
[convert_to_word][pdfrest.PdfRestClient.convert_to_word],
[convert_to_excel][pdfrest.PdfRestClient.convert_to_excel],
[convert_to_powerpoint][pdfrest.PdfRestClient.convert_to_powerpoint],
Expand Down
2 changes: 2 additions & 0 deletions docs/llms-full.txt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ to the API guide and API reference for exact method names and options.
- Convert to Office/image/Markdown formats (for example Word, Excel,
PowerPoint, PNG/JPEG/TIFF/GIF/BMP, Markdown)
- Convert source assets to PDF (Office, HTML, URL, email, images, PostScript)
- Convert PDF pages to PostScript with language-level, page-range, scaling,
rotation, fitting, binary-output, and annotation controls
- Apply an uploaded `.joboptions` Distiller profile when converting PostScript
or EPS with `convert_postscript_to_pdf`
- Convert to standards formats (`PDF/A`, `PDF/X`)
Expand Down
4 changes: 4 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ supported interpreter matrix.
- `examples/convert_to_image/convert_password_protected_pdf_to_png_example.py` –
convert a password-protected PDF into PNG images by supplying its open
password.
- `examples/convert_pdf_to_postscript/convert_pdf_to_postscript_example.py` –
synchronously convert selected PDF pages and verify the PostScript output.
- `examples/convert_pdf_to_postscript/async_convert_pdf_to_postscript_example.py`
– asynchronously convert a PDF page and verify the PostScript output.
- `examples/delete/delete_example.py` – demonstrate file deletion (sync + async
variants).
- `examples/extract_text/extract_pdf_text_example.py` – run `extract_pdf_text`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# /// script
# requires-python = ">=3.10"
# dependencies = ["pdfrest", "python-dotenv"]
# ///
"""Asynchronously convert a PDF to PostScript and verify the result.

This sample demonstrates how to:

1. Upload the checked-in PDF resource with ``AsyncPdfRestClient``.
2. Convert its first page to level 2 PostScript.
3. Download the result and verify its PostScript header and page markers.
4. Print the generated file's metadata and download URL.

Set ``PDFREST_API_KEY`` and optionally ``PDFREST_BASE_URL`` for a self-hosted
deployment, then run from the repository root with
``uv run examples/convert_pdf_to_postscript/async_convert_pdf_to_postscript_example.py``.
The required input PDF is included in the repository.
"""

from __future__ import annotations

import asyncio
import os
from pathlib import Path

from dotenv import load_dotenv

from pdfrest import AsyncPdfRestClient

RESOURCE_DIRECTORY = Path(__file__).resolve().parents[1] / "resources"
PDF_PATH = RESOURCE_DIRECTORY / "report.pdf"


def _language_level(postscript: bytes, expected_level: int) -> str:
"""Return and verify the generated PostScript language-level marker."""
expected = f"%%LanguageLevel: {expected_level}".encode()
if expected not in postscript.splitlines():
msg = f"The generated file is not PostScript level {expected_level}."
raise RuntimeError(msg)
return expected.decode("ascii")


async def convert_pdf_to_postscript() -> None:
"""Upload a PDF, convert its first page, and verify the PostScript."""
load_dotenv()
base_url = os.getenv("PDFREST_BASE_URL")

async with AsyncPdfRestClient(base_url=base_url) as client:
source = (await client.files.create_from_paths(PDF_PATH))[0]
response = await client.convert_pdf_to_postscript(
source,
ps_level=2,
page_range="1",
binary_output=False,
scale=0.75,
rotate=True,
shrink_to_fit=True,
print_annotations=False,
output="async-print-ready",
)
postscript = await client.files.read_bytes(response.output_file)

output = response.output_file
if not postscript.startswith(b"%!PS-Adobe"):
msg = "The generated file does not have a PostScript header."
raise RuntimeError(msg)
language_level = _language_level(postscript, 2)

print(f"Created: {output.name}")
print(f"MIME type: {output.type}")
print(f"Size: {output.size} bytes")
print(f"Header: {postscript.splitlines()[0].decode('ascii')}")
print(f"Language level: {language_level}")
print(f"Pages: {postscript.count(b'%%Page:')}")
print(f"Download URL: {output.url}")


if __name__ == "__main__": # pragma: no cover - manual example
asyncio.run(convert_pdf_to_postscript())
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# /// script
# requires-python = ">=3.10"
# dependencies = ["pdfrest", "python-dotenv"]
# ///
"""Convert selected PDF pages to PostScript and verify the result.

This sample demonstrates how to:

1. Upload the checked-in PDF resource.
2. Convert selected pages to level 3 PostScript with print controls.
3. Download the result and verify its PostScript header and page markers.
4. Print the generated file's metadata and download URL.

Set ``PDFREST_API_KEY`` and optionally ``PDFREST_BASE_URL`` for a self-hosted
deployment, then run from the repository root with
``uv run examples/convert_pdf_to_postscript/convert_pdf_to_postscript_example.py``.
The required input PDF is included in the repository.
"""

from __future__ import annotations

import os
from pathlib import Path

from dotenv import load_dotenv

from pdfrest import PdfRestClient

RESOURCE_DIRECTORY = Path(__file__).resolve().parents[1] / "resources"
PDF_PATH = RESOURCE_DIRECTORY / "report.pdf"


def _language_level(postscript: bytes, expected_level: int) -> str:
"""Return and verify the generated PostScript language-level marker."""
expected = f"%%LanguageLevel: {expected_level}".encode()
if expected not in postscript.splitlines():
msg = f"The generated file is not PostScript level {expected_level}."
raise RuntimeError(msg)
return expected.decode("ascii")


def convert_pdf_to_postscript() -> None:
"""Upload a PDF, convert selected pages, and verify the PostScript."""
load_dotenv()
base_url = os.getenv("PDFREST_BASE_URL")

with PdfRestClient(base_url=base_url) as client:
source = client.files.create_from_paths(PDF_PATH)[0]
response = client.convert_pdf_to_postscript(
source,
ps_level=3,
page_range="1-last",
binary_output=False,
scale=1,
shrink_to_fit=True,
print_annotations=False,
output="print-ready",
)
postscript = client.files.read_bytes(response.output_file)

output = response.output_file
if not postscript.startswith(b"%!PS-Adobe"):
msg = "The generated file does not have a PostScript header."
raise RuntimeError(msg)
language_level = _language_level(postscript, 3)

print(f"Created: {output.name}")
print(f"MIME type: {output.type}")
print(f"Size: {output.size} bytes")
print(f"Header: {postscript.splitlines()[0].decode('ascii')}")
print(f"Language level: {language_level}")
print(f"Pages: {postscript.count(b'%%Page:')}")
print(f"Download URL: {output.url}")


if __name__ == "__main__": # pragma: no cover - manual example
convert_pdf_to_postscript()
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pdfrest"
version = "1.3.0"
version = "1.4.0"
description = "Python client library for interacting with the pdfRest API"
readme = { file = "README.md", content-type = "text/markdown" }
authors = [
Expand Down
146 changes: 146 additions & 0 deletions src/pdfrest/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
ConvertJsonToPdfPayload,
ConvertMarkdownToPdfPayload,
ConvertOfficeToPdfPayload,
ConvertPdfToPostscriptPayload,
ConvertPlainTextToPdfPayload,
ConvertPostscriptToPdfPayload,
ConvertToMarkdownPayload,
Expand Down Expand Up @@ -178,6 +179,7 @@
PdfTextColor,
PdfXType,
PngColorModel,
PostScriptLevel,
SummaryFormat,
SummaryOutputFormat,
TiffColorModel,
Expand Down Expand Up @@ -3840,6 +3842,78 @@ def convert_to_powerpoint(
timeout=timeout,
)

def convert_pdf_to_postscript(
self,
file: PdfRestFile | Sequence[PdfRestFile],
*,
ps_level: PostScriptLevel = 3,
page_range: str = "all",
binary_output: bool = True,
scale: float = 1,
rotate: bool = False,
shrink_to_fit: bool = False,
print_annotations: bool = True,
output: str | None = None,
extra_query: Query | None = None,
extra_headers: AnyMapping | None = None,
extra_body: Body | None = None,
timeout: TimeoutTypes | None = None,
) -> PdfRestFileBasedResponse:
"""Convert a PDF to PostScript level 2 or level 3.

Produces print-oriented PostScript with page selection, scaling,
rotation, binary output, fitting, and annotation controls.

Args:
file: Uploaded PDF as a `PdfRestFile` object.
ps_level: PostScript language level for the generated file.
page_range: Pages to convert, such as `all`, `1`, `1,3-5`, or
`14-last`.
binary_output: Allows binary data in the PostScript stream when
supported.
scale: Page-content scale multiplier; must be greater than zero.
rotate: Enables page rotation in the generated PostScript.
shrink_to_fit: Shrinks page content when needed to fit the output
page.
print_annotations: Includes printable PDF annotations in the
output.
output: Output filename prefix used by pdfRest when creating the
PostScript file.
extra_query: Additional query parameters merged into the request.
extra_headers: Additional HTTP headers merged into the request.
extra_body: Additional request body fields merged into the payload.
timeout: Request timeout override for this call.

Returns:
Validated file-based response returned by pdfRest.

Raises:
PdfRestError: If request execution fails at the client or API layer.
ValidationError: If local payload validation fails before sending.
"""
payload: dict[str, Any] = {
"files": file,
"ps_level": ps_level,
"page_range": page_range,
"binary_output": binary_output,
"scale": scale,
"rotate": rotate,
"shrink_to_fit": shrink_to_fit,
"print_annotations": print_annotations,
}
if output is not None:
payload["output"] = output

return self._post_file_operation(
endpoint="/postscript",
payload=payload,
payload_model=ConvertPdfToPostscriptPayload,
extra_query=extra_query,
extra_headers=extra_headers,
extra_body=extra_body,
timeout=timeout,
)

def convert_xfa_to_acroforms(
self,
file: PdfRestFile | Sequence[PdfRestFile],
Expand Down Expand Up @@ -7304,6 +7378,78 @@ async def convert_to_powerpoint(
timeout=timeout,
)

async def convert_pdf_to_postscript(
self,
file: PdfRestFile | Sequence[PdfRestFile],
*,
ps_level: PostScriptLevel = 3,
page_range: str = "all",
binary_output: bool = True,
scale: float = 1,
rotate: bool = False,
shrink_to_fit: bool = False,
print_annotations: bool = True,
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 convert a PDF to PostScript level 2 or level 3.

This is the asynchronous variant of
[PdfRestClient.convert_pdf_to_postscript][pdfrest.PdfRestClient.convert_pdf_to_postscript].

Args:
file: Uploaded PDF as a `PdfRestFile` object.
ps_level: PostScript language level for the generated file.
page_range: Pages to convert, such as `all`, `1`, `1,3-5`, or
`14-last`.
binary_output: Allows binary data in the PostScript stream when
supported.
scale: Page-content scale multiplier; must be greater than zero.
rotate: Enables page rotation in the generated PostScript.
shrink_to_fit: Shrinks page content when needed to fit the output
page.
print_annotations: Includes printable PDF annotations in the
output.
output: Output filename prefix used by pdfRest when creating the
PostScript file.
extra_query: Additional query parameters merged into the request.
extra_headers: Additional HTTP headers merged into the request.
extra_body: Additional request body fields merged into the payload.
timeout: Request timeout override for this call.

Returns:
Validated file-based response returned by pdfRest.

Raises:
PdfRestError: If request execution fails at the client or API layer.
ValidationError: If local payload validation fails before sending.
"""
payload: dict[str, Any] = {
"files": file,
"ps_level": ps_level,
"page_range": page_range,
"binary_output": binary_output,
"scale": scale,
"rotate": rotate,
"shrink_to_fit": shrink_to_fit,
"print_annotations": print_annotations,
}
if output is not None:
payload["output"] = output

return await self._post_file_operation(
endpoint="/postscript",
payload=payload,
payload_model=ConvertPdfToPostscriptPayload,
extra_query=extra_query,
extra_headers=extra_headers,
extra_body=extra_body,
timeout=timeout,
)

async def convert_xfa_to_acroforms(
self,
file: PdfRestFile | Sequence[PdfRestFile],
Expand Down
Loading
Loading