【问题标题】:OpenXml image relationship doesn't existOpenXml 图像关系不存在
【发布时间】:2018-05-22 17:16:51
【问题描述】:

我是一家大公司的实习生,我的任务是处理以前实习生的项目,该项目显然在某个时候有效,但现在它已经坏了。该程序的作用是从文档中取出一堆文本和图像并将它们插入到模板文档中。问题是,大约一半的图像没有形成关系,我得到了红色的 X“图像无法显示”空框。我一直在使用生产力工具进行一些挖掘,我发现有几个重复的 ID,以及相当多的不存在的关系,尽管查看他的代码我不确定是什么原因造成的.以下是他复制图像的两种方法:

internal static void CopyImages(OpenXmlElement oldTable, OpenXmlElement newTable,
        WordprocessingDocument testData, WordprocessingDocument testReport)
    {
        List<Blip> sourceBlips = DocumentHelper.GetAllBlips(oldTable);
        List<Blip> targetBlips = DocumentHelper.GetAllBlips(newTable);

        foreach (Blip sourceBlip in sourceBlips)
        {
            foreach (Blip targetBlip in targetBlips)
            {
                if (targetBlip.Embed.Value == sourceBlip.Embed.Value)
                {
                    if (testData.MainDocumentPart.GetPartById(sourceBlip.Embed.Value) is ImagePart imagePart)
                    {
                        ImagePart newImagePart = testReport.MainDocumentPart.AddPart(imagePart);

                        targetBlip.Embed.Value = testReport.MainDocumentPart.GetIdOfPart(newImagePart);
                        break;
                    }
                }
            }
        }
    }

    internal static void CopyEmbeddedVisioImages(OpenXmlElement oldTable, OpenXmlElement newTable,
        WordprocessingDocument testData, WordprocessingDocument testReport)
    {
        List<EmbeddedObject> sourceObjects = oldTable.Descendants<EmbeddedObject>().ToList();
        List<EmbeddedObject> targetObjects = newTable.Descendants<EmbeddedObject>().ToList();

        foreach (EmbeddedObject targetobj in targetObjects)
        {
            foreach (EmbeddedObject sourceObj in sourceObjects)
            {
                if (testData.MainDocumentPart.GetPartById(sourceObj.Descendants<ImageData>()
                                .FirstOrDefault().RelationshipId) is ImagePart oldImagePart)
                {
                    ImagePart newImagePart = testReport.MainDocumentPart.AddPart(oldImagePart);

                    targetobj.Descendants<ImageData>().FirstOrDefault().RelationshipId =
                    testReport.MainDocumentPart.GetIdOfPart(newImagePart);
                }


                if (testData.MainDocumentPart.GetPartById(sourceObj.Descendants<OleObject>()
                                .FirstOrDefault().Id) is OpenXmlPart openXmlPart)
                {
                    EmbeddedObjectPart newEmbeddedObj = (EmbeddedObjectPart)testReport.MainDocumentPart.AddPart(openXmlPart);

                    targetobj.Descendants<OleObject>().FirstOrDefault().Id =
                                testReport.MainDocumentPart.GetIdOfPart(newEmbeddedObj);
                }
            }
        }
    }

我尝试在文档上调用 Save() 和 Close()。我什至尝试调用 Dispose()。 using(WordprocessingDocument foo = WordprocessingDocument.Open(bar, false){} 似乎也没有帮助。我现在不太担心重复的 ID,但我不知道为什么只有一些关系正在形成而其他关系没有。这是一个庞大的项目,因此浏览其中的一些项目可能会非常棘手。

编辑:可能还值得一提的是,图像在某个时刻停止形成关系。这不是随机的。大约 2/3 的路径下没有任何图像。

这是更新后的方法集

internal static void CopyImages(OpenXmlElement oldTable, OpenXmlElement newTable,
        WordprocessingDocument testData, WordprocessingDocument testReport)
    {
        List<Blip> sourceBlips = DocumentHelper.GetAllBlips(oldTable);
        List<Blip> targetBlips = DocumentHelper.GetAllBlips(newTable);

        foreach (Blip sourceBlip in sourceBlips)
        {
            foreach (Blip targetBlip in targetBlips)
            {
                if (targetBlip.Embed.Value == sourceBlip.Embed.Value)
                {
                    if (testData.MainDocumentPart.GetPartById(sourceBlip.Embed.Value) is ImagePart imagePart)
                    {
                        //ImagePart newImagePart = testReport.MainDocumentPart.AddPart(imagePart);
                        ImagePart newImagePart = testReport.MainDocumentPart.AddImagePart(imagePart.ContentType);
                        newImagePart.FeedData(imagePart.GetStream(FileMode.Open, FileAccess.Read));
                        targetBlip.Embed.Value = testReport.MainDocumentPart.GetIdOfPart(newImagePart);
                        break;
                    }
                }
            }
        }
    }

    internal static void CopyEmbeddedVisioImages(OpenXmlElement oldTable, OpenXmlElement newTable,
        WordprocessingDocument testData, WordprocessingDocument testReport)
    {
        List<EmbeddedObject> sourceObjects = oldTable.Descendants<EmbeddedObject>().ToList();
        List<EmbeddedObject> targetObjects = newTable.Descendants<EmbeddedObject>().ToList();

        foreach (EmbeddedObject targetobj in targetObjects)
        {
            foreach (EmbeddedObject sourceObj in sourceObjects)
            {
                if (testData.MainDocumentPart.GetPartById(sourceObj.Descendants<ImageData>()
                                .FirstOrDefault().RelationshipId) is ImagePart oldImagePart)
                {
                    //ImagePart newImagePart = testReport.MainDocumentPart.AddPart(oldImagePart);
                    ImagePart newImagePart = testReport.MainDocumentPart.AddImagePart(oldImagePart.ContentType);
                    newImagePart.FeedData(oldImagePart.GetStream(FileMode.Open, FileAccess.Read));

                    targetobj.Descendants<ImageData>().FirstOrDefault().RelationshipId =
                    testReport.MainDocumentPart.GetIdOfPart(newImagePart);
                }


                if (testData.MainDocumentPart.GetPartById(sourceObj.Descendants<OleObject>()
                                .FirstOrDefault().Id) is OpenXmlPart openXmlPart)
                {
                    EmbeddedObjectPart newEmbeddedObj = (EmbeddedObjectPart)testReport.MainDocumentPart.AddPart(openXmlPart);

                    targetobj.Descendants<OleObject>().FirstOrDefault().Id =
                                testReport.MainDocumentPart.GetIdOfPart(newEmbeddedObj);
                }
            }
        }
    }

这是我发现的最新信息。

  • 整个文档共有 25 个 blip。
  • targetBlip.Embed.Value != sourceBlip.Embed.Value 在大多数情况下还是其他原因?
  • 包含图片的元素从源文档中克隆,然后保存到目标文档中。
  • 正在读取所有元素。包含关系破裂的图片的表格存在并且填充了其他内容,因此它不会缺少这些元素。
  • 重复的 ID 是由于目标文档开始包含几个图像,所以当我复制其他图像时,其中一些 ID 是重复的。这不是我现在关心的问题。

【问题讨论】:

  • 您不应该以使答案无效的方式编辑/更新您的问题。请回滚您的编辑,然后将更改代码的相关部分添加为问题末尾的单独部分。这也将允许那些试图帮助比较代码的人。

标签: c# ms-word openxml-sdk


【解决方案1】:

源文档中的图像不能按原样添加到目标文档中; 图像在其父文档中具有唯一的 ID/编号,如果已存在具有相同 ID 的图像,则该图像可能与目标文档冲突。 替换以下行

ImagePart newImagePart = testReport.MainDocumentPart.AddPart(imagePart);

与下面的一个。这里嵌入了一个全新的图像文件并分配了一个新的 id。

ImagePart newImagePart = testReport.MainDocumentPart.AddImagePart(oldImagePart.ContentType);
newImagePart.FeedData(oldImagePart.GetStream(FileMode.Open, FileAccess.Read));

目标文档中的 id 必须是唯一的,这一点很重要。 我分享了一些(旧的(er))代码片段,关于我如何处理将图像从一个文档合并到另一个文档。 (这是一个更完整/更复杂的实现的片段,其中检测到重复图像并防止多次插入。)

它首先遍历源文档中的所有绘图并构建一个列表以及它们在此源文档中的原始 ID。然后所有图像都被插入到目标文档中;这样做时,目标文档中的新 id 会映射到每个项目。

源文档中的每个绘图都使用目标文档中的 id 进行更新;该列表包含原始源 ID 和新目标 ID。 (这听起来很奇怪,但对我来说,当时只有这给出了预期的结果。)

只有在图像合并完成后,内容(段落和表格)才会合并到目标文档中,其中包括添加这些项目的克隆。

public class DocumentMerger
{
    private readonly WordprocessingDocument _targetDocument;

    public DocumentMerger(WordprocessingDocument targetDocument)
    {
        this._targetDocument = targetDocument;
    }    

    public void Merge(WordprocessingDocument sourceDocument)
    {
        ImagesMerger imagesMerger = new ImagesMerger(this._targetDocument);
        this._imagesMerger.Merge(sourceDocument);

        // Merge the content; paragraphs and tables.

        this._targetDocumentPart.Document.Save();
    }    
}


public class ImageInfo
{
    private String _id;
    private ImagePart _image;
    private readonly String _originalId;

    private ImageInfo(ImagePart image, String id)
    {  
        this._id = id;
        this._image = image;
        this._originalId = id;
    }

    public String Id 
    { 
        get { return this._id; } 
    }

    public ImagePart Image
    {
        get { return this._image; }
    }

    public String OriginalId
    {
        get { return this._originalId; }
    }

    public static ImageInfo Create(MainDocumentPart documentPart, ImagePart image)
    {
        String id = documentPart.GetIdOfPart(image);
        ImageInfo r = new ImageInfo(image, id);
        return r;
    }    

    public void Reparent(MainDocumentPart documentPart)
    {   
        ImagePart newImage = documentPart.AddImagePart(this._image.ContentType);                
        newImage.FeedData(this._image.GetStream(FileMode.Open, FileAccess.Read));
        String newId = documentPart.GetIdOfPart(newImage);                        
        this._id = newId;
        this._image = newImage;                
    }    
}


public class ImagesMerger 
{
    private readonly IList<ImageInfo> _imageInfosOfTheTargetDocument = new List<ImageInfo>();        
    private readonly MainDocumentPart _targetDocumentPart;

    public ImagesMerger(WordprocessingDocument targetDocument)
    {
        this._targetDocumentPart = targetDocument.MainDocumentPart;
    }

    public void Merge(WordprocessingDocument sourceDocument)
    {
        MainDocumentPart sourceDocumentPart = sourceDocument.MainDocumentPart;
        IList<ImageInfo> imageInfosOfTheSourceDocument = this.getImageInfos(sourceDocumentPart);
        if (0 == imageInfosOfTheSourceDocument.Count) { return; }

        this.addTheImagesToTheTargetDocument(imageInfosOfTheSourceDocument);
        this.rereferenceTheImagesToTheirCorrespondingImageParts(sourceDocumentPart, imageInfosOfTheSourceDocument);
    }

    private void addTheImagesToTheTargetDocument(IList<ImageInfo> imageInfosOfTheSourceDocument)
    {
        for (Int32 i = 0, j = imageInfosOfTheSourceDocument.Count; i < j; i++)
        {
            imageInfoOfTheSourceDocument.Reparent(this._targetDocumentPart);
            this._imageInfosOfTheTargetDocument.Add(imageInfoOfTheSourceDocument);                    
        }            
    }

    private IList<ImageInfo> getImageInfos(MainDocumentPart documentPart)
    {
        List<ImageInfo> r = new List<ImageInfo>();

        foreach (ImagePart image in documentPart.ImageParts)
        {
            ImageInfo imageInfo = ImageInfo.Create(documentPart, image);
            r.Add(imageInfo);
        }

        return r;
    }

    private void rereferenceTheImagesToTheirCorrespondingImageParts(MainDocumentPart sourceDocumentPart, IList<ImageInfo> imageInfosOfTheSourceDocument)
    {
        IEnumerable<Drawing> images = sourceDocumentPart.Document.Body.Descendants<Drawing>();

        foreach (Drawing image in images)
        {
            Blip blip = image.Inline.Graphic.GraphicData.Descendants<Blip>().FirstOrDefault();
            String originalId = blip.Embed.Value;

            ImageInfo imageInfo = imageInfosOfTheSourceDocument.FirstOrDefault(o => o.OriginalId._Equals(originalId));
            blip.Embed.Value = imageInfo.Id;
        }
    }
}

【讨论】:

  • 不幸的是,即使在替换之后,我也得到了完全相同的结果。我已经用修改更新了我的原始帖子。我不认为重复的 ID 与关系问题有任何关系吗?
  • 您能否解压缩 Word 文档(必要时提供 .zip 扩展名)并查看源文档和目标文档的 \word\media\ 子文件夹中存在哪些图像?
  • 当然。这里似乎也没有任何特定的模式。其中一些保存为.jpeg,一些为.png,一些为.wmf。未包含的图像都是 .png 或 .jpeg。
  • AddImagePart/FeedData 代码运行的次数是否与源文档中的图像数量一样多?您是否在 \word\media\ 子文件夹中找到所有图像?以下行假设 2 个不同文档的 id 匹配,如果 (targetBlip.Embed.Value == sourceBlip.Embed.Value) 则不会有太多机会
  • 无论出于何种原因,它只运行了 25 次,\word\media\ 文件夹中只有 25 张图片
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-21
  • 1970-01-01
  • 2018-12-21
  • 2017-04-20
  • 2022-01-22
相关资源
最近更新 更多