【问题标题】:Programmatically adding Images to RTF Document以编程方式将图像添加到 RTF 文档
【发布时间】:2010-12-02 05:19:11
【问题描述】:

我正在尝试将图像添加到我正在创建的 RTF 文档中。我宁愿不使用清除剪贴板的“复制/粘贴”方法(涉及将图像粘贴到 RichTextBox 中,然后访问 .RTF 属性)(因为这会给我的最终用户带来麻烦和困惑)。

到目前为止,我的代码返回了需要插入到 RTF 文档中以打印图像的字符串。输入的图像(位于 $path)通常是 bmp 或 jpeg 格式,但在这个阶段我并不关心图像如何存储在 RTF 中,只是我可以让它工作。

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
}

但问题是这段代码不起作用,因为据我所知,字符串 str 没有正确的字符串转换以在 RTF 中工作。

编辑:我的问题是在 @"\hex " 中的 \hex 之后缺少一个空格,并且也没有从 BitConverter 的返回值中去除 "-" 字符

【问题讨论】:

    标签: c# image rtf image-formats


    【解决方案1】:

    试试这些链接

    您必须将“picwgoa”更改为“picwgoal”,将“pichgoa”更改为“pichgoal”

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

    这里有一个支持的图像格式列表

    \emfblip 图片的来源是一个 EMF(增强的图元文件)。 \pngblip 图片来源是PNG。 \jpegblip 图片来源是JPEG。 \shpict 指定 Word 97-2000 图片。这是一个目的地控制字。 \nonshpict 指定 Word 97-2000 已写入 {\pict 目标,它不会在输入时读取。此关键字是为了与其他阅读器兼容。 \macpict 图片来源为 QuickDraw。 \pmetafileN 图片的来源是一个 OS/2 元文件。 N 参数标识元文件类型。 N 值在下面的 \pmetafile 表中进行了描述。 \wmetafileN 图片的来源是 Windows 元文件。 N 参数标识元文件类型(默认为 1)。 \dibitmapN 图片的来源是一个独立于 Windows 设备的位图。 N 参数标识位图类型(必须等于 0)。来自 Windows 设备无关位图的 RTF 中包含的信息是 BITMAPINFO 结构后跟实际像素数据的串联。 \wbitmapN 图片的来源是 Windows 设备相关的位图。 N 参数标识位图类型(必须等于 0)。来自 Windows 设备相关位图的 RTF 中包含的信息是 GetBitmapBits 函数的结果。

    【讨论】:

    • 在 Winform Rtf 中无法识别高于 rtf 字符串(图像)。有人知道如何进行 Winform RTF 吗?
    【解决方案2】:

    我发现大多数 RTF 框使用以下格式:

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

    [hex/bin] 是代表图像格式的大量十六进制字符串时。这种方式既适用于 Word rtf,也适用于 RTF 框 - 所以效率更高。

    在我的计算机图像中,大小为 180x320 像素的图像被转换为​​ 2699x4799 twips,这意味着 1pix=15 twips,据我所知,在我一直在测试的 3 台计算机中是这样的,它们之间是 WinXP教授,WinXP 家庭版和 Win 7。

    【讨论】:

    • 澄清一下,“一缇是点的二十分之一”(source)。
    • Twips = Pixels / 96 * 1440 是因子 15 的来源,因为图像通常是每英寸 96 点,而缇是 1/1440 英寸。
    【解决方案3】:

    以后访问此页面的访问者(例如几天前的我)可能会发现以下链接很有用:Convert an image into WMF with .NET

    人们会发现写字板会忽略任何未以正确的 Windows MetaFile 格式存储的图像。因此,此页面上的前一个示例根本不会显示(即使它在 OpenOffice 和 Word 本身中工作得很好)。写字板将支持的格式是:

    {/pict/wmetafile8/picw[width]/pich[height]/picwgoal[scaledwidth]/pichgoal[scaledheight] [image-as-string-of-byte-hex-values]}(方括号中的术语替换为适当的数据)。

    可以按照上述链接中的步骤获取“适当的数据”。对于那些使用 python 寻找解决方案的人,这里是一个开始(我相信一些 dpi/缩放问题仍然存在)。这需要 PIL(或 Pillow)、ctypesclr (Python .NET)。使用 PIL/Pillow 并首先打开图像。在这里,我将它打开为“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)
    

    【讨论】:

      【解决方案4】:

      为此花了一天左右的时间在谷歌上搜索答案。将来自 stackoverflow 和其他来源的花絮拼凑在一起。将此图像提供给它,它将返回您需要添加到您的richtextbox.rtf 扩展名的字符串。图片宽度变化需要计算,给出公式。

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

      【讨论】:

      • VS 2019,在 WinForms RichTextbox 和 Wordpad 中运行良好...完美!
      【解决方案5】:

      我合并了来自 RRUZ 和 JiffyWhip 的答案并清理了代码。

      现在您可以将图像插入为 PNG 或 WMF。

      我还用白色替换了透明度,因为 WMF 不支持不透明度。

      private static string GetWidthAndHeight(Image image, float dpiX, float dpiY)
      {
          float width = (float)image.Width / dpiX;
          float height = (float)image.Height / dpiY;
      
          int picw = (int)(width * 2540);
          int pich = (int)(height * 2540);
      
          int picwgoal = (int)(width * 1440);
          int pichgoal = (int)(height * 1440);
      
          return "\\picw" + picw + "\\pich" + pich + "\\picwgoal" + picwgoal + "\\pichgoal" + pichgoal;
      }
      
      public static string InsertPngImage(Image image)
      {
          byte[] buffer;
      
          using (var stream = new MemoryStream())
          {
              image.Save(stream, ImageFormat.Png);
              buffer = stream.ToArray();
          }
      
          string hex = BitConverter.ToString(buffer, 0).Replace("-", string.Empty);
      
          string widthAndHeight = GetWidthAndHeight(image, image.HorizontalResolution, image.VerticalResolution);
      
          return "{\\pict\\pngblip" + widthAndHeight + " " + hex + "}";
      }
      
      [Flags]
      enum EmfToWmfBitsFlags
      {
          EmfToWmfBitsFlagsDefault = 0x00000000,
          EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
          EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
          EmfToWmfBitsFlagsNoXORClip = 0x00000004
      }
      
      private static int MM_ANISOTROPIC = 8;
      
      [DllImportAttribute("gdiplus.dll")]
      private static extern uint GdipEmfToWmfBits(IntPtr hEmf, uint bufferSize, byte[] buffer, int mappingMode, EmfToWmfBitsFlags flags);
      
      public static string InsertWmfImage(Image image)
      {
          image = ReplaceTransparency(image, Color.White);
      
          Metafile metaFile;
          float dpiX;
          float dpiY;
      
          using (Graphics graphics = Graphics.FromImage(image))
          {
              IntPtr hdc = graphics.GetHdc();
              metaFile = new Metafile(hdc, EmfType.EmfOnly);
              graphics.ReleaseHdc(hdc);
          }
      
          using (Graphics graphics = Graphics.FromImage(metaFile))
          {
              graphics.DrawImage(image, 0, 0, image.Width, image.Height);
              dpiX = graphics.DpiX;
              dpiY = graphics.DpiY;
          }
      
          IntPtr hEmf = metaFile.GetHenhmetafile();
      
          uint bufferSize = GdipEmfToWmfBits(hEmf, 0, null, MM_ANISOTROPIC, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
      
          byte[] buffer = new byte[bufferSize];
      
          uint convertedSize = GdipEmfToWmfBits(hEmf, bufferSize, buffer, MM_ANISOTROPIC, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
      
          string hex = BitConverter.ToString(buffer, 0).Replace("-", string.Empty);
      
          string widthAndHeight = GetWidthAndHeight(image, dpiX, dpiY);
      
          return "{\\pict\\wmetafile8" + widthAndHeight + " " + hex + "}";
      }
      
      private static Image ReplaceTransparency(Image image, Color background)
      {
          Bitmap bitmap = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb);
      
          using (Graphics graphics = Graphics.FromImage(bitmap))
          {
              graphics.Clear(background);
              graphics.CompositingMode = CompositingMode.SourceOver;
              graphics.DrawImage(image, 0, 0, image.Width, image.Height);
          }
      
          return bitmap;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多