Skip to content

Commit 5aeb926

Browse files
BUG: Format floats using their intrinsic decimal precision (#1267)
Since FloatObject is represented as a decimal, format numbers using their intrinsic precision, instead of reducing the precision to 5 decimal places. This fixes rendering issues for PDFs that contain coordinates, transformations, etc. with real numbers containing more than 5 decimal places of precision. For example, PDFs exported from Microsoft PowerPoint contain numbers with up to 11 decimal places. Fixes #1266
1 parent 71de6c8 commit 5aeb926

File tree

3 files changed

+40
-7
lines changed

3 files changed

+40
-7
lines changed

PyPDF2/_writer.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1921,8 +1921,11 @@ def _create_outline_item(
19211921
if color:
19221922
if isinstance(color, str):
19231923
color = hex_to_rgb(color)
1924+
prec = decimal.Decimal('1.00000')
19241925
outline_item.update(
1925-
{NameObject("/C"): ArrayObject([FloatObject(c) for c in color])}
1926+
{NameObject("/C"): ArrayObject([
1927+
FloatObject(decimal.Decimal(c).quantize(prec)) for c in color
1928+
])}
19261929
)
19271930
if italic or bold:
19281931
format_flag = 0

PyPDF2/generic/_base.py

+4-6
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,12 @@ def __new__(
241241

242242
def __repr__(self) -> str:
243243
if self == self.to_integral():
244+
# If this is an integer, format it with no decimal place.
244245
return str(self.quantize(decimal.Decimal(1)))
245246
else:
246-
# Standard formatting adds useless extraneous zeros.
247-
o = f"{self:.5f}"
248-
# Remove the zeros.
249-
while o and o[-1] == "0":
250-
o = o[:-1]
251-
return o
247+
# Otherwise, format it with a decimal place, taking care to
248+
# remove any extraneous trailing zeros.
249+
return f"{self:f}".rstrip("0")
252250

253251
def as_numeric(self) -> float:
254252
return float(repr(self).encode("utf8"))

tests/test_generic.py

+32
Original file line numberDiff line numberDiff line change
@@ -869,3 +869,35 @@ def test_create_string_object_force():
869869
assert create_string_object(b"Hello World", []) == "Hello World"
870870
assert create_string_object(b"Hello World", {72: "A"}) == "Aello World"
871871
assert create_string_object(b"Hello World", "utf8") == "Hello World"
872+
873+
874+
@pytest.mark.parametrize(
875+
("value", "expected"),
876+
[
877+
("0.000000", "0"),
878+
("0.0", "0"),
879+
("1.0", "1"),
880+
("0.123000", "0.123"),
881+
("0.000123000", "0.000123"),
882+
("0.0", "0"),
883+
("0", "0"),
884+
("1", "1"),
885+
("1.0", "1"),
886+
("1.01", "1.01"),
887+
("1.010", "1.01"),
888+
("0000.0000", "0"),
889+
("0.10101010", "0.1010101"),
890+
("50000000000", "50000000000"),
891+
("99900000000000000123", "99900000000000000123"),
892+
("99900000000000000123.456000", "99900000000000000123.456"),
893+
("0.00000000000000000000123", "0.00000000000000000000123"),
894+
("0.00000000000000000000123000", "0.00000000000000000000123"),
895+
("50032481330523882508234.00000000000000000000123000", "50032481330523882508234.00000000000000000000123"),
896+
(
897+
"928457298572093487502198745102973402987412908743.75249875981374981237498213740000",
898+
"928457298572093487502198745102973402987412908743.7524987598137498123749821374"
899+
),
900+
]
901+
)
902+
def test_float_object_decimal_to_string(value, expected):
903+
assert repr(FloatObject(value)) == expected

0 commit comments

Comments
 (0)