I am attempting to implement the solutions given here and/or here.
I have a .pptx file that contains zero slides initially. One of the layouts is named "One content". For now, I just want to produce a new PPTX file with a single slide based on this layout. Should be trivial, no? No, apparently not.
In file OpenXmlUtils.cs I have the following method which I use to create a new PPTX from the "template" file:
public static void CopyTemplate(string template, string target)
{
string targetPath = Path.GetFullPath(target);
string targetFolder = Path.GetDirectoryName(targetPath);
if (!System.IO.Directory.Exists(targetFolder))
{
System.IO.Directory.CreateDirectory(targetFolder);
}
System.IO.File.Copy(template, targetPath, true);
}
My PPTWriter.cs broken down to MCVE:
public PPTOpenXMLWriter(string templatePath, string presSaveAsPath)
{
if (File.Exists(presSaveAsPath)) { File.Delete(presSaveAsPath); }
OpenXmlUtils.CopyTemplate(templatePath, presSaveAsPath);
_createPresentation(presSaveAsPath);
}
private void _createPresentation(string presSaveAsPath)
{
using (PresentationDocument presentationDocument = PresentationDocument.Open(presSaveAsPath, true))
{
string layoutName = "One content";
_insertNewSlide(presentationDocument.PresentationPart, layoutName);
presentationDocument.Save();
}
}
private void _insertNewSlide(PresentationPart presentationPart, string layoutName)
{
Slide slide = new Slide(new CommonSlideData(new ShapeTree()));
SlidePart slidePart = presentationPart.AddNewPart<SlidePart>();
slide.Save(slidePart);
SlideMasterPart slideMasterPart = presentationPart.SlideMasterParts.FirstOrDefault();
SlideLayoutPart slideLayoutPart = slideMasterPart.SlideLayoutParts.SingleOrDefault
(sl => sl.SlideLayout.CommonSlideData.Name.Value.Equals(layoutName, StringComparison.OrdinalIgnoreCase));
slidePart.AddPart<SlideLayoutPart>(slideLayoutPart);
slidePart.Slide.CommonSlideData = (CommonSlideData)slideLayoutPart.SlideLayout.CommonSlideData.Clone();
SlideIdList slideIdList = null;
if ( presentationPart.Presentation.SlideIdList is null)
{
presentationPart.Presentation.SlideIdList = new SlideIdList();
}
slideIdList = presentationPart.Presentation.SlideIdList;
// find the highest id
uint maxSlideId = 0;
if (slideIdList.ChildElements.Count() > 0)
maxSlideId = slideIdList.ChildElements
.Cast<SlideId>()
.Max(x => x.Id.Value);
// Insert the new slide into the slide list after the previous slide.
SlideId newSlideId = new SlideId();
slideIdList.Append(newSlideId);
newSlideId.Id = maxSlideId;
newSlideId.RelationshipId = presentationPart.GetIdOfPart(slidePart);
// Save the modified presentation.
presentationPart.Presentation.Save();
}
The resulting file is corrupt and needs to be "repaired" by PowerPoint, after which repair process the slide layout is not the layout that was specified. In fact it's a completely different layout with a radically different XML structure and all I can gather is that it's somehow defaulting back to the ordinally first layout in the master ("Title"), because it doesn't know how to handle whatever it's actually been given via OpenXML.
This seems like it ought to be a fairly common use-case, and perhaps my expectations are wrong, but it seems like given an already existing slide layout, you ought to be able to (relatively easily) create a new slide based on that layout which will contain all of the same placeholder shapes, etc.