openlp/openlp/plugins/presentations/lib/powerpointmaccontroller.py

348 lines
13 KiB
Python

# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2020 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, either version 3 of the License, or #
# (at your option) any later version. #
# #
# 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, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
This module is for controlling keynote.
"""
import logging
import applescript
try:
import fitz
PYMUPDF_AVAILABLE = True
except ImportError:
PYMUPDF_AVAILABLE = False
from openlp.plugins.presentations.lib.applescriptbasecontroller import AppleScriptBaseController,\
AppleScriptBaseDocument
log = logging.getLogger(__name__)
POWERPOINT_MAC_APPLESCRIPT = """
on check_available()
set appID to "com.microsoft.PowerPoint"
set doesExist to false
try
tell application "Finder" to get application file id appID
set doesExist to true
end try
return doesExist
end check_available
on get_version()
return (version of application "Microsoft PowerPoint")
end get_version
on start_application()
tell application "Microsoft PowerPoint"
launch
-- Wait for powerpoint to start
tell application "System Events"
repeat while true
set PowerPointUp to (count of (every process whose name is "Microsoft PowerPoint")) > 0
if PowerPointUp then
exit repeat
else
delay 0.2
end if
end repeat
end tell
end tell
end start_application
-- helper function to write text to files
on write_file(fileName, theData)
set fileDescriptor to open for access the fileName with write permission
write theData to fileDescriptor as «class utf8»
close access fileDescriptor
end write_file
-- helper function to create empty file
on create_empty_file(f)
write_file(f, "")
return (POSIX path of f) as POSIX file
end create_empty_file
on create_thumbs(documentName, outputPath)
tell application "Microsoft PowerPoint"
-- Open document
open documentName
-- wait for document to load
repeat until document window 1 exists
delay 0.2
end repeat
-- Export slides to jpg
--save active presentation in outputPath as save as PNG
-- Export slides to pdf
save active presentation in my create_empty_file(outputPath & "/tmp.pdf") as save as PDF
-- Get the Presentation Notes and slide title
set the slideTitles to ""
set titleTypes to {placeholder type title placeholder, placeholder type center title placeholder, ¬
placeholder type vertical title placeholder}
repeat with tSlide in (get slides of active presentation)
-- Only handle non-hidden slides
if hidden of slide show transition of tSlide is false then
--text = slide.Shapes.Title.TextFrame.TextRange.Text
-- Get the title of the current slide
repeat with currentShape in (get shapes of tSlide)
tell currentShape to if has text frame then tell its text frame to if has text then
set aType to placeholder type of currentShape
if aType is not missing value and aType is in titleTypes then
set slideTitles to slideTitles & (content of its text range) & return
exit repeat
end if
end if
end repeat
set the noteText to ""
repeat with t_shape in (get shapes of notes page of tSlide)
tell t_shape to if has text frame then tell its text frame to if has text then
-- get the note of this slide
set the noteText to (content of its text range)
exit repeat
end if
end repeat
-- Save note to file
set noteFilename to outputPath & "/slideNotes" & (slide index of tSlide as string) & ".txt"
my write_file(noteFilename, noteText)
end if
end repeat
-- Save the titles to file
set titlesFilename to outputPath & "/titles.txt"
my write_file(titlesFilename, slideTitles)
close active presentation
end tell
end create_thumbs
on close_presentation()
tell application "Microsoft PowerPoint"
-- close presentation
close active presentation
-- Hide powerpoint
tell application "System Events"
set visible of application process "Microsoft PowerPoint" to false
end tell
end tell
end close_presentation
on start_presentation(documentName, new_top, new_left_pos, new_width, new_height, fullscreen)
tell application "Microsoft PowerPoint"
-- Open document
open documentName
-- wait for document to load
repeat until document window 1 exists
delay 0.2
end repeat
-- disable presenter console
set showpresenterview of slide show settings of active presentation to 0
-- set presentation to show presentation in window
set show type of slide show settings of active presentation to slide show type window
-- start presentation
run slide show slide show settings of active presentation
-- set the size and postion on presentation window
set top of slide show window of active presentation to new_top
set left position of slide show window of active presentation to new_left_pos
if fullscreen is true then
-- put it in fullscreen mode
tell application "System Events" to tell process "Microsoft PowerPoint"
set value of attribute "AXFullScreen" of front window to true
end tell
else
set height of slide show window of active presentation to new_height
set width of slide show window of active presentation to new_width
end if
-- move to the front
activate
end tell
end start_presentation
on stop_presentation()
tell application "Microsoft PowerPoint"
-- stop presenting - self.presentation.SlideShowWindow.View.Exit()
exit slide show slide show view of slide show window 1
-- close presentation
close active presentation
-- Hide powerpoint
tell application "System Events"
set visible of application process "Microsoft PowerPoint" to false
end tell
end tell
end stop_presentation
on is_loaded(documentPath)
tell application "Microsoft PowerPoint"
return (full name of active presentation is (documentPath as string))
end tell
end is_loaded
on is_active()
tell application "Microsoft PowerPoint"
-- when the presentation window is out of focus it is paused, so check for both paused and running
return (slide state of slide show view of slide show window of active presentation is ¬
slide show state running) ¬
or (slide state of slide show view of slide show window of active presentation is ¬
slide show state paused)
end tell
end is_active
on next_slide()
tell application "Microsoft PowerPoint"
--activate
-- self.presentation.SlideShowWindow.View.Next()
go to next slide slide show view of slide show window 1
-- Print(?) slide number
-- slide number of slide of slideshow view of slide show window of active presentation
end tell
end next_slide
on previous_slide()
tell application "Microsoft PowerPoint"
--activate
go to previous slide slide show view of slide show window 1
-- Print(?) slide number
-- self.presentation.SlideShowWindow.View.Slide.SlideNumber
-- slide number of slide of view of slide show window of active presentation
end tell
end previous_slide
on goto_slide(slideNumber)
tell application "Microsoft PowerPoint"
activate
-- self.presentation.SlideShowWindow.View.GotoSlide(slideNumber)
--go to slide slideNumber of slide show window of active presentation
-- there seems to be no applescipt-way of going to a specific slide, so send keystroke
tell application "System Events"
keystroke (slideNumber as string)
key code 36
end tell
end tell
end goto_slide
on get_current_slide()
tell application "Microsoft PowerPoint"
--activate
-- Print(?) slide number
return slide number of slide of slide show view of slide show window of active presentation
end tell
end get_current_slide
on blank()
tell application "Microsoft PowerPoint"
--activate
-- self.presentation.SlideShowWindow.View.State = 3
--set state of slide of view of slide show window of active presentation to 3
set slide state of slide show view of slide show window 1 to slide show state black screen
end tell
end blank
on unblank()
tell application "Microsoft PowerPoint"
--activate
-- self.presentation.SlideShowWindow.View.State = 1
set slide state of slide show view of slide show window 1 to slide show state running
end tell
end unblank
"""
class PowerPointMacController(AppleScriptBaseController):
"""
Class to control interactions with a Powerpoint for Mac.
As well as triggering the correct activities based on the users input.
"""
log.info('PowerPointMacController loaded')
base_class = False
def __init__(self, plugin):
"""
Initialise the class
"""
log.debug('Initialising')
super(PowerPointMacController, self).__init__(plugin, 'PowerPointMac', PowerPointMacDocument)
# Compiled script expected to be set by subclasses
try:
self.applescript = applescript.AppleScript(POWERPOINT_MAC_APPLESCRIPT)
except applescript.ScriptError:
log.exception('Compilation of Powerpoint applescript failed')
self.supports = ['ppt', 'pps', 'pptx', 'ppsx', 'pptm']
self.also_supports = ['odp']
def check_available(self):
"""
Program is able to run on this machine.
"""
ret = super().check_available()
if ret:
# Powerpoint is available! now check if the version is 15 (Powerpoint 2016) or above
try:
version = self.applescript.call('get_version')
except Exception:
log.exception('script execution failed')
return False
try:
major_version = int(version.split('.')[0])
except ValueError:
return False
if major_version >= 15:
return True
class PowerPointMacDocument(AppleScriptBaseDocument):
"""
Class which holds information and controls a single presentation.
"""
def __init__(self, controller, document_path):
"""
Constructor, store information about the file and initialise.
:param controller:
:param pathlib.Path document_path: Path to the document to load
:rtype: None
"""
log.debug('Init Presentation Applescript')
super().__init__(controller, document_path)
self.presentation_loaded = False
self.is_blank = False
def load_presentation(self):
ret = super().load_presentation()
if ret:
# we make powerpoint export to PDF and then convert the pdf to thumbnails
if PYMUPDF_AVAILABLE:
pdf_file = self.get_thumbnail_folder() / 'tmp.pdf'
# if the pdf file does not exists anymore it is assume the convertion has been done
if not pdf_file.exists():
return True
log.debug('converting pdf to png thumbnails using PyMuPDF')
pdf = fitz.open(str(pdf_file))
for i, page in enumerate(pdf, start=1):
src_size = page.bound().round()
# keep aspect ratio
scale = min(640 / src_size.width, 480 / src_size.height)
m = fitz.Matrix(scale, scale)
page.getPixmap(m, alpha=False).writeImage(str(self.get_thumbnail_folder() /
'slide{num}.png'.format(num=i)))
pdf.close()
# delete pdf
pdf_file.unlink()
return ret