449 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			449 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #! /opt/openoffice4/program/python
 | |
| # -*- coding: utf-8 -*-
 | |
| # OpenOffice mobile presentation constroller, as Python
 | |
| #   by imacat <imacat@mail.imacat.idv.tw>, 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 argparse
 | |
| import os
 | |
| import os.path
 | |
| import sys
 | |
| import ssl
 | |
| import time
 | |
| import random
 | |
| import socket
 | |
| import BaseHTTPServer
 | |
| 
 | |
| 
 | |
| def append_uno_path():
 | |
|     """Append the path of the uno module to the import path."""
 | |
| 
 | |
|     is_found_uno = False
 | |
|     for p in sys.path:
 | |
|         if os.path.exists(os.path.join(p, "uno.py")):
 | |
|             return
 | |
|     # For uno.py on MacOS
 | |
|     cand = "/Applications/OpenOffice.app/Contents/MacOS"
 | |
|     if os.path.exists(os.path.join(cand, "uno.py")):
 | |
|         sys.path.append(cand)
 | |
|         return
 | |
|     # Finds uno.py for MS-Windows
 | |
|     cand = sys.executable
 | |
|     while cand != os.path.dirname(cand):
 | |
|         cand = os.path.dirname(cand)
 | |
|         if os.path.exists(os.path.join(cand, "uno.py")):
 | |
|             sys.path.append(cand)
 | |
|             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."""
 | |
|     t_start = time.time()
 | |
|     global args
 | |
| 
 | |
|     # Parses the arguments
 | |
|     parse_args()
 | |
| 
 | |
|     run(args.docfile, args.wwwport, args.usessl)
 | |
| 
 | |
| 
 | |
| def parse_args():
 | |
|     """Parse the arguments."""
 | |
|     global args
 | |
| 
 | |
|     parser = argparse.ArgumentParser(
 | |
|         description=("Synchronize the local Basic scripts"
 | |
|                      " with OpenOffice/LibreOffice Basic."))
 | |
|     parser.add_argument(
 | |
|         "docfile", metavar="PRESENTATIOON", type=presentation_doc,
 | |
|         help=("The presentation document to play."))
 | |
|     parser.add_argument(
 | |
|         "-w", "--wwwport", 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.docfile = os.path.abspath(args.docfile)
 | |
| 
 | |
|     # Check whether we are using SSL.
 | |
|     # TODO: Changed to an option or find the certificates automatically
 | |
|     docdir = os.path.dirname(os.path.abspath(sys.argv[0]))
 | |
|     keyfile = os.path.join(docdir, "server.key.pem")
 | |
|     crtfile = os.path.join(docdir, "server.crt.pem")
 | |
|     args.usessl = False
 | |
|     if os.path.isfile(keyfile) and os.path.isfile(crtfile):
 | |
|         args.usessl = (keyfile, crtfile)
 | |
| 
 | |
|     return
 | |
| 
 | |
| 
 | |
| def presentation_doc(docfile):
 | |
|     """Checks the supplied presentation document argument.
 | |
| 
 | |
|     Arguments:
 | |
|         docfile: The supplied presentation document argument.
 | |
| 
 | |
|     Returns:
 | |
|         The supplied presentation document argument.
 | |
|     """
 | |
|     s = docfile.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" % docfile)
 | |
|     path = os.path.abspath(docfile)
 | |
|     if not os.path.exists(path):
 | |
|         raise argparse.ArgumentTypeError(
 | |
|             "%s: File does not exit" % docfile)
 | |
|     if not os.path.isfile(path):
 | |
|         raise argparse.ArgumentTypeError(
 | |
|             "%s: Not a file" % docfile)
 | |
|     return docfile
 | |
| 
 | |
| 
 | |
| def run(doc, port, usessl):
 | |
|     """
 | |
|     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 usessl is not False:
 | |
|         httpd.socket = ssl.wrap_socket(
 | |
|             httpd.socket,
 | |
|             keyfile=usessl[0],
 | |
|             certfile=usessl[1],
 | |
|             server_side=True)
 | |
|     httpd.serve_forever()
 | |
| 
 | |
| 
 | |
| class PresentationController:
 | |
|     """The OpenOffice mobile presentation controller."""
 | |
| 
 | |
|     def __init__(self, docfile):
 | |
|         """Initialize the object."""
 | |
|         self.file = docfile
 | |
|         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 connect(self):
 | |
|         """Connect to the running OpenOffice/LibreOffice process.
 | |
| 
 | |
|         Run OpenOffice/LibreOffice in server listening mode if it is
 | |
|         not running yet.
 | |
|         """
 | |
|         # Obtains the local context
 | |
|         local_context = uno.getComponentContext()
 | |
|         # Obtains the local service manager
 | |
|         local_service_manager = local_context.getServiceManager()
 | |
|         # Obtains the URL resolver
 | |
|         url_resolver = local_service_manager.createInstanceWithContext(
 | |
|             "com.sun.star.bridge.UnoUrlResolver", local_context)
 | |
|         # Obtains 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
 | |
|         # Obtains the service manager
 | |
|         self.service_manager = self.bootstrap_context.getServiceManager()
 | |
|         # Obtains 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")
 | |
|             DETACHED_PROCESS = 0x00000008
 | |
|             Popen([soffice,
 | |
|                    "-accept=socket,host=localhost,port=%d;urp;" %
 | |
|                    self.port],
 | |
|                   close_fds=True, creationflags=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)
 | |
| 
 | |
|     def __find_posix_soffice(self):
 | |
|         """Find soffice on POSIX systems (Linux or MacOSX).
 | |
| 
 | |
|         Returns:
 | |
|             The found soffice executable, or None if not found.
 | |
|         """
 | |
|         # Checkes 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 vender installation.
 | |
| 
 | |
|         # LibreOffice on MacOSX.
 | |
|         soffice = "/Applications/LibreOffice.app/Contents/MacOS/soffice"
 | |
|         if os.path.exists(soffice):
 | |
|             return soffice
 | |
| 
 | |
|         # Linux OpenOffice/LibreOffice vender installation.
 | |
|         soffice = "/usr/bin/soffice"
 | |
|         if os.path.exists(soffice):
 | |
|             return soffice
 | |
| 
 | |
|         # Not found
 | |
|         return None
 | |
| 
 | |
|     def __is_soffice_lo(self, 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
 | |
| 
 | |
|         # Checks 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
 | |
| 
 | |
|     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
 | |
| 
 | |
| 
 | |
| class MyHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
 | |
|     """The local HTTP request handler."""
 | |
| 
 | |
|     def do_GET(self):
 | |
|         """Handle the GET requests."""
 | |
|         oo.check_valid()
 | |
|         html = """<?xml version="1.0" encoding="UTF-8" ?>
 | |
| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
 | |
|     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 | |
| <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
 | |
| <head>
 | |
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 | |
| <meta name="viewport" content="width=device-width, initial-scale=1.5" />
 | |
| <style type="text/css">
 | |
| .pocket input {
 | |
|     font-size: 8em;
 | |
| }
 | |
| </style>
 | |
| <title>OpenOffice Presentation Controller</title>
 | |
| </head>
 | |
| <body>
 | |
| 
 | |
| <div class="pocket">
 | |
| <form action="/next" method="POST">
 | |
| <input type="submit" value="▲" />
 | |
| </form>
 | |
| </div>
 | |
| 
 | |
| <div class="pocket">
 | |
| <form action="/prev" method="POST">
 | |
| <input type="submit" value="▼" />
 | |
| </form>
 | |
| </div>
 | |
| 
 | |
| </body>"""
 | |
|         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 = """<?xml version="1.0" encoding="US-ASCII" ?>
 | |
| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
 | |
|     "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
 | |
| <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
 | |
| <head>
 | |
| <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII" />
 | |
| <meta name="viewport" content="width=device-width, initial-scale=1.5" />
 | |
| <title>303 See Others</title>
 | |
| </head>
 | |
| <body>
 | |
| <h1>303 See Others</h1>
 | |
| <p>Please follow <a href="/">this location</a>.</p>
 | |
| </body>
 | |
| </html>"""
 | |
|         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)
 | |
|     docfile = os.path.abspath(sys.argv[1])
 | |
|     s = docfile.lower()
 | |
|     if not (s.endswith(".odp")
 | |
|             or s.endswith(".sxi")
 | |
|             or s.endswith(".ppt")
 | |
|             or s.endswith(".pptx")):
 | |
|         sys.stderr.write(docfile + ": not a presentation document.\n")
 | |
|         sys.exit(1)
 | |
|     if not os.path.exists(docfile):
 | |
|         sys.stderr.write(docfile + ": file does not exist.\n")
 | |
|         sys.exit(1)
 | |
|     if not os.path.isfile(docfile):
 | |
|         sys.stderr.write(docfile + ": file does not exist.\n")
 | |
|         sys.exit(1)
 | |
|     mydir = os.path.dirname(os.path.abspath(sys.argv[0]))
 | |
|     keyfile = os.path.join(mydir, "server.key.pem")
 | |
|     crtfile = os.path.join(mydir, "server.crt.pem")
 | |
|     usessl = False
 | |
|     if os.path.isfile(keyfile) and os.path.isfile(crtfile):
 | |
|         usessl = (keyfile, crtfile)
 | |
|     oo = PresentationController(docfile)
 | |
|     run()
 | |
|     """
 |