Skip to content

Commit 6436ee2

Browse files
watermark-pdf: Remediate text color
- `client.py`: Merge rgb and cmyk into one, `text_color` - `client.py`: Remove `Sequence[int]` from type - `_internal.py`: Assign the text color value to the appropriate payload field Assisted-by: Codex
1 parent 9b71d74 commit 6436ee2

3 files changed

Lines changed: 41 additions & 33 deletions

File tree

src/pdfrest/client.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3628,8 +3628,7 @@ def watermark_pdf_with_text(
36283628
output: str | None = None,
36293629
font: str | None = None,
36303630
text_size: int = 72,
3631-
text_color_rgb: PdfRGBColor | Sequence[int] | None = None,
3632-
text_color_cmyk: PdfCMYKColor | Sequence[int] | None = None,
3631+
text_color: PdfRGBColor | PdfCMYKColor = (0, 0, 0),
36333632
opacity: float = 0.5,
36343633
horizontal_alignment: WatermarkHorizontalAlignment = "center",
36353634
vertical_alignment: WatermarkVerticalAlignment = "center",
@@ -3651,8 +3650,7 @@ def watermark_pdf_with_text(
36513650
"output": output,
36523651
"font": font,
36533652
"text_size": text_size,
3654-
"text_color_rgb": text_color_rgb,
3655-
"text_color_cmyk": text_color_cmyk,
3653+
"text_color": text_color,
36563654
"opacity": opacity,
36573655
"horizontal_alignment": horizontal_alignment,
36583656
"vertical_alignment": vertical_alignment,
@@ -5527,8 +5525,7 @@ async def watermark_pdf_with_text(
55275525
output: str | None = None,
55285526
font: str | None = None,
55295527
text_size: int = 72,
5530-
text_color_rgb: PdfRGBColor | Sequence[int] | None = None,
5531-
text_color_cmyk: PdfCMYKColor | Sequence[int] | None = None,
5528+
text_color: PdfRGBColor | PdfCMYKColor = (0, 0, 0),
55325529
opacity: float = 0.5,
55335530
horizontal_alignment: WatermarkHorizontalAlignment = "center",
55345531
vertical_alignment: WatermarkVerticalAlignment = "center",
@@ -5550,8 +5547,7 @@ async def watermark_pdf_with_text(
55505547
"output": output,
55515548
"font": font,
55525549
"text_size": text_size,
5553-
"text_color_rgb": text_color_rgb,
5554-
"text_color_cmyk": text_color_cmyk,
5550+
"text_color": text_color,
55555551
"opacity": opacity,
55565552
"horizontal_alignment": horizontal_alignment,
55575553
"vertical_alignment": vertical_alignment,

src/pdfrest/models/_internal.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,10 @@ class ExtractImagesPayload(BaseModel):
938938

939939
RgbChannel = Annotated[int, Field(ge=0, le=255)]
940940
CmykChannel = Annotated[int, Field(ge=0, le=100)]
941+
WatermarkTextColor = (
942+
tuple[RgbChannel, RgbChannel, RgbChannel]
943+
| tuple[CmykChannel, CmykChannel, CmykChannel, CmykChannel]
944+
)
941945

942946

943947
class PdfLiteralRedactionModel(BaseModel):
@@ -1669,25 +1673,33 @@ class PdfTextWatermarkPayload(_BasePdfWatermarkPayload):
16691673
int,
16701674
Field(serialization_alias="text_size", ge=5, le=100, default=72),
16711675
] = 72
1672-
text_color_rgb: Annotated[
1673-
tuple[RgbChannel, RgbChannel, RgbChannel] | None,
1674-
Field(serialization_alias="text_color_rgb", default=None),
1675-
BeforeValidator(_split_comma_string),
1676-
PlainSerializer(_serialize_as_comma_separated_string),
1677-
] = None
1678-
text_color_cmyk: Annotated[
1679-
tuple[CmykChannel, CmykChannel, CmykChannel, CmykChannel] | None,
1680-
Field(serialization_alias="text_color_cmyk", default=None),
1676+
text_color: Annotated[
1677+
WatermarkTextColor,
1678+
Field(
1679+
default=(0, 0, 0),
1680+
),
16811681
BeforeValidator(_split_comma_string),
1682-
PlainSerializer(_serialize_as_comma_separated_string),
1683-
] = None
1682+
] = (0, 0, 0)
16841683

1685-
@model_validator(mode="after")
1686-
def _validate_text_colors(self) -> PdfTextWatermarkPayload:
1687-
if self.text_color_rgb is not None and self.text_color_cmyk is not None:
1688-
msg = "Specify only one of text_color_rgb or text_color_cmyk."
1689-
raise ValueError(msg)
1690-
return self
1684+
@model_serializer(mode="wrap")
1685+
def _serialize_text_color(
1686+
self, handler: Callable[[PdfTextWatermarkPayload], dict[str, Any]]
1687+
) -> dict[str, Any]:
1688+
payload = handler(self)
1689+
text_color = cast(
1690+
list[Any] | tuple[int, ...],
1691+
payload.pop("text_color", self.text_color),
1692+
)
1693+
serialized_color = list(text_color)
1694+
if len(text_color) == 3:
1695+
payload["text_color_rgb"] = _serialize_as_comma_separated_string(
1696+
serialized_color
1697+
)
1698+
else:
1699+
payload["text_color_cmyk"] = _serialize_as_comma_separated_string(
1700+
serialized_color
1701+
)
1702+
return payload
16911703

16921704

16931705
class PdfImageWatermarkPayload(_BasePdfWatermarkPayload):

tests/test_watermark_pdf.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from __future__ import annotations
22

33
import json
4-
import re
54

65
import httpx
76
import pytest
@@ -29,7 +28,7 @@ def test_watermark_pdf_with_text(monkeypatch: pytest.MonkeyPatch) -> None:
2928
"files": [input_file],
3029
"watermark_text": "Confidential",
3130
"text_size": 72,
32-
"text_color_rgb": (255, 0, 0),
31+
"text_color": (255, 0, 0),
3332
"opacity": 0.5,
3433
"horizontal_alignment": "center",
3534
"vertical_alignment": "center",
@@ -75,7 +74,7 @@ def handler(request: httpx.Request) -> httpx.Response:
7574
response = client.watermark_pdf_with_text(
7675
input_file,
7776
watermark_text="Confidential",
78-
text_color_rgb=(255, 0, 0),
77+
text_color=(255, 0, 0),
7978
pages=["1", "3-5"],
8079
output="watermarked",
8180
)
@@ -174,6 +173,7 @@ def handler(request: httpx.Request) -> httpx.Response:
174173
payload = json.loads(request.content.decode("utf-8"))
175174
assert payload["watermark_text"] == "Draft"
176175
assert payload["text_color_cmyk"] == "0,0,0,50"
176+
assert "text_color_rgb" not in payload
177177
assert payload["opacity"] == 0.25
178178
assert payload["output"] == "custom"
179179
assert payload["debug"] == "yes"
@@ -205,7 +205,7 @@ def handler(request: httpx.Request) -> httpx.Response:
205205
response = client.watermark_pdf_with_text(
206206
input_file,
207207
watermark_text="Draft",
208-
text_color_cmyk=(0, 0, 0, 50),
208+
text_color=(0, 0, 0, 50),
209209
opacity=0.25,
210210
output="custom",
211211
extra_query={"trace": "true"},
@@ -226,7 +226,7 @@ def handler(request: httpx.Request) -> httpx.Response:
226226
assert timeout_value == pytest.approx(0.31)
227227

228228

229-
def test_watermark_pdf_with_text_validation_color_choice(
229+
def test_watermark_pdf_with_text_validation_rejects_invalid_text_color(
230230
monkeypatch: pytest.MonkeyPatch,
231231
) -> None:
232232
monkeypatch.delenv("PDFREST_API_KEY", raising=False)
@@ -240,14 +240,13 @@ def test_watermark_pdf_with_text_validation_color_choice(
240240
) as client,
241241
pytest.raises(
242242
ValidationError,
243-
match=re.escape("Specify only one of text_color_rgb or text_color_cmyk."),
243+
match=r"text_color",
244244
),
245245
):
246246
client.watermark_pdf_with_text(
247247
input_file,
248248
watermark_text="Confidential",
249-
text_color_rgb=(0, 0, 0),
250-
text_color_cmyk=(0, 0, 0, 0),
249+
text_color=(0, 0),
251250
)
252251

253252

@@ -334,6 +333,7 @@ async def test_async_watermark_pdf_with_text(monkeypatch: pytest.MonkeyPatch) ->
334333
"files": [input_file],
335334
"watermark_text": "Async",
336335
"text_size": 72,
336+
"text_color": (0, 0, 0),
337337
"opacity": 0.6,
338338
"horizontal_alignment": "center",
339339
"vertical_alignment": "center",

0 commit comments

Comments
 (0)