diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index cbb3c8458..297e2a175 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -320,6 +320,26 @@ class ServiceItem(object): self._raw_frames.append({'title': file_name, 'image': image, 'path': path}) self._new_item() + def add_from_presentation(self, path, file_name, image, displaytitle, notes): + """ + Add a slide from a presentation. + + ``path`` + The path of the presentation + + ``file_name`` + The filename of the presentation + + ``image`` + Full path (including file name) to the thumbnail + + ``displaytitle`` + The title to display on the list and remote + """ + self.service_item_type = ServiceItemType.Command + self._raw_frames.append({'title': file_name, 'image': image, 'path': path, 'displaytitle': displaytitle, 'notes': notes}) + self._new_item() + def get_service_repr(self, lite_save): """ This method returns some text which can be saved into the service @@ -362,7 +382,11 @@ class ServiceItem(object): 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']}) + #if len(slide['displaytitle'])>0: + service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path'], + 'displaytitle': slide['displaytitle'], 'notes': slide['notes']}) + #else: + # service_data.append({'title': slide['title'], 'image': slide['image'], 'path': slide['path']}) return {'header': service_header, 'data': service_data} def set_from_service(self, serviceitem, path=None): @@ -434,7 +458,10 @@ class ServiceItem(object): self.title = text_image['title'] if path: self.has_original_files = False - self.add_from_command(path, text_image['title'], text_image['image']) + if serviceitem['serviceitem']['header']['plugin']=='presentations': + self.add_from_presentation(path, text_image['title'], text_image['image'], text_image['displaytitle'], text_image['notes']) + else: + self.add_from_command(path, text_image['title'], text_image['image']) else: self.add_from_command(text_image['path'], text_image['title'], text_image['image']) self._new_item() diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 488c26c50..298f6be05 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -466,3 +466,10 @@ class ImpressDocument(PresentationDocument): if shape.supportsService("com.sun.star.drawing.Text"): text += shape.getString() + '\n' return text + + def get_titles_and_notes(self): + """ + Returns a list of titles and a list of notes for the current presentation + """ + # FIXME: somebody with impress expertise + return [],[] diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 695baddc5..5479d33ad 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -263,13 +263,20 @@ class PresentationMediaItem(MediaManagerItem): return False controller = self.controllers[service_item.processor] doc = controller.add_document(filename) + titles, notes = doc.get_titles_and_notes() if doc.get_thumbnail_path(1, True) is None: doc.load_presentation() i = 1 img = doc.get_thumbnail_path(i, True) if img: while img: - service_item.add_from_command(path, name, img) + title = name + if i <= len(titles): + title = titles[i-1] + note = '' + if i <= len(notes): + note = notes[i-1] + service_item.add_from_presentation(path, name, img, title, note) i += 1 img = doc.get_thumbnail_path(i, True) doc.close_presentation() diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 6ab723c9e..43f93cf2c 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -316,7 +316,7 @@ class MessageListener(object): hide_mode = message[2] file = item.get_frame_path() self.handler = item.processor - if self.handler == self.media_item.Automatic: + if self.handler == self.media_item.automatic: self.handler = self.media_item.findControllerByType(file) if not self.handler: return diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index a1d644be3..3e6a52361 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -132,6 +132,7 @@ class PowerpointDocument(PresentationDocument): return False self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count) self.create_thumbnails() + self.create_titles_and_notes() return True def create_thumbnails(self): @@ -316,6 +317,39 @@ 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 = [] + num = 0 + for slide in self.presentation.Slides: + try: + titles.append(slide.Shapes.Title.TextFrame.TextRange.Text + '\n') + num += 1 + notes = _get_text_from_shapes(slide.NotesPage.Shapes) + if len(notes) > 0: + notesfile = os.path.join(self.get_thumbnail_folder(), 'slideNotes%d.txt' % (num)) + with open(notesfile, mode='w') as fn: + fn.write(notes) + except Exception as e: + log.exception(e) + titles.append('\n') + titlesfile = os.path.join(self.get_thumbnail_folder(), 'titles.txt') + with open(titlesfile, mode='w') as fo: + fo.writelines(titles) + return + + def get_titles_and_notes(self): + """ + Reads the titles from the titles file and + the notes files and returns the contents + in a two lists + """ + return super().get_titles_and_notes() def _get_text_from_shapes(shapes): """ @@ -325,8 +359,8 @@ def _get_text_from_shapes(shapes): A set of shapes to search for text. """ text = '' - for index in range(shapes.Count): - shape = shapes(index + 1) - if shape.HasTextFrame: + for shape in shapes: + if shape.PlaceholderFormat.Type == 2 and 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 34f5129b8..0ac42add5 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -29,6 +29,7 @@ import os import logging +import zipfile if os.name == 'nt': from ctypes import cdll @@ -146,6 +147,80 @@ 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 + """ + # let's make sure we have a valid zipped presentation + if 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) + print ("slide count: " + str(len(nodes))) + + # 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(): + nodeType = '' + index = -1 + listToAdd = None + + match = re.search("slides/slide(.+)\.xml", zip_info.filename) + if match: + index = int(match.group(1))-1 + nodeType = 'ctrTitle' + listToAdd = titles + + match = re.search("notesSlides/notesSlide(.+)\.xml", zip_info.filename) + if match: + index = int(match.group(1))-1 + nodeType = 'body' + listToAdd = 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='" + nodeType + "']../../..//p:txBody//a:t", namespaces=namespaces) + if nodes and len(nodes)>0: + for node in nodes: + if len(text) > 0: + text += '\n' + text += node.text + print( 'slide file: ' + zip_info.filename + ' ' + text ) + listToAdd[index] = text + + print( titles ) + print( notes ) + + # now let's write the files + titlesfile = os.path.join(self.get_thumbnail_folder(), 'titles.txt') + with open(titlesfile, mode='w') as fo: + fo.writelines(titles) + for num in range(len(notes)): + notesfile = os.path.join(self.get_thumbnail_folder(), 'slideNotes%d.txt' % (num+1)) + with open(notesfile, mode='w') as fn: + fn.write(notes) + return + + + def close_presentation(self): """ Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being @@ -240,3 +315,11 @@ class PptviewDocument(PresentationDocument): Triggers the previous slide on the running presentation. """ self.controller.process.PrevStep(self.ppt_id) + + def get_titles_and_notes(self): + """ + Reads the titles from the titles file and + the notes files and returns the contents + in a two lists + """ + return super().get_titles_and_notes() diff --git a/openlp/plugins/presentations/lib/pptviewlib/ppttest.py b/openlp/plugins/presentations/lib/pptviewlib/ppttest.py index 6931cee6f..1477e62d7 100644 --- a/openlp/plugins/presentations/lib/pptviewlib/ppttest.py +++ b/openlp/plugins/presentations/lib/pptviewlib/ppttest.py @@ -28,6 +28,9 @@ ############################################################################### import sys +import zipfile +import re +from xml.etree import ElementTree from PyQt4 import QtGui, QtCore from ctypes import * from ctypes.wintypes import RECT @@ -174,6 +177,50 @@ class PPTViewer(QtGui.QWidget): int(self.widthEdit.text()), int(self.heightEdit.text())) filename = str(self.pptEdit.text().replace('/', '\\')) folder = str(self.folderEdit.text().replace('/', '\\')) + + if zipfile.is_zipfile(filename): + namespaces = {"p": "http://schemas.openxmlformats.org/presentationml/2006/main", + "a": "http://schemas.openxmlformats.org/drawingml/2006/main"} + with zipfile.ZipFile(filename) as zip_file: + with zip_file.open('ppt/presentation.xml') as pres: + tree = ElementTree.parse(pres) + nodes = tree.getroot().findall(".//p:sldIdLst/p:sldId", namespaces=namespaces) + print ("slide count: " + str(len(nodes))) + titles = [None for i in range(len(nodes))] + notes = [None for i in range(len(nodes))] + + for zip_info in zip_file.infolist(): + nodeType = '' + index = -1 + listToAdd = None + match = re.search("slides/slide(.+)\.xml", zip_info.filename) + if match: + index = int(match.group(1))-1 + nodeType = 'ctrTitle' + listToAdd = titles + match = re.search("notesSlides/notesSlide(.+)\.xml", zip_info.filename) + if match: + index = int(match.group(1))-1 + nodeType = 'body' + listToAdd = notes + + if len(nodeType)>0: + with zip_file.open(zip_info) as zipped_file: + tree = ElementTree.parse(zipped_file) + text = '' + + nodes = tree.getroot().findall(".//p:ph[@type='" + nodeType + "']../../..//p:txBody//a:t", namespaces=namespaces) + if nodes and len(nodes)>0: + for node in nodes: + if len(text) > 0: + text += '\n' + text += node.text + print( 'slide file: ' + zip_info.filename + ' ' + text ) + listToAdd[index] = text + + print( titles ) + print( notes ) + print(filename, folder) self.pptid = self.pptdll.OpenPPT(filename, None, rect, folder) print('id: ' + str(self.pptid)) 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 0a70b174a..f8db6aee7 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -289,6 +289,30 @@ class PresentationDocument(object): """ return '' + def get_titles_and_notes(self): + """ + Reads the titles from the titles file and + the notes files and returns the contents + in a two lists + """ + titles = [] + notes = [] + titlesfile = os.path.join(self.get_thumbnail_folder(), 'titles.txt') + with open(titlesfile) as fi: + titles = fi.readlines() + for index in range(len(titles)): + notesfile = os.path.join(self.get_thumbnail_folder(), 'slideNotes%d.txt' % (index + 1)) + note = '' + try: + if os.path.exists(notesfile): + with open(notesfile) as fn: + note = fn.read() + except: + note = '' + notes.append(note) + return titles, notes + + class PresentationController(object): """ diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index f12fbb290..cb0310a85 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -470,7 +470,10 @@ class HttpRouter(object): item['html'] = str(frame['html']) else: item['tag'] = str(index + 1) - item['text'] = str(frame['title']) + if current_item.name == 'presentations': + item['text'] = str(frame['displaytitle']) + '\n' + str(frame['notes']) + else: + item['text'] = str(frame['title']) item['html'] = str(frame['title']) item['selected'] = (self.live_controller.selected_row == index) data.append(item)