Skip to content
Draft
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
7 changes: 5 additions & 2 deletions docs/api-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,12 @@ Use this group to add visible content or remove sensitive content.
- Add overlays:
[add_text_to_pdf][pdfrest.PdfRestClient.add_text_to_pdf],
[add_image_to_pdf][pdfrest.PdfRestClient.add_image_to_pdf],
[add_images_to_pdf][pdfrest.PdfRestClient.add_images_to_pdf],
[add_shapes_to_pdf][pdfrest.PdfRestClient.add_shapes_to_pdf]
(`add_text_to_pdf` can tag newly added text and identify its natural language
for assistive technology)
(`add_image_to_pdf` preserves the concise single-image workflow, while
`add_images_to_pdf` accepts typed placements for multiple or reused uploaded
images; image and text overlays can include accessibility tagging and a BCP 47
language tag)
- Watermarking:
[watermark_pdf_with_text][pdfrest.PdfRestClient.watermark_pdf_with_text],
[watermark_pdf_with_image][pdfrest.PdfRestClient.watermark_pdf_with_image]
Expand Down
2 changes: 2 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ supported interpreter matrix.

## Available Examples

- `examples/add_images/add_images_to_pdf_example.py` – add multiple uploaded
images with typed placements, sizing, reuse, and accessibility metadata.
- `examples/apply_tdm_reservation/apply_tdm_reservation_example.py` – embed a
hosted TDM policy URL in an uploaded PDF.
- `examples/set_pdf_page_boxes/set_pdf_page_boxes_example.py` – enlarge a PDF's
Expand Down
67 changes: 67 additions & 0 deletions examples/add_images/add_images_to_pdf_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# /// script
# requires-python = ">=3.10"
# dependencies = ["pdfrest", "python-dotenv"]
# ///
"""Add two uploaded images to a PDF with reusable typed placements.

This example loads ``PDFREST_API_KEY`` (optionally from ``.env``), uploads the
checked-in PDF, PNG, and JPEG fixtures under ``examples/resources/``, adds the
images with sizing and accessible tag metadata, and prints the generated PDF
details.

Run from the repository root with:
``uv run examples/add_images/add_images_to_pdf_example.py``.
"""

import os
from pathlib import Path

from dotenv import load_dotenv

from pdfrest import PdfRestClient
from pdfrest.types import PdfAddImageObject

load_dotenv()

resources_directory = Path(__file__).resolve().parents[1] / "resources"
pdf_path = resources_directory / "report.pdf"
image_paths = [
resources_directory / "image-overlay-logo.png",
resources_directory / "image-overlay-photo.jpg",
]

with PdfRestClient(api_key=os.environ["PDFREST_API_KEY"]) as client:
source_pdf = client.files.create_from_paths([pdf_path])[0]
logo, photo = client.files.create_from_paths(image_paths)
placements: list[PdfAddImageObject] = [
PdfAddImageObject(
image=logo,
page="1-last",
x=36,
y=36,
width=72,
tag_alt_text="Company logo",
tag_structure_type="Figure",
),
PdfAddImageObject(
image=photo,
page=1,
x=144,
y=36,
height=72,
tag_alt_text="Sample photograph",
tag_structure_type="Figure",
),
]
response = client.add_images_to_pdf(
source_pdf,
image_objects=placements,
tag_enabled=True,
tag_language="en-US",
output="report-with-images",
)

print(f"Created: {response.output_file.name}")
print(f"MIME type: {response.output_file.type}")
print(f"Size: {response.output_file.size} bytes")
print(f"URL: {response.output_file.url}")
Binary file added examples/resources/image-overlay-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/resources/image-overlay-photo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
201 changes: 194 additions & 7 deletions src/pdfrest/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
OcrPdfPayload,
PdfAddAttachmentPayload,
PdfAddImagePayload,
PdfAddImagesPayload,
PdfAddShapesPayload,
PdfAddTextPayload,
PdfBlankPayload,
Expand Down Expand Up @@ -156,9 +157,11 @@
HtmlWebLayout,
JpegColorModel,
OcrLanguage,
PdfAddImageObject,
PdfAddShapeObject,
PdfAddTextObject,
PdfAType,
PdfContentStructureType,
PdfConversionCompression,
PdfConversionDownsample,
PdfConversionLocale,
Expand Down Expand Up @@ -3549,22 +3552,43 @@ def add_image_to_pdf(
x: int,
y: int,
page: int,
width: float | None = None,
height: float | None = None,
tag_enabled: bool | None = None,
tag_language: str | None = None,
tag_alt_text: str | None = None,
tag_is_artifact: bool | None = None,
tag_structure_type: PdfContentStructureType | None = None,
output: str | None = None,
extra_query: Query | None = None,
extra_headers: AnyMapping | None = None,
extra_body: Body | None = None,
timeout: TimeoutTypes | None = None,
) -> PdfRestFileBasedResponse:
"""Insert an image into a single page of a PDF.
"""Insert one image into a single page of a PDF.

Places an uploaded image onto target pages in a PDF with size/position controls. This is commonly used for logos, seals, and branding overlays.
This backward-compatible helper is intended for one image placement. Use
[add_images_to_pdf][pdfrest.PdfRestClient.add_images_to_pdf] for multiple
images, page expressions, or repeated placements.

Args:
file: Uploaded input file or files as `PdfRestFile` objects.
image: Image/PDF resource to place into the source document.
image: Uploaded JPEG, PNG, TIFF, or GIF resource to place.
x: Horizontal position in PDF points.
y: Vertical position in PDF points.
page: Target page number (or selector) where inserted content should be applied.
page: Positive one-based target page number.
width: Optional placed width in PDF points. Must be greater than 0.
height: Optional placed height in PDF points. Must be greater than 0.
tag_enabled: Whether to tag the added image. Must be true when any
other tag option is provided.
tag_language: Optional BCP 47 language tag, such as ``"en-US"``.
Requires ``tag_enabled=True``.
tag_alt_text: Optional non-empty alternate text. Requires
``tag_enabled=True``.
tag_is_artifact: Whether the image is decorative. Requires
``tag_enabled=True`` when provided.
tag_structure_type: Optional semantic PDF structure type. ``"Figure"``
is normally appropriate. Requires ``tag_enabled=True``.
output: Output filename prefix used by pdfRest when creating files.
extra_query: Additional query parameters merged into the request.
extra_headers: Additional HTTP headers merged into the request.
Expand All @@ -3585,6 +3609,18 @@ def add_image_to_pdf(
"y": y,
"page": page,
}
optional_payload = {
"width": width,
"height": height,
"tag_enabled": tag_enabled,
"tag_language": tag_language,
"tag_alt_text": tag_alt_text,
"tag_is_artifact": tag_is_artifact,
"tag_structure_type": tag_structure_type,
}
payload.update(
{key: value for key, value in optional_payload.items() if value is not None}
)
if output is not None:
payload["output"] = output

Expand All @@ -3598,6 +3634,67 @@ def add_image_to_pdf(
timeout=timeout,
)

def add_images_to_pdf(
self,
file: PdfRestFile | Sequence[PdfRestFile],
*,
image_objects: PdfAddImageObject | Sequence[PdfAddImageObject],
tag_enabled: bool | None = None,
tag_language: str | None = None,
output: str | None = None,
extra_query: Query | None = None,
extra_headers: AnyMapping | None = None,
extra_body: Body | None = None,
timeout: TimeoutTypes | None = None,
) -> PdfRestFileBasedResponse:
"""Insert one or more uploaded images using typed placement objects.

Each object can select one or more pages, size its image, and provide
image-specific accessibility metadata. Reuse the same ``PdfRestFile`` in
multiple objects to place one uploaded image more than once.

Args:
file: One uploaded PDF to modify.
image_objects: One placement object or a non-empty sequence of
placement objects.
tag_enabled: Whether to tag the added images. Must be true when
``tag_language`` or per-image tag metadata is provided.
tag_language: Optional BCP 47 language tag for image metadata, such
as ``"en-US"``. Requires ``tag_enabled=True``.
output: Output filename prefix used by pdfRest when creating files.
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,
"image_objects": image_objects,
}
if tag_enabled is not None:
payload["tag_enabled"] = tag_enabled
if tag_language is not None:
payload["tag_language"] = tag_language
if output is not None:
payload["output"] = output

return self._post_file_operation(
endpoint="/pdf-with-added-image",
payload=payload,
payload_model=PdfAddImagesPayload,
extra_query=extra_query,
extra_headers=extra_headers,
extra_body=extra_body,
timeout=timeout,
)

def set_pdf_page_boxes(
self,
file: PdfRestFile | Sequence[PdfRestFile],
Expand Down Expand Up @@ -7227,6 +7324,13 @@ async def add_image_to_pdf(
x: int,
y: int,
page: int,
width: float | None = None,
height: float | None = None,
tag_enabled: bool | None = None,
tag_language: str | None = None,
tag_alt_text: str | None = None,
tag_is_artifact: bool | None = None,
tag_structure_type: PdfContentStructureType | None = None,
output: str | None = None,
extra_query: Query | None = None,
extra_headers: AnyMapping | None = None,
Expand All @@ -7235,14 +7339,28 @@ async def add_image_to_pdf(
) -> PdfRestFileBasedResponse:
"""Asynchronous variant of [PdfRestClient.add_image_to_pdf][pdfrest.PdfRestClient.add_image_to_pdf].

Places an uploaded image onto target pages in a PDF with size/position controls. This is commonly used for logos, seals, and branding overlays.
This backward-compatible helper is intended for one image placement. Use
[add_images_to_pdf][pdfrest.AsyncPdfRestClient.add_images_to_pdf] for
multiple images, page expressions, or repeated placements.

Args:
file: Uploaded input file or files as `PdfRestFile` objects.
image: Image/PDF resource to place into the source document.
image: Uploaded JPEG, PNG, TIFF, or GIF resource to place.
x: Horizontal position in PDF points.
y: Vertical position in PDF points.
page: Target page number (or selector) where inserted content should be applied.
page: Positive one-based target page number.
width: Optional placed width in PDF points. Must be greater than 0.
height: Optional placed height in PDF points. Must be greater than 0.
tag_enabled: Whether to tag the added image. Must be true when any
other tag option is provided.
tag_language: Optional BCP 47 language tag, such as ``"en-US"``.
Requires ``tag_enabled=True``.
tag_alt_text: Optional non-empty alternate text. Requires
``tag_enabled=True``.
tag_is_artifact: Whether the image is decorative. Requires
``tag_enabled=True`` when provided.
tag_structure_type: Optional semantic PDF structure type. ``"Figure"``
is normally appropriate. Requires ``tag_enabled=True``.
output: Output filename prefix used by pdfRest when creating files.
extra_query: Additional query parameters merged into the request.
extra_headers: Additional HTTP headers merged into the request.
Expand All @@ -7263,6 +7381,18 @@ async def add_image_to_pdf(
"y": y,
"page": page,
}
optional_payload = {
"width": width,
"height": height,
"tag_enabled": tag_enabled,
"tag_language": tag_language,
"tag_alt_text": tag_alt_text,
"tag_is_artifact": tag_is_artifact,
"tag_structure_type": tag_structure_type,
}
payload.update(
{key: value for key, value in optional_payload.items() if value is not None}
)
if output is not None:
payload["output"] = output

Expand All @@ -7276,6 +7406,63 @@ async def add_image_to_pdf(
timeout=timeout,
)

async def add_images_to_pdf(
self,
file: PdfRestFile | Sequence[PdfRestFile],
*,
image_objects: PdfAddImageObject | Sequence[PdfAddImageObject],
tag_enabled: bool | None = None,
tag_language: str | None = None,
output: str | None = None,
extra_query: Query | None = None,
extra_headers: AnyMapping | None = None,
extra_body: Body | None = None,
timeout: TimeoutTypes | None = None,
) -> PdfRestFileBasedResponse:
"""Asynchronous variant of [PdfRestClient.add_images_to_pdf][pdfrest.PdfRestClient.add_images_to_pdf].

Args:
file: One uploaded PDF to modify.
image_objects: One placement object or a non-empty sequence of
placement objects.
tag_enabled: Whether to tag the added images. Must be true when
``tag_language`` or per-image tag metadata is provided.
tag_language: Optional BCP 47 language tag for image metadata, such
as ``"en-US"``. Requires ``tag_enabled=True``.
output: Output filename prefix used by pdfRest when creating files.
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,
"image_objects": image_objects,
}
if tag_enabled is not None:
payload["tag_enabled"] = tag_enabled
if tag_language is not None:
payload["tag_language"] = tag_language
if output is not None:
payload["output"] = output

return await self._post_file_operation(
endpoint="/pdf-with-added-image",
payload=payload,
payload_model=PdfAddImagesPayload,
extra_query=extra_query,
extra_headers=extra_headers,
extra_body=extra_body,
timeout=timeout,
)

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