Added orkarounf for Debian an others, where uno.py is not in the
[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 __soffice_executable = None
25
26 def __find_executable():
27     """
28     Find 'soffice' executable.
29
30     Normaly soffice should be in the same directory as uno.py, but eg.
31     Debian does not obaj to this convention. Thus we need to search
32     the executable.
33     """
34     global __soffice_executable
35     if __soffice_executable:
36         return __soffice_executable
37     for p in [os.path.dirname(uno.__file__),
38               '/usr/lib/openoffice/program']:
39         office = os.path.join(p, "soffice")
40         if os.path.exists(office):
41             break
42     else:
43         pathes = glob.glob('/usr/lib/ooo-[234].*/program/soffice')
44         if not pathes:
45             raise BootstrapExceptions('soffice executable not found')
46         pathes.sort(reverse=True)
47         office = pathes[0]
48     __soffice_executable = office
49     return office
50     
51     
52 def _build_connect_string(pipename=None, host=None, port=None):
53     """
54     Returns a connection description string to be used to connect to
55     this process via UnoUrlResolver().resolve().
56
57     Pass either a pipename for a named pipe or host and port for a
58     socket connection.
59
60     Note: You should use the naemd pipe whenever possible, since
61     socket are unsecure: Everybody can connect to a socket and access
62     your documents without any access control.
63     """
64     if pipename:
65         connectString = "pipe,name=%s" % pipename
66     else:
67         connectString = 'socket'
68         if host: connectString += ',host='+str(host)
69         if port: connectString += ',port='+str(port)
70     return connectString
71
72
73 def _build_cmd_args(connectString, unaccept=False):
74     """
75     Returns command arguments (including executable and options)
76     suitable for staring an background OOo instance listening on named
77     pipe or socket descripted in parameter 'connectString'.
78
79     If unaccept is true, the parmeter '-accept=...' will become
80     '-unaccept=...' for making the instance unlisten.
81     """
82     # soffice script used on *ix, Mac; soffice.exe used on Windoof
83     office = __find_executable()
84     if sys.platform.startswith("win"):
85         office += ".exe"
86
87     # -headless includes -invisible (which includes -nologo -nodefault)
88     accept = unaccept and '-unaccept' or '-accept'
89     cmdArray = (office,
90                 "-headless", "-norestore", "%s=%s;urp;" %
91                 (accept, connectString))
92     return cmdArray
93
94
95 def _start_OOo(connectString):
96     """Start an OOo process listening on named pipe or socket
97     descripted in parameter 'connectString'.
98     """
99     cmdArray = _build_cmd_args(connectString)
100     # Start the office proces, don't check for exit status since
101     # an exception is caught anyway if the office terminates
102     # unexpectedly.
103     return os.spawnv(os.P_NOWAIT, cmdArray[0], cmdArray)
104
105
106 def connect(connectString):
107     """
108     Connect to an OOo instance listening on named pipe or socket
109     descripted in parameter 'connectString'.
110     """
111     localContext = uno.getComponentContext()
112     resolver = localContext.ServiceManager.createInstanceWithContext(
113         "com.sun.star.bridge.UnoUrlResolver", localContext)
114     connect = "uno:" + connectString + ";urp;StarOffice.ComponentContext"
115
116     # Wait until an office is started, but loop only 20 times (10 seconds)
117     for i in range(20):
118         try:
119             context = resolver.resolve(connect)
120             break
121         except NoConnectException:
122             sleep(0.5)  # Sleep 1/2 second.
123     else:
124         # for-loop completed without 'break' (this is: no connection found)
125         raise BootstrapException("Cannot connect to soffice server.")
126
127     return context
128
129
130 def bootstrap(pipename=None, host=None, port=None):
131     """Bootstrap OOo and PyUNO Runtime.
132
133     If either pipename, host or port are given, connect to the OOo
134     instance listening on this pipe or hosst/port. (This is mainly for
135     convenience, so programms do not need to distinguish these cases.)
136
137     If non of these parameters are give, the soffice process is
138     started opening a named pipe of random name, then the local
139     context is used to access the pipe.
140
141     This function directly returns the remote component context, from
142     whereon you can get the ServiceManager by calling
143     getServiceManager() on the returned object.
144     """
145     if pipename or host or port:
146         connectString = _build_connect_string(pipename=pipename,
147                                               host=host, port=port)
148     else:
149         # Generate a random pipe name.
150         pipename = "uno" + str(random.random())[2:]
151         connectString = _build_connect_string(pipename=pipename)
152         # start OOo listening on this named pipe
153         _start_OOo(connectString)
154     # get component context and return it
155     context = connect(connectString)
156     return context