From b5571efffe0fd92c9e3b159477fcf36c3c0fd411 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 16 Aug 2013 14:58:59 +0530 Subject: [PATCH] Start work on DOCX output --- src/calibre/ebooks/docx/writer/__init__.py | 10 +++ src/calibre/ebooks/docx/writer/container.py | 99 +++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 src/calibre/ebooks/docx/writer/__init__.py create mode 100644 src/calibre/ebooks/docx/writer/container.py diff --git a/src/calibre/ebooks/docx/writer/__init__.py b/src/calibre/ebooks/docx/writer/__init__.py new file mode 100644 index 0000000000..2dfbaa2fb8 --- /dev/null +++ b/src/calibre/ebooks/docx/writer/__init__.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2013, Kovid Goyal ' + + + diff --git a/src/calibre/ebooks/docx/writer/container.py b/src/calibre/ebooks/docx/writer/container.py new file mode 100644 index 0000000000..620935791e --- /dev/null +++ b/src/calibre/ebooks/docx/writer/container.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2013, Kovid Goyal ' + +import textwrap + +from lxml.builder import ElementMaker + +from calibre import guess_type +from calibre.constants import numeric_version, __appname__ +from calibre.ebooks.docx.names import namespaces +from calibre.ebooks.oeb.base import xml2str +from calibre.utils.zipfile import ZipFile + +class DOCX(object): + + def __init__(self): + pass + + # Boilerplate {{{ + @property + def contenttypes(self): + E = ElementMaker(namespace=namespaces['ct'], nsmap={None:namespaces['ct']}) + types = E.Types() + for partname, mt in { + "/word/footnotes.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml", + "/word/document.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml", + "/word/numbering.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml", + "/word/styles.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml", + "/word/endnotes.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml", + "/word/settings.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml", + "/word/theme/theme1.xml": "application/vnd.openxmlformats-officedocument.theme+xml", + "/word/fontTable.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml", + "/word/webSettings.xml": "application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml", + "/docProps/core.xml": "application/vnd.openxmlformats-package.core-properties+xml", + "/docProps/app.xml": "application/vnd.openxmlformats-officedocument.extended-properties+xml", + }.iteritems(): + types.append(E.Override(PartName=partname, ContentType=mt)) + added = {'png', 'gif', 'jpeg', 'jpg', 'svg', 'xml'} + for ext in added: + types.append(E.Default(Extension=ext, ContentType=guess_type('a.'+ext)[0])) + for ext, mt in { + "rels": "application/vnd.openxmlformats-package.relationships+xml", + "odttf": "application/vnd.openxmlformats-officedocument.obfuscatedFont", + }.iteritems(): + added.add(ext) + types.append(E.Default(Extension=ext, ContentType=mt)) + # TODO: Iterate over all resources and add mimetypes for any that are + # not already added + return xml2str(types, pretty_print=True) + + @property + def appproperties(self): + E = ElementMaker(namespace=namespaces['ep'], nsmap={None:namespaces['ep']}) + props = E.Properties( + E.Application(__appname__), + E.AppVersion('%02d.%04d' % numeric_version[:2]), + E.DocSecurity('0'), + E.HyperlinksChanged('false'), + E.LinksUpToDate('true'), + E.ScaleCrop('false'), + E.SharedDoc('false'), + ) + return xml2str(props, pretty_print=True) + + @property + def containerrels(self): + return textwrap.dedent(b'''\ + + + + + + ''') + + @property + def websettings(self): + E = ElementMaker(namespace=namespaces['w'], nsmap={'w':namespaces['w']}) + ws = E.webSettings( + E.optimizeForBrowser, E.allowPNG, E.doNotSaveAsSingleFile) + return xml2str(ws, pretty_print=True) + + # }}} + + def write(self, path_or_stream): + with ZipFile(path_or_stream, 'w') as zf: + zf.writestr('[Content_Types].xml', self.contenttypes) + zf.writestr('_rels/.rels', self.containerrels) + zf.writestr('docProps/app.xml', self.appproperties) + zf.writestr('word/webSettings.xml', self.websettings) + # TODO: Write document and document relationships + +if __name__ == '__main__': + d = DOCX() + print (d.websettings)