Coverage for lino/utils/xmlgen/odf/__init__.py : 0%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: UTF-8 -*- # Copyright 2012 Luc Saffre # License: BSD (see file COPYING for details)
u""" Tools for generating `Open Document <http://lists.oasis-open.org/archives/tc-announce/201201/msg00001.html>`_ files and chunks thereof.
It uses :term:`lxml` and a copy of the original RelaxNG schema (:srcref:`/lino/utils/xmlgen/odf/relaxng/OpenDocument-v1.2-os-schema.rng`)
Unlike Søren Roug's `ODFPY <https://joinup.ec.europa.eu/software/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) #doctest: +ELLIPSIS <text:p xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0">Hello world!</text:p> <BLANKLINE>
What if we want to make sure that our chunk is syntactically correct?
>>> validate(mystory) #doctest: +ELLIPSIS 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) #doctest: +ELLIPSIS 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) #doctest: +ELLIPSIS
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) #doctest: +ELLIPSIS <office:document xmlns:...> <office:body> <office:text> <text:p>Hello world!</text:p> </office:text> </office:body> </office:document> <BLANKLINE>
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) #doctest: +ELLIPSIS Traceback (most recent call last): ... Warning: ... Element p has extra content: text
ODF chunks for :term:`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="lino.utils.xmlgen.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="lino.utils.xmlgen.odf example 2") File xmlgen_odf_2.odt has been created.
"""
import os
def rngpath(*parts): p1 = os.path.abspath(os.path.dirname(__file__)) return os.path.join(p1, 'relaxng', *parts)
try: from lxml import etree rng_tree = etree.parse(rngpath('OpenDocument-v1.2-os-schema.rng')) validator = etree.RelaxNG(rng_tree) except ImportError: from lino.utils.xmlgen import etree
from lino.utils import xmlgen as xg
""" <grammar xmlns="http://relaxng.org/ns/structure/1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:db="urn:oasis:names:tc:opendocument:xmlns:database:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:anim="urn:oasis:names:tc:opendocument:xmlns:animation:1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:smil="urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> """
class Text(xg.Namespace):
def setup_namespace(self): self.define_names(""" p style-name """)
text = Text('text', "urn:oasis:names:tc:opendocument:xmlns:text:1.0") #~ text = xg.Namespace('text',"urn:oasis:names:tc:opendocument:xmlns:text:1.0")
class Table(xg.Namespace):
def setup_namespace(self): self.define_names(""" table name style-name number-columns-repeated table-rows table-header-rows table-row table-cell table-columns table-column table-column-group """) table = Table('table', "urn:oasis:names:tc:opendocument:xmlns:table:1.0")
class Style(xg.Namespace):
def setup_namespace(self): self.define_names(""" styles automaticstyles style name family parent-style-name paragraph-properties text-properties number-lines line-number table-column-properties table-cell-properties table-row-properties column-width """) style = Style('style', "urn:oasis:names:tc:opendocument:xmlns:style:1.0")
class FO(xg.Namespace):
def setup_namespace(self): self.define_names(""" text-align justify-single-word margin-top margin-bottom margin-left margin-right padding border padding-left padding-right padding-top padding-bottom font-weight background-color """) fo = FO('fo', "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0")
class Office(xg.Namespace): used_namespaces = [text, table, style]
def setup_namespace(self): self.define_names(""" document body text version mimetype value-type """)
office = Office('office', "urn:oasis:names:tc:opendocument:xmlns:office:1.0")
table.value_type = office.value_type
if False: for prefix, url in rng_tree.getroot().nsmap.items(): if prefix is not None: assert not globals().has_key(prefix) globals()[prefix] = xg.Namespace(prefix, url)
def validate(root): validator.assertValid(root) #~ if not validator.validate(root): #~ raise xg.Warning(validator.error_log.last_error)
def validate_chunks(*chunks): root = office.document( office.makeattribs( version="1.2", mimetype="application/vnd.oasis.opendocument.text"), office.body( office.text(*chunks)), ) validate(root)
def render_to_odt(target_file, startfile=False, **context): """ Render a chunk to an odt file using a default template. """ from appy.pod.renderer import Renderer dir = os.path.abspath(os.path.dirname(__file__)) template_file = os.path.join(dir, "Template.odt") #~ target_file = os.path.join(dir,"tmp.odt") if os.path.exists(target_file): os.remove(target_file) #~ context = dict(body=body,self="lino.utils.xmlgen.odf example") renderer = Renderer(template_file, context, target_file) renderer.run() if startfile: os.startfile(target_file) return "File %s has been created." % target_file
#~ class OpenDocument(xg.Namespace): #~ rng_filename = rngpath('OpenDocument-v1.2-os-schema.rng') #~ used_namespaces = [ iic ] #~ odf = OpenDocument()
def unused_table2odt(headers, fields, widths, rows): """ Not finished because I discovered the `ODFPY <https://joinup.ec.europa.eu/software/odfpy>`_ library """ sums = [0 for col in fields]
story = [] story.append(office.automatic_styles()) 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)
yield html.TR(*headers)
recno = 0 for row in rows: recno += 1 cells = [TD(x) for x in row2cells(row, sums)] #~ yield html.TR(*cells,**cellattrs) yield html.TR(*cells)
has_sum = False for i in sums: if i: has_sum = True break if has_sum: yield html.TR( *[html.TD(x, **cellattrs) for x in ar.ah.store.sums2html(ar, fields, sums)])
def _test(): import doctest doctest.testmod()
if __name__ == "__main__": _test() |