This repository has been archived on 2023-02-21. You can view files and clone it, but cannot push or open issues or pull requests.
tabellioOOo/legi2odf/legi2odf.py

159 lines
5.4 KiB
Python

#! /usr/bin/env python
from cStringIO import StringIO
import os
import sys
import libxml2
import libxslt
import zipfile
from optparse import OptionParser
try:
import xml.etree.ElementTree as ET
except ImportError:
import elementtree.ElementTree as ET
base_dir = os.path.dirname(os.path.abspath(__file__))
OFFICE_NS = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
TEXT_NS = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'
TABLE_NS = 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'
STYLE_NS = 'urn:oasis:names:tc:opendocument:xmlns:style:1.0'
def get_as_pt(value):
unit = value[-2:]
qty = float(value[:-2])
if unit == 'cm':
return qty / 0.03514598035
elif unit == 'pt':
return qty
def get_as_cm(value):
unit = value[-2:]
qty = float(value[:-2])
if unit == 'cm':
return qty
elif unit == 'pt':
return qty * 0.03514598035
def convert(input, output):
extra_files = []
try:
z = zipfile.ZipFile(input)
except zipfile.BadZipfile:
content = file(input).read()
else:
content_zfile = None
for zfile in z.namelist():
if zfile == 'contents.xml':
content_zfile = zfile
else:
# extra files
extra_files.append((zfile, z.read(zfile)))
if content_zfile is None:
# missing contents.xml
sys.exit(1)
content = z.read(content_zfile)
styledoc = libxml2.parseFile(os.path.join(base_dir, 'legi2odf.xsl'))
style = libxslt.parseStylesheetDoc(styledoc)
doc = libxml2.parseMemory(content, len(content))
result = style.applyStylesheet(doc, None)
data = style.saveResultToString(result)
style.freeStylesheet()
result.freeDoc()
styledoc = libxml2.parseFile(os.path.join(base_dir, 'legi2odf-meta.xsl'))
style = libxslt.parseStylesheetDoc(styledoc)
result = style.applyStylesheet(doc, None)
meta = style.saveResultToString(result)
style.freeStylesheet()
result.freeDoc()
doc.freeDoc()
# second pass, for things that are too difficult to handle in xsl
content_tree = ET.ElementTree(ET.fromstring(data))
styles = content_tree.findall('.//{%s}automatic-styles' % OFFICE_NS)[0]
for i, table in enumerate(content_tree.findall('.//{%s}table' % TABLE_NS)):
table.attrib['{%s}style-name' % TABLE_NS] = 'Tab.%s' % i
colspecs = table.findall('{urn:tabellio}colspec')
for j, colspec in enumerate(reversed(colspecs)):
style = ET.SubElement(styles, '{%s}style' % STYLE_NS)
style_name = 'Tab.%s.%s' % (i, j)
style.attrib['{%s}name' % STYLE_NS] = style_name
style.attrib['{%s}family' % STYLE_NS] = 'table-column'
props = ET.SubElement(style, '{%s}table-column-properties' % STYLE_NS)
props.attrib['{%s}column-width' % STYLE_NS] = colspec.attrib['{urn:tabellio}colwidth']
tablecol = ET.Element('{%s}table-column' % TABLE_NS,
{'{%s}style-name' % TABLE_NS: style_name})
table.insert(0, tablecol)
style = ET.SubElement(styles, '{%s}style' % STYLE_NS)
style_name = 'Tab.%s' % i
style.attrib['{%s}name' % STYLE_NS] = style_name
style.attrib['{%s}family' % STYLE_NS] = 'table'
props = ET.SubElement(style, '{%s}table-properties' % STYLE_NS)
props.attrib['{%s}align' % TABLE_NS] = 'left'
def sum_pt(colspecs):
total = 0
for colspec in colspecs:
width = colspec.attrib['{urn:tabellio}colwidth']
total += get_as_pt(width)
return '%spt' % total
props.attrib['{%s}width' % STYLE_NS] = sum_pt(colspecs)
out = StringIO()
content_tree.write(out)
data = out.getvalue()
if output == '-':
print data
return
z = zipfile.ZipFile(output, 'w')
z.write(os.path.join(base_dir, '../template-pcf/styles.xml'), 'styles.xml')
z.write(os.path.join(base_dir, '../template-pcf/Configurations2/menubar/menubar.xml'),
'Configurations2/menubar/menubar.xml')
z.writestr('content.xml', data)
z.writestr('meta.xml', meta)
for filename, filecontent in extra_files:
z.writestr('Pictures/%s' % filename, filecontent)
extra_manifest_entries = '\n'.join([' <manifest:file-entry manifest:media-type="" manifest:full-path="Pictures/%s"/>\n' % x[0] for x in extra_files])
z.writestr('META-INF/manifest.xml', '''<?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
<manifest:file-entry manifest:media-type="application/vnd.oasis.opendocument.text" manifest:full-path="/"/>
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="content.xml"/>
<manifest:file-entry manifest:media-type="text/xml" manifest:full-path="styles.xml"/>
<manifest:file-entry manifest:media-type="" manifest:full-path="Configurations2/menubar/menubar.xml"/>
%s
</manifest:manifest>''' % extra_manifest_entries)
z.close()
def main():
parser = OptionParser()
options, args = parser.parse_args()
if len(args) == 2:
convert(args[0], args[1])
else:
if args[0].endswith('.legi'):
dest = args[0].replace('.legi', '.odt')
elif args[0].endswith('.xml'):
dest = args[0].replace('.xml', '.odt')
else:
dest = args[0] + '.odt'
convert(args[0], dest)
if __name__ == '__main__':
main()