Программное добавление изображений в документ RTF
-
18-09-2019 - |
Вопрос
Я пытаюсь добавить изображение в документ 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.
Решение
попробуйте эти ссылки
- Спецификация расширенного текстового формата (RTF), версия 1.6
- Как вставить изображение в RichTextBox?
- Вставить изображение в документ rtf
вы должны изменить «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 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.
Другие советы
Потратил день или около того на поиск ответов на этот вопрос.Собрал воедино самые интересные факты со всего 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;
}
Позже посетители этой страницы (такие, как я несколько дней назад) могут найти полезной следующую ссылку: Преобразование изображения в WMF с помощью .NET
Вы обнаружите, что WordPad игнорирует любое изображение, не сохраненное в соответствующем формате метафайла Windows.Таким образом, предыдущий пример на этой странице вообще не будет отображаться (хотя он отлично работает в OpenOffice и Word).WordPad БУДЕТ поддерживать следующий формат:
{/pict/wmetafile8/picw[ширина]/pich[высота]/picwgoal[scaledwidth]/pichgoal[scaledheight] [изображение-как строка-байтовых-шестнадцатеричных значений]} (с терминами в квадратных скобках, замененными на соответствующие данные).
Получить «соответствующие данные» можно, выполнив процедуры, описанные в приведенной выше ссылке.Для тех, кто ищет решение на Python, вот его начало (я считаю, что некоторые проблемы с разрешением/масштабированием остаются).Это требует ПИЛ(или Подушка), cтипы, и 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)
Я обнаружил, что большинство блоков RTF используют следующий формат:
{\object\objemb{\*\objclass Paint.Picture}\objw2699\objh4799{\*\objdata [hex/bin]}}
Когда [hex/bin]
— большое количество шестнадцатеричных строк, представляющих формат изображения.Этот способ работает как для Word rtf, так и для коробки RTF, поэтому он более эффективен.
У меня на компьютере изображение размером 180х320 пикселей конвертировано в твипы 2699х4799, что значит 1пикс=15 твипов, насколько я вижу, так есть на 3-х компьютерах, которые я тестировал, между ними WinXP prof, WinXP дома и Вин 7.