Sunday, November 25, 2007

PowerPoint -> PDF (Part 2)

After writing a VBA Macro to convert a PowerPoint with animation into a flat PowerPoint without animation (suitable for conversion to PDF), I wrote my talk. After writing my talk I realised I not only needed elements to appear on a click, but also disappear and reappear. Unfortunately my original macro script did not do that, so I've updated it with two new features:

Safer

The original code removed all slides which were not hidden to return to the previous state. Unfortunately, if you haven't yet run the AddElements script, that results in it deleting all your slides! Undo saves the day, but it would be nicer for RemElements to be a bit more considerate. This version tags all the slides it creates, then RemElements simply removes those with the appropriate tag - hopefully this removes the obvious "Doh!" moment from this tool.

Works with show/hide/reshow

The original code used the Shape.AnimationSettings properties to detect what happened to objects. Unfortunately, this property only records the first action associated with an object - I suspect in the past PowerPoint only allowed one action and this is merely here for compatibility. To get the full range of events you need to use the Slide.TimeLine property. Writing the code I ran into two issues: (1) objects with silly names; (2) mutability.

Objects With Silly Names

Some objects have properties which don't do what you might think! Effect.EffectType = msoAnimEffectAppear implies that the animation is to appear, but if Effect.Exit = msoTrue then this is the disappear effect! I was confused by this one for quite a while.

In order to solve all the naming problems, I made extensive use of the Visual Basic debugger included with Office, which puts many other debuggers to shame. It is at least 1000x better than any Haskell debugger I've ever seen, and equally far ahead of things like GDB. Microsoft's software may be maligned, but their debuggers are truly fantastic! It really does allow an entirely new style of development, and is particularly suited to dipping into a new API without having a large learning curve.

Mutability

Mutability is a bad idea. If you delete a shape, while you are iterating over a collection of shapes, you silently skip the element that comes after the deleted shape! If you delete a shape, and that shape is the subject of a transition, then that corresponding transition is deleted. If you change the Shape.AnimationSettings.Animate to msoFalse, this removes any associated transitions. All this means that to try and iterate over something starts to become a challenge!

The problem with mutability in this particular task is that it is unclear what is changing and when, leading to subtle bugs and lots of debugging. Again, the debugger helped, but not as much as before - having to single-step through quite deep properties is not a particularly fun task.

The Code

And here is the code, to be use as in the same way to the previous post.


Option Explicit

Sub AddElements()
Dim shp As Shape

Dim i As Integer, n As Integer
n = ActivePresentation.Slides.Count
For i = 1 To n
Dim s As Slide
Set s = ActivePresentation.Slides(i)
s.SlideShowTransition.Hidden = msoTrue

Dim max As Integer: max = AnimationElements(s)
Dim k As Integer, s2 As Slide
For k = 1 To max
Set s2 = s.Duplicate(1)
s2.Name = "AutoGenerated: " & s2.SlideID
s2.SlideShowTransition.Hidden = msoFalse
s2.MoveTo ActivePresentation.Slides.Count

Dim i2 As Integer, h As Shape
Dim Del As New Collection
For i2 = s2.Shapes.Count To 1 Step -1
Set h = s2.Shapes(i2)
If Not IsVisible(s2, h, k) Then Del.Add h
Next
Dim j As Integer
For j = s.TimeLine.MainSequence.Count To 1 Step -1
s2.TimeLine.MainSequence.Item(1).Delete
Next
For j = Del.Count To 1 Step -1
Del(j).Delete
Del.Remove j
Next
Next
Next
End Sub

'is the shape on this slide visible at point this time step (1..n)
Function IsVisible(s As Slide, h As Shape, i As Integer) As Boolean

'first search for a start state
Dim e As Effect
IsVisible = True
For Each e In s.TimeLine.MainSequence
If e.Shape Is h Then
IsVisible = Not (e.Exit = msoFalse)
Exit For
End If
Next

'now run forward animating it
Dim n As Integer: n = 1
For Each e In s.TimeLine.MainSequence
If e.Timing.TriggerType = msoAnimTriggerOnPageClick Then n = n + 1
If n > i Then Exit For
If e.Shape Is h Then IsVisible = (e.Exit = msoFalse)
Next
End Function

'How many animation steps are there
'1 for a slide with no additional elements
Function AnimationElements(s As Slide) As Integer
AnimationElements = 1
Dim e As Effect
For Each e In s.TimeLine.MainSequence
If e.Timing.TriggerType = msoAnimTriggerOnPageClick Then
AnimationElements = AnimationElements + 1
End If
Next
End Function

Sub RemElements()
Dim i As Integer, n As Integer
Dim s As Slide
n = ActivePresentation.Slides.Count
For i = n To 1 Step -1
Set s = ActivePresentation.Slides(i)
If s.SlideShowTransition.Hidden = msoTrue Then
s.SlideShowTransition.Hidden = msoFalse
ElseIf Left$(s.Name, 13) = "AutoGenerated" Then
s.Delete
End If
Next
End Sub


As before, no warranty, and please do backup first!

46 comments:

Anonymous said...

Great script! Exactly the functionality I have been looking for. If the script could handle appearing/disappearing bullets in a list WITHIN a text object as well there would be nothing left to ask for. Keep up the good work!

Anonymous said...

A great script indeed! I'd like to use it with work-related presentations, but since you did not mention any license, I'm not sure if it's OK to use it. Could you add the kind of license that you intended the script to be used with? (public domain i.e. free to use for anything, GPL, BSD, commercial, or something completely different)

Neil Mitchell said...

Roger: I may extend it in such a direction, if it turns out not to be too hard. I'll probably put it on a proper web page and as a downloadable file, to make it a bit easier.

Kari: BSD with no advertising - the source code is free, you are free to do what you want with it. If BSD doesn't meet your needs, let me know.

Anonymous said...

Hi Neil,

just another quick "thanks" for your code - it pretty much solves exactly the problem I was right now dealing with :-)

I will keep an eye on this page to not miss the new homepage for the VB Script...

Cheers,
Christian

Anonymous said...

It works fine except for:

When you add multi-step animations to a text block sentence by sentence, this coding cannot recognize that. It will treat the whole text block as 1 step animation.

Anonymous said...

Dear Neil,

is there any chance to modify the code such that the numbers of the slides are preserved? My use case is to provide slides for students on our web page, and it would be extremely useful to still be able to point them to a particular slide from the original talk after the conversion has been performed...

Thanks,
Christian (christian at uni-paderborn de)

Anonymous said...

Great Work.
Would be nice to hide objects that are hidden.
I might try and add it when I have some free time.
Worked nicely on Office 2007, BTW.

Neil Mitchell said...

@Christian and Ophir: Quite a lot of people seem interested in this, so I'll set up a darcs repo, a web page, and attempt to make a proper release. Once I've got a proper source code repo for it, then it will be easier to add/accept features.

John W. Myrna said...

A great utility! Echo others in that the only thing I had to handle manually was bulleted items within a list.

I use that mode a lot. A list of itmes, each one revealed on a mouse click.

Unknown said...

You sir, have just saved my day!

Wonderful workaround to the pdf animation problem - thank you so much for sharing it with us less VBA savvy PP users :)

Anonymous said...

After copying AddElements into a module and tryin to run, I get an error "compile error: method or data member not found" running on mac word 2004. Is there something I'm missing?

Anonymous said...

Thank you so much for the script.
I have an important presentation in a week and you help a lot!

Ulrike said...

Many many thanks also from me! The only thing I've done manually is move the hidden slides to the back so that the numbering starts with "1" in the pdf. Would be nice but by no means crucial if that would happen automatically.

JAE said...

Thank you! I just used this with my pptx file (Microsoft Office 2007) and it worked beautifully!

Thank you again!

Marc Vayssières said...

Very useful script! Thank you. Marc

Anonymous said...

You just saved my job!!!! When i needed to send a powerpoint with custom animations to a group of tech-challenged colleagues (it would be cruel to have them install powerpoint viewer) your code was a life saver. thank you. thank you. thank you.

Hardeep Singh said...

Thanks, its a nice script and works well for me

Anonymous said...

Neil,
Thank you so much for sharing this script. It works great and saves me so much time!

Best,
Ram

Sri said...

Neil, Great work.

Here's is one more add-in. It works perfectly with 07.

http://www.dia.uniroma3.it/~rimondin/downloads.php

Abhilash said...

This is brilliant. Was looking for this and was thinking about contacting my IT to get this script written. Helped me a huge amount of time and effort.
Thanks a mill!!!

Anonymous said...

Saved my day: thank you very very much! ;)

Unknown said...

Great script! Thanks! One problem is that page numbering is not retained. Any suggestions???

DHorl said...

Hi Neil.

I had difficulty to get this into Powerpoint2010. Cant find the button to paste this code.

Your assistance is highly appreciated

Anonymous said...

Hello,

Thanks for this tip that is really useful.

Just a drawback: the incrementation of the slides numbers :-( (my final slide is slide # 99/15)

Do you have a trick to avoid this?

Thanks in advance ;-)

Anonymous said...

Hello,

Thanks for this tip that is really useful.

Just a drawback: the incrementation of the slides numbers :-( (my final slide is slide # 99/15)

Do you have a trick to avoid this?

Thanks in advance ;-)

Unknown said...

Hi,

I had a similar idea and cannot find why I have errors. The idea was to create a macro opening all my .pptx within a specified folder and create two kinds of pdfs from these.
Note: these pdf are password protected.

However I am now blocked as it works fine for the 1st powerpoint and then it crashes for the second telling me that the Exportasfixedformat does not work.
Would you have any tips for me, please?

Public wbPPT As Presentation
Public fPPT As String
Public fInput As String
Public fpath As String


Sub BatchBuilding_pdf_from_PPT()

Dim nfichier As String, nfichier2 As String, intpos As Byte


fInput = InputBox("Please enter the local address where are stored your PPT files (e.g. C:\... ) ")
If fInput = "" Then
MsgBox ("No path entered, the Importing process is cancelled")
Exit Sub
Else
If Right(fInput, 1) <> "\" Then
fInput = fInput & "\"
Else
fInput = fInput
End If

fpath = fInput

fPPT = Dir(fpath & "*.pptx")

Do While Len(fPPT) > 0

nfichier = fPPT
'find where is the extension in the name
intpos = InStrRev(nfichier, ".")
'replace the pptx by pdf
nfichier = Left(nfichier, intpos - 1)
nfichier2 = nfichier & ".pdf"

With ProtectedViewWindows.Open(fpath & fPPT, "password").Edit("password")
.ExportAsFixedFormat Path:=fpath & "Without notes\" & nfichier2, FixedFormatType:=ppFixedFormatTypePDF, Intent:=ppFixedFormatIntentPrint, FrameSlides:=msoTrue, PrintHiddenSlides:=msoTrue, OutputType:=ppPrintOutputSlides
.ExportAsFixedFormat Path:=fpath & "With notes\" & nfichier2, FixedFormatType:=ppFixedFormatTypePDF, Intent:=ppFixedFormatIntentPrint, FrameSlides:=msoTrue, PrintHiddenSlides:=msoTrue, OutputType:=ppPrintOutputNotesPages
.Close
End With

fPPT = Dir

'same code as before to prepare for the next file (just in case)
nfichier = fPPT
'find where is the extension in the name
intpos = InStrRev(nfichier, ".")
'replace the pptx by pdf
nfichier = Left(nfichier, intpos - 1)
nfichier2 = nfichier & ".pdf"

Loop

MsgBox ("All presentations have been PDFied")
End If

End Sub

Thanks in advance.

Neil Mitchell said...

Sebastien, no idea I'm afraid - I don't even have PowerPoint installed on this machine anymore.

Anonymous said...

8 years later, still works a charm! Thanks a lot!

Anonymous said...

Mac users say: god bless you!

It's amazing how 8 years later MS still haven't sorted this out (I think keynote has an option to break animations up when creating a PDF). In particular, the few scripts I could find out there are all for windows, so your script is a real life-saver for us OSX users.

Minor issue: when using text animations (i.e. just having bullet points appear one by one), the script will break the slide into multiple slides, but will keep all bullet points in every slide (rather than adding a new bullet point to each slide), requiring one to go and manually delete some bullet points. Still, small price to pay compared to having to do everything manually!

Thanks!!!!!!!!

p.s. - tested on OSX Yosemite running MS PowerPoint 2011 for mac.

Anonymous said...

Where do I paste the code? Please anyone describe exactly (Mac Yosemite; PPT 14.4.5 2011 for mac). Thx!

Jami said...

Pure Genius! thank you sooo much!!

Jami said...

For those wondering. post the code into a VB module or open your macro menu, type in a AddElements to the Macro Name and click "create" then paste the code in the window the VB code editor window that comes up. Close and return, then you should see the two macros (addElements and RemElements) listed in your macro list.

Sensorgrafie.de said...

The script even works in PowerPoint 15 on macOS, however it is (like other commented) not able to split animated bullet point lists into single items. As a Mac user I just found out that (the meanwhile free) Apple Keynote can read PowerPoint pretty well and Keynote is able to export really reach animation to PDF. So lucky am I and others using a Mac.

Unknown said...

As it is always the case, there is still room for improvement.
One thing that does not work with macro are page numbers. All added slides get new page numbers and don't retain the same numbers as they would have in the original.

Anonymous said...

Wow. I've never run a macro, used VBE etc. I had to hunt a bit through confusing crap on MS Support to stumble into what I should do to use your code on PowerPoint for Mac 2016. But it worked. Thank you so much.

Anonymous said...

Thank you!!

Philipp Moritz said...

Here is how you can use this code and retain the slide numbers from the old presentation:

https://gist.github.com/pcmoritz/4b0e1be7f2dfcc4e51e2ace50426f67d

You need to deactivate the slide numbers, this will insert a text box per slide with the right number from the old slide.

Neil Mitchell said...

Thanks Philipp - very cool! I would have updated the blog to include a link to your code, but unfortunately any edits to the page make it break (I guess something about blogger changed in the last decade...)

Taylor said...

Hi Neil,

I'm not a VBA expert (so apologies if these are dumb questions):

1. For your code to work, do you have to have an Adobe elements (or other) subscription? I.e. can anyone run this code, or do you have to have a subscription?

The reason I ask is, I just published a post on converting PowerPoint to PDF and one of my points was that animations do not carry over (which is something I'm often asked).

If this does work for anyone (which would be amazing), I would love to include a link back to your code or even expand on it for people who are not fluent in VBA so they can FINALLY make this a reality.

Below is the blog post I created for reference.
https://nutsandboltsspeedtraining.com/powerpoint-tutorials/ppt-to-pdf/

Would love to get your insight so I can refer people back to this assuming it works for everyone.

Thanks!
Taylor


Neil Mitchell said...

Thanks Taylor - it doesn't require Adobe elements or anything beyond basic PowerPoint. I assume it still works, but don't use PowerPoint much anymore. Happy for you to take it and expand as you wish. https://github.com/ndmitchell/office/blob/master/PowerFlat.bas Is my latest version.

Taylor said...

Thanks Neil...

By expand on it, I just meant advise people how to install and run VBA (which not a lot of people have done before).

Thanks for the confirmation, I will link to it from my post.

totally other random question, do you have a course on building macros in PowerPoint by chance? I get asked about this occasionally and don't know of anyone who really has one.

Thanks again,
Taylor

Neil Mitchell said...

Taylor - no course of similar - I'm an ex VB5-6 programmer who got into it that way.

Simontok said...

how to convert pdf to powerpont and enable edited file?

Anonymous said...

Thank you so much, that was easy!

Unknown said...

Dear Sir, thank you so much !

The only problem is that powerpoint keeps going into "program not responding any more"
I need to convert 108 slides into pdfs by tomorrow, you happen to have any experience why powerpoint becomes non responsive after launching the code? thank you

Unknown said...

I just had to wait very long, it worked!