diff --git a/stubs/fpdf2/@tests/requirements-stubtest.txt b/stubs/fpdf2/@tests/requirements-stubtest.txt new file mode 100644 index 000000000000..0d38bc5ea259 --- /dev/null +++ b/stubs/fpdf2/@tests/requirements-stubtest.txt @@ -0,0 +1 @@ +cryptography diff --git a/stubs/fpdf2/@tests/stubtest_allowlist.txt b/stubs/fpdf2/@tests/stubtest_allowlist.txt index 45c179096727..593989e8bece 100644 --- a/stubs/fpdf2/@tests/stubtest_allowlist.txt +++ b/stubs/fpdf2/@tests/stubtest_allowlist.txt @@ -12,3 +12,8 @@ fpdf.output.signer # Argument has default at runtime, but using it raises a TypeError. fpdf.FPDF.set_creation_date fpdf.fpdf.FPDF.set_creation_date + +# Checking the following function crashes stubtest 0.991, but seems to be +# fixed in later versions. +fpdf.FPDF.set_encryption +fpdf.fpdf.FPDF.set_encryption diff --git a/stubs/fpdf2/METADATA.toml b/stubs/fpdf2/METADATA.toml index 0c85c376b796..eecaf0a51206 100644 --- a/stubs/fpdf2/METADATA.toml +++ b/stubs/fpdf2/METADATA.toml @@ -1,5 +1,5 @@ -version = "2.6.0" -requires = ["types-Pillow"] +version = "2.6.1" +requires = ["types-Pillow>=9.2.0"] [tool.stubtest] ignore_missing_stub = false diff --git a/stubs/fpdf2/fpdf/__init__.pyi b/stubs/fpdf2/fpdf/__init__.pyi index 28e11ff14c1e..250fb72a4a1d 100644 --- a/stubs/fpdf2/fpdf/__init__.pyi +++ b/stubs/fpdf2/fpdf/__init__.pyi @@ -1,6 +1,6 @@ from pathlib import Path -from .enums import XPos as XPos, YPos as YPos +from .enums import Align as Align, XPos as XPos, YPos as YPos from .fpdf import FPDF as FPDF, TitleStyle as TitleStyle from .html import HTML2FPDF as HTML2FPDF, HTMLMixin as HTMLMixin from .prefs import ViewerPreferences as ViewerPreferences @@ -15,6 +15,7 @@ __all__ = [ "__version__", "__license__", "FPDF", + "Align", "XPos", "YPos", "Template", diff --git a/stubs/fpdf2/fpdf/encryption.pyi b/stubs/fpdf2/fpdf/encryption.pyi new file mode 100644 index 000000000000..1888d6f3bc43 --- /dev/null +++ b/stubs/fpdf2/fpdf/encryption.pyi @@ -0,0 +1,89 @@ +from _typeshed import Incomplete, SupportsLenAndGetItem +from collections.abc import Generator, Iterable +from typing import ClassVar, Protocol, TypeVar +from typing_extensions import TypeAlias + +from .enums import EncryptionMethod +from .fpdf import FPDF +from .syntax import Name, PDFObject + +_Key: TypeAlias = SupportsLenAndGetItem[int] +_T_co = TypeVar("_T_co", covariant=True) + +import_error: ImportError | None + +class _SupportsGetItem(Protocol[_T_co]): + def __getitem__(self, __k: int) -> _T_co: ... + +class ARC4: + MOD: ClassVar[int] + def KSA(self, key: _Key) -> list[int]: ... + def PRGA(self, S: _SupportsGetItem[int]) -> Generator[int, None, None]: ... + def encrypt(self, key: _Key, text: Iterable[int]) -> list[int]: ... + +class CryptFilter: + type: Name + c_f_m: Name + length: int + def __init__(self, mode, length) -> None: ... + def serialize(self) -> str: ... + +class EncryptionDictionary(PDFObject): + filter: Name + length: int + r: int + o: str + u: str + v: int + p: int + encrypt_metadata: str # not always defined + c_f: str # not always defined + stm_f: Name + str_f: Name + def __init__(self, security_handler: StandardSecurityHandler) -> None: ... + +class StandardSecurityHandler: + DEFAULT_PADDING: ClassVar[bytes] + fpdf: FPDF + access_permission: int + owner_password: str + user_password: str + encryption_method: EncryptionMethod | None + cf: CryptFilter | None + key_length: int + v: int + r: int + encrypt_metadata: bool + + # The following fields are only defined after a call to generate_passwords(). + file_id: Incomplete + info_id: Incomplete + o: str + k: str + u: str + + def __init__( + self, + fpdf: FPDF, + owner_password: str, + user_password: str | None = None, + permission: Incomplete | None = None, + encryption_method: EncryptionMethod | None = None, + encrypt_metadata: bool = False, + ) -> None: ... + def generate_passwords(self, file_id) -> None: ... + def get_encryption_obj(self) -> EncryptionDictionary: ... + def encrypt(self, text: str | bytes | bytearray, obj_id) -> bytes: ... + def encrypt_string(self, string, obj_id): ... + def encrypt_stream(self, stream, obj_id): ... + def is_aes_algorithm(self) -> bool: ... + def encrypt_bytes(self, data, obj_id) -> list[int]: ... + def encrypt_AES_cryptography(self, key, data): ... + def get_initialization_vector(self, size: int) -> bytearray: ... + def padded_password(self, password: str) -> bytearray: ... + def generate_owner_password(self) -> str: ... + def generate_user_password(self) -> str: ... + def generate_encryption_key(self) -> bytes: ... + +def md5(data: bytes) -> bytes: ... +def int32(n: int) -> int: ... diff --git a/stubs/fpdf2/fpdf/enums.pyi b/stubs/fpdf2/fpdf/enums.pyi index c756ddfaf35d..395e28103fd4 100644 --- a/stubs/fpdf2/fpdf/enums.pyi +++ b/stubs/fpdf2/fpdf/enums.pyi @@ -1,5 +1,6 @@ -from _typeshed import Incomplete, Self -from enum import Enum, Flag, IntEnum +from _typeshed import Self +from enum import Enum, Flag, IntEnum, IntFlag +from typing_extensions import Literal from .syntax import Name @@ -182,4 +183,23 @@ class FontDescriptorFlags(Flag): ITALIC: int FORCE_BOLD: int -__pdoc__: Incomplete +class AccessPermission(IntFlag): + PRINT_LOW_RES: int + MODIFY: int + COPY: int + ANNOTATION: int + FILL_FORMS: int + COPY_FOR_ACCESSIBILITY: int + ASSEMBLE: int + PRINT_HIGH_RES: int + @classmethod + def all(cls) -> int: ... + @classmethod + def none(cls) -> Literal[0]: ... + +class EncryptionMethod(Enum): + NO_ENCRYPTION: int + RC4: int + AES_128: int + +__pdoc__: dict[str, bool] diff --git a/stubs/fpdf2/fpdf/fpdf.pyi b/stubs/fpdf2/fpdf/fpdf.pyi index 9e97ea957314..67d131aec257 100644 --- a/stubs/fpdf2/fpdf/fpdf.pyi +++ b/stubs/fpdf2/fpdf/fpdf.pyi @@ -3,6 +3,7 @@ from _typeshed import Incomplete, StrPath from collections.abc import Callable, Iterable, Sequence from contextlib import _GeneratorContextManager from io import BytesIO +from re import Pattern from typing import Any, ClassVar, NamedTuple, overload from typing_extensions import Literal, TypeAlias @@ -72,6 +73,8 @@ class FPDF: MARKDOWN_BOLD_MARKER: ClassVar[str] MARKDOWN_ITALICS_MARKER: ClassVar[str] MARKDOWN_UNDERLINE_MARKER: ClassVar[str] + MARKDOWN_LINK_REGEX: ClassVar[Pattern[str]] + MARKDOWN_LINK_COLOR: ClassVar[Incomplete | None] HTML2FPDF_CLASS: ClassVar[type[HTML2FPDF]] @@ -144,6 +147,16 @@ class FPDF: format: _Format | tuple[float, float] = ..., font_cache_dir: Literal["DEPRECATED"] = ..., ) -> None: ... + # The following definition crashes stubtest 0.991, but seems to be fixed + # in later versions. + # def set_encryption( + # self, + # owner_password: str, + # user_password: str | None = None, + # encryption_method: EncryptionMethod | str = ..., + # permissions: AccessPermission = ..., + # encrypt_metadata: bool = False, + # ) -> None: ... # args and kwargs are passed to HTML2FPDF_CLASS constructor. def write_html(self, text: str, *args: Any, **kwargs: Any) -> None: ... @property @@ -288,8 +301,8 @@ class FPDF: def set_font_size(self, size: float) -> None: ... def set_char_spacing(self, spacing: float) -> None: ... def set_stretching(self, stretching: float) -> None: ... - def add_link(self) -> int: ... - def set_link(self, link, y: int = ..., x: int = ..., page: int = ..., zoom: float | Literal["null"] = ...) -> None: ... + def add_link(self, y: float = 0, x: float = 0, page: int = -1, zoom: float | Literal["null"] = ...) -> int: ... + def set_link(self, link, y: float = 0, x: float = 0, page: int = -1, zoom: float | Literal["null"] = ...) -> None: ... def link( self, x: float, y: float, w: float, h: float, link: str | int, alt_text: str | None = ..., border_width: int = ... ) -> AnnotationDict: ... @@ -364,6 +377,9 @@ class FPDF: def text(self, x: float, y: float, txt: str = ...) -> None: ... def rotate(self, angle: float, x: float | None = ..., y: float | None = ...) -> None: ... def rotation(self, angle: float, x: float | None = ..., y: float | None = ...) -> _GeneratorContextManager[None]: ... + def skew( + self, ax: float = 0, ay: float = 0, x: float | None = None, y: float | None = None + ) -> _GeneratorContextManager[None]: ... def local_context( self, font_family: Incomplete | None = ..., @@ -415,14 +431,15 @@ class FPDF: def image( self, name: str | Image.Image | BytesIO | StrPath, - x: float | None = ..., - y: float | None = ..., - w: float = ..., - h: float = ..., + x: float | Align | None = None, + y: float | None = None, + w: float = 0, + h: float = 0, type: str = ..., link: str = ..., - title: str | None = ..., - alt_text: str | None = ..., + title: str | None = None, + alt_text: str | None = None, + dims: tuple[float, float] | None = None, ) -> _Image: ... def ln(self, h: float | None = ...) -> None: ... def get_x(self) -> float: ... @@ -477,4 +494,4 @@ class FPDF: level5: TitleStyle | None = ..., level6: TitleStyle | None = ..., ) -> None: ... - def start_section(self, name: str, level: int = ...) -> None: ... + def start_section(self, name: str, level: int = 0, strict: bool = True) -> None: ... diff --git a/stubs/fpdf2/fpdf/html.pyi b/stubs/fpdf2/fpdf/html.pyi index 8b05ff9c80d2..f3c6679eda07 100644 --- a/stubs/fpdf2/fpdf/html.pyi +++ b/stubs/fpdf2/fpdf/html.pyi @@ -3,6 +3,7 @@ from collections.abc import Callable from html.parser import HTMLParser from logging import Logger from re import Match, Pattern +from typing import ClassVar from typing_extensions import Final from fpdf import FPDF @@ -24,6 +25,7 @@ def px2mm(px: float) -> float: ... def color_as_decimal(color: str | None = ...) -> tuple[int, int, int] | None: ... class HTML2FPDF(HTMLParser): + HTML_UNCLOSED_TAGS: ClassVar[tuple[str, ...]] pdf: Incomplete image_map: Incomplete li_tag_indent: Incomplete @@ -55,15 +57,17 @@ class HTML2FPDF(HTMLParser): heading_sizes: Incomplete heading_above: float heading_below: float + warn_on_tags_not_matching: bool def __init__( self, pdf: FPDF, - image_map: Callable[[str], str] | None = ..., - li_tag_indent: int = ..., - dd_tag_indent: int = ..., - table_line_separators: bool = ..., + image_map: Callable[[str], str] | None = None, + li_tag_indent: int = 5, + dd_tag_indent: int = 10, + table_line_separators: bool = False, ul_bullet_char: str = ..., - heading_sizes: Incomplete | None = ..., + heading_sizes: Incomplete | None = None, + warn_on_tags_not_matching: bool = True, **_: object, ): ... def width2unit(self, length): ... diff --git a/stubs/fpdf2/fpdf/image_parsing.pyi b/stubs/fpdf2/fpdf/image_parsing.pyi index 586aad2a4f83..a1b9849bf400 100644 --- a/stubs/fpdf2/fpdf/image_parsing.pyi +++ b/stubs/fpdf2/fpdf/image_parsing.pyi @@ -1,8 +1,11 @@ from typing import Any from typing_extensions import Literal, TypeAlias +from PIL.Image import Resampling + _ImageFilter: TypeAlias = Literal["AUTO", "FlateDecode", "DCTDecode", "JPXDecode"] +RESAMPLE: Resampling SUPPORTED_IMAGE_FILTERS: tuple[_ImageFilter, ...] def load_image(filename): ... diff --git a/stubs/fpdf2/fpdf/line_break.pyi b/stubs/fpdf2/fpdf/line_break.pyi index 9793b3e7e892..01d3a3518cb1 100644 --- a/stubs/fpdf2/fpdf/line_break.pyi +++ b/stubs/fpdf2/fpdf/line_break.pyi @@ -11,7 +11,10 @@ class Fragment: characters: list[str] graphics_state: dict[str, Incomplete] k: float - def __init__(self, characters: list[str] | str, graphics_state: dict[str, Incomplete], k: float) -> None: ... + url: str | None + def __init__( + self, characters: list[str] | str, graphics_state: dict[str, Incomplete], k: float, url: str | None = None + ) -> None: ... @property def font(self): ... @font.setter @@ -96,6 +99,7 @@ class CurrentLine: k: float, original_fragment_index: int, original_character_index: int, + url: str | None = None, ): ... def manual_break(self, justify: bool = ..., trailing_nl: bool = ...): ... def automatic_break_possible(self): ... diff --git a/stubs/fpdf2/fpdf/linearization.pyi b/stubs/fpdf2/fpdf/linearization.pyi index 98c280ce993b..cfbaaf5ba42d 100644 --- a/stubs/fpdf2/fpdf/linearization.pyi +++ b/stubs/fpdf2/fpdf/linearization.pyi @@ -1,6 +1,7 @@ from _typeshed import Incomplete from typing_extensions import Final +from .encryption import StandardSecurityHandler from .output import ContentWithoutID, OutputProducer from .syntax import PDFContentStream, PDFObject @@ -34,7 +35,7 @@ class PDFXrefAndTrailer(ContentWithoutID): def is_first_xref(self) -> bool: ... @property def is_main_xref(self) -> bool: ... - def serialize(self) -> str: ... + def serialize(self, _security_handler: StandardSecurityHandler | None = None) -> str: ... class PDFHintStream(PDFContentStream): s: Incomplete | None diff --git a/stubs/fpdf2/fpdf/output.pyi b/stubs/fpdf2/fpdf/output.pyi index dffef9fccdcc..533945b3666d 100644 --- a/stubs/fpdf2/fpdf/output.pyi +++ b/stubs/fpdf2/fpdf/output.pyi @@ -4,17 +4,19 @@ from logging import Logger from typing_extensions import Final from .annotations import AnnotationDict +from .encryption import StandardSecurityHandler from .syntax import Name, PDFArray, PDFContentStream, PDFObject LOGGER: Logger ZOOM_CONFIGS: Final[dict[str, tuple[str, ...]]] -class ContentWithoutID: ... +class ContentWithoutID: + def serialize(self, _security_handler: StandardSecurityHandler | None = None) -> str | None: ... class PDFHeader(ContentWithoutID): pdf_version: str def __init__(self, pdf_version: str) -> None: ... - def serialize(self) -> str: ... + def serialize(self, _security_handler: StandardSecurityHandler | None = None) -> str: ... class PDFFont(PDFObject): type: Name @@ -149,7 +151,8 @@ class PDFPage(PDFObject): struct_parents: Incomplete | None resources: Incomplete | None parent: Incomplete | None - def __init__(self, duration: Incomplete | None, transition, contents) -> None: ... + def __init__(self, duration: Incomplete | None, transition, contents, index) -> None: ... + def index(self): ... def dimensions(self) -> tuple[float | None, float | None]: ... def set_dimensions(self, width_pt: float | None, height_pt: float | None) -> None: ... @@ -162,7 +165,7 @@ class PDFPagesRoot(PDFObject): class PDFExtGState(PDFObject): def __init__(self, dict_as_str) -> None: ... - def serialize(self, obj_dict: object = ...) -> str: ... + def serialize(self, obj_dict: object = None, _security_handler: StandardSecurityHandler | None = None) -> str: ... class PDFXrefAndTrailer(ContentWithoutID): output_builder: Incomplete @@ -170,7 +173,7 @@ class PDFXrefAndTrailer(ContentWithoutID): catalog_obj: Incomplete | None info_obj: Incomplete | None def __init__(self, output_builder) -> None: ... - def serialize(self) -> str: ... + def serialize(self, _security_handler: StandardSecurityHandler | None = None) -> str: ... class OutputProducer: fpdf: Incomplete diff --git a/stubs/fpdf2/fpdf/structure_tree.pyi b/stubs/fpdf2/fpdf/structure_tree.pyi index c3a7731134bf..1ce33a01c8ad 100644 --- a/stubs/fpdf2/fpdf/structure_tree.pyi +++ b/stubs/fpdf2/fpdf/structure_tree.pyi @@ -2,12 +2,13 @@ from _typeshed import Incomplete from collections import defaultdict from collections.abc import Generator, Iterable +from .encryption import StandardSecurityHandler from .syntax import PDFArray, PDFObject, PDFString class NumberTree(PDFObject): nums: defaultdict[Incomplete, list[Incomplete]] def __init__(self) -> None: ... - def serialize(self, obj_dict: object = ...) -> str: ... + def serialize(self, obj_dict: object = ..., _security_handler: StandardSecurityHandler | None = None) -> str: ... class StructTreeRoot(PDFObject): type: str diff --git a/stubs/fpdf2/fpdf/syntax.pyi b/stubs/fpdf2/fpdf/syntax.pyi index c40f468213ec..bff2ec0f07bc 100644 --- a/stubs/fpdf2/fpdf/syntax.pyi +++ b/stubs/fpdf2/fpdf/syntax.pyi @@ -4,6 +4,8 @@ from re import Pattern from typing import ClassVar, Generic, TypeVar from typing_extensions import Literal +from .encryption import StandardSecurityHandler + _T = TypeVar("_T") def clear_empty_fields(d): ... @@ -17,7 +19,9 @@ def create_dictionary_string( ): ... def create_list_string(list_): ... def iobj_ref(n): ... -def create_stream(stream): ... +def create_stream( + stream: str | bytes | bytearray, encryption_handler: StandardSecurityHandler | None = None, obj_id: Incomplete | None = None +): ... class Raw(str): ... @@ -33,13 +37,14 @@ class PDFObject: def id(self, n: int) -> None: ... @property def ref(self) -> str: ... - def serialize(self, obj_dict: Incomplete | None = ...) -> str: ... + def serialize(self, obj_dict: Incomplete | None = ..., _security_handler: StandardSecurityHandler | None = None) -> str: ... def content_stream(self) -> bytes: ... class PDFContentStream(PDFObject): filter: Name | None length: int def __init__(self, contents: bytes, compress: bool = ...) -> None: ... + def encrypt(self, security_handler: StandardSecurityHandler) -> None: ... def build_obj_dict(key_values: SupportsItems[str, Incomplete]) -> dict[str, str]: ... def camel_case(snake_case: str) -> str: ...