3
# PyODConverter (Python OpenDocument Converter) v1.1 - 2009-11-14
4
# Modifications by Mikko Rantalainen <mikko.rantalainen@peda.net>
6
# This script converts a document from one office format to another by
7
# connecting to an OpenOffice.org instance via Python-UNO bridge.
9
# Copyright (C) 2008-2009 Mirko Nasato <mirko@artofsolving.com>
10
# Licensed under the GNU LGPL v2.1 - http://www.gnu.org/licenses/lgpl-2.1.html
11
# - or any later version.
14
# http://www.artofsolving.com/opensource/pyodconverter
15
# http://www.linuxjournal.com/content/starting-stopping-and-connecting-openoffice-python
18
DEFAULT_OPENOFFICE_PORT = 8100
26
('/usr/lib/openoffice/program', '/usr/lib/openoffice/program'),
27
('/usr/lib64/ooo-2.0/program', '/usr/lib64/ooo-2.0/program'),
28
('/opt/openoffice.org3/program', '/opt/openoffice.org/basis3.0/program'),
32
if os.path.exists(p[0]):
33
OPENOFFICE_PATH = p[0]
34
OPENOFFICE_BIN = os.path.join(OPENOFFICE_PATH, 'soffice')
35
OPENOFFICE_LIBPATH = p[1]
37
# Add to path so we can find uno.
38
if sys.path.count(OPENOFFICE_LIBPATH) == 0:
39
sys.path.insert(0, OPENOFFICE_LIBPATH)
43
from os.path import abspath, isfile, splitext
44
from com.sun.star.beans import PropertyValue
45
from com.sun.star.task import ErrorCodeIOException
46
from com.sun.star.connection import NoConnectException
50
FAMILY_SPREADSHEET = "Spreadsheet"
51
FAMILY_PRESENTATION = "Presentation"
52
FAMILY_DRAWING = "Drawing"
54
#---------------------#
55
# Configuration Start #
56
#---------------------#
58
# see http://wiki.services.openoffice.org/wiki/Framework/Article/Filter
60
# most formats are auto-detected; only those requiring options are defined here
63
"FilterName": "Text (encoded)",
64
"FilterOptions": "utf8"
67
"FilterName": "Text - txt - csv (StarCalc)",
68
"FilterOptions": "44,34,0"
74
FAMILY_TEXT: { "FilterName": "writer_pdf_Export" },
75
FAMILY_WEB: { "FilterName": "writer_web_pdf_Export" },
76
FAMILY_SPREADSHEET: { "FilterName": "calc_pdf_Export" },
77
FAMILY_PRESENTATION: { "FilterName": "impress_pdf_Export" },
78
FAMILY_DRAWING: { "FilterName": "draw_pdf_Export" }
81
FAMILY_TEXT: { "FilterName": "HTML (StarWriter)" },
82
FAMILY_SPREADSHEET: { "FilterName": "HTML (StarCalc)" },
83
FAMILY_PRESENTATION: { "FilterName": "impress_html_Export" }
86
FAMILY_TEXT: { "FilterName": "writer8" },
87
FAMILY_WEB: { "FilterName": "writerweb8_writer" }
90
FAMILY_TEXT: { "FilterName": "MS Word 97" }
93
FAMILY_TEXT: { "FilterName": "Rich Text Format" }
98
"FilterOptions": "utf8"
102
FAMILY_SPREADSHEET: { "FilterName": "calc8" }
105
FAMILY_SPREADSHEET: { "FilterName": "MS Excel 97" }
108
FAMILY_SPREADSHEET: {
109
"FilterName": "Text - txt - csv (StarCalc)",
110
"FilterOptions": "44,34,0"
114
FAMILY_PRESENTATION: { "FilterName": "impress8" }
117
FAMILY_PRESENTATION: { "FilterName": "MS PowerPoint 97" }
120
FAMILY_DRAWING: { "FilterName": "draw_flash_Export" },
121
FAMILY_PRESENTATION: { "FilterName": "impress_flash_Export" }
125
PAGE_STYLE_OVERRIDE_PROPERTIES = {
126
FAMILY_SPREADSHEET: {
127
#--- Scale options: uncomment 1 of the 3 ---
128
# a) 'Reduce / enlarge printout': 'Scaling factor'
130
# b) 'Fit print range(s) to width / height': 'Width in pages' and 'Height in pages'
131
#"ScaleToPagesX": 1, "ScaleToPagesY": 1000,
132
# c) 'Fit print range(s) on number of pages': 'Fit print range(s) on number of pages'
138
#-------------------#
139
# Configuration End #
140
#-------------------#
144
Start, stop, and connect to OpenOffice.
146
def __init__(self, port=DEFAULT_OPENOFFICE_PORT):
147
""" Create OORunner that connects on the specified port. """
151
def connect(self, no_startup=False):
153
Connect to OpenOffice.
154
If a connection cannot be established try to start OpenOffice.
156
localContext = uno.getComponentContext()
157
resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext)
163
context = resolver.resolve("uno:socket,host=localhost,port=%d;urp;StarOffice.ComponentContext" % self.port)
165
except NoConnectException:
168
# If first connect failed then try starting OpenOffice.
170
# Exit loop if startup not desired.
175
# Pause and try again to connect
180
raise Exception, "Failed to connect to OpenOffice on port %d" % self.port
182
desktop = context.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", context)
185
raise Exception, "Failed to create OpenOffice desktop on port %d" % self.port
192
Start a headless instance of OpenOffice.
194
args = [OPENOFFICE_BIN,
195
'-accept=socket,host=localhost,port=%d;urp;StarOffice.ServiceManager' % self.port,
197
'-nofirststartwizard',
201
env = {'PATH' : '/bin:/usr/bin:%s' % OPENOFFICE_PATH,
202
'PYTHONPATH' : OPENOFFICE_LIBPATH,
206
pid = os.spawnve(os.P_NOWAIT, args[0], args, env)
208
raise Exception, "Failed to start OpenOffice on port %d: %s" % (self.port, e.message)
211
raise Exception, "Failed to start OpenOffice on port %d" % self.port
219
desktop = self.connect(True)
224
raise Exception, "Failed to shutdown the process: %s" % (e.message)
229
class DocumentConversionException(Exception):
231
def __init__(self, message):
232
self.message = message
238
class DocumentConverter:
240
def __init__(self, port=DEFAULT_OPENOFFICE_PORT):
241
localContext = uno.getComponentContext()
242
resolver = localContext.ServiceManager.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", localContext)
244
context = resolver.resolve("uno:socket,host=localhost,port=%s;urp;StarOffice.ComponentContext" % port)
245
except NoConnectException:
246
raise DocumentConversionException, "failed to connect to OpenOffice.org on port %s" % port
247
self.desktop = context.ServiceManager.createInstanceWithContext("com.sun.star.frame.Desktop", context)
250
self.desktop.terminate()
252
def convert(self, inputFile, outputFile):
254
inputUrl = self._toFileUrl(inputFile)
255
outputUrl = self._toFileUrl(outputFile)
257
loadProperties = { "Hidden": True }
258
inputExt = self._getFileExt(inputFile)
259
if IMPORT_FILTER_MAP.has_key(inputExt):
260
loadProperties.update(IMPORT_FILTER_MAP[inputExt])
262
document = self.desktop.loadComponentFromURL(inputUrl, "_blank", 0, self._toProperties(loadProperties))
265
except AttributeError:
268
family = self._detectFamily(document)
269
self._overridePageStyleProperties(document, family)
271
outputExt = self._getFileExt(outputFile)
272
storeProperties = self._getStoreProperties(document, outputExt)
275
document.storeToURL(outputUrl, self._toProperties(storeProperties))
279
def _overridePageStyleProperties(self, document, family):
280
if PAGE_STYLE_OVERRIDE_PROPERTIES.has_key(family):
281
properties = PAGE_STYLE_OVERRIDE_PROPERTIES[family]
282
pageStyles = document.getStyleFamilies().getByName('PageStyles')
283
for styleName in pageStyles.getElementNames():
284
pageStyle = pageStyles.getByName(styleName)
285
for name, value in properties.items():
286
pageStyle.setPropertyValue(name, value)
288
def _getStoreProperties(self, document, outputExt):
289
family = self._detectFamily(document)
291
propertiesByFamily = EXPORT_FILTER_MAP[outputExt]
293
raise DocumentConversionException, "unknown output format: '%s'" % outputExt
295
return propertiesByFamily[family]
297
raise DocumentConversionException, "unsupported conversion: from '%s' to '%s'" % (family, outputExt)
299
def _detectFamily(self, document):
300
if document.supportsService("com.sun.star.text.WebDocument"):
302
if document.supportsService("com.sun.star.text.GenericTextDocument"):
303
# must be TextDocument or GlobalDocument
305
if document.supportsService("com.sun.star.sheet.SpreadsheetDocument"):
306
return FAMILY_SPREADSHEET
307
if document.supportsService("com.sun.star.presentation.PresentationDocument"):
308
return FAMILY_PRESENTATION
309
if document.supportsService("com.sun.star.drawing.DrawingDocument"):
310
return FAMILY_DRAWING
311
raise DocumentConversionException, "unknown document family: %s" % document
313
def _getFileExt(self, path):
314
ext = splitext(path)[1]
316
return ext[1:].lower()
318
def _toFileUrl(self, path):
319
return uno.systemPathToFileUrl(abspath(path))
321
def _toProperties(self, dict):
324
prop = PropertyValue()
326
prop.Value = dict[key]
331
if __name__ == "__main__":
332
from sys import argv, exit
334
if argv[1] == "--daemon":
336
service = OOService()
339
except ErrorCodeIOException, e:
340
print "Failed to start daemon process: %s" % e.message
343
if argv[1] == "--shutdown":
345
service = OOService()
348
except ErrorCodeIOException, e:
349
print "Failed to shut down daemon process: %s" % e.message
353
print "USAGE: python %s <input-file> <output-file>" % argv[0]
355
elif not isfile(argv[1]):
356
print "no such input file: %s" % argv[1]
359
converter = DocumentConverter()
360
converter.convert(argv[1], argv[2])
361
except DocumentConversionException, exception:
362
print "ERROR! " + str(exception)
364
except ErrorCodeIOException, exception:
365
print "ERROR! ErrorCodeIOException %d" % exception.ErrCode