使用C#更新Word(.doc)图片:如何检测衬于文字下方的图片
解决Word文档中“衬于文字下方”图片的替换问题
问题描述
我正在开发一款C#应用,用于遍历文件夹中的Word(.doc)文档,找到InlineShape(图片)后将其替换为新图片。程序对默认“嵌入型”环绕的图片处理正常,但遇到部分设置了“衬于文字下方”环绕方式的图片时无法处理,请问如何检测这类图片?
现有代码:
class Program { Application wordApp; Document doc; string imagePath = @"\\Your\Folder\Path"; string folderPath = @"\\Your\Folder\Path"; List<InlineShape> InlineShapesToDelete; public Program() { wordApp = new Application(); InlineShapesToDelete = new List<InlineShape>(); }// end of program construction public void Run() { try { if (Directory.Exists(folderPath)) { string[] files = Directory.GetFiles(folderPath, "*.doc"); foreach (string filePath in files) { ProcessDocument(filePath); } } else { Console.WriteLine("Folder not found."); } }// end of outter try catch (Exception ex) { Console.WriteLine("Error: " + ex.Message); }// end of catch finally { CleanupApplication(); }// end of finally }// end of Run private void ProcessDocument(string filePath) { Console.WriteLine($"\nProcessing File: {Path.GetFileName(filePath)}"); try { doc = wordApp.Documents.Open(filePath); Console.WriteLine($"\tOpened File: {Path.GetFileName(filePath)}"); InlineShapesToDelete.Clear(); foreach (Section section in doc.Sections) { ProcessSection(section, filePath); } if (InlineShapesToDelete.Count == 0) { Console.WriteLine($"\t\tNo Picture Found in doc {Path.GetFileName(filePath)}"); } foreach (InlineShape shapeToDelete in InlineShapesToDelete) { shapeToDelete.Delete(); } doc.Save(); Console.WriteLine($"\t\t\tClosed File: {Path.GetFileName(filePath)}"); }// end of inner try catch (Exception ex) { Console.WriteLine($"Error: processing file {Path.GetFileName(filePath)}: {ex.Message}"); }// end of catch finally { CleanupDocument(); }// end of finally }// end of ProcessDocument private void ProcessSection(Section section, string filePath) { //bool replaced = false; int imageCount = 0; foreach (InlineShape shape in section.Range.InlineShapes) { if (shape.Type == WdInlineShapeType.wdInlineShapePicture) { shape.Select(); InlineShapesToDelete.Add(shape); shape.Range.InlineShapes.AddPicture(imagePath); imageCount++; Console.WriteLine($"\t\tImage \"{imageCount}\" Changed in file: {Path.GetFileName(filePath)}"); // replaced = true; } } }// end of ProcessSection private void CleanupDocument() { if (doc != null) { doc.Close(); } }// end of CleanupDocument private void CleanupApplication() { if (wordApp != null) { wordApp.Quit(); Marshal.ReleaseComObject(wordApp); } }// end of CleanupApplication static void Main(string[] args) { Program program = new Program(); program.Run(); } }
核心原因
Word中,嵌入型图片属于InlineShape对象,而所有带有文字环绕属性的浮动图片(包括“衬于文字下方”)都归类为Shape对象,你的现有代码只遍历了InlineShapes集合,因此无法处理这类浮动图片。
解决建议
1. 新增Shape对象的处理逻辑
在现有代码基础上,添加对Shapes集合的遍历,筛选出符合条件的图片:
- 用
Shape.Type判断是否为图片(对应WdShapeType.wdShapePicture) - 用
Shape.WrapFormat.Type判断环绕方式,“衬于文字下方”对应WdWrapType.wdWrapBehind
2. 修改后的完整代码片段
class Program { Application wordApp; Document doc; string imagePath = @"\\Your\Folder\Path"; string folderPath = @"\\Your\Folder\Path"; List<InlineShape> InlineShapesToDelete; List<Shape> ShapesToDelete; // 新增存储浮动图片的列表 public Program() { wordApp = new Application(); InlineShapesToDelete = new List<InlineShape>(); ShapesToDelete = new List<Shape>(); // 初始化列表 }// end of program construction public void Run() { // 原Run方法逻辑不变,此处省略 }// end of Run private void ProcessDocument(string filePath) { Console.WriteLine($"\nProcessing File: {Path.GetFileName(filePath)}"); try { doc = wordApp.Documents.Open(filePath); Console.WriteLine($"\tOpened File: {Path.GetFileName(filePath)}"); InlineShapesToDelete.Clear(); ShapesToDelete.Clear(); // 清空浮动图片列表 foreach (Section section in doc.Sections) { ProcessSection(section, filePath); ProcessSectionShapes(section, filePath); // 新增处理浮动图片的方法调用 } if (InlineShapesToDelete.Count == 0 && ShapesToDelete.Count == 0) { Console.WriteLine($"\t\tNo Picture Found in doc {Path.GetFileName(filePath)}"); } // 删除嵌入型图片 foreach (InlineShape shapeToDelete in InlineShapesToDelete) { shapeToDelete.Delete(); } // 删除浮动图片 foreach (Shape shapeToDelete in ShapesToDelete) { shapeToDelete.Delete(); } doc.Save(); Console.WriteLine($"\t\t\tClosed File: {Path.GetFileName(filePath)}"); }// end of inner try catch (Exception ex) { Console.WriteLine($"Error: processing file {Path.GetFileName(filePath)}: {ex.Message}"); }// end of catch finally { CleanupDocument(); }// end of finally }// end of ProcessDocument // 原ProcessSection方法不变,此处省略 // 新增处理浮动图片的方法 private void ProcessSectionShapes(Section section, string filePath) { int imageCount = 0; foreach (Shape shape in section.Range.Shapes) { // 判断是否为图片且环绕方式为衬于文字下方 if (shape.Type == WdShapeType.wdShapePicture && shape.WrapFormat.Type == WdWrapType.wdWrapBehind) { // 保留原图片的位置、尺寸属性 float left = shape.Left; float top = shape.Top; float width = shape.Width; float height = shape.Height; ShapesToDelete.Add(shape); // 在原位置插入新图片并设置相同环绕方式 Shape newShape = section.Range.Shapes.AddPicture( Filename: imagePath, LinkToFile: false, SaveWithDocument: true, Left: left, Top: top, Width: width, Height: height ); newShape.WrapFormat.Type = WdWrapType.wdWrapBehind; imageCount++; Console.WriteLine($"\t\t浮动图片 \"{imageCount}\" Changed in file: {Path.GetFileName(filePath)}"); } } }// end of ProcessSectionShapes // 原CleanupDocument、CleanupApplication、Main方法不变,此处省略 }
3. 注意事项
- 不要在遍历
Shapes或InlineShapes集合时直接删除对象,先存入列表再统一处理,避免集合遍历异常 - 插入新图片时需保留原图片的位置、尺寸和环绕属性,保证文档布局一致性
- 处理完COM对象后及时释放,避免内存泄漏
内容的提问来源于stack exchange,提问作者Maiz
相关产品推荐
相关产品推荐

