Skip to content

Commit

Permalink
BUG: Handle IndirectObject in CCITTFaxDecode filter (#2965)
Browse files Browse the repository at this point in the history
* BUG: Handle IndirectObject in CCITTFaxDecode filter

* fix types

* further typing fix

* add test

* add missing annotation

* update parameter order to keep backwards compatibility
  • Loading branch information
stefan6419846 authored Dec 25, 2024
1 parent 1951fdf commit 6d4e2ec
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 16 deletions.
29 changes: 15 additions & 14 deletions pypdf/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import struct
import zlib
from base64 import a85decode
from dataclasses import dataclass
from io import BytesIO
from typing import Any, Dict, List, Optional, Tuple, Union, cast

Expand Down Expand Up @@ -477,17 +478,17 @@ def decode(
return data


@dataclass
class CCITParameters:
"""§7.4.6, optional parameters for the CCITTFaxDecode filter."""

def __init__(self, K: int = 0, columns: int = 0, rows: int = 0) -> None:
self.K = K
self.EndOfBlock = None
self.EndOfLine = None
self.EncodedByteAlign = None
self.columns = columns # width
self.rows = rows # height
self.DamagedRowsBeforeError = None
K: int = 0
columns: int = 0
rows: int = 0
EndOfBlock: Union[int, None] = None
EndOfLine: Union[int, None] = None
EncodedByteAlign: Union[int, None] = None
DamagedRowsBeforeError: Union[int, None] = None

@property
def group(self) -> int:
Expand All @@ -513,7 +514,7 @@ class CCITTFaxDecode:
@staticmethod
def _get_parameters(
parameters: Union[None, ArrayObject, DictionaryObject, IndirectObject],
rows: int,
rows: Union[int, IndirectObject],
) -> CCITParameters:
# §7.4.6, optional parameters for the CCITTFaxDecode filter
k = 0
Expand All @@ -525,16 +526,16 @@ def _get_parameters(
if isinstance(parameters_unwrapped, ArrayObject):
for decode_parm in parameters_unwrapped:
if CCITT.COLUMNS in decode_parm:
columns = decode_parm[CCITT.COLUMNS]
columns = decode_parm[CCITT.COLUMNS].get_object()
if CCITT.K in decode_parm:
k = decode_parm[CCITT.K]
k = decode_parm[CCITT.K].get_object()
else:
if CCITT.COLUMNS in parameters_unwrapped:
columns = parameters_unwrapped[CCITT.COLUMNS] # type: ignore
columns = parameters_unwrapped[CCITT.COLUMNS].get_object() # type: ignore
if CCITT.K in parameters_unwrapped:
k = parameters_unwrapped[CCITT.K] # type: ignore
k = parameters_unwrapped[CCITT.K].get_object() # type: ignore

return CCITParameters(k, columns, rows)
return CCITParameters(K=k, columns=columns, rows=int(rows))

@staticmethod
def decode(
Expand Down
4 changes: 4 additions & 0 deletions pypdf/generic/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,10 @@ def __float__(self) -> str:
# in this case we are looking for the pointed data
return self.get_object().__float__() # type: ignore

def __int__(self) -> int:
# in this case we are looking for the pointed data
return self.get_object().__int__() # type: ignore

def __str__(self) -> str:
# in this case we are looking for the pointed data
return self.get_object().__str__()
Expand Down
15 changes: 13 additions & 2 deletions tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
CCITTFaxDecode,
FlateDecode,
)
from pypdf.generic import ArrayObject, DictionaryObject, NameObject, NumberObject
from pypdf.generic import ArrayObject, DictionaryObject, IndirectObject, NameObject, NumberObject

from . import PILContext, get_data_from_url
from .test_encryption import HAS_AES
Expand Down Expand Up @@ -196,14 +196,25 @@ def test_ccitparameters():
("parameters", "expected_k"),
[
(None, 0),
(ArrayObject([{"/K": 1}, {"/Columns": 13}]), 1),
(ArrayObject([{"/K": NumberObject(1)}, {"/Columns": NumberObject(13)}]), 1),
],
)
def test_ccitt_get_parameters(parameters, expected_k):
parameters = CCITTFaxDecode._get_parameters(parameters=parameters, rows=0)
assert parameters.K == expected_k # noqa: SIM300


def test_ccitt_get_parameters__indirect_object():
class Pdf:
def get_object(self, reference) -> NumberObject:
return NumberObject(42)

parameters = CCITTFaxDecode._get_parameters(
parameters=None, rows=IndirectObject(13, 1, Pdf())
)
assert parameters.rows == 42


def test_ccitt_fax_decode():
data = b""
parameters = DictionaryObject(
Expand Down

0 comments on commit 6d4e2ec

Please sign in to comment.