diff --git a/fpdf/drawing.py b/fpdf/drawing.py index f4645be46..4f4d81ad6 100644 --- a/fpdf/drawing.py +++ b/fpdf/drawing.py @@ -195,7 +195,7 @@ class DeviceRGB( OPERATOR = "rg" """The PDF drawing operator used to specify this type of color.""" - def __new__(cls, r, g, b, a=None): + def __new__(cls, r: float, g: float, b: float, a: Optional[float] = None): if a is not None: _check_range(a) @@ -240,7 +240,7 @@ class DeviceGray( OPERATOR = "g" """The PDF drawing operator used to specify this type of color.""" - def __new__(cls, g, a=None): + def __new__(cls, g: float, a: Optional[float] = None): if a is not None: _check_range(a) @@ -3240,7 +3240,7 @@ class PaintedPath: primitive path elements and `GraphicsContext`. """ - def __init__(self, x=0, y=0): + def __init__(self, x: float = 0, y: float = 0) -> None: self._root_graphics_context = GraphicsContext() self._graphics_context = self._root_graphics_context diff --git a/fpdf/errors.py b/fpdf/errors.py index 6ce8cd461..3d80db047 100644 --- a/fpdf/errors.py +++ b/fpdf/errors.py @@ -5,7 +5,7 @@ class FPDFException(Exception): class FPDFPageFormatException(FPDFException): """Error is thrown when a bad page format is given""" - def __init__(self, argument, unknown=False, one=False): + def __init__(self, argument, unknown: bool = False, one: bool = False): super().__init__() if unknown and one: raise TypeError( diff --git a/fpdf/fpdf.py b/fpdf/fpdf.py index 8c4c359d9..f29cab5f0 100644 --- a/fpdf/fpdf.py +++ b/fpdf/fpdf.py @@ -19,7 +19,9 @@ from numbers import Number from os.path import splitext from pathlib import Path -from typing import Callable, NamedTuple, Optional, Union, List, Tuple +from typing import Callable, NamedTuple, Optional, Union, List, Tuple, Any + +from .drawing import DeviceGray, DeviceRGB try: from endesive import signer @@ -235,10 +237,10 @@ class FPDF(GraphicsStateMixin): def __init__( self, - orientation="portrait", - unit="mm", - format="A4", - font_cache_dir="DEPRECATED", + orientation: str = "portrait", + unit: Union[str, int, float] = "mm", + format: str = "A4", + font_cache_dir: Union[Path, str] = "DEPRECATED", ): """ Args: @@ -399,11 +401,11 @@ def write_html(self, text, *args, **kwargs): text = unescape(text) # To deal with HTML entities html2pdf.feed(text) - def _set_min_pdf_version(self, version): + def _set_min_pdf_version(self, version: str) -> None: self.pdf_version = max(self.pdf_version, version) @property - def is_ttf_font(self): + def is_ttf_font(self) -> bool: return self.current_font and self.current_font.type == "TTF" @property @@ -449,7 +451,7 @@ def set_margin(self, margin): self.set_margins(margin, margin) self.set_auto_page_break(self.auto_page_break, margin) - def set_margins(self, left, top, right=-1): + def set_margins(self, left: float, top: float, right: float = -1): """ Sets the document left, top & optionaly right margins to the same value. By default, they equal 1 cm. @@ -468,7 +470,7 @@ def set_margins(self, left, top, right=-1): right = left self.r_margin = right - def set_left_margin(self, margin): + def set_left_margin(self, margin: float): """ Sets the document left margin. Also sets the current FPDF.x on the page to this minimum horizontal position. @@ -480,7 +482,7 @@ def set_left_margin(self, margin): self.x = margin self.l_margin = margin - def set_top_margin(self, margin): + def set_top_margin(self, margin: float): """ Sets the document top margin. @@ -489,7 +491,7 @@ def set_top_margin(self, margin): """ self.t_margin = margin - def set_right_margin(self, margin): + def set_right_margin(self, margin: float): """ Sets the document right margin. @@ -521,7 +523,7 @@ def default_page_dimensions(self): else (self.dh_pt, self.dw_pt) ) - def _set_orientation(self, orientation, page_width_pt, page_height_pt): + def _set_orientation(self, orientation: str, page_width_pt, page_height_pt): orientation = orientation.lower() if orientation in ("p", "portrait"): self.cur_orientation = "P" @@ -536,7 +538,7 @@ def _set_orientation(self, orientation, page_width_pt, page_height_pt): self.w = self.w_pt / self.k self.h = self.h_pt / self.k - def set_display_mode(self, zoom, layout="continuous"): + def set_display_mode(self, zoom: Union[str, int], layout="continuous"): """ Defines the way the document is to be displayed by the viewer. @@ -938,7 +940,12 @@ def set_fill_color(self, r, g: int = -1, b: int = -1): if self.page > 0: self._out(self.fill_color.serialize().lower()) - def set_text_color(self, r, g=-1, b=-1): + def set_text_color( + self, + r: Union[int, Tuple[float, float, float], DeviceGray, DeviceRGB], + g=-1, + b=-1, + ): """ Defines the color used for text. It can be expressed in RGB components or grey scale. @@ -1381,7 +1388,7 @@ def ellipse(self, x, y, w, h, style=None): style = RenderStyle.coerce(style) self._draw_ellipse(x, y, w, h, style.operator) - def _draw_ellipse(self, x, y, w, h, operator): + def _draw_ellipse(self, x: float, y: float, w: float, h: float, operator): cx = x + w / 2 cy = y + h / 2 rx = w / 2 @@ -3059,7 +3066,7 @@ def _render_styled_text_line( return page_break_triggered - def _add_quad_points(self, x, y, w, h): + def _add_quad_points(self, x: float, y: float, w: float, h: float): self._text_quad_points[self.page].extend( [ x * self.k, @@ -3073,7 +3080,7 @@ def _add_quad_points(self, x, y, w, h): ] ) - def _preload_font_styles(self, txt, markdown): + def _preload_font_styles(self, txt: str, markdown: bool): """ When Markdown styling is enabled, we require secondary fonts to ender text in bold & italics. @@ -3105,7 +3112,7 @@ def _preload_font_styles(self, txt, markdown): self.page = page return styled_txt_frags - def _parse_chars(self, txt): + def _parse_chars(self, txt: str): "Check if the font has all the necessary glyphs. If a glyph from a fallback font is used, break into fragments" fragments = [] txt_frag = [] @@ -3273,12 +3280,12 @@ def _perform_page_break_if_need_be(self, h): return True return False - def _perform_page_break(self): + def _perform_page_break(self) -> None: x = self.x self.add_page(same=True) self.x = x # restore x but not y after drawing header - def _has_next_page(self): + def _has_next_page(self) -> bool: return self.pages_count > self.page @contextmanager @@ -4059,11 +4066,11 @@ def ln(self, h=None): self.x = self.l_margin self.y += self._lasth if h is None else h - def get_x(self): + def get_x(self) -> float: """Returns the abscissa of the current position.""" return self.x - def set_x(self, x): + def set_x(self, x: float) -> None: """ Defines the abscissa of the current position. If the value provided is negative, it is relative to the right of the page. @@ -4073,7 +4080,7 @@ def set_x(self, x): """ self.x = x if x >= 0 else self.w + x - def get_y(self): + def get_y(self) -> float: """Returns the ordinate of the current position.""" if self._in_unbreakable: raise FPDFException( @@ -4081,7 +4088,7 @@ def get_y(self): ) return self.y - def set_y(self, y): + def set_y(self, y: float) -> None: """ Moves the current abscissa back to the left margin and sets the ordinate. If the value provided is negative, it is relative to the bottom of the page. @@ -4121,13 +4128,13 @@ def normalize_text(self, txt: str): def sign_pkcs12( self, - pkcs_filepath, + pkcs_filepath: str, password=None, - hashalgo="sha256", - contact_info=None, - location=None, - signing_time=None, - reason=None, + hashalgo: str = "sha256", + contact_info: Optional[str] = None, + location: Optional[str] = None, + signing_time: Optional[datetime] = None, + reason: Optional[str] = None, flags=(AnnotationFlag.PRINT, AnnotationFlag.LOCKED), ): """ @@ -4170,11 +4177,11 @@ def sign( key, cert, extra_certs=(), - hashalgo="sha256", - contact_info=None, - location=None, - signing_time=None, - reason=None, + hashalgo: str = "sha256", + contact_info: Optional[str] = None, + location: Optional[str] = None, + signing_time: Optional[datetime] = None, + reason: Optional[str] = None, flags=(AnnotationFlag.PRINT, AnnotationFlag.LOCKED), ): """ @@ -4368,7 +4375,7 @@ def interleaved2of5(self, txt, x, y, w=1, h=10): x += line_width @check_page - def code39(self, txt, x, y, w=1.5, h=5): + def code39(self, txt: str, x: float, y: float, w: float = 1.5, h: float = 5): """Barcode 3of9""" dim = {"w": w, "n": w / 3} if not txt.startswith("*") or not txt.endswith("*"): @@ -4436,7 +4443,7 @@ def code39(self, txt, x, y, w=1.5, h=5): @check_page @contextmanager - def rect_clip(self, x, y, w, h): + def rect_clip(self, x: float, y: float, w: float, h: float): """ Context manager that defines a rectangular crop zone, useful to render only part of an image. @@ -4458,7 +4465,7 @@ def rect_clip(self, x, y, w, h): @check_page @contextmanager - def elliptic_clip(self, x, y, w, h): + def elliptic_clip(self, x: float, y: float, w: float, h: float): """ Context manager that defines an elliptic crop zone, useful to render only part of an image. @@ -4476,7 +4483,7 @@ def elliptic_clip(self, x, y, w, h): @check_page @contextmanager - def round_clip(self, x, y, r): + def round_clip(self, x: float, y: float, r: float): """ Context manager that defines a circular crop zone, useful to render only part of an image. @@ -4536,7 +4543,7 @@ def offset_rendering(self): recorder.rewind() @check_page - def insert_toc_placeholder(self, render_toc_function, pages=1): + def insert_toc_placeholder(self, render_toc_function, pages: int = 1): """ Configure Table Of Contents rendering at the end of the document generation, and reserve some vertical space right now in order to insert it. @@ -4566,13 +4573,13 @@ def insert_toc_placeholder(self, render_toc_function, pages=1): def set_section_title_styles( self, - level0, - level1=None, - level2=None, - level3=None, - level4=None, - level5=None, - level6=None, + level0: TitleStyle, + level1: Optional[TitleStyle] = None, + level2: Optional[TitleStyle] = None, + level3: Optional[TitleStyle] = None, + level4: Optional[TitleStyle] = None, + level5: Optional[TitleStyle] = None, + level6: Optional[TitleStyle] = None, ): """ Defines a style for section titles. @@ -4603,7 +4610,7 @@ def set_section_title_styles( } @check_page - def start_section(self, name, level=0, strict=True): + def start_section(self, name: str, level: int = 0, strict: bool = True): """ Start a section in the document outline. If section_title_styles have been configured, @@ -4728,7 +4735,11 @@ def table(self, *args, **kwargs): table.render() def output( - self, name="", dest="", linearize=False, output_producer_class=OutputProducer + self, + name: str = "", + dest: str = "", + linearize: bool = False, + output_producer_class: type = OutputProducer, ): """ Output PDF to some destination. @@ -4788,7 +4799,7 @@ def _convert_to_drawing_color(r, g, b): return drawing.DeviceRGB(r / 255, g / 255, b / 255) -def _is_svg(bytes): +def _is_svg(bytes) -> bool: return bytes.startswith(b" None: self.pdf = pdf self._initial = deepcopy(self.pdf.__dict__) self._calls = [] @@ -41,7 +41,7 @@ def rewind(self): self.pdf.__dict__ = self._initial self._initial = deepcopy(self.pdf.__dict__) - def replay(self): + def replay(self) -> None: for call in self._calls: func, args, kwargs = call try: @@ -59,7 +59,7 @@ def replay(self): class CallRecorder: - def __init__(self, func, calls): + def __init__(self, func, calls) -> None: self._func = func self._calls = calls diff --git a/fpdf/sign.py b/fpdf/sign.py index cff8c8a0e..2c2d6ddba 100644 --- a/fpdf/sign.py +++ b/fpdf/sign.py @@ -1,6 +1,7 @@ "Document signature generation" import hashlib from datetime import timezone +from typing import Optional from unittest.mock import patch from .syntax import build_obj_dict, Name @@ -9,7 +10,13 @@ class Signature: - def __init__(self, contact_info=None, location=None, m=None, reason=None): + def __init__( + self, + contact_info: Optional[str] = None, + location: Optional[str] = None, + m: Optional[str] = None, + reason: Optional[str] = None, + ): self.type = Name("Sig") self.filter = Name("Adobe.PPKLite") self.sub_filter = Name("adbe.pkcs7.detached") diff --git a/fpdf/syntax.py b/fpdf/syntax.py index fea4c9701..a2d088778 100644 --- a/fpdf/syntax.py +++ b/fpdf/syntax.py @@ -344,7 +344,7 @@ def serialize(self, _security_handler=None, _obj_id=None): class DestinationXYZ(Destination): - def __init__(self, page, top, left=0, zoom="null"): + def __init__(self, page, top, left: int = 0, zoom: str = "null"): self.page_number = page self.top = top self.left = left diff --git a/fpdf/template.py b/fpdf/template.py index 68e2be7de..396bb21af 100644 --- a/fpdf/template.py +++ b/fpdf/template.py @@ -5,7 +5,7 @@ __license__ = "LGPL 3.0" import csv, locale, warnings -from typing import Optional +from typing import Optional, List from .errors import FPDFException from .fpdf import FPDF @@ -30,7 +30,7 @@ class FlexTemplate: a document in any combination. """ - def __init__(self, pdf, elements=None): + def __init__(self, pdf: FPDF, elements: Optional[List[dict]] = None) -> None: """ Arguments: @@ -60,7 +60,7 @@ def __init__(self, pdf, elements=None): } self.texts = {} - def load_elements(self, elements): + def load_elements(self, elements: List[dict]): """ Load a template definition. @@ -152,7 +152,13 @@ def _parse_multiline(s): return False return None - def parse_csv(self, infile, delimiter=",", decimal_sep=".", encoding=None): + def parse_csv( + self, + infile: str, + delimiter: str = ",", + decimal_sep: str = ".", + encoding: Optional[str] = None, + ): """ Load the template definition from a CSV file. @@ -175,7 +181,7 @@ def parse_csv(self, infile, delimiter=",", decimal_sep=".", encoding=None): """ - def _varsep_float(s, default="0"): + def _varsep_float(s: str, default: str = "0"): """Convert to float with given decimal seperator""" # glad to have nonlocal scoping... return float((s.strip() or default).replace(decimal_sep, ".")) @@ -674,7 +680,7 @@ def add_page(self): self.pdf.add_page() # pylint: disable=arguments-differ - def render(self, outfile: Optional[str] = None, dest: Optional[str] = None): + def render(self, outfile: Optional[str] = None, dest: Optional[str] = None): # type: ignore[override] """ Finish the document and process all pending data. diff --git a/fpdf/util.py b/fpdf/util.py index 638062fa6..9d912f2d2 100644 --- a/fpdf/util.py +++ b/fpdf/util.py @@ -115,10 +115,10 @@ def get_process_rss_as_mib() -> Union[Number, None]: try: with open(f"/proc/{pid}/statm", encoding="utf8") as statm: return ( - int(statm.readline().split()[1]) # type: ignore[return-value] + int(statm.readline().split()[1]) * os.sysconf("SC_PAGE_SIZE") / 1024 - / 1024 + / 1024 # type: ignore[return-value] ) except FileNotFoundError: # /proc files only exist under Linux return None