Skip to content

Commit

Permalink
Allow setting zip file generation options when generating opendocumen…
Browse files Browse the repository at this point in the history
…t files

Closes #72
  • Loading branch information
nicoe committed Sep 3, 2024
1 parent 2eee904 commit 9a20e30
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 7 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
* Allow setting zip file generation options on opendocument templates

0.10.2 - 20240421
-----------------
* Defer files added to serializer before call
Expand Down
26 changes: 26 additions & 0 deletions doc/indepthexample.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,32 @@ And thus here is our invoice, generated through relatorio:

.. image:: basic_generated.png

Advanced zip options
--------------------

It can happen that the document being generated result in a file which
uncompressed size is bigger than 2GiB. The default settings of Python's
`zipfile library`_ do not allow to generate those files.

To circumvent this issue, opendocument templates support additional parameters
to the ``generate`` method. Those parameters are:

:_relatorio_compresslevel: This parameter defines the *compresslevel* parameter
of the underlying Zipfile_ call.

:_relatorio_chunksize: This parameter defines the size of each chunk of data
streamed to the underlying Zipfile_ object.

:_relatorio_zip64: This parameter forces the handling of ZIP64 extensions.
It should be set to true if the size of the file may be
bigger than 2GiB.

:_relatorio_compression_method: This parameter defines the *compression*
paramater of the underlygin Zipfile_ call.

.. _`zipfile library`: https://docs.python.org/3/library/zipfile.html
.. _Zipfile: https://docs.python.org/3/library/zipfile.html#zipfile.ZipFile

One step further: OpenOffice Calc and OpenOffice Impress templates
------------------------------------------------------------------

Expand Down
32 changes: 25 additions & 7 deletions relatorio/templates/opendocument.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,9 +859,19 @@ def _guess_type(self, val):
attrs['{%s}value-type' % self.namespaces['calcext']] = type_
return attrs

def generate(self, *args, **kwargs):
def generate(self, *args,
_relatorio_compresslevel=None,
_relatorio_chunksize=64,
_relatorio_zip64=False,
_relatorio_compression_method=zipfile.ZIP_DEFLATED,
**kwargs):
"creates the RelatorioStream."
serializer = OOSerializer(self._source, self._files)
serializer = OOSerializer(
self._source, self._files,
compresslevel=_relatorio_compresslevel,
zip64=_relatorio_zip64,
chunksize=_relatorio_chunksize,
compression_method=_relatorio_compression_method)
kwargs['__relatorio_make_href'] = ImageHref(serializer, kwargs)
kwargs['__relatorio_make_dimension'] = ImageDimension(self.namespaces)
kwargs['__relatorio_guess_type'] = self._guess_type
Expand Down Expand Up @@ -1039,9 +1049,10 @@ def remove_file_entry(self, path):


class _AbstractZipWriteSplitStream(object):
def __init__(self, zipfile, chunksize=64):
def __init__(self, zipfile, chunksize=64, zip64=False):
self.zipfile = zipfile
self.chunksize = chunksize
self.zip64 = zip64

def open(self, zinfo):
raise NotImplementedError
Expand Down Expand Up @@ -1088,7 +1099,8 @@ def write(self, data):

def flush(self):
if not self._fp:
self._fp = self.zipfile.open(self._zinfo, mode='w')
self._fp = self.zipfile.open(
self._zinfo, mode='w', force_zip64=self.zip64)
self._fp.write(b''.join(self._buffer))
self._buffer.clear()
else:
Expand Down Expand Up @@ -1116,12 +1128,17 @@ def write(self, data):

class OOSerializer:

def __init__(self, source, files, chunksize=64):
def __init__(self, source, files, chunksize=64,
compresslevel=None, zip64=False,
compression_method=zipfile.ZIP_DEFLATED):
self.inzip = get_zip_file(source)
self.manifest = Manifest(self.inzip.read(MANIFEST))
self.xml_serializer = genshi.output.XMLSerializer()
self._files = files
self.chunksize = chunksize
self.compresslevel = None
self.zip64 = zip64
self.compression_method = compression_method
self.outzip = None
self._deferred = []

Expand All @@ -1131,7 +1148,8 @@ def __call__(self, stream, method=None, encoding='utf-8', out=None):
else:
result = out
self.outzip = zipfile.ZipFile(
result, mode='w', compression=zipfile.ZIP_DEFLATED)
result, mode='w', compression=self.compression_method,
compresslevel=self.compresslevel)
files = {}
now = time.localtime()[:6]
manifest_info = None
Expand All @@ -1152,7 +1170,7 @@ def __call__(self, stream, method=None, encoding='utf-8', out=None):
else:
self.outzip.writestr(f_info, self.inzip.read(f_info.filename))

writer = _ZipWriteSplitStream(self.outzip, self.chunksize)
writer = _ZipWriteSplitStream(self.outzip, self.chunksize, self.zip64)
output_encode(
self.xml_serializer(writer(stream)), encoding=encoding, out=writer)

Expand Down

0 comments on commit 9a20e30

Please sign in to comment.