#!/usr/bin/python

### This program is free software; you can redistribute it and/or modify
### it under the terms of the GNU Library General Public License as published by
### the Free Software Foundation; version 2 only
###
### This program is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
### GNU Library General Public License for more details.
###
### You should have received a copy of the GNU Library General Public License
### along with this program; if not, write to the Free Software
### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
### Copyright 2007 Dag Wieers <dag@wieers.com>

import getopt, sys, os, distutils.sysconfig, glob

try:
    import uno
except:
#    extrapaths = ('/usr/lib/openoffice/program/', '/usr/lib/openoffice.org2.0/program/')
    extrapaths = glob.glob('/usr/lib*/openoffice*/program/')
    for path in extrapaths:
        if os.path.isfile(os.path.join(path, 'pyuno.so')):
            sys.path.append(path)
            break
    else:
        print >>sys.stderr, "unoconv: Cannot find the pyuno.so library."
        print >>sys.stderr, "Please locate this library and send feedback to <tools@lists.rpmforge.net>."
        sys.exit(1)

import uno, unohelper
from com.sun.star.beans import PropertyValue
from com.sun.star.uno import Exception as UnoException
from com.sun.star.io import IOException, XOutputStream
from com.sun.star.connection import NoConnectException
from com.sun.star.script import CannotConvertException

__version__ = "$Revision$"
# $Source$

VERSION = '0.2'

class Fmt:
    def __init__(self, doctype, name, extension, summary, filter):
        self.doctype = doctype
        self.name = name
        self.extension = extension
        self.summary = summary
        self.filter = filter

class FmtList:
    def __init__(self):
        self.list = []
    def add(self, doctype, name, extension, summary, filter):
        self.list.append(Fmt(doctype, name, extension, summary, filter))
    def byname(self, name):
        ret = []
        for fmt in self.list:
            if fmt.name == name:
                ret.append(fmt)
        return ret
    def byextension(self, extension):
        ret = []
        for fmt in self.list:
            if fmt.extension == extension:
                ret.append(fmt)
        return ret
    def bydoctype(self, doctype, name):
        for fmt in self.list:
            if fmt.name == name and fmt.doctype == doctype:
                return (fmt, )
    def display(self, doctype):
        print >>sys.stderr, 'The following list of %s formats are currently available:' % doctype
        print >>sys.stderr
        for fmt in self.list:
            if fmt.doctype == doctype:
                print >>sys.stderr, "  %-8s - %s [.%s]" % (fmt.name, fmt.summary, fmt.extension)
        print >>sys.stderr

class OutputStream( unohelper.Base, XOutputStream ):
    def __init__( self ):
        self.closed = 0
    def closeOutput(self):
        self.closed = 1
    def writeBytes( self, seq ):
        sys.stdout.write( seq.value )
    def flush( self ):
        pass

doctypes = ('document', 'graphics', 'presentation', 'spreadsheet')
fmts = FmtList()

### Document / Writer
fmts.add('document', 'bib', 'bib', 'BibTeX', 'BibTeX_Writer')
fmts.add('document', 'doc', 'doc', 'Microsoft Word 97/2000/XP', 'MS Word 97')
fmts.add('document', 'doc6', 'doc', 'Microsoft Word 6.0', 'MS WinWord 6.0')
fmts.add('document', 'doc95', 'doc', 'Microsoft Word 95', 'MS Word 95')
fmts.add('document', 'docbook', 'xml', 'DocBook', 'DocBook File')
fmts.add('document', 'html', 'html', 'HTML Document (OpenOffice.org Writer)', 'HTML (StarWriter)')
fmts.add('document', 'odt', 'odt', 'Open Document Text', 'writer8')
fmts.add('document', 'ott', 'ott', 'Open Document Text', 'writer8_template')
fmts.add('document', 'ooxml', 'xml', 'Microsoft Office Open XML', 'MS Word 2003 XML')
fmts.add('document', 'pdb', 'pdb', 'AportisDoc (Palm)', 'AportisDoc Palm DB')
fmts.add('document', 'pdf', 'pdf', 'Portable Document Format', 'writer_pdf_Export')
fmts.add('document', 'psw', 'psw', 'Pocket Word', 'PocketWord File')
fmts.add('document', 'rtf', 'rtf', 'Rich Text Format', 'Rich Text Format')
fmts.add('document', 'latex', 'ltx', 'LaTeX 2e', 'LaTeX_Writer')
fmts.add('document', 'sdw', 'sdw', 'StarWriter 5.0', 'StarWriter 5.0')
fmts.add('document', 'sdw4', 'sdw', 'StarWriter 4.0', 'StarWriter 4.0')
fmts.add('document', 'sdw3', 'sdw', 'StarWriter 3.0', 'StarWriter 3.0')
fmts.add('document', 'stw', 'stw', 'Open Office.org 1.0 Text Document Template', 'writer_StarOffice_XML_Writer_Template')
fmts.add('document', 'sxw', 'sxw', 'Open Office.org 1.0 Text Document', 'StarOffice XML (Writer)')
fmts.add('document', 'text', 'txt', 'Text Encoded', 'Text (encoded)')
fmts.add('document', 'txt', 'txt', 'Plain Text', 'Text')
fmts.add('document', 'vor', 'vor', 'StarWriter 5.0 Template', 'StarWriter 5.0 Vorlage/Template')
fmts.add('document', 'vor4', 'vor', 'StarWriter 4.0 Template', 'StarWriter 4.0 Vorlage/Template')
fmts.add('document', 'vor3', 'vor', 'StarWriter 3.0 Template', 'StarWriter 3.0 Vorlage/Template')
fmts.add('document', 'xhtml', 'html', 'XHTML Document', 'XHTML Writer File')

### Spreadsheet
fmts.add('spreadsheet', 'csv', 'csv', 'Text CSV', 'Text - txt - csv (StarCalc)')
fmts.add('spreadsheet', 'dbf', 'dbf', 'dBase', 'dBase')
fmts.add('spreadsheet', 'dif', 'dif', 'Data Interchange Format', 'DIF')
fmts.add('spreadsheet', 'html', 'html', 'HTML Document (OpenOffice.org Calc)', 'HTML (StarCalc)')
fmts.add('spreadsheet', 'ods', 'ods', 'Open Document Spreadsheet', 'calc8')
fmts.add('spreadsheet', 'ooxml', 'xml', 'Microsoft Excel 2003 XML', 'MS Excel 2003 XML')
fmts.add('spreadsheet', 'pdf', 'pdf', 'Portable Document Format', 'calc_pdf_Export')
fmts.add('spreadsheet', 'pts', 'pts', 'OpenDocument Spreadsheet Template', 'calc8_template')
fmts.add('spreadsheet', 'pxl', 'pxl', 'Pocket Excel', 'Pocket Excel')
fmts.add('spreadsheet', 'sdc', 'sdc', 'StarCalc 5.0', 'StarCalc 5.0')
fmts.add('spreadsheet', 'sdc4', 'sdc', 'StarCalc 4.0', 'StarCalc 4.0')
fmts.add('spreadsheet', 'sdc3', 'sdc', 'StarCalc 3.0', 'StarCalc 3.0')
fmts.add('spreadsheet', 'slk', 'slk', 'SYLK', 'SYLK')
fmts.add('spreadsheet', 'stc', 'stc', 'OpenOffice.org 1.0 Spreadsheet Template', 'calc_StarOffice_XML_Calc_Template')
fmts.add('spreadsheet', 'sxc', 'sxc', 'OpenOffice.org 1.0 Spreadsheet', 'StarOffice XML (Calc)')
fmts.add('spreadsheet', 'vor3', 'vor', 'StarCalc 3.0 Template', 'StarCalc 3.0 Vorlage/Template')
fmts.add('spreadsheet', 'vor4', 'vor', 'StarCalc 4.0 Template', 'StarCalc 4.0 Vorlage/Template')
fmts.add('spreadsheet', 'vor', 'vor', 'StarCalc 5.0 Template', 'StarCalc 5.0 Vorlage/Template')
fmts.add('spreadsheet', 'xhtml', 'xhtml', 'XHTML', 'XHTML Calc File')
fmts.add('spreadsheet', 'xls', 'xls', 'Microsoft Excel 97/2000/XP', 'MS Excel 97')
fmts.add('spreadsheet', 'xls5', 'xls', 'Microsoft Excel 5.0', 'MS Excel 5.0/95')
fmts.add('spreadsheet', 'xls95', 'xls', 'Microsoft Excel 95', 'MS Excel 95')
fmts.add('spreadsheet', 'xlt', 'xlt', 'Microsoft Excel 97/2000/XP Template', 'MS Excel 97 Vorlage/Template')
fmts.add('spreadsheet', 'xlt5', 'xlt', 'Microsoft Excel 5.0 Template', 'MS Excel 5.0/95 Vorlage/Template')
fmts.add('spreadsheet', 'xlt95', 'xlt', 'Microsoft Excel 95 Template', 'MS Excel 95 Vorlage/Template')

### Graphics
fmts.add('graphics', 'bmp', 'bmp', 'Windows Bitmap', 'draw_bmp_Export')
fmts.add('graphics', 'emf', 'emf', 'Enhanced Metafile', 'draw_emf_Export')
fmts.add('graphics', 'eps', 'eps', 'Encapsulated PostScript', 'draw_eps_Export')
fmts.add('graphics', 'gif', 'gif', 'Graphics Interchange Format', 'draw_gif_Export')
fmts.add('graphics', 'html', 'html', 'HTML Document (OpenOffice.org Draw)', 'draw_html_Export')
fmts.add('graphics', 'jpg', 'jpg', 'Joint Photographic Experts Group', 'draw_jpg_Export')
fmts.add('graphics', 'met', 'met', 'OS/2 Metafile', 'draw_met_Export')
fmts.add('graphics', 'odd', 'odd', 'OpenDocument Drawing', 'draw8')
fmts.add('graphics', 'otg', 'otg', 'OpenDocument Drawing Template', 'draw8_template')
fmts.add('graphics', 'pbm', 'pbm', 'Portable Bitmap', 'draw_pbm_Export')
fmts.add('graphics', 'pct', 'pct', 'Mac Pict', 'draw_pct_Export')
fmts.add('graphics', 'pdf', 'pdf', 'Portable Document Format', 'draw_pdf_Export')
fmts.add('graphics', 'pgm', 'pgm', 'Portable Graymap', 'draw_pgm_Export')
fmts.add('graphics', 'png', 'png', 'Portable Network Graphic', 'draw_png_Export')
fmts.add('graphics', 'ppm', 'ppm', 'Portable Pixelmap', 'draw_ppm_Export')
fmts.add('graphics', 'ras', 'ras', 'Sun Raster Image', 'draw_ras_Export')
fmts.add('graphics', 'std', 'std', 'OpenOffice.org 1.0 Drawing Template', 'draw_StarOffice_XML_Draw_Template')
fmts.add('graphics', 'svg', 'svg', 'Scalable Vector Graphics', 'draw_svg_Export')
fmts.add('graphics', 'svm', 'svm', 'StarView Metafile', 'draw_svm_Export')
fmts.add('graphics', 'swf', 'swf', 'Macromedia Flash (SWF)', 'draw_flash_Export')
fmts.add('graphics', 'sxd', 'sxd', 'OpenOffice.org 1.0 Drawing', 'StarOffice XML (Draw)')
fmts.add('graphics', 'sxd3', 'sxd', 'StarDraw 3.0', 'StarDraw 3.0')
fmts.add('graphics', 'sxd5', 'sxd', 'StarDraw 5.0', 'StarDraw 5.0')
fmts.add('graphics', 'tiff', 'tiff', 'Tagged Image File Format', 'draw_tif_Export')
fmts.add('graphics', 'vor', 'vor', 'StarDraw 5.0 Template', 'StarDraw 5.0 Vorlage')
fmts.add('graphics', 'vor3', 'vor', 'StarDraw 3.0 Template', 'StarDraw 3.0 Vorlage')
fmts.add('graphics', 'wmf', 'wmf', 'Windows Metafile', 'draw_wmf_Export')
fmts.add('graphics', 'xhtml', 'xhtml', 'XHTML', 'XHTML Draw File')
fmts.add('graphics', 'xpm', 'xpm', 'X PixMap', 'draw_xpm_Export')

### Presentation
fmts.add('presentation', 'bmp', 'bmp', 'Windows Bitmap', 'impress_bmp_Export')
fmts.add('presentation', 'emf', 'emf', 'Enhanced Metafile', 'impress_emf_Export')
fmts.add('presentation', 'eps', 'eps', 'Encapsulated PostScript', 'impress_eps_Export')
fmts.add('presentation', 'gif', 'gif', 'Graphics Interchange Format', 'impress_gif_Export')
fmts.add('presentation', 'html', 'html', 'HTML Document (OpenOffice.org Impress)', 'impress_html_Export')
fmts.add('presentation', 'jpg', 'jpg', 'Joint Photographic Experts Group', 'impress_jpg_Export')
fmts.add('presentation', 'met', 'met', 'OS/2 Metafile', 'impress_met_Export')
fmts.add('presentation', 'odd', 'odd', 'OpenDocument Drawing (Impress)', 'impress8_draw')
fmts.add('presentation', 'odg', 'odg', 'OpenOffice.org 1.0 Drawing (OpenOffice.org Impress)', 'impress_StarOffice_XML_Draw')
fmts.add('presentation', 'odp', 'odp', 'OpenDocument Presentation', 'impress8')
fmts.add('presentation', 'pbm', 'pbm', 'Portable Bitmap', 'impress_pbm_Export')
fmts.add('presentation', 'pct', 'pct', 'Mac Pict', 'impress_pct_Export')
fmts.add('presentation', 'pdf', 'pdf', 'Portable Document Format', 'impress_pdf_Export')
fmts.add('presentation', 'pgm', 'pgm', 'Portable Graymap', 'impress_pgm_Export')
fmts.add('presentation', 'png', 'png', 'Portable Network Graphic', 'impress_png_Export')
fmts.add('presentation', 'pot', 'pot', 'Microsoft PowerPoint 97/2000/XP Template', 'MS PowerPoint 97 Vorlage')
fmts.add('presentation', 'ppm', 'ppm', 'Portable Pixelmap', 'impress_ppm_Export')
fmts.add('presentation', 'ppt', 'ppt', 'Microsoft PowerPoint 97/2000/XP', 'MS PowerPoint 97')
fmts.add('presentation', 'pwp', 'pwp', 'PlaceWare', 'placeware_Export')
fmts.add('presentation', 'ras', 'ras', 'Sun Raster Image', 'impress_ras_Export')
fmts.add('presentation', 'sda', 'sda', 'StarDraw 5.0 (OpenOffice.org Impress)', 'StarDraw 5.0 (StarImpress)')
fmts.add('presentation', 'sdd', 'sdd', 'StarImpress 5.0', 'StarImpress 5.0')
fmts.add('presentation', 'sdd3', 'sdd', 'StarDraw 3.0 (OpenOffice.org Impress)', 'StarDraw 3.0 (StarImpress)')
fmts.add('presentation', 'sdd4', 'sdd', 'StarImpress 4.0', 'StarImpress 4.0')
fmts.add('presentation', 'sti', 'sti', 'OpenOffice.org 1.0 Presentation Template', 'impress_StarOffice_XML_Impress_Template')
fmts.add('presentation', 'stp', 'stp', 'OpenDocument Presentation Template', 'impress8_template')
fmts.add('presentation', 'svg', 'svg', 'Scalable Vector Graphics', 'impress_svg_Export')
fmts.add('presentation', 'svm', 'svm', 'StarView Metafile', 'impress_svm_Export')
fmts.add('presentation', 'swf', 'swf', 'Macromedia Flash (SWF)', 'impress_flash_Export')
fmts.add('presentation', 'sxi', 'sxi', 'OpenOffice.org 1.0 Presentation', 'StarOffice XML (Impress)')
fmts.add('presentation', 'tiff', 'tiff', 'Tagged Image File Format', 'impress_tif_Export')
fmts.add('presentation', 'vor', 'vor', 'StarImpress 5.0 Template', 'StarImpress 5.0 Vorlage')
fmts.add('presentation', 'vor3', 'vor', 'StarDraw 3.0 Template (OpenOffice.org Impress)', 'StarDraw 3.0 Vorlage (StarImpress)')
fmts.add('presentation', 'vor4', 'vor', 'StarImpress 4.0 Template', 'StarImpress 4.0 Vorlage')
fmts.add('presentation', 'vor5', 'vor', 'StarDraw 5.0 Template (OpenOffice.org Impress)', 'StarDraw 5.0 Vorlage (StarImpress)')
fmts.add('presentation', 'wmf', 'wmf', 'Windows Metafile', 'impress_wmf_Export')
fmts.add('presentation', 'xhtml', 'xml', 'XHTML', 'XHTML Impress File')
fmts.add('presentation', 'xpm', 'xpm', 'X PixMap', 'impress_xpm_Export')

def version():
    print 'unoconv %s' % VERSION
    print 'Written by Dag Wieers <dag@wieers.com>'
    print 'Homepage at http://dag.wieers.com/home-made/unoconv/'
    print
    print 'platform %s/%s' % (os.name, sys.platform)
    print 'python %s' % sys.version
    print
    print 'build revision $Rev$'

def usage():
    print >>sys.stderr, 'usage: unoconv [options] <fmt> file1 file2'

def help():
    print >>sys.stderr, '''Convert from and to any format supported by OpenOffice

unoconv options:
  -b, --backend=format     specify the output format
  -c, --connection=string  use a custom connection string
  -d, --doctype=type       specify document type (document, graphics, presentation, spreadsheet)
  -l, --list               list the available output formats
  -s, --stdout             write output to stdout
'''

args = sys.argv[1:]
stdout = False
showlist = False
format = None
verbose = 0
doctype = None
server = 'localhost'
port = '2002'
connection = "uno:socket,host=%s,port=%s;urp;StarOffice.ComponentContext" % (server, port)

try:
    opts, args = getopt.getopt (args, 'b:c:d:hl:st:v',
                ['backend=', 'connection=', 'doctype=', 'help', 'list', 'stdout', 'verbose', 'version'] )
except getopt.error, exc:
    print >>sys.stderr, 'unoconv: %s, try unoconv -h for a list of all the options' % str(exc)
    sys.exit(1)

for opt, arg in opts:
    if opt in ['-h', '--help']:
        help()
        sys.exit(1)
    elif opt in ['-b', '--backend']:
        format = arg
    elif opt in ['-c', '--connection']:
        connection = arg
    elif opt in ['-d', '--doctype']:
        doctype = arg
    elif opt in ['-l', '--list']:
        showlist = True
    elif opt in ['-s', '--stdout']:
        stdout = True
    elif opt in ['-v', '--verbose']:
        verbose = verbose + 1
    elif opt in ['--version']:
        version()
        sys.exit(1)

if verbose >= 3:
    print 'Verbosity set to level %d' % (verbose - 1)

if doctype:
    if doctype.startswith('d'):
        doctype = 'document'
    elif doctype.startswith('g'):
        doctype = 'graphics'
    elif doctype.startswith('p'):
        doctype = 'presentation'
    elif doctype.startswith('s'):
        doctype = 'spreadsheet'

if showlist or format == 'list':
    if doctype:
        fmts.display(doctype)
    else:
        for t in doctypes:
            fmts.display(t)
    sys.exit(1)

if not format:
    format = 'pdf'
    doctype = 'document'

if not args:
    print >>sys.stderr, 'unoconv: you have to provide a filename as argument'
    print >>sys.stderr, 'Try `unoconv -h\' for more information.'
    sys.exit(1)

if doctype:
    outputfmt = fmts.bydoctype(doctype, format)
else:
    outputfmt = fmts.byname(format)
    if not outputfmt:
        outputfmt = fmts.byextension(format)

if not outputfmt:
    print >>sys.stderr, 'unoconv: format `%s\' is not known to unoconv.' % format
    sys.exit(1)
elif len(outputfmt) > 1:
    print >>sys.stderr, 'unoconv: format `%s\' is part of multiple doctypes %s, selecting `%s\'.' % (format, [fmt.doctype for fmt in outputfmt], outputfmt[0].doctype)

outputfmt = outputfmt[0]
if verbose > 0:
    print >>sys.stderr, 'Selected format: %s [.%s]' % (outputfmt.summary, outputfmt.extension)
    print >>sys.stderr, 'Selected filter: %s' % (outputfmt.filter)
    print >>sys.stderr, 'Used doctype:', outputfmt.doctype

rc = 0
doc = None

try:
    context = uno.getComponentContext()
    svcmgr = context.ServiceManager

    resolver = svcmgr.createInstanceWithContext("com.sun.star.bridge.UnoUrlResolver", context)
    unocontext = resolver.resolve(connection)
    unosvcmgr = unocontext.ServiceManager

    desktop = unosvcmgr.createInstanceWithContext("com.sun.star.frame.Desktop", unocontext)
    cwd = unohelper.systemPathToFileUrl( os.getcwd() )

    inputprops = (
                PropertyValue( "Hidden" , 0 , True, 0 ),
                )
    outputprops = (
                PropertyValue( "FilterName" , 0, outputfmt.filter , 0 ),
                PropertyValue( "Overwrite" , 0, True , 0 ),
                PropertyValue( "OutputStream", 0, OutputStream(), 0),
               )
    
    for inputfn in args:
        try:

            if verbose > 0:
                print >>sys.stderr, 'Input file:', inputfn

            if not os.path.exists(inputfn):
                print >>sys.stderr, 'unoconv: file `%s\' does not exist.' % inputfn
                rc = 1
                continue

            inputurl = unohelper.absolutize(cwd, unohelper.systemPathToFileUrl(inputfn))
            doc = desktop.loadComponentFromURL( inputurl , "_blank", 0, inputprops )
    
            if not doc:
                raise UnoException("File could not be loaded by OpenOffice", None)
    
            if not stdout:
                (outputfn, ext) = os.path.splitext(inputfn)
                outputfn = outputfn + '.' + outputfmt.extension
                outputurl = unohelper.absolutize( cwd, unohelper.systemPathToFileUrl(outputfn) )
                doc.storeToURL(outputurl, outputprops)
                if verbose > 0:
                    print >>sys.stderr, 'Output file:', outputfn
            else:
                doc.storeToURL("private:stream", outputprops)

        except IOException, e:
            print >>sys.stderr, "unoconv: error during conversion: %s" % e.Message
            print >>sys.stderr, "The provided document cannot be exported to %s [%s]." % (outputfmt.summary, outputfmt.extension)
            sys.exit(1)

        except CannotConvertException, e:
            print >>sys.stderr, "unoconv: convert error: %s" % e.Message

        except UnoException, e:
            print >>sys.stderr, "unoconv: error during conversion: %s" % e.Message
            print >>sys.stderr, "The provided document cannot be imported."
            rc = 1
            if doc:
                doc.dispose()
 
except NoConnectException, e:
    print >>sys.stderr, "unoconv: could not find an existing connection to Open Office at %s:%s." % (server, port)
    print >>sys.stderr, "Please start an ooffice instance by doing:\n    ooffice -headless -accept=\"socket,host=%s,port=%s;urp;\"" % (server, port)
    sys.exit(1)

except UnoException, e:
    print >>sys.stderr, "unoconv: error (%s) : %s" % (repr(e.__class__), e.Message)
    sys.exit(1)

sys.exit(rc)
