Copying a slide from one presentation to another using Open XML SDK - c#

Sorry my English :)
There is a code
using (var sourceDoc = PresentationDocument.Open(#"d:\source.pptx", false))
{
using (var destDoc = PresentationDocument.Open(#"d:\dest.pptx", true))
{
}
}
I try copy slide №2 from sourceDoc and paste into destDoc position 4.
There are articles with same title "Copying A Slide From One Presentation To Another" and "How to Assemble Multiple PowerPoint Decks", but unfortunately I cannot apply that. I just confused. For example:
uniqueId = GetMaxIdFromChild(destPresPart.Presentation.SlideMasterIdList);
what does mean? Compile time say error.
UPDATE #1
I created method to copy slide to another presentation
/// <summary>
/// Copy one slide to another presentation
/// </summary>
/// <param name="sourcePresentationPath"></param>
/// <param name="slidePosition">
/// Slide number from source presentation which will be copy to destinition presentation
/// </param>
/// <param name="destPresentationPath"></param>
/// <remarks>Slide copy to end destinition presentation</remarks>
public static void CopySlideTo(string sourcePresentationPath, int slidePosition, string destPresentationPath)
{
using (PresentationDocument sourcePresentationDocument = PresentationDocument.Open(sourcePresentationPath, false))
{
var sourcePresentationPart = sourcePresentationDocument.PresentationPart;
var sourcePresentation = sourcePresentationPart.Presentation;
SlideIdList sourceSlideIdList = sourcePresentation.SlideIdList;
SlideId slideIdSelectedSlide = sourceSlideIdList.ChildElements[slidePosition - 1] as SlideId;
SlidePart sourceSlidePart = sourcePresentationPart.GetPartById(slideIdSelectedSlide.RelationshipId) as SlidePart;
SlidePart destSlidePart = null;
SlideIdList destSlideIdList = null;
PresentationPart destPresentationPart = null;
using (PresentationDocument destPresentationDocument = PresentationDocument.Open(destPresentationPath, true))
{
var addedSlidePart = destPresentationDocument.PresentationPart.AddPart(sourceSlidePart);
destSlideIdList = destPresentationDocument.PresentationPart.Presentation.SlideIdList;
destPresentationPart = destPresentationDocument.PresentationPart;
SlideId lastSlideIdInDestPresentation = destSlideIdList.ChildElements.Last() as SlideId;
// Insert the new slide into the slide list after last slide
SlideId addedSlideId = destSlideIdList.InsertAfter(new SlideId(), lastSlideIdInDestPresentation);
addedSlideId.Id = lastSlideIdInDestPresentation.Id++;
addedSlideId.RelationshipId = destPresentationPart.GetIdOfPart(addedSlidePart);
destPresentationPart.Presentation.Save();
}
}
Slide is copied, but without background. How also move background?
UPDATE #2
I finally created method to copy a slide to another presentation
public class Extensions
{
static uint uniqueId;
/// <summary>
/// Copy one slide to another presentation
/// </summary>
/// <param name="sourcePresentationPath"></param>
/// <param name="slidePosition">
/// Slide number from source presentation which will be copy to destinition presentation
/// </param>
/// <param name="destPresentationPath"></param>
/// <remarks>Slide is copied to end destinition presentation</remarks>
[SuppressMessage("ReSharper", "SuggestVarOrType_SimpleTypes")]
public static void CopySlideTo(string sourcePresentationPath, int slidePosition, string destPresentationPath)
{
using (PresentationDocument sourcePresentationDocument = PresentationDocument.Open(sourcePresentationPath, false))
{
var sourcePresentationPart = sourcePresentationDocument.PresentationPart;
var sourcePresentation = sourcePresentationPart.Presentation;
SlideIdList sourceSlideIdList = sourcePresentation.SlideIdList;
SlideId slideIdSelectedSlide = sourceSlideIdList.ChildElements[slidePosition - 1] as SlideId;
SlidePart sourceSlidePart = sourcePresentationPart.GetPartById(slideIdSelectedSlide.RelationshipId) as SlidePart;
using (PresentationDocument destPresentationDocument = PresentationDocument.Open(destPresentationPath, true))
{
var addedSlidePart = destPresentationDocument.PresentationPart.AddPart(sourceSlidePart);
var destSlideIdList = destPresentationDocument.PresentationPart.Presentation.SlideIdList;
var destPresentationPart = destPresentationDocument.PresentationPart;
SlideId lastSlideIdInDestPresentation = destSlideIdList.ChildElements.Last() as SlideId;
// Insert the new slide into the slide list after last slide
SlideId addedSlideId = destSlideIdList.InsertAfter(new SlideId(), lastSlideIdInDestPresentation);
addedSlideId.Id = lastSlideIdInDestPresentation.Id++;
addedSlideId.RelationshipId = destPresentationPart.GetIdOfPart(addedSlidePart);
// Get the existing slide master part.
SlideMasterPart destPresPartSlideMasterPart = destPresentationPart.SlideMasterParts.ElementAt(0);
string relationshipId = destPresentationPart.GetIdOfPart(destPresPartSlideMasterPart);
// Get the new slide master part.
SlideMasterPart sourcePresPartSlideMasterPart = sourcePresentationPart.SlideMasterParts.ElementAt(0);
// Remove the existing theme part.
destPresentationPart.DeletePart(destPresentationPart.ThemePart);
// Remove the old slide master part.
destPresentationPart.DeletePart(destPresPartSlideMasterPart);
// Import the new slide master part, and reuse the old relationship ID.
sourcePresPartSlideMasterPart = destPresentationPart.AddPart(sourcePresPartSlideMasterPart, relationshipId);
// Change to the new theme part.
destPresentationPart.AddPart(sourcePresPartSlideMasterPart.ThemePart);
var newSlideLayouts = new Dictionary<string, SlideLayoutPart>();
foreach (var slideLayoutPart in sourcePresPartSlideMasterPart.SlideLayoutParts)
{
newSlideLayouts.Add(GetSlideLayoutType(slideLayoutPart), slideLayoutPart);
}
// Remove the slide layout relationship on all slides.
foreach (var slidePart in destPresentationPart.SlideParts)
{
string layoutType = null;
if (slidePart.SlideLayoutPart != null)
{
// Determine the slide layout type for each slide.
layoutType = GetSlideLayoutType(slidePart.SlideLayoutPart);
// Delete the old layout part.
slidePart.DeletePart(slidePart.SlideLayoutPart);
}
SlideLayoutPart newLayoutPart = null;
if (layoutType != null && newSlideLayouts.TryGetValue(layoutType, out newLayoutPart))
{
// Apply the new layout part.
slidePart.AddPart(newLayoutPart);
}
}
FixSlideLayoutIds(destPresentationPart);
destPresentationPart.Presentation.Save();
}
}
}
static void FixSlideLayoutIds(PresentationPart presPart)
{
// Make sure that all slide layouts have unique ids.
foreach (SlideMasterPart slideMasterPart in presPart.SlideMasterParts)
{
foreach (SlideLayoutId slideLayoutId in slideMasterPart.SlideMaster.SlideLayoutIdList)
{
uniqueId++;
slideLayoutId.Id = (uint)uniqueId;
}
slideMasterPart.SlideMaster.Save();
}
}
/// <summary>
/// Get the slide layout type.
/// </summary>
/// <param name="slideLayoutPart"></param>
/// <returns></returns>
private static string GetSlideLayoutType(SlideLayoutPart slideLayoutPart)
{
CommonSlideData slideData = slideLayoutPart.SlideLayout.CommonSlideData;
return slideData.Name;
}
}
// Client code
Extensions.CopySlideTo(#"D:\temp\source.pptx", 1, #"D:\temp\dest.pptx");
Now when I open result (d:\temp\dest.pptx) see
However, if click Repair result is ok.
Why this is happend? How fix it? I suspect that something wrong with slide layout adding.

Luckily, I still pulled out the win:
public static void Copy(Stream sourcePresentationStream, uint copiedSlidePosition, Stream destPresentationStream)
{
using (var destDoc = PresentationDocument.Open(destPresentationStream, true))
{
var sourceDoc = PresentationDocument.Open(sourcePresentationStream, false);
var destPresentationPart = destDoc.PresentationPart;
var destPresentation = destPresentationPart.Presentation;
_uniqueId = GetMaxIdFromChild(destPresentation.SlideMasterIdList);
uint maxId = GetMaxIdFromChild(destPresentation.SlideIdList);
var sourcePresentationPart = sourceDoc.PresentationPart;
var sourcePresentation = sourcePresentationPart.Presentation;
int copiedSlideIndex = (int)--copiedSlidePosition;
int countSlidesInSourcePresentation = sourcePresentation.SlideIdList.Count();
if (copiedSlideIndex < 0 || copiedSlideIndex >= countSlidesInSourcePresentation)
throw new ArgumentOutOfRangeException(nameof(copiedSlidePosition));
SlideId copiedSlideId = sourcePresentationPart.Presentation.SlideIdList.ChildElements[copiedSlideIndex] as SlideId;
SlidePart copiedSlidePart = sourcePresentationPart.GetPartById(copiedSlideId.RelationshipId) as SlidePart;
SlidePart addedSlidePart = destPresentationPart.AddPart<SlidePart>(copiedSlidePart);
SlideMasterPart addedSlideMasterPart = destPresentationPart.AddPart(addedSlidePart.SlideLayoutPart.SlideMasterPart);
// Create new slide ID
maxId++;
SlideId slideId = new SlideId
{
Id = maxId,
RelationshipId = destDoc.PresentationPart.GetIdOfPart(addedSlidePart)
};
destPresentation.SlideIdList.Append(slideId);
// Create new master slide ID
_uniqueId++;
SlideMasterId slideMaterId = new SlideMasterId
{
Id = _uniqueId,
RelationshipId = destDoc.PresentationPart.GetIdOfPart(addedSlideMasterPart)
};
destDoc.PresentationPart.Presentation.SlideMasterIdList.Append(slideMaterId);
// change slide layout ID
FixSlideLayoutIds(destDoc.PresentationPart);
destDoc.PresentationPart.Presentation.Save();
}
sourcePresentationStream.Close();
destPresentationStream.Close();
}

You didn't declare the variable type. This is what you meant to do right?
var uniqueId = GetMaxIdFromChild(destPresPart.Presentation.SlideMasterIdList);

Related

Read All Value From Cache

i am using .netcore with Microsoft.Extensions.Caching.Distributed , i have a scenario to get all the keys and also i need to flush all the values.
I have searched many articles no one gives the exact idea to get all values or Flush values. IDistributedCache don't have flush the redis cache.
Can anyone help on this.
For my project ,the follow function return all matched keys:
//using StackExchange.Redis;
/// <summary>
/// 搜索所有与<see cref="keyStr"/>相匹配的缓存的key
/// </summary>
/// <param name="keyStr">搜索词,*表示任意字符</param>
/// <param name="dbIndex">redis中查找db</param>
/// <param name="trimRedisInstanceName">是否在查询前及返回前去除<see cref="Extension.RedisInstanceName"/>前缀</param>
/// <returns>redis中的key列表,<see cref="trimRedisInstanceName"/>参数设置是否包括<see cref="Extension.RedisInstanceName"/></returns>
protected async Task<List<string>> FilterByKey(string keyStr, int dbIndex = 0, bool trimRedisInstanceName = true)
{
//创建连接
var conn = await ConnectionMultiplexer.ConnectAsync(_configuration.GetSection("Cache")["Redis"]);
//获取db
var db = conn.GetDatabase(dbIndex);
var listResult = new List<string>();
//遍历集群内服务器
foreach (var endPoint in conn.GetEndPoints())
{
//获取指定服务器
var server = conn.GetServer(endPoint);
//在指定服务器上使用 keys 或者 scan 命令来遍历key
foreach (var key in server.Keys(dbIndex, Extension.RedisInstanceName + keyStr))
{
if (trimRedisInstanceName)
{
listResult.Add(key.ToString().Replace(Extension.RedisInstanceName, ""));
}
else
{
listResult.Add(key);
}
//获取key对于的值
//var val = db.StringGet(key);
Console.WriteLine($"key: {key}, value:");
}
}
return listResult;
}
In the method,Extension.RedisInstanceName used in
services.AddStackExchangeRedisCache(options =>
{
options.Configuration = config.GetSection("Cache")["Redis"];
options.InstanceName = RedisInstanceName;
});
it used by this:
//刷新缓存
var allCacheCompany = await FilterByKey("xxxx.*");
foreach (var companyKey in allCacheCompany)
{
await _cache.RemoveAsync(companyKey);
}

C# OpenXML SDK - Inserting a new slide from slide masters

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.
Got it. The following is working for my test scenarios (thanks to your code for help):
presentationPart.InsertNewSlide("CV Full page");
presentationPart.InsertNewSlide("CV Half page");
presentationPart.InsertNewSlide("Credential full page");
presentationPart.InsertNewSlide("CV or Credential 5 to a page", 3);
public static void InsertNewSlide(this PresentationPart presentationPart, string layoutName, int? position = null)
{
Slide slide = new Slide();
SlidePart slidePart = presentationPart.AddNewPart<SlidePart>();
slide.Save(slidePart);
SlideMasterPart slideMasterPart = presentationPart.SlideMasterParts.FirstOrDefault();
SlideLayoutPart slideLayoutPart = slideMasterPart.GetSlideLayoutPartByLayoutName(layoutName);
slidePart.AddPart(slideLayoutPart, slideMasterPart.GetIdOfPart(slideLayoutPart));
slidePart.Slide.CommonSlideData = (CommonSlideData)slideLayoutPart.SlideLayout.CommonSlideData.Clone();
string id = slideMasterPart.GetIdOfPart(slideLayoutPart);
slidePart.CloneSlideLayout(slideLayoutPart, id);
slideMasterPart.AddPart(slideLayoutPart, id);
presentationPart.SetSlideID(slidePart, position);
}
public static void SetSlideID(this PresentationPart presentationPart, SlidePart slidePart, int? position = null)
{
SlideIdList slideIdList = presentationPart.Presentation.SlideIdList;
if (slideIdList == null)
{
slideIdList = new SlideIdList();
presentationPart.Presentation.SlideIdList = slideIdList;
}
if (position != null && position > slideIdList.Count())
throw new InvalidOperationException($"Unable to set slide to position '{position}'. There are only '{slideIdList.Count()}' slides.");
uint newId = slideIdList.ChildElements.Count() == 0 ? 256 : slideIdList.GetMaxSlideId() + 1;
if (position == null)
{
var newSlideId = slideIdList.AppendChild(new SlideId());
newSlideId.Id = newId;
newSlideId.RelationshipId = presentationPart.GetIdOfPart(slidePart);
}
else
{
SlideId nextSlideId = (SlideId)slideIdList.ChildElements[position.Value - 1];
var newSlideId = slideIdList.InsertBefore(new SlideId(), nextSlideId);
newSlideId.Id = newId;
newSlideId.RelationshipId = presentationPart.GetIdOfPart(slidePart);
}
}
public static uint GetMaxSlideId(this SlideIdList slideIdList)
{
uint maxSlideId = 0;
if (slideIdList.ChildElements.Count() > 0)
maxSlideId = slideIdList.ChildElements
.Cast<SlideId>()
.Max(x => x.Id.Value);
return maxSlideId;
}
public static SlideLayoutPart GetSlideLayoutPartByLayoutName(this SlideMasterPart slideMasterPart, string layoutName)
{
return slideMasterPart.SlideLayoutParts.SingleOrDefault
(sl => sl.SlideLayout.CommonSlideData.Name.Value.Equals(layoutName, StringComparison.OrdinalIgnoreCase));
}
public static void CloneSlideLayout(this SlidePart newSlidePart, SlideLayoutPart slPart, string id)
{
/* ensure we added the rel ID to this part */
newSlidePart.AddPart(slPart, id);
using (Stream stream = slPart.GetStream()) { newSlidePart.SlideLayoutPart.FeedData(stream); }
newSlidePart.Slide.CommonSlideData = (CommonSlideData)slPart.SlideLayout.CommonSlideData.Clone();
foreach (ImagePart iPart in slPart.ImageParts)
newSlidePart.AddPart(iPart, slPart.GetIdOfPart(iPart));
}
I noticed some discrepancies in the slide's .rels, from the correct, manually produced slide:
<?xml version="1.0" encoding="UTF-8" standalone="true"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Target="../slideLayouts/slideLayout8.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout" Id="rId1"/>
</Relationships>
And the incorrect one looked like:
<?xml version="1.0" encoding="UTF-8"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="R522c7c9989a04964" Target="/ppt/slideLayouts/slideLayout8.xml" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout"/>
<Relationship Id="rId5" Target="/ppt/media/image2.bin" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image"/>
</Relationships>
Two discrepancies, which I believe are as follows:
The image2.bin I believe I traced this back to a 1x1 pixel autoshape "object" that was present on several of the slide masters. I manually removed that from each slide master where it existed, and resaved my template pptx file.
The slide is missing the rel ID back to the slide layout, seems easy enough. I added some extension methods to the OpenXmlUtils class, and modified the _insertNewSlide method as follows:
private void _insertNewSlide(PresentationPart presentationPart, string layoutName)
{
Slide slide = new Slide();
SlidePart slidePart = presentationPart.AddNewPart<SlidePart>();
slide.Save(slidePart);
SlideMasterPart slideMasterPart = presentationPart.SlideMasterParts.FirstOrDefault();
SlideLayoutPart slideLayoutPart = slideMasterPart.GetSlideLayoutPartByLayoutName(layoutName); // extension method
/* ensure we added the rel ID to this part */
slidePart.AddPart<SlideLayoutPart>(slideLayoutPart, slideMasterPart.GetIdOfPart(slideLayoutPart));
slidePart.Slide.CommonSlideData = (CommonSlideData)slideLayoutPart.SlideLayout.CommonSlideData.Clone();
slidePart.CloneSlideLayout(slideLayoutPart); // extension method
presentationPart.AppendSlide(slidePart); // extension method
}
I've added the following extension methods in OpenXmlUtils.cs:
public static void CloneSlideLayout(this SlidePart newSlidePart, SlideLayoutPart slPart, string id)
{
// creates a Slide from a SlideLayout
/* ensure we added the rel ID to this part */
newSlidePart.AddPart(slPart, id);
using (Stream stream = slPart.GetStream()) { newSlidePart.SlideLayoutPart.FeedData(stream); }
newSlidePart.Slide.CommonSlideData = (CommonSlideData)slPart.SlideLayout.CommonSlideData.Clone();
foreach (ImagePart iPart in slPart.ImageParts)
{
newSlidePart.AddPart<ImagePart>(iPart, slPart.GetIdOfPart(iPart));
}
}
public static uint GetNextSlideId(this SlideIdList slideIdList)
{
uint nextId;
uint maxId = GetMaxSlideId(slideIdList);
if (maxId == 0)
{
// Slide Id must be >= 256
nextId = 256;
}
else
{
nextId = maxId++;
}
return nextId;
}
public static uint GetMaxSlideId(this SlideIdList slideIdList)
{
// find the highest id
uint maxSlideId = 0;
if (slideIdList.ChildElements.Count() > 0)
maxSlideId = slideIdList.ChildElements
.Cast<SlideId>()
.Max(x => x.Id.Value);
return maxSlideId;
}
public static SlideLayoutPart GetSlideLayoutPartByLayoutName(this SlideMasterPart slideMasterPart, string layoutName)
{
return slideMasterPart.SlideLayoutParts.SingleOrDefault
(sl => sl.SlideLayout.CommonSlideData.Name.Value.Equals(layoutName, StringComparison.OrdinalIgnoreCase));
}
public static void AppendSlide(this PresentationPart presentationPart, SlidePart newSlidePart)
{
SlideMasterPart slideMasterPart = presentationPart.SlideMasterParts.FirstOrDefault();
SlideLayoutPart slideLayoutPart = slideMasterPart.GetSlideLayoutPartByLayoutName(layoutName);
Slide slide = new Slide( );
SlidePart slidePart = presentationPart.AddNewPart<SlidePart>();
slide.Save(slidePart);
string id = slideMasterPart.GetIdOfPart(slideLayoutPart);
slidePart.CloneSlideLayout(slideLayoutPart, id);
presentationPart.AppendSlide(slidePart);
}
Having implemented these changes, I can successfully produce the "One content" slide from the master, and it looks like most of the other layouts are output correctly as well, but if I try to create an instance of each slide layout, there is still a "repair" issue which I'll need to isolate.
Update:

create a copy of template presentation ".potx" to a new ".pptx" using openXML

I have a powerpoint template with .potx extension, i need to create a copy of the template file, but the created file does not have any content present in it, it is a blank presentation, below is the function i am using to create a destination file using template file.
/// <summary>
/// Creates a copy of the source template file
/// </summary>
/// <param name="sourceFile">sourcce file which has to be copied</param>
/// <param name="destinationFile">destination file where the new file has to be placed.</param>
public static void CreateFileFromTemplate(this string sourceFile, string destinationFile)
{
try
{
//File.Copy(sourceFile, destinationFile, true);
if (System.IO.Path.GetExtension(sourceFile) == ".potx")
{
if (System.IO.Path.GetExtension(destinationFile) == ".potx")
{
PresentationDocument destinationDoc = PresentationDocument.Create(destinationFile, PresentationDocumentType.Presentation);
PresentationPart presentationPart = destinationDoc.AddPresentationPart();
presentationPart.Presentation = new Presentation();
presentationPart.Presentation.Save();
destinationDoc.Close();
//Package destinationPackage = Package.Open(destinationFile, FileMode.Open, FileAccess.ReadWrite);
using (destinationDoc = PresentationDocument.Open(destinationFile, true))
{
Package templatePackage = Package.Open(sourceFile, FileMode.Open);
using (PresentationDocument templateDocument = PresentationDocument.Open(templatePackage))
{
PresentationPart sourcePresPart = templateDocument.PresentationPart;
PresentationPart destinationPresrPart = destinationDoc.PresentationPart;
//var templateSlideCount = templateDocument.CountSlides();
//for (int slides = 1; slides <= templateSlideCount; slides++)
//{
//}
SlidePart sourceSlidePart;
SlidePart destinationSlidePart;
foreach (SlideId slideId in sourcePresPart.Presentation.SlideIdList)
{
//int i=0;
sourceSlidePart = (SlidePart)sourcePresPart.GetPartById(slideId.RelationshipId);
destinationSlidePart = sourceSlidePart.Clone();
SlideIdList slideIdList = destinationPresrPart.Presentation.SlideIdList;
destinationPresrPart.AddPart(destinationSlidePart);
// Save the modified presentation.
destinationPresrPart.Presentation.Save();
}
}
}
}
else
{
throw new FileFormatException("Invalid destination file format, Valid file should have .pptx as extension");
}
}
else
{
throw new FileFormatException("Invalid file format, Valid file should have .potx as extension");
}
}
catch (Exception ex)
{
throw;
}
}
Extension method used to clone a slide
/// <summary>
/// Clones the specified slide.
/// </summary>
/// <param name="sourceSlide">The slide to clone.</param>
/// <returns>Cloned slide.</returns>
public static SlidePart Clone(this SlidePart sourceSlide)
{
// find the presentationPart: makes the API more fluent
var presentationPart = sourceSlide.GetParentParts()
.OfType<PresentationPart>()
.Single();
// clone slide contents
Slide currentSlide = (Slide)sourceSlide.Slide.CloneNode(true);
var slidePartClone = presentationPart.AddNewPart<SlidePart>();
currentSlide.Save(slidePartClone);
// copy layout part
slidePartClone.AddPart(sourceSlide.SlideLayoutPart);
//copy all embedded elements
foreach (ChartPart part in sourceSlide.ChartParts)
{
ChartPart newpart = slidePartClone.AddNewPart<ChartPart>(part.ContentType, sourceSlide.GetIdOfPart(part));
newpart.FeedData(part.GetStream());
newpart.AddNewPart<EmbeddedPackagePart>(part.EmbeddedPackagePart.ContentType, part.GetIdOfPart(part.EmbeddedPackagePart));
newpart.EmbeddedPackagePart.FeedData(part.EmbeddedPackagePart.GetStream());
}
foreach (EmbeddedObjectPart part in sourceSlide.EmbeddedObjectParts)
{
EmbeddedObjectPart newpart = slidePartClone.AddNewPart<EmbeddedObjectPart>(part.ContentType, sourceSlide.GetIdOfPart(part));
newpart.FeedData(part.GetStream());
}
foreach (EmbeddedPackagePart part in sourceSlide.EmbeddedPackageParts)
{
EmbeddedPackagePart newpart = slidePartClone.AddNewPart<EmbeddedPackagePart>(part.ContentType, sourceSlide.GetIdOfPart(part));
newpart.FeedData(part.GetStream());
}
foreach (ImagePart part in sourceSlide.ImageParts)
{
ImagePart newpart = slidePartClone.AddNewPart<ImagePart>(part.ContentType, sourceSlide.GetIdOfPart(part));
newpart.FeedData(part.GetStream());
}
foreach (VmlDrawingPart part in sourceSlide.VmlDrawingParts)
{
VmlDrawingPart newpart = slidePartClone.AddNewPart<VmlDrawingPart>(part.ContentType, sourceSlide.GetIdOfPart(part));
newpart.FeedData(part.GetStream());
}
foreach (UserDefinedTagsPart part in sourceSlide.UserDefinedTagsParts)
{
UserDefinedTagsPart newpart = slidePartClone.AddNewPart<UserDefinedTagsPart>(part.ContentType, sourceSlide.GetIdOfPart(part));
newpart.FeedData(part.GetStream());
}
return slidePartClone;
}
in my main method i have below code which calls these functions to create the ppt
string sourceFilePath =#"C:\TempFolder\template.potx";
string destinationFilePath = #"C:\TempFolder\ClonedFile.potx";
sourceFilePath.CreateFileFromTemplate(destinationFilePath);
but the final created ppt will not have any slides in it,
any help would be appreciated
I posted a similar answer before on saving an excel template to an actual excel document here and you should be able to apply the same logic with a power point template. Instead of the SpreadsheetDocument you will want to use the PresentationDocument and call ChangeDocumentType on it. Something along these lines:
byte[] byteArray = File.ReadAllBytes("C:\\temp\\sa123.potx");
using (MemoryStream stream = new MemoryStream())
{
stream.Write(byteArray, 0, (int)byteArray.Length);
using (PresentationDocument presentationDoc = PresentationDocument.Open(stream, true))
{
// Change from template type to presentation type
presentationDoc.ChangeDocumentType(PresentationDocumentType.Presentation);
}
File.WriteAllBytes("C:\\temp\\sa123.pptx", stream.ToArray());
}
You can then reopen that saved file and do whatever extra stuff you want to it without touching the template file.

How to Create CycleTile Dynamically in Windows Phone 8

I would like to use the new Cycle Tile feature available in Windows Phone 8, although I am having trouble creating this dynamically. My issue arises when attempting to set the images for the Cycle Tile, I do not seem to be getting the correct paths for the images. What I have so far is as follows
MainPage.xaml.cs
private void CreateApplicationTile()
{
//App.PictureList.Pictures grabs all of my application images from IsolatedStorage
int count = App.PictureList.Pictures.Count();
int count1 = count - 9;
var cycleImages = new List<Uri>();
if (count > 0)
{
//I only want to add the 9 most recent images if available
for (int i = count; i > count1; i--)
{
int index = i - 1;
if (index > -1)
{
//Set file to type CapturedPicture, which contains the jpg, name, date, etc.
var file = App.PictureList.Pictures[index] as CapturedPicture;
//TilePictureRepository class saves the file (picture) to "Shared/ShellContent/"
//TilePictureRepository.IsolatedStoragePath = "Shared/ShellContent/"
TilePictureRepository.Instance.SaveToLocalStorage(file, TilePictureRepository.IsolatedStoragePath);
}
}
}
// Select the application tile
ShellTile myTile = ShellTile.ActiveTiles.First();
if (myTile != null)
{
Uri[] u = new Uri[9];
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
IEnumerable<string> files = isf.GetFileNames(TilePictureRepository.IsolatedStoragePath + "*").Reverse();
int x = 0;
foreach (string file in files)
{
if (x < 9)
{
u[x] = new Uri("isostore:/Shared/ShellContent/" + file, UriKind.Absolute);
cycleImages.Add(u[x]);
x++;
}
}
}
CycleTileData newTileData = new CycleTileData
{
Title = "Tile Test",
SmallBackgroundImage = new Uri("/Assets/Tiles/Tile_Small_159x159.png", UriKind.Relative),
CycleImages = cycleImages,
};
myTile.Update(newTileData);
}
}
From what I understand, tile images for the Cycle Tile should be saved in the ShellTile directory within IsolatedStorage. To do this, I am using the following class which I quickly modified (I really only am using the SaveToLocalStorage method)
TilePictureRepository.cs
#region Constants
public const string IsolatedStoragePath = "Shared/ShellContent/";
#endregion
#region Fields
private readonly ObservableCollection<Picture> _pictures = new ObservableCollection<Picture>();
#endregion
#region Properties
public ObservableCollection<Picture> Pictures
{
get { return _pictures; }
}
#endregion
#region Singleton Pattern
private TilePictureRepository()
{
LoadAllPicturesFromIsolatedStorage();
}
public static readonly TilePictureRepository Instance = new TilePictureRepository();
#endregion
/// <summary>
/// Saves to local storage
/// This method gets two parameters: the captured picture instance and the name of the pictures folder in the isolated storage
/// </summary>
/// <param name="capturedPicture"></param>
/// <param name="directory"></param>
public void SaveToLocalStorage(CapturedPicture capturedPicture, string directory)
{
//call IsolatedStorageFile.GetUserStoreForApplication to get an isolated storage file
var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
//Call the IsolatedStorageFile.EnsureDirectory extension method located in the Common IsolatedStorageFileExtensions class to confirm that the pictures folder exists.
isoFile.EnsureDirectory(directory);
//Combine the pictures folder and captured picture file name and use this path to create a new file
string filePath = Path.Combine(directory, capturedPicture.FileName);
using (var fileStream = isoFile.CreateFile(filePath))
{
using (var writer = new BinaryWriter(fileStream))
{
capturedPicture.Serialize(writer);
}
}
}
/// <summary>
/// To load all saved pictures and add them to the pictures list page
/// </summary>
public CapturedPicture LoadFromLocalStorage(string fileName, string directory)
{
//To open the file, add a call to the IsolatedStorageFile.GetUserStoreForApplication
var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
//Combine the directory and file name
string filePath = Path.Combine(directory, fileName);
//use the path to open the picture file from the isolated storage by using the IsolatedStorageFile.OpenFile method
using (var fileStream = isoFile.OpenFile(filePath, FileMode.Open, FileAccess.Read))
{
//create a BinaryReader instance for deserializing the CapturedPicture instance
using (var reader = new BinaryReader(fileStream))
{
var capturedPicture = new CapturedPicture();
//create a new instance of the type CapturedPicture called CapturedPicture.Deserialize to deserialize the captured picture and return it
capturedPicture.Deserialize(reader);
return capturedPicture;
}
}
}
/// <summary>
/// To load all the pictures at start time
/// </summary>
private void LoadAllPicturesFromIsolatedStorage()
{
//add call to the IsolatedStorageFile.GetUserStoreForApplication to open an isolated storage file
var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
//Call the IsolatedStorageFile.EnsureDirectory extension method located in the Common IsolatedStorageFileExtensions class to confirm that the pictures folder exists
isoFile.EnsureDirectory(IsolatedStoragePath);
//Call the IsolatedStorageFile.GetFileNames using the pictures directory and *.jpg as a filter to get all saved pictures
var pictureFiles = isoFile.GetFileNames(Path.Combine(IsolatedStoragePath, "*.jpg"));
//Iterate through all the picture files in the list and load each using the LoadFromLocalStorage you created earlier
foreach (var pictureFile in pictureFiles)
{
var picture = LoadFromLocalStorage(pictureFile, IsolatedStoragePath);
_pictures.Add(picture);
}
}
My result is that as the smallest tile available on the start screen, I see the application tile, while the medium and large tile sizes only show a blank tile (just the accent color and application name).

iterate through and infoPath form

I have a infopath form that may contain multiple attachments: By using a group of repeating elements, the user can click an “click add item” option he will be able to upload more attachments.
In Sharepoint I am using a workflow to extract the attachments and put them in a separate list. So far I manage only to extract the first one and the workflow finishes successfully.
can I put a loop or something to iterate trough the form?
I attach the code below:
public sealed partial class FileCopyFeature : SharePointSequentialWorkflowActivity
{
public FileCopyFeature()
{
InitializeComponent();
}
public Guid workflowId = default(System.Guid);
public Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties workflowProperties = new Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties();
private void CopyFile(object sender, EventArgs e)
{
// Retrieve the file associated with the item
// on which the workflow has been instantiated
SPFile file = workflowProperties.Item.File;
if (file == null)
return;
// Get the binary data of the file
byte[] xmlFormData = null;
xmlFormData = file.OpenBinary();
// Load the data into an XPathDocument object
XPathDocument ipForm = null;
if (xmlFormData != null)
{
using (MemoryStream ms = new MemoryStream(xmlFormData))
{
ipForm = new XPathDocument(ms);
ms.Close();
}
}
if (ipForm == null)
return;
// Create an XPathNavigator object to navigate the XML
XPathNavigator ipFormNav = ipForm.CreateNavigator();
ipFormNav.MoveToFollowing(XPathNodeType.Element);
XmlNamespaceManager nsManager =
new XmlNamespaceManager(new NameTable());
foreach (KeyValuePair<string, string> ns
in ipFormNav.GetNamespacesInScope(XmlNamespaceScope.All))
{
if (ns.Key == String.Empty)
{
nsManager.AddNamespace("def", ns.Value);
}
else
{
nsManager.AddNamespace(ns.Key, ns.Value);
}
}
do
{
XPathNavigator nodeNav = ipFormNav.SelectSingleNode("//my:field2", nsManager);
// Retrieve the value of the attachment in the InfoPath form
//XPathNavigator nodeNav = ipFormNav.SelectSingleNode(
//"//my:field2", nsManager);
string ipFieldValue = string.Empty;
if (nodeNav != null)
{
ipFieldValue = nodeNav.Value;
// Decode the InfoPath file attachment
InfoPathAttachmentDecoder dec =
new InfoPathAttachmentDecoder(ipFieldValue);
string fileName = dec.Filename;
byte[] data = dec.DecodedAttachment;
// Add the file to a document library
using (SPWeb web = workflowProperties.Web)
{
SPFolder docLib = web.Folders["Doc"];
docLib.Files.Add(fileName, data);
docLib.Update();
// workflowProperties.Item.CopyTo(data + "/Doc/" + fileName);
}
}
}
while (ipFormNav.MoveToNext());
}
}
/// <summary>
/// Decodes a file attachment and saves it to a specified path.
/// </summary>
public class InfoPathAttachmentDecoder
{
private const int SP1Header_Size = 20;
private const int FIXED_HEADER = 16;
private int fileSize;
private int attachmentNameLength;
private string attachmentName;
private byte[] decodedAttachment;
/// <summary>
/// Accepts the Base64 encoded string
/// that is the attachment.
/// </summary>
public InfoPathAttachmentDecoder(string theBase64EncodedString)
{
byte[] theData = Convert.FromBase64String(theBase64EncodedString);
using (MemoryStream ms = new MemoryStream(theData))
{
BinaryReader theReader = new BinaryReader(ms);
DecodeAttachment(theReader);
}
}
private void DecodeAttachment(BinaryReader theReader)
{
//Position the reader to get the file size.
byte[] headerData = new byte[FIXED_HEADER];
headerData = theReader.ReadBytes(headerData.Length);
fileSize = (int)theReader.ReadUInt32();
attachmentNameLength = (int)theReader.ReadUInt32() * 2;
byte[] fileNameBytes = theReader.ReadBytes(attachmentNameLength);
//InfoPath uses UTF8 encoding.
Encoding enc = Encoding.Unicode;
attachmentName = enc.GetString(fileNameBytes, 0, attachmentNameLength - 2);
decodedAttachment = theReader.ReadBytes(fileSize);
}
public void SaveAttachment(string saveLocation)
{
string fullFileName = saveLocation;
if (!fullFileName.EndsWith(Path.DirectorySeparatorChar.ToString()))
{
fullFileName += Path.DirectorySeparatorChar;
}
fullFileName += attachmentName;
if (File.Exists(fullFileName))
File.Delete(fullFileName);
FileStream fs = new FileStream(fullFileName, FileMode.CreateNew);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(decodedAttachment);
bw.Close();
fs.Close();
}
public string Filename
{
get { return attachmentName; }
}
public byte[] DecodedAttachment
{
get { return decodedAttachment; }
}
It appears that your issue has to do with your use of MoveToNext. Per the documentation, this function moves to the next sibling, and does not navigate to children elements. Your code appears to go to the first element it finds (presumably my:myFields), looks for the first child named my:field2 (it only pulls the first one since you are using SelectSingleNode, and then goes to the next sibling of my:myFields (not the next sibling of my:field2). One way to fix this might be to replace your current do-while loop with a call to SelectNodes like the following and then iterate over nodeList.
XmlNodeList nodelist = ipFormNav.SelectNodes("//my:field2", nsManager);

Categories

Resources