Следование за моим последним вопросом здесь
OpenXML похож на него, вероятно, делает точно, что я хочу, но документация ужасна. Час поиска с помощью Google не имеет меня немного ближе к выяснению, что я должен сделать.
Я перебрасываюсь парой слов документ. Я хочу добавить изображение к тому документу слова (использующий слово) таким способом, которым я могу затем открыть документ в OpenXML и замене то изображение. Должно быть достаточно простым, да?
Я предполагаю, что должен смочь дать моему изображению 'заполнителя' какой-то идентификатор и затем использовать GetPartById
определять местоположение изображения и заменять его. Это было бы корректным методом? Каков этот идентификатор? Как Вы добавляете его с помощью Word?
Каждый пример, который я могу найти, который делает что-либо удаленно подобные запуски путем создания целого документа слова с нуля в ML, который действительно не является большим использованием.
Править: мне пришло в голову, что это будет легче просто заменить изображение в папке медиа с новым изображением, но снова не может найти признак того, как сделать это.
Хотя документация по 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();
Я хотел бы обновить эту ветку и добавить к ответу Адама выше в интересах других.
Мне действительно удалось на днях (до того, как Адам опубликовал свой ответ) вместе взломать рабочий код, но это было довольно сложно. Документация действительно скудная, да и информации там немного.
Я не знал об элементах 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, что не идеально, хотя, возможно, оно достаточно умен, чтобы, так сказать, собирать мусор. Может быть, есть способ получше, но я не смог его найти.
В общем, вот оно. Эта ветка теперь является наиболее полной документацией о том, как поменять изображение в любом месте в Интернете (я знаю это, я потратил часы на поиск). Надеюсь, кому-то это пригодится.