Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
_link.py145 linesDownload Raw Back to generic
1# All rights reserved.2#3# Redistribution and use in source and binary forms, with or without4# modification, are permitted provided that the following conditions are5# met:6#7# * Redistributions of source code must retain the above copyright notice,8# this list of conditions and the following disclaimer.9# * Redistributions in binary form must reproduce the above copyright notice,10# this list of conditions and the following disclaimer in the documentation11# and/or other materials provided with the distribution.12# * The name of the author may not be used to endorse or promote products13# derived from this software without specific prior written permission.14#15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"16# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE19# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR20# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF21# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS22# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN23# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)24# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE25# POSSIBILITY OF SUCH DAMAGE.26 27 28# This module contains code used by _writer.py to track links in pages29# being added to the writer until the links can be resolved.30 31from typing import TYPE_CHECKING, Optional, Union, cast32 33from .._utils import logger_warning34from . import ArrayObject, DictionaryObject, IndirectObject, PdfObject, TextStringObject, is_null_or_none35 36if TYPE_CHECKING:37    from .._page import PageObject38    from .._reader import PdfReader39    from .._writer import PdfWriter40 41 42class NamedReferenceLink:43    """Named reference link being preserved until we can resolve it correctly."""44 45    def __init__(self, reference: TextStringObject, source_pdf: "PdfReader") -> None:46        """reference: TextStringObject with named reference"""47        self._reference = reference48        self._source_pdf = source_pdf49 50    def find_referenced_page(self) -> Union[IndirectObject, None]:51        destination = self._source_pdf.named_destinations.get(str(self._reference))52        return destination.page if destination else None53 54    def patch_reference(self, target_pdf: "PdfWriter", new_page: IndirectObject) -> None:55        """target_pdf: PdfWriter which the new link went into"""56        # point named destination in new PDF to the new page57        if str(self._reference) not in target_pdf.named_destinations:58            target_pdf.add_named_destination(str(self._reference), new_page.page_number)59 60 61class DirectReferenceLink:62    """Direct reference link being preserved until we can resolve it correctly."""63 64    def __init__(self, reference: ArrayObject) -> None:65        """reference: an ArrayObject whose first element is the Page indirect object"""66        self._reference = reference67 68    def find_referenced_page(self) -> IndirectObject:69        return cast(IndirectObject, self._reference[0])70 71    def patch_reference(self, target_pdf: "PdfWriter", new_page: IndirectObject) -> None:72        """target_pdf: PdfWriter which the new link went into"""73        self._reference[0] = new_page74 75 76ReferenceLink = Union[NamedReferenceLink, DirectReferenceLink]77 78 79def extract_links(new_page: "PageObject", old_page: "PageObject") -> list[tuple[ReferenceLink, ReferenceLink]]:80    """Extracts links from two pages on the assumption that the two pages are81    the same. Produces one list of (new link, old link) tuples.82 83    Non-link annotations are ignored before pairing to avoid dropping valid84    links when one page includes additional non-link annotation entries.85    """86    new_annotations = new_page.get("/Annots", ArrayObject()).get_object()87    old_annotations = old_page.get("/Annots", ArrayObject()).get_object()88    if is_null_or_none(new_annotations):89        new_annotations = ArrayObject()90    if is_null_or_none(old_annotations):91        old_annotations = ArrayObject()92    if not isinstance(new_annotations, ArrayObject) or not isinstance(old_annotations, ArrayObject):93        logger_warning(94            f"Expected annotation arrays: {old_annotations} {new_annotations}. Ignoring annotations.",95            __name__96        )97        return []98    new_links = [99        link100        for annotation in new_annotations101        if (link := _build_link(annotation, new_page)) is not None102    ]103    old_links = [104        link105        for annotation in old_annotations106        if (link := _build_link(annotation, old_page)) is not None107    ]108 109    if len(new_links) != len(old_links):110        logger_warning(111            f"Annotation sizes differ: {old_links} vs. {new_links}",112            __name__,113        )114 115    return list(zip(new_links, old_links))116 117 118def _build_link(indirect_object: IndirectObject, page: "PageObject") -> Optional[ReferenceLink]:119    src = cast("PdfReader", page.pdf)120    link = cast(DictionaryObject, indirect_object.get_object())121    if (not isinstance(link, DictionaryObject)) or link.get("/Subtype") != "/Link":122        return None123 124    if "/A" in link:125        action = cast(DictionaryObject, link["/A"])126        if action.get("/S") != "/GoTo":127            return None128 129        if "/D" not in action:130            return None131        return _create_link(action["/D"], src)132 133    if "/Dest" in link:134        return _create_link(link["/Dest"], src)135 136    return None  # Nothing to do here137 138 139def _create_link(reference: PdfObject, source_pdf: "PdfReader") -> Optional[ReferenceLink]:140    if isinstance(reference, TextStringObject):141        return NamedReferenceLink(reference, source_pdf)142    if isinstance(reference, ArrayObject):143        return DirectReferenceLink(reference)144    return None145 
codekingpro/portable-devtools · Team Ai