// $Id$ using System; using PowerPoint = Microsoft.Office.Interop.PowerPoint; using Core = Microsoft.Office.Core; using PPTLibrary; using PPTPaneManagement; using UW.ClassroomPresenter.Model.Presentation; using System.Security.Cryptography; using System.Threading; using System.IO; using System.CodeDom.Compiler; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Runtime.Serialization.Formatters.Binary; using System.Diagnostics; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Text; using UW.ClassroomPresenter.Model.Background; using UW.ClassroomPresenter.Viewer.Background; namespace UW.ClassroomPresenter.Decks { /// /// Summary description for PPTDeckIO. /// public class PPTDeckIO { #region OpenPPT + Helper Method public class PPTNotInstalledException : ApplicationException { public PPTNotInstalledException(string message, Exception innerException) : base(message, innerException) { } } public class PPTFileOpenException : ApplicationException { public PPTFileOpenException(string message, Exception innerException) : base(message, innerException) { } } public static DeckModel OpenPPT(FileInfo file) { return PPTDeckIO.OpenPPT(file, null, null); } //TODO: Image type needs to be more dynamically chosen... public static DeckModel OpenPPT(FileInfo file, BackgroundWorker worker, DoWorkEventArgs progress) { //Start the progress bar if (worker != null) { worker.ReportProgress(0, " Initializing..."); } //Make the default flat tree representation of the PPT //Try to detect if powerpoint is already running (powerpnt.exe) bool pptAlreadyRunning = false; Process[] processes = Process.GetProcesses(); for (int i = 0; i < processes.Length; i++) { string currentProcess = processes[i].ProcessName.ToLower(); if (currentProcess == "powerpnt") { pptAlreadyRunning = true; break; } } //Open PowerPoint + open file PowerPoint.Application pptapp; try { pptapp = new PowerPoint.Application(); } catch (Exception e) { throw new PPTNotInstalledException("Failed to create PowerPoint Application. See InnerException for details.", e); } PowerPoint._Presentation presentation; try { presentation = pptapp.Presentations.Open(file.FullName, Core.MsoTriState.msoTrue, Core.MsoTriState.msoFalse, Core.MsoTriState.msoFalse); } catch (Exception e) { throw new PPTFileOpenException("Failed to open PowerPoint file. See InnerException for details.", e); } //Initialize the PPT Shape tag reader PPTPaneManagement.PPTPaneManager pptpm = new PPTPaneManagement.PPTPaneManager(); //Create a new DeckModel Guid deckGuid = Guid.Empty; try { string g = presentation.Tags["WEBEXPORTGUID"]; if (g == "") { deckGuid = Guid.NewGuid(); } else { deckGuid = new Guid(g); } } catch { deckGuid = Guid.NewGuid(); } DeckModel deck = new DeckModel(deckGuid, DeckDisposition.Empty, file.Name); //Initialize a temporary file collection that will be where slide images are exported to TempFileCollection tempFileCollection = new TempFileCollection(); string dirpath = tempFileCollection.BasePath; if (!Directory.Exists(dirpath)) { Directory.CreateDirectory(dirpath); } else { Directory.Delete(dirpath, true); Directory.CreateDirectory(dirpath); } //Lock it using(Synchronizer.Lock(deck.SyncRoot)) { //Iterate over all slides for (int i = 1; i <= presentation.Slides.Count; i++) { if (progress != null && progress.Cancel) break; //Get the slide PowerPoint._Slide currentSlide= presentation.Slides[i]; if (currentSlide.SlideShowTransition.Hidden == Core.MsoTriState.msoTrue) continue; SlideModel newSlideModel = CreateSlide(presentation.PageSetup, pptpm, deck, tempFileCollection, dirpath, currentSlide); //Create a new Entry + reference SlideModel TableOfContentsModel.Entry newEntry = new TableOfContentsModel.Entry(Guid.NewGuid(), deck.TableOfContents, newSlideModel); //Lock the TOC using(Synchronizer.Lock(deck.TableOfContents.SyncRoot)) { //Add Entry to TOC deck.TableOfContents.Entries.Add(newEntry); } //Increment the ProgressBarForm if (worker != null) { worker.ReportProgress((i * 100) / presentation.Slides.Count, " Reading slide " + i + " of " + presentation.Slides.Count); } } } //Close the presentation presentation.Close(); presentation = null; //If PowerPoint was not open before, close PowerPoint if (!pptAlreadyRunning) { pptapp.Quit(); pptapp = null; } GC.Collect(); //Delete temp directory tempFileCollection.Delete(); Directory.Delete(dirpath); if (worker != null) worker.ReportProgress(100, " Done!"); //Return the deck if (progress != null) progress.Result = deck; return deck; } /// /// PowerPoint shape with additional information /// struct TaggedShape { public PowerPoint.Shape shape; public Model.Presentation.SheetDisposition disp; public int index; public bool isImage; } /// /// Create a slide model from a powerpoint slide /// /// /// /// /// /// /// /// private static SlideModel CreateSlide(PowerPoint.PageSetup pageSetup, PPTPaneManagement.PPTPaneManager pptpm, DeckModel deck, TempFileCollection tempFileCollection, string dirpath, PowerPoint._Slide currentSlide) { int slideWidth = (int)pageSetup.SlideWidth; //Standard = 720 => 6000 int slideHeight = (int)pageSetup.SlideHeight; //Standard = 540 => 4500 float emfWidth = slideWidth * 25 / 3; float emfHeight = slideHeight * 25 / 3; // Adding an empty textbox in the upper left corner of the the // slide before layers are exported works around a common // issue with shape positioning. Without this, text shapes often get pushed too far // to the right and down due to some extra padding that PPT inserts on // the top and left of the exported images. It doesn't pad the top left of an // empty text box, so the positioning is corrected. // This isn't perfect since the workaround should probably be applied to // every layer with text boxes. //currentSlide.Shapes.AddTextbox(Core.MsoTextOrientation.msoTextOrientationHorizontal, 0, 0, 1, 1); PowerPoint.Shapes currentShapes = currentSlide.Shapes; List taggedShapeList = PPTDeckIO.BuildTaggedShapeList(currentShapes, pptpm); //Create a new SlideModel SlideModel newSlideModel = new SlideModel(Guid.NewGuid(), new LocalId(), SlideDisposition.Empty, new Rectangle(0, 0, slideWidth, slideHeight)); //Lock it using (Synchronizer.Lock(newSlideModel.SyncRoot)) { //Set the slide's title newSlideModel.Title = PPTDeckIO.FindSlideTitle(taggedShapeList, currentSlide); PPTDeckIO.MakeShapesInvisible(currentShapes); //Create the Background image //Generate a new filename string filename = PPTDeckIO.GenerateFilename(); bool bitmapMode = true; if (bitmapMode) { filename = dirpath + "\\" + filename + ".png"; currentSlide.Export(filename, "PNG", 0, 0); // Need to also export as EMF to get the size of the slide in inches currentSlide.Export(filename + "_TEMP", "EMF", 0, 0); tempFileCollection.AddFile( filename + "_TEMP", false ); } else { filename = dirpath + "\\" + filename + ".emf"; currentSlide.Export(filename, "EMF", 0, 0); } tempFileCollection.AddFile(filename, false); //Compute the MD5 of the BG FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read); MD5 md5Provider = new MD5CryptoServiceProvider(); byte[] md5 = md5Provider.ComputeHash(fs); fs.Seek(0, SeekOrigin.Begin); Image image = Image.FromStream(fs); if (bitmapMode) { image = DisassociateBitmap(image); } fs.Close(); // Open the EMF version if we used a bitmap to get the conversion if( bitmapMode ) { FileStream fsEMF = new FileStream( filename + "_TEMP", FileMode.Open, FileAccess.Read ); Image image_emf = Image.FromStream( fsEMF ); emfWidth = image_emf.Width; emfHeight = image_emf.Height; fsEMF.Close(); image_emf.Dispose(); } else { emfWidth = image.Width; emfHeight = image.Height; } //Create the ImageSheet ImageSheetModel sheet = new ImageSheetModel(deck, Guid.NewGuid(), Model.Presentation.SheetDisposition.Background, new Rectangle(0, 0, slideWidth, slideHeight), (ByteArray)md5, 1); //Add the ImageSheet to the Slide newSlideModel.ContentSheets.Add(sheet); //Add the Image+MD5 to the deck deck.AddSlideContent((ByteArray)md5, image); // Restore visibility - this makes everything visible - a bug? PPTDeckIO.MakeShapesVisible(currentShapes); List> layerList = PPTDeckIO.SeparateIntoLayers(taggedShapeList); int startHeight = 2; foreach (List layer in layerList) PPTDeckIO.ProcessLayer( layer, tempFileCollection, currentShapes, deck, newSlideModel, slideWidth/emfWidth, slideHeight/emfHeight, startHeight++ ); //Add SlideModel to the deck deck.InsertSlide(newSlideModel); } return newSlideModel; } /// /// Split a list into sublist of common disposition and picture status. Images are assigned to separate layers /// /// /// private static List> SeparateIntoLayers(List taggedShapeList) { List> listList = new List>(); if (taggedShapeList.Count == 0) return listList; List list = new List(); for(int i = 0; i < taggedShapeList.Count; i++){ if (i > 0 && (taggedShapeList[i - 1].isImage || taggedShapeList[i].isImage || taggedShapeList[i - 1].disp != taggedShapeList[i].disp)) { listList.Add(list); list = new List(); } list.Add(taggedShapeList[i]); } listList.Add(list); return listList; } /// /// Remove the PPT 2007 formatting from a shape to control the image blow up on conversion /// private static void Scrub2007EffectsFromShape(PowerPoint.Shape shape) { //These shape types are thought to always except when some of the following properties are set // Note this information is based on observing a small number of cases. // There are other types that sometimes except, but in some cases are important to scrub. if (shape.Type == Core.MsoShapeType.msoPicture || shape.Type == Core.MsoShapeType.msoEmbeddedOLEObject || shape.Type == Core.MsoShapeType.msoTable) return; try { if (shape.Shadow != null) { shape.Shadow.Visible = Microsoft.Office.Core.MsoTriState.msoFalse; } if (shape.TextFrame2 != null) { shape.TextFrame2.ThreeD.BevelTopType = Microsoft.Office.Core.MsoBevelType.msoBevelNone; shape.TextFrame2.TextRange.Font.Reflection.Type = Microsoft.Office.Core.MsoReflectionType.msoReflectionTypeNone; } if (shape.TextFrame != null) { shape.TextFrame.TextRange.Font.Shadow = Microsoft.Office.Core.MsoTriState.msoFalse; } //Trace.WriteLine("Scrubbed effects for shape type: " + shape.Type.ToString()); } catch (Exception e) { Trace.WriteLine("Failed to set PPT 2007 property for shape type: " + shape.Type.ToString() + "\r\n" + e.ToString()); } } /// /// Tag all the shapes with their visibility mode, index, and whether or not they are a picture /// /// /// /// private static List BuildTaggedShapeList(PowerPoint.Shapes shapes, PPTPaneManagement.PPTPaneManager pptpm) { List tsList = new List(); int index = 1; double version; if (!double.TryParse(((PowerPoint.Application)shapes.Application).Version,out version)) { version = 0.0; } for (int i = 1; i <= shapes.Count; i++) { PowerPoint.Shape shape = shapes[i]; if(version >= 12.0) Scrub2007EffectsFromShape(shape); if( shape.Type == Microsoft.Office.Core.MsoShapeType.msoGroup && (version < 12.0)) { shape.Ungroup(); i--; continue; } else AddTaggedShapeToList( tsList, ref index, shape, pptpm ); } return tsList; } private static void AddTaggedShapeToList( List tsList, ref int index, PowerPoint.Shape shape, PPTPaneManagement.PPTPaneManager pptpm ) { TaggedShape ts; ts.shape = shape; ts.index = index; index++; ts.isImage = (shape.Name.StartsWith( "Picture" )); // A shape is an image if its called a picture! string[] modes = pptpm.GetModes( shape ); if( modes.Length == 0 ) { ts.disp = SheetDisposition.All; tsList.Add( ts ); } else { foreach( string mode in modes ) { ts.disp = PPTDeckIO.Disposition( mode ); // Make a copy for each mode, and add to the list tsList.Add( ts ); } } } /// /// Find the title of the slide. /// /// /// private static string FindSlideTitle(List tsList, PowerPoint._Slide currentSlide){ try { //First look for a proper title on the slide. Assume a slide title always has SheetDisposition.All. if (currentSlide.Shapes.HasTitle == Microsoft.Office.Core.MsoTriState.msoTrue && currentSlide.Shapes.Title.HasTextFrame == Microsoft.Office.Core.MsoTriState.msoTrue && currentSlide.Shapes.Title.TextFrame.HasText == Microsoft.Office.Core.MsoTriState.msoTrue) { return PPTDeckIO.CleanString(currentSlide.Shapes.Title.TextFrame.TextRange.Text); } //If there is no title, take the first object with text that has SheetDispositon.All foreach (TaggedShape ts in tsList) { if (ts.disp == SheetDisposition.All && ts.shape.HasTextFrame == Microsoft.Office.Core.MsoTriState.msoTrue && ts.shape.TextFrame.HasText == Microsoft.Office.Core.MsoTriState.msoTrue) { return PPTDeckIO.CleanString(ts.shape.TextFrame.TextRange.Text); } } } catch (Exception e) { Debug.WriteLine(e.ToString()); } //Last resort: Set the title to "Slide N" return "Slide " + currentSlide.SlideIndex.ToString(); } private static void MakeShapesVisible(PowerPoint.Shapes shapes) { foreach (PowerPoint.Shape shape in shapes) shape.Visible = Microsoft.Office.Core.MsoTriState.msoTrue; } private static void MakeShapesInvisible(PowerPoint.Shapes shapes) { foreach (PowerPoint.Shape shape in shapes) shape.Visible = Microsoft.Office.Core.MsoTriState.msoFalse; } /// /// Disassociate bitmaps from the filestream so that we can close the stream /// /// /// private static Image DisassociateBitmap(Image image) { //Need to disassociate bitmaps from the filestream so that we can close the stream //Workaround from http://support.microsoft.com/?id=814675 if (((Bitmap)image).PixelFormat == System.Drawing.Imaging.PixelFormat.Format8bppIndexed || ((Bitmap)image).PixelFormat == System.Drawing.Imaging.PixelFormat.Format4bppIndexed || ((Bitmap)image).PixelFormat == System.Drawing.Imaging.PixelFormat.Format1bppIndexed) { //The workaround does not support indexed bitmap formats, we convert it to something that isn't Bitmap orig = ((Bitmap)image).Clone(new Rectangle(0, 0, image.Width, image.Height), System.Drawing.Imaging.PixelFormat.Format24bppRgb); Bitmap b = new Bitmap(image.Width, image.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb); Graphics g = Graphics.FromImage(b); // NOTE: Need to possibly stretch image since original and new bitmaps may have different pixels per inch. g.DrawImage(orig, 0, 0, image.Width, image.Height); g.Dispose(); orig.Dispose(); image.Dispose(); image = b; } else { Bitmap b = new Bitmap(image.Width, image.Height, image.PixelFormat); Graphics g = Graphics.FromImage(b); g.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height)); g.Dispose(); image.Dispose(); image = b; } return image; } /// /// Extract the shapes range from a list of tagged shapes /// /// /// private static int[] BuildIntRange(List layer) { int[] shapeIndices = new int[layer.Count]; for (int i = 0; i < layer.Count; i++) shapeIndices[i] = layer[i].index; return shapeIndices; } /// /// /// /// /// /// /// /// /// /// /// The starting height to place this sheet at private static void ProcessLayer( List layer, TempFileCollection tfc, PowerPoint.Shapes shapes, Model.Presentation.DeckModel deck, Model.Presentation.SlideModel slide, float emfWidthRatio, float emfHeightRatio, int startHeight ) { if (layer.Count < 1) return; //Create the image int[] range = PPTDeckIO.BuildIntRange(layer); PowerPoint.ShapeRange sr = shapes.Range(range); PowerPoint.PpShapeFormat format; string fileExt = ""; bool bitmapMode = layer[0].isImage; if (bitmapMode) { format = PowerPoint.PpShapeFormat.ppShapeFormatPNG; fileExt = "png"; } else { format = PowerPoint.PpShapeFormat.ppShapeFormatEMF; fileExt = "emf"; } //Generate a new filename string dirpath = tfc.BasePath; string filename = PPTDeckIO.GenerateFilename(); filename = dirpath + "\\" + filename + "." + fileExt; while (File.Exists(filename)) { filename = PPTDeckIO.GenerateFilename(); filename = dirpath + "\\" + filename + "." + fileExt; } sr.Export(filename, format, 0, 0, PowerPoint.PpExportMode.ppRelativeToSlide); tfc.AddFile(filename, false); //Compute the MD5 of the BG FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read); MD5 md5Provider = new MD5CryptoServiceProvider(); byte[] md5 = md5Provider.ComputeHash(fs); fs.Seek(0, SeekOrigin.Begin); Image image = Image.FromStream(fs); if (bitmapMode) image = DisassociateBitmap(image); fs.Close(); //Calculate the geometry int xCoord = 0; int yCoord = 0; int width = 0; int height = 0; PPTDeckIO.CalculateGeometry( image, shapes, range, emfWidthRatio, emfHeightRatio, ref xCoord, ref yCoord, ref width, ref height ); //Create the ImageSheet ImageSheetModel sheet = new ImageSheetModel(deck, Guid.NewGuid(), layer[0].disp, new Rectangle(xCoord, yCoord, width, height), (ByteArray)md5, startHeight); //Add the ImageSheet to the Slide slide.ContentSheets.Add(sheet); //Add the Image+MD5 to the deck deck.AddSlideContent((ByteArray)md5, image); } private static PointF GetUpperCorner( float rotation, float left, float top, float width, float height ) { // BUG 1006: Need to account for rotation when determining bounds if( rotation != 0 ) { PointF[] pts = new PointF[4]; pts[0] = new PointF( left, top ); pts[1] = new PointF( left, top + height ); pts[2] = new PointF( left + width, top ); pts[3] = new PointF( left + width, top + height ); // Rotate the points System.Drawing.Drawing2D.Matrix m = new System.Drawing.Drawing2D.Matrix(); m.RotateAt( rotation, new PointF( left + width/2.0f, top + height/2.0f ) ); m.TransformPoints( pts ); return new PointF( Math.Min( Math.Min( Math.Min( pts[0].X, pts[1].X ), pts[2].X ), pts[3].X ), Math.Min( Math.Min( Math.Min( pts[0].Y, pts[1].Y ), pts[2].Y ), pts[3].Y ) ); } else { return new PointF( left, top ); } } private static void CalculateGeometry( Image image, PowerPoint.Shapes shapes, int[] range, float emfWidthRatio, float emfHeightRatio, ref int xCoord, ref int yCoord, ref int width, ref int height ) { float top = -1; float left = -1; // Get the size in pixels of the image if( image is System.Drawing.Imaging.Metafile ) { width = (int)(image.Width * emfWidthRatio); height = (int)(image.Height * emfHeightRatio); } else { width = image.Width; height = image.Height; } // Get the start coordinate of the image foreach( int i in range ) { PowerPoint.Shape currentShape = shapes[i]; if( currentShape.Type == Microsoft.Office.Core.MsoShapeType.msoAutoShape && currentShape.AutoShapeType == Microsoft.Office.Core.MsoAutoShapeType.msoShapeArc ) continue; PointF corner = GetUpperCorner( currentShape.Rotation, currentShape.Left, currentShape.Top, currentShape.Width, currentShape.Height ); float shapeLeft = corner.X; float shapeTop = corner.Y; if( top == -1 || shapeTop < top ) { top = shapeTop; } if( left == -1 || shapeLeft < left ) { left = shapeLeft; } if( currentShape.HasTextFrame == Microsoft.Office.Core.MsoTriState.msoTrue ) { float x1, x2, x3, x4; float y1, y2, y3, y4; currentShape.TextFrame.TextRange.RotatedBounds( out x1, out y1, out x2, out y2, out x3, out y3, out x4, out y4 ); float left_new = Math.Min( Math.Min( Math.Min( x1, x2 ), x3 ), x4 ); float top_new = Math.Min( Math.Min( Math.Min( y1, y2 ), y3 ), y4 ); if( top == -1 || ( top_new < top && currentShape.Height>currentShape.TextFrame.TextRange.BoundHeight)) { top = top_new; } if( left == -1 || ( left_new < left && currentShape.Width>currentShape.TextFrame.TextRange.BoundWidth) ) { left = left_new; } } } yCoord = (int)Math.Round( top ); xCoord = (int)Math.Round( left ); // Unsure non-zero width and height (NOTE: Must be at least 2 to display some lines) if( width <= 1 ) width = 2; if( height <= 1 ) height = 2; } /* OBSOLETE private static void ProcessLayer(TempFileCollection tfc, PowerPoint.Shapes shapes, int[] range, Model.Presentation.DeckModel deck, Model.Presentation.SlideModel slide, Model.Presentation.SheetDisposition disposition, int emfHeight, int startHeight) { //Create the image if (range.Length > 0) { PowerPoint.ShapeRange sr = shapes.Range(range); //Generate a new filename string dirpath = tfc.BasePath; string filename = PPTDeckIO.GenerateFilename(); filename = dirpath + "\\" + filename + ".emf"; while (File.Exists(filename)) { filename = PPTDeckIO.GenerateFilename(); filename = dirpath + "\\" + filename + ".emf"; } sr.Export(filename, PowerPoint.PpShapeFormat.ppShapeFormatEMF, 0, 0, PowerPoint.PpExportMode.ppRelativeToSlide); tfc.AddFile(filename, false); //Compute the MD5 of the BG FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read); MD5 md5Provider = new MD5CryptoServiceProvider(); byte[] md5 = md5Provider.ComputeHash(fs); fs.Seek(0, SeekOrigin.Begin); Image image = Image.FromStream(fs); fs.Close(); //Calculate the geometry int xCoord = 0; int yCoord = 0; int width = 0; int height = 0; PPTDeckIO.CalculateGeometry(image, slide, emfHeight, shapes, range, ref xCoord, ref yCoord, ref width, ref height); //Create the ImageSheet ImageSheetModel sheet = new ImageSheetModel(deck, Guid.NewGuid(), disposition, new Rectangle(xCoord, yCoord, width, height), (ByteArray)md5, startHeight); //Add the ImageSheet to the Slide slide.ContentSheets.Add(sheet); //Add the Image+MD5 to the deck deck.AddSlideContent((ByteArray)md5, image); } } */ /* UNUSED /// /// Construct a sheet from a single shape /// /// /// /// /// /// /// /// The starting height to place this sheet at private static void ProcessSheet(TempFileCollection tfc, PowerPoint.Shape shape, Model.Presentation.DeckModel deck, Model.Presentation.SlideModel slide, Model.Presentation.SheetDisposition disposition, int emfHeight, int startHeight) { PowerPoint.PpShapeFormat format; string fileExt = ""; bool bitmapMode = false; if (shape.Name.StartsWith("Picture")) bitmapMode = true; if (bitmapMode) { format = PowerPoint.PpShapeFormat.ppShapeFormatJPG; fileExt = "jpg"; } else { format = PowerPoint.PpShapeFormat.ppShapeFormatEMF; fileExt = "emf"; } string dirpath = tfc.BasePath; string filename = PPTDeckIO.GenerateFilename(); filename = dirpath + "\\" + filename + "." + fileExt; while (File.Exists(filename)) { filename = PPTDeckIO.GenerateFilename(); filename = dirpath + "\\" + filename + "." + fileExt; } shape.Export(filename, format, 0, 0, PowerPoint.PpExportMode.ppRelativeToSlide); tfc.AddFile(filename, false); //Compute the MD5 of the BG FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read); MD5 md5Provider = new MD5CryptoServiceProvider(); byte[] md5 = md5Provider.ComputeHash(fs); fs.Seek(0, SeekOrigin.Begin); Image image = Image.FromStream(fs); if (bitmapMode) image = DisassociateBitmap(image); fs.Close(); //Calculate the geometry int xCoord = (int) shape.Left - 2; int yCoord = (int) shape.Top - 2; int width = (int) shape.Width + 4; // Hackery to make lines visibile int height = (int) shape.Height + 4; // Ignoring EMF Normalization //Create the ImageSheet ImageSheetModel sheet = new ImageSheetModel(deck, Guid.NewGuid(), disposition, new Rectangle(xCoord, yCoord, width, height), (ByteArray)md5, startHeight); //Add the ImageSheet to the Slide slide.ContentSheets.Add(sheet); //Add the Image+MD5 to the deck deck.AddSlideContent((ByteArray)md5, image); } */ /* OBSOLETE private static void CalculateGeometry(Image image, SlideModel slide, int emfHeight, PowerPoint.Shapes shapes, int[] range, ref int xCoord, ref int yCoord, ref int width, ref int height) { float topmost = -1; float leftmost = -1; float bottommost = -1; float rightmost = -1; foreach (int i in range) { PowerPoint.Shape currentShape = shapes[i]; if (topmost == -1 || currentShape.Top < topmost) { topmost = currentShape.Top; } if (leftmost == -1 || currentShape.Left < leftmost) { leftmost = currentShape.Left; } // Fix BUG 926: Single lines invisible // Horizontal and vertical lines have a width/height of 0, need to account for width and height of line. float currentShapeWidth = currentShape.Width; float currentShapeHeight = currentShape.Height; if( currentShape.Type == Microsoft.Office.Core.MsoShapeType.msoLine ) { currentShapeWidth = Math.Max( currentShape.Width, currentShape.Line.Weight ); currentShapeHeight = Math.Max( currentShape.Height, currentShape.Line.Weight ); } float currentRight = currentShape.Left + currentShapeWidth; if (rightmost == -1 || currentRight > rightmost) { rightmost = currentRight; } float currentBottom = currentShape.Top + currentShapeHeight; if (bottommost == -1 || currentBottom > bottommost) { bottommost = currentBottom; } // Fix BUG 618: Slide content scrunched vertically // Some text boxes have a text frame that is larger than the shape itself so we need to calculate bounds based on this as well if( currentShape.HasTextFrame == Microsoft.Office.Core.MsoTriState.msoTrue ) { if( topmost == -1 || currentShape.TextFrame.TextRange.BoundTop < topmost ) { topmost = currentShape.TextFrame.TextRange.BoundTop; } if( leftmost == -1 || currentShape.TextFrame.TextRange.BoundLeft < leftmost ) { leftmost = currentShape.TextFrame.TextRange.BoundLeft; } float currentRight2 = currentShape.TextFrame.TextRange.BoundLeft + currentShape.TextFrame.TextRange.BoundWidth; if( rightmost == -1 || currentRight2 > rightmost ) { rightmost = currentRight2; } float currentBottom2 = currentShape.TextFrame.TextRange.BoundTop + currentShape.TextFrame.TextRange.BoundHeight; if( bottommost == -1 || currentBottom2 > bottommost ) { bottommost = currentBottom2; } } } yCoord = (int)Math.Round(topmost); xCoord = (int)Math.Round(leftmost); width = (int)Math.Max(Math.Ceiling(rightmost - leftmost), 2); height = (int)Math.Max(Math.Ceiling(bottommost - topmost), 2); } */ #endregion #region OpenCP3 Method public static DeckModel OpenCP3(FileInfo file) { //Deserialize it FileStream fs = file.Open(FileMode.Open, FileAccess.Read); try { BinaryFormatter bf = new BinaryFormatter(); DeckModel toReturn = (DeckModel)bf.Deserialize(fs); toReturn.Filename = file.FullName; return toReturn; } finally { fs.Close(); } } #endregion #region SaveAsCP3 Method public static void SaveAsCP3(FileInfo file, DeckModel in_deck) { //Update the deck's filename DeckModel deck = in_deck.Copy(); deck.current_subs = false; deck.current_poll = false; if ((deck.Disposition & DeckDisposition.Remote) != 0){ deck.Disposition = DeckDisposition.Empty; } using (Synchronizer.Lock(deck)) { deck.Filename = file.FullName; } //Serialize it FileStream fs = file.Open(FileMode.Create, FileAccess.Write); try { BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fs, deck); fs.Flush(); } finally { fs.Close(); } } #endregion #region Export Deck Method //WARNING - This method assumes that all necessary sanity checks for path have been done! /// /// /// /// /// /// /// a List of strings with all the names of the image files public static List ExportDeck(DefaultDeckTraversalModel traversal, string path, System.Drawing.Imaging.ImageFormat format) { return PPTDeckIO.ExportDeck(traversal, path, format, 0, 0, 1.0f); } //WARNING - This method assumes that all necessary sanity checks for path have been done! /// /// /// /// /// /// /// a List of strings with all the names of the image files public static List ExportDeck(DefaultDeckTraversalModel traversal, string path, System.Drawing.Imaging.ImageFormat format, int width, int height, float scale ) { List file_names = new List(); //Info here: http://gotdotnet.com/Community/MessageBoard/Thread.aspx?id=27804 //Iterate over all the slides using (Synchronizer.Lock(traversal.SyncRoot)) { using (Synchronizer.Lock(traversal.Deck.SyncRoot)) { using (Synchronizer.Lock(traversal.Deck.TableOfContents.SyncRoot)) { //Create the directory, if it doesn't already exist if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } int imageCount = 1; TableOfContentsModel.Entry currentEntry = traversal.Deck.TableOfContents.Entries[0]; while (currentEntry != null) { Color background = Color.Transparent; BackgroundTemplate template = null; // Add the background color // First see if there is a Slide BG, if not, try the Deck. Otherwise, use transparent. using (Synchronizer.Lock(currentEntry.Slide.SyncRoot)) { if (currentEntry.Slide.BackgroundColor != Color.Empty) { background = currentEntry.Slide.BackgroundColor; } else if (traversal.Deck.DeckBackgroundColor != Color.Empty) { background = traversal.Deck.DeckBackgroundColor; } if (currentEntry.Slide.BackgroundTemplate != null) { template = currentEntry.Slide.BackgroundTemplate; } else if (traversal.Deck.DeckBackgroundTemplate != null) { template = traversal.Deck.DeckBackgroundTemplate; } } // Get the Image Bitmap toExport = DrawSlide(currentEntry, template, background, SheetDisposition.Background | SheetDisposition.Public | SheetDisposition.Student | SheetDisposition.All, width, height, scale ); // Format the imageCount string number = ""; if (imageCount >= 0 && imageCount < 10) { number = "00" + imageCount; } else if (imageCount >= 10 && imageCount < 100) { number = "0" + imageCount; } else { number = "" + imageCount; } // Get the extension string based on the exported image type string extension = ""; if (format == System.Drawing.Imaging.ImageFormat.Bmp) { extension = "bmp"; } else if (format == System.Drawing.Imaging.ImageFormat.Emf) { extension = "emf"; } else if (format == System.Drawing.Imaging.ImageFormat.Exif) { extension = "exf"; } else if (format == System.Drawing.Imaging.ImageFormat.Gif) { extension = "gif"; } else if (format == System.Drawing.Imaging.ImageFormat.Icon) { extension = "ico"; } else if (format == System.Drawing.Imaging.ImageFormat.Jpeg) { extension = "jpg"; } else if (format == System.Drawing.Imaging.ImageFormat.Png) { extension = "png"; } else if (format == System.Drawing.Imaging.ImageFormat.Tiff) { extension = "tif"; } else if (format == System.Drawing.Imaging.ImageFormat.Wmf) { extension = "wmf"; } else { extension = "bmp"; } // Construct the complete file name and save string file_name = traversal.Deck.HumanName + "_" + number + "." + extension; //string file_name = "slide" + imageCount.ToString() + "." + extension; toExport.Save(path + "\\" + file_name, format); /// Save the file name to our list file_names.Add(file_name); // Increase the counter imageCount++; // Done, get next entry currentEntry = traversal.FindNext(currentEntry); // Cleanup toExport.Dispose(); } } } } return file_names; } // NOTE: Eventually this code should be converted to use SlideRenderer instead of each SheetRenderer. There were some issues with doing // this initially so for the time being we will keep it like this. public static Bitmap DrawSlide(TableOfContentsModel.Entry currentEntry, BackgroundTemplate template, System.Drawing.Color background, SheetDisposition dispositions) { return PPTDeckIO.DrawSlide(currentEntry, template, background, dispositions, 0, 0, 1.0f); } // NOTE: Eventually this code should be converted to use SlideRenderer instead of each SheetRenderer. There were some issues with doing // this initially so for the time being we will keep it like this. public static Bitmap DrawSlide(TableOfContentsModel.Entry currentEntry, BackgroundTemplate template, System.Drawing.Color background, SheetDisposition dispositions, int width, int height, float scale ) { // Save the old state System.Drawing.Drawing2D.GraphicsState oldState; using (Synchronizer.Lock(currentEntry.Slide.SyncRoot)) { Rectangle rect; int boundsWidth = currentEntry.Slide.Bounds.Width; int boundsHeight = currentEntry.Slide.Bounds.Height; int exportWidth = width; int exportHeight = height; if (width > 0 && height > 0) { // Do Nothing } else if (width > 0) { exportHeight = (int)Math.Round( ( (float)width / (float)boundsWidth ) * (float)boundsHeight ); } else if (height > 0) { exportWidth = (int)Math.Round( ( (float)height / (float)boundsHeight ) * (float)boundsWidth ); } else { exportWidth = boundsWidth; exportHeight = boundsHeight; } // Scale the size exportWidth = (int)Math.Round( exportWidth * scale ); exportHeight = (int)Math.Round( exportHeight * scale ); rect = new Rectangle(0, 0, exportWidth, exportHeight); //Note: Uses DibGraphicsBuffer from TPC SDK to do antialiasing //See: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dntablet/html/tbconprintingink.asp /// create the bitmap we're exporting to Bitmap toExport = new Bitmap(rect.Width, rect.Height); /// create what we will be drawing on to the export Graphics toSave = Graphics.FromImage(toExport); /// draw the slide data on a temporary graphics object in a temporary form System.Windows.Forms.Form tempForm = new System.Windows.Forms.Form(); Graphics screenGraphics = tempForm.CreateGraphics(); DibGraphicsBuffer dib = new DibGraphicsBuffer(); Graphics tempGraphics = dib.RequestBuffer(screenGraphics, rect.Width, rect.Height); //Add the background color //First see if there is a Slide BG, if not, try the Deck. Otherwise, use transparent. tempGraphics.Clear(background); //Add the background template if (template != null) { using (BackgroundTemplateRenderer bkgRender = new BackgroundTemplateRenderer(template)) { bkgRender.DrawAll(tempGraphics, rect); } } //Get the Slide content and draw it oldState = tempGraphics.Save(); Model.Presentation.SlideModel.SheetCollection sheets = currentEntry.Slide.ContentSheets; for (int i = 0; i < sheets.Count; i++) { Model.Viewer.SlideDisplayModel display = new Model.Viewer.SlideDisplayModel(tempGraphics, null); Rectangle slide = rect; float zoom = 1f; if (currentEntry.Slide != null) { slide = currentEntry.Slide.Bounds; zoom = currentEntry.Slide.Zoom; } System.Drawing.Drawing2D.Matrix pixel, ink; display.FitSlideToBounds(System.Windows.Forms.DockStyle.Fill, rect, zoom, ref slide, out pixel, out ink); using (Synchronizer.Lock(display.SyncRoot)) { display.SheetDisposition = dispositions; display.Bounds = slide; display.PixelTransform = pixel; display.InkTransform = ink; } Viewer.Slides.SheetRenderer r = Viewer.Slides.SheetRenderer.ForStaticSheet(display, sheets[i]); if ((r.Sheet.Disposition & dispositions) != 0) r.Paint(new System.Windows.Forms.PaintEventArgs(tempGraphics, rect)); r.Dispose(); } //Restore the Old State tempGraphics.Restore(oldState); oldState = tempGraphics.Save(); //Get the Annotation content and draw it sheets = currentEntry.Slide.AnnotationSheets; for (int i = 0; i < sheets.Count; i++) { Model.Viewer.SlideDisplayModel display = new Model.Viewer.SlideDisplayModel(tempGraphics, null); Rectangle slide = rect; float zoom = 1f; if (currentEntry.Slide != null) { slide = currentEntry.Slide.Bounds; zoom = currentEntry.Slide.Zoom; } System.Drawing.Drawing2D.Matrix pixel, ink; display.FitSlideToBounds(System.Windows.Forms.DockStyle.Fill, rect, zoom, ref slide, out pixel, out ink); using (Synchronizer.Lock(display.SyncRoot)) { display.SheetDisposition = dispositions; display.Bounds = slide; display.PixelTransform = pixel; display.InkTransform = ink; } Viewer.Slides.SheetRenderer r = Viewer.Slides.SheetRenderer.ForStaticSheet(display, sheets[i]); if ((r.Sheet.Disposition & dispositions) != 0) r.Paint(new System.Windows.Forms.PaintEventArgs(tempGraphics, rect)); r.Dispose(); } //Restore the Old State tempGraphics.Restore(oldState); //Export the image //Merge the graphics dib.PaintBuffer(toSave, 0, 0); //Dispose all the graphics toSave.Dispose(); screenGraphics.Dispose(); tempGraphics.Dispose(); return toExport; } } #endregion #region Helper Methods /// /// Make sure all characters in a string are printable - replace any out of range characters by a space /// /// Input string /// private static string CleanString(string str){ StringBuilder sb = new StringBuilder(); for (int i = 0; i < str.Length; i++){ char c = str[i]; if (! (Char.IsLetterOrDigit(c) || Char.IsPunctuation(c) || Char.IsSymbol(c))) { sb.Append(' '); } else { sb.Append(c); } } return sb.ToString(); } /// /// Construct a new file name, which is unique for the session /// /// File name of the from "cptmpfilexx" private static string GenerateFilename() { string result = "cptmpfile" + tmpFileCtr; tmpFileCtr++; return result; } private static int tmpFileCtr = 0; /// /// Convert from a string to the corresponding sheet disposition. /// /// /// private static Model.Presentation.SheetDisposition Disposition(string str) { if (str.Equals("Instructor")) return Model.Presentation.SheetDisposition.Instructor; if (str.Equals("Student")) return Model.Presentation.SheetDisposition.Student; if (str.Equals("Shared")) return Model.Presentation.SheetDisposition.Public; return Model.Presentation.SheetDisposition.All; // All as the default } #endregion } }