openlp/openlp/plugins/songs/lib/importers/openoffice.py

256 lines
10 KiB
Python

# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# 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 General Public License for #
# more details. #
# #
# You should have received a copy of the GNU 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 #
###############################################################################
import logging
import time
from PyQt5 import QtCore
from openlp.core.common import get_uno_command, get_uno_instance, is_win, normalize_str
from openlp.core.common.i18n import translate
from .songimport import SongImport
log = logging.getLogger(__name__)
if is_win():
from win32com.client import Dispatch
NoConnectException = Exception
else:
import uno
from com.sun.star.connection import NoConnectException
from com.sun.star.beans import PropertyValue
try:
from com.sun.star.style.BreakType import PAGE_BEFORE, PAGE_AFTER, PAGE_BOTH
except ImportError:
PAGE_BEFORE = 4
PAGE_AFTER = 5
PAGE_BOTH = 6
class OpenOfficeImport(SongImport):
"""
Import songs from Impress/Powerpoint docs using Impress
"""
def __init__(self, manager, **kwargs):
"""
Initialise the class. Requires a songmanager class which is passed to SongImport for writing song to disk
"""
super(OpenOfficeImport, self).__init__(manager, **kwargs)
self.document = None
self.process_started = False
def do_import(self):
if not isinstance(self.import_source, list):
return
try:
self.start_ooo()
except NoConnectException as exc:
self.log_error(
self.import_source[0],
translate('SongsPlugin.SongImport', 'Cannot access OpenOffice or LibreOffice'))
log.error(exc)
return
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
for file_path in self.import_source:
if self.stop_import_flag:
break
if file_path.is_file():
self.open_ooo_file(file_path)
if self.document:
self.process_ooo_document()
self.close_ooo_file()
else:
self.log_error(self.file_path, translate('SongsPlugin.SongImport', 'Unable to open file'))
else:
self.log_error(self.file_path, translate('SongsPlugin.SongImport', 'File not found'))
self.close_ooo()
def process_ooo_document(self):
"""
Handle the import process for OpenOffice files. This method facilitates
allowing subclasses to handle specific types of OpenOffice files.
"""
if self.document.supportsService("com.sun.star.presentation.PresentationDocument"):
self.process_presentation()
if self.document.supportsService("com.sun.star.text.TextDocument"):
self.process_doc()
def start_ooo(self):
"""
Start OpenOffice.org process
TODO: The presentation/Impress plugin may already have it running
"""
if is_win():
self.start_ooo_process()
self.desktop = self.ooo_manager.createInstance('com.sun.star.frame.Desktop')
else:
context = uno.getComponentContext()
resolver = context.ServiceManager.createInstanceWithContext('com.sun.star.bridge.UnoUrlResolver', context)
uno_instance = None
loop = 0
while uno_instance is None and loop < 5:
try:
uno_instance = get_uno_instance(resolver)
except NoConnectException:
time.sleep(0.1)
log.exception("Failed to resolve uno connection")
self.start_ooo_process()
loop += 1
else:
manager = uno_instance.ServiceManager
self.desktop = manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
return
raise Exception('Unable to start LibreOffice')
def start_ooo_process(self):
"""
Start the OO Process
"""
try:
if is_win():
self.ooo_manager = Dispatch('com.sun.star.ServiceManager')
self.ooo_manager._FlagAsMethod('Bridge_GetStruct')
self.ooo_manager._FlagAsMethod('Bridge_GetValueObject')
else:
cmd = get_uno_command()
process = QtCore.QProcess()
process.startDetached(cmd)
self.process_started = True
except Exception:
log.exception("start_ooo_process failed")
def open_ooo_file(self, file_path):
"""
Open the passed file in OpenOffice.org Impress
"""
self.file_path = file_path
url = file_path.as_uri()
properties = []
properties.append(self.create_property('Hidden', True))
properties = tuple(properties)
try:
self.document = self.desktop.loadComponentFromURL(url, '_blank', 0, properties)
if not self.document.supportsService("com.sun.star.presentation.PresentationDocument") and not \
self.document.supportsService("com.sun.star.text.TextDocument"):
self.close_ooo_file()
else:
self.import_wizard.increment_progress_bar('Processing file {file_path}'.format(file_path=file_path), 0)
except AttributeError:
log.exception("open_ooo_file failed: {url}".format(url=url))
return
def create_property(self, name, value):
"""
Create an OOo style property object which are passed into some Uno methods.
"""
log.debug('create property OpenOffice')
if is_win():
property_object = self.ooo_manager.Bridge_GetStruct('com.sun.star.beans.PropertyValue')
else:
property_object = PropertyValue()
property_object.Name = name
property_object.Value = value
return property_object
def close_ooo_file(self):
"""
Close file.
"""
try:
self.document.close(True)
except Exception:
log.exception('Exception in close_ooo_file - trying to ignore it.')
self.document = None
def close_ooo(self):
"""
Close OOo. But only if we started it and not on windows
"""
if self.process_started:
try:
self.desktop.terminate()
except Exception:
log.exception('Exception in close_ooo - trying to ignore it.')
def process_presentation(self):
"""
Process the file
"""
doc = self.document
slides = doc.getDrawPages()
text = ''
for slide_no in range(slides.getCount()):
if self.stop_import_flag:
self.import_wizard.increment_progress_bar('Import cancelled', 0)
return
slide = slides.getByIndex(slide_no)
slide_text = ''
for idx in range(slide.getCount()):
shape = slide.getByIndex(idx)
if shape.supportsService("com.sun.star.drawing.Text"):
if shape.getString().strip() != '':
slide_text += shape.getString().strip() + '\n\n'
if slide_text.strip() == '':
slide_text = '\f'
text += slide_text
self.process_songs_text(text)
return
def process_doc(self):
"""
Process the doc file, a paragraph at a time
"""
text = ''
paragraphs = self.document.getText().createEnumeration()
while paragraphs.hasMoreElements():
para_text = ''
paragraph = paragraphs.nextElement()
if paragraph.supportsService("com.sun.star.text.Paragraph"):
text_portions = paragraph.createEnumeration()
while text_portions.hasMoreElements():
text_portion = text_portions.nextElement()
if text_portion.BreakType in (PAGE_BEFORE, PAGE_BOTH):
para_text += '\f'
para_text += text_portion.getString()
if text_portion.BreakType in (PAGE_AFTER, PAGE_BOTH):
para_text += '\f'
text += para_text + '\n'
self.process_songs_text(text)
def process_songs_text(self, text):
"""
Process the songs text
:param text: The text.
"""
song_texts = normalize_str(text).split('\f')
self.set_defaults()
for song_text in song_texts:
if song_text.strip():
self.process_song_text(song_text.strip())
if self.check_complete():
self.finish()
self.set_defaults()
if self.check_complete():
self.finish()