Skip to content

Commit

Permalink
initial implementation of ResourceCatalog
Browse files Browse the repository at this point in the history
  • Loading branch information
andersonhc committed Dec 29, 2024
1 parent 7f3f7a0 commit 87fc1a7
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 19 deletions.
11 changes: 11 additions & 0 deletions fpdf/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -1068,3 +1068,14 @@ def coerce(cls, value):
if isinstance(value, str):
value = value.upper()
return super(cls, cls).coerce(value)


class PDFResourceType(Enum):
EXT_G_STATE = intern("ExtGState")
COLOR_SPACE = intern("ColorSpece")
PATTERN = intern("Pattern")
SHADDING = intern("Shading")
X_OBJECT = intern("XObject")
FONT = intern("Font")
PROC_SET = intern("ProcSet")
PROPERTIES = intern("Properties")
21 changes: 8 additions & 13 deletions fpdf/fpdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class Image:
PageMode,
PageOrientation,
PathPaintRule,
PDFResourceType,
RenderStyle,
TextDirection,
TextEmphasis,
Expand Down Expand Up @@ -123,6 +124,7 @@ class Image:
OutputProducer,
PDFPage,
PDFPageLabel,
ResourceCatalog,
stream_content_for_raster_image,
)
from .recorder import FPDFRecorder
Expand Down Expand Up @@ -366,12 +368,8 @@ def __init__(
self._drawing_graphics_state_registry = GraphicsStateDictRegistry()
# map page numbers to a set of GraphicsState names:
self.graphics_style_names_per_page_number = defaultdict(set)
self.shadings_per_page_number = defaultdict(set)
self.patterns_per_page_number = defaultdict(set)
self._shading_registry = {}
self._pattern_registry = {}

self._record_text_quad_points = False
self._resource_catalog = ResourceCatalog()

# page number -> array of 8 × n numbers:
self._text_quad_points = defaultdict(list)
Expand Down Expand Up @@ -1275,14 +1273,12 @@ def use_pattern(self, shading):
"""
Create a context for using a shading pattern on the current page.
"""
if shading not in self._shading_registry:
self._shading_registry[shading] = f"Sh{len(self._shading_registry) + 1}"
self._resource_catalog.add(PDFResourceType.SHADDING, shading, self.page)
pattern = shading.get_pattern()
if pattern not in self._pattern_registry:
self._pattern_registry[pattern] = "P" + str(len(self._pattern_registry) + 1)
self._out(f"/Pattern cs /{self._pattern_registry[pattern]} scn")
self.shadings_per_page_number[self.page].add(self._shading_registry[shading])
self.patterns_per_page_number[self.page].add(self._pattern_registry[pattern])
pattern_name = self._resource_catalog.add(
PDFResourceType.PATTERN, pattern, self.page
)
self._out(f"/Pattern cs /{pattern_name} scn")
try:
yield
finally:
Expand Down Expand Up @@ -4875,7 +4871,6 @@ def _default_file_id(self, buffer):
# > When a file is first written, both identifiers shall be set to the same value.
id_hash = hashlib.new("md5", usedforsecurity=False) # nosec B324
id_hash.update(buffer)
print(str(buffer))
if self.creation_date:
id_hash.update(self.creation_date.strftime("%Y%m%d%H%M%S").encode("utf8"))
hash_hex = id_hash.hexdigest().upper()
Expand Down
53 changes: 48 additions & 5 deletions fpdf/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from fontTools import subset as ftsubset

from .annotations import PDFAnnotation
from .enums import PageLabelStyle, SignatureFlag
from .enums import PDFResourceType, PageLabelStyle, SignatureFlag
from .errors import FPDFException
from .line_break import TotalPagesSubstitutionFragment
from .image_datastructures import RasterImageInfo
Expand Down Expand Up @@ -432,6 +432,41 @@ def serialize(self, _security_handler=None):
return "\n".join(out)


class ResourceCatalog:
"Manage the indexing of resources and association to the pages they are used"

def __init__(self):
self.resources = defaultdict(dict)
self.resources_per_page = defaultdict(set)

def add(self, resource_type: PDFResourceType, resource, page_number: int):
registry = self.resources[resource_type]
if resource not in registry:
registry[resource] = f"{self._get_prefix(resource_type)}{len(registry) + 1}"
self.resources_per_page[(page_number, resource_type)].add(registry[resource])
return registry[resource]

def get_items(self, resource_type: PDFResourceType):
return self.resources[resource_type].items()

@classmethod
def _get_prefix(cls, resource_type: PDFResourceType):
match resource_type:
case PDFResourceType.EXT_G_STATE:
return "GS"
# COLOR_SPACE = intern("ColorSpece")
case PDFResourceType.PATTERN:
return "P"
case PDFResourceType.SHADDING:
return "Sh"
case PDFResourceType.X_OBJECT:
return "I"
case PDFResourceType.FONT:
return "F"
case _:
raise ValueError(f"No prefix for resource type {resource_type}")


class OutputProducer:
"Generates the final bytearray representing the PDF document, based on a FPDF instance."

Expand Down Expand Up @@ -893,7 +928,9 @@ def _add_gfxstates(self):

def _add_shadings(self):
shading_objs_per_name = OrderedDict()
for shading, name in self.fpdf._shading_registry.items():
for shading, name in self.fpdf._resource_catalog.get_items(
PDFResourceType.SHADDING
):
for function in shading.functions:
self._add_pdf_obj(function, "function")
shading_obj = shading.get_shading_object()
Expand All @@ -903,7 +940,9 @@ def _add_shadings(self):

def _add_patterns(self):
pattern_objs_per_name = OrderedDict()
for pattern, name in self.fpdf._pattern_registry.items():
for pattern, name in self.fpdf._resource_catalog.get_items(
PDFResourceType.PATTERN
):
self._add_pdf_obj(pattern, "pattern")
pattern_objs_per_name[name] = pattern
return pattern_objs_per_name
Expand Down Expand Up @@ -943,11 +982,15 @@ def _insert_resources(self, page_objs):
}
page_shading_objs_per_name = {
shading_name: shading_objs_per_name[shading_name]
for shading_name in self.fpdf.shadings_per_page_number[page_number]
for shading_name in self.fpdf._resource_catalog.resources_per_page[
(page_number, PDFResourceType.SHADDING)
]
}
page_pattern_objs_per_name = {
pattern_name: pattern_objs_per_name[pattern_name]
for pattern_name in self.fpdf.patterns_per_page_number[page_number]
for pattern_name in self.fpdf._resource_catalog.resources_per_page[
(page_number, PDFResourceType.PATTERN)
]
}
page_obj.resources = self._add_resources_dict(
page_font_objs_per_index,
Expand Down
4 changes: 3 additions & 1 deletion fpdf/pattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ def __init__(
super().__init__()
self.shading_type = shading_type
self.background = (
f'[{" ".join(f"{c:.2f}" for c in background.colors)}]' if background else None
f'[{" ".join(f"{c:.2f}" for c in background.colors)}]'
if background
else None
)
self.color_space = Name(color_space)
self.coords = coords
Expand Down

0 comments on commit 87fc1a7

Please sign in to comment.