# Attachment for https://bz.apache.org/ooo/show_bug.cgi?id=127395 """ Not sure if this helps, but I am a python-er and this is a butchered subset of the UNOCONV script (ref: https://github.com/dagwieers/unoconv) which I was trying to modify to save .ods sheets as .csv I run on Windows 10 and so, to simplify it, this is hard-wired for that environment. I assume you could make it run elsewhere by changing the variables at the front. In a folder containing oo-save-ods-sheet.py and the 3-sheet oo-save-ods-sheet.ods, I run it using: "C:\Program Files (x86)\OpenOffice 4\program/python.exe" oo-save-ods-sheet.py Change the Hidden= setting on line 92 to illustrate the problem. """ from __future__ import print_function import os import subprocess import sys import traceback OOprogram = "C:\\Program Files (x86)\\OpenOffice 4" OOprogramPath = OOprogram + "\\program" OFFICE_BINARY = OOprogramPath + "\\soffice.exe" INPUT_FILENAME = "oo-save-ods-sheet.ods" OUTPUT_FILENAME = "oo-save-ods-sheet.csv" OO_CONNECTION = "socket,host=127.0.0.1,port=2002,tcpNoDelay=1;urp;StarOffice.ComponentContext" global convertor, ooproc ooproc = None uno = unohelper = None class Convertor: def __init__(self): global ooproc # unocontext = None self.context = uno.getComponentContext() self.svcmgr = self.context.ServiceManager resolver = self.svcmgr.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", self.context) self.unocontext = self.connect(resolver) if not self.unocontext: die(251, "Unable to connect or start own listener. Aborting.") self.unosvcmgr = self.unocontext.ServiceManager self.desktop = self.unosvcmgr.createInstanceWithContext("com.sun.star.frame.Desktop", self.unocontext) self.cwd = unohelper.systemPathToFileUrl( os.getcwd() ) def connect(self, resolver): global ooproc unocontext = None try: ooproc = subprocess.Popen([OFFICE_BINARY, "-headless", "-invisible", "-nocrashreport", "-nodefault", "-nofirststartwizard", "-nologo", "-norestore", "-accept=%s" % OO_CONNECTION], env=os.environ) print('listener successfully started. (pid=%s)' % (ooproc.pid)) ### Try connection to it for op.timeout seconds timeout = 0 while timeout <= 6: # op.timeout: ### Is it already/still running ? retcode = ooproc.poll() if retcode == 81: return self.connect(resolver) break elif retcode != None: break try: unocontext = resolver.resolve("uno:%s" % OO_CONNECTION) break except NoConnectException: time.sleep(0.5) timeout += 0.5 except: raise else: print("Failed to connect to %s (pid=%s) in 6 seconds.\n%s" % (OFFICE_BINARY, ooproc.pid, e)) except Exception as e: raise print("Launch of %s failed:\n%s" % (OFFICE_BINARY, e)) return unocontext def convert(self, inputfn): document = None try: ### On next line, if Hidden is False then document.storeToURL stores the active sheet, as expected. ### but if Hidden is True it stores the first sheet, not the active one. inputprops = UnoProps(Hidden=True, ReadOnly=True, UpdateDocMode=QUIET_UPDATE) inputurl = unohelper.absolutize(self.cwd, unohelper.systemPathToFileUrl(inputfn)) document = self.desktop.loadComponentFromURL( inputurl , "_blank", 0, inputprops ) if not document: raise UnoException("The document '%s' could not be opened." % inputurl, None) outputprops = UnoProps(FilterName='Text - txt - csv (StarCalc)', OutputStream=OutputStream(), Overwrite=True) outputprops += UnoProps(FilterOptions="44,34,UTF8") outputurl = unohelper.absolutize( self.cwd, unohelper.systemPathToFileUrl(OUTPUT_FILENAME) ) try: oSheets = document.Sheets usePatch = True if usePatch: """ ' Workaround with these lines oFrame = oDoc.CurrentController.Frame dispatcher = createUnoService("com.sun.star.frame.DispatchHelper") Args1(0).Name = "Tables" Args1(0).Value = Array(2) 'index # for sheet dispatcher.executeDispatch(oFrame, ".uno:SelectTables", "", 0, args1()) """ oFrame = document.CurrentController.Frame dispatcher = self.unosvcmgr.createInstanceWithContext("com.sun.star.frame.DispatchHelper", self.unocontext) tableIndex = tuple([2]) # Following don't work either: 2 tuple({2}) [2] print("Trying to save sheet using tableIndex (" + str(type(tableIndex)) + "): " + str(tableIndex)) patchProps = UnoProps(Name="Tables", Value=tableIndex) # Value is supposed to be index # for sheet in an array for some reason dispatcher.executeDispatch(oFrame, ".uno:SelectTables", "", 0, patchProps) # outputprops = UnoProps(FilterName='Text - txt - csv (StarCalc)') else: for i in range(oSheets.Count): sheet = oSheets.getByIndex(i) document.CurrentController.setActiveSheet(sheet) print("Set active sheet: " + sheet.Name) document.storeToURL(outputurl, tuple(outputprops) ) print("Supposedly saved active sheet") except IOException as e: raise UnoException("Unable to store document to %s (ErrCode %d)\n\nProperties: %s" % (outputurl, e.ErrCode, outputprops), None) document.dispose() document.close(True) except Exception as e: print("Exception " + str(e)) print(traceback.format_exc()) def die(ret, msg=None): "Print optional error and exit with errorcode" global convertor, ooproc if msg: print('Error: %s' % msg) if ooproc and convertor: ### If there is a GUI now attached to the instance, disable listener if convertor.desktop.getCurrentFrame(): print('Trying to stop GUI listener') try: subprocess.Popen([OFFICE_BINARY, "-headless", "-invisible", "-nocrashreport", "-nodefault", "-nofirststartwizard", "-nologo", "-norestore", "-unaccept=%s" % OO_CONNECTION], env=os.environ) ooproc.wait() except Exception as e: print("Terminate using %s failed.\n%s" % (OFFICE_BINARY, e)) ### If there is no GUI attached to the instance, terminate instance else: print('Terminating instance') try: convertor.desktop.terminate() except DisposedException: try: ooproc.terminate() except AttributeError: os.kill(ooproc.pid, 15) ooproc.wait() ### Is it still running ? if ooproc.poll() == None: print("instance still running, please investigate...") ooproc.wait() try: ooproc.kill() except AttributeError: os.kill(ooproc.pid, 9) ooproc.wait() sys.exit(ret) ### Main entrance if __name__ == '__main__': os.environ['PATH'] = OOprogramPath + os.pathsep + os.environ['PATH'] os.environ['UNO_PATH'] = OOprogramPath os.environ['URE_BOOTSTRAP'] = "vnd.sun.star.pathname:" + OOprogramPath + "\\fundamental.ini" sys.path.append(OOprogramPath) import uno, unohelper ### Now that we have found a working pyuno library, let's import some classes from com.sun.star.beans import PropertyValue from com.sun.star.document.UpdateDocMode import QUIET_UPDATE from com.sun.star.io import IOException, XOutputStream ### And now that we have those classes, build on them class OutputStream( unohelper.Base, XOutputStream ): def __init__( self ): self.closed = 0 def closeOutput(self): self.closed = 1 def writeBytes( self, seq ): try: sys.stdout.buffer.write( seq.value ) except AttributeError: sys.stdout.write( seq.value ) def flush( self ): pass def UnoProps(**args): props = [] for key in args: prop = PropertyValue() prop.Name = key prop.Value = args[key] props.append(prop) return tuple(props) try: convertor = None try: convertor = Convertor() convertor.convert(INPUT_FILENAME) except Exception as e: print("Exception - " + str(e)) print(traceback.format_exc()) except KeyboardInterrupt as e: die(6, 'Exiting on user request') die(0)