mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-26 18:06:18 +03:00
365 lines
11 KiB
ReStructuredText
365 lines
11 KiB
ReStructuredText
.. py:module:: PIL.OleFileIO
|
|
.. py:currentmodule:: PIL.OleFileIO
|
|
|
|
:py:mod:`OleFileIO` Module
|
|
===========================
|
|
|
|
The :py:mod:`OleFileIO` module reads Microsoft OLE2 files (also called
|
|
Structured Storage or Microsoft Compound Document File Format), such
|
|
as Microsoft Office documents, Image Composer and FlashPix files, and
|
|
Outlook messages.
|
|
|
|
This module is the `OleFileIO\_PL`_ project by Philippe Lagadec, v0.30,
|
|
merged back into Pillow.
|
|
|
|
.. _OleFileIO\_PL: http://www.decalage.info/python/olefileio
|
|
|
|
How to use this module
|
|
----------------------
|
|
|
|
For more information, see also the file **PIL/OleFileIO.py**, sample
|
|
code at the end of the module itself, and docstrings within the code.
|
|
|
|
About the structure of OLE files
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
An OLE file can be seen as a mini file system or a Zip archive: It
|
|
contains **streams** of data that look like files embedded within the
|
|
OLE file. Each stream has a name. For example, the main stream of a MS
|
|
Word document containing its text is named "WordDocument".
|
|
|
|
An OLE file can also contain **storages**. A storage is a folder that
|
|
contains streams or other storages. For example, a MS Word document with
|
|
VBA macros has a storage called "Macros".
|
|
|
|
Special streams can contain **properties**. A property is a specific
|
|
value that can be used to store information such as the metadata of a
|
|
document (title, author, creation date, etc). Property stream names
|
|
usually start with the character '05'.
|
|
|
|
For example, a typical MS Word document may look like this:
|
|
|
|
::
|
|
|
|
\x05DocumentSummaryInformation (stream)
|
|
\x05SummaryInformation (stream)
|
|
WordDocument (stream)
|
|
Macros (storage)
|
|
PROJECT (stream)
|
|
PROJECTwm (stream)
|
|
VBA (storage)
|
|
Module1 (stream)
|
|
ThisDocument (stream)
|
|
_VBA_PROJECT (stream)
|
|
dir (stream)
|
|
ObjectPool (storage)
|
|
|
|
Test if a file is an OLE container
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Use isOleFile to check if the first bytes of the file contain the Magic
|
|
for OLE files, before opening it. isOleFile returns True if it is an OLE
|
|
file, False otherwise.
|
|
|
|
.. code-block:: python
|
|
|
|
assert OleFileIO.isOleFile('myfile.doc')
|
|
|
|
Open an OLE file from disk
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Create an OleFileIO object with the file path as parameter:
|
|
|
|
.. code-block:: python
|
|
|
|
ole = OleFileIO.OleFileIO('myfile.doc')
|
|
|
|
Open an OLE file from a file-like object
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
This is useful if the file is not on disk, e.g. already stored in a
|
|
string or as a file-like object.
|
|
|
|
.. code-block:: python
|
|
|
|
ole = OleFileIO.OleFileIO(f)
|
|
|
|
For example the code below reads a file into a string, then uses BytesIO
|
|
to turn it into a file-like object.
|
|
|
|
.. code-block:: python
|
|
|
|
data = open('myfile.doc', 'rb').read()
|
|
f = io.BytesIO(data) # or StringIO.StringIO for Python 2.x
|
|
ole = OleFileIO.OleFileIO(f)
|
|
|
|
How to handle malformed OLE files
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
By default, the parser is configured to be as robust and permissive as
|
|
possible, allowing to parse most malformed OLE files. Only fatal errors
|
|
will raise an exception. It is possible to tell the parser to be more
|
|
strict in order to raise exceptions for files that do not fully conform
|
|
to the OLE specifications, using the raise\_defect option:
|
|
|
|
.. code-block:: python
|
|
|
|
ole = OleFileIO.OleFileIO('myfile.doc', raise_defects=DEFECT_INCORRECT)
|
|
|
|
When the parsing is done, the list of non-fatal issues detected is
|
|
available as a list in the parsing\_issues attribute of the OleFileIO
|
|
object:
|
|
|
|
.. code-block:: python
|
|
|
|
print('Non-fatal issues raised during parsing:')
|
|
if ole.parsing_issues:
|
|
for exctype, msg in ole.parsing_issues:
|
|
print('- %s: %s' % (exctype.__name__, msg))
|
|
else:
|
|
print('None')
|
|
|
|
Syntax for stream and storage path
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Two different syntaxes are allowed for methods that need or return the
|
|
path of streams and storages:
|
|
|
|
1) Either a **list of strings** including all the storages from the root
|
|
up to the stream/storage name. For example a stream called
|
|
"WordDocument" at the root will have ['WordDocument'] as full path. A
|
|
stream called "ThisDocument" located in the storage "Macros/VBA" will
|
|
be ['Macros', 'VBA', 'ThisDocument']. This is the original syntax
|
|
from PIL. While hard to read and not very convenient, this syntax
|
|
works in all cases.
|
|
|
|
2) Or a **single string with slashes** to separate storage and stream
|
|
names (similar to the Unix path syntax). The previous examples would
|
|
be 'WordDocument' and 'Macros/VBA/ThisDocument'. This syntax is
|
|
easier, but may fail if a stream or storage name contains a slash.
|
|
|
|
Both are case-insensitive.
|
|
|
|
Switching between the two is easy:
|
|
|
|
.. code-block:: python
|
|
|
|
slash_path = '/'.join(list_path)
|
|
list_path = slash_path.split('/')
|
|
|
|
Get the list of streams
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
listdir() returns a list of all the streams contained in the OLE file,
|
|
including those stored in storages. Each stream is listed itself as a
|
|
list, as described above.
|
|
|
|
.. code-block:: python
|
|
|
|
print(ole.listdir())
|
|
|
|
Sample result:
|
|
|
|
.. code-block:: python
|
|
|
|
[['\x01CompObj'], ['\x05DocumentSummaryInformation'], ['\x05SummaryInformation']
|
|
, ['1Table'], ['Macros', 'PROJECT'], ['Macros', 'PROJECTwm'], ['Macros', 'VBA',
|
|
'Module1'], ['Macros', 'VBA', 'ThisDocument'], ['Macros', 'VBA', '_VBA_PROJECT']
|
|
, ['Macros', 'VBA', 'dir'], ['ObjectPool'], ['WordDocument']]
|
|
|
|
As an option it is possible to choose if storages should also be listed,
|
|
with or without streams:
|
|
|
|
.. code-block:: python
|
|
|
|
ole.listdir (streams=False, storages=True)
|
|
|
|
Test if known streams/storages exist:
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
exists(path) checks if a given stream or storage exists in the OLE file.
|
|
|
|
.. code-block:: python
|
|
|
|
if ole.exists('worddocument'):
|
|
print("This is a Word document.")
|
|
if ole.exists('macros/vba'):
|
|
print("This document seems to contain VBA macros.")
|
|
|
|
Read data from a stream
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
openstream(path) opens a stream as a file-like object.
|
|
|
|
The following example extracts the "Pictures" stream from a PPT file:
|
|
|
|
.. code-block:: python
|
|
|
|
pics = ole.openstream('Pictures')
|
|
data = pics.read()
|
|
|
|
|
|
Get information about a stream/storage
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Several methods can provide the size, type and timestamps of a given
|
|
stream/storage:
|
|
|
|
get\_size(path) returns the size of a stream in bytes:
|
|
|
|
.. code-block:: python
|
|
|
|
s = ole.get_size('WordDocument')
|
|
|
|
get\_type(path) returns the type of a stream/storage, as one of the
|
|
following constants: STGTY\_STREAM for a stream, STGTY\_STORAGE for a
|
|
storage, STGTY\_ROOT for the root entry, and False for a non existing
|
|
path.
|
|
|
|
.. code-block:: python
|
|
|
|
t = ole.get_type('WordDocument')
|
|
|
|
get\_ctime(path) and get\_mtime(path) return the creation and
|
|
modification timestamps of a stream/storage, as a Python datetime object
|
|
with UTC timezone. Please note that these timestamps are only present if
|
|
the application that created the OLE file explicitly stored them, which
|
|
is rarely the case. When not present, these methods return None.
|
|
|
|
.. code-block:: python
|
|
|
|
c = ole.get_ctime('WordDocument')
|
|
m = ole.get_mtime('WordDocument')
|
|
|
|
The root storage is a special case: You can get its creation and
|
|
modification timestamps using the OleFileIO.root attribute:
|
|
|
|
.. code-block:: python
|
|
|
|
c = ole.root.getctime()
|
|
m = ole.root.getmtime()
|
|
|
|
Extract metadata
|
|
~~~~~~~~~~~~~~~~
|
|
|
|
get\_metadata() will check if standard property streams exist, parse all
|
|
the properties they contain, and return an OleMetadata object with the
|
|
found properties as attributes.
|
|
|
|
.. code-block:: python
|
|
|
|
meta = ole.get_metadata()
|
|
print('Author:', meta.author)
|
|
print('Title:', meta.title)
|
|
print('Creation date:', meta.create_time)
|
|
# print all metadata:
|
|
meta.dump()
|
|
|
|
Available attributes include:
|
|
|
|
::
|
|
|
|
codepage, title, subject, author, keywords, comments, template,
|
|
last_saved_by, revision_number, total_edit_time, last_printed, create_time,
|
|
last_saved_time, num_pages, num_words, num_chars, thumbnail,
|
|
creating_application, security, codepage_doc, category, presentation_target,
|
|
bytes, lines, paragraphs, slides, notes, hidden_slides, mm_clips,
|
|
scale_crop, heading_pairs, titles_of_parts, manager, company, links_dirty,
|
|
chars_with_spaces, unused, shared_doc, link_base, hlinks, hlinks_changed,
|
|
version, dig_sig, content_type, content_status, language, doc_version
|
|
|
|
See the source code of the OleMetadata class for more information.
|
|
|
|
Parse a property stream
|
|
~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
get\_properties(path) can be used to parse any property stream that is
|
|
not handled by get\_metadata. It returns a dictionary indexed by
|
|
integers. Each integer is the index of the property, pointing to its
|
|
value. For example in the standard property stream
|
|
'05SummaryInformation', the document title is property #2, and the
|
|
subject is #3.
|
|
|
|
.. code-block:: python
|
|
|
|
p = ole.getproperties('specialprops')
|
|
|
|
By default as in the original PIL version, timestamp properties are
|
|
converted into a number of seconds since Jan 1,1601. With the option
|
|
convert\_time, you can obtain more convenient Python datetime objects
|
|
(UTC timezone). If some time properties should not be converted (such as
|
|
total editing time in '05SummaryInformation'), the list of indexes can
|
|
be passed as no\_conversion:
|
|
|
|
.. code-block:: python
|
|
|
|
p = ole.getproperties('specialprops', convert_time=True, no_conversion=[10])
|
|
|
|
Close the OLE file
|
|
~~~~~~~~~~~~~~~~~~
|
|
|
|
Unless your application is a simple script that terminates after
|
|
processing an OLE file, do not forget to close each OleFileIO object
|
|
after parsing to close the file on disk.
|
|
|
|
.. code-block:: python
|
|
|
|
ole.close()
|
|
|
|
Use OleFileIO as a script
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
OleFileIO can also be used as a script from the command-line to
|
|
display the structure of an OLE file and its metadata, for example:
|
|
|
|
::
|
|
|
|
PIL/OleFileIO.py myfile.doc
|
|
|
|
You can use the option -c to check that all streams can be read fully,
|
|
and -d to generate very verbose debugging information.
|
|
|
|
How to contribute
|
|
-----------------
|
|
|
|
The code is available in `a Mercurial repository on
|
|
bitbucket <https://bitbucket.org/decalage/olefileio_pl>`_. You may use
|
|
it to submit enhancements or to report any issue.
|
|
|
|
If you would like to help us improve this module, or simply provide
|
|
feedback, please `contact me <http://decalage.info/contact>`_. You can
|
|
help in many ways:
|
|
|
|
- test this module on different platforms / Python versions
|
|
- find and report bugs
|
|
- improve documentation, code samples, docstrings
|
|
- write unittest test cases
|
|
- provide tricky malformed files
|
|
|
|
How to report bugs
|
|
------------------
|
|
|
|
To report a bug, for example a normal file which is not parsed
|
|
correctly, please use the `issue reporting
|
|
page <https://bitbucket.org/decalage/olefileio_pl/issues?status=new&status=open>`_,
|
|
or if you prefer to do it privately, use this `contact
|
|
form <http://decalage.info/contact>`_. Please provide all the
|
|
information about the context and how to reproduce the bug.
|
|
|
|
If possible please join the debugging output of OleFileIO. For this,
|
|
launch the following command :
|
|
|
|
::
|
|
|
|
PIL/OleFileIO.py -d -c file >debug.txt
|
|
|
|
|
|
Classes and Methods
|
|
-------------------
|
|
|
|
.. automodule:: PIL.OleFileIO
|
|
:members:
|
|
:undoc-members:
|
|
:show-inheritance:
|
|
:noindex:
|