diff options
Diffstat (limited to '.venv/lib/python3.12/site-packages/PyPDF2/generic/_annotations.py')
-rw-r--r-- | .venv/lib/python3.12/site-packages/PyPDF2/generic/_annotations.py | 275 |
1 files changed, 275 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/PyPDF2/generic/_annotations.py b/.venv/lib/python3.12/site-packages/PyPDF2/generic/_annotations.py new file mode 100644 index 00000000..bb46dd90 --- /dev/null +++ b/.venv/lib/python3.12/site-packages/PyPDF2/generic/_annotations.py @@ -0,0 +1,275 @@ +from typing import Optional, Tuple, Union + +from ._base import ( + BooleanObject, + FloatObject, + NameObject, + NumberObject, + TextStringObject, +) +from ._data_structures import ArrayObject, DictionaryObject +from ._fit import DEFAULT_FIT, Fit +from ._rectangle import RectangleObject +from ._utils import hex_to_rgb + + +class AnnotationBuilder: + """ + The AnnotationBuilder creates dictionaries representing PDF annotations. + + Those dictionaries can be modified before they are added to a PdfWriter + instance via `writer.add_annotation`. + + See `adding PDF annotations <../user/adding-pdf-annotations.html>`_ for + it's usage combined with PdfWriter. + """ + + from ..types import FitType, ZoomArgType + + @staticmethod + def text( + rect: Union[RectangleObject, Tuple[float, float, float, float]], + text: str, + open: bool = False, + flags: int = 0, + ) -> DictionaryObject: + """ + Add text annotation. + + :param Tuple[int, int, int, int] rect: + or array of four integers specifying the clickable rectangular area + ``[xLL, yLL, xUR, yUR]`` + :param bool open: + :param int flags: + """ + # TABLE 8.23 Additional entries specific to a text annotation + text_obj = DictionaryObject( + { + NameObject("/Type"): NameObject("/Annot"), + NameObject("/Subtype"): NameObject("/Text"), + NameObject("/Rect"): RectangleObject(rect), + NameObject("/Contents"): TextStringObject(text), + NameObject("/Open"): BooleanObject(open), + NameObject("/Flags"): NumberObject(flags), + } + ) + return text_obj + + @staticmethod + def free_text( + text: str, + rect: Union[RectangleObject, Tuple[float, float, float, float]], + font: str = "Helvetica", + bold: bool = False, + italic: bool = False, + font_size: str = "14pt", + font_color: str = "000000", + border_color: str = "000000", + background_color: str = "ffffff", + ) -> DictionaryObject: + """ + Add text in a rectangle to a page. + + :param str text: Text to be added + :param RectangleObject rect: or array of four integers + specifying the clickable rectangular area ``[xLL, yLL, xUR, yUR]`` + :param str font: Name of the Font, e.g. 'Helvetica' + :param bool bold: Print the text in bold + :param bool italic: Print the text in italic + :param str font_size: How big the text will be, e.g. '14pt' + :param str font_color: Hex-string for the color + :param str border_color: Hex-string for the border color + :param str background_color: Hex-string for the background of the annotation + """ + font_str = "font: " + if bold is True: + font_str = font_str + "bold " + if italic is True: + font_str = font_str + "italic " + font_str = font_str + font + " " + font_size + font_str = font_str + ";text-align:left;color:#" + font_color + + bg_color_str = "" + for st in hex_to_rgb(border_color): + bg_color_str = bg_color_str + str(st) + " " + bg_color_str = bg_color_str + "rg" + + free_text = DictionaryObject() + free_text.update( + { + NameObject("/Type"): NameObject("/Annot"), + NameObject("/Subtype"): NameObject("/FreeText"), + NameObject("/Rect"): RectangleObject(rect), + NameObject("/Contents"): TextStringObject(text), + # font size color + NameObject("/DS"): TextStringObject(font_str), + # border color + NameObject("/DA"): TextStringObject(bg_color_str), + # background color + NameObject("/C"): ArrayObject( + [FloatObject(n) for n in hex_to_rgb(background_color)] + ), + } + ) + return free_text + + @staticmethod + def line( + p1: Tuple[float, float], + p2: Tuple[float, float], + rect: Union[RectangleObject, Tuple[float, float, float, float]], + text: str = "", + title_bar: str = "", + ) -> DictionaryObject: + """ + Draw a line on the PDF. + + :param Tuple[float, float] p1: First point + :param Tuple[float, float] p2: Second point + :param RectangleObject rect: or array of four + integers specifying the clickable rectangular area + ``[xLL, yLL, xUR, yUR]`` + :param str text: Text to be displayed as the line annotation + :param str title_bar: Text to be displayed in the title bar of the + annotation; by convention this is the name of the author + """ + line_obj = DictionaryObject( + { + NameObject("/Type"): NameObject("/Annot"), + NameObject("/Subtype"): NameObject("/Line"), + NameObject("/Rect"): RectangleObject(rect), + NameObject("/T"): TextStringObject(title_bar), + NameObject("/L"): ArrayObject( + [ + FloatObject(p1[0]), + FloatObject(p1[1]), + FloatObject(p2[0]), + FloatObject(p2[1]), + ] + ), + NameObject("/LE"): ArrayObject( + [ + NameObject(None), + NameObject(None), + ] + ), + NameObject("/IC"): ArrayObject( + [ + FloatObject(0.5), + FloatObject(0.5), + FloatObject(0.5), + ] + ), + NameObject("/Contents"): TextStringObject(text), + } + ) + return line_obj + + @staticmethod + def rectangle( + rect: Union[RectangleObject, Tuple[float, float, float, float]], + interiour_color: Optional[str] = None, + ) -> DictionaryObject: + """ + Draw a rectangle on the PDF. + + :param RectangleObject rect: or array of four + integers specifying the clickable rectangular area + ``[xLL, yLL, xUR, yUR]`` + """ + square_obj = DictionaryObject( + { + NameObject("/Type"): NameObject("/Annot"), + NameObject("/Subtype"): NameObject("/Square"), + NameObject("/Rect"): RectangleObject(rect), + } + ) + + if interiour_color: + square_obj[NameObject("/IC")] = ArrayObject( + [FloatObject(n) for n in hex_to_rgb(interiour_color)] + ) + + return square_obj + + @staticmethod + def link( + rect: Union[RectangleObject, Tuple[float, float, float, float]], + border: Optional[ArrayObject] = None, + url: Optional[str] = None, + target_page_index: Optional[int] = None, + fit: Fit = DEFAULT_FIT, + ) -> DictionaryObject: + """ + Add a link to the document. + + The link can either be an external link or an internal link. + + An external link requires the URL parameter. + An internal link requires the target_page_index, fit, and fit args. + + + :param RectangleObject rect: or array of four + integers specifying the clickable rectangular area + ``[xLL, yLL, xUR, yUR]`` + :param border: if provided, an array describing border-drawing + properties. See the PDF spec for details. No border will be + drawn if this argument is omitted. + - horizontal corner radius, + - vertical corner radius, and + - border width + - Optionally: Dash + :param str url: Link to a website (if you want to make an external link) + :param int target_page_index: index of the page to which the link should go + (if you want to make an internal link) + :param Fit fit: Page fit or 'zoom' option. + """ + from ..types import BorderArrayType + + is_external = url is not None + is_internal = target_page_index is not None + if not is_external and not is_internal: + raise ValueError( + "Either 'url' or 'target_page_index' have to be provided. Both were None." + ) + if is_external and is_internal: + raise ValueError( + f"Either 'url' or 'target_page_index' have to be provided. url={url}, target_page_index={target_page_index}" + ) + + border_arr: BorderArrayType + if border is not None: + border_arr = [NameObject(n) for n in border[:3]] + if len(border) == 4: + dash_pattern = ArrayObject([NameObject(n) for n in border[3]]) + border_arr.append(dash_pattern) + else: + border_arr = [NumberObject(0)] * 3 + + link_obj = DictionaryObject( + { + NameObject("/Type"): NameObject("/Annot"), + NameObject("/Subtype"): NameObject("/Link"), + NameObject("/Rect"): RectangleObject(rect), + NameObject("/Border"): ArrayObject(border_arr), + } + ) + if is_external: + link_obj[NameObject("/A")] = DictionaryObject( + { + NameObject("/S"): NameObject("/URI"), + NameObject("/Type"): NameObject("/Action"), + NameObject("/URI"): TextStringObject(url), + } + ) + if is_internal: + # This needs to be updated later! + dest_deferred = DictionaryObject( + { + "target_page_index": NumberObject(target_page_index), + "fit": NameObject(fit.fit_type), + "fit_args": fit.fit_args, + } + ) + link_obj[NameObject("/Dest")] = dest_deferred + return link_obj |