diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py
index a7cba33a0..b85070445 100644
--- a/openlp/core/lib/__init__.py
+++ b/openlp/core/lib/__init__.py
@@ -145,11 +145,13 @@ def build_icon(icon):
return button_icon
-def image_to_byte(image):
+def image_to_byte(image, base_64=True):
"""
Resize an image to fit on the current screen for the web and returns it as a byte stream.
:param image: The image to converted.
+ :param base_64: If True returns the image as Base64 bytes, otherwise the image is returned as a byte array.
+ To preserve original intention, this defaults to True
"""
log.debug('image_to_byte - start')
byte_array = QtCore.QByteArray()
@@ -158,6 +160,8 @@ def image_to_byte(image):
buffie.open(QtCore.QIODevice.WriteOnly)
image.save(buffie, "PNG")
log.debug('image_to_byte - end')
+ if not base_64:
+ return byte_array
# convert to base64 encoding so does not get missed!
return bytes(byte_array.toBase64()).decode('utf-8')
diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py
index cba393815..46fab10d2 100644
--- a/openlp/core/lib/imagemanager.py
+++ b/openlp/core/lib/imagemanager.py
@@ -106,7 +106,7 @@ class Image(object):
"""
secondary_priority = 0
- def __init__(self, path, source, background):
+ def __init__(self, path, source, background, width=-1, height=-1):
"""
Create an image for the :class:`ImageManager`'s cache.
@@ -115,7 +115,8 @@ class Image(object):
:class:`~openlp.core.lib.ImageSource` class.
:param background: A ``QtGui.QColor`` object specifying the colour to be used to fill the gabs if the image's
ratio does not match with the display ratio.
-
+ :param width: The width of the image, defaults to -1 meaning that the screen width will be used.
+ :param height: The height of the image, defaults to -1 meaning that the screen height will be used.
"""
self.path = path
self.image = None
@@ -124,6 +125,8 @@ class Image(object):
self.source = source
self.background = background
self.timestamp = 0
+ self.width = width
+ self.height = height
# FIXME: We assume that the path exist. The caller has to take care that it exists!
if os.path.exists(path):
self.timestamp = os.stat(path).st_mtime
@@ -210,13 +213,13 @@ class ImageManager(QtCore.QObject):
image.background = background
self._reset_image(image)
- def update_image_border(self, path, source, background):
+ def update_image_border(self, path, source, background, width=-1, height=-1):
"""
Border has changed so update the image affected.
"""
log.debug('update_image_border')
# Mark the image as dirty for a rebuild by setting the image and byte stream to None.
- image = self._cache[(path, source)]
+ image = self._cache[(path, source, width, height)]
if image.source == source:
image.background = background
self._reset_image(image)
@@ -237,12 +240,12 @@ class ImageManager(QtCore.QObject):
if not self.image_thread.isRunning():
self.image_thread.start()
- def get_image(self, path, source):
+ def get_image(self, path, source, width=-1, height=-1):
"""
Return the ``QImage`` from the cache. If not present wait for the background thread to process it.
"""
log.debug('getImage %s' % path)
- image = self._cache[(path, source)]
+ image = self._cache[(path, source, width, height)]
if image.image is None:
self._conversion_queue.modify_priority(image, Priority.High)
# make sure we are running and if not give it a kick
@@ -257,12 +260,12 @@ class ImageManager(QtCore.QObject):
self._conversion_queue.modify_priority(image, Priority.Low)
return image.image
- def get_image_bytes(self, path, source):
+ def get_image_bytes(self, path, source, width=-1, height=-1):
"""
Returns the byte string for an image. If not present wait for the background thread to process it.
"""
log.debug('get_image_bytes %s' % path)
- image = self._cache[(path, source)]
+ image = self._cache[(path, source, width, height)]
if image.image_bytes is None:
self._conversion_queue.modify_priority(image, Priority.Urgent)
# make sure we are running and if not give it a kick
@@ -272,14 +275,14 @@ class ImageManager(QtCore.QObject):
time.sleep(0.1)
return image.image_bytes
- def add_image(self, path, source, background):
+ def add_image(self, path, source, background, width=-1, height=-1):
"""
Add image to cache if it is not already there.
"""
log.debug('add_image %s' % path)
- if not (path, source) in self._cache:
- image = Image(path, source, background)
- self._cache[(path, source)] = image
+ if not (path, source, width, height) in self._cache:
+ image = Image(path, source, background, width, height)
+ self._cache[(path, source, width, height)] = image
self._conversion_queue.put((image.priority, image.secondary_priority, image))
# Check if the there are any images with the same path and check if the timestamp has changed.
for image in list(self._cache.values()):
@@ -308,7 +311,10 @@ class ImageManager(QtCore.QObject):
image = self._conversion_queue.get()[2]
# Generate the QImage for the image.
if image.image is None:
- image.image = resize_image(image.path, self.width, self.height, image.background)
+ # Let's see if the image was requested with specific dimensions
+ width = self.width if image.width == -1 else image.width
+ height = self.height if image.height == -1 else image.height
+ image.image = resize_image(image.path, width, height, image.background)
# Set the priority to Lowest and stop here as we need to process more important images first.
if image.priority == Priority.Normal:
self._conversion_queue.modify_priority(image, Priority.Lowest)
diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py
index ecd6ca5bd..4e7ff032a 100644
--- a/openlp/core/lib/serviceitem.py
+++ b/openlp/core/lib/serviceitem.py
@@ -39,8 +39,8 @@ import uuid
from PyQt4 import QtGui
-from openlp.core.common import RegistryProperties, Settings, translate
-from openlp.core.lib import ImageSource, build_icon, clean_tags, expand_tags
+from openlp.core.common import RegistryProperties, Settings, translate, AppLocation
+from openlp.core.lib import ImageSource, build_icon, clean_tags, expand_tags, create_thumb
log = logging.getLogger(__name__)
@@ -112,7 +112,17 @@ class ItemCapabilities(object):
The capability to edit the title of the item
``IsOptical``
- .Determines is the service_item is based on an optical device
+ Determines is the service_item is based on an optical device
+
+ ``HasDisplayTitle``
+ The item contains 'displaytitle' on every frame which should be
+ preferred over 'title' when displaying the item
+
+ ``HasNotes``
+ The item contains 'notes'
+
+ ``HasThumbnails``
+ The item has related thumbnails available
"""
CanPreview = 1
@@ -133,6 +143,9 @@ class ItemCapabilities(object):
CanAutoStartForLive = 16
CanEditTitle = 17
IsOptical = 18
+ HasDisplayTitle = 19
+ HasNotes = 20
+ HasThumbnails = 21
class ServiceItem(RegistryProperties):
@@ -272,18 +285,22 @@ class ServiceItem(RegistryProperties):
self.raw_footer = []
self.foot_text = '
'.join([_f for _f in self.raw_footer if _f])
- def add_from_image(self, path, title, background=None):
+ def add_from_image(self, path, title, background=None, thumbnail=None):
"""
Add an image slide to the service item.
:param path: The directory in which the image file is located.
:param title: A title for the slide in the service item.
:param background:
+ :param thumbnail: Optional alternative thumbnail, used for remote thumbnails.
"""
if background:
self.image_border = background
self.service_item_type = ServiceItemType.Image
- self._raw_frames.append({'title': title, 'path': path})
+ if not thumbnail:
+ self._raw_frames.append({'title': title, 'path': path})
+ else:
+ self._raw_frames.append({'title': title, 'path': path, 'image': thumbnail})
self.image_manager.add_image(path, ImageSource.ImagePlugin, self.image_border)
self._new_item()
@@ -301,16 +318,22 @@ class ServiceItem(RegistryProperties):
self._raw_frames.append({'title': title, 'raw_slide': raw_slide, 'verseTag': verse_tag})
self._new_item()
- def add_from_command(self, path, file_name, image):
+ def add_from_command(self, path, file_name, image, display_title=None, notes=None):
"""
Add a slide from a command.
:param path: The title of the slide in the service item.
:param file_name: The title of the slide in the service item.
:param image: The command of/for the slide.
+ :param display_title: Title to show in gui/webinterface, optional.
+ :param notes: Notes to show in the webinteface, optional.
"""
self.service_item_type = ServiceItemType.Command
- self._raw_frames.append({'title': file_name, 'image': image, 'path': path})
+ # If the item should have a display title but this frame doesn't have one, we make one up
+ if self.is_capable(ItemCapabilities.HasDisplayTitle) and not display_title:
+ display_title = translate('OpenLP.ServiceItem', '[slide %d]') % (len(self._raw_frames) + 1)
+ self._raw_frames.append({'title': file_name, 'image': image, 'path': path,
+ 'display_title': display_title, 'notes': notes})
self._new_item()
def get_service_repr(self, lite_save):
@@ -354,7 +377,8 @@ class ServiceItem(RegistryProperties):
service_data = [slide['title'] for slide in self._raw_frames]
elif self.service_item_type == ServiceItemType.Command:
for slide in self._raw_frames:
- service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path']})
+ service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path'],
+ 'display_title': slide['display_title'], 'notes': slide['notes']})
return {'header': service_header, 'data': service_data}
def set_from_service(self, service_item, path=None):
@@ -425,7 +449,8 @@ class ServiceItem(RegistryProperties):
self.add_from_command(text_image['path'], text_image['title'], text_image['image'])
elif path:
self.has_original_files = False
- self.add_from_command(path, text_image['title'], text_image['image'])
+ self.add_from_command(path, text_image['title'], text_image['image'],
+ text_image.get('display_title', ''), text_image.get('notes', ''))
else:
self.add_from_command(text_image['path'], text_image['title'], text_image['image'])
self._new_item()
diff --git a/openlp/core/ui/listpreviewwidget.py b/openlp/core/ui/listpreviewwidget.py
index 831eb182b..3909c6a31 100644
--- a/openlp/core/ui/listpreviewwidget.py
+++ b/openlp/core/ui/listpreviewwidget.py
@@ -94,8 +94,8 @@ class ListPreviewWidget(QtGui.QTableWidget, RegistryProperties):
Displays the given slide.
"""
self.service_item = service_item
- self.clear()
self.setRowCount(0)
+ self.clear()
self.setColumnWidth(0, width)
row = 0
text = []
diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py
index 369137694..d0ac1560e 100644
--- a/openlp/core/ui/servicemanager.py
+++ b/openlp/core/ui/servicemanager.py
@@ -1281,7 +1281,11 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
# Add the children to their parent tree_widget_item.
for count, frame in enumerate(service_item_from_item.get_frames()):
child = QtGui.QTreeWidgetItem(tree_widget_item)
- text = frame['title'].replace('\n', ' ')
+ # prefer to use a display_title
+ if service_item_from_item.is_capable(ItemCapabilities.HasDisplayTitle):
+ text = frame['display_title'].replace('\n', ' ')
+ else:
+ text = frame['title'].replace('\n', ' ')
child.setText(0, text[:40])
child.setData(0, QtCore.Qt.UserRole, count)
if service_item == item_count:
diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index 44c28deb6..b12438679 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -873,6 +873,7 @@ class SlideController(DisplayController, RegistryProperties):
if self.service_item.is_command():
Registry().execute('%s_slide' % self.service_item.name.lower(), [self.service_item, self.is_live, index])
self.update_preview()
+ self.selected_row = index
else:
self.preview_widget.change_slide(index)
self.slide_selected()
@@ -1042,8 +1043,8 @@ class SlideController(DisplayController, RegistryProperties):
self.display.image(to_display)
# reset the store used to display first image
self.service_item.bg_image_bytes = None
- self.update_preview()
self.selected_row = row
+ self.update_preview()
self.preview_widget.change_slide(row)
self.display.setFocus()
@@ -1055,6 +1056,7 @@ class SlideController(DisplayController, RegistryProperties):
"""
self.preview_widget.change_slide(row)
self.update_preview()
+ self.selected_row = row
def update_preview(self):
"""
diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py
index 7dab8b3d7..436a7e34b 100644
--- a/openlp/plugins/bibles/lib/manager.py
+++ b/openlp/plugins/bibles/lib/manager.py
@@ -85,7 +85,7 @@ class BibleFormat(object):
BibleFormat.CSV,
BibleFormat.OpenSong,
BibleFormat.WebDownload,
- BibleFormar.Zefania,
+ BibleFormat.Zefania,
]
diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py
index 36df55dac..d5892c250 100644
--- a/openlp/plugins/images/lib/mediaitem.py
+++ b/openlp/plugins/images/lib/mediaitem.py
@@ -551,6 +551,7 @@ class ImageMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.CanLoop)
service_item.add_capability(ItemCapabilities.CanAppend)
service_item.add_capability(ItemCapabilities.CanEditTitle)
+ service_item.add_capability(ItemCapabilities.HasThumbnails)
# force a nonexistent theme
service_item.theme = -1
missing_items_file_names = []
@@ -589,7 +590,7 @@ class ImageMediaItem(MediaManagerItem):
# Continue with the existing images.
for filename in images_file_names:
name = os.path.split(filename)[1]
- service_item.add_from_image(filename, name, background)
+ service_item.add_from_image(filename, name, background, os.path.join(self.service_path, name))
return True
def check_group_exists(self, new_group):
diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py
index d032b9161..a6e52411b 100644
--- a/openlp/plugins/presentations/lib/impresscontroller.py
+++ b/openlp/plugins/presentations/lib/impresscontroller.py
@@ -65,7 +65,7 @@ from PyQt4 import QtCore
from openlp.core.lib import ScreenList
from openlp.core.utils import delete_file, get_uno_command, get_uno_instance
-from .presentationcontroller import PresentationController, PresentationDocument
+from .presentationcontroller import PresentationController, PresentationDocument, TextType
log = logging.getLogger(__name__)
@@ -257,6 +257,7 @@ class ImpressDocument(PresentationDocument):
self.presentation.Display = ScreenList().current['number'] + 1
self.control = None
self.create_thumbnails()
+ self.create_titles_and_notes()
return True
def create_thumbnails(self):
@@ -450,22 +451,44 @@ class ImpressDocument(PresentationDocument):
:param slide_no: The slide the notes are required for, starting at 1
"""
- return self.__get_text_from_page(slide_no, True)
+ return self.__get_text_from_page(slide_no, TextType.Notes)
- def __get_text_from_page(self, slide_no, notes=False):
+ def __get_text_from_page(self, slide_no, text_type=TextType.SlideText):
"""
Return any text extracted from the presentation page.
:param slide_no: The slide the notes are required for, starting at 1
:param notes: A boolean. If set the method searches the notes of the slide.
+ :param text_type: A TextType. Enumeration of the types of supported text.
"""
text = ''
- pages = self.document.getDrawPages()
- page = pages.getByIndex(slide_no - 1)
- if notes:
- page = page.getNotesPage()
- for index in range(page.getCount()):
- shape = page.getByIndex(index)
- if shape.supportsService("com.sun.star.drawing.Text"):
- text += shape.getString() + '\n'
+ if TextType.Title <= text_type <= TextType.Notes:
+ pages = self.document.getDrawPages()
+ if 0 < slide_no <= pages.getCount():
+ page = pages.getByIndex(slide_no - 1)
+ if text_type == TextType.Notes:
+ page = page.getNotesPage()
+ for index in range(page.getCount()):
+ shape = page.getByIndex(index)
+ shape_type = shape.getShapeType()
+ if shape.supportsService("com.sun.star.drawing.Text"):
+ # if they requested title, make sure it is the title
+ if text_type != TextType.Title or shape_type == "com.sun.star.presentation.TitleTextShape":
+ text += shape.getString() + '\n'
return text
+
+ def create_titles_and_notes(self):
+ """
+ Writes the list of titles (one per slide) to 'titles.txt' and the notes to 'slideNotes[x].txt'
+ in the thumbnails directory
+ """
+ titles = []
+ notes = []
+ pages = self.document.getDrawPages()
+ for slide_no in range(1, pages.getCount() + 1):
+ titles.append(self.__get_text_from_page(slide_no, TextType.Title).replace('\n', ' ') + '\n')
+ note = self.__get_text_from_page(slide_no, TextType.Notes)
+ if len(note) == 0:
+ note = ' '
+ notes.append(note)
+ self.save_titles_and_notes(titles, notes)
diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py
index 5b503d50f..5467d117d 100644
--- a/openlp/plugins/presentations/lib/mediaitem.py
+++ b/openlp/plugins/presentations/lib/mediaitem.py
@@ -288,13 +288,14 @@ class PresentationMediaItem(MediaManagerItem):
os.path.join(doc.get_temp_folder(), 'mainslide001.png')):
doc.load_presentation()
i = 1
- image_file = 'mainslide%03d.png' % i
- image = os.path.join(doc.get_temp_folder(), image_file)
+ image = os.path.join(doc.get_temp_folder(), 'mainslide%03d.png' % i)
+ thumbnail = os.path.join(doc.get_thumbnail_folder(), 'slide%d.png' % i)
while os.path.isfile(image):
- service_item.add_from_image(image, name)
+ service_item.add_from_image(image, name, thumbnail=thumbnail)
i += 1
- image_file = 'mainslide%03d.png' % i
- image = os.path.join(doc.get_temp_folder(), image_file)
+ image = os.path.join(doc.get_temp_folder(), 'mainslide%03d.png' % i)
+ thumbnail = os.path.join(doc.get_thumbnail_folder(), 'slide%d.png' % i)
+ service_item.add_capability(ItemCapabilities.HasThumbnails)
doc.close_presentation()
return True
else:
@@ -323,8 +324,21 @@ class PresentationMediaItem(MediaManagerItem):
i = 1
img = doc.get_thumbnail_path(i, True)
if img:
+ # Get titles and notes
+ titles, notes = doc.get_titles_and_notes()
+ service_item.add_capability(ItemCapabilities.HasDisplayTitle)
+ if notes.count('') != len(notes):
+ service_item.add_capability(ItemCapabilities.HasNotes)
+ service_item.add_capability(ItemCapabilities.HasThumbnails)
while img:
- service_item.add_from_command(path, name, img)
+ # Use title and note if available
+ title = ''
+ if titles and len(titles) >= i:
+ title = titles[i - 1]
+ note = ''
+ if notes and len(notes) >= i:
+ note = notes[i - 1]
+ service_item.add_from_command(path, name, img, title, note)
i += 1
img = doc.get_thumbnail_path(i, True)
doc.close_presentation()
diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py
index f42e4f814..cf0f5bb99 100644
--- a/openlp/plugins/presentations/lib/powerpointcontroller.py
+++ b/openlp/plugins/presentations/lib/powerpointcontroller.py
@@ -27,7 +27,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
-This modul is for controlling powerpiont. PPT API documentation:
+This module is for controlling powerpoint. PPT API documentation:
`http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx`_
"""
import os
@@ -37,16 +37,17 @@ from openlp.core.common import is_win
if is_win():
from win32com.client import Dispatch
+ import win32com
import winreg
import win32ui
import pywintypes
from openlp.core.lib import ScreenList
+from openlp.core.common import Registry
from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate
from openlp.core.common import trace_error_handler
from .presentationcontroller import PresentationController, PresentationDocument
-
log = logging.getLogger(__name__)
@@ -136,6 +137,7 @@ class PowerpointDocument(PresentationDocument):
self.controller.process.Presentations.Open(self.file_path, False, False, True)
self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
self.create_thumbnails()
+ self.create_titles_and_notes()
# Powerpoint 2013 pops up when loading a file, so we minimize it again
if self.presentation.Application.Version == u'15.0':
try:
@@ -392,6 +394,28 @@ class PowerpointDocument(PresentationDocument):
"""
return _get_text_from_shapes(self.presentation.Slides(slide_no).NotesPage.Shapes)
+ def create_titles_and_notes(self):
+ """
+ Writes the list of titles (one per slide)
+ to 'titles.txt'
+ and the notes to 'slideNotes[x].txt'
+ in the thumbnails directory
+ """
+ titles = []
+ notes = []
+ for slide in self.presentation.Slides:
+ try:
+ text = slide.Shapes.Title.TextFrame.TextRange.Text
+ except Exception as e:
+ log.exception(e)
+ text = ''
+ titles.append(text.replace('\n', ' ').replace('\x0b', ' ') + '\n')
+ note = _get_text_from_shapes(slide.NotesPage.Shapes)
+ if len(note) == 0:
+ note = ' '
+ notes.append(note)
+ self.save_titles_and_notes(titles, notes)
+
def show_error_msg(self):
"""
Stop presentation and display an error message.
@@ -410,8 +434,8 @@ def _get_text_from_shapes(shapes):
:param shapes: A set of shapes to search for text.
"""
text = ''
- for index in range(shapes.Count):
- shape = shapes(index + 1)
- if shape.HasTextFrame:
- text += shape.TextFrame.TextRange.Text + '\n'
+ for shape in shapes:
+ if shape.PlaceholderFormat.Type == 2: # 2 from is enum PpPlaceholderType.ppPlaceholderBody
+ if shape.HasTextFrame and shape.TextFrame.HasText:
+ text += shape.TextFrame.TextRange.Text + '\n'
return text
diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py
index 7e03e322f..4aea501b4 100644
--- a/openlp/plugins/presentations/lib/pptviewcontroller.py
+++ b/openlp/plugins/presentations/lib/pptviewcontroller.py
@@ -29,6 +29,11 @@
import logging
import os
+import logging
+import zipfile
+import re
+from xml.etree import ElementTree
+
from openlp.core.common import is_win
@@ -127,14 +132,14 @@ class PptviewDocument(PresentationDocument):
temp_folder = self.get_temp_folder()
size = ScreenList().current['size']
rect = RECT(size.x(), size.y(), size.right(), size.bottom())
- file_path = os.path.normpath(self.file_path)
+ self.file_path = os.path.normpath(self.file_path)
preview_path = os.path.join(temp_folder, 'slide')
# Ensure that the paths are null terminated
- file_path = file_path.encode('utf-16-le') + b'\0'
+ self.file_path = self.file_path.encode('utf-16-le') + b'\0'
preview_path = preview_path.encode('utf-16-le') + b'\0'
if not os.path.isdir(temp_folder):
os.makedirs(temp_folder)
- self.ppt_id = self.controller.process.OpenPPT(file_path, None, rect, preview_path)
+ self.ppt_id = self.controller.process.OpenPPT(self.file_path, None, rect, preview_path)
if self.ppt_id >= 0:
self.create_thumbnails()
self.stop_presentation()
@@ -154,6 +159,68 @@ class PptviewDocument(PresentationDocument):
path = '%s\\slide%s.bmp' % (self.get_temp_folder(), str(idx + 1))
self.convert_thumbnail(path, idx + 1)
+ def create_titles_and_notes(self):
+ """
+ Extracts the titles and notes from the zipped file
+ and writes the list of titles (one per slide)
+ to 'titles.txt'
+ and the notes to 'slideNotes[x].txt'
+ in the thumbnails directory
+ """
+ titles = None
+ notes = None
+ filename = os.path.normpath(self.file_path)
+ # let's make sure we have a valid zipped presentation
+ if os.path.exists(filename) and zipfile.is_zipfile(filename):
+ namespaces = {"p": "http://schemas.openxmlformats.org/presentationml/2006/main",
+ "a": "http://schemas.openxmlformats.org/drawingml/2006/main"}
+ # open the file
+ with zipfile.ZipFile(filename) as zip_file:
+ # find the presentation.xml to get the slide count
+ with zip_file.open('ppt/presentation.xml') as pres:
+ tree = ElementTree.parse(pres)
+ nodes = tree.getroot().findall(".//p:sldIdLst/p:sldId", namespaces=namespaces)
+ # initialize the lists
+ titles = ['' for i in range(len(nodes))]
+ notes = ['' for i in range(len(nodes))]
+ # loop thru the file list to find slides and notes
+ for zip_info in zip_file.infolist():
+ node_type = ''
+ index = -1
+ list_to_add = None
+ # check if it is a slide
+ match = re.search("slides/slide(.+)\.xml", zip_info.filename)
+ if match:
+ index = int(match.group(1))-1
+ node_type = 'ctrTitle'
+ list_to_add = titles
+ # or a note
+ match = re.search("notesSlides/notesSlide(.+)\.xml", zip_info.filename)
+ if match:
+ index = int(match.group(1))-1
+ node_type = 'body'
+ list_to_add = notes
+ # if it is one of our files, index shouldn't be -1
+ if index >= 0:
+ with zip_file.open(zip_info) as zipped_file:
+ tree = ElementTree.parse(zipped_file)
+ text = ''
+ nodes = tree.getroot().findall(".//p:ph[@type='" + node_type + "']../../..//p:txBody//a:t",
+ namespaces=namespaces)
+ # if we found any content
+ if nodes and len(nodes) > 0:
+ for node in nodes:
+ if len(text) > 0:
+ text += '\n'
+ text += node.text
+ # Let's remove the \n from the titles and
+ # just add one at the end
+ if node_type == 'ctrTitle':
+ text = text.replace('\n', ' ').replace('\x0b', ' ') + '\n'
+ list_to_add[index] = text
+ # now let's write the files
+ self.save_titles_and_notes(titles, notes)
+
def close_presentation(self):
"""
Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
diff --git a/openlp/plugins/presentations/lib/pptviewlib/test.pptx b/openlp/plugins/presentations/lib/pptviewlib/test.pptx
new file mode 100644
index 000000000..c8beab172
Binary files /dev/null and b/openlp/plugins/presentations/lib/pptviewlib/test.pptx differ
diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py
index 3060bcdb0..c64d70016 100644
--- a/openlp/plugins/presentations/lib/presentationcontroller.py
+++ b/openlp/plugins/presentations/lib/presentationcontroller.py
@@ -293,6 +293,49 @@ class PresentationDocument(object):
"""
return ''
+ def get_titles_and_notes(self):
+ """
+ Reads the titles from the titles file and
+ the notes files and returns the content in two lists
+ """
+ titles = []
+ notes = []
+ titles_file = os.path.join(self.get_thumbnail_folder(), 'titles.txt')
+ if os.path.exists(titles_file):
+ try:
+ with open(titles_file) as fi:
+ titles = fi.read().splitlines()
+ except:
+ log.exception('Failed to open/read existing titles file')
+ titles = []
+ for slide_no, title in enumerate(titles, 1):
+ notes_file = os.path.join(self.get_thumbnail_folder(), 'slideNotes%d.txt' % slide_no)
+ note = ''
+ if os.path.exists(notes_file):
+ try:
+ with open(notes_file) as fn:
+ note = fn.read()
+ except:
+ log.exception('Failed to open/read notes file')
+ note = ''
+ notes.append(note)
+ return titles, notes
+
+ def save_titles_and_notes(self, titles, notes):
+ """
+ Performs the actual persisting of titles to the titles.txt
+ and notes to the slideNote%.txt
+ """
+ if titles:
+ titles_file = os.path.join(self.get_thumbnail_folder(), 'titles.txt')
+ with open(titles_file, mode='w') as fo:
+ fo.writelines(titles)
+ if notes:
+ for slide_no, note in enumerate(notes, 1):
+ notes_file = os.path.join(self.get_thumbnail_folder(), 'slideNotes%d.txt' % slide_no)
+ with open(notes_file, mode='w') as fn:
+ fn.write(note)
+
class PresentationController(object):
"""
@@ -427,3 +470,12 @@ class PresentationController(object):
def close_presentation(self):
pass
+
+
+class TextType(object):
+ """
+ Type Enumeration for Types of Text to request
+ """
+ Title = 0
+ SlideText = 1
+ Notes = 2
diff --git a/openlp/plugins/remotes/html/openlp.js b/openlp/plugins/remotes/html/openlp.js
index ffc9430c2..9f18c1552 100644
--- a/openlp/plugins/remotes/html/openlp.js
+++ b/openlp/plugins/remotes/html/openlp.js
@@ -87,16 +87,30 @@ window.OpenLP = {
var ul = $("#slide-controller > div[data-role=content] > ul[data-role=listview]");
ul.html("");
for (idx in data.results.slides) {
- var text = data.results.slides[idx]["tag"];
- if (text != "") text = text + ": ";
- text = text + data.results.slides[idx]["text"];
+ var indexInt = parseInt(idx,10);
+ var slide = data.results.slides[idx];
+ var text = slide["tag"];
+ if (text != "") {
+ text = text + ": ";
+ }
+ if (slide["title"]) {
+ text += slide["title"]
+ } else {
+ text += slide["text"];
+ }
+ if (slide["notes"]) {
+ text += ("