Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MAINT: Deduplicate Code #1022

Merged
merged 14 commits into from
Jun 29, 2022
91 changes: 27 additions & 64 deletions PyPDF2/_merger.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
from ._reader import PdfReader
from ._utils import StrByteType, deprecate_with_replacement, str_
from ._writer import PdfWriter
from .constants import GoToActionArguments
from .constants import PagesAttributes as PA
from .constants import TypArguments, TypFitArguments
from .generic import (
ArrayObject,
Bookmark,
Expand All @@ -48,14 +50,7 @@
TreeObject,
)
from .pagerange import PageRange, PageRangeSpec
from .types import (
BookmarkTypes,
FitType,
LayoutType,
OutlinesType,
PagemodeType,
ZoomArgType,
)
from .types import FitType, LayoutType, OutlinesType, PagemodeType, ZoomArgType

ERR_CLOSED_WRITER = "close() was called and thus the writer cannot be used anymore"

Expand Down Expand Up @@ -153,7 +148,7 @@ def merge(
bookmark_typ = Bookmark(
TextStringObject(bookmark),
NumberObject(self.id_count),
NameObject("/Fit"),
NameObject(TypFitArguments.FIT),
)
self.bookmarks += [bookmark_typ, outline] # type: ignore
else:
Expand Down Expand Up @@ -476,65 +471,33 @@ def _write_bookmarks(
def _write_bookmark_on_page(
self, bookmark: Union[Bookmark, Destination], page: _MergedPage
) -> None:
# b[NameObject('/Page')] = p.out_pagedata
bm_type = cast(BookmarkTypes, bookmark["/Type"])
bm_type = cast(str, bookmark["/Type"])
args = [NumberObject(page.id), NameObject(bm_type)]
# nothing more to add
# if b['/Type'] == '/Fit' or b['/Type'] == '/FitB'
if bm_type == "/FitH" or bm_type == "/FitBH":
if "/Top" in bookmark and not isinstance(bookmark["/Top"], NullObject):
args.append(FloatObject(bookmark["/Top"]))
else:
args.append(FloatObject(0))
del bookmark["/Top"]
elif bm_type == "/FitV" or bm_type == "/FitBV":
if "/Left" in bookmark and not isinstance(bookmark["/Left"], NullObject):
args.append(FloatObject(bookmark["/Left"]))
else:
args.append(FloatObject(0))
del bookmark["/Left"]
elif bm_type == "/XYZ":
if "/Left" in bookmark and not isinstance(bookmark["/Left"], NullObject):
args.append(FloatObject(bookmark["/Left"]))
else:
args.append(FloatObject(0))
if "/Top" in bookmark and not isinstance(bookmark["/Top"], NullObject):
args.append(FloatObject(bookmark["/Top"]))
else:
args.append(FloatObject(0))
if "/Zoom" in bookmark and not isinstance(bookmark["/Zoom"], NullObject):
args.append(FloatObject(bookmark["/Zoom"]))
fit2arg_keys: Dict[str, Tuple[str, ...]] = {
TypFitArguments.FIT_H: (TypArguments.TOP,),
TypFitArguments.FIT_BH: (TypArguments.TOP,),
TypFitArguments.FIT_V: (TypArguments.LEFT,),
TypFitArguments.FIT_BV: (TypArguments.LEFT,),
TypFitArguments.XYZ: (TypArguments.LEFT, TypArguments.TOP, "/Zoom"),
TypFitArguments.FIT_R: (
TypArguments.LEFT,
TypArguments.BOTTOM,
TypArguments.RIGHT,
TypArguments.TOP,
),
}
for arg_key in fit2arg_keys.get(bm_type, tuple()):
if arg_key in bookmark and not isinstance(bookmark[arg_key], NullObject):
args.append(FloatObject(bookmark[arg_key]))
else:
args.append(FloatObject(0))
del bookmark["/Top"], bookmark["/Zoom"], bookmark["/Left"]
elif bm_type == "/FitR":
if "/Left" in bookmark and not isinstance(bookmark["/Left"], NullObject):
args.append(FloatObject(bookmark["/Left"]))
else:
args.append(FloatObject(0))
if "/Bottom" in bookmark and not isinstance(
bookmark["/Bottom"], NullObject
):
args.append(FloatObject(bookmark["/Bottom"]))
else:
args.append(FloatObject(0))
if "/Right" in bookmark and not isinstance(bookmark["/Right"], NullObject):
args.append(FloatObject(bookmark["/Right"]))
else:
args.append(FloatObject(0))
if "/Top" in bookmark and not isinstance(bookmark["/Top"], NullObject):
args.append(FloatObject(bookmark["/Top"]))
else:
args.append(FloatObject(0))
del (
bookmark["/Left"],
bookmark["/Right"],
bookmark["/Bottom"],
bookmark["/Top"],
)
del bookmark[arg_key]

bookmark[NameObject("/A")] = DictionaryObject(
{NameObject("/S"): NameObject("/GoTo"), NameObject("/D"): ArrayObject(args)}
{
NameObject(GoToActionArguments.S): NameObject("/GoTo"),
NameObject(GoToActionArguments.D): ArrayObject(args),
}
)

def _associate_dests_to_pages(self, pages: List[_MergedPage]) -> None:
Expand Down Expand Up @@ -671,7 +634,7 @@ def add_named_destination(self, title: str, pagenum: int) -> None:
dest = Destination(
TextStringObject(title),
NumberObject(pagenum),
NameObject("/FitH"),
NameObject(TypFitArguments.FIT_H),
NumberObject(826),
)
self.named_dests.append(dest)
Expand Down
37 changes: 17 additions & 20 deletions PyPDF2/_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@
skip_over_whitespace,
)
from .constants import CatalogAttributes as CA
from .constants import CatalogDictionary
from .constants import CatalogDictionary as CD
from .constants import Core as CO
from .constants import DocumentInformationAttributes as DI
from .constants import FieldDictionaryAttributes, GoToActionArguments
from .constants import PageAttributes as PG
from .constants import PagesAttributes as PA
from .constants import TrailerKeys as TK
Expand Down Expand Up @@ -473,22 +475,13 @@ def get_fields(
default, the mapping name is used for keys.
``None`` if form data could not be located.
"""
field_attributes = {
"/FT": "Field Type",
PA.PARENT: "Parent",
"/T": "Field Name",
"/TU": "Alternate Field Name",
"/TM": "Mapping Name",
"/Ff": "Field Flags",
"/V": "Value",
"/DV": "Default Value",
}
field_attributes = FieldDictionaryAttributes.attributes_dict()
if retval is None:
retval = {}
catalog = cast(DictionaryObject, self.trailer[TK.ROOT])
# get the AcroForm tree
if "/AcroForm" in catalog:
tree = cast(Optional[TreeObject], catalog["/AcroForm"])
if CatalogDictionary.ACRO_FORM in catalog:
tree = cast(Optional[TreeObject], catalog[CatalogDictionary.ACRO_FORM])
else:
return None
if tree is None:
Expand Down Expand Up @@ -553,11 +546,15 @@ def _check_kids(
self.get_fields(kid.get_object(), retval, fileobj)

def _write_field(self, fileobj: Any, field: Any, field_attributes: Any) -> None:
order = ("/TM", "/T", "/FT", PA.PARENT, "/TU", "/Ff", "/V", "/DV")
for attr in order:
for attr in FieldDictionaryAttributes.attributes():
if attr in (
FieldDictionaryAttributes.Kids,
FieldDictionaryAttributes.AA,
):
continue
attr_name = field_attributes[attr]
try:
if attr == "/FT":
if attr == FieldDictionaryAttributes.FT:
# Make the field type value more clear
types = {
"/Btn": "Button",
Expand All @@ -567,12 +564,12 @@ def _write_field(self, fileobj: Any, field: Any, field_attributes: Any) -> None:
}
if field[attr] in types:
fileobj.write(attr_name + ": " + types[field[attr]] + "\n")
elif attr == PA.PARENT:
elif attr == FieldDictionaryAttributes.Parent:
# Let's just write the name of the parent
try:
name = field[PA.PARENT]["/TM"]
name = field[attr][FieldDictionaryAttributes.TM]
except KeyError:
name = field[PA.PARENT]["/T"]
name = field[attr][FieldDictionaryAttributes.T]
fileobj.write(attr_name + ": " + name + "\n")
else:
fileobj.write(attr_name + ": " + str(field[attr]) + "\n")
Expand Down Expand Up @@ -819,9 +816,9 @@ def _build_outline(self, node: DictionaryObject) -> Optional[Destination]:
# Action, section 8.5 (only type GoTo supported)
title = node["/Title"]
action = cast(DictionaryObject, node["/A"])
action_type = cast(NameObject, action["/S"])
action_type = cast(NameObject, action[GoToActionArguments.S])
if action_type == "/GoTo":
dest = action["/D"]
dest = action[GoToActionArguments.D]
elif "/Dest" in node and "/Title" in node:
# Destination, section 8.2.1
title = node["/Title"]
Expand Down
68 changes: 48 additions & 20 deletions PyPDF2/_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,22 @@
b_,
deprecate_with_replacement,
)
from .constants import AnnotationDictionaryAttributes
from .constants import CatalogAttributes as CA
from .constants import CatalogDictionary
from .constants import Core as CO
from .constants import EncryptionDictAttributes as ED
from .constants import (
FieldDictionaryAttributes,
FileSpecificationDictionaryEntries,
GoToActionArguments,
InteractiveFormDictEntries,
)
from .constants import PageAttributes as PG
from .constants import PagesAttributes as PA
from .constants import StreamAttributes as SA
from .constants import TrailerKeys as TK
from .constants import TypFitArguments
from .generic import (
ArrayObject,
BooleanObject,
Expand Down Expand Up @@ -187,17 +196,17 @@ def set_need_appearances_writer(self) -> None:
try:
catalog = self._root_object
# get the AcroForm tree
if "/AcroForm" not in catalog:
if CatalogDictionary.ACRO_FORM not in catalog:
self._root_object.update(
{
NameObject("/AcroForm"): IndirectObject(
NameObject(CatalogDictionary.ACRO_FORM): IndirectObject(
len(self._objects), 0, self
)
}
)

need_appearances = NameObject("/NeedAppearances")
self._root_object["/AcroForm"][need_appearances] = BooleanObject(True) # type: ignore
need_appearances = NameObject(InteractiveFormDictEntries.NeedAppearances)
self._root_object[CatalogDictionary.ACRO_FORM][need_appearances] = BooleanObject(True) # type: ignore

except Exception as exc:
logger.error("set_need_appearances_writer() catch : ", repr(exc))
Expand Down Expand Up @@ -465,10 +474,10 @@ def add_attachment(self, filename: str, data: Union[str, bytes]) -> None:
filespec.update(
{
NameObject(PA.TYPE): NameObject("/Filespec"),
NameObject("/F"): create_string_object(
NameObject(FileSpecificationDictionaryEntries.F): create_string_object(
filename
), # Perhaps also try TextStringObject
NameObject("/EF"): ef_entry,
NameObject(FileSpecificationDictionaryEntries.EF): ef_entry,
}
)

Expand Down Expand Up @@ -579,15 +588,29 @@ def update_page_form_field_values(
if PG.PARENT in writer_annot:
writer_parent_annot = writer_annot[PG.PARENT]
for field in fields:
if writer_annot.get("/T") == field:
if writer_annot.get(FieldDictionaryAttributes.T) == field:
writer_annot.update(
{NameObject("/V"): TextStringObject(fields[field])}
{
NameObject(FieldDictionaryAttributes.V): TextStringObject(
fields[field]
)
}
)
if flags:
writer_annot.update({NameObject("/Ff"): NumberObject(flags)})
elif writer_parent_annot.get("/T") == field:
writer_annot.update(
{
NameObject(FieldDictionaryAttributes.Ff): NumberObject(
flags
)
}
)
elif writer_parent_annot.get(FieldDictionaryAttributes.T) == field:
writer_parent_annot.update(
{NameObject("/V"): TextStringObject(fields[field])}
{
NameObject(FieldDictionaryAttributes.V): TextStringObject(
fields[field]
)
}
)

def updatePageFormFieldValues(
Expand Down Expand Up @@ -1109,7 +1132,10 @@ def add_bookmark(
)
dest_array = dest.dest_array
action.update(
{NameObject("/D"): dest_array, NameObject("/S"): NameObject("/GoTo")}
{
NameObject(GoToActionArguments.D): dest_array,
NameObject(GoToActionArguments.S): NameObject("/GoTo"),
}
)
action_ref = self._add_object(action)

Expand Down Expand Up @@ -1174,10 +1200,10 @@ def add_named_destination(self, title: str, pagenum: int) -> IndirectObject:
dest = DictionaryObject()
dest.update(
{
NameObject("/D"): ArrayObject(
[page_ref, NameObject("/FitH"), NumberObject(826)]
NameObject(GoToActionArguments.D): ArrayObject(
[page_ref, NameObject(TypFitArguments.FIT_H), NumberObject(826)]
),
NameObject("/S"): NameObject("/GoTo"),
NameObject(GoToActionArguments.S): NameObject("/GoTo"),
}
)

Expand Down Expand Up @@ -1407,12 +1433,14 @@ def add_uri(
lnk = DictionaryObject()
lnk.update(
{
NameObject("/Type"): NameObject(PG.ANNOTS),
NameObject("/Subtype"): NameObject("/Link"),
NameObject("/P"): page_link,
NameObject("/Rect"): rect,
NameObject(AnnotationDictionaryAttributes.Type): NameObject(PG.ANNOTS),
NameObject(AnnotationDictionaryAttributes.Subtype): NameObject("/Link"),
NameObject(AnnotationDictionaryAttributes.P): page_link,
NameObject(AnnotationDictionaryAttributes.Rect): rect,
NameObject("/H"): NameObject("/I"),
NameObject("/Border"): ArrayObject(border_arr),
NameObject(AnnotationDictionaryAttributes.Border): ArrayObject(
border_arr
),
NameObject("/A"): lnk2,
}
)
Expand Down
Loading