C# 以编程方式将图像添加到 RTF 文档
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1490734/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Programmatically adding Images to RTF Document
提问by
I am trying to add a image to a RTF document which I am creating. I would prefer to not use 'copy/paste' methods (that involve pasting the image within a RichTextBox and then accessing the .RTF property) which purge the clipboard (as this will be a bother and confusion for my end users).
我正在尝试将图像添加到我正在创建的 RTF 文档中。我宁愿不使用清除剪贴板的“复制/粘贴”方法(包括将图像粘贴到 RichTextBox 中,然后访问 .RTF 属性)(因为这对我的最终用户来说会是一个麻烦和困惑)。
The code I have so far returns the string that needs to be inserted into the RTF document to print the image. The inputted image (located at $path) is usually in bmp or jpeg format, but at this stage I am not concerned with how the image is stored within the RTF only that i can get it to work.
到目前为止,我的代码返回需要插入到 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
}
However the problem is that this code does not work because as far as I can tell, the string str does not have the correct string conversion to work within the RTF.
然而问题是这段代码不起作用,因为据我所知,字符串 str 没有正确的字符串转换来在 RTF 中工作。
Edit: My problem was missing a space after the \hex in @"\hex " and also not stripping out the "-" characters from the returned value of the BitConverter
编辑:我的问题是在 @"\hex " 中的 \hex 之后缺少一个空格,并且也没有从 BitConverter 的返回值中去除“-”字符
采纳答案by RRUZ
try these links
试试这些链接
- Rich Text Format (RTF) Specification, version 1.6
- How can I insert an image into a RichTextBox?
- Insert Image into rtf document
you must change "picwgoa" to "picwgoal" and "pichgoa" to "pichgoal"
您必须将“picwgoa”更改为“picwgoal”并将“pichgoa”更改为“pichgoal”
string mpic = @"{\pict\pngblip\picw" +
img.Width.ToString() + @"\pich" + img.Height.ToString() +
@"\picwgoal" + width.ToString() + @"\pichgoal" + height.ToString() +
@"\bin " + str + "}";
Here you have a list of the supported image formats
这里有支持的图像格式列表
\emfblip Source of the picture is an EMF (enhanced metafile). \pngblip Source of the picture is a PNG. \jpegblip Source of the picture is a JPEG. \shppict Specifies a Word 97-2000 picture. This is a destination control word. \nonshppict Specifies that Word 97-2000 has written a {\pict destination that it will not read on input. This keyword is for compatibility with other readers. \macpict Source of the picture is QuickDraw. \pmmetafileN Source of the picture is an OS/2 metafile. The N argument identifies the metafile type. The N values are described in the \pmmetafile table below. \wmetafileN Source of the picture is a Windows metafile. The N argument identifies the metafile type (the default is 1). \dibitmapN Source of the picture is a Windows device-independent bitmap. The N argument identifies the bitmap type (must equal 0).The information to be included in RTF from a Windows device-independent bitmap is the concatenation of the BITMAPINFO structure followed by the actual pixel data. \wbitmapN Source of the picture is a Windows device-dependent bitmap. The N argument identifies the bitmap type (must equal 0).The information to be included in RTF from a Windows device-dependent bitmap is the result of the GetBitmapBits function.
回答by Xerix
I found that most of RTF boxes using the following format:
我发现大多数 RTF 框使用以下格式:
{\object\objemb{\*\objclass Paint.Picture}\objw2699\objh4799{\*\objdata [hex/bin]}}
When [hex/bin]
is large amount of hex strings representing the image format. This way working both for Word rtf, and both for RTF box - so it is more efficient.
何时[hex/bin]
是大量表示图像格式的十六进制字符串。这种方式既适用于 Word rtf,也适用于 RTF 框 - 因此效率更高。
In my computer image in a size of 180x320 pixels been converted to 2699x4799 twips, which means 1pix=15 twips, as far as I can see, it is like this in 3 computers that I've been testing, between them WinXP prof, WinXP home and Win 7.
在我的电脑图像中,180x320像素大小被转换为2699x4799 twips,即1pix=15 twips,据我所知,在我测试过的3台电脑上都是这样的,它们之间是WinXP prof,WinXP家和Win 7。
回答by Darren Ringer
Later visitors to this page (such as I was a few days ago) may find the following link useful: Convert an image into WMF with .NET
稍后访问此页面的访问者(例如几天前的我)可能会发现以下链接很有用:使用 .NET 将图像转换为 WMF
One will find that WordPad ignores any image not stored in the proper Windows MetaFile format. Thus, the previous example on this page will not show up at all (Even though it works just fine in OpenOffice and Word itself). The format that WordPad WILL support is:
人们会发现写字板会忽略任何未以正确的 Windows 元文件格式存储的图像。因此,此页面上的前一个示例根本不会显示(即使它在 OpenOffice 和 Word 本身中运行良好)。写字板将支持的格式是:
{/pict/wmetafile8/picw[width]/pich[height]/picwgoal[scaledwidth]/pichgoal[scaledheight] [image-as-string-of-byte-hex-values]} (with terms in square brackets replaced with the appropriate data).
{/pict/wmetafile8/picw[width]/pict[height]/picwgoal[scaledwidth]/pichgoal[scaledheight] [image-as-string-of-byte-hex-values]}(方括号中的术语替换为适当的数据)。
Getting the 'appropriate data' can be done by following the procedures in the above link. For those with python looking for a solution, here is the start of one (I believe some dpi/scaling issues remain). This requires PIL(or Pillow), ctypes, and clr (Python .NET). Use PIL/Pillow and open up the image first. Here I have it open as "canv":
可以按照上述链接中的步骤获取“适当的数据”。对于那些使用 python 寻找解决方案的人来说,这是一个开始(我相信一些 dpi/缩放问题仍然存在)。这需要PIL(或Pillow)、ctypes和clr (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)
回答by JiffyWhip
Spent a day or so googling answers for this. Pieced together tidbits from all over stackoverflow and other sources. Feed this an image, it'll return the string you need to add to your richtextbox.rtf extension. The image width changes and needs to be calculated, the formula is given.
为此花了一天左右的时间在谷歌上搜索答案。将来自 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;
}
回答by Jinjinov
I merged answers from RRUZ and JiffyWhip and cleaned up the code.
我合并了 RRUZ 和 JiffyWhip 的答案并清理了代码。
Now you can insert an image as PNG or as WMF.
现在您可以将图像插入为 PNG 或 WMF。
I also replaced transparency with white color, because WMF doesn't support opacity.
我还用白色替换了透明度,因为 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;
}