Skip to content

Commit 7c7ef77

Browse files
authored
ENH: Add ability to add hex encoded colors to outline items (#1186)
1 parent 42ae312 commit 7c7ef77

File tree

3 files changed

+36
-14
lines changed

3 files changed

+36
-14
lines changed

PyPDF2/_writer.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,7 +1164,7 @@ def add_outline_item(
11641164
title: str,
11651165
pagenum: int,
11661166
parent: Union[None, TreeObject, IndirectObject] = None,
1167-
color: Optional[Tuple[float, float, float]] = None,
1167+
color: Optional[Union[Tuple[float, float, float], str]] = None,
11681168
bold: bool = False,
11691169
italic: bool = False,
11701170
fit: FitType = "/Fit",
@@ -1178,7 +1178,7 @@ def add_outline_item(
11781178
:param parent: A reference to a parent outline item to create nested
11791179
outline items.
11801180
:param tuple color: Color of the outline item's font as a red, green, blue tuple
1181-
from 0.0 to 1.0
1181+
from 0.0 to 1.0 or as a Hex String (#RRGGBB)
11821182
:param bool bold: Outline item font is bold
11831183
:param bool italic: Outline item font is italic
11841184
:param str fit: The fit of the destination page. See

PyPDF2/generic.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,30 +2017,29 @@ def create_string_object(
20172017
def _create_outline_item(
20182018
action_ref: IndirectObject,
20192019
title: str,
2020-
color: Optional[Tuple[float, float, float]],
2020+
color: Union[Tuple[float, float, float], str, None],
20212021
italic: bool,
20222022
bold: bool,
20232023
) -> TreeObject:
20242024
outline_item = TreeObject()
2025-
20262025
outline_item.update(
20272026
{
20282027
NameObject("/A"): action_ref,
20292028
NameObject("/Title"): create_string_object(title),
20302029
}
20312030
)
2032-
2033-
if color is not None:
2031+
if color:
2032+
if isinstance(color, str):
2033+
color = hex_to_rgb(color)
20342034
outline_item.update(
20352035
{NameObject("/C"): ArrayObject([FloatObject(c) for c in color])}
20362036
)
2037-
2038-
format_flag = 0
2039-
if italic:
2040-
format_flag += 1
2041-
if bold:
2042-
format_flag += 2
2043-
if format_flag:
2037+
if italic or bold:
2038+
format_flag = 0
2039+
if italic:
2040+
format_flag += 1
2041+
if bold:
2042+
format_flag += 2
20442043
outline_item.update({NameObject("/F"): NumberObject(format_flag)})
20452044
return outline_item
20462045

@@ -2074,7 +2073,7 @@ def decode_pdfdocencoding(byte_array: bytes) -> str:
20742073

20752074

20762075
def hex_to_rgb(value: str) -> Tuple[float, float, float]:
2077-
return tuple(int(value[i : i + 2], 16) / 255.0 for i in (0, 2, 4)) # type: ignore
2076+
return tuple(int(value.lstrip("#")[i : i + 2], 16) / 255.0 for i in (0, 2, 4)) # type: ignore
20782077

20792078

20802079
class AnnotationBuilder:

tests/test_writer.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
TESTS_ROOT = Path(__file__).parent.resolve()
1414
PROJECT_ROOT = TESTS_ROOT.parent
1515
RESOURCE_ROOT = PROJECT_ROOT / "resources"
16+
EXTERNAL_ROOT = Path(PROJECT_ROOT) / "sample-files"
1617

1718

1819
def test_writer_clone():
@@ -633,3 +634,25 @@ def test_deprecate_bookmark_decorator():
633634
match="bookmark is deprecated as an argument. Use outline_item instead",
634635
):
635636
writer.add_outline_item_dict(bookmark=outline_item)
637+
638+
639+
def test_colors_in_outline_item():
640+
reader = PdfReader(EXTERNAL_ROOT / "004-pdflatex-4-pages/pdflatex-4-pages.pdf")
641+
writer = PdfWriter()
642+
writer.clone_document_from_reader(reader)
643+
purple_rgb = (0.50196, 0, 0.50196)
644+
writer.add_outline_item("First Outline Item", pagenum=2, color="800080")
645+
writer.add_outline_item("Second Outline Item", pagenum=3, color="#800080")
646+
writer.add_outline_item("Third Outline Item", pagenum=4, color=purple_rgb)
647+
648+
target = "tmp-named-color-outline.pdf"
649+
with open(target, "wb") as f:
650+
writer.write(f)
651+
652+
reader2 = PdfReader(target)
653+
for outline_item in reader2.outline:
654+
# convert float to string because of mutability
655+
assert [str(c) for c in outline_item.color] == [str(p) for p in purple_rgb]
656+
657+
# Cleanup
658+
os.remove(target) # remove for testing

0 commit comments

Comments
 (0)