I have been struggling with C# and PowerPoint, I posted earlier about how to update Links in ppt using C# if the links were set to manual, I didn't get any responses so I figured I would try to circumvent the issue by setting the links to automatic in the file, then when C# opens it it updates them, saves the file, and then breaks them and saves it as another file name, but that hasn't proven any easier.
All I need to know is how to break the links. I know some VBA and wrote a code to break them, but when I call a macro in C# with a RunMacro method it doesn't seem to be working with the method I am using (? - I'm new to C# so I don't fully understand why this is, but if you Google "run macro PowerPoint C#" you will find the way I am sure I tried to go about it.) Please help, I am at a complete loss.
My script looks something like this
Using PowerPoint = Microsoft.Office.Interop.PowerPoint;
public void Main()
{
PowerPoint.Application ppt = new PowerPoint.Application();
PowerPoint.Presentation PRS = ppt.Presentations.Open(#"Filename.pptm",
Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoFalse, Microsoft.Office.Core.MsoTriState.msoTrue);
PRS.UpdateLinks();
PRS.Save
//here is where I need to break the links
PRS.SaveAs(#"filename with links broken.pptm",
Microsoft.Office.Interop.PowerPoint.PpSaveAsFileType.ppSaveAsOpenXMLPresentationMacroEnabled, MsoTriState.msoTrue);
PRS.Close();
ppt.Quit();
}
I tried setting the linkformat to manual before opening the file, but that doesn't affect any shapes that are already created, only new ones created within the program afterwards.
I have done a similar project in Powerpoint which also involves breaking links. In my program I am reading from an Excel file and take this data and put it into a Powerpoint presentation. In my Powerpoint template file I have all of my links to my Excel file laid out and formatted the way that I want it to look. The program begins to run and this fills the Excel file. After the writing to Excel is finished, I open up Powerpoint and UpdateLinks() to my presentation. Once the links are updated I break the links using a for loop on the Shapes in the Powerpoint. After, I save and close the document. Below is my function that I use to create my Powerpoint file based off of each of my ppt file templates (for my example I iterate through multiple ppt files because each ppt file contains only one slide. They are all combined into one slideshow later in the process).
public void CreatePowerpoint()
{
string[] fileArray = Directory.GetFiles(#"C:\Users\Me\Desktop\test");
Microsoft.Office.Interop.PowerPoint.Application pptApp = new Microsoft.Office.Interop.PowerPoint.Application();
for (int i = 0; i < fileArray.Length; i++)
{
string file = fileArray[i];
Microsoft.Office.Interop.PowerPoint.Presentation powerpoint = pptApp.Presentations.Open(file);
powerpoint.UpdateLinks();
Microsoft.Office.Interop.PowerPoint.Slides slides = powerpoint.Slides;
Microsoft.Office.Interop.PowerPoint.Slide slide = slides[1];
Microsoft.Office.Interop.PowerPoint.Shapes pptShapes = (Microsoft.Office.Interop.PowerPoint.Shapes)slide.Shapes;
foreach (Microsoft.Office.Interop.PowerPoint.Shape y in pptShapes)
{
Microsoft.Office.Interop.PowerPoint.Shape j = (Microsoft.Office.Interop.PowerPoint.Shape)y;
try
{
//If auto link update is disabled for links
//j.LinkFormat.j.Update();
if (j.LinkFormat != null)
{
j.LinkFormat.BreakLink();
}
}
catch (Exception)
{
}
}
powerpoint.SaveAs(#"C:\Users\Me\Desktop\test" + i + ".pptx");
powerpoint.Close();
Thread.Sleep(3000);
}
pptApp.Quit();
}
Changing things before opening a file will have no effect on files that aren't open, of course. Iterate through the shapes collection on each slide and for each shape:
If .Type = msoLinkedOLEObject Then
.LinkFormat.BreakLink
End If
msoLinkedOLEObject = 10
Related
I am trying to copy slides from one PowerPoint presentation to another. I have used the procedure outlined in the following article, and it generally works fine.
https://learn.microsoft.com/en-us/previous-versions/office/developer/office-2007/ee361883(v=office.12)?redirectedfrom=MSDN
However, when the slide to be copied contains notes, the resulting presentation after copying is corrupted. I've noticed that the code generates a new notesMaster which is not added to the notesMasterIdLst in presentation.xml, and have a suspicion this might be the issue. However, I cannot add the new notes master to the presentation, as a presentation can only have one notesMaster.
According to the Microsoft Documentation, Open XML SDK is defined this way:
The Open XML SDK 2.5 simplifies the task of manipulating Open XML
packages and the underlying Open XML schema elements within a package.
The Open XML SDK 2.5 encapsulates many common tasks that developers
perform on Open XML packages, so that you can perform complex
operations with just a few lines of code.
It looks like it is not easy to solve your problem using the Open XML SDK. If you use Aspose.Slides for .NET you will copy a slide with its notes as shown below:
var sourceFileName = "example1.pptx";
var targetFileName = "example2.pptx";
var slideIndex = 0;
using (var sourcePresentation = new Presentation(sourceFileName))
using (var targetPresentation = new Presentation(targetFileName))
{
var slide = sourcePresentation.Slides[slideIndex];
targetPresentation.Slides.AddClone(slide);
targetPresentation.Save(targetFileName, SaveFormat.Pptx);
}
You can also evaluate Aspose.Slides Cloud for presentation manipulating. This REST-based API allows you to make 150 free API calls per month for API learning and presentation processing. The following code example shows you how to do the same using Aspose.Slides Cloud:
var slidesApi = new SlidesApi("my_client_id", "my_client_key");
var sourceFileName = "example1.pptx";
var targetFileName = "example2.pptx";
var slideIndex = 1;
using (var sourceStream = File.OpenRead(sourceFileName))
slidesApi.UploadFile(sourceFileName, sourceStream);
using (var targetStream = File.OpenRead(targetFileName))
slidesApi.UploadFile(targetFileName, targetStream);
slidesApi.CopySlide(targetFileName, slideIndex, null, sourceFileName);
using (var resultStream = slidesApi.DownloadFile(targetFileName))
using (var fileStream = File.OpenWrite(targetFileName))
resultStream.CopyTo(fileStream);
I work as a Support Developer at Aspose.
I believe I managed to solve this issue by doing the following steps:
Make the source presentations editable when opening them:
using (PresentationDocument mySourceDeck =
PresentationDocument.Open(
presentationFolder + sourcePresentation, true))
{
PresentationPart sourcePresPart =
mySourceDeck.PresentationPart;
Copy the notes slide CommonSlideData from the slide, then delete the notes slide part from the slide:
sp = (SlidePart)sourcePresPart.GetPartById(slideId.RelationshipId);
CommonSlideData notesSlideData = null;
if (sp.NotesSlidePart != null)
{
notesSlideData = (CommonSlideData)sp.NotesSlidePart.NotesSlide.CommonSlideData.CloneNode(true);
sp.DeletePart(sp.NotesSlidePart);
}
Readd any existing notes slide data by adding a new NotesSlidePart to the copied slide (now added to the target presentation and called destSp), adding relationship parts and a new NotesSlide object initialised with the copied notes slide data.
if (notesSlideData != null)
{
NotesSlidePart notesSlidePart1 = destSp.AddNewPart<NotesSlidePart>();
notesSlidePart1.AddPart(destSp);
notesSlidePart1.AddPart(destPresPart.NotesMasterPart);
NotesSlide notesSlide = new NotesSlide(notesSlideData);
notesSlidePart1.NotesSlide = notesSlide;
}
Warning: The notes slides will be deleted from the source presentation files, so you might want to make a copy of them first, or add them back to the presentation after it has been copied/merged.
This seems to retain at least some existing formatting of the notes slides, such as bold text. However, I have not yet tested this on a lot of different presentations so I suppose there could be some issues if the notes slides are based on very different notes slide masters, but I'm not sure.
Related to this, I ran into a similar issue after getting the notes slides to work, which seemed to be because of any custom xml parts that existed on the presentation to be copied. These presentations worked after adding some code to add relationships to the copied presentation's CustomXmlPart to the target presentation:
foreach (var customXmlPart in destSp.GetPartsOfType<CustomXmlPart>())
{
destPresPart.AddPart(customXmlPart);
}
I have a Microsoft word 2010 add-in project in visual studio, I just followed the MSDN guide to making a new tab with custom functionality on the ribbon. I've done some googling, but I cant seem to find any examples (or if its even possible) to use the C# to find a bookmark, then use the bookmarks name in an SQL query and populate it. The documents I am working with can have dozens of bookmarks, and there are hundreds of documents. Automating this process is a high priority.
So basically if you want to automate word documents (building word document templates via word bookmarks) Here's how I normally go about doing it.
Copy The Template
Work On The Template
Save In Desired Format
Delete Template Copy
Each of the sections that you are replacing within your word document you have to insert a bookmark for that location (easiest way to input text in an area).
I always create a function to accomplish this, and I end up passing in the path - as well as all of the text to replace my in-document bookmarks. The function call can get long sometimes, but it works for me.
Application app = new Application();
Document doc = app.Documents.Open("sDocumentCopyPath.docx");
if (doc.Bookmarks.Exists("bookmark_1"))
{
object oBookMark = "bookmark_1";
doc.Bookmarks.get_Item(ref oBookMark).Range.Text = My Text To Replace bookmark_1;
}
if (doc.Bookmarks.Exists("bookmark_2"))
{
object oBookMark = "bookmark_2";
doc.Bookmarks.get_Item(ref oBookMark).Range.Text = My Text To Replace bookmark_2;
}
doc.ExportAsFixedFormat("myNewPdf.pdf", WdExportFormat.wdExportFormatPDF);
((_Document)doc).Close();
((_Application)app).Quit();
This code should get you up and running unless you want to pass in all the values into a function.
Sometimes if you have large amounts of fields you can build objects/classes to contain the values.
If you need more examples I'm working on a blog post as well, so I have a lot more detail if this wasn't clear enough for your use case.
You can use Spire.Doc or FreeSpire.Doc library for this purpose. I have a github repo, that I showed an example how to working it.
using Spire.Doc;
using Spire.Doc.Documents;
using Spire.Doc.Fields;
using System;
using System.Configuration;
using System.Drawing;
using System.IO;
namespace WorkingDocAndPdf
{
class Program
{
static void Main(string[] args)
{
var sourceFilePath = ConfigurationManager.AppSettings["SourceFilePath"];
var saveFilePath = ConfigurationManager.AppSettings["SaveFilePath"];
var document = new Document(sourceFilePath);
var bookmarksNavigator = new BookmarksNavigator(document);
bookmarksNavigator.MoveToBookmark("client_name");
bookmarksNavigator.ReplaceBookmarkContent("Ramil", true);
bookmarksNavigator.MoveToBookmark("client_taxno");
bookmarksNavigator.ReplaceBookmarkContent("VN-12300254178XY6", true);
bookmarksNavigator.MoveToBookmark("amount");
bookmarksNavigator.ReplaceBookmarkContent("871 AZN", true);
bookmarksNavigator.MoveToBookmark("date");
bookmarksNavigator.ReplaceBookmarkContent(DateTime.Now.ToString("dd.MM.yyyy"), true);
//It is for picture
var sealPath = ConfigurationManager.AppSettings["SealPath"];
bookmarksNavigator.MoveToBookmark("seal", true, true);
var section = document.AddSection();
var image = Image.FromFile(sealPath);
var paragraph = section.AddParagraph();
paragraph.AppendPicture(image);
bookmarksNavigator.InsertParagraph(paragraph);
document.Sections.Remove(section);
if (!Directory.Exists(saveFilePath))
Directory.CreateDirectory(saveFilePath);
var saveFileFullPath = $"{saveFilePath}\\{Guid.NewGuid()}.pdf";
//It is for refresh cross reference bookmark, that you can use one bookmark on different location in document. In word shortcut it is `CTRL A + F9`
document.IsUpdateFields = true;
document.SaveToFile(saveFileFullPath, FileFormat.PDF);
}
}
}
For more information, you can visit my github repo: WorkingDocAndPdf_FreeSpireDoc
My article about FreeSpire.Doc (but written in Azerbaijani): C# working Word and PDF files. Print forms
I am trying to delete all embedded object from Word and PowerPoint files using openxml SDK. I am new to Open XML and not sure whether I am doing this correctly. Below is the code I have. My intention is to remove any objects embedded and to delete images embedded. Both codes when executed are giving errors.
Code that I tried to delete all embedded items in the document.
using (var wdDoc = WordprocessingDocument.Open(wordFilePath, true))
{
var docPart = wdDoc.MainDocumentPart;
var document = docPart.Document;
var embeddedObjectsCount = docPart.EmbeddedObjectParts.Count();
while (embeddedObjectsCount > 0)
{
docPart.DeletePart(docPart.EmbeddedObjectParts.FirstOrDefault());
embeddedObjectsCount = docPart.EmbeddedObjectParts.Count();
}
}
Code that I tried to delete all images in the document. (This works partially if I don't have any objects embedded in the document.)
using (var wdDoc = WordprocessingDocument.Open(wordFilePath, true))
{
var docPart = wdDoc.MainDocumentPart;
var document = docPart.Document;
var imageObjectsCount = docPart.ImageParts.Count();
while (imageObjectsCount > 0)
{
docPart.DeletePart(docPart.ImageParts.FirstOrDefault());
imageObjectsCount = docPart.ImageParts.Count();
}
}
When I run the above code the file I use is getting corrupted. I would like to know how to remove all embedded objects from Word without corrupting the file.
I haven't done anything on PowerPoint yet, but I hope it would be similar to Word document.
I managed to find a solution for my problem. I had to dive in to the concepts of Open XML SDK to get this. However, I am not so sure on whether this is the optimal solution.
Goal
Remove all embedded objects in PowerPoint and Word.
Remove all images in PowerPoint and Word.
For Word
//using Ovml = DocumentFormat.OpenXml.Vml.Office;
//Determine whether there are any Embedded Objects in the document
using (var wdDoc = WordprocessingDocument.Open(wordFilePath, true))
{
var docPart = wdDoc.MainDocumentPart;
var docHasEmbeddedOleObjects = document.Body.Descendants<Ovml.OleObject>().Any();
if (docHasEmbeddedOleObjects)
{
foreach (var oleObj in document.Body.Descendants<Ovml.OleObject>())
{
oleObj.Remove(); //Remove each ole object in the document. This will remove the object from view in word.
}
//Delete the embedded objects. This will remove the actual attached files from the document.
docPart.DeleteParts(docPart.EmbeddedObjectParts);
//Delete all picture in the document
docPart.DeleteParts(docPart.ImageParts);
}
}
For PowerPoint
using (var ppt = PresentationDocument.Open(powerPointFilePath, true))
{
foreach (var slide in slides)
{
//Remove Ole Objects
var oleObjectCount = slide.Slide.Descendants<OleObject>().Count();
while (oleObjectCount > 0)
{
var oleObj = slide.Slide.Descendants<OleObject>().FirstOrDefault();
var oleObjGraphicFrame = oleObj?.Ancestors<GraphicFrame>().FirstOrDefault();
if (oleObjGraphicFrame != null)
{
oleObjGraphicFrame.RemoveAllChildren();
oleObjGraphicFrame.Remove();
}
oleObjectCount = slide.Slide.Descendants<OleObject>().Count();
}
//Delete embedded objects
slide.DeleteParts(slide.EmbeddedObjectParts);
//Delete all pictures
slide.DeleteParts(slide.ImageParts);
}
}
In my experience, the fastest way to "corrupt" an OpenXML document is to have a bad relation pointer. The fastest way to get a handle of what's behind those cryptic error messages is to go straight to the raw OpenXML markup.
To get an idea of what is happening:
Make a copy of your file before running your code, call this A.docx
Run your code and make a copy of your result, call this B.docx
Rename A.docx and B.docx to A.zip and B.zip
Investigate the source file
First, inside of A.zip, open the file called [Content_Types].xml. Take note of the parts that you would like to remove. Think of this file as a declaration to the word processor of the types of files that it will encounter in the sub-directories.
Parts such as the document content (word/document.xml) or the footnotes part (word/footnotes.xml) have their own relations parts named as [part path here].rels.
For example, document.xml.rels will hold relation information for things like charts, hyperlinks, and images in document.xml; likewise, footnotes.xml.rels holds information on things like hyperlinks in footnotes.xml.
Investigate the result file
Now open B.zip and compare the [Content_Types].xml files. Do you see a part there that you intended to delete? Is there a part missing that you did not intend to delete?
Inside of the word sub-directory in B.zip, do you see any embedded files that are not listed in the [Content_Types].xml file?
If you take a look at the raw markup, and the error doesn't jump out at you, feel free to comment with what some more details about your file structure and we can troubleshoot from there.
I'm using Microsoft.Office.Interop.PowerPoint library to open and view a PowerPoint file. I'm opening the file as an SlideShow without problems. The code to open the file is the following:
Microsoft.Office.Interop.PowerPoint.Application _pptApp;
Microsoft.Office.Interop.PowerPoint.Presentation _ppt;
Microsoft.Office.Interop.PowerPoint.Presentations _pptPresentations;
Microsoft.Office.Interop.PowerPoint.SlideShowWindow _pptSlideShowWindow;
private void Open(string fileName)
{
_pptApp = new Microsoft.Office.Interop.PowerPoint.Application();
_pptPresentations = _pptApp.Presentations;
//fileName represents the full path of the file
_ppt = _pptPresentations.Open(fileName, Microsoft.Office.Core.MsoTriState.msoTrue,
Microsoft.Office.Core.MsoTriState.msoTrue,Microsoft.Office.Core.MsoTriState.msoFalse);
_ppt.SlideShowSettings.ShowPresenterView = Microsoft.Office.Core.MsoTriState.msoFalse;
_ppt.SlideShowSettings.ShowType = Microsoft.Office.Interop.PowerPoint.PpSlideShowType.ppShowTypeSpeaker;
_pptSlideShowWindow = _ppt.SlideShowSettings.Run();
}
The code above works fine. With the _pptSlideShowWindow I can do some operations, like go to next slide _pptSlideShowWindow.View.Next(); or go to previous slide _pptSlideShowWindow.View.Previous();.
I would like to Zoom in and zoom out the presentations while it is on SlideShow. I've been looking around and I haven't seen any solution that works for me. The SlideShowView has a Zoom property but it's read only:
http://msdn.microsoft.com/en-us/library/microsoft.office.interop.powerpoint.slideshowview.zoom.aspx
I've tried not to open as SlideShow but the result is not good, as I haven't managed to open the file in reading mode programatically.
I'm a bit lost with this. Any help with this will be really appreciated.
Regards,
Pau
I am currently trying to change some settings in a .pptx files via C# and Microsoft.Office.Interop.PowerPoint. I have some .wmv movies linked on several slides of the presentation. At the time the presentations were created, all movies play as soon as they are clicked. However, I want to change this to start automatically playing as soon as the slide is viewed. This this has to be done to a lot of presentations, so there is no way to do this manually.
I found the PlaySettings.PlayOnEntry property, but I can't figure out how to use it. I found several examples how to do this with a new movie to be embedded (and then, only for Visual Basic), but since the movies are already embedded, this is not what I want.
I also have no idea how I can actually access any objects on the current slide, maybe there is a way to check if a shape is a video-file and then change above setting, but the MSDN-Reference is not very helpful on Office-Topics. I'm using Powerpoint 2007 and Visual Studio 2010 if that matters.
#Lennart's solution is part of it, you then need a page trigger
var videoAnimation = slide.TimeLine.MainSequence.FindFirstAnimationFor(objShapes);
if (videoAnimation != null)
{
videoAnimation.Timing.TriggerType = PowerPoint.MsoAnimTriggerType.msoAnimTriggerWithPrevious;
}
Got it. Searching through all shapes of the Presentation and filtering out the movies works:
//While iterating through all slides i:
objShapes = objPres.Slides[i].Shapes;
foreach (Microsoft.Office.Interop.PowerPoint.Shape s in objShapes) {
if(s.Name.Contains(".wmv")){
s.AnimationSettings.PlaySettings.PlayOnEntry = MsoTriState.msoTrue;
}
}