diff --git a/stubs/fpdf2/@tests/stubtest_allowlist.txt b/stubs/fpdf2/@tests/stubtest_allowlist.txt index 4ceb15988f18..a176497098a3 100644 --- a/stubs/fpdf2/@tests/stubtest_allowlist.txt +++ b/stubs/fpdf2/@tests/stubtest_allowlist.txt @@ -2,9 +2,19 @@ fpdf.FPDF.set_creation_date fpdf.fpdf.FPDF.set_creation_date +# Dynamically added, deprecated arguments. +fpdf.FPDF.add_font +fpdf.FPDF.output +fpdf.fpdf.FPDF.add_font +fpdf.fpdf.FPDF.output + # fonttools shims since we can't import it fpdf._fonttools_shims +# Deprecated attributes supported via __getattr__ and __setattr__. +fpdf.deprecation.WarnOnDeprecatedModuleAttributes.FPDF_CACHE_DIR +fpdf.deprecation.WarnOnDeprecatedModuleAttributes.FPDF_CACHE_MODE + # Only present if harfbuzz is installed fpdf.fonts.HarfBuzzFont diff --git a/stubs/fpdf2/METADATA.toml b/stubs/fpdf2/METADATA.toml index 99ca1632cb80..798fcf1d018c 100644 --- a/stubs/fpdf2/METADATA.toml +++ b/stubs/fpdf2/METADATA.toml @@ -1,4 +1,4 @@ -version = "2.8.4" +version = "2.8.5" upstream_repository = "https://github.com/PyFPDF/fpdf2" requires = ["Pillow>=10.3.0"] diff --git a/stubs/fpdf2/fpdf/_fonttools_shims.pyi b/stubs/fpdf2/fpdf/_fonttools_shims.pyi index f68d651014d6..9832ce3eb61c 100644 --- a/stubs/fpdf2/fpdf/_fonttools_shims.pyi +++ b/stubs/fpdf2/fpdf/_fonttools_shims.pyi @@ -1,8 +1,8 @@ -# from fontTools.misc.loggingTools +from _typeshed import Incomplete from abc import ABCMeta, abstractmethod from collections.abc import Mapping from logging import Logger -from typing import Any, Protocol +from typing import Protocol from typing_extensions import TypeAlias # from fonttools.ttLib.ttGlyphSet @@ -13,8 +13,13 @@ class _TTGlyph(Protocol): _TTGlyphSet: TypeAlias = Mapping[str, _TTGlyph] # Simplified for our needs -# fonttools.ttLib.TTFont -_TTFont: TypeAlias = Any # noqa: Y047 +# from fonttools.ttLib.TTFont +_TTFont: TypeAlias = Incomplete # noqa: Y047 + +# from fonttools.ttLib.tables.otTables +CompositeMode: TypeAlias = Incomplete +Paint: TypeAlias = Incomplete +PaintFormat: TypeAlias = Incomplete # from fontTools.misc.loggingTools @@ -53,3 +58,6 @@ class BasePen(DecomposingPen): def lineTo(self, pt: tuple[float, float]) -> None: ... def curveTo(self, *points: tuple[float, float]) -> None: ... def qCurveTo(self, *points: tuple[float, float]) -> None: ... + +# from fontTools.varLib.varStore +VarStoreInstancer: TypeAlias = Incomplete diff --git a/stubs/fpdf2/fpdf/annotations.pyi b/stubs/fpdf2/fpdf/annotations.pyi index b55b30978dff..90d82fc0272f 100644 --- a/stubs/fpdf2/fpdf/annotations.pyi +++ b/stubs/fpdf2/fpdf/annotations.pyi @@ -1,10 +1,9 @@ from _typeshed import Incomplete from datetime import datetime -from typing import NamedTuple from .actions import Action -from .enums import AnnotationFlag, AnnotationName, FileAttachmentAnnotationName -from .syntax import Destination, Name, PDFContentStream, PDFObject +from .enums import AnnotationFlag, AnnotationName, AssociatedFileRelationship, FileAttachmentAnnotationName +from .syntax import Destination, Name, PDFContentStream, PDFObject, PDFString DEFAULT_ANNOT_FLAGS: tuple[AnnotationFlag, ...] @@ -87,6 +86,8 @@ class PDFEmbeddedFile(PDFContentStream): desc: str = "", creation_date: datetime | None = None, modification_date: datetime | None = None, + mime_type: str | None = None, + af_relationship: AssociatedFileRelationship | None = None, compress: bool = False, checksum: bool = False, ) -> None: ... @@ -95,8 +96,20 @@ class PDFEmbeddedFile(PDFContentStream): def basename(self) -> str: ... def file_spec(self) -> FileSpec: ... -class FileSpec(NamedTuple): +class FileSpec(PDFObject): + type: Name + f: PDFString + uf: PDFString embedded_file: PDFEmbeddedFile + desc: PDFString # Only exists if provided to __init__ + a_f_relationship: Name # Only exists if provided to __init__ basename: str - desc: str - def serialize(self) -> str: ... + def __init__( + self, + embedded_file: PDFEmbeddedFile, + basename: str, + desc: str | None = None, + af_relationship: AssociatedFileRelationship | None = None, + ) -> None: ... + @property + def e_f(self) -> str: ... diff --git a/stubs/fpdf2/fpdf/data/__init__.pyi b/stubs/fpdf2/fpdf/data/__init__.pyi new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/stubs/fpdf2/fpdf/data/color_profiles/__init__.pyi b/stubs/fpdf2/fpdf/data/color_profiles/__init__.pyi new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/stubs/fpdf2/fpdf/deprecation.pyi b/stubs/fpdf2/fpdf/deprecation.pyi index a1399a56191a..2b33548d1b56 100644 --- a/stubs/fpdf2/fpdf/deprecation.pyi +++ b/stubs/fpdf2/fpdf/deprecation.pyi @@ -1,11 +1,29 @@ +from _typeshed import Unused +from collections.abc import Callable, Iterable from types import ModuleType -from typing import Any, NoReturn +from typing import Any, NoReturn, TypeVar +from typing_extensions import deprecated -def support_deprecated_txt_arg(fn): ... +_R = TypeVar("_R") +_CallableT = TypeVar("_CallableT", bound=Callable[..., Any]) + +# This changes the signature by replacing a `txt` parameter with `text`. +def support_deprecated_txt_arg(fn: Callable[..., _R]) -> Callable[..., _R]: ... +def deprecated_parameter(parameters: Iterable[tuple[str, str]]) -> Callable[[_CallableT], _CallableT]: ... class WarnOnDeprecatedModuleAttributes(ModuleType): def __call__(self) -> NoReturn: ... - def __getattr__(self, name: str) -> Any: ... - def __setattr__(self, name: str, value: Any) -> None: ... + @property + @deprecated("deprecated in favour of FPDF(font_cache_dir=...)") + def FPDF_CACHE_DIR(self) -> None: ... + @FPDF_CACHE_DIR.setter + @deprecated("deprecated in favour of FPDF(font_cache_dir=...)") + def FPDF_CACHE_DIR(self, value: Unused, /) -> None: ... + @property + @deprecated("deprecated in favour of FPDF(font_cache_dir=...)") + def FPDF_CACHE_MODE(self) -> None: ... + @FPDF_CACHE_MODE.setter + @deprecated("deprecated in favour of FPDF(font_cache_dir=...)") + def FPDF_CACHE_MODE(self, value: Unused, /) -> None: ... def get_stack_level() -> int: ... diff --git a/stubs/fpdf2/fpdf/drawing.pyi b/stubs/fpdf2/fpdf/drawing.pyi index c393f1f7eaa7..112f67bc7dac 100644 --- a/stubs/fpdf2/fpdf/drawing.pyi +++ b/stubs/fpdf2/fpdf/drawing.pyi @@ -1,180 +1,116 @@ -import decimal import sys -from _typeshed import Incomplete, SupportsWrite -from collections import OrderedDict -from collections.abc import Callable, Generator, Iterable, Sequence +from _typeshed import SupportsWrite, Unused +from binascii import Incomplete +from collections.abc import Generator, Iterable from contextlib import contextmanager -from re import Pattern -from typing import Any, ClassVar, Literal, NamedTuple, Protocol, TypeVar, overload, type_check_only +from typing import Final, Literal, NamedTuple, Protocol, runtime_checkable, type_check_only from typing_extensions import Self, TypeAlias +from ._fonttools_shims import BasePen, _TTGlyphSet +from .drawing_primitives import ( + DeviceCMYK as DeviceCMYK, + DeviceGray as DeviceGray, + DeviceRGB as DeviceRGB, + Number as Number, + NumberClass as NumberClass, + Point as Point, + Transform as Transform, + check_range as check_range, + color_from_hex_string as color_from_hex_string, + force_nodocument as force_nodocument, + number_to_str as number_to_str, +) +from .enums import BlendMode, CompositingOperation, GradientSpreadMethod, GradientUnits, PathPaintRule +from .output import ResourceCatalog +from .pattern import Gradient +from .syntax import Name, Raw + if sys.version_info >= (3, 10): from types import EllipsisType else: # Rely on builtins.ellipsis from builtins import ellipsis as EllipsisType -from .enums import PathPaintRule -from .syntax import Name, Raw - -__pdoc__: dict[str, bool] - -_T = TypeVar("_T") -_CallableT = TypeVar("_CallableT", bound=Callable[..., Any]) - @type_check_only class _SupportsSerialize(Protocol): def serialize(self) -> str: ... -@type_check_only -class _SupportsEndPoint(Protocol): - @property - def end_point(self) -> Point: ... - -def force_nodocument(item: _CallableT) -> _CallableT: ... -def force_document(item: _CallableT) -> _CallableT: ... - -Number: TypeAlias = int | float | decimal.Decimal -NumberClass: tuple[type, ...] -WHITESPACE: frozenset[str] -EOL_CHARS: frozenset[str] -DELIMITERS: frozenset[str] -STR_ESC: Pattern[str] -STR_ESC_MAP: dict[str, str] +# Type check only type alias _Primitive: TypeAlias = ( - _SupportsSerialize - | Number - | str - | bytes - | bool - | Raw - | list[_Primitive] - | tuple[_Primitive, ...] - | dict[Name, _Primitive] - | None + _SupportsSerialize | Number | str | bytes | bool | list[_Primitive] | tuple[_Primitive, ...] | dict[Name, _Primitive] | None ) -class GraphicsStateDictRegistry(OrderedDict[Raw, Name]): - def register_style(self, style: GraphicsStyle) -> Name | None: ... - -def number_to_str(number: Number) -> str: ... def render_pdf_primitive(primitive: _Primitive) -> Raw: ... -class _DeviceRGBBase(NamedTuple): - r: Number - g: Number - b: Number - a: Number | None - -class DeviceRGB(_DeviceRGBBase): - OPERATOR: ClassVar[str] - def __new__(cls, r: Number, g: Number, b: Number, a: Number | None = None) -> Self: ... - @property - def colors(self) -> tuple[Number, Number, Number]: ... - @property - def colors255(self) -> tuple[Number, Number, Number]: ... - def serialize(self) -> str: ... - -class _DeviceGrayBase(NamedTuple): - g: Number - a: Number | None - -class DeviceGray(_DeviceGrayBase): - OPERATOR: ClassVar[str] - def __new__(cls, g: Number, a: Number | None = None) -> Self: ... - @property - def colors(self) -> tuple[Number, Number, Number]: ... - @property - def colors255(self) -> tuple[Number, Number, Number]: ... - def serialize(self) -> str: ... +class GradientPaint: + __slots__ = ("gradient", "units", "gradient_transform", "apply_page_ctm", "skip_alpha", "spread_method") -class _DeviceCMYKBase(NamedTuple): - c: Number - m: Number - y: Number - k: Number - a: Number | None + gradient: Gradient + units: GradientUnits + gradient_transform: Transform + apply_page_ctm: bool + skip_alpha: bool + spread_method: GradientSpreadMethod -class DeviceCMYK(_DeviceCMYKBase): - OPERATOR: ClassVar[str] - def __new__(cls, c: Number, m: Number, y: Number, k: Number, a: Number | None = None) -> Self: ... - @property - def colors(self) -> tuple[Number, Number, Number, Number]: ... - def serialize(self) -> str: ... + def __init__( + self, + gradient: Gradient, + units: GradientUnits | str = GradientUnits.USER_SPACE_ON_USE, + gradient_transform: Transform | None = None, + apply_page_ctm: bool = True, + spread_method: GradientSpreadMethod | str | None = None, + ) -> None: ... + def emit_fill(self, resource_catalog, bbox: BoundingBox | None) -> str: ... + def emit_stroke(self, resource_catalog, bbox: BoundingBox | None) -> str: ... + def has_alpha(self) -> bool: ... + +class BoundingBox(NamedTuple): + x0: float + y0: float + x1: float + y1: float -def rgb8(r: Number, g: Number, b: Number, a: Number | None = None) -> DeviceRGB: ... -def gray8(g: Number, a: Number | None = None) -> DeviceGray: ... -@overload -def convert_to_device_color(r: DeviceCMYK) -> DeviceCMYK: ... -@overload -def convert_to_device_color(r: DeviceGray) -> DeviceGray: ... -@overload -def convert_to_device_color(r: DeviceRGB) -> DeviceRGB: ... -@overload -def convert_to_device_color(r: str) -> DeviceRGB: ... -@overload -def convert_to_device_color(r: int, g: Literal[-1] = -1, b: Literal[-1] = -1) -> DeviceGray: ... -@overload -def convert_to_device_color(r: Sequence[int] | int, g: int, b: int) -> DeviceGray | DeviceRGB: ... -def cmyk8(c, m, y, k, a=None) -> DeviceCMYK: ... -def color_from_hex_string(hexstr: str) -> DeviceRGB: ... -def color_from_rgb_string(rgbstr: str) -> DeviceRGB: ... - -class Point(NamedTuple): - x: Number - y: Number - def render(self) -> str: ... - def dot(self, other: Point) -> Number: ... - def angle(self, other: Point) -> float: ... - def mag(self) -> Number: ... - def __add__(self, other: Point) -> Point: ... # type: ignore[override] - def __sub__(self, other: Point) -> Point: ... - def __neg__(self) -> Point: ... - def __mul__(self, other: Number) -> Point: ... # type: ignore[override] - def __rmul__(self, other: Number) -> Point: ... # type: ignore[override] - def __truediv__(self, other: Number) -> Point: ... - def __floordiv__(self, other: Number) -> Point: ... - def __matmul__(self, other: Transform) -> Point: ... - -class Transform(NamedTuple): - a: Number - b: Number - c: Number - d: Number - e: Number - f: Number - @classmethod - def identity(cls) -> Self: ... - @classmethod - def translation(cls, x: Number, y: Number) -> Self: ... @classmethod - def scaling(cls, x: Number, y: Number | None = None) -> Self: ... + def empty(cls) -> Self: ... + def is_valid(self) -> bool: ... @classmethod - def rotation(cls, theta: Number) -> Self: ... - @classmethod - def rotation_d(cls, theta_d: Number) -> Self: ... - @classmethod - def shearing(cls, x: Number, y: Number | None = None) -> Self: ... - def translate(self, x: Number, y: Number) -> Self: ... - def scale(self, x: Number, y: Number | None = None) -> Self: ... - def rotate(self, theta: Number) -> Self: ... - def rotate_d(self, theta_d: Number) -> Self: ... - def shear(self, x: Number, y: Number | None = None) -> Self: ... - def about(self, x: Number, y: Number) -> Transform: ... - def __mul__(self, other: Number) -> Transform: ... # type: ignore[override] - def __rmul__(self, other: Number) -> Transform: ... # type: ignore[override] - def __matmul__(self, other: Transform) -> Self: ... - def render(self, last_item: _T) -> tuple[str, _T]: ... + def from_points(cls, points: Iterable[Point]) -> Self: ... + def merge(self, other: BoundingBox) -> BoundingBox: ... + def transformed(self, tf: Transform) -> Self: ... + def expanded(self, dx: float, dy: float | None = None) -> BoundingBox: ... + def expanded_to_stroke(self, style: GraphicsStyle, row_norms: tuple[float, float] = (1.0, 1.0)) -> BoundingBox: ... + def to_tuple(self) -> tuple[float, float, float, float]: ... + def to_pdf_array(self) -> str: ... + def corners(self) -> tuple[tuple[float, float], tuple[float, float], tuple[float, float], tuple[float, float]]: ... + def project_interval_on_axis(self, x1: float, y1: float, x2: float, y2: float) -> tuple[float, float, float]: ... + def max_distance_to_point(self, cx: float, cy: float) -> float: ... + @property + def width(self) -> float: ... + @property + def height(self) -> float: ... + def __eq__(self, other: object) -> bool: ... + def __hash__(self) -> int: ... class GraphicsStyle: - INHERIT: ClassVar[EllipsisType] - MERGE_PROPERTIES: ClassVar[tuple[str, ...]] - TRANSPARENCY_KEYS: ClassVar[tuple[Name, ...]] - PDF_STYLE_KEYS: ClassVar[tuple[Name, ...]] + ca: Incomplete + BM: Incomplete + CA: Incomplete + SA: Incomplete + LW: Incomplete + LC: Incomplete + LJ: Incomplete + ML: Incomplete + SMask: Incomplete + + INHERIT: Final[object] # singleton value to indicate inheritance + MERGE_PROPERTIES: Final[tuple[str, ...]] + TRANSPARENCY_KEYS: Final[tuple[Name, ...]] + PDF_STYLE_KEYS: Final[tuple[Name, ...]] + @classmethod - def merge(cls, parent, child) -> Self: ... + def merge(cls, parent: GraphicsStyle, child: GraphicsStyle) -> Self: ... def __init__(self) -> None: ... - def __deepcopy__(self, memo) -> Self: ... + def __deepcopy__(self, memo: Unused) -> Self: ... @property def allow_transparency(self): ... @allow_transparency.setter @@ -235,113 +171,226 @@ class GraphicsStyle: def stroke_dash_phase(self): ... @stroke_dash_phase.setter def stroke_dash_phase(self, value: Number | EllipsisType): ... + @property + def soft_mask(self): ... + @soft_mask.setter + def soft_mask(self, value) -> None: ... def serialize(self) -> Raw | None: ... def resolve_paint_rule(self) -> PathPaintRule: ... +@runtime_checkable +class Renderable(Protocol): + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + class Move(NamedTuple): pt: Point @property def end_point(self) -> Point: ... + def bounding_box(self, start: Unused) -> tuple[BoundingBox, Point]: ... def render( - self, gsd_registry: GraphicsStateDictRegistry, style: GraphicsStyle, last_item: _SupportsEndPoint, initial_point: Point - ) -> tuple[str, Self, Point]: ... + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... def render_debug( self, - gsd_registry: GraphicsStateDictRegistry, + resource_registry: ResourceCatalog, style: GraphicsStyle, - last_item: _SupportsEndPoint, + last_item: Renderable, initial_point: Point, debug_stream: SupportsWrite[str], pfx: str, - ) -> tuple[str, Self, Point]: ... + ) -> tuple[str, Renderable, Point]: ... class RelativeMove(NamedTuple): pt: Point + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... def render( - self, gsd_registry: GraphicsStateDictRegistry, style: GraphicsStyle, last_item: _SupportsEndPoint, initial_point: Point - ) -> tuple[str, Move, Point]: ... + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... def render_debug( self, - gsd_registry: GraphicsStateDictRegistry, + resource_registry: ResourceCatalog, style: GraphicsStyle, - last_item: _SupportsEndPoint, + last_item: Renderable, initial_point: Point, debug_stream: SupportsWrite[str], pfx: str, - ) -> tuple[str, Move, Point]: ... + ) -> tuple[str, Renderable, Point]: ... class Line(NamedTuple): pt: Point @property def end_point(self) -> Point: ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... def render( - self, gsd_registry: GraphicsStateDictRegistry, style: GraphicsStyle, last_item: _SupportsEndPoint, initial_point: Point - ) -> tuple[str, Self, Point]: ... + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... def render_debug( self, - gsd_registry: GraphicsStateDictRegistry, + resource_registry: ResourceCatalog, style: GraphicsStyle, - last_item: _SupportsEndPoint, + last_item: Renderable, initial_point: Point, debug_stream: SupportsWrite[str], pfx: str, - ) -> tuple[str, Self, Point]: ... + ) -> tuple[str, Renderable, Point]: ... class RelativeLine(NamedTuple): pt: Point - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class HorizontalLine(NamedTuple): x: Number - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class RelativeHorizontalLine(NamedTuple): x: Number - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class VerticalLine(NamedTuple): y: Number - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class RelativeVerticalLine(NamedTuple): y: Number - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class BezierCurve(NamedTuple): c1: Point c2: Point end: Point @property - def end_point(self) -> Point: ... - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def end_point(self): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class RelativeBezierCurve(NamedTuple): c1: Point c2: Point end: Point - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class QuadraticBezierCurve(NamedTuple): ctrl: Point end: Point @property def end_point(self) -> Point: ... - def to_cubic_curve(self, start_point): ... - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def to_cubic_curve(self, start_point: Point) -> BezierCurve: ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class RelativeQuadraticBezierCurve(NamedTuple): ctrl: Point end: Point - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class Arc(NamedTuple): radii: Point @@ -350,9 +399,20 @@ class Arc(NamedTuple): sweep: bool end: Point @staticmethod - def subdivde_sweep(sweep_angle: Number) -> Generator[tuple[Point, Point, Point]]: ... - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def subdivide_sweep(sweep_angle: float) -> Generator[tuple[Point, Point, Point]]: ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class RelativeArc(NamedTuple): radii: Point @@ -360,65 +420,173 @@ class RelativeArc(NamedTuple): large: bool sweep: bool end: Point - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class Rectangle(NamedTuple): org: Point size: Point - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point | None = None) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class RoundedRectangle(NamedTuple): org: Point size: Point corner_radii: Point - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class Ellipse(NamedTuple): radii: Point center: Point - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... + +class TextRun(NamedTuple): + text: str + family: str + emphasis: str + size: float + dx: float = 0.0 + dy: float = 0.0 + abs_x: float | None = None + abs_y: float | None = None + transform: Transform | None = None + run_style: GraphicsStyle | None = None + +class Text(NamedTuple): + x: float + y: float + text_runs: tuple[TextRun, ...] + text_anchor: Literal["start", "middle", "end"] = "start" + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class ImplicitClose(NamedTuple): - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class Close(NamedTuple): - def render(self, gsd_registry, style, last_item, initial_point): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point) -> tuple[BoundingBox, Point]: ... + def render( + self, resource_registry: ResourceCatalog, style: GraphicsStyle, last_item: Renderable, initial_point: Point + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class DrawingContext: def __init__(self) -> None: ... - def add_item(self, item, _copy: bool = True) -> None: ... - def render(self, gsd_registry, first_point, scale, height, starting_style): ... - def render_debug(self, gsd_registry, first_point, scale, height, starting_style, debug_stream): ... + def add_item(self, item: GraphicsContext | PaintedPath | PaintComposite, _copy: bool = True) -> None: ... + def render( + self, resource_registry: ResourceCatalog, first_point: Point, scale: float, height: float, starting_style: GraphicsStyle + ) -> None: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + first_point: Point, + scale: float, + height: float, + starting_style: GraphicsStyle, + debug_stream: SupportsWrite[str], + ) -> None: ... class PaintedPath: - def __init__(self, x: Number = 0, y: Number = 0) -> None: ... + def __init__(self, x: float = 0, y: float = 0) -> None: ... def __deepcopy__(self, memo) -> Self: ... @property def style(self) -> GraphicsStyle: ... @property - def transform(self): ... + def transform(self) -> Transform | None: ... @transform.setter - def transform(self, tf) -> None: ... + def transform(self, tf: Transform) -> None: ... @property - def auto_close(self): ... + def auto_close(self) -> bool: ... @auto_close.setter - def auto_close(self, should) -> None: ... + def auto_close(self, should: bool) -> None: ... @property - def paint_rule(self): ... + def paint_rule(self) -> PathPaintRule: ... @paint_rule.setter - def paint_rule(self, style) -> None: ... + def paint_rule(self, style: PathPaintRule) -> None: ... @property def clipping_path(self): ... @clipping_path.setter def clipping_path(self, new_clipath) -> None: ... + def get_graphics_context(self) -> GraphicsContext: ... @contextmanager - def transform_group(self, transform) -> Generator[Self]: ... + def transform_group(self, transform: Transform) -> Generator[Self]: ... def add_path_element(self, item, _copy: bool = True) -> None: ... def remove_last_path_element(self) -> None: ... def rectangle(self, x: Number, y: Number, w: Number, h: Number, rx: Number = 0, ry: Number = 0) -> Self: ... @@ -437,43 +605,216 @@ class PaintedPath: def quadratic_curve_to(self, x1: Number, y1: Number, x2: Number, y2: Number) -> Self: ... def quadratic_curve_relative(self, dx1: Number, dy1: Number, dx2: Number, dy2: Number) -> Self: ... def arc_to( - self, rx: Number, ry: Number, rotation: Number, large_arc: bool, positive_sweep: bool, x: Number, y: Number + self, rx: Number, ry: Number, rotation: Number, large_arc: Number, positive_sweep: Number, x: Number, y: Number ) -> Self: ... def arc_relative( - self, rx: Number, ry: Number, rotation: Number, large_arc: bool, positive_sweep: bool, dx: Number, dy: Number + self, rx: Number, ry: Number, rotation: Number, large_arc: Number, positive_sweep: Number, dx: Number, dy: Number + ) -> Self: ... + def text( + self, + x: float, + y: float, + content: str, + *, + font_family: str = "helvetica", + font_style: Literal["", "B", "I", "BI"] = "", + font_size: float = 12.0, + text_anchor: Literal["start", "middle", "end"] = "start", ) -> Self: ... def close(self) -> None: ... - def render(self, gsd_registry, style, last_item, initial_point, debug_stream=None, pfx=None): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def bounding_box(self, start: Point, expand_for_stroke: bool = True) -> tuple[BoundingBox, Point]: ... + def render( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str] | None = None, + pfx: str | None = None, + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class ClippingPath(PaintedPath): paint_rule: PathPaintRule - def __init__(self, x: Number = 0, y: Number = 0) -> None: ... - def render(self, gsd_registry, style, last_item, initial_point, debug_stream=None, pfx=None): ... - def render_debug(self, gsd_registry, style, last_item, initial_point, debug_stream, pfx): ... + def __init__(self, x: float = 0, y: float = 0) -> None: ... + def render( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str] | None = None, + pfx: str | None = None, + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... class GraphicsContext: style: GraphicsStyle - path_items: list[Incomplete] + path_items: list[Renderable] + def __init__(self) -> None: ... def __deepcopy__(self, memo) -> Self: ... @property def transform(self) -> Transform | None: ... @transform.setter - def transform(self, tf) -> None: ... + def transform(self, tf: Transform) -> None: ... @property def clipping_path(self) -> ClippingPath | None: ... @clipping_path.setter - def clipping_path(self, new_clipath) -> None: ... - def add_item(self, item, _copy: bool = True) -> None: ... + def clipping_path(self, new_clipath: ClippingPath) -> None: ... + def add_item(self, item: Renderable, _copy: bool = True) -> None: ... def remove_last_item(self) -> None: ... - def merge(self, other_context) -> None: ... + def merge(self, other_context: GraphicsContext) -> None: ... def build_render_list( - self, gsd_registry, style, last_item, initial_point, debug_stream=None, pfx=None, _push_stack: bool = True - ): ... + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str] | None = None, + pfx: str | None = None, + _push_stack: bool = True, + ) -> tuple[list[str], Renderable, Point]: ... + def bounding_box( + self, start: Point, style: GraphicsStyle | None = None, expand_for_stroke: bool = True, transformed: bool = True + ) -> tuple[BoundingBox, Point]: ... + def render( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str] | None = None, + pfx: str | None = None, + _push_stack: bool = True, + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + _push_stack: bool = True, + ) -> tuple[str, Renderable, Point]: ... + +class PaintSoftMask: + __slots__ = ("mask_path", "invert", "resources", "use_luminosity", "object_id", "matrix") + + mask_path: PaintedPath | GraphicsContext + invert: bool + use_luminosity: bool + resources: set[Incomplete] + object_id: int + matrix: Transform + + def __init__( + self, + mask_path: PaintedPath | GraphicsContext, + invert: bool = False, + use_luminosity: bool = False, + matrix: Transform = ..., + ) -> None: ... + def serialize(self) -> str: ... + def get_bounding_box(self) -> tuple[float, float, float, float]: ... + def get_resource_dictionary(self, gfxstate_objs_per_name, pattern_objs_per_name) -> str: ... + def render(self, resource_registry: ResourceCatalog) -> str: ... + @staticmethod + def coverage_white(node: PaintedPath | GraphicsContext) -> PaintedPath | GraphicsContext: ... + @staticmethod + def alpha_layers_from(node: PaintedPath | GraphicsContext) -> GraphicsContext | None: ... + @classmethod + def from_AB( + cls, + A: GraphicsContext | None, + B: PaintedPath | GraphicsContext, + invert: bool, + registry, + region_bbox: BoundingBox | None = None, + ) -> Self: ... + +def clone_structure(node): ... + +class PaintComposite: + backdrop: PaintedPath | GraphicsContext + source: PaintedPath | GraphicsContext + mode: CompositingOperation + + def __init__( + self, backdrop: PaintedPath | GraphicsContext, source: PaintedPath | GraphicsContext, operation: CompositingOperation + ) -> None: ... def render( - self, gsd_registry, style: DrawingContext, last_item, initial_point, debug_stream=None, pfx=None, _push_stack: bool = True - ): ... + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str] | None = None, + pfx: str | None = None, + ) -> tuple[str, Renderable, Point]: ... def render_debug( - self, gsd_registry, style: DrawingContext, last_item, initial_point, debug_stream, pfx, _push_stack: bool = True - ): ... + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... + +class PaintBlendComposite: + __slots__ = ("backdrop", "source", "blend_mode", "_form_index") + + backdrop: GraphicsContext | PaintedPath + source: GraphicsContext | PaintedPath + blend_mode: BlendMode + _form_index: int | None + + def __init__( + self, backdrop: GraphicsContext | PaintedPath, source: GraphicsContext | PaintedPath, blend_mode: BlendMode + ) -> None: ... + def render( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str] | None = None, + pfx: str | None = None, + ) -> tuple[str, Renderable, Point]: ... + def render_debug( + self, + resource_registry: ResourceCatalog, + style: GraphicsStyle, + last_item: Renderable, + initial_point: Point, + debug_stream: SupportsWrite[str], + pfx: str, + ) -> tuple[str, Renderable, Point]: ... + +class PathPen(BasePen): + pdf_path: PaintedPath + last_was_line_to: bool + first_is_move: bool | None + + def __init__(self, pdf_path: PaintedPath, glyphSet: _TTGlyphSet | None = ...) -> None: ... + def arcTo(self, rx, ry, rotation, arc, sweep, end) -> None: ... + +class GlyphPathPen(PathPen): ... diff --git a/stubs/fpdf2/fpdf/drawing_primitives.pyi b/stubs/fpdf2/fpdf/drawing_primitives.pyi new file mode 100644 index 000000000000..bfc5f2475bef --- /dev/null +++ b/stubs/fpdf2/fpdf/drawing_primitives.pyi @@ -0,0 +1,141 @@ +import decimal +from collections.abc import Callable, Sequence +from typing import Any, ClassVar, Literal, NamedTuple, TypeVar, overload, type_check_only +from typing_extensions import Self, TypeAlias + +from .drawing import Renderable + +_CallableT = TypeVar("_CallableT", bound=Callable[..., Any]) + +__pdoc__: dict[str, bool] + +def force_nodocument(item: _CallableT) -> _CallableT: ... +def force_document(item: _CallableT) -> _CallableT: ... + +Number: TypeAlias = int | float | decimal.Decimal +NumberClass: tuple[type, ...] + +def check_range(value: float, minimum: float = 0.0, maximum: float = 1.0) -> float: ... +def number_to_str(number: Number) -> str: ... +@type_check_only +class _DeviceRGBBase(NamedTuple): + r: Number + g: Number + b: Number + a: Number | None + +class DeviceRGB(_DeviceRGBBase): + OPERATOR: ClassVar[str] + def __new__(cls, r: Number, g: Number, b: Number, a: Number | None = None) -> Self: ... + @property + def colors(self) -> tuple[Number, Number, Number]: ... + @property + def colors255(self) -> tuple[Number, Number, Number]: ... + def serialize(self) -> str: ... + def is_achromatic(self) -> bool: ... + def to_gray(self) -> DeviceGray: ... + +@type_check_only +class _DeviceGrayBase(NamedTuple): + g: Number + a: Number | None + +class DeviceGray(_DeviceGrayBase): + OPERATOR: ClassVar[str] + def __new__(cls, g: Number, a: Number | None = None) -> Self: ... + @property + def colors(self) -> tuple[Number, Number, Number]: ... + @property + def colors255(self) -> tuple[Number, Number, Number]: ... + def serialize(self) -> str: ... + +@type_check_only +class _DeviceCMYKBase(NamedTuple): + c: Number + m: Number + y: Number + k: Number + a: Number | None + +class DeviceCMYK(_DeviceCMYKBase): + OPERATOR: ClassVar[str] + def __new__(cls, c: Number, m: Number, y: Number, k: Number, a: Number | None = None) -> Self: ... + @property + def colors(self) -> tuple[Number, Number, Number, Number]: ... + def serialize(self) -> str: ... + +def rgb8(r: Number, g: Number, b: Number, a: Number | None = None) -> DeviceRGB: ... +def gray8(g: Number, a: Number | None = None) -> DeviceGray: ... +@overload +def convert_to_device_color(r: DeviceCMYK) -> DeviceCMYK: ... # type: ignore[overload-overlap] +@overload +def convert_to_device_color(r: DeviceGray) -> DeviceGray: ... +@overload +def convert_to_device_color(r: DeviceRGB) -> DeviceRGB: ... +@overload +def convert_to_device_color(r: str) -> DeviceRGB: ... +@overload +def convert_to_device_color(r: Sequence[Number]) -> DeviceGray | DeviceRGB: ... +@overload +def convert_to_device_color(r: int, g: Literal[-1] = -1, b: Literal[-1] = -1) -> DeviceGray: ... +@overload +def convert_to_device_color(r: int, g: int, b: int) -> DeviceGray | DeviceRGB: ... +def cmyk8(c: Number, m: Number, y: Number, k: Number, a: Number | None = None) -> DeviceCMYK: ... +def color_from_hex_string(hexstr: str) -> DeviceRGB: ... +def color_from_rgb_string(rgbstr: str) -> DeviceRGB: ... + +class Point(NamedTuple): + x: float + y: float + def render(self) -> str: ... + def dot(self, other: Point) -> float: ... + def angle(self, other: Point) -> float: ... + def mag(self) -> float: ... + def __add__(self, other: Point) -> Point: ... # type: ignore[override] + def __sub__(self, other: Point) -> Point: ... + def __neg__(self) -> Point: ... + def __mul__(self, other: Point) -> Point: ... # type: ignore[override] + # Alias to __rmul__ at runtime, but that confuses type checkers and stubtest + def __rmul__(self, other: Point) -> Point: ... # type: ignore[override] + def __truediv__(self, other: Number) -> Point: ... + def __floordiv__(self, other: Number) -> Point: ... + def __matmul__(self, other: Transform) -> Point: ... + +class Transform(NamedTuple): + a: float + b: float + c: float + d: float + e: float + f: float + @classmethod + def identity(cls) -> Self: ... + @classmethod + def translation(cls, x: Number, y: Number) -> Self: ... + @classmethod + def scaling(cls, x: Number, y: Number | None = None) -> Self: ... + @classmethod + def rotation(cls, theta: Number) -> Self: ... + @classmethod + def rotation_d(cls, theta_d: Number) -> Self: ... + @classmethod + def shearing(cls, x: Number, y: Number | None = None) -> Self: ... + @classmethod + def skewing(cls, ax: Number = 0, ay: Number | None = None) -> Self: ... + @classmethod + def skewing_d(cls, ax_d: Number = 0, ay_d: Number | None = None) -> Self: ... + def translate(self, x: Number, y: Number) -> Self: ... + def scale(self, x: Number, y: Number | None = None) -> Self: ... + def rotate(self, theta: Number) -> Self: ... + def rotate_d(self, theta_d: Number) -> Self: ... + def shear(self, x: Number, y: Number | None = None) -> Self: ... + def skew(self, ax: Number = 0, ay: Number | None = None) -> Self: ... + def skew_d(self, ax_d: Number = 0, ay_d: Number | None = None) -> Self: ... + def about(self, x: Number, y: Number) -> Self: ... + def inverse(self) -> Transform: ... + def __mul__(self, other: Number) -> Transform: ... # type: ignore[override] + # Alias to __mul__ at runtime, but that confuses type checkers and stubtest + def __rmul__(self, other: Number) -> Transform: ... # type: ignore[override] + def __matmul__(self, other: Transform) -> Self: ... + def render(self, last_item: Renderable) -> tuple[str, Renderable]: ... + def row_norms(self) -> tuple[float, float]: ... diff --git a/stubs/fpdf2/fpdf/enums.pyi b/stubs/fpdf2/fpdf/enums.pyi index 5c11f04e31ce..554daa282e86 100644 --- a/stubs/fpdf2/fpdf/enums.pyi +++ b/stubs/fpdf2/fpdf/enums.pyi @@ -277,6 +277,20 @@ class BlendMode(CoerciveEnum): COLOR = Name("Color") LUMINOSITY = Name("Luminosity") +class CompositingOperation(CoerciveEnum): + CLEAR = Name("Clear") + SOURCE = Name("Source") + DESTINATION = Name("Destination") + SOURCE_OVER = Name("SourceOver") + DESTINATION_OVER = Name("DestinationOver") + SOURCE_IN = Name("SourceIn") + DESTINATION_IN = Name("DestinationIn") + SOURCE_OUT = Name("SourceOut") + DESTINATION_OUT = Name("DestinationOut") + SOURCE_ATOP = Name("SourceAtop") + DESTINATION_ATOP = Name("DestinationAtop") + XOR = Name("XOR") + class AnnotationFlag(CoerciveIntEnum): INVISIBLE = 1 HIDDEN = 2 @@ -339,6 +353,7 @@ class PDFStyleKeys(Enum): STROKE_JOIN_STYLE = Name("LJ") STROKE_MITER_LIMIT = Name("ML") STROKE_DASH_PATTERN = Name("D") + SOFT_MASK = Name("SMask") class Corner(CoerciveEnum): TOP_RIGHT = "TOP_RIGHT" @@ -414,8 +429,48 @@ class PDFResourceType(Enum): EXT_G_STATE = "ExtGState" COLOR_SPACE = "ColorSpace" PATTERN = "Pattern" - SHADDING = "Shading" + SHADING = "Shading" X_OBJECT = "XObject" FONT = "Font" PROC_SET = "ProcSet" PROPERTIES = "Properties" + +class GradientUnits(CoerciveEnum): + OBJECT_BOUNDING_BOX = "objectBoundingBox" + USER_SPACE_ON_USE = "userSpaceOnUse" + +class GradientSpreadMethod(CoerciveEnum): + PAD = "pad" + REFLECT = "reflect" + REPEAT = "repeat" + +class DocumentCompliance(Enum): + PDFA_1B = ("PDFA", 1, "B") + PDFA_2B = ("PDFA", 2, "B") + PDFA_2U = ("PDFA", 2, "U") + PDFA_3B = ("PDFA", 3, "B") + PDFA_3U = ("PDFA", 3, "U") + PDFA_4 = ("PDFA", 4, None) + PDFA_4E = ("PDFA", 4, "E") + PDFA_4F = ("PDFA", 4, "F") + + @property + def profile(self) -> str: ... + @property + def part(self) -> int: ... + @property + def conformance(self) -> str | None: ... + @property + def label(self) -> str: ... + @classmethod + def coerce(cls, value: DocumentCompliance | str) -> DocumentCompliance: ... + +class AssociatedFileRelationship(CoerciveEnum): + SOURCE = "Source" + DATA = "Data" + ALTERNATIVE = "Alternative" + SUPPLEMENT = "Supplement" + ENCRYPTED_PAYLOAD = "EncryptedPayload" + FORM_DATA = "FormData" + SCHEMA = "Schema" + UNSPECIFIED = "Unspecified" diff --git a/stubs/fpdf2/fpdf/errors.pyi b/stubs/fpdf2/fpdf/errors.pyi index a2aff7bb139b..8c2504e69b54 100644 --- a/stubs/fpdf2/fpdf/errors.pyi +++ b/stubs/fpdf2/fpdf/errors.pyi @@ -10,3 +10,6 @@ class FPDFPageFormatException(FPDFException): class FPDFUnicodeEncodingException(FPDFException): def __init__(self, text_index, character, font_name) -> None: ... + +class ComplianceError(FPDFException): ... +class PDFAComplianceError(ComplianceError): ... diff --git a/stubs/fpdf2/fpdf/font_type_3.pyi b/stubs/fpdf2/fpdf/font_type_3.pyi new file mode 100644 index 000000000000..45177b570b3f --- /dev/null +++ b/stubs/fpdf2/fpdf/font_type_3.pyi @@ -0,0 +1,92 @@ +from _typeshed import Incomplete +from collections.abc import Iterable +from logging import Logger +from typing import Final, Literal + +from fpdf.enums import BlendMode + +from ._fonttools_shims import CompositeMode, Paint, PaintFormat, VarStoreInstancer +from .drawing import BoundingBox, DeviceRGB, GraphicsContext, PaintedPath, Transform +from .fonts import TTFFont +from .fpdf import FPDF + +LOGGER: Logger + +PAINT_VAR_MAPPING: Final[dict[PaintFormat, PaintFormat]] + +class Type3FontGlyph: + __slots__ = ("obj_id", "glyph_id", "unicode", "glyph_name", "glyph_width", "glyph", "_glyph_bounds") + + obj_id: int + glyph_id: int + unicode: tuple[Incomplete, ...] + glyph_name: str + glyph_width: int + glyph: str + _glyph_bounds: tuple[int, int, int, int] + + def __init__(self) -> None: ... + def __hash__(self) -> int: ... + +class Type3Font: + i: int + type: str + fpdf: FPDF + base_font: TTFFont + upem: float + scale: float + images_used: set[Incomplete] + graphics_style_used: set[Incomplete] + patterns_used: set[Incomplete] + glyphs: list[Type3FontGlyph] + + def __init__(self, fpdf: FPDF, base_font: TTFFont) -> None: ... + def get_notdef_glyph(self, glyph_id: int) -> Type3FontGlyph: ... + def get_space_glyph(self, glyph_id: int) -> Type3FontGlyph: ... + def load_glyphs(self) -> None: ... + def add_glyph(self, glyph_name: str, char_id: int) -> None: ... + @classmethod + def get_target_ppem(cls, font_size_pt: int) -> int: ... + def load_glyph_image(self, glyph: Type3FontGlyph) -> None: ... + def glyph_exists(self, glyph_name: str) -> bool: ... + +class SVGColorFont(Type3Font): ... + +class COLRFont(Type3Font): + colrv0_glyphs: list[Incomplete] + colrv1_glyphs: list[Incomplete] + version: Incomplete + colrv1_clip_boxes: dict[Incomplete, Incomplete] + colr_var_instancer: Incomplete | None + colr_var_index_map: Incomplete | None + palette: Incomplete | None + + def __init__(self, fpdf: FPDF, base_font: TTFFont, palette_index: int = 0) -> None: ... + def metric_bbox(self) -> BoundingBox: ... + def get_color(self, color_index: int, alpha=1) -> DeviceRGB: ... + def draw_glyph_colrv0(self, layers: Iterable[Incomplete]) -> GraphicsContext: ... + def draw_glyph_colrv1(self, glyph_name: str) -> GraphicsContext: ... + def draw_colrv1_paint( + self, + paint: Paint, + parent: GraphicsContext, + target_path: PaintedPath | None = None, + ctm: Transform | None = None, + visited_glyphs: set[Incomplete] | None = None, + ) -> tuple[GraphicsContext, PaintedPath | None]: ... + def get_paint_surface(self) -> PaintedPath: ... + @classmethod + def get_composite_mode( + cls, composite_mode: CompositeMode + ) -> tuple[Literal["Compositing"], CompositeMode] | tuple[Literal["Blend"], BlendMode]: ... + +class VarTableWrapper: + def __init__(self, wrapped, instancer: VarStoreInstancer, var_index_map=None, format_override: int | None = None) -> None: ... + def __getattr__(self, attr_name: str): ... + +class CBDTColorFont(Type3Font): ... + +class SBIXColorFont(Type3Font): + def get_strike_index(self) -> int: ... + +def get_color_font_object(fpdf: FPDF, base_font: TTFFont, palette_index: int = 0) -> Type3Font | None: ... diff --git a/stubs/fpdf2/fpdf/fonts.pyi b/stubs/fpdf2/fpdf/fonts.pyi index 72d97ebd1e73..1478fa1efc51 100644 --- a/stubs/fpdf2/fpdf/fonts.pyi +++ b/stubs/fpdf2/fpdf/fonts.pyi @@ -10,6 +10,7 @@ from typing_extensions import Self, deprecated from ._fonttools_shims import _TTFont from .drawing import DeviceGray, DeviceRGB, Number from .enums import Align, TextEmphasis +from .fpdf import FPDF from .syntax import PDFObject LOGGER: Logger @@ -91,7 +92,7 @@ class CoreFont: cw: int fontkey: str emphasis: TextEmphasis - def __init__(self, fpdf, fontkey: str, style: int) -> None: ... + def __init__(self, i: int, fontkey: str, style: int) -> None: ... def get_text_width(self, text: str, font_size_pt: int, _: Unused) -> float: ... def encode_text(self, text: str) -> str: ... @@ -102,7 +103,7 @@ class TTFFont: "name", "desc", "glyph_ids", - "hbfont", + "_hbfont", "sp", "ss", "up", @@ -116,11 +117,17 @@ class TTFFont: "cmap", "ttfont", "missing_glyphs", + "biggest_size_pt", + "color_font", + "unicode_range", + "palette_index", ) i: int type: str ttffile: Incomplete + _hbfont: HarfBuzzFont | None fontkey: str + biggest_size_pt: float ttfont: _TTFont scale: float desc: PDFFontDescriptor @@ -135,10 +142,18 @@ class TTFFont: ss: int emphasis: TextEmphasis subset: SubsetMap - hbfont: HarfBuzzFont | None # Not always defined. - def __init__(self, fpdf, font_file_path, fontkey: str, style: int) -> None: ... + palette_index: int + color_font: Incomplete | None + unicode_range: Incomplete # not set, but part of __slots__ + + def __init__( + self, fpdf: FPDF, font_file_path, fontkey: str, style: int, unicode_range=None, axes_dict=None, palette_index=None + ) -> None: ... + @property + def hbfont(self) -> HarfBuzzFont: ... def __deepcopy__(self, memo) -> Self: ... def close(self) -> None: ... + def escape_text(self, text: str) -> str: ... def get_text_width(self, text: str, font_size_pt: int, text_shaping_params): ... def shaped_text_width(self, text: str, font_size_pt: int, text_shaping_params): ... def perform_harfbuzz_shaping(self, text: str, font_size_pt: int, text_shaping_params): ... diff --git a/stubs/fpdf2/fpdf/fpdf.pyi b/stubs/fpdf2/fpdf/fpdf.pyi index 820ba83c6b55..2114cda69093 100644 --- a/stubs/fpdf2/fpdf/fpdf.pyi +++ b/stubs/fpdf2/fpdf/fpdf.pyi @@ -1,7 +1,7 @@ import datetime from _typeshed import Incomplete, StrPath, Unused from collections.abc import Callable, Generator, Iterable, Sequence -from contextlib import _GeneratorContextManager +from contextlib import _GeneratorContextManager, contextmanager from io import BytesIO from pathlib import PurePath from re import Pattern @@ -20,6 +20,7 @@ from .enums import ( AnnotationFlag, AnnotationName, Corner, + DocumentCompliance, EncryptionMethod, FileAttachmentAnnotationName, MethodReturnValue, @@ -57,7 +58,7 @@ from .image_datastructures import ( from .output import OutputProducer, PDFICCProfile, PDFPage from .recorder import FPDFRecorder from .structure_tree import StructureTreeBuilder -from .syntax import DestinationXYZ +from .syntax import Destination, DestinationXYZ from .table import Table from .transitions import Transition from .util import Padding, _Unit @@ -109,6 +110,7 @@ _FontStyles: TypeAlias = Literal[ ] FPDF_VERSION: Final[str] +__version__: Final[str] PAGE_FORMATS: Final[dict[_Format, tuple[float, float]]] class ToCPlaceholder(NamedTuple): @@ -135,9 +137,9 @@ class FPDF(GraphicsStateMixin): page: int pages: dict[int, PDFPage] - fonts: dict[str, CoreFont | TTFFont] fonts_used_per_page_number: dict[int, set[int]] links: dict[int, DestinationXYZ] + named_destinations: dict[str, Destination] embedded_files: list[PDFEmbeddedFile] image_cache: ImageCache images_used_per_page_number: dict[int, set[int]] @@ -176,6 +178,7 @@ class FPDF(GraphicsStateMixin): compress: bool pdf_version: str creation_date: datetime.datetime + render_color_fonts: bool buffer: bytearray | None @@ -188,11 +191,15 @@ class FPDF(GraphicsStateMixin): def __init__( self, - orientation: _Orientation = "portrait", + orientation: PageOrientation | _Orientation | None = PageOrientation.PORTRAIT, unit: _Unit | float = "mm", format: _Format | tuple[float, float] = "A4", font_cache_dir: Literal["DEPRECATED"] = "DEPRECATED", + *, + enforce_compliance: DocumentCompliance | str | None = None, ) -> None: ... + @property + def fonts(self) -> dict[str, CoreFont | TTFFont]: ... def set_encryption( self, owner_password: str, @@ -304,6 +311,9 @@ class FPDF(GraphicsStateMixin): ) -> _GeneratorContextManager[PaintedPath]: ... def draw_path(self, path: PaintedPath, debug_stream=None) -> None: ... def set_dash_pattern(self, dash: float = 0, gap: float = 0, phase: float = 0) -> None: ... + @contextmanager + def glyph_drawing_context(self) -> Generator[DrawingContext]: ... + def draw_vector_glyph(self, path: PaintedPath, font) -> str | None: ... def line(self, x1: float, y1: float, x2: float, y2: float) -> None: ... def polyline( self, @@ -380,20 +390,61 @@ class FPDF(GraphicsStateMixin): style: RenderStyle | Literal["D", "F", "DF", "FD"] | None = None, ) -> None: ... def use_pattern(self, shading) -> _GeneratorContextManager[None]: ... + @overload + @deprecated("The 'uni' parameter is deprecated since 2.5.1") + def add_font( + self, + family: str | None = None, + style: _FontStyle = "", + fname: str | PurePath | None = None, + *, + uni: bool, + unicode_range: str | int | tuple[Incomplete, ...] | list[Incomplete] | None = None, + variations: dict[Incomplete, Incomplete] | None = None, + palette: int | None = None, + ) -> None: ... + @overload + @deprecated("The 'uni' parameter is deprecated since 2.5.1") + def add_font( + self, + family: str | None, + style: _FontStyle, + fname: str | PurePath | None, + uni: bool, + *, + unicode_range: str | int | tuple[Incomplete, ...] | list[Incomplete] | None = None, + variations: dict[Incomplete, Incomplete] | None = None, + palette: int | None = None, + ) -> None: ... + @overload def add_font( self, family: str | None = None, style: _FontStyle = "", fname: str | PurePath | None = None, - uni: bool | Literal["DEPRECATED"] = "DEPRECATED", + *, + unicode_range: str | int | tuple[Incomplete, ...] | list[Incomplete] | None = None, + variations: dict[Incomplete, Incomplete] | None = None, + palette: int | None = None, ) -> None: ... def set_font(self, family: str | None = None, style: _FontStyles | TextEmphasis = "", size: int = 0) -> None: ... def set_font_size(self, size: float) -> None: ... def set_char_spacing(self, spacing: float) -> None: ... def set_stretching(self, stretching: float) -> None: ... def set_fallback_fonts(self, fallback_fonts: Iterable[str], exact_match: bool = True) -> None: ... - def add_link(self, y: float = 0, x: float = 0, page: int = -1, zoom: float | Literal["null"] = "null") -> int: ... - def set_link(self, link, y: float = 0, x: float = 0, page: int = -1, zoom: float | Literal["null"] = "null") -> None: ... + def add_link( + self, y: float = 0, x: float = 0, page: int = -1, zoom: float | Literal["null"] = "null", name: str | None = None + ) -> int: ... + def get_named_destination(self, name: str) -> str: ... + def set_link( + self, + link: int | None = None, + y: float = 0, + x: float = 0, + page: int = -1, + zoom: float | Literal["null"] = "null", + name: str | None = None, + ) -> None: ... def link( self, x: float, @@ -412,6 +463,8 @@ class FPDF(GraphicsStateMixin): bytes: bytes | None = None, basename: str | None = None, modification_date: datetime.datetime | None = None, + mime_type: str | None = None, + associated_file_relationship: str | None = None, *, creation_date: datetime.datetime | None = ..., desc: str = ..., @@ -623,6 +676,7 @@ class FPDF(GraphicsStateMixin): def preload_image( self, name: str | Image.Image | BytesIO, dims: tuple[float, float] | None = None ) -> tuple[str, Any, ImageInfo]: ... + def preload_glyph_image(self, glyph_image_bytes) -> tuple[str, BytesIO | Image.Image | None, ImageInfo]: ... def ln(self, h: float | None = None) -> None: ... def get_x(self) -> float: ... def set_x(self, x: float) -> None: ... @@ -707,14 +761,29 @@ class FPDF(GraphicsStateMixin): repeat_headings: TableHeadingsDisplay | int = 1, ) -> _GeneratorContextManager[Table]: ... @overload + @deprecated("The 'dest' parameter is deprecated since 2.2.0") def output( # type: ignore[overload-overlap] self, name: Literal[""] | None = "", - dest: Unused = "", + *, + dest: Unused, linearize: bool = False, output_producer_class: Callable[[FPDF], OutputProducer] = ..., ) -> bytearray: ... @overload + def output( # type: ignore[overload-overlap] + self, + name: Literal[""] | None = "", + *, + linearize: bool = False, + output_producer_class: Callable[[FPDF], OutputProducer] = ..., + ) -> bytearray: ... + @overload + @deprecated("The 'dest' parameter is deprecated since 2.2.0") + def output( + self, name: str, *, dest: Unused, linearize: bool = False, output_producer_class: Callable[[FPDF], OutputProducer] = ... + ) -> None: ... + @overload def output( - self, name: str, dest: Unused = "", linearize: bool = False, output_producer_class: Callable[[FPDF], OutputProducer] = ... + self, name: str, *, linearize: bool = False, output_producer_class: Callable[[FPDF], OutputProducer] = ... ) -> None: ... diff --git a/stubs/fpdf2/fpdf/output.pyi b/stubs/fpdf2/fpdf/output.pyi index 86c1b56c855e..3f134440b651 100644 --- a/stubs/fpdf2/fpdf/output.pyi +++ b/stubs/fpdf2/fpdf/output.pyi @@ -1,11 +1,15 @@ from _typeshed import Incomplete, Unused -from collections import defaultdict +from collections import OrderedDict, defaultdict from logging import Logger +from re import Pattern from typing import Final from .annotations import AnnotationDict +from .drawing import PaintSoftMask from .encryption import StandardSecurityHandler from .enums import OutputIntentSubType, PageLabelStyle, PDFResourceType +from .font_type_3 import Type3Font +from .fonts import CoreFont, TTFFont from .fpdf import FPDF from .image_datastructures import RasterImageInfo from .line_break import TotalPagesSubstitutionFragment @@ -41,6 +45,27 @@ class CIDSystemInfo(PDFObject): ordering: PDFString supplement: int +class PDFType3Font(PDFObject): + type: Name + name: Name + subtype: Name + font_b_box: str + font_matrix: str + first_char: int + last_char: int + resources: str | None + to_unicode: Incomplete | None + + def __init__(self, font3: Type3Font) -> None: ... + @property + def char_procs(self) -> str: ... + @property + def encoding(self) -> str: ... + @property + def widths(self) -> str: ... + def generate_resources(self, img_objs_per_index, gfxstate_objs_per_name, pattern_objs_per_name) -> None: ... + def differences_table(self) -> str: ... + class PDFInfo(PDFObject): title: str | None subject: str | None @@ -240,13 +265,31 @@ class OutputIntentDictionary: def serialize(self, _security_handler: StandardSecurityHandler | None = None, _obj_id=None): ... class ResourceCatalog: + GS_REGEX: Final[Pattern[str]] + IMG_REGEX: Final[Pattern[str]] + PATTERN_FILL_REGEX: Final[Pattern[str]] + PATTERN_STROKE_REGEX: Final[Pattern[str]] + FONT_REGEX: Final[Pattern[str]] + resources: defaultdict[PDFResourceType, dict[Incomplete, Incomplete]] resources_per_page: defaultdict[tuple[int, PDFResourceType], set[Incomplete]] + graphics_styles: OrderedDict[Incomplete, Incomplete] + soft_mask_xobjects: list[Incomplete] + form_xobjects: list[Incomplete] + last_reserved_object_id: int + font_registry: dict[str, CoreFont | TTFFont] + next_xobject_index: int - def add(self, resource_type: PDFResourceType, resource, page_number: int) -> Incomplete | None: ... + def add(self, resource_type: PDFResourceType, resource, page_number: int) -> int | None: ... + def register_graphics_style(self, style) -> Name | None: ... + def register_soft_mask(self, soft_mask: PaintSoftMask) -> int: ... + def register_blend_form(self, blend_group) -> int: ... + def scan_stream(self, rendered: str) -> list[tuple[PDFResourceType, str]]: ... + def index_stream_resources(self, rendered: str, page_number: int) -> None: ... def get_items(self, resource_type: PDFResourceType): ... def get_resources_per_page(self, page_number: int, resource_type: PDFResourceType): ... def get_used_resources(self, resource_type: PDFResourceType) -> set[Incomplete]: ... + def get_font_from_family(self, font_family: str, font_style: str = "") -> CoreFont | TTFFont: ... class OutputProducer: fpdf: FPDF @@ -269,3 +312,5 @@ def stream_content_for_raster_image( scale: float = 1, pdf_height_to_flip: float | None = None, ) -> str: ... +def soft_mask_path_to_xobject(path, resource_catalog: ResourceCatalog) -> PDFContentStream: ... +def blend_group_to_xobject(group, resource_catalog: ResourceCatalog) -> PDFContentStream: ... diff --git a/stubs/fpdf2/fpdf/pattern.pyi b/stubs/fpdf2/fpdf/pattern.pyi index 6d0d043764c6..09b65867fa2d 100644 --- a/stubs/fpdf2/fpdf/pattern.pyi +++ b/stubs/fpdf2/fpdf/pattern.pyi @@ -1,18 +1,40 @@ -from _typeshed import Incomplete +from _typeshed import Incomplete, Unused from abc import ABC from collections.abc import Iterable from typing import Final, Literal +from typing_extensions import Never, Self, TypeAlias -from .drawing import DeviceCMYK, DeviceGray, DeviceRGB -from .fpdf import FPDF -from .syntax import Name, PDFObject +from .drawing import BoundingBox, DeviceCMYK, DeviceGray, DeviceRGB, Transform +from .enums import GradientSpreadMethod +from .syntax import Name, PDFArray, PDFContentStream, PDFObject + +Color: TypeAlias = DeviceRGB | DeviceGray | DeviceCMYK + +TOLERANCE: Final = 1e-9 + +def lerp(a: float, b: float, t: float) -> float: ... +def lerp_tuple(a: tuple[float, ...], b: tuple[float, ...], t: float) -> tuple[float, ...]: ... +def pick_colorspace_and_promote(colors: list[Color]) -> tuple[str, list[Color]]: ... +def normalize_stops( + stops: list[tuple[float, Color | str]], coerce_to_device: bool = True, *, return_raw: bool = False +) -> tuple[str, list[tuple[float, Color]]] | tuple[str, list[tuple[float, Color]], list[tuple[float, Color]]]: ... +def merge_near_duplicates(pairs: list[tuple[float, Color | str]]) -> list[tuple[float, Color | str]]: ... +def spread_map(u: float, method: GradientSpreadMethod) -> float: ... +def sample_stops(stops01: list[tuple[float, Color]], u: float) -> tuple[float, ...]: ... +def extract_alpha_stops(stops01: list[tuple[float, Color]]) -> list[tuple[float, float]]: ... class Pattern(PDFObject): type: Name pattern_type: int - def __init__(self, shading: LinearGradient | RadialGradient) -> None: ... + def __init__(self, shading: Gradient) -> None: ... @property def shading(self) -> str: ... + @property + def matrix(self) -> str: ... + def set_matrix(self, matrix) -> Self: ... + def get_matrix(self) -> Transform: ... + def set_apply_page_ctm(self, apply: bool) -> None: ... + def get_apply_page_ctm(self) -> bool: ... class Type2Function(PDFObject): function_type: Final = 2 @@ -22,6 +44,14 @@ class Type2Function(PDFObject): n: int def __init__(self, color_1, color_2) -> None: ... +class Type2FunctionGray(PDFObject): + function_type: Final = 2 + domain: str + c0: str + c1: str + n: int + def __init__(self, g0: float, g1: float): ... + class Type3Function(PDFObject): function_type: Final = 3 domain: str @@ -38,18 +68,23 @@ class Shading(PDFObject): background: str | None color_space: Name coords: list[int] - function: str extend: str + anti_alias: bool + def __init__( self, shading_type: Literal[2, 3], - background: DeviceRGB | DeviceGray | DeviceCMYK | None, + background: Color | None, color_space: str, - coords: list[int], - function: Type2Function | Type3Function, + coords: list[float], + functions: list[Type2Function | Type3Function], extend_before: bool, extend_after: bool, ) -> None: ... + @property + def function(self) -> str: ... + def get_functions(self) -> list[Type2Function | Type3Function]: ... + def get_shading_object(self) -> Self: ... class Gradient(ABC): color_space: str @@ -58,21 +93,23 @@ class Gradient(ABC): extend_before: Incomplete extend_after: Incomplete bounds: Incomplete - functions: Incomplete + functions: list[Type2Function | Type3Function] pattern: Pattern coords: Incomplete | None shading_type: int def __init__(self, colors, background, extend_before, extend_after, bounds): ... + def get_functions(self) -> list[Type2Function | Type3Function]: ... def get_shading_object(self) -> Shading: ... def get_pattern(self) -> Pattern: ... + def has_alpha(self) -> bool: ... + def get_alpha_shading_object(self, _: Unused = None) -> Shading | None: ... class LinearGradient(Gradient): - coords: list[str] + coords: list[float] # has four elements shading_type: int def __init__( self, - fpdf: FPDF, from_x: float, from_y: float, to_x: float, @@ -81,15 +118,14 @@ class LinearGradient(Gradient): background=None, extend_before: bool = False, extend_after: bool = False, - bounds: list[int] | None = None, + bounds: list[float] | None = None, ) -> None: ... class RadialGradient(Gradient): - coords: list[str] + coords: list[float] # has six elements shading_type: int def __init__( self, - fpdf: FPDF, start_circle_x: float, start_circle_y: float, start_circle_radius: float, @@ -100,5 +136,105 @@ class RadialGradient(Gradient): background=None, extend_before: bool = False, extend_after: bool = False, - bounds: list[int] | None = None, + bounds: list[float] | None = None, ): ... + +class MeshShading(PDFContentStream): + type: Name + shading_type: Final = 4 + color_space: Name + background: str + anti_alias: bool + bits_per_coordinate: int + bits_per_component: int + bits_per_flag: int + decode: PDFArray[str] + + def __init__( + self, + *, + color_space: str, + bbox: BoundingBox, + comp_count: int, + triangles: list[tuple[tuple[float, float], tuple[float, float], tuple[float, float]]], + colors: list[tuple[tuple[float, ...], tuple[float, ...], tuple[float, ...]]], + background: Color | None = None, + anti_alias: bool = True, + ) -> None: ... + def get_shading_object(self) -> MeshShading: ... + @classmethod + def get_functions(cls) -> list[Never]: ... + +class SweepGradient(PDFObject): + __slots__ = ( + "cx", + "cy", + "start_angle", + "end_angle", + "stops", + "spread_method", + "segments", + "inner_radius_factor", + "_cached_key", + "_shading", + "_alpha_shading", + ) + cx: float + cy: float + start_angle: float + end_angle: float + stops: list[tuple[float, Color | str]] + spread_method: GradientSpreadMethod + segments: int | None + inner_radius_factor: float + _cached_key: Incomplete | None + _shading: Incomplete | None + _alpha_shading: Incomplete | None + + def __init__( + self, + cx: float, + cy: float, + start_angle: float, + end_angle: float, + stops: list[tuple[float, Color | str]], + spread_method: GradientSpreadMethod | str = GradientSpreadMethod.PAD, + segments: int | None = None, + inner_radius_factor: float = 0.002, + ) -> None: ... + def has_alpha(self) -> bool: ... + def get_shading_object(self, bbox: BoundingBox) -> MeshShading: ... + def get_alpha_shading_object(self, bbox: BoundingBox) -> MeshShading | None: ... + +def shape_sweep_gradient_as_mesh( + cx: float, + cy: float, + start_angle: float, + end_angle: float, + stops: list[tuple[float, Color | str]], + *, + spread_method: GradientSpreadMethod, + bbox: BoundingBox, + segments: int | None = None, + inner_radius_factor: float = 0.002, +) -> MeshShading: ... +def shape_linear_gradient( + x1: float, + y1: float, + x2: float, + y2: float, + stops: list[tuple[float, Color | str]], + spread_method: GradientSpreadMethod | str = GradientSpreadMethod.PAD, + bbox: BoundingBox | None = None, +) -> LinearGradient: ... +def shape_radial_gradient( + cx: float, + cy: float, + r: float, + stops: list[tuple[float, Color | str]], + fx: float | None = None, + fy: float | None = None, + fr: float = 0.0, + spread_method: GradientSpreadMethod | str = GradientSpreadMethod.PAD, + bbox: BoundingBox | None = None, +) -> RadialGradient: ... diff --git a/stubs/fpdf2/fpdf/svg.pyi b/stubs/fpdf2/fpdf/svg.pyi index 2a2eb78fb606..0b68d9ec13d1 100644 --- a/stubs/fpdf2/fpdf/svg.pyi +++ b/stubs/fpdf2/fpdf/svg.pyi @@ -3,9 +3,9 @@ from collections.abc import Callable from logging import Logger from re import Pattern from typing import Final, Literal, NamedTuple, Protocol, TypeVar, overload, type_check_only +from typing_extensions import Self -from ._fonttools_shims import BasePen, _TTGlyphSet -from .drawing import ClippingPath, PaintedPath +from .drawing import ClippingPath, GraphicsStyle, PaintedPath, PathPen as PathPen from .fpdf import FPDF from .image_datastructures import ImageCache @@ -22,6 +22,8 @@ def force_nodocument(item: _T) -> _T: ... NUMBER_SPLIT: Final[Pattern[str]] TRANSFORM_GETTER: Final[Pattern[str]] +CSS_COMMENT_RE: Final[Pattern[str]] +CSS_BLOCK_RE: Final[Pattern[str]] class Percent(float): ... @@ -42,12 +44,18 @@ def svgcolor(colorstr): ... def convert_stroke_width(incoming): ... def convert_miterlimit(incoming): ... def clamp_float(min_val, max_val): ... -def inheritable(value, converter=...): ... -def optional(value, converter=...): ... +@overload +def inheritable(value: Literal["inherit", "currentColor"], converter: Unused = ...) -> GraphicsStyle: ... +@overload +def inheritable(value, converter: Callable[[Incomplete], Incomplete] = ...): ... +@overload +def optional(value: None | Literal["", "none"], converter: Unused = ...) -> None: ... +@overload +def optional(value, converter: Callable[[Incomplete], Incomplete] = ...): ... svg_attr_map: dict[str, Callable[[Incomplete], tuple[str, Incomplete]]] -def apply_styles(stylable, svg_element) -> None: ... +def apply_styles(stylable, svg_element, computed_style=None) -> None: ... class ShapeBuilder: @overload @@ -86,30 +94,25 @@ class ShapeBuilder: def polygon(cls, tag, clipping_path: Literal[False] = False) -> PaintedPath: ... def convert_transforms(tfstr): ... - -class PathPen(BasePen): - pdf_path: PaintedPath - last_was_line_to: bool - first_is_move: bool | None - def __init__(self, pdf_path: PaintedPath, glyphSet: _TTGlyphSet | None = ...): ... - def arcTo(self, rx, ry, rotation, arc, sweep, end) -> None: ... - def svg_path_converter(pdf_path: PaintedPath, svg_path: str) -> None: ... class SVGObject: image_cache: ImageCache | None + cross_references: dict[Incomplete, Incomplete] + css_class_styles: dict[Incomplete, Incomplete] - @classmethod - def from_file(cls, filename, *args, encoding: str = "utf-8", **kwargs): ... - cross_references: Incomplete - def __init__(self, svg_text, image_cache: ImageCache | None = None) -> None: ... preserve_ar: Incomplete width: Incomplete height: Incomplete viewbox: Incomplete + + base_group: Incomplete + + @classmethod + def from_file(cls, filename, *args, encoding: str = "utf-8", **kwargs) -> Self: ... + def __init__(self, svg_text, image_cache: ImageCache | None = None) -> None: ... def update_xref(self, key: str | None, referenced) -> None: ... def extract_shape_info(self, root_tag) -> None: ... - base_group: Incomplete def convert_graphics(self, root_tag) -> None: ... def transform_to_page_viewport(self, pdf, align_viewbox: bool = True): ... def transform_to_rect_viewport( @@ -118,12 +121,13 @@ class SVGObject: def draw_to_page(self, pdf: FPDF, x=None, y=None, debug_stream=None) -> None: ... def handle_defs(self, defs) -> None: ... def build_xref(self, xref): ... - def build_group(self, group, pdf_group=None): ... + def build_group(self, group, pdf_group=None, inherited_style=None): ... def build_path(self, path): ... def build_shape(self, shape): ... def build_clipping_path(self, shape, clip_id): ... - def apply_clipping_path(self, stylable, svg_element) -> None: ... + def apply_clipping_path(self, stylable, svg_element, style_map=None) -> None: ... def build_image(self, image) -> SVGImage: ... + def build_text(self, text_tag, inherited_style=None): ... class SVGImage(NamedTuple): href: str @@ -135,8 +139,8 @@ class SVGImage(NamedTuple): def __deepcopy__(self, _memo: Unused) -> SVGImage: ... def render( - self, _gsd_registry: Unused, _style: Unused, last_item, initial_point + self, _resource_registry: Unused, _style: Unused, last_item, initial_point ) -> tuple[Incomplete, Incomplete, Incomplete]: ... def render_debug( - self, gsd_registry: Unused, style: Unused, last_item, initial_point, debug_stream, _pfx: Unused + self, resource_registry: Unused, style: Unused, last_item, initial_point, debug_stream, _pfx: Unused ) -> tuple[Incomplete, Incomplete, Incomplete]: ... diff --git a/stubs/fpdf2/fpdf/unicode_script.pyi b/stubs/fpdf2/fpdf/unicode_script.pyi index 49e56f2d2923..69a82f688bd2 100644 --- a/stubs/fpdf2/fpdf/unicode_script.pyi +++ b/stubs/fpdf2/fpdf/unicode_script.pyi @@ -165,6 +165,13 @@ class UnicodeScript(IntEnum): VITHKUQI = 160 KAWI = 161 NAG_MUNDARI = 162 + GARAY = 163 + GURUNG_KHEMA = 164 + KIRAT_RAI = 165 + OL_ONAL = 166 + SUNUWAR = 167 + TODHRI = 168 + TULU_TIGALARI = 169 UNKNOWN = 999 UNICODE_RANGE_TO_SCRIPT: Final[tuple[tuple[int, int, int]]] diff --git a/stubs/fpdf2/fpdf/util.pyi b/stubs/fpdf2/fpdf/util.pyi index f94c5861ad2c..59f9660bf4b7 100644 --- a/stubs/fpdf2/fpdf/util.pyi +++ b/stubs/fpdf2/fpdf/util.pyi @@ -28,6 +28,9 @@ ROMAN_NUMERAL_MAP: Final[tuple[tuple[str, int], ...]] def int2roman(n: int | None) -> str: ... def int_to_letters(n: int) -> str: ... +def builtin_srgb2014_bytes() -> bytes: ... +def format_number(x: float, digits: int = 8) -> str: ... +def get_parsed_unicode_range(unicode_range: Iterable[tuple[int | str, int | str] | int | str] | str) -> set[int]: ... def print_mem_usage(prefix: str) -> None: ... def get_mem_usage(prefix: str) -> str: ... def get_process_rss() -> str: ...