Jaa


Generate Random Content for SharePoint – 4

This is the last chapter of the Generate Random Content For SharePoint series. This time, detailing the challenges you might face when you want to create PowerPoint presentations.

Chapters:

(If you don’t want to read through all the drama, jump directly to the Falling Action section.)

 

Exposition

Last, but not least we got to the last chapter, in the generation of random content for our stress test. We have big files, we have Word documents, Excel Sheets and here we will discuss the automatic creation of PowerPoint presentations.

Just as in the earlier posts, let's see what do we have in a PowerPoint presentation. We have slides with text, maybe pictures, tables here and there and charts perhaps. I could have gone really deep into creating all these objects in my presentation, but I think simple texts and pictures will do for most of the scenarios. If not, at least you'll have a baseline to start with, and setting up these extra functions should not be more than a few minutes of coding.

Rising Action

Act 1

Even though we are talking about the same suit, there are important differences between the Office Applications, as you might have already noticed. For example, while in Word and Excel you can use simple string for defining 'True' and 'False' values, in PowerPoint, you have to use explicit casting, otherwise you'll get scary error messages:

  $msoTrue = [Microsoft.Office.Core.MsoTriState]::msoTrue<br>$msoFalse = [Microsoft.Office.Core.MsoTriState]::msoFalse<br>$PPTDocument = $PPTApplication.Presentations.Add($msoFalse) 

For the full list of MsoTriStates check out the MsoTriState Enumeration MSDN article.

 

Act 2

Due to the special format of the slides, it does not worth generating the text in a text file as we used it with Word or Excel. Luckily however the content of a PowerPoint presentation is not that big to worry that it would take too long to generate even a few hundreds of files.

In fact, in my test generating 100 files with 20 slides in each, with 10 words on each slide and randomly inserting pictures on the slides took only 79 seconds, which is not that bad.

 

Climax

Act 3

Otherwise the generation of the PowerPoint slides themselves is not much different than the one we have seen with the Excel sheets: we have to address the Document, then the Slide (in Excel the Sheet), then the Shape (in Excel the cell).

Because you can create slides from the various slide templates available in the Slide Master gallery, when creating the slide, you have to choose it too. Just as with the 'True' and 'False' values, these have to be explicitly casted. In the below example you can see a simple Text Slide:

  $PPTSlideLayout = [microsoft.office.interop.powerpoint.ppSlideLayout]::ppLayoutText<br>$Slide = $PPTDocumet.Slides.Add($SlideCounter,$PPTSlideLayout) 

A list of the OOB available slide templates can be found on MSDN of course under the ppSlideLayout Enumeration article.

The $SlideCounter variable above is the actual number of the slide that you want to use. The counter starts from one and goes all the way up to the number of slides you want to create, of course. Be aware, if you use a number that is already used, for example you already have two slides and $SlideCounter = 2, then the new slide will be inserted to be the second slide and the rest of the slides will be shifted.

Adding text to the slide itself is not magic, but given the fact that in PowerPoint you can do way more with a Shape than what you can do with an Cell in Excel, adding a text to a Title or simple Text shape is a bit more complex:

  $Slide.Shapes.Title.TextFrame.TextRange.Text = $TitleText<br>$Slide.Shapes.Item(2).TextFrame.TextRange.Text = $SlideText

Of course, the $TitleText and $SlideText variables are filled out with the help of the dictionary file that we have used in the earlier posts.

 

Act 4

Adding a picture to the slide is a bit more tricky though. First and foremost, the picture size is not measured in pixels as you might think, but points. What the reason is behind this, I can't tell, but this is not the focus of my blog post anyway. The important thing you need to learn, however, is that the base reference is 72 dots per inch. What does this mean? It means that to be able to address the size correctly in your code, you will have to do some math:

  $PictureWidth = [math]::Round(($Picture.Width / $Picture.HorizontalResolution * 72),4)$PictureHeight = [math]::Round(($Picture.Height / $Picture.VerticalResolution * 72),4) 

This way the numbers you put into your code to size of the picture will not distort it at all. Otherwise you might see that your pictures grow a bit bigger than you expect.

Speaking of resizing... I put in a little rutin that scales the pictures according to the maximal size you want to use on your slides. The process is pretty straight forward, the only thing needs to be done is we need to make sure the picture is scaled both horizontally and vertically:

 If ($PictureWidth -gt $PicturesMaxWidth)<br>{    $Multiplier = $PicturesMaxWidth / $PictureWidth    $PictureHeight = $PictureHeight * $Multiplier<br>}<br>If ($PictureHeight -gt $PicturesmaxHeight)<br>{    $Multiplier = $PicturesmaxHeight = $PictureHeight    $PictureWidth = $PictureWidth * $Multiplier<br>} 



Of course, this is just half of the solution as adding the picture requires some extra directives, such as whether we want to reference to the original picture or not, or if we want to save the picture with the file or not.

  $pic = $Slide.Shapes.AddPicture(($RandomPicture.FilePath),$msoFalse, $msoTrue,$PicturesLeft,$PicturesTop, $RandomPicture.Width, $RandomPicture.Height) 

Details of the AddPicture method can be found on MSDN.

Additionally you might wish to insert only pictures of a specific MIME type. To do that you will have to use the ImageFormat Class Enumeration.

  $Imgage.RawFormat.Equals([System.Drawing.Imaging.ImageFormat]::Png) 

 

Act 5

There's one more thing that could be interesting and that is the transitions of the slides. This might seem like an obsolete feature to put into some test documents, but remember, these are not only for search, but maybe other purposes. For example, you could test the Office WebApps, or Office Online server's functionality with these nice transitions.

As you know, the transition can be bound to shapes and slides as well, so you can assign the animation to both.

  $Slide.SlideShowTransition.EntryEffect = 769$Slide.Shapes.Title.AnimationSettings.TextLevelEffect = 1<br>$Slide.Shapes.Title.AnimationSettings.EntryEffect = 769$Slide.Shapes.Title.AnimationSettings.Animate = $msoTrue



If you paid enough attention, you might have noticed that the EntryEffect property is using digit value. These numbers are coming from the PpEntryEffect Enumeration MSDN article, but unfortunately I have not found a way to get only the animations available in PowerPoint, so I had to do some dirty casting:

  $Animations = 3844, 769, 770, 3074, 3073, 1025, 1026, 3845, ... ... , 3347, 3348

Then using this array to create an animation is pretty simple:

  $RandomAnimation = ($TextAnimationEffects | Get-Random)<br>$Slide.Shapes.Title.AnimationSettings.EntryEffect = $RandomAnimation

 

Falling Action

Act 6

There is one single thing that bothers me a lot and that is proper cleanup. I was not able to find a way to properly close the COM objects and as such, the PowerPoint application stays in the memory, even after I closed the PowerShell window. This is what I used:

  [gc]::collect()[gc]::WaitForPendingFinalizers()[gc]::collect()<br>[gc]::WaitForPendingFinalizers()$PPTDocument.Saved = $msoTrue<br>$PPTDocument.Close()<br>$null = [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($PPTDocument)<br>$PPTDocument = $null<br>[gc]::collect()<br>[gc]::WaitForPendingFinalizers()<br>[gc]::collect()<br>[gc]::WaitForPendingFinalizers()<br>$PPTApplication.quit()<br>$null = [System.Runtime.InteropServices.Marshal]::FinalReleaseComObject($PPTApplication)<br>$PPTApplication = $null<br>[gc]::collect()<br>[gc]::WaitForPendingFinalizers()<br>[gc]::collect()<br>[gc]::WaitForPendingFinalizers() 


If any of you guys have a better - and working - way to do a proper cleanup, please feel free to share it in the comments.

The scripts are still available on Codeplex (link). Of course this solution still has room for improvement, but that is up for you guys to play with.

Dénouement

Now that we have different files available, we can start our precious performance testing. Keep an eye out for those entries soon.