New module openoffice.officehelper, which is fastly enhanced over 'officehelper.py...
[openoffice-python:openoffice-python.git] / openoffice / officehelper.py
1 # -*- mode: python ; coding: utf-8 -*-
2 #
3 # Copyright (c) 2008 by Hartmut Goebel <h.goebel@goebel-consult.de>
4 # Licenced under the GNU General Public License v3 (GPLv3)
5 # see file LICENSE-gpl-3.0.txt
6 #
7 # Originally based on officehelper.py 1.2 comming with OOo
8 #
9
10 __author__ = "Hartmut Goebel <h.goebel@goebel-consult.de>"
11 __copyright__ = "Copyright (c) 2008 by Hartmut Goebel <h.goebel@goebel-consult.de>"
12 __licence__ = "GPLv3 - GNU General Public License v3"
13
14 import os
15 import random
16 import sys
17 from time import sleep
18
19 import uno
20 from com.sun.star.connection import NoConnectException
21
22 class BootstrapException(Exception): pass
23
24 def _build_connect_string(pipename=None, host=None, port=None):
25     """
26     Returns a connection description string to be used to connect to
27     this process via UnoUrlResolver().resolve().
28
29     Pass either a pipename for a named pipe or host and port for a
30     socket connection.
31
32     Note: You should use the naemd pipe whenever possible, since
33     socket are unsecure: Everybody can connect to a socket and access
34     your documents without any access control.
35     """
36     if pipename:
37         connectString = "pipe,name=%s" % pipename
38     else:
39         connectString = 'socket'
40         if host: connectString += ',host='+str(host)
41         if port: connectString += ',port='+str(port)
42     return connectString
43
44
45 def _build_cmd_args(connectString, unaccept=False):
46     """
47     Returns command arguments (including executable and options)
48     suitable for staring an background OOo instance listening on named
49     pipe or socket descripted in parameter 'connectString'.
50
51     If unaccept is true, the parmeter '-accept=...' will become
52     '-unaccept=...' for making the instance unlisten.
53     """
54     # soffice script used on *ix, Mac; soffice.exe used on Windoof
55     office = os.path.join(os.path.dirname(uno.__file__), "soffice")
56     if sys.platform.startswith("win"):
57         office += ".exe"
58
59     # -headless includes -invisible (which includes -nologo -nodefault)
60     accept = unaccept and '-unaccept' or '-accept'
61     cmdArray = (office,
62                 "-headless", "-norestore", "%s=%s;urp;" %
63                 (accept, connectString))
64     return cmdArray
65
66
67 def _start_OOo(connectString):
68     """Start an OOo process listening on named pipe or socket
69     descripted in parameter 'connectString'.
70     """
71     cmdArray = _build_cmd_args(connectString)
72     # Start the office proces, don't check for exit status since
73     # an exception is caught anyway if the office terminates
74     # unexpectedly.
75     os.spawnv(os.P_NOWAIT, cmdArray[0], cmdArray)
76
77
78 def connect(connectString):
79     """
80     Connect to an OOo instance listening on named pipe or socket
81     descripted in parameter 'connectString'.
82     """
83     localContext = uno.getComponentContext()
84     resolver = localContext.ServiceManager.createInstanceWithContext(
85         "com.sun.star.bridge.UnoUrlResolver", localContext)
86     connect = "uno:" + connectString + ";urp;StarOffice.ComponentContext"
87
88     # Wait until an office is started, but loop only 20 times (10 seconds)
89     for i in range(20):
90         try:
91             context = resolver.resolve(connect)
92             break
93         except NoConnectException:
94             sleep(0.5)  # Sleep 1/2 second.
95     else:
96         # for-loop completed without 'break' (this is: no connection found)
97         raise BootstrapException("Cannot connect to soffice server.")
98
99     return context
100
101
102 def bootstrap(pipename=None, host=None, port=None):
103     """Bootstrap OOo and PyUNO Runtime.
104
105     If either pipename, host or port are given, connect to the OOo
106     instance listening on this pipe or hosst/port. (This is mainly for
107     convenience, so programms do not need to distinguish these cases.)
108
109     If non of these parameters are give, the soffice process is
110     started opening a named pipe of random name, then the local
111     context is used to access the pipe.
112
113     This function directly returns the remote component context, from
114     whereon you can get the ServiceManager by calling
115     getServiceManager() on the returned object.
116     """
117     if pipename or host or port:
118         connectString = _build_connect_string(pipename=pipename,
119                                               host=host, port=port)
120     else:
121         # Generate a random pipe name.
122         pipename = "uno" + str(random.random())[2:]
123         connectString = _build_connect_string(pipename=pipename)
124         # start OOo listening on this named pipe
125         _start_OOo(connectString)
126     # get component context and return it
127     context = connect(connectString)
128     return context