C#使用Open XML向Word页眉插入图片显示空白框问题
页眉插入图片显示空白框的修复方案
核心错误原因
现有页眉插入逻辑存在4个问题,直接导致图片无法正常渲染:
- 图片部件挂载位置错误:页眉是独立于正文的
HeaderPart部件,Open XML的部件关系是隔离的,代码将图片资源挂载到了MainDocumentPart下,页眉内的资源引用无法跨部件找到对应图片,只能渲染空白占位框。 - 尺寸单位未做转换:正文插入逻辑里做了像素到EMU(Open XML绘图专用单位)的换算,页眉逻辑里直接把原始像素值填入尺寸属性,不符合格式规范导致渲染失败。
- 页眉存在性判断逻辑写反:判断条件
!mainDocPart.HeaderParts.Any()意味着只有文档完全没有页眉时才会执行插入逻辑,文档自带默认页眉的场景下插入代码完全不会运行。 - 尺寸读取路径硬编码:
GeneratePicHeader方法读取图片尺寸时用了写死的本地路径C:\Users\SAKS\HeaderPic.png,和实际传入的待插入图片路径不一致,既可能触发文件不存在的异常,也会导致尺寸和实际图片不匹配。
修复后代码
修正后的页眉插入入口方法
static void AddPicInHeader(string document, string imagePath) { using(WordprocessingDocument wordprocessingDocument = WordprocessingDocument.Open(document, true)) { var mainDocPart = wordprocessingDocument.MainDocumentPart; // 获取文档最后一个节的属性 var sectionProps = mainDocPart.Document.Body.Elements<SectionProperties>().LastOrDefault(); if (sectionProps == null) { sectionProps = new SectionProperties(); mainDocPart.Document.Body.Append(sectionProps); } HeaderPart targetHeaderPart; // 优先复用已存在的页眉,不存在则新建 var existingHeaderRef = sectionProps.GetFirstChild<HeaderReference>(); if (existingHeaderRef != null) { targetHeaderPart = mainDocPart.GetPartById(existingHeaderRef.Id) as HeaderPart; } else { targetHeaderPart = mainDocPart.AddNewPart<HeaderPart>(); var newHeaderRef = new HeaderReference() { Id = mainDocPart.GetIdOfPart(targetHeaderPart) }; sectionProps.RemoveAllChildren<HeaderReference>(); sectionProps.Append(newHeaderRef); targetHeaderPart.Header = new Header(); } // 关键:图片资源必须挂载到当前HeaderPart下,不能挂在MainDocumentPart var imgPart = targetHeaderPart.AddImagePart(ImagePartType.Png); using(FileStream stream = new FileStream(imagePath, FileMode.Open)) { imgPart.FeedData(stream); } var imagePartId = targetHeaderPart.GetIdOfPart(imgPart); // 生成图片元素追加到页眉 var headerPic = GenerateHeaderPicElement(imagePartId, imagePath); targetHeaderPart.Header.AppendChild(new Paragraph(new Run(headerPic))); targetHeaderPart.Header.Save(); } }
修正后的图片元素生成方法
static Drawing GenerateHeaderPicElement(string relationshipId, string actualImagePath) { long pixelWidth = 0; long pixelHeight = 0; // 读取实际插入图片的尺寸,不要硬编码路径 using(System.Drawing.Bitmap bmp = new Bitmap(actualImagePath)) { pixelHeight = bmp.Height; pixelWidth = bmp.Width; } // 像素转EMU单位,标准换算系数为9525(1像素=9525EMU) long emuWidth = pixelWidth * 9525; long emuHeight = pixelHeight * 9525; return new Drawing( new DW.Inline( new DW.Extent() { Cx = emuWidth, Cy = emuHeight }, new DW.EffectExtent() { LeftEdge = 0L, TopEdge = 0L, RightEdge = 0L, BottomEdge = 0L }, new DW.DocProperties() { Id = (UInt32Value)1U, Name = Path.GetFileName(actualImagePath) }, new A.Graphic( new A.GraphicData( new PIC.Picture( new PIC.NonVisualPictureProperties( new PIC.NonVisualDrawingProperties() { Id = (UInt32Value)0U, Name = Path.GetFileName(actualImagePath) }, new PIC.NonVisualPictureDrawingProperties()), new PIC.BlipFill( new A.Blip( new A.BlipExtensionList( new A.BlipExtension() { Uri = "{28A0092B-C50C-407E-A947-70E740481C1C}" }) ) { Embed = relationshipId, CompressionState = A.BlipCompressionValues.Print }, new A.Stretch(new A.FillRectangle())), new PIC.ShapeProperties( new A.Transform2D( new A.Offset() { X = 0L, Y = 0L }, new A.Extents() { Cx = emuWidth, Cy = emuHeight }), new A.PresetGeometry(new A.AdjustValueList()) { Preset = A.ShapeTypeValues.Rectangle })) ) { Uri = "http://schemas.openxmlformats.org/drawingml/2006/picture" }) ) { DistanceFromTop = (UInt32Value)0U, DistanceFromBottom = (UInt32Value)0U, DistanceFromLeft = (UInt32Value)0U, DistanceFromRight = (UInt32Value)0U, EditId = "50D07946" }); }
开发注意事项
- Open XML中所有独立部件(页眉、页脚、正文、批注、脚注等)都有独立的关系表,部件内引用的资源(图片、超链接等)必须挂载到当前部件或可访问的父级部件下,跨部件直接引用关系ID会出现资源找不到的问题。
- 所有绘图相关的尺寸参数必须使用EMU单位,禁止直接传入像素值,否则会出现尺寸异常、图片不渲染的问题。
- 如果文档存在多个分节,需要遍历所有节的
SectionProperties分别处理页眉,否则仅最后一个节会显示插入的页眉图片。
内容的提问来源于stack exchange,提问作者Sayeed Ahmed
相关产品推荐
相关产品推荐

