Skip to content

Instantly share code, notes, and snippets.

@maffoo
Created May 18, 2011 00:19
Show Gist options
  • Save maffoo/977752 to your computer and use it in GitHub Desktop.
Save maffoo/977752 to your computer and use it in GitHub Desktop.
Macro to expand animations in a LibreOffice/OpenOffice.org presentation into separate slides, suitable for pdf export.
' ExpandAnimations
' Copyright 2010 Matthew Neeley.
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
' You should have received a copy of the GNU Lesser General Public License
' along with this program. If not, see <http://www.gnu.org/licenses/>.
' future enhancements:
' - delete unnecessary animations from expanded slides
' - turn appear/disappear animations or fades into slide transitions
' - add progress dialog and make the operation modal
' - use the standard presenter machinery to "play" anims and grab frames.
' in particular, this would let us handle other types of animation like
' objects moving, resizing, changing colors, etc.
Private ANIMSET as String
Private ENUMACCESS as String
Private VISATTR as String
sub ExpandAnimations
ANIMSET = "com.sun.star.animations.XAnimateSet"
ENUMACCESS = "com.sun.star.container.XEnumerationAccess"
VISATTR = "Visibility"
doc = thisComponent
numSlides = doc.getDrawPages().getCount()
' go through pages in reverse order
for i = numSlides-1 to 0 step -1
slide = doc.drawPages(i)
if hasAnimation(slide) then
n = countAnimationSteps(slide)
if n > 1 then
origName = slide.Name
replicateSlide(doc, slide, n)
visArray = getShapeVisibility(slide, n)
for frame = 0 to n-1
currentSlide = doc.drawPages(i + frame)
currentSlide.Name = origName & " (" & CStr(frame+1) & ")"
removeInvisibleShapes(currentSlide, visArray, frame)
next
end if
end if
next
finalSlides = doc.getDrawPages().getCount()
MsgBox("Done! Expanded " & CStr(numSlides) & " slides to " & CStr(finalSlides) & ".")
end sub
' get a list of all shapes whose visibility is changed during the animation
function getAnimatedShapes(slide as Object)
shapes = Array() ' start with an empty array
if not hasAnimation(slide) then
getAnimatedShapes = shapes
exit function
end if
mainSequence = getMainSequence(slide)
clickNodes = mainSequence.createEnumeration()
while clickNodes.hasMoreElements()
clickNode = clickNodes.nextElement()
groupNodes = clickNode.createEnumeration()
while groupNodes.hasMoreElements()
groupNode = groupNodes.nextElement()
effectNodes = groupNode.createEnumeration()
while effectNodes.hasMoreElements()
effectNode = effectNodes.nextElement()
animNodes = effectNode.createEnumeration()
while animNodes.hasMoreElements()
animNode = animNodes.nextElement()
if isVisibilityAnimation(animNode) then
target = animNode.target
' if we haven't seen this shape yet, add it to the array
if not containsObject(shapes, target) then
newUBound = UBound(shapes) + 1
reDim preserve shapes(newUBound)
shapes(newUBound) = target
end if
end if
wend
wend
wend
wend
getAnimatedShapes = shapes
end function
' create a 2-D array giving the visibility of each animated
' shape for each frame in the expanded animation
function getShapeVisibility(slide as Object, nFrames as Integer)
shapes = getAnimatedShapes(slide)
dim visibility(UBound(shapes), nFrames-1) as Boolean
' loop over all animated shapes
for n = 0 to UBound(shapes)
shape = shapes(n)
visKnown = false
visCurrent = false
' iterate over the animations for this slide,
' looking for those that change the visibility
' of the current shape
mainSequence = getMainSequence(slide)
if HasUnoInterfaces(mainSequence, ENUMACCESS) then
clickNodes = mainSequence.createEnumeration()
currentFrame = 0
while clickNodes.hasMoreElements()
clickNode = clickNodes.nextElement()
groupNodes = clickNode.createEnumeration()
while groupNodes.hasMoreElements()
groupNode = groupNodes.nextElement()
effectNodes = groupNode.createEnumeration()
while effectNodes.hasMoreElements()
effectNode = effectNodes.nextElement()
animNodes = effectNode.createEnumeration()
while animNodes.hasMoreElements()
animNode = animNodes.nextElement()
if isVisibilityAnimation(animNode) then
target = animNode.target
' if this is the shape we want, check the visibility
if EqualUnoObjects(shape, target) then
visCurrent = animNode.To
' if this is the first time we've seen this
' shape, set the visibility on the previous frames
if visKnown = false then
for i = 0 to currentFrame
visibility(n, i) = not visCurrent
next
visKnown = true
end if
end if
end if
wend
wend
wend
currentFrame = currentFrame + 1
visibility(n, currentFrame) = visCurrent
wend
end if
next
getShapeVisibility = visibility
end function
' remove from the given slide all shapes that are invisible in the specified frame
sub removeInvisibleShapes(slide as Object, visibility, frame as Integer)
shapes = getAnimatedShapes(slide)
for n = 0 to UBound(shapes)
if visibility(n, frame) = false then
slide.remove(shapes(n))
end if
next
end sub
' checks if the given animation node changes a shape's visibility
function isVisibilityAnimation(animNode as Object) as Boolean
isVisibilityAnimation = HasUnoInterfaces(animNode, ANIMSET) and _
(animNode.AttributeName = VISATTR)
end function
' check if an object (needle) is contained in an array (haystack)
function containsObject(haystack as Object, needle as Object) as Boolean
containsObject = false
for each item in haystack
if EqualUnoObjects(item, needle) then
containsObject = true
exit function
end if
next item
end function
' determine whether the given drawPage has animations attached
function hasAnimation(slide as Object) as Boolean
mainSequence = getMainSequence(slide)
hasAnimation = HasUnoInterfaces(mainSequence, ENUMACCESS)
end function
' count the number of frames in the animation for the given slide
function countAnimationSteps(slide as Object) as Integer
mainSequence = getMainSequence(slide)
countAnimationSteps = countElements(mainSequence) + 1
end function
' count the number of elements in an enumerable object
function countElements(enumerable as Object) as Integer
oEnum = enumerable.createEnumeration()
n = 0
while oEnum.hasMoreElements()
n = n + 1
oEnum.nextElement()
wend
countElements = n
end function
' make n-1 copies of the given slide in the given doc
' when done, the slide will appear a total of n times
' note that doc.duplicate adds copies immediately after
' the page being copied
function replicateSlide(doc, slide, n)
for i = 1 to n-1
doc.duplicate(slide)
next
end function
' get the main sequence from the given draw page
function getMainSequence(oPage as Object) as Object
on error resume next
mainSeq = com.sun.star.presentation.EffectNodeType.MAIN_SEQUENCE
oNodes = oPage.AnimationNode.createEnumeration()
while oNodes.hasMoreElements()
oNode = oNodes.nextElement()
if getNodeType(oNode) = mainSeq then
getMainSequence = oNode
exit function
end if
wend
end function
' get the type of a node
function getNodeType(oNode as Object) as Integer
on error resume next
for each oData in oNode.UserData
if oData.Name = "node-type" then
getNodeType = oData.Value
exit function
end if
next oData
end function
' get the class of an effect
function getEffectClass(oEffectNode as Object) as String
on error resume next
for each oData in oEffectNode.UserData
if oData.Name = "preset-class" then
getEffectId = oData.Value
exit function
end if
next oData
End Function
' get the id of an effect
function getEffectId(oEffectNode as Object) as String
on error resume next
for each oData in oEffectNode.UserData
if oData.Name = "preset-id" then
getEffectId = oData.Value
exit function
end if
next oData
end function
@zirneklitis
Copy link

Hi,
I have a presentation which could not be parsed with the given macro. May I send it to You?
Best regards,
zirneklitis

@olivierpons
Copy link

Hi,

I'd just like to select a text in Impress and add a simple "ooo-entrance-fade-in" effect (duration: 1second) to all the text on the current slide that have no effect attached. May I ask you to help me on this (or maybe a link to a tutorial specific to Impress because all I can find is about Calc or Word)

Thank you very much

Olivier Pons

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment