Skip to content

Commit

Permalink
bugfix release
Browse files Browse the repository at this point in the history
  • Loading branch information
jorisschellekens committed Mar 27, 2021
1 parent 44413b1 commit c15d744
Show file tree
Hide file tree
Showing 500 changed files with 20,434 additions and 18,196 deletions.
177 changes: 105 additions & 72 deletions EXAMPLES.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
include ptext/pdf/canvas/font/afm/*.afm
include ptext/io/write/ascii_art/ascii_logo.txt
include ptext/io/write/ascii_art/ascii_logo.txt
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,8 @@ They include;
- adding lists to a PDF
- using a layout
and much more


## 2. Acknowledgements

I would like to thank the following people, for their contributions / advice with regards to developing `pText`:
- Michael Klink
142 changes: 31 additions & 111 deletions ptext/io/read/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,32 @@


def add_base_methods(object: typing.Any) -> typing.Any:
def _to_json_serializable(to_convert=None):
"""
Convert this object to a representation that
can be serialized as JSON
"""
if isinstance(to_convert, dict):
return {
to_json_serializable(k): to_json_serializable(v)
for k, v in to_convert.items()
}
if isinstance(to_convert, list):
return [to_json_serializable(x) for x in to_convert]
if isinstance(to_convert, Decimal):
return float(to_convert)
if (
isinstance(to_convert, HexadecimalString)
or isinstance(to_convert, String)
or isinstance(to_convert, Name)
or isinstance(to_convert, CanvasOperatorName)
):
return str(to_convert)
return None

def to_json_serializable(self):
return _to_json_serializable(self)

def image_hash_method(self):
w = self.width
h = self.height
Expand All @@ -36,6 +62,7 @@ def image_hash_method(self):
return hashcode

def deepcopy_mod(self, memodict={}):
print("copying %s" % self.__class__.__name__)
prev_function_ptr = self.__deepcopy__
self.__deepcopy__ = None
# copy
Expand Down Expand Up @@ -91,7 +118,7 @@ def event_occurred(self, event: "Event"): # type: ignore [name-defined]
return self

# set_reference
def set_reference(self, reference: "Reference") -> "BaseClass":
def set_reference(self, reference: "Reference"):
if "_reference" not in vars(self):
setattr(self, "_reference", None)
assert (
Expand All @@ -114,7 +141,7 @@ def get_reference(self) -> typing.Optional["Reference"]:
return self._reference

# set_can_be_referenced
def set_can_be_referenced(self, a_flag: bool) -> "BaseClass":
def set_can_be_referenced(self, a_flag: bool):
if "_can_be_referenced" not in vars(self):
setattr(self, "_can_be_referenced", None)
self._can_be_referenced = a_flag
Expand All @@ -136,119 +163,12 @@ def can_be_referenced(self) -> bool:
object.get_reference = types.MethodType(get_reference, object)
object.set_can_be_referenced = types.MethodType(set_can_be_referenced, object)
object.can_be_referenced = types.MethodType(can_be_referenced, object)
object.__deepcopy__ = types.MethodType(deepcopy_mod, object)
object.to_json_serializable = types.MethodType(to_json_serializable, object)
if isinstance(object, Image):
object.__deepcopy__ = types.MethodType(deepcopy_mod, object)
object.__hash__ = types.MethodType(image_hash_method, object)


class BaseClass:
def __init__(self):
self._parent: typing.Optional["BaseClass"] = None
self._event_listeners: typing.List[EventListener] = []
self._reference: typing.Optional["Reference"] = None
self._can_be_referenced: bool = True

def add_event_listener(self, event_listener: "EventListener") -> "BaseClass":
if "_event_listeners" not in vars(self):
setattr(self, "_event_listeners", [])
self._event_listeners.append(event_listener)
return self

def get_event_listeners(self) -> typing.List["EventListener"]:
if "_event_listeners" not in vars(self):
setattr(self, "_event_listeners", [])
return self._event_listeners

def event_occurred(self, event: "Event") -> "BaseClass": # type: ignore [name-defined]
if "_event_listeners" not in vars(self):
setattr(self, "_event_listeners", [])
for l in self._event_listeners:
l.event_occurred(event)
parent = self.get_parent()
if parent is not None:
parent.event_occurred(event)
return self

def get_parent(self) -> Optional["BaseClass"]:
"""
This function returns the parent object of this object
"""
if "_parent" not in vars(self):
setattr(self, "_parent", None)
return self._parent

def set_parent(self, parent: "BaseClass") -> "BaseClass":
"""
Set the parent object of this object
"""
if "_parent" not in vars(self):
setattr(self, "_parent", None)
self._parent = parent
return self

def get_root(self) -> Optional["BaseClass"]:
"""
This function returns the root object of this object
"""
e = self
while e.get_parent() is not None:
p = e.get_parent()
if p is not None:
e = p
return e

def set_reference(self, reference: "Reference") -> "BaseClass":
"""
This method sets the Reference for this Object
"""
if "_reference" not in vars(self):
setattr(self, "_reference", None)
self._reference = reference
return self

def get_reference(self) -> typing.Optional["Reference"]:
"""
This function returns the Reference for this Object
"""
if "_reference" not in vars(self):
setattr(self, "_reference", None)
return self._reference

def set_can_be_referenced(self, a_flag: bool) -> "BaseClass":
if "_can_be_referenced" not in vars(self):
setattr(self, "_can_be_referenced", None)
self._can_be_referenced = a_flag
return self

def can_be_referenced(self) -> bool:
if "_can_be_referenced" not in vars(self):
setattr(self, "_can_be_referenced", True)
return self._can_be_referenced

def to_json_serializable(self, to_convert=None):
"""
Convert this object to a representation that
can be serialized as JSON
"""
if isinstance(to_convert, dict):
return {
self.to_json_serializable(k): self.to_json_serializable(v)
for k, v in to_convert.items()
}
if isinstance(to_convert, list):
return [self.to_json_serializable(x) for x in to_convert]
if isinstance(to_convert, Decimal):
return float(to_convert)
if (
isinstance(to_convert, HexadecimalString)
or isinstance(to_convert, String)
or isinstance(to_convert, Name)
or isinstance(to_convert, CanvasOperatorName)
):
return str(to_convert)
return None


class Boolean:
def __init__(self, value: bool):
super(Boolean, self).__init__()
Expand Down
2 changes: 1 addition & 1 deletion ptext/io/write/ascii_art/ascii_logo.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pText version 1.6.1
pText version 1.8.2
Joris Schellekens
40 changes: 33 additions & 7 deletions ptext/io/write/image/write_image_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import io
from typing import Optional

from PIL.Image import Image # type: ignore [import]
from PIL import Image as PILImage # type: ignore [import]

from ptext.io.read.types import AnyPDFType, Name, Stream, Reference
from ptext.io.read.types import AnyPDFType, Name, Stream, Reference, add_base_methods
from ptext.io.read.types import Decimal as pDecimal
from ptext.io.write.write_base_transformer import (
WriteBaseTransformer,
Expand All @@ -23,7 +23,27 @@ class WriteImageTransformer(WriteBaseTransformer):
"""

def can_be_transformed(self, any: AnyPDFType):
return isinstance(any, Image)
return isinstance(any, PILImage.Image)

def _convert_png_to_jpg(self, image: PILImage.Image) -> PILImage.Image:

# omit transparency
fill_color = (255, 255, 255) # new background color
image_out = image.convert("RGBA") # it had mode P after DL it from OP
if image_out.mode in ("RGBA", "LA"):
background = PILImage.new(image_out.mode[:-1], image_out.size, fill_color)
background.paste(image_out, image_out.split()[-1]) # omit transparency
image_out = background

# convert to RGB
image_out = image_out.convert("RGB")

# add methods
add_base_methods(image_out)
image_out.set_reference(image.get_reference())

# return
return image_out

def transform(
self,
Expand All @@ -35,13 +55,14 @@ def transform(
"""
assert context is not None
assert context.destination is not None
assert isinstance(object_to_transform, Image)
assert isinstance(object_to_transform, PILImage.Image)

# get image bytes
contents = None
filter_name: Optional[Name] = None
try:
with io.BytesIO() as output:
assert isinstance(object_to_transform, PILImage.Image)
object_to_transform.save(output, format="JPEG")
contents = output.getvalue()
filter_name = Name("DCTDecode")
Expand All @@ -50,12 +71,14 @@ def transform(

if contents is None:
try:
# TODO : store PNG
# TODO : properly store PNG (instead of converting it)
with io.BytesIO() as output:
object_to_transform.convert("RGB").save(output, format="JPEG")
object_to_transform = self._convert_png_to_jpg(object_to_transform)
assert isinstance(object_to_transform, PILImage.Image)
object_to_transform.save(output, format="JPEG")
contents = output.getvalue()
filter_name = Name("DCTDecode")
except:
except Exception as e:
pass
assert contents is not None

Expand Down Expand Up @@ -84,7 +107,10 @@ def transform(
self.start_object(out_value, context)

# write stream
cl = context.compression_level
context.compression_level = 9
self.get_root_transformer().transform(out_value, context)
context.compression_level = cl

# end object if needed
if started_object:
Expand Down
25 changes: 24 additions & 1 deletion ptext/io/write/object/write_stream_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"""
import logging
import typing
import zlib
from typing import Optional

from ptext.io.read.types import (
Expand All @@ -14,7 +15,9 @@
Stream,
Reference,
List,
Name,
)
from ptext.io.read.types import Decimal as pDecimal
from ptext.io.write.write_base_transformer import (
WriteBaseTransformer,
WriteTransformerContext,
Expand All @@ -28,6 +31,9 @@ class WriteStreamTransformer(WriteBaseTransformer):
This implementation of WriteBaseTransformer is responsible for writing Stream objects
"""

def __init__(self):
super(WriteStreamTransformer, self).__init__()

def can_be_transformed(self, any: AnyPDFType):
return isinstance(any, Stream)

Expand Down Expand Up @@ -81,14 +87,31 @@ def transform(
else:
stream_dictionary[k] = v

# if self.compression_level == 0, remove \Filter
if context.compression_level == 0 and Name("Filter") in stream_dictionary:
stream_dictionary.pop(Name("Filter"))

# handle compression
if "DecodedBytes" in object_to_transform:
if context.compression_level == 0:
bts = object_to_transform["DecodedBytes"]
else:
bts = zlib.compress(
object_to_transform["DecodedBytes"], context.compression_level
)
stream_dictionary[Name("Length")] = pDecimal(len(bts))
else:
assert "Bytes" in object_to_transform
bts = object_to_transform["Bytes"]

# write stream dictionary
self.get_root_transformer().transform(stream_dictionary, context)

# write "stream"
context.destination.write(bytes("stream\n", "latin1"))

# write bytes
context.destination.write(object_to_transform["Bytes"])
context.destination.write(bts)

# write "endstream"
context.destination.write(bytes("\nendstream\n", "latin1"))
Expand Down
1 change: 1 addition & 0 deletions ptext/io/write/write_base_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def __init__(
self.resolved_references: typing.List[
Reference
] = [] # these references have already been written
self.compression_level = 9


class WriteBaseTransformer:
Expand Down
2 changes: 1 addition & 1 deletion ptext/io/write/write_pdf_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def _timestamp_to_str() -> str:
now = datetime.datetime.now()
for n in [now.year, now.month, now.day, now.hour, now.minute, now.second]:
timestamp_str += "{0:02}".format(n)
timestamp_str += "+00"
timestamp_str += "Z00"
return timestamp_str

@staticmethod
Expand Down
Loading

0 comments on commit c15d744

Please sign in to comment.