forked from openlp/openlp
Read titles and notes from the presentation files and display them on the remote
This commit is contained in:
parent
e75ef1f261
commit
348ebee661
@ -320,6 +320,26 @@ class ServiceItem(object):
|
|||||||
self._raw_frames.append({'title': file_name, 'image': image, 'path': path})
|
self._raw_frames.append({'title': file_name, 'image': image, 'path': path})
|
||||||
self._new_item()
|
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):
|
def get_service_repr(self, lite_save):
|
||||||
"""
|
"""
|
||||||
This method returns some text which can be saved into the service
|
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]
|
service_data = [slide['title'] for slide in self._raw_frames]
|
||||||
elif self.service_item_type == ServiceItemType.Command:
|
elif self.service_item_type == ServiceItemType.Command:
|
||||||
for slide in self._raw_frames:
|
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}
|
return {'header': service_header, 'data': service_data}
|
||||||
|
|
||||||
def set_from_service(self, serviceitem, path=None):
|
def set_from_service(self, serviceitem, path=None):
|
||||||
@ -434,6 +458,9 @@ class ServiceItem(object):
|
|||||||
self.title = text_image['title']
|
self.title = text_image['title']
|
||||||
if path:
|
if path:
|
||||||
self.has_original_files = False
|
self.has_original_files = False
|
||||||
|
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'])
|
self.add_from_command(path, text_image['title'], text_image['image'])
|
||||||
else:
|
else:
|
||||||
self.add_from_command(text_image['path'], text_image['title'], text_image['image'])
|
self.add_from_command(text_image['path'], text_image['title'], text_image['image'])
|
||||||
|
@ -466,3 +466,10 @@ class ImpressDocument(PresentationDocument):
|
|||||||
if shape.supportsService("com.sun.star.drawing.Text"):
|
if shape.supportsService("com.sun.star.drawing.Text"):
|
||||||
text += shape.getString() + '\n'
|
text += shape.getString() + '\n'
|
||||||
return text
|
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 [],[]
|
||||||
|
@ -263,13 +263,20 @@ class PresentationMediaItem(MediaManagerItem):
|
|||||||
return False
|
return False
|
||||||
controller = self.controllers[service_item.processor]
|
controller = self.controllers[service_item.processor]
|
||||||
doc = controller.add_document(filename)
|
doc = controller.add_document(filename)
|
||||||
|
titles, notes = doc.get_titles_and_notes()
|
||||||
if doc.get_thumbnail_path(1, True) is None:
|
if doc.get_thumbnail_path(1, True) is None:
|
||||||
doc.load_presentation()
|
doc.load_presentation()
|
||||||
i = 1
|
i = 1
|
||||||
img = doc.get_thumbnail_path(i, True)
|
img = doc.get_thumbnail_path(i, True)
|
||||||
if img:
|
if img:
|
||||||
while 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
|
i += 1
|
||||||
img = doc.get_thumbnail_path(i, True)
|
img = doc.get_thumbnail_path(i, True)
|
||||||
doc.close_presentation()
|
doc.close_presentation()
|
||||||
|
@ -316,7 +316,7 @@ class MessageListener(object):
|
|||||||
hide_mode = message[2]
|
hide_mode = message[2]
|
||||||
file = item.get_frame_path()
|
file = item.get_frame_path()
|
||||||
self.handler = item.processor
|
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)
|
self.handler = self.media_item.findControllerByType(file)
|
||||||
if not self.handler:
|
if not self.handler:
|
||||||
return
|
return
|
||||||
|
@ -132,6 +132,7 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
return False
|
return False
|
||||||
self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
|
self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
|
||||||
self.create_thumbnails()
|
self.create_thumbnails()
|
||||||
|
self.create_titles_and_notes()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def create_thumbnails(self):
|
def create_thumbnails(self):
|
||||||
@ -316,6 +317,39 @@ class PowerpointDocument(PresentationDocument):
|
|||||||
"""
|
"""
|
||||||
return _get_text_from_shapes(self.presentation.Slides(slide_no).NotesPage.Shapes)
|
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):
|
def _get_text_from_shapes(shapes):
|
||||||
"""
|
"""
|
||||||
@ -325,8 +359,8 @@ def _get_text_from_shapes(shapes):
|
|||||||
A set of shapes to search for text.
|
A set of shapes to search for text.
|
||||||
"""
|
"""
|
||||||
text = ''
|
text = ''
|
||||||
for index in range(shapes.Count):
|
for shape in shapes:
|
||||||
shape = shapes(index + 1)
|
if shape.PlaceholderFormat.Type == 2 and shape.HasTextFrame and shape.TextFrame.HasText:
|
||||||
if shape.HasTextFrame:
|
|
||||||
text += shape.TextFrame.TextRange.Text + '\n'
|
text += shape.TextFrame.TextRange.Text + '\n'
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
import zipfile
|
||||||
|
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
from ctypes import cdll
|
from ctypes import cdll
|
||||||
@ -146,6 +147,80 @@ class PptviewDocument(PresentationDocument):
|
|||||||
path = '%s\\slide%s.bmp' % (self.get_temp_folder(), str(idx + 1))
|
path = '%s\\slide%s.bmp' % (self.get_temp_folder(), str(idx + 1))
|
||||||
self.convert_thumbnail(path, 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):
|
def close_presentation(self):
|
||||||
"""
|
"""
|
||||||
Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
|
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.
|
Triggers the previous slide on the running presentation.
|
||||||
"""
|
"""
|
||||||
self.controller.process.PrevStep(self.ppt_id)
|
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()
|
||||||
|
@ -28,6 +28,9 @@
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import zipfile
|
||||||
|
import re
|
||||||
|
from xml.etree import ElementTree
|
||||||
from PyQt4 import QtGui, QtCore
|
from PyQt4 import QtGui, QtCore
|
||||||
from ctypes import *
|
from ctypes import *
|
||||||
from ctypes.wintypes import RECT
|
from ctypes.wintypes import RECT
|
||||||
@ -174,6 +177,50 @@ class PPTViewer(QtGui.QWidget):
|
|||||||
int(self.widthEdit.text()), int(self.heightEdit.text()))
|
int(self.widthEdit.text()), int(self.heightEdit.text()))
|
||||||
filename = str(self.pptEdit.text().replace('/', '\\'))
|
filename = str(self.pptEdit.text().replace('/', '\\'))
|
||||||
folder = str(self.folderEdit.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)
|
print(filename, folder)
|
||||||
self.pptid = self.pptdll.OpenPPT(filename, None, rect, folder)
|
self.pptid = self.pptdll.OpenPPT(filename, None, rect, folder)
|
||||||
print('id: ' + str(self.pptid))
|
print('id: ' + str(self.pptid))
|
||||||
|
BIN
openlp/plugins/presentations/lib/pptviewlib/test.pptx
Normal file
BIN
openlp/plugins/presentations/lib/pptviewlib/test.pptx
Normal file
Binary file not shown.
@ -289,6 +289,30 @@ class PresentationDocument(object):
|
|||||||
"""
|
"""
|
||||||
return ''
|
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):
|
class PresentationController(object):
|
||||||
"""
|
"""
|
||||||
|
@ -470,6 +470,9 @@ class HttpRouter(object):
|
|||||||
item['html'] = str(frame['html'])
|
item['html'] = str(frame['html'])
|
||||||
else:
|
else:
|
||||||
item['tag'] = str(index + 1)
|
item['tag'] = str(index + 1)
|
||||||
|
if current_item.name == 'presentations':
|
||||||
|
item['text'] = str(frame['displaytitle']) + '\n' + str(frame['notes'])
|
||||||
|
else:
|
||||||
item['text'] = str(frame['title'])
|
item['text'] = str(frame['title'])
|
||||||
item['html'] = str(frame['title'])
|
item['html'] = str(frame['title'])
|
||||||
item['selected'] = (self.live_controller.selected_row == index)
|
item['selected'] = (self.live_controller.selected_row == index)
|
||||||
|
Loading…
Reference in New Issue
Block a user