#! /opt/openoffice4/program/python # -*- coding: utf-8 -*- # OpenOffice mobile presentation controller, as Python # by imacat , 2014-02-28 # Copyright (c) 2016 imacat. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Controls OpenOffice presentation with mobile devices. """ from __future__ import print_function import BaseHTTPServer import argparse import os import os.path import ssl import sys import time def append_uno_path(): """Append the path of the uno module to the import path.""" for p in sys.path: if os.path.exists(os.path.join(p, "uno.py")): return # For uno.py on MacOS candidate = "/Applications/OpenOffice.app/Contents/MacOS" if os.path.exists(os.path.join(candidate, "uno.py")): sys.path.append(candidate) return # Find uno.py for MS-Windows candidate = sys.executable while candidate != os.path.dirname(candidate): candidate = os.path.dirname(candidate) if os.path.exists(os.path.join(candidate, "uno.py")): sys.path.append(candidate) return append_uno_path() import uno from com.sun.star.beans import PropertyValue from com.sun.star.lang import DisposedException from com.sun.star.connection import NoConnectException def main(): """The main program.""" global args # Parses the arguments parse_args() run(args.doc_file, args.www_port, args.use_ssl) def parse_args(): """Parse the arguments.""" global args parser = argparse.ArgumentParser( description=("Synchronize the local Basic scripts" " with OpenOffice/LibreOffice Basic.")) parser.add_argument( "doc_file", metavar="PRESENTATION", type=presentation_doc, help="The presentation document to play.") parser.add_argument( "-w", "--www-port", metavar="N", type=int, default=53177, help=("The TCP port for the web presentation controller " "(default: %(default)s)")) parser.add_argument( "-p", "--port", metavar="N", type=int, default=2002, help=("The TCP port to communicate with " "OpenOffice/LibreOffice with (default: %(default)s)")) parser.add_argument( "-v", "--version", action="version", version="%(prog)s 1.0") args = parser.parse_args() # Obtain the absolute path args.doc_file = os.path.abspath(args.doc_file) # Check whether we are using SSL. # TODO: Changed to an option or find the certificates automatically doc_dir = os.path.dirname(os.path.abspath(sys.argv[0])) key_file = os.path.join(doc_dir, "server.key.pem") crt_file = os.path.join(doc_dir, "server.crt.pem") args.use_ssl = False if os.path.isfile(key_file) and os.path.isfile(crt_file): args.use_ssl = (key_file, crt_file) return def presentation_doc(doc_file): """Check the supplied presentation document argument. Arguments: doc_file: The supplied presentation document argument. Returns: The supplied presentation document argument. """ s = doc_file.lower() if not (s.endswith(".odp") or s.endswith(".sxi") or s.endswith(".ppt") or s.endswith(".pptx")): raise argparse.ArgumentTypeError( "%s: Not a presentation document" % doc_file) path = os.path.abspath(doc_file) if not os.path.exists(path): raise argparse.ArgumentTypeError( "%s: File does not exit" % doc_file) if not os.path.isfile(path): raise argparse.ArgumentTypeError( "%s: Not a file" % doc_file) return doc_file def run(doc, port, use_ssl): """ Start the presentation and run the web presentation controller. """ global oo print("Listen on port %d" % port) oo = PresentationController(doc) oo.check_valid() server_address = ("", port) httpd = BaseHTTPServer.HTTPServer(server_address, MyHTTPRequestHandler) if use_ssl is not False: httpd.socket = ssl.wrap_socket( httpd.socket, keyfile=use_ssl[0], certfile=use_ssl[1], server_side=True) httpd.serve_forever() class PresentationController: """The OpenOffice mobile presentation controller.""" WIN_DETACHED_PROCESS = 0x00000008 def __init__(self, doc_file): """Initialize the object.""" self.file = doc_file self.port = 2002 self.bootstrap_context = None self.service_manager = None self.desktop = None self.doc = None def check_valid(self): """ Check the validity of the connection and the opened document. """ try: presentation = self.doc.getPresentation() except (AttributeError, DisposedException): try: self.bootstrap_context.getServiceManager() except (AttributeError, DisposedException): self.__connect() self.open() presentation = self.doc.getPresentation() if not presentation.isRunning(): presentation.start() def open(self): """Open an office document.""" file_content_provider = self.service_manager.createInstance( "com.sun.star.ucb.FileContentProvider") url = file_content_provider.getFileURLFromSystemPath("", self.file) enum = self.desktop.getComponents().createEnumeration() while enum.hasMoreElements(): component = enum.nextElement() if component.supportsService( "com.sun.star.presentation.PresentationDocument"): if component.getURL() == url: self.doc = component return prop1 = PropertyValue() prop1.Name = "ReadOnly" prop1.Value = True prop2 = PropertyValue() prop2.Name = "MacroExecutionMode" # com.sun.star.document.MacroExecMode.ALWAYS_EXECUTE prop2.Value = 2 self.doc = self.desktop.loadComponentFromURL( url, "_default", 0, (prop2,)) if not self.doc.supportsService( "com.sun.star.presentation.PresentationDocument"): sys.stderr.write(self.file + ": not a presentation document.\n") sys.exit(1) return def goto_next_slide(self): """Go to the next slide.""" self.doc.getPresentation().getController().gotoNextSlide() return def goto_prev_slide(self): """Go to the previous slide.""" self.doc.getPresentation().getController().gotoPreviousSlide() return def __connect(self): """Connect to the running OpenOffice/LibreOffice process. Run OpenOffice/LibreOffice in server listening mode if it is not running yet. """ # Obtain the local context local_context = uno.getComponentContext() # Obtain the local service manager local_service_manager = local_context.getServiceManager() # Obtain the URL resolver url_resolver = local_service_manager.createInstanceWithContext( "com.sun.star.bridge.UnoUrlResolver", local_context) # Obtain the context url = ("uno:socket,host=localhost,port=%d;" "urp;StarOffice.ComponentContext") % self.port while True: try: self.bootstrap_context = url_resolver.resolve(url) except NoConnectException: self.__start_oo() else: break # Obtain the service manager self.service_manager = self.bootstrap_context.getServiceManager() # Obtain the desktop service self.desktop = self.service_manager.createInstanceWithContext( "com.sun.star.frame.Desktop", self.bootstrap_context) def __start_oo(self): """Start OpenOffice/LibreOffice in server listening mode.""" # For MS-Windows, which does not have fork() if os.name == "nt": from subprocess import Popen soffice = os.path.join( os.path.dirname(uno.__file__), "soffice.exe") Popen([soffice, "-accept=socket,host=localhost,port=%d;urp;" % self.port], close_fds=True, creationflags=self.WIN_DETACHED_PROCESS) time.sleep(2) return # For POSIX systems, including Linux and MacOSX try: pid = os.fork() except OSError: print("Failed to fork().", file=sys.stderr) sys.exit(1) if pid != 0: time.sleep(2) return os.setsid() soffice = self.__find_posix_soffice() if soffice is None: print("Failed to find the " "OpenOffice/LibreOffice installation.", file=sys.stderr) sys.exit(1) param = "-accept=socket,host=localhost,port=%d;urp;" % \ self.port # LibreOffice on POSIX systems uses --accept instead of # -accept now. if self.__is_soffice_lo(soffice): param = "-" + param try: os.execl(soffice, soffice, param) except OSError: print("%s: Failed to run the" " OpenOffice/LibreOffice server." % soffice, file=sys.stderr) sys.exit(1) @staticmethod def __find_posix_soffice(): """Find soffice on POSIX systems (Linux or MacOSX). Returns: The found soffice executable, or None if not found. """ # Check soffice in the same directory of uno.py # This works for Linux OpenOffice/LibreOffice local # installation, and OpenOffice on MacOSX. soffice = os.path.join( os.path.dirname(uno.__file__), "soffice") if os.path.exists(soffice): return soffice # Now we have LibreOffice on MacOSX and Linux # OpenOffice/LibreOffice vendor installation. # LibreOffice on MacOSX. soffice = "/Applications/LibreOffice.app/Contents/MacOS/soffice" if os.path.exists(soffice): return soffice # Linux OpenOffice/LibreOffice vendor installation. soffice = "/usr/bin/soffice" if os.path.exists(soffice): return soffice # Not found return None @staticmethod def __is_soffice_lo(soffice): """Check whether the soffice executable is LibreOffice. LibreOffice on POSIX systems accepts "--accept" instead of "-accept" now. Returns: True if soffice is LibreOffice, or False otherwise. """ # This works for most cases. if soffice.lower().find("libreoffice") != -1: return True # Check the symbolic link at /usr/bin/soffice if soffice == "/usr/bin/soffice" and os.path.islink(soffice): if os.readlink(soffice).lower().find("libreoffice") != -1: return True # Not found return False class MyHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): """The local HTTP request handler.""" def do_GET(self): """Handle the GET requests.""" oo.check_valid() html = """ OpenOffice Presentation Controller
""" self.send_response(200) self.send_header("Content-Type", "text/html; charset=UTF-8") self.send_header("Content-Length", len(html)) self.end_headers() self.wfile.write(html) def do_POST(self): """Handle the POST requests.""" oo.check_valid() if self.path == "/next": oo.goto_next_slide() elif self.path == "/prev": oo.goto_prev_slide() html = """ 303 See Others

303 See Others

Please follow this location.

""" self.send_response(303) self.send_header("Location", "/") self.send_header("Content-Type", "text/html; charset=US-ASCII") self.send_header("Content-Length", len(html)) self.end_headers() self.wfile.write(html) if __name__ == "__main__": main() """ if len(sys.argv) != 2: sys.stderr.write("Please specify the presentation document.\n") sys.exit(1) doc_file = os.path.abspath(sys.argv[1]) s = doc_file.lower() if not (s.endswith(".odp") or s.endswith(".sxi") or s.endswith(".ppt") or s.endswith(".pptx")): sys.stderr.write(doc_file + ": not a presentation document.\n") sys.exit(1) if not os.path.exists(doc_file): sys.stderr.write(doc_file + ": file does not exist.\n") sys.exit(1) if not os.path.isfile(doc_file): sys.stderr.write(doc_file + ": file does not exist.\n") sys.exit(1) my_dir = os.path.dirname(os.path.abspath(sys.argv[0])) key_file = os.path.join(my_dir, "server.key.pem") crt_file = os.path.join(my_dir, "server.crt.pem") use_ssl = False if os.path.isfile(key_file) and os.path.isfile(crt_file): use_ssl = (key_file, crt_file) oo = PresentationController(doc_file) run() """