1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
|
"""Test the pypdf.constants module."""
import re
from typing import Callable
import pytest
from pypdf.constants import PDF_KEYS, GraphicsStateParameters, UserAccessPermissions
def test_slash_prefix():
"""
Naming conventions of PDF_KEYS (constant names) are followed.
This test function validates if PDF key names follow the required pattern:
- Starts with a slash "/"
- Followed by an uppercase letter
- Contains alphanumeric characters (letters and digits)
- The attribute name should be a case-insensitive match, with underscores removed
"""
pattern = re.compile(r"^\/[A-Z]+[a-zA-Z0-9]*$")
for cls in PDF_KEYS:
for attr in dir(cls):
# Skip magic methods
if attr.startswith("__") and attr.endswith("__"):
continue
# Skip methods
constant_value = getattr(cls, attr)
if isinstance(constant_value, Callable):
continue
assert constant_value.startswith("/")
assert attr.replace("_", "").casefold() == constant_value[1:].casefold()
# There are a few exceptions that may be lowercase
if cls == GraphicsStateParameters and attr in ["ca", "op"]:
continue
assert pattern.match(constant_value)
def test_user_access_permissions__dict_handling():
# Value is mix of configurable and reserved bits.
# Reserved bits should not be part of the dictionary.
as_dict = UserAccessPermissions(512 + 64 + 8).to_dict()
assert as_dict == {
"add_or_modify": False,
"assemble_doc": False,
"extract": False,
"extract_text_and_graphics": True,
"fill_form_fields": False,
"modify": True,
"print": False,
"print_to_representation": False,
}
# Convert the dictionary back to an integer.
# This should add the reserved bits automatically.
permissions = UserAccessPermissions.from_dict(as_dict)
assert permissions == 4294963912
# Roundtrip for valid dictionary.
data = {
"add_or_modify": True,
"assemble_doc": False,
"extract": False,
"extract_text_and_graphics": True,
"fill_form_fields": False,
"modify": True,
"print": False,
"print_to_representation": True,
}
assert UserAccessPermissions.from_dict(data).to_dict() == data
# Empty inputs.
assert UserAccessPermissions.from_dict({}) == 4294963392 # Reserved bits.
assert UserAccessPermissions(0).to_dict() == {
"add_or_modify": False,
"assemble_doc": False,
"extract": False,
"extract_text_and_graphics": False,
"fill_form_fields": False,
"modify": False,
"print": False,
"print_to_representation": False,
}
# Unknown dictionary keys.
data = {
"add_or_modify": True,
"key1": False,
"key2": True,
}
unknown = {
"key1": False,
"key2": True,
}
with pytest.raises(
ValueError,
match=f"Unknown dictionary keys: {unknown!r}"
):
UserAccessPermissions.from_dict(data)
def test_user_access_permissions__all():
all_permissions = UserAccessPermissions.all()
all_int = int(all_permissions)
all_string = bin(all_permissions)
assert all_string.startswith("0b")
assert len(all_string[2:]) == 32 # 32-bit integer
assert all_int & UserAccessPermissions.R1 == 0
assert all_int & UserAccessPermissions.R2 == 0
assert all_int & UserAccessPermissions.PRINT == UserAccessPermissions.PRINT
assert all_int & UserAccessPermissions.R7 == UserAccessPermissions.R7
assert all_int & UserAccessPermissions.R31 == UserAccessPermissions.R31
|