etgen.odf

Tools for generating Open Document files and chunks thereof.

Not actively used.

It uses lxml and a copy of the original RelaxNG schema (https://github.com/lino-framework/etgen/blob/master//etgen/odf/relaxng/OpenDocument-v1.2-os-schema.rng)

Unlike Søren Roug’s ODFPY library, validation is done only upon request.

Generating validated chunks of ODF

We instantiate a simple chunk of ODF…

>>> mystory = text.p("Hello world!")

… and look how it gets rendered:

>>> print etree.tostring(mystory,pretty_print=True) 
<text:p xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">Hello world!</text:p>

What if we want to make sure that our chunk is syntactically correct?

>>> validate(mystory) 
Traceback (most recent call last):
...
Warning: ... Did not expect element p there

Yes, in order to validate our chunk of XML, we need to wrap it into a document:

>>> root = office.document(
...     office.body(
...       office.text(mystory)),
...     version="1.2",
...     mimetype="application/vnd.oasis.opendocument.text"
... )
>>> validate(root) 
Traceback (most recent call last):
...
Warning: ... Element document failed to validate attributes

That’s almost good, except for a little “detail”: when using Python syntax, we cannot specify a namespace for attributes (here version and mimetype).

How to specify the namespace of attributes?

First attempt:

>>> OFFICE = "{"+office.targetNamespace+"}"
>>> attribs = {
...     OFFICE + 'version' : "1.2",
...     OFFICE + 'mimetype' : "application/vnd.oasis.opendocument.text",
... }
>>> root = office.document(
...     office.body(
...       office.text(
...         text.p("Hello world!")
...     )),
...     **attribs
... )
>>> validate(root) 

This works, but is rather heavy to code.

Here is how we suggest to set attributes:

>>> office.update_attribs(root,
...     version="1.2",
...     mimetype="application/vnd.oasis.opendocument.text")
>>> validate(root)

This is functionally equivalent (but more easy to code) :

>>> root.set(office.version().tag,"1.2")
>>> root.set(office.mimetype().tag,"application/vnd.oasis.opendocument.text")
>>> print etree.tostring(root,pretty_print=True) 
<office:document xmlns:...>
  <office:body>
    <office:text>
      <text:p>Hello world!</text:p>
    </office:text>
  </office:body>
</office:document>

There is also a shortcut function validate_chunks which does all this wrapping work:

>>> validate_chunks(text.p("Hello world!"))

Here an example of invalid tree:

>>> mystory = text.p("Foo",text.p("Bar"))
>>> validate_chunks(mystory) 
Traceback (most recent call last):
...
Warning: ... Element p has extra content: text

ODF chunks for appy.pod

To understand why we want to generate such chunks of validated ODF, please read the first section of the following documentation page: http://appyframework.org/podWritingAdvancedTemplates.html. We have a file Template.odt with basically one single contains control code:

do text
from body()

And here is the body() function called there:

>>> mystory = text.p("Hello world!")

Here is an example on how to use it:

>>> print render_to_odt("xmlgen_odf_1.odt",
...   body=etree.tostring(mystory),
...   title="etgen.odf example 1")
File xmlgen_odf_1.odt has been created.

Examples

>>> t = table.table(
...   table.makeattribs(name="Mytable",style_name="Mytable"),
...   table.table_column(table.makeattribs(style_name="A",number_columns_repeated="2")),
...   table.table_row(
...     table.table_cell(text.p("First"),table.makeattribs(value_type="string")),
...     table.table_cell(text.p("Second"),table.makeattribs(value_type="string")),
...   ),
...   table.table_row(
...     table.table_cell(text.p("Third"),table.makeattribs(value_type="string")),
...     table.table_cell(text.p("Fourth"),table.makeattribs(value_type="string")),
...   ),
... )
>>> validate_chunks(t)
>>> print render_to_odt("xmlgen_odf_2.odt",
...   body=etree.tostring(t),
...   title="etgen.odf example 2")
File xmlgen_odf_2.odt has been created.

Functions

render_to_odt(target_file[, startfile])

Render a chunk to an odt file using a default template.

rngpath(*parts)

unused_table2odt(headers, fields, widths, rows)

validate(root)

validate_chunks(*chunks)

Classes

FO([targetNamespace, names, prefix])

Office([targetNamespace, names, prefix])

Style([targetNamespace, names, prefix])

Table([targetNamespace, names, prefix])

Text([targetNamespace, names, prefix])

etgen.odf.render_to_odt(target_file, startfile=False, **context)

Render a chunk to an odt file using a default template.

etgen.odf.unused_table2odt(headers, fields, widths, rows)

Not finished because I discovered the ODFPY library