General Document Management

How to open an ODF document?

doc = ezodf.opendoc(filename)

see Open an existing Document

How to open an ODF document from a file-like object?

stream is a file-like object, opened in binary mode:

buffer =
doc =

How to create a new ODF document?

doc = ezodf.newdoc(doctype, filename)

see Create a new Document

How to save the document?

# save with filename given at opendoc() or newdoc()
# save with a new name
doc.saveas(filename, backup=True)

How to manage the document without filesystem access?

You can also get the document zip-file as bytes.

# get content as bytes
buffer = doc.tobytes()
# and reopen the document
doc = ezodf.opendoc(buffer)
# or use it as template for a new document
doc = ezodf.newdoc(template=buffer)

Where is the document content?

All ODF objects, like Paragraph or Heading, resides in the body attribute of the document object. All data management function are method calls of this object.

How to append/insert ODF objects to a document?

Use the body attribute of the document object:

p1 = doc.body.append(Paragraph('text1'))
p2 = doc.body.insert_before(p1, Paragraph('text2'))

# insert object at 'position'
doc.body.insert(0, Paragraph('New first paragraph.'))

How to get ODF objects from a document?

  1. You can iterate over all objects at the top-level of the body object:

    for obj in doc.body:
        # do something
  2. If you know the position of the object, you can access the object by the subscript operator:

    p1 = doc.body[0] # first object of the document
    count = len(doc.body) # get count of the top-level objects
  3. If you look for an specific object type, use the filter() method to search for all child objects selected by the kind attribute:

    # kind is the name of the element wrapper class
    paragraphs = doc.body.filter(kind='Paragraph')

    or use the findall() method to find all children by their XML tag (in Clark Notation, see CN() function), this is a wrapper for the lxml.Element.findall() method (see lxml API Reference):

    paragraphs = doc.body.findall(CN('text:p'))

How to get the position of an object?

pos = doc.body.index(p1)

# get the previous object of p1
prev = doc.body[pos-1]

Text Documents


# create a new text document
doc = ezodf.newdoc(doctype='odt', filename='text.odt')
# or open an existing text document
doc = ezodf.opendoc('text.odt')

How to add a heading?

Add Heading object to the body attribute of the document:

doc.body.append(Heading('A text paragraph.')

How to add a text paragraph?

Add Paragraph object to the body attribute of the document:

doc.body.append(Paragraph('A text paragraph.')

How to insert a page break?

Add SoftPageBreak object to heading or paragraph:

p = doc.body.append(Paragraph("some text"))

How to create a simple list?

Use the ezodf.ezlist() function, creates an unnumbered list as default, use the style_name parameter to use a different list-style:

ulist = ezodf.ezlist(['Point 1', 'Point 2', 'Point 3'])

Spreadsheet Documents


from ezodf import newdoc, opendoc, Sheet

# create a new spreadsheet document
doc = newdoc(doctype='ods', filename='spreadsheet.ods')
# or open an existing spreadsheet document
doc = opendoc('spreadsheet.ods')

Managing Sheets

How to add a new sheet?

Sheets are Sheet objects and resides in the sheets attribute of the spreadsheet-document:

# append new sheets at the end of the document
doc.sheets += Sheet('ANewSheet')
# or insert the new sheet at an arbitary position
doc.sheet.insert(1, Sheet('AsSecondSheet'))

How to get sheets from document?

You can get sheets by index or by name:

# get first sheet
sheet = doc.sheets[0]
# get last sheet
sheet = doc.sheets[-1]
# get sheet by name
sheet = doc.sheets['ANewSheet']

# iterate over sheets
for sheet in doc.sheets:

How to get position of a sheet?

index = doc.sheets.index(sheet)

# get count of sheets
count = len(doc.sheets)

How to delete a sheet?

del doc.sheets[0]

How to replace a sheet?

doc.sheets[0] = Sheet('ReplaceFirstSheet')

How to move a sheet?

To move a sheet in front of another sheet just insert the moving sheet:

sheet1 = doc.sheets[0]
sheet2 = doc.sheets[1]
# move sheet2 in front of sheet1
doc.sheets.insert(0, sheet2)

You can insert a sheet as often you want, there is alway just one instance of the sheet in the document. You can also move a sheet to another document, but referenced styles will not be copied automatically:

sheet = doc1.sheets[0]

How to copy a sheet?

Make a copy of the sheet and insert or append the copy:

duplicate = sheet.copy(newname='CopyOf'
doc.sheets += duplicate

How to get all sheet names?

names = doc.sheets.names() # returns a generator object

Managing Sheet Geometry


sheet = doc.sheets[0]

How to get sheet metrics?

count_of_rows = sheet.nrows()
count_of_colmns = sheet.ncols()
name =

How to insert/append new rows/columns?


insert operations break cell references in formulas

Appending new empty rows/columns:


Inserting new empty rows/columns at position index:

sheet.insert_rows(index=3, count=2)
sheet.insert_columns(index=3, count=2)

How to delete rows/columns?


delete operations break cell references in formulas

sheet.delete_rows(index=3, count=2)
sheet.delete_columns(index=3, count=2)

Managing Sheet Content


sheet = doc.sheets[0]

How to reference cells?

Cells are referenced by a (row, column) tuple or by classsic spreadsheet references like 'A1' for cell (0, 0), letters stands for columns, numbers stands for rows, and as you see the row/column index is zero-based, where classic references start with row = '1' and column = 'A'.

How to get the cell content?

The cell content is manged by the Cell class:

cell = sheet['A1']
value = cell.value
value_type = cell.value_type

value = sheet['A1'].value
  • for 'string', 'date' and 'time': you get str objects
  • for 'float', 'precentage' and 'currency': you get float objects
  • for 'boolean': you get bool objects

How to modify cell content?

# for str, float and bolean values, you can ignore the value_type
sheet['A1'].set_value('a string value')

# setting a currency
sheet['A1'].set_value(100, currency='EUR') # is equal to
sheet['A1'].set_value(100, 'currency', 'EUR')

# setting a date
sheet['A1'].set_value('2011-02-05', 'date')
sheet['A1'].set_value('2011-02-05T09:24:00', 'date') # /w time

# setting a time-period 1:10:05
sheet['A1'].set_value('PT01H10M05,0000S', 'time')

to convert date/timeperiod values see TimeParser class. Here just a few examples:

from ezodf.timeparser import TimeParser

date_object = TimeParser.parse('2011-02-05')
datetime_object = TimeParser.parse('2011-02-05T09:24:00')
timedelta_object = TimeParser.parse('PT01H10M05,0000S')

# timedelta to str: 'PThhHmmMss,ffffS'
time_period_str = str(TimeParser(timedelta_object))

How to get rows/columns?

row0 = sheet.row(0) # as list of Cell() objects
col0 = sheet.column(0) # as list of Cell() objects

How to iterate over rows/columns?

for row in sheet.rows():
   print row # row is a list of Cell() objects

for column in sheet.columns():
   print column # column is a list of Cell() objects

How about spreadsheet calculations?

ezodf has no calculation engine included, you can get/set formulas as strings, nothing more. So display form and cell value will not be updated if content is changed, and inserting/deleting rows/columns will also break cell references in formulas.

Presentation Documents

Drawing Documents

Style Management

How to use styles, while style-management is not implemented?

In existing documents, you can use the included styles, you find the needed style:name attributes in styles.xml or content.xml, search for <style:style style:name="..."> elements.

For new documents you can copy&paste styles from other documents:

  • style an object with LibreOffice or OpenOffice
  • save & unzip document
  • in content.xml: search styled object, search the associated automatic style, search for <style:style style:name="..."> elements
  • copy style-element (<style:style> ... </style:style>) to clipboard

rest follows in Python, use a meaningful and unique style:name attribute:

doc.inject_style("""... insert clipboard content ...""")

or use a document including styles as template: newdoc(‘odt’, template=’template.odt’)

to apply the style, just use the name associated by the style:name attribute:

doc.append(Paragraph("some text", style_name='...'))