Изображение замены в использовании документа в формате Word OpenXML

Следование за моим последним вопросом здесь

OpenXML похож на него, вероятно, делает точно, что я хочу, но документация ужасна. Час поиска с помощью Google не имеет меня немного ближе к выяснению, что я должен сделать.

Я перебрасываюсь парой слов документ. Я хочу добавить изображение к тому документу слова (использующий слово) таким способом, которым я могу затем открыть документ в OpenXML и замене то изображение. Должно быть достаточно простым, да?

Я предполагаю, что должен смочь дать моему изображению 'заполнителя' какой-то идентификатор и затем использовать GetPartById определять местоположение изображения и заменять его. Это было бы корректным методом? Каков этот идентификатор? Как Вы добавляете его с помощью Word?

Каждый пример, который я могу найти, который делает что-либо удаленно подобные запуски путем создания целого документа слова с нуля в ML, который действительно не является большим использованием.

Править: мне пришло в голову, что это будет легче просто заменить изображение в папке медиа с новым изображением, но снова не может найти признак того, как сделать это.

26
задан Community 23 May 2017 в 12:09
поделиться

2 ответа

Хотя документация по OpenXML не очень хороша, есть отличный инструмент, который можно использовать для просмотра того, как построены существующие документы Word. Если вы установите OpenXml SDK, он поставляется с DocumentReflector.exe инструментом в Open XML Format SDK\V2.0\tools каталоге.

Изображения в документах Word состоят из данных изображения и присвоенного ему идентификатора, на который ссылаются в теле документа. Похоже, что вашу проблему можно разделить на две части: найти идентификатор изображения в документе, а затем переписать данные изображения для него.

Чтобы найти идентификатор изображения, необходимо разобрать MainDocumentPart. Изображения хранятся в Runs как элемент Drawing

<w:p>
  <w:r>
    <w:drawing>
      <wp:inline>
        <wp:extent cx="3200400" cy="704850" /> <!-- describes the size of the image -->
        <wp:docPr id="2" name="Picture 1" descr="filename.JPG" />
        <a:graphic>
          <a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture">
            <pic:pic>
              <pic:nvPicPr>
                <pic:cNvPr id="0" name="filename.JPG" />
                <pic:cNvPicPr />
              </pic:nvPicPr>
              <pic:blipFill>
                <a:blip r:embed="rId5" /> <!-- this is the ID you need to find -->
                <a:stretch>
                  <a:fillRect />
                </a:stretch>
              </pic:blipFill>
              <pic:spPr>
                <a:xfrm>
                  <a:ext cx="3200400" cy="704850" />
                </a:xfrm>
                <a:prstGeom prst="rect" />
              </pic:spPr>
            </pic:pic>
          </a:graphicData>
        </a:graphic>
      </wp:inline>
    </w:drawing>
  </w:r>
</w:p>

В приведенном выше примере вам нужно найти идентификатор изображения, хранящегося в элементе blip. Как вы собираетесь это сделать, зависит от вашей проблемы, но если вы знаете имя файла исходного изображения, вы можете посмотреть на элемент docPr:

using (WordprocessingDocument document = WordprocessingDocument.Open("docfilename.docx", true)) {

  // go through the document and pull out the inline image elements
  IEnumerable<Inline> imageElements = from run in Document.MainDocumentPart.Document.Descendants<Run>()
      where run.Descendants<Inline>().First() != null
      select run.Descendants<Inline>().First();

  // select the image that has the correct filename (chooses the first if there are many)
  Inline selectedImage = (from image in imageElements
      where (image.DocProperties != null &&
          image.DocProperties.Equals("image filename"))
      select image).First();

  // get the ID from the inline element
  string imageId = "default value";
  Blip blipElement = selectedImage.Descendants<Blip>().First();
  if (blipElement != null) {
      imageId = blipElement.Embed.Value;
  }
}

Затем, когда у вас есть идентификатор изображения, вы можете использовать его для перезаписи данных изображения. Я думаю, вот как это можно сделать:

ImagePart imagePart = (ImagePart)document.MainDocumentPart.GetPartById(imageId);
byte[] imageBytes = File.ReadAllBytes("new_image.jpg");
BinaryWriter writer = new BinaryWriter(imagePart.GetStream());
writer.Write(imageBytes);
writer.Close();
33
ответ дан 28 November 2019 в 06:33
поделиться

Я хотел бы обновить эту ветку и добавить к ответу Адама выше в интересах других.

Мне действительно удалось на днях (до того, как Адам опубликовал свой ответ) вместе взломать рабочий код, но это было довольно сложно. Документация действительно скудная, да и информации там немного.

Я не знал об элементах Inline и Run , которые Адам использует в своем ответе, но уловка, похоже, заключается в том, чтобы добраться до Потомков <> , и тогда вы сможете анализировать любой элемент, как обычное отображение XML.

byte[] docBytes = File.ReadAllBytes(_myFilePath);
using (MemoryStream ms = new MemoryStream())
{
    ms.Write(docBytes, 0, docBytes.Length);

    using (WordprocessingDocument wpdoc = WordprocessingDocument.Open(ms, true))
    {
        MainDocumentPart mainPart = wpdoc.MainDocumentPart;
        Document doc = mainPart.Document;

        // now you can use doc.Descendants<T>()
    }
}

Когда у вас есть это, довольно легко искать вещи, хотя вам нужно выяснить, как все это называется. Например, равно Picture.NonVisualPictureProperties и т. Д.

Как правильно говорит Адам, элемент, который вам нужно найти для замены изображения, это Элемент Blip . Но вам нужно найти правильный значок, соответствующий изображению, которое вы пытаетесь заменить.

Адам показывает способ использования встроенного элемента .Я просто нырнул и искал все элементы изображения. Я не уверен, какой способ лучше или надежнее (я не знаю, насколько согласована структура xml между документами и вызывает ли это нарушение кода).

Blip GetBlipForPicture(string picName, Document document)
{
    return document.Descendants<Picture>()
         .Where(p => picName == p.NonVisualPictureProperties.NonVisualDrawingProperties.Name)
         .Select(p => p.BlipFill.Blip)
         .Single(); // return First or ToList or whatever here, there can be more than one
}

См. Пример XML Адама, чтобы разобраться в различных элементах здесь и увидеть, что я ищу.

Сообщение имеет идентификатор в свойстве Embed , например: , это значит сопоставьте Blip с изображением в папке Media (вы можете увидеть все эти папки и файлы, если переименуете файл .docx в .zip и разархивируете его). Сопоставление можно найти в _rels \ document.xml.rels :

Итак, что вам нужно сделать, это добавить новое изображение, а затем указать этой меткой на идентификатор вашего вновь созданного изображения:

// add new ImagePart
ImagePart newImg = mainPart.AddImagePart(ImagePartType.Png);
// Put image data into the ImagePart (from a filestream)
newImg .FeedData(File.Open(_myImgPath, FileMode.Open, FileAccess.Read));
// Get the blip
Blip blip = GetBlipForPicture("MyPlaceholder.png", doc);
// Point blip at new image
blip.Embed = mainPart.GetIdOfPart(newImg);

I Предположим, это просто лишает старое изображение в папке Media, что не идеально, хотя, возможно, оно достаточно умен, чтобы, так сказать, собирать мусор. Может быть, есть способ получше, но я не смог его найти.

В общем, вот оно. Эта ветка теперь является наиболее полной документацией о том, как поменять изображение в любом месте в Интернете (я знаю это, я потратил часы на поиск). Надеюсь, кому-то это пригодится.

17
ответ дан 28 November 2019 в 06:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: