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