Wrench value from your Creo Investments Home Measure the benefits Simple Automation can bring Why
Automate?
A personal way to do PDM Peer to Peer
PDM
3D Drawings Model Based
Design
Smooth out unwanted sharp edges Other
Articles
www.proetoolbox.co.uk - Simple Automation made Simple

Integrating Creo with PowerPoint

Almost everyone involved in Engineering will at one time or another snap a picture and put that picture into PowerPoint. Some folks use specialty picture capture tools such as SnagIt. Recently I have had the opportunity to witness first hand how organizations drive users to build non-associative documentation. The users cited re-work loops of the order of days to re-capture imagery for 'Revision 2' review. There are a whole range of opinions on this matter and indeed alternatives to PowerPoint. This article adds another dimension to that space without fully analysing the pros and cons of using ad-hoc documentation tools like PowerPoint versus associative systems like Arbortext, Creo Illustrate, Windchill and so on.

...
//Add image d:\ptc\creoworkdir\demo.tif into currently active PowerPoint
var oPPT = new ActiveXObject("PowerPoint.Application"); 
oPPT.Visible = true;
var newPPT = oPPT.ActivePresentation; 
var oSlide = oPPT.ActiveWindow.View.Slide;
var oShp = oSlide.Shapes.AddPicture("d:\\ptc\\creoworkdir\\demo.tif",
							-1,1,60,75,600,400);
oShp.LockAspectRatio = 1;
...

PowerPoint is available as an ActiveX control

Being able to write calls to PowerPoint means that our Simple Automation tools can easily join up workflows.

 

// stuff for Exporting an Image
...
var CurWin = oSession.CurrentWindow;
...
//Set up export instructions
var ImageWidth = 17;
var ImageHeight = 11;
var InstsFactory = pfcCreate("pfcTIFFImageExportInstructions");
var Instructions = InstsFactory.Create (ImageWidth, ImageHeight); 
Instructions.ImageDepth = pfcCreate("pfcRasterDepth").RASTERDEPTH_24; 
Instructions.DotsPerInch = pfcCreate("pfcDotsPerInch").RASTERDPI_300;
...
//This will drop the Raster Image to the working directory
CurWin.ExportRasterImage ("demo.tif",  Instructions) ;
...

Exporting a Raster of Active Window

The code to export a raster image to disk is relatively trivial.

 

...
//Set Configs to make nicer pictures
oSession.SetConfigOption ("shade_quality", "10");
oSession.SetConfigOption ("display", "shadewithedges");
oSession.SetConfigOption ("edge_display_quality", "very_high");
oSession.SetConfigOption ("shade_with_edge_tangent", "no");
oSession.SetConfigOption ("smooth_lines", "yes");
oSession.SetConfigOption ("system_colors_file", "p:\\colors\\syscol_whitebkgd.scl");
oSession.SetConfigOption ("display_planes", "no");
oSession.SetConfigOption ("spin_center_display", "no");

CurWin.Repaint () ; 
...

Setting a White background

In production applications, you'd probably want to make a reset configs button as well.

I noticed that setting these properties can take quite a bit of time relative to your expectations for larger assemblies, especially those with problems.

 

...
var oShp = oSlide.Shapes.AddPicture("d:\\ptc\\creoworkdir\\demo.tif",
							-1,1,60,75,600,400);
oShp.LockAspectRatio = 1;
var d = new Date();
var dTxt = d.getDate()+"/"+(d.getMonth()+1)+"/"+d.getFullYear();
oShp.Title = CurMdl.FileName+" "+dTxt;
oShp.AlternativeText = "";
...

Tagging an Image

If one wants the ability to re-export an exact image at a later point (after a bunch of changes!) then there has to be a mechanism to reconcile what each asset in the PowerPoint is. PowerPoint has two properties on shapes (Title and AlternativeText) that I figured would be reasonable to commandeer for this purpose.

 

...
var CurMdl = oSession.CurrentModel;
var CurViewTrans = CurMdl.GetCurrentView().Transform.Matrix ; 
var CurMdlViews = CurMdl.ListViews () ;
for (var i=0;i<CurMdlViews.Count;i++)
{
	var CurMdlView = CurMdlViews.Item(i);
	var CurMdlViewTrans = CurMdlView.Transform.Matrix;
	var IsView = true;
	for (var j=0;j<4;j++)
	{
		for (var k=0;k<4;k++)
		{
			if (CurViewTrans.Item(j,k)!=CurMdlViewTrans(j,k))
			{
				IsView = false;
			}
		}
	}
	if (IsView)
	{
		return CurMdlView.Name;
	}
}
...

Calculating Active View

I found in development that the API in Creo was broken post M090 (GetCurrentView().Name). Instead I had to compare the view matrices against the Currently active View.

 

...
var AdHocViewName = "ZADHOC_1";
for (var i=1;i<200;i++)
{
	AdHocViewName = "ZADHOC_"+i;
	if (CurMdl.GetView (AdHocViewName)==null)
	{
		break;
	}
}
CurMdl.SaveView (AdHocViewName); 
return AdHocViewName;
...

Saving an Ad-Hoc View

I figured that the best course of action for coming back to a VIEW would be for that view to be in the model. So if we don't find a view then the code snippet can add that view to the standard views in the model.

 

...
return CurMdl.GetActiveSimpRep ().GetName () ;
...

Determining the Simplified Rep

This is actually quite simple.

 

...
return CurMdl.GetActiveExplodedState ().GetName ()  ;
...

Determining the Explode State

This is actually quite simple.

 

...
var Slides = newPPT.Slides;
var NumSlides = Slides.Count; //Loop around only the current slides
for (var i=1;i<=NumSlides;i++)
{
  var curSlide = Slides.Item(i);

  var shapes = curSlide.Shapes;
  var NumShapes= shapes.Count;
  for (var j=NumShapes;j>=1;j--)
  {
    var curShape = shapes.Item(j);

    if (curShape.Type ==11 || curShape.Type ==14) //LinkedPicture and Placeholders
    {
      //Work out what the name and details of the model should be
      var curShapeTitle = curShape.Title;
      var curShapeAltTxt = curShape.AlternativeText;
      if (curShapeTitle.indexOf(".asm")==-1 || 
         curShapeTitle.indexOf("UPDATE")==-1 || 
	curShapeTitle.indexOf("DELETE")>-1)
      {
        //Must be a shape referring to a model that IS tagged for UPDATING
        //and IS NOT tagged for deletion.
        continue;
      }
      else
      {
         ...
	//Figure out image spec
	//Setup Window to match image spec
	//Add Picture
	//Apply Cropping/Re-Sizing as per PowerPoint
	...
      }
    }
  }
}
...
//Delete any images marked for deletion
var shapes = curSlide.Shapes;
var NumShapes= shapes.Count;
for (var j=NumShapes;j>=1;j--)
{
	var curShape = shapes(j);
	var curShapeTitle = curShape.Title;
	if (curShapeTitle.indexOf("DELETE")>-1)
	{
		curShape.Delete();
		NumShapes =shapes.Count;
		j=NumShapes;
	}
}
...
//Push images to the back
var shapes = new Enumerator(curSlide.Shapes);
shapes.moveFirst();
while (shapes.atEnd() == false)
{
	var curShape = shapes.item();
	if (curShape.Type ==11) //LinkedPicture
	{
		curShape.ZOrder(1);
	}
	shapes.moveNext();
}
...

Looping around PowerPoint shapes

After a number of annoying rounds of confusion, I realized that if I was to loop around and delete and then add pictures (e.g. if i was trying to update images by adding and removing them) then I was likely going to risk an infinite loop. I decided upon a two stage approach, first loop through and add images that would be UPDATED. Then go around the images again and remove any marked for DELETE. Notice how my delete strategy is brute force.

The loop that does Z order is interesting. I ended up having to use an Enumerator because Z Order in PowerPoint is basically exactly the same as Item. So if i was to set ZOrder on a slide, my collection would instantly go out of order. I was incredibly confused until I bumped into this fact.

 

...
//Set the rep as per image spec
if (curShapeSR==null)
{
	pwl.pwlSimprepMasterActivate(CurMdl.InstanceName+".asm");
}
else
{
	pwl.pwlSimprepActivate (CurMdl.InstanceName+".asm", curShapeSR) ;
}
...

Setting the Simplified Rep

By far the easiest way to handle Simplified Reps is to use the old style web.link pfcScript methods.

 

...
CurMdl.RetrieveView (curShapeView);
...

Setting the View

This is straightforward.

 

...
//Set Exploded state as per image spec
if (curShapeES == null)
{
	pwl.pwlAssemblyExplodeStatusSet (CurMdl.InstanceName+".asm", false); 
}
else
{
	pwl.pwlAssemblyExplodeStateSet (CurMdl.InstanceName+".asm", curShapeES); 
	pwl.pwlAssemblyExplodeStatusSet (CurMdl.InstanceName+".asm", true); 
}
...

Setting the Explode State

This is straightforward, like Simplified Reps, easiest done with the pfcScript methods.

 

...
CurWin.ExportRasterImage ("demo.tif",  Instructions) ;
				
var oShp = shapes.AddPicture(CurWorkDir+"\\demo.tif",-1,1,60,75,600,400);

oShp.Top = curShape.Top;
oShp.Left =curShape.Left;
oShp.Width =curShape.Width;
oShp.Height = curShape.Height;

oShp.PictureFormat.Crop.PictureOffsetX = curShape.PictureFormat.Crop.PictureOffsetX;
oShp.PictureFormat.Crop.PictureOffsetY = curShape.PictureFormat.Crop.PictureOffsetY;
oShp.PictureFormat.Crop.PictureHeight = curShape.PictureFormat.Crop.PictureHeight;
oShp.PictureFormat.Crop.PictureWidth = curShape.PictureFormat.Crop.PictureWidth;
oShp.PictureFormat.Crop.ShapeHeight = curShape.PictureFormat.Crop.ShapeHeight;
oShp.PictureFormat.Crop.ShapeLeft = curShape.PictureFormat.Crop.ShapeLeft;
oShp.PictureFormat.Crop.ShapeTop = curShape.PictureFormat.Crop.ShapeTop;
oShp.PictureFormat.Crop.ShapeWidth = curShape.PictureFormat.Crop.ShapeWidth;
...

Resetting Size and Cropping Values

This took a little figuring out. The order in which these values are set appeared to make a difference in my testing.

Conclusion

It would be nice if Pro/Web.Link was provided with access to the Combination Views (it won't) and it would be nice if we could automate the rendering commands (no we won't get those either). On the plus side though, a large amount of time could be massively reduced simply by building an intelligent app to capture and at a later date, re-capture images that go into PowerPoints. I leave it up to the readers discretion to build their own.