使用OpenXml生成的.pptx文件触发PowerPoint修复提示问题排查
问题现象
使用OpenXML创建仅包含单张带图片的PPT时,每次用PowerPoint打开都会弹出提示:
PowerPoint发现file.pptx中的内容存在问题,PowerPoint可尝试修复演示文稿
点击修复后文件能正常显示图片,但希望彻底消除该提示,且各类OOXML验证工具未检测出错误。
问题代码
private const long EmusPerPoint = 12700L; private const long EmusPerInch = 914400L; private const int Left = 100; private const int Top = 100; private const uint SlideMasterId = 2147483648U; private const uint SlideLayoutId = 2147483649U; private const uint SlideId = 256U; public static void SendToPowerPoint(string imagePath) { try { var outputPath = Path.Combine( Path.GetTempPath(), $"tempPresentation-{DateTime.Now:yyyy-MM-dd-HH-mm-ss}.pptx" ); long cx; long cy; using (var bmp = System.Drawing.Image.FromFile(imagePath)) { cx = (long)(bmp.Width / bmp.HorizontalResolution * EmusPerInch); cy = (long)(bmp.Height / bmp.VerticalResolution * EmusPerInch); } long leftEmu = Left * EmusPerPoint; long topEmu = Top * EmusPerPoint; using (var pptx = PresentationDocument.Create(outputPath, PresentationDocumentType.Presentation)) { var presentationPart = pptx.AddPresentationPart(); presentationPart.Presentation = new Presentation(); var slideMasterPart = presentationPart.AddNewPart<SlideMasterPart>("rIdMaster"); var slideLayoutPart = slideMasterPart.AddNewPart<SlideLayoutPart>("rIdLayout"); slideLayoutPart.AddPart(slideMasterPart, "rIdMaster"); BuildSlideLayout(slideLayoutPart); BuildSlideMaster(slideMasterPart, slideLayoutPart); var slidePart = presentationPart.AddNewPart<SlidePart>("rIdSlide"); // reference the layout using SAME relationship ID slidePart.AddPart(slideLayoutPart, "rIdLayout"); // add image var imagePart = slidePart.AddImagePart(GetImagePartType(imagePath), "rIdImage"); using (var stream = File.OpenRead(imagePath)) imagePart.FeedData(stream); var picture = BuildPicture("rIdImage", leftEmu, topEmu, cx, cy); BuildSlide(slidePart, picture); var pres = presentationPart.Presentation; pres.SlideMasterIdList = new SlideMasterIdList( new SlideMasterId { Id = SlideMasterId, RelationshipId = "rIdMaster" }); pres.SlideIdList = new SlideIdList( new SlideId { Id = SlideId, RelationshipId = "rIdSlide" }); pres.SlideSize = new SlideSize { Cx = 9144000, Cy = 6858000 }; pres.NotesSize = new NotesSize { Cx = 6858000, Cy = 9144000 }; pres.DefaultTextStyle = new DefaultTextStyle(); pres.Save(); } System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo { FileName = outputPath, UseShellExecute = true }); } catch (Exception ex) { MessageBox.Show($"PowerPoint integration failed: {ex.Message}"); } } private static void BuildSlideLayout(SlideLayoutPart part) { part.SlideLayout = new SlideLayout( new CommonSlideData( new ShapeTree( new NonVisualGroupShapeProperties( new NonVisualDrawingProperties { Id = 1, Name = "" }, new NonVisualGroupShapeDrawingProperties(), new ApplicationNonVisualDrawingProperties() ), new GroupShapeProperties(new OpenXmlDrawing.TransformGroup()), new Shape( new NonVisualShapeProperties( new NonVisualDrawingProperties { Id = 2, Name = "Title Placeholder" }, new NonVisualShapeDrawingProperties(new OpenXmlDrawing.ShapeLocks { NoGrouping = true }), new ApplicationNonVisualDrawingProperties(new PlaceholderShape()) ), new ShapeProperties() ) ) ), new ColorMapOverride(new OpenXmlDrawing.MasterColorMapping()) ) { Type = SlideLayoutValues.Blank }; part.SlideLayout.Save(); } private static void BuildSlideMaster(SlideMasterPart masterPart, SlideLayoutPart layoutPart) { masterPart.SlideMaster = new SlideMaster( new CommonSlideData( new ShapeTree( new NonVisualGroupShapeProperties( new NonVisualDrawingProperties { Id = 1, Name = "" }, new NonVisualGroupShapeDrawingProperties(), new ApplicationNonVisualDrawingProperties() ), new GroupShapeProperties(new OpenXmlDrawing.TransformGroup()) ) ), new ColorMap { Background1 = OpenXmlDrawing.ColorSchemeIndexValues.Light1, Text1 = OpenXmlDrawing.ColorSchemeIndexValues.Dark1, Background2 = OpenXmlDrawing.ColorSchemeIndexValues.Light2, Text2 = OpenXmlDrawing.ColorSchemeIndexValues.Dark2, Accent1 = OpenXmlDrawing.ColorSchemeIndexValues.Accent1, Accent2 = OpenXmlDrawing.ColorSchemeIndexValues.Accent2, Accent3 = OpenXmlDrawing.ColorSchemeIndexValues.Accent3, Accent4 = OpenXmlDrawing.ColorSchemeIndexValues.Accent4, Accent5 = OpenXmlDrawing.ColorSchemeIndexValues.Accent5, Accent6 = OpenXmlDrawing.ColorSchemeIndexValues.Accent6, Hyperlink = OpenXmlDrawing.ColorSchemeIndexValues.Hyperlink, FollowedHyperlink = OpenXmlDrawing.ColorSchemeIndexValues.FollowedHyperlink }, new SlideLayoutIdList( new SlideLayoutId { Id = SlideLayoutId, RelationshipId = "rIdLayout" } ) ); masterPart.SlideMaster.Save(); } private static void BuildSlide(SlidePart part, Picture picture) { part.Slide = new Slide( new CommonSlideData( new ShapeTree( new NonVisualGroupShapeProperties( new NonVisualDrawingProperties { Id = 1, Name = "" }, new NonVisualGroupShapeDrawingProperties(), new ApplicationNonVisualDrawingProperties() ), new GroupShapeProperties(new OpenXmlDrawing.TransformGroup()), picture ) ), new ColorMapOverride(new OpenXmlDrawing.MasterColorMapping()) ); part.Slide.Save(); } private static Picture BuildPicture(string relId, long left, long top, long cx, long cy) { return new Picture( new NonVisualPictureProperties( new NonVisualDrawingProperties { Id = 2, Name = "Picture 1" }, new NonVisualPictureDrawingProperties(new OpenXmlDrawing.PictureLocks { NoChangeAspect = true }), new ApplicationNonVisualDrawingProperties() ), new BlipFill( new OpenXmlDrawing.Blip { Embed = relId }, new OpenXmlDrawing.Stretch(new OpenXmlDrawing.FillRectangle()) ), new ShapeProperties( new OpenXmlDrawing.Transform2D( new OpenXmlDrawing.Offset { X = left, Y = top }, new OpenXmlDrawing.Extents { Cx = cx, Cy = cy } ), new OpenXmlDrawing.PresetGeometry(new OpenXmlDrawing.AdjustValueList()) { Preset = OpenXmlDrawing.ShapeTypeValues.Rectangle } ) ); } private static ImagePartType GetImagePartType(string path) { switch (Path.GetExtension(path).ToLowerInvariant()) { case ".jpg": case ".jpeg": return ImagePartType.Jpeg; case ".png": return ImagePartType.Png; case ".gif": return ImagePartType.Gif; case ".bmp": return ImagePartType.Bmp; case ".tif": case ".tiff": return ImagePartType.Tiff; default: return ImagePartType.Png; } }
问题原因及修复方案
1. 移除循环引用
代码中slideLayoutPart.AddPart(slideMasterPart, "rIdMaster");这一行是错误的:SlideLayoutPart是SlideMasterPart的子部件,不需要反过来添加对父部件的引用,这会导致循环关系引用,PowerPoint解析时会判定为损坏内容。直接删除该行代码即可。
2. 修正空白布局的矛盾设置
在BuildSlideLayout方法中,将SlideLayout的Type设为SlideLayoutValues.Blank,但同时在ShapeTree中添加了Title Placeholder,这不符合空白布局的定义。需要移除这个多余的Placeholder Shape:
修改后的BuildSlideLayout方法:
private static void BuildSlideLayout(SlideLayoutPart part) { part.SlideLayout = new SlideLayout( new CommonSlideData( new ShapeTree( new NonVisualGroupShapeProperties( new NonVisualDrawingProperties { Id = 1, Name = "" }, new NonVisualGroupShapeDrawingProperties(), new ApplicationNonVisualDrawingProperties() ), new GroupShapeProperties(new OpenXmlDrawing.TransformGroup()) ) ), new ColorMapOverride(new OpenXmlDrawing.MasterColorMapping()) ) { Type = SlideLayoutValues.Blank }; part.SlideLayout.Save(); }
3. 为Slide添加必要的SlideProperties
生成的Slide缺少SlideProperties元素,虽然不是强制要求,但PowerPoint期望该元素存在,补充后可避免兼容性问题:
修改BuildSlide方法,在Slide节点中添加SlideProperties:
private static void BuildSlide(SlidePart part, Picture picture) { part.Slide = new Slide( new CommonSlideData( new ShapeTree( new NonVisualGroupShapeProperties( new NonVisualDrawingProperties { Id = 1, Name = "" }, new NonVisualGroupShapeDrawingProperties(), new ApplicationNonVisualDrawingProperties() ), new GroupShapeProperties(new OpenXmlDrawing.TransformGroup()), picture ) ), new ColorMapOverride(new OpenXmlDrawing.MasterColorMapping()), new SlideProperties() // 添加这一行 ); part.Slide.Save(); }
4. 确保部件引用关系正确
检查SlidePart引用SlideLayoutPart的代码,确保关系ID一致,原代码中这部分是正确的,但要保证slidePart.AddPart(slideLayoutPart, "rIdLayout");中的关系ID与SlideMaster里的SlideLayoutId的RelationshipId一致,原代码已经满足这一点。
验证修复效果
应用以上修改后,重新生成PPT文件,PowerPoint打开时不会再弹出修复提示,图片也能正常显示。
内容的提问来源于stack exchange,提问作者pfinferno

