dotnet OpenXML reads PPT main sequence entry exit emphasis animation

This article tells you how to read the PPT file, put the entry and exit and emphasized animation of the main animation sequence MainSequence, and the storage method in OpenXML

For example, the following courseware content adds an entry and exit animation to an element. There is no relevant impact between the animations. Click to trigger the animation, as shown in the figure below

The general animation content is as follows

  <p:timing>
    <p:tnLst>
      <p:par>
        <p:cTn id="1" dur="indefinite" restart="never" nodeType="tmRoot">
          <p:childTnLst>
            <p:seq concurrent="1" nextAc="seek">
              <p:cTn id="2" dur="indefinite" nodeType="mainSeq">
                <p:childTnLst>
                  <p:par>
                    <p:cTn id="3" fill="hold">
                      <p:stCondLst>
                        <p:cond delay="indefinite" />
                      </p:stCondLst>
                      <p:childTnLst>
                        <p:par>
                          <p:cTn id="4" fill="hold">
                            <p:stCondLst>
                              <p:cond delay="0" />
                            </p:stCondLst>
                            <p:childTnLst>
                              <p:par>
                                <p:cTn id="5" presetID="1" presetClass="entr" presetSubtype="0" fill="hold" grpId="0" nodeType="clickEffect">
                                  <p:stCondLst>
                                    <p:cond delay="0" />
                                  </p:stCondLst>
                                  <p:childTnLst>
                                    <!-- Ignore code-->
                                  </p:childTnLst>
                                </p:cTn>
                              </p:par>
                            </p:childTnLst>
                          </p:cTn>
                        </p:par>
                      </p:childTnLst>
                    </p:cTn>
                  </p:par>
                  <p:par>
                    <p:cTn id="7" fill="hold">
                      <p:stCondLst>
                        <p:cond delay="indefinite" />
                      </p:stCondLst>
                      <p:childTnLst>
                        <p:par>
                          <p:cTn id="8" fill="hold">
                            <p:stCondLst>
                              <p:cond delay="0" />
                            </p:stCondLst>
                            <p:childTnLst>
                              <p:par>
                                <p:cTn id="9" presetID="25" presetClass="emph" presetSubtype="0" fill="hold" grpId="2" nodeType="clickEffect">
                                  <p:stCondLst>
                                    <p:cond delay="0" />
                                  </p:stCondLst>
                                  <p:childTnLst>
                                     <!-- Ignore code-->
                                  </p:childTnLst>
                                </p:cTn>
                              </p:par>
                            </p:childTnLst>
                          </p:cTn>
                        </p:par>
                      </p:childTnLst>
                    </p:cTn>
                  </p:par>
                  <p:par>
                    <p:cTn id="14" fill="hold">
                      <p:stCondLst>
                        <p:cond delay="indefinite" />
                      </p:stCondLst>
                      <p:childTnLst>
                        <p:par>
                          <p:cTn id="15" fill="hold">
                            <p:stCondLst>
                              <p:cond delay="0" />
                            </p:stCondLst>
                            <p:childTnLst>
                              <p:par>
                                <p:cTn id="16" presetID="10" presetClass="exit" presetSubtype="0" fill="hold" grpId="1" nodeType="clickEffect">
                                    <!-- Ignore code-->
                                </p:cTn>
                              </p:par>
                            </p:childTnLst>
                          </p:cTn>
                        </p:par>
                      </p:childTnLst>
                    </p:cTn>
                  </p:par>
                </p:childTnLst>
              </p:cTn>
              <!-- Ignore code-->
            </p:seq>
          </p:childTnLst>
        </p:cTn>
      </p:par>
    </p:tnLst>
    <!-- Ignore code-->
  </p:timing>

According to the PresetClass attribute of the CommonTimeNode type defined by cTn, that is, the OpenXML sdk, you can know that the animation id of 5 is the entry animation, the animation id of 9 is the emphasis animation, and the animation id of 10 is the exit animation

You can see that there are many different animations in the PPT. These animations are not related, that is, they are triggered by clicking after the last playback. The contents of the animation placed in the main sequence are as follows

  <p:timing>
    <p:tnLst>
      <p:par>
        <p:cTn id="1" dur="indefinite" restart="never" nodeType="tmRoot">
          <p:childTnLst>
            <p:seq concurrent="1" nextAc="seek">
              <p:cTn id="2" dur="indefinite" nodeType="mainSeq">
                <p:childTnLst>
                  <p:par>
                    <!-- Enter animation-->
                  </p:par>
                  <p:par>
                    <!-- Emphasize animation-->
                  </p:par>
                  <p:par>
                    <!-- Exit animation-->
                  </p:par>
                </p:childTnLst>
              </p:cTn>
              <!-- Ignore code-->
            </p:seq>
          </p:childTnLst>
        </p:cTn>
      </p:par>
    </p:tnLst>
    <!-- Ignore code-->
  </p:timing>

As mentioned above, you can probably understand the storage method, but in the PPT, there are multiple paralleltimenodes and commontimenodes nested. The actual entry animation obtained from the main animation sequence of mainSeq, that is, MainSequence, can only be obtained through the following path

cTn (mainSeq) -> childTnLst -> par -> cTn (id="3") -> childTnLst -> par -> cTn (id="4") -> childTnLst -> par -> cTn (id="5" presetClass="entr")

You can use the following code to read

        static void Main(string[] args)
        {
            using var presentationDocument =
                DocumentFormat.OpenXml.Packaging.PresentationDocument.Open("Test.pptx", false);
            var presentationPart = presentationDocument.PresentationPart;
            var slidePart = presentationPart!.SlideParts.First();
            var slide = slidePart.Slide;
            var timing = slide.Timing;
            // There is only one item in the first level by default
            var commonTimeNode = timing?.TimeNodeList?.ParallelTimeNode?.CommonTimeNode;

            if (commonTimeNode?.NodeType?.Value == TimeNodeValues.TmingRoot)
            {
                // This is in accordance with the agreement
                // nodeType="tmRoot"
            }

            if (commonTimeNode?.ChildTimeNodeList == null) return;
            // Theoretically, there is only one term, and it must be of type SequenceTimeNode
            var sequenceTimeNode = commonTimeNode.ChildTimeNodeList.GetFirstChild<SequenceTimeNode>();

            var mainSequenceTimeNode = sequenceTimeNode.CommonTimeNode;
            if (mainSequenceTimeNode?.NodeType?.Value == TimeNodeValues.MainSequence)
            {
                // [timeline object (PowerPoint) | Microsoft docs]( https://docs.microsoft.com/zh-cn/office/vba/api/PowerPoint.TimeLine  )
                //  MainSequence main animation sequence
                var mainParallelTimeNode = mainSequenceTimeNode.ChildTimeNodeList;

                foreach (var openXmlElement in mainParallelTimeNode)
                {
                    // Parallel relational
                    if (openXmlElement is ParallelTimeNode parallelTimeNode)
                    {
                        var timeNode = parallelTimeNode.CommonTimeNode.ChildTimeNodeList
                            .GetFirstChild<ParallelTimeNode>().CommonTimeNode.ChildTimeNodeList
                            .GetFirstChild<ParallelTimeNode>().CommonTimeNode;

                        switch (timeNode.PresetClass.Value)
                        {
                            case TimeNodePresetClassValues.Entrance:
                                // Enter animation
                                break;
                            case TimeNodePresetClassValues.Exit:
                                // Exit animation
                                break;
                            case TimeNodePresetClassValues.Emphasis:
                                // Emphasize animation
                                break;
                            case TimeNodePresetClassValues.Path:
                                // Path animation
                                break;
                            case TimeNodePresetClassValues.Verb:
                                break;
                            case TimeNodePresetClassValues.MediaCall:
                                // Play animation
                                break;
                            default:
                                throw new ArgumentOutOfRangeException();
                        }
                    }
                }
            }

            // The document stipulates that there must be an AttributeNameList list, and there must be an AttributeName element. If there are multiple, only the first element will be taken.
            // See "[MS-OI 29500].PDF section 2.1.1137 (g option)"
        }

The code above this article is placed in github and gitee Welcome to visit

You can obtain the source code of this article in the following ways. First create an empty folder, then use the command line cd command to enter this empty folder, and enter the following code on the command line to obtain the code of this article

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 2c06ddf74e45c31ad7842dd06dc09bcc67b6142e

The above uses the source of gitee. If gitee cannot access it, please replace it with the source of github

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

After obtaining the code, enter the PptxDemo folder

The PPT courseware used for the above tests is also placed in this folder

The attribute of this paper is to rely on dotnet OpenXML decompress document to folder tool This tool is an open source and free tool. Welcome to use it

This article will be updated frequently. Please read the original text: https://blog.lindexi.com/post/dotnet-OpenXML-%E8%AF%BB%E5%8F%96-PPT-%E4%B8%BB%E5%BA%8F%E5%88%97%E8%BF%9B%E5%85%A5%E9%80%80%E5%87%BA%E5%BC%BA%E8%B0%83%E5%8A%A8%E7%94%BB.html In order to avoid the misleading of old wrong knowledge and have a better reading experience.

This work adopts Knowledge sharing Attribution - non-commercial use - sharing in the same way 4.0 international license agreement License. Welcome to reprint, use and republish, but be sure to keep the signed Lin Dexi (including link: https://blog.lindexi.com ), shall not be used for commercial purposes, and the works modified based on this article must be distributed under the same license.

Added by adrianuk29 on Sat, 25 Dec 2021 07:29:40 +0200