una resolucion power perder nitidez mejorar insertar imagenes imagen guardar convertir con como cambiar calidad buena aumentar .net ms-word openxml

.net - resolucion - como mejorar la nitidez de una imagen en word



Reemplazar imagen en word doc usando OpenXML (8)

Siguiendo de mi última pregunta here

OpenXML parece que probablemente hace exactamente lo que quiero, pero la documentación es terrible. Una hora de googlear no me ha acercado más a averiguar qué debo hacer.

Tengo un documento de Word. Quiero agregar una imagen a ese documento de Word (utilizando word) de tal manera que luego pueda abrir el documento en OpenXML y reemplazar esa imagen. Debería ser lo suficientemente simple, ¿sí?

Supongo que debería poder GetPartById a mi imagen ''marcador de posición'' una identificación de algún tipo y luego usar GetPartById para ubicar la imagen y reemplazarla. ¿Sería este el método correcto? ¿Qué es esta identificación? ¿Cómo lo agregas usando Word?

Cada ejemplo que puedo encontrar que hace algo remotamente similar comienza por construir todo el documento de Word desde cero en ML, lo que realmente no es de mucha utilidad.

EDITAR: se me ocurrió que sería más fácil reemplazar la imagen en la carpeta de medios con la nueva imagen, pero nuevamente no puedo encontrar ninguna indicación de cómo hacer esto.


Aunque la documentación para OpenXML no es excelente, existe una excelente herramienta que puede utilizar para ver cómo se crean los documentos de Word existentes. Si instala OpenXml SDK, viene con la herramienta DocumentReflector.exe en el directorio Open XML Format SDK / V2.0 / tools .

Las imágenes en documentos de Word consisten en los datos de imagen y una identificación que se le asigna a la que se hace referencia en el cuerpo del documento. Parece que su problema se puede dividir en dos partes: encontrar el ID de la imagen en el documento y luego volver a escribir los datos de la imagen para él.

Para encontrar el ID de la imagen, deberá analizar MainDocumentPart. Las imágenes se almacenan en Runs como un elemento de dibujo.

<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>

En el ejemplo anterior, necesita encontrar el ID de la imagen almacenada en el elemento blip. Cómo encuentra que depende de su problema, pero si conoce el nombre de archivo de la imagen original, puede mirar el elemento 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; } }

Luego, cuando tenga la ID de la imagen, puede usarla para volver a escribir los datos de la imagen. Creo que así es como lo harías:

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();


El siguiente código recuperará las imágenes del documento especificado (nombre de archivo) y las guardará en una carpeta D: / TestArea usando los nombres de archivo internos. Las respuestas en esta página me ayudaron a encontrar mi solución.

Nota: esta solución no ayuda a alguien a reemplazar una imagen en un documento de Word, sin embargo, en todas mis búsquedas sobre cómo recuperar una imagen de un documento de Word, este fue el único enlace más cercano que pude encontrar; En caso de que alguien más esté en el mismo barco, publico mi solución aquí.

private void ProcessImages(string filename) { var xpic = ""; var xr = "http://schemas.openxmlformats.org/officeDocument/2006/relationships"; using (WordprocessingDocument document = WordprocessingDocument.Open(filename, true)) { var imageParts = from paragraph in document.MainDocumentPart.Document.Body from graphic in paragraph.Descendants<Graphic>() let graphicData = graphic.Descendants<GraphicData>().FirstOrDefault() let pic = graphicData.ElementAt(0) let nvPicPrt = pic.ElementAt(0).FirstOrDefault() let blip = pic.Descendants<Blip>().FirstOrDefault() select new { Id = blip.GetAttribute("embed",xr).Value, Filename = nvPicPrt.GetAttribute("name",xpic).Value }; foreach(var image in imageParts) { var outputFilename = string.Format(@"d:/TestArea/{0}",image.Filename); Debug.WriteLine(string.Format("Creating file: {0}",outputFilename)); // Get image from document var imageData = document.MainDocumentPart.GetPartById(image.Id); // Read image data into bytestream var stream = imageData.GetStream(); var byteStream = new byte[stream.Length]; int length = (int)stream.Length; stream.Read(byteStream, 0, length); // Write bytestream to disk using (var fileStream = new FileStream(outputFilename,FileMode.OpenOrCreate)) { fileStream.Write(byteStream, 0, length); } } } }


La documentación de openXml es muy delgada y la mayoría de las transacciones requiere demasiado tiempo. Estaba haciendo una tarea específica y quiero compartir la solución. Espero que ayude a la gente y te ahorren tiempo. Tuve que obtener una imagen de un lugar particular en el texto, particularmente si es un objeto de Ejecutar.

static string AddPictureToHtml(DocumentFormat.OpenXml.Wordprocessing.Picture pic) { string exit = ""; DocumentFormat.OpenXml.Vml.Shape shape = pic.Descendants<DocumentFormat.OpenXml.Vml.Shape>().First(); DocumentFormat.OpenXml.Vml.ImageData imageData = shape.Descendants<DocumentFormat.OpenXml.Vml.ImageData>().First(); //style image string style = shape.Style; style = style.Replace("width:", ""); style = style.Replace("height:", ""); style = style.Replace(''.'', '',''); style = style.Replace("pt", ""); string[] arr = style.Split('';''); float styleW = float.Parse(arr[0]);//width picture float styleH = float.Parse(arr[1]);//height picture string relationId = imageData.RelationshipId; var img = doc.MainDocumentPart.GetPartById(relationId); var uri = img.Uri;//path in file var fileName = uri.ToString().Split(''/'').Last();//name picture var fileWordMedia = img.GetStream(FileMode.Open); exit = String.Format("<img src=/"" + docPath+uri+ "/" width=/""+styleW+"/" heigth=/""+styleH+"/" > "); return exit; }

Más específicamente, necesito traducir el párrafo del documento en formato html.

static void SavePictures(ImagePart img, string savePath) { var uri = img.Uri; var fileName = uri.ToString().Split(''/'').Last(); var fileWordMedia = img.GetStream(FileMode.Open); string imgPath = savePath + fileName; FileStream fileHtmlMedia = new FileStream(imgPath, FileMode.Create); int i = 0; while (i != (-1)) { i = fileWordMedia.ReadByte(); if (i != (-1)) { fileHtmlMedia.WriteByte((byte)i); } } fileHtmlMedia.Close(); fileWordMedia.Close(); }

uri es una ruta a la imagen en el archivo .docx, por ejemplo: "test.docx / media / image.bmp" usando esta imagen de información para que pueda obtener una imagen

static void SavePictures(ImagePart img, string savePath) { var uri = img.Uri; var fileName = uri.ToString().Split(''/'').Last(); var fileWordMedia = img.GetStream(FileMode.Open); string imgPath = savePath + fileName; FileStream fileHtmlMedia = new FileStream(imgPath, FileMode.Create); int i = 0; while (i != (-1)) { i = fileWordMedia.ReadByte(); if (i != (-1)) { fileHtmlMedia.WriteByte((byte)i); } } fileHtmlMedia.Close(); fileWordMedia.Close(); }


Me divertí lo mismo intentando averiguar cómo hacer esto hasta que vi este hilo. Excelentes respuestas útiles chicos.

Una forma sencilla de seleccionar el ImagePart si conoce el nombre de la imagen en el paquete es verificar el Uri

ImagePart GetImagePart(WordprocessingDocument document, string imageName) { return document.MainDocumentPart.ImageParts .Where(p => p.Uri.ToString().Contains(imageName)) // or EndsWith .First(); }

Entonces puedes hacer

var imagePart = GetImagePart(document, imageName); var newImageBytes = GetNewImageBytes(): // however the image is generated or obtained using(var writer = new BinaryWriter(imagePart.GetStream())) { writer.Write(newImageBytes); }


Me encanta esta Sección, porque hay mucha mala documentación sobre este tema, y ​​después de muchas horas de intentar que las respuestas anteriores funcionen. Se me ocurrió mi propia solución.

Cómo le doy a la imagen un nombre de etiqueta:

Primero selecciono la imagen que deseo reemplazar en palabra y le doy un nombre (por ejemplo, "toReplace") luego recorro los Dibujos, selecciono la imagen con la etiqueta correcta y escribo mi propia imagen en su lugar.

System.Collections.Generic.IEnumerable<ImagePart> imageParts = doc.MainDocumentPart.ImageParts; foreach (ImagePart img in imageParts) { var uri = img.Uri; var fileName = uri.ToString().Split(''/'').Last(); var fileWordMedia = img.GetStream(FileMode.Open); string imgPath = mediaPath + fileName;//mediaPath it is folder FileStream fileHtmlMedia = new FileStream(imgPath, FileMode.Create); int i = 0; while (i != (-1)) { i = fileWordMedia.ReadByte(); if (i != (-1)) { fileHtmlMedia.WriteByte((byte)i); } } fileHtmlMedia.Close(); fileWordMedia.Close(); }


Me gustaría actualizar este hilo y agregarlo a la respuesta de Adam para beneficio de los demás.

El otro día logré hackear un código de trabajo (antes de que Adam publicara su respuesta), pero fue bastante difícil. La documentación es realmente pobre y no hay mucha información por ahí.

No sabía acerca de los elementos Inline y Run que Adam usa en su respuesta, pero el truco parece ser llegar a la propiedad de los Descendants<> y luego puedes analizar prácticamente cualquier elemento como una asignación XML normal.

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>() } }

Una vez que tienes esto, es bastante fácil buscar cosas, aunque debes resolver cómo se llama todo. Por ejemplo, <pic:nvPicPr> es Picture.NonVisualPictureProperties , etc.

Como Adam dice correctamente, el elemento que necesitas encontrar para reemplazar la imagen es el elemento Blip . Pero debe encontrar el blip correcto que corresponda a la imagen que está tratando de reemplazar.

Adán muestra una forma utilizando el elemento Inline . Acabo de bucear y busqué todos los elementos de la imagen. No estoy seguro de cuál es la forma mejor o más robusta (no sé qué tan consistente es la estructura xml entre los documentos y si esto causa un código de ruptura).

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 }

Vea el ejemplo XML de Adam para dar sentido a los diferentes elementos aquí y ver lo que estoy buscando.

El blip tiene un ID en la propiedad Embed , por ejemplo: <a:blip r:embed="rId4" cstate="print" /> , lo que esto hace es asignar el Blip a una imagen en la carpeta de Medios (puedes ver todos estas carpetas y archivos si cambia el nombre de .docx a un .zip y lo descomprime). Puede encontrar la asignación en _rels/document.xml.rels :

<Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image1.png" />

Entonces, lo que debe hacer es agregar una nueva imagen y luego señalar este punto a la identificación de su imagen recién creada:

// 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);

Supongo que esto solo huérfanos de la vieja imagen en la carpeta Media, que no es ideal, aunque quizás sea lo suficientemente inteligente como para que la basura la recoja, por así decirlo. Puede haber una mejor manera de hacerlo, pero no pude encontrarlo.

De todos modos, ahí lo tienen. Este hilo es ahora la documentación más completa sobre cómo intercambiar una imagen en cualquier lugar de la web (lo sé, pasé horas buscando). Así que espero que algunas personas lo encuentren útil.


Para obtener imágenes y copiarlas en una carpeta, puede utilizar un método más simple.

private void ReplaceImage(string tagName, string imagePath) { this.wordDoc = WordprocessingDocument.Open(this.stream, true); IEnumerable<Drawing> drawings = this.wordDoc.MainDocumentPart.Document.Descendants<Drawing>().ToList(); foreach (Drawing drawing in drawings) { DocProperties dpr = drawing.Descendants<DocProperties>().FirstOrDefault(); if (dpr != null && dpr.Name == tagName) { foreach (DocumentFormat.OpenXml.Drawing.Blip b in drawing.Descendants<DocumentFormat.OpenXml.Drawing.Blip>().ToList()) { OpenXmlPart imagePart = wordDoc.MainDocumentPart.GetPartById(b.Embed); using (var writer = new BinaryWriter(imagePart.GetStream())) { writer.Write(File.ReadAllBytes(imagePath)); } } } } }


La excelente respuesta de @Lispuesta funcionó perfectamente para mí, pero me costó un poco de investigación para averiguar cómo configurar el nombre de la imagen en Word en primer lugar. Para cualquier otra persona que no hable alemán, esta es la forma de hacerlo:

En MS Word, haga clic en la imagen y luego en la cinta de inicio, seleccione Seleccionar -> Panel de selección en la cinta para mostrar la lista de imágenes en la navegación de la derecha:

Luego puede hacer clic en el nombre / etiqueta de una imagen en el Panel de selección para cambiar su nombre:

Una vez que haya hecho eso, puede ver cómo se incorporó ese texto en el archivo Open XML utilizando la herramienta de productividad Open XML SDK 2.5:

Una vez hecho esto, extendí la solución de @Ludisposed ligeramente a un método reutilizable, y modifiqué el código para que pasar una matriz de bytes nulos provocara la eliminación de la imagen del documento:

static string RunToHTML(Run r) { string exit = ""; OpenXmlElementList list = r.ChildElements; foreach (OpenXmlElement element in list) { if (element is DocumentFormat.OpenXml.Wordprocessing.Picture) { exit += AddPictureToHtml((DocumentFormat.OpenXml.Wordprocessing.Picture)element); return exit; } }