dotnet / Open-XML-SDK

Open XML SDK by Microsoft

Home Page:https://www.nuget.org/packages/DocumentFormat.OpenXml/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

When opening a pptx file, it prompts that there is a problem with the content and needs to be repaired.

HanJason opened this issue · comments

Describe the bug
When opening a pptx file, it prompts that there is a problem with the content and needs to be repaired.

Screenshots
image

image

To Reproduce

Only insert a slide into the target file.

Steps to reproduce the behavior:
Run the program and open the output file

Observed behavior
PowerPoint found a problem with content in c:..\bin\Debug\net6.0\abc_copy.pptx.
PowerPoint can attempt to repair the presentation.

Expected behavior
A clear and concise description of what you expected to happen.

Desktop (please complete the following information):

  • OS: [Windows]
  • .NET Target: ( .NET Core NET6.0)
  • DocumentFormat.OpenXml Version: (3.0.0)

Additional context

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Presentation;

var pathfile = AppDomain.CurrentDomain.BaseDirectory + "abc.pptx";
var destinationFilePath = AppDomain.CurrentDomain.BaseDirectory + "abc_copy.pptx";

using (PresentationDocument sourcePresentation = PresentationDocument.Open(pathfile, false))
{
    using var mStream = new MemoryStream();
    PresentationDocument destinationPresentation = PresentationDocument.Create(mStream, PresentationDocumentType.Presentation);

    foreach (var part in sourcePresentation.Parts)
    {
        destinationPresentation.AddPart(part.OpenXmlPart, part.RelationshipId);
    }

    PresentationPart? presentationPart = destinationPresentation.PresentationPart;
    if (presentationPart is not null && presentationPart.Presentation is not null)
    {
        Presentation presentation = presentationPart.Presentation;

        if (presentation.SlideIdList is not null)
        {
            //presentation.SlideIdList.AppendChild(new SlideId() { Id = (uint)234533, RelationshipId = "r_rid1" });
            List<string> titlesList = new List<string>();
            var len = presentation.SlideIdList.Elements<SlideId>().Count();
            var slideIdList = presentation.SlideIdList.Elements<SlideId>()?.ToList() ?? new List<SlideId>();
            for (int i = 0; i < len; i++)
            {
                var slideId = slideIdList[i];
                if (slideId.RelationshipId is null)
                {
                    continue;
                }
                SlidePart slidePart = (SlidePart)presentationPart.GetPartById(slideId.RelationshipId!);
                if (slidePart is null)
                {
                    throw new ArgumentNullException("presentationDocument");
                }
                //copy slide part 
                SlidePart newSlidePart = presentationPart.AddNewPart<SlidePart>();
                newSlidePart.FeedData(slidePart.GetStream());
                newSlidePart.AddPart(slidePart.SlideLayoutPart);

                slideId.InsertAfterSelf<SlideId>(new SlideId() { RelationshipId = presentationPart.GetIdOfPart(newSlidePart) });
            }
        }
    }
    destinationPresentation.Save();
    File.WriteAllBytes(destinationFilePath, mStream.ToArray());
}

abc.pptx
abc_copy.pptx

@HanJason If your goal is to insert a new slide or even to copy the existing slide, the presentation.xml part will have to have a unique slide id and relationship id in each of sldIdLst's children sldId elements. The diff between your original and the new presentation show:

Original

  <p:sldIdLst>
    <p:sldId id="260" r:id="rId2"/>
    <p:sldId r:id="R4ce3bde4cfff4c72"/>
  </p:sldIdLst>

New

  <p:sldIdLst>
    <p:sldId id="260" r:id="rId2"/>
    <p:sldId id="256" r:id="rId3"/>
  </p:sldIdLst>

That would be a starting point. Note, the id you use has to be greater than or equal to 256 per ISO 29500-1 19.7.13 ST_SlideId (Slide Identifier).

When I test this by adding the new id (using 256) PowerPoint is happy.