Agregando imágenes programáticamente al documento RTF

Estoy tratando de agregar una imagen a un documento RTF que estoy creando. Preferiría no usar métodos de “copiar / pegar” (que implican pegar la imagen dentro de un RichTextBox y luego acceder a la propiedad .RTF) que purga el portapapeles (ya que esto será una molestia y confusión para mis usuarios finales).

El código que tengo hasta ahora devuelve la cadena que debe insertarse en el documento RTF para imprimir la imagen. La imagen ingresada (ubicada en $ path) suele estar en formato bmp o jpeg, pero en esta etapa no me preocupa cómo se almacena la imagen dentro del RTF, solo que puedo hacer que funcione.

public string GetImage(string path, int width, int height) { MemoryStream stream = new MemoryStream(); string newPath = Path.Combine(Environment.CurrentDirectory, path); Image img = Image.FromFile(newPath); img.Save(stream, System.Drawing.Imaging.ImageFormat.Png); byte [] bytes = stream.ToArray(); string str = BitConverter.ToString(bytes, 0).Replace("-", string.Empty); //string str = System.Text.Encoding.UTF8.GetString(bytes); string mpic = @"{\pict\pngblip\picw" + img.Width.ToString() + @"\pich" + img.Height.ToString() + @"\picwgoa" + width.ToString() + @"\pichgoa" + height.ToString() + @"\hex " + str + "}"; return mpic } 

Sin embargo, el problema es que este código no funciona porque, por lo que puedo decir, la cadena str no tiene la conversión de cadena correcta para trabajar dentro del RTF.

Editar: A mi problema le faltaba un espacio después del \ hex en @ “\ hex” y tampoco eliminaba los caracteres “-” del valor devuelto del BitConverter

prueba estos enlaces

  • Especificación de formato de texto enriquecido (RTF), versión 1.6
  • ¿Cómo puedo insertar una imagen en un RichTextBox?
  • Insertar imagen en el documento rtf

debes cambiar “picwgoa” por “picwgoal” y “pichgoa” por “pichgoal”

 string mpic = @"{\pict\pngblip\picw" + img.Width.ToString() + @"\pich" + img.Height.ToString() + @"\picwgoal" + width.ToString() + @"\pichgoal" + height.ToString() + @"\bin " + str + "}"; 

Aquí tienes una lista de los formatos de imagen compatibles

 \ emfblip La fuente de la imagen es un EMF (metarchivo mejorado).
 \ pngblip La fuente de la imagen es un PNG.
 \ jpegblip La fuente de la imagen es un JPEG.
 \ shppict Especifica una imagen de Word 97-2000.  Esta es una palabra de control de destino.
 \ nonshppict Especifica que Word 97-2000 ha escrito un destino {\ pict que no se leerá en la entrada.  Esta palabra clave es para compatibilidad con otros lectores.
 \ macpict La fuente de la imagen es QuickDraw.
 \ pmmetafileN La fuente de la imagen es un metarchivo OS / 2.  El argumento N identifica el tipo de metarchivo.  Los N valores se describen en la tabla \ pmmetafile a continuación.
 \ wmetafileN El origen de la imagen es un metarchivo de Windows.  El argumento N identifica el tipo de metarchivo (el valor predeterminado es 1).
 \ dibitmapN La fuente de la imagen es un bitmap independiente del dispositivo de Windows.  El argumento N identifica el tipo de bitmap (debe ser igual a 0). La información que debe incluirse en RTF desde un bitmap independiente de dispositivos de Windows es la concatenación de la estructura BITMAPINFO seguida de los datos de píxeles reales.    
 \ wbitmapN La fuente de la imagen es un bitmap dependiente del dispositivo de Windows.  El argumento N identifica el tipo de bitmap (debe ser igual a 0). La información que se incluirá en RTF a partir de un bitmap dependiente del dispositivo de Windows es el resultado de la función GetBitmapBits.

Pasé un día o así google buscando respuestas para esto. Pedazos recogidos de todo el stackoverflow y otras fonts. Alimenta esta imagen, devolverá la cadena que necesitas agregar a tu extensión richtextbox.rtf. El ancho de la imagen cambia y debe calcularse, se da la fórmula.

  // RTF Image Format // {\pict\wmetafile8\picw[A]\pich[B]\picwgoal[C]\pichgoal[D] // // A = (Image Width in Pixels / Graphics.DpiX) * 2540 // // B = (Image Height in Pixels / Graphics.DpiX) * 2540 // // C = (Image Width in Pixels / Graphics.DpiX) * 1440 // // D = (Image Height in Pixels / Graphics.DpiX) * 1440 [Flags] enum EmfToWmfBitsFlags { EmfToWmfBitsFlagsDefault = 0x00000000, EmfToWmfBitsFlagsEmbedEmf = 0x00000001, EmfToWmfBitsFlagsIncludePlaceable = 0x00000002, EmfToWmfBitsFlagsNoXORClip = 0x00000004 } const int MM_ISOTROPIC = 7; const int MM_ANISOTROPIC = 8; [DllImport("gdiplus.dll")] private static extern uint GdipEmfToWmfBits(IntPtr _hEmf, uint _bufferSize, byte[] _buffer, int _mappingMode, EmfToWmfBitsFlags _flags); [DllImport("gdi32.dll")] private static extern IntPtr SetMetaFileBitsEx(uint _bufferSize, byte[] _buffer); [DllImport("gdi32.dll")] private static extern IntPtr CopyMetaFile(IntPtr hWmf, string filename); [DllImport("gdi32.dll")] private static extern bool DeleteMetaFile(IntPtr hWmf); [DllImport("gdi32.dll")] private static extern bool DeleteEnhMetaFile(IntPtr hEmf); public static string GetEmbedImageString(Bitmap image) { Metafile metafile = null; float dpiX; float dpiY; using (Graphics g = Graphics.FromImage (image)) { IntPtr hDC = g.GetHdc (); metafile = new Metafile (hDC, EmfType.EmfOnly); g.ReleaseHdc (hDC); } using (Graphics g = Graphics.FromImage (metafile)) { g.DrawImage (image, 0, 0); dpiX = g.DpiX; dpiY = g.DpiY; } IntPtr _hEmf = metafile.GetHenhmetafile (); uint _bufferSize = GdipEmfToWmfBits (_hEmf, 0, null, MM_ANISOTROPIC, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault); byte[] _buffer = new byte[_bufferSize]; GdipEmfToWmfBits (_hEmf, _bufferSize, _buffer, MM_ANISOTROPIC, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault); IntPtr hmf = SetMetaFileBitsEx (_bufferSize, _buffer); string tempfile = Path.GetTempFileName (); CopyMetaFile (hmf, tempfile); DeleteMetaFile (hmf); DeleteEnhMetaFile (_hEmf); var stream = new MemoryStream (); byte[] data = File.ReadAllBytes (tempfile); //File.Delete (tempfile); int count = data.Length; stream.Write (data, 0, count); string proto = @"{\rtf1{\pict\wmetafile8\picw" + (int)( ( (float)image.Width / dpiX ) * 2540 ) + @"\pich" + (int)( ( (float)image.Height / dpiY ) * 2540 ) + @"\picwgoal" + (int)( ( (float)image.Width / dpiX ) * 1440 ) + @"\pichgoal" + (int)( ( (float)image.Height / dpiY ) * 1440 ) + " " + BitConverter.ToString(stream.ToArray()).Replace("-", "") + "}}"; return proto; } 

Más tarde, los visitantes de esta página (como yo lo fui hace unos días) pueden encontrar útil el siguiente enlace: Convertir una imagen en WMF con .NET

Uno encontrará que WordPad ignora cualquier imagen que no esté almacenada en el formato correcto de MetaFile de Windows. Por lo tanto, el ejemplo anterior en esta página no se mostrará en absoluto (a pesar de que funciona bien en OpenOffice y Word en sí). El formato que WordPad SOPORTE es:

{/ pict / wmetafile8 / picw [width] / pich [height] / picwgoal [scale] / pichgoal [scale] [image-as-string-of-byte-hex-values]} (con los términos entre corchetes reemplazados por datos apropiados).

Obtener los ‘datos apropiados’ se puede hacer siguiendo los procedimientos en el enlace de arriba. Para aquellos con python que buscan una solución, aquí está el comienzo de una (creo que quedan algunos problemas de dpi / escalado). Esto requiere PIL (o Pillow ), ctypes y clr (Python .NET) . Use PIL / Pillow y abra la imagen primero. Aquí lo tengo abierto como “canv”:

 from ctypes import * import clr clr.AddReference("System.IO") clr.AddReference("System.Drawing") from System import IntPtr from System.Drawing import SolidBrush from System.Drawing import Color from System.Drawing import Imaging from System.Drawing import Graphics from System.IO import FileStream from System.IO import FileMode from System.IO import MemoryStream from System.IO import File def byte_to_hex(bytefile): acc = '' b = bytefile.read(1) while b: acc+=("%02X" % ord(b)) b = bytefile.read(1) return acc.strip() #... in here is some code where 'canv' is created as the PIL image object, and #... 'template' is defined as a string with placeholders for picw, pich, #... picwgoal, pichgoal, and the image data mfstream = MemoryStream() offscrDC = Graphics.FromHwndInternal(IntPtr.Zero) imgptr = offscrDC.GetHdc() mfile = Imaging.Metafile(mfstream, imgptr, Imaging.EmfType.EmfOnly) gfx = Graphics.FromImage(mfile) width,height = canv.size pixels = canv.load() for x in range(width): for y in range(height): _r,_g,_b = pixels[x, y] c = Color.FromArgb(_r, _g, _b) brush = SolidBrush(c) gfx.FillRectangle(brush, x, y, 1, 1) gfx.Dispose() offscrDC.ReleaseHdc() _hEmf = mfile.GetHenhmetafile() GdipEmfToWmfBits = windll.gdiplus.GdipEmfToWmfBits _bufferSize = GdipEmfToWmfBits( int(str(_hEmf)), c_uint(0), None, c_int(8), # MM_ANISOTROPIC c_uint(0x00000000)) # Default flags _buffer = c_int * _bufferSize _buffer = _buffer(*[0 for x in range(_bufferSize)]) GdipEmfToWmfBits( int(str(_hEmf)), c_uint(_bufferSize), _buffer, c_int(8), # MM_ANISOTROPIC c_uint(0x00000000) ) # Default flags hmf = windll.gdi32.SetMetaFileBitsEx(c_uint(_bufferSize), _buffer) windll.gdi32.CopyMetaFileA(int(str(hmf)), "temp.wmf") windll.gdi32.DeleteMetaFile(int(str(hmf))) windll.gdi32.DeleteEnhMetaFile(int(str(_hEmf))) mfstream.Close() imgstr = open("temp.wmf", 'rb') imgstr = byte_to_hex(imgstr) with open('script-out.rtf','wb') as outf: template = template % (str(_cx),str(_cy),str(15*_cx),str(15*_cy),imgstr) outf.write(template) 

Encontré que la mayoría de las cajas RTF usan el siguiente formato:

 {\object\objemb{\*\objclass Paint.Picture}\objw2699\objh4799{\*\objdata [hex/bin]}} 

Cuando [hex/bin] es una gran cantidad de cadenas hexagonales que representan el formato de la imagen. De esta forma, funciona tanto para Word rtf como para el cuadro RTF, por lo que es más eficiente.

En mi imagen de computadora en un tamaño de 180×320 píxeles se han convertido a 2699×4799 twips, lo que significa 1pix = 15 twips, por lo que puedo ver, es así en 3 computadoras que he estado probando, entre ellos WinXP prof, WinXP casa y Win 7.