Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to save a presentation to a file-like object in Python 3

Python 3 replaced StringIO.StringIO with io.StringIO. I've been able to successfully save presentations using the former, but it doesn't appear to work for the latter.

from pptx import Presentation
from io import StringIO

presentation = Presentation('presentation.pptx')
output = StringIO()
presentation.save(output)

The above code produces:

Traceback (most recent call last): File "<stdin>", line 1, in <module> File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\presentation.py", line 46, in save self.part.save(file) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\parts\presentation.py", line 118, in save self.package.save(path_or_stream) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\opc\package.py", line 166, in save PackageWriter.write(pkg_file, self.rels, self.parts) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\opc\pkgwriter.py", line 33, in write PackageWriter._write_content_types_stream(phys_writer, parts) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\opc\pkgwriter.py", line 47, in _write_content_types_stream phys_writer.write(CONTENT_TYPES_URI, content_types_blob) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\site-packages\pptx\opc\phys_pkg.py", line 156, in write self._zipf.writestr(pack_uri.membername, blob) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\zipfile.py", line 1645, in writestr with self.open(zinfo, mode='w') as dest: File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\zipfile.py", line 1349, in open return self._open_to_write(zinfo, force_zip64=force_zip64) File "C:\Users\mgplante\AppData\Local\Continuum\Anaconda2\envs\ppt_gen\lib\zipfile.py", line 1462, in _open_to_write self.fp.write(zinfo.FileHeader(zip64)) TypeError: string argument expected, got 'bytes'

Is there a way to save a presentation to to a file-like object in Python 3, or am I going to have to use Python 2 for this project?

like image 231
MichaelPlante Avatar asked Oct 27 '17 16:10

MichaelPlante


3 Answers

How about BytesIO()?

from pptx import Presentation
from io import BytesIO

presentation = Presentation('presentation.pptx')
output = BytesIO()
presentation.save(output)
output.seek(0)
# from here do what you like with output, e.g. pass it to something expecting bytes with output.read()

This removes the error at least.

like image 166
Hannu Avatar answered Oct 24 '22 16:10

Hannu


Hannu's answer is quite right, and is precisely the code that is used to verify this behavior in the test suite for python-pptx:

stream = BytesIO()
presentation.save(stream)

https://github.com/scanny/python-pptx/blob/master/features/steps/presentation.py#L105

If that code is giving you a blank presentation, then something else is going on. I would reproduce that behavior, get it stable and repeatable, then ask the question to the effect "Why am I getting a blank presentation?" in another SO question, posting with it the full minimum code that gives you that behavior.

This is the second time I've heard of something like this, which makes me suspect there's actually something half-way systematic happening under the covers to produce this behavior. But at the same time, it's extremely unlikely that you would end up with a fully working presentation, just empty of slides, as a partial failure of attempting a save to a stream.

A common situation that could lead to this is saving a newly-opened default presentation, like:

prs = Presentation()
output = BytesIO()
prs.save(output)

This of course is not something you would likely do on purpose, but easy enough to do by accident, so I thought I'd mention.

If you can help us repeat your result we'll get it figured out :)

like image 21
scanny Avatar answered Oct 24 '22 15:10

scanny


I faced the same problem while developing CGI-like presentation. My pptx shoud be sent as a file to user. You can send pptx as a file using BytesIO, not StringIO

qs = cgi.FieldStorage()
link = qs.getfirst('link', 'default_link_for_debug')

...
...

target_stream = BytesIO()
prs.save(target_stream)
target_stream.seek(0) # important!

length = target_stream.getbuffer().nbytes
buffer = target_stream.getbuffer()
#buffer = target_stream.read() # it also works

if 'HTTP_HOST' in os.environ:
    sys.stdout.buffer.write(b'Content-Type: application/vnd.openxmlformats-officedocument.presentationml.presentation\r\n')
    sys.stdout.buffer.write('Content-Disposition: attachment; filename="offer-{0}.pptx"\r\n'.format(link).encode('ascii'))
    sys.stdout.buffer.write('Content-Length: {0}\r\n'.format(length).encode('ascii'))
    sys.stdout.buffer.write(b'Pragma: no-cache\r\n')
    sys.stdout.buffer.write(b'\r\n')
    sys.stdout.buffer.write(buffer)

else: # for debug
    with open("offer-{0}.pptx".format(link),'wb') as out: 
        out.write(buffer) 
like image 2
Павел П Avatar answered Oct 24 '22 17:10

Павел П