How to Insert an Image into a Richtextbox

How can I insert an image into a RichTextBox?

The most straightforward way would be to modify the RTF code to insert the picture yourself.

In RTF, a picture is defined like this:

'{' \pict (brdr? & shading? & picttype & pictsize & metafileinfo?) data '}'
A question mark indicates the control word is optional.
"data" is simply the content of the file in hex format. If you want to use binary, use the \bin control word.

For instance:

{\pict\pngblip\picw10449\pich3280\picwgoal5924\pichgoal1860 hex data}
{\pict\pngblip\picw10449\pich3280\picwgoal5924\pichgoal1860\bin binary data}

\pict = starts a picture group,
\pngblip = png picture
\picwX = width of the picture (X is the pixel value)
\pichX = height of the picture
\picwgoalX = desired width of the picture in twips

So, to insert a picture, you just need to open your picture, convert the data to hex, load these data into a string and add the RTF codes around it to define a RTF picture. Now, you have a self contained string with picture data which you can insert in the RTF code of a document. Don't forget the closing "}"

Next, you get the RTF code from your RichTextBox (rtbBox.Rtf), insert the picture at the proper location, and set the code of rtbBox.Rtf

One issue you may run into is that .NET RTB does not have a very good support of the RTF standard.

I have just made a small application* which allows you to quickly test some RTF code inside a RTB and see how it handles it. You can download it here:
RTB tester (http://your-translations.com/toys).

You can paste some RTF content (from Word, for instance) into the left RTF box and click on the "Show RTF codes" to display the RTF codes in the right RTF box, or you can paste RTF code in the right RTB and click on "Apply RTF codes" to see the results on the left hand side.

You can of course edit the codes as you like, which makes it quite convenient for testing whether or not the RichTextBox supports the commands you need, or learn how to use the RTF control words.

You can download a full specification for RTF online.


NB It's just a little thing I slapped together in 5 minutes, so I didn't implement file open or save, drag and drop, or other civilized stuff.

Adding an image inside a RichTextBox WPF

You have to bind Height and Width of Image to ActualHeight and ActualWidth of RichTextBox.

<Image Source="C:\Temp\Penguins.jpg"
Width="{Binding ActualWidth, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=RichTextBox}}"
Height="{Binding ActualHeight, RelativeSource={RelativeSource
Mode=FindAncestor, AncestorType=RichTextBox}}"/>

UPDATE

There seems internal padding of RichTextBox. You can set that to negative value to remove that padding.

<RichTexBox Padding="-5,-2,-5,-2"> // It reads Left, Top, Right, Bottom
....
</RichTexBox>

Change -5,-2,-5,-2 to desired value which seems fit for you.

How do I embed an Image into a RichTextBox?

I got a solution for the problem. I am not sure its the right way, but still this solution is working for me. Hope it may help others.

The solution is to iterate through all image tags in flow document and copy the images to a repository folder which is portable. Then load the images from that repository folder next time to show the images back in to richtextbox.

foreach (Block block in rtb.Blocks)
{
if (block is Paragraph)
{
Paragraph paragraph = (Paragraph)block;
foreach (Inline inline in paragraph.Inlines)
{
if (inline is InlineUIContainer)
{
InlineUIContainer uiContainer = (InlineUIContainer)inline;
if (uiContainer.Child is Image)
{
Image image = (Image)uiContainer.Child;
//Copy Image source to another repository folder and save the path.
}
}
}
}

how to add image in Rich TextBox in VB.NET

You can skip all rtf stuff and work only with images and text. For example, if you want to have image and then some text, you could load that image into clipboard and then paste it into richTextBox:

Image img = Image.FromFile(filename); //if you want to load it from file...
Clipboard.SetImage(img);
richTextBox1.Paste();
richTextBox1.AppendText("your text");

Insert Image at Cursor Position in Rich Text box

I've prepared a fully functional example for you using the solution posted here exploiting the RTF power.

As Hans Passant wrote: the solution is quite tricky and there are some valid alternatives to achieve it.

BTW, this is your code (rewrited):

private bool CheckIfImage(string filename)
{
var valids = new[] {".jpeg", ".jpg", ".png", ".ico", ".gif", ".bmp", ".emp", ".wmf", ".tiff"};
return valids.Contains(System.IO.Path.GetExtension(filename));
}

private void openFileDialog2_FileOk(object sender, CancelEventArgs e)
{
if (CheckIfImage(openFileDialog2.FileName.ToLower()) == true)
embedImage(Image.FromFile(openFileDialog2.FileName));
else
MessageBox.Show("Invalid Image File Selected");
}

This is the embedImage method:

    private void embedImage(Image img)
{
var rtf = new StringBuilder();

// Append the RTF header
rtf.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1033");
// Create the font table using the RichTextBox's current font and append
// it to the RTF string
rtf.Append(GetFontTable(this.Font));
// Create the image control string and append it to the RTF string
rtf.Append(GetImagePrefix(img));
// Create the Windows Metafile and append its bytes in HEX format
rtf.Append(getRtfImage(img));
// Close the RTF image control string
rtf.Append(@"}");
richTextBox1.SelectedRtf = rtf.ToString();
}

Here there are all the necessary methods:

    private enum EmfToWmfBitsFlags
{
EmfToWmfBitsFlagsDefault = 0x00000000,
EmfToWmfBitsFlagsEmbedEmf = 0x00000001,
EmfToWmfBitsFlagsIncludePlaceable = 0x00000002,
EmfToWmfBitsFlagsNoXORClip = 0x00000004
};

private struct RtfFontFamilyDef
{
public const string Unknown = @"\fnil";
public const string Roman = @"\froman";
public const string Swiss = @"\fswiss";
public const string Modern = @"\fmodern";
public const string Script = @"\fscript";
public const string Decor = @"\fdecor";
public const string Technical = @"\ftech";
public const string BiDirect = @"\fbidi";
}

[DllImport("gdiplus.dll")]
private static extern uint GdipEmfToWmfBits(IntPtr _hEmf,
uint _bufferSize, byte[] _buffer,
int _mappingMode, EmfToWmfBitsFlags _flags);


private string GetFontTable(Font font)
{
var fontTable = new StringBuilder();
// Append table control string
fontTable.Append(@"{\fonttbl{\f0");
fontTable.Append(@"\");
var rtfFontFamily = new HybridDictionary();
rtfFontFamily.Add(FontFamily.GenericMonospace.Name, RtfFontFamilyDef.Modern);
rtfFontFamily.Add(FontFamily.GenericSansSerif, RtfFontFamilyDef.Swiss);
rtfFontFamily.Add(FontFamily.GenericSerif, RtfFontFamilyDef.Roman);
rtfFontFamily.Add("UNKNOWN", RtfFontFamilyDef.Unknown);

// If the font's family corresponds to an RTF family, append the
// RTF family name, else, append the RTF for unknown font family.
fontTable.Append(rtfFontFamily.Contains(font.FontFamily.Name) ? rtfFontFamily[font.FontFamily.Name] : rtfFontFamily["UNKNOWN"]);
// \fcharset specifies the character set of a font in the font table.
// 0 is for ANSI.
fontTable.Append(@"\fcharset0 ");
// Append the name of the font
fontTable.Append(font.Name);
// Close control string
fontTable.Append(@";}}");
return fontTable.ToString();
}

private string GetImagePrefix(Image _image)
{
float xDpi, yDpi;
var rtf = new StringBuilder();
using (Graphics graphics = CreateGraphics())
{
xDpi = graphics.DpiX;
yDpi = graphics.DpiY;
}
// Calculate the current width of the image in (0.01)mm
var picw = (int)Math.Round((_image.Width / xDpi) * 2540);
// Calculate the current height of the image in (0.01)mm
var pich = (int)Math.Round((_image.Height / yDpi) * 2540);
// Calculate the target width of the image in twips
var picwgoal = (int)Math.Round((_image.Width / xDpi) * 1440);
// Calculate the target height of the image in twips
var pichgoal = (int)Math.Round((_image.Height / yDpi) * 1440);
// Append values to RTF string
rtf.Append(@"{\pict\wmetafile8");
rtf.Append(@"\picw");
rtf.Append(picw);
rtf.Append(@"\pich");
rtf.Append(pich);
rtf.Append(@"\picwgoal");
rtf.Append(picwgoal);
rtf.Append(@"\pichgoal");
rtf.Append(pichgoal);
rtf.Append(" ");

return rtf.ToString();
}

private string getRtfImage(Image image)
{
// Used to store the enhanced metafile
MemoryStream stream = null;
// Used to create the metafile and draw the image
Graphics graphics = null;
// The enhanced metafile
Metafile metaFile = null;
try
{
var rtf = new StringBuilder();
stream = new MemoryStream();
// Get a graphics context from the RichTextBox
using (graphics = CreateGraphics())
{
// Get the device context from the graphics context
IntPtr hdc = graphics.GetHdc();
// Create a new Enhanced Metafile from the device context
metaFile = new Metafile(stream, hdc);
// Release the device context
graphics.ReleaseHdc(hdc);
}

// Get a graphics context from the Enhanced Metafile
using (graphics = Graphics.FromImage(metaFile))
{
// Draw the image on the Enhanced Metafile
graphics.DrawImage(image, new Rectangle(0, 0, image.Width, image.Height));
}

// Get the handle of the Enhanced Metafile
IntPtr hEmf = metaFile.GetHenhmetafile();
// A call to EmfToWmfBits with a null buffer return the size of the
// buffer need to store the WMF bits. Use this to get the buffer
// size.
uint bufferSize = GdipEmfToWmfBits(hEmf, 0, null, 8, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
// Create an array to hold the bits
var buffer = new byte[bufferSize];
// A call to EmfToWmfBits with a valid buffer copies the bits into the
// buffer an returns the number of bits in the WMF.
uint _convertedSize = GdipEmfToWmfBits(hEmf, bufferSize, buffer, 8, EmfToWmfBitsFlags.EmfToWmfBitsFlagsDefault);
// Append the bits to the RTF string
foreach (byte t in buffer)
{
rtf.Append(String.Format("{0:X2}", t));
}
return rtf.ToString();
}
finally
{
if (graphics != null)
graphics.Dispose();
if (metaFile != null)
metaFile.Dispose();
if (stream != null)
stream.Close();
}
}

I suggest you to wrap this into your own UserControl.

WPF RichTextBox Insert Image and Undo Operation

I found the solution. I had to copy class WpfLoad from RichTextBox source code. This code save Package as Stream with Image as Content and Uri as Source of the document.

Because class WpfPayload is internal I don't have access to this class. I need to create my own.

Below is the source of the class WpfPayLoad.

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Markup;
using System.Windows.Media.Imaging;

namespace YATE
{
internal class WpfPayload
{
private const string XamlPayloadDirectory = "/Xaml"; //
private const string XamlEntryName = "/Document.xaml"; //
private const string XamlContentType = "application/vnd.ms-wpf.xaml+xml";
private const string XamlImageName = "/Image"; //
private const string XamlRelationshipFromPackageToEntryPart = "http://schemas.microsoft.com/wpf/2005/10/xaml/entry";
private const string XamlRelationshipFromXamlPartToComponentPart = "http://schemas.microsoft.com/wpf/2005/10/xaml/component";

internal const string ImageBmpContentType = "image/bmp";
private const string ImageGifContentType = "image/gif";
private const string ImageJpegContentType = "image/jpeg";
private const string ImageTiffContentType = "image/tiff";
private const string ImagePngContentType = "image/png";

private const string ImageBmpFileExtension = ".bmp";
private const string ImageGifFileExtension = ".gif";
private const string ImageJpegFileExtension = ".jpeg";
private const string ImageJpgFileExtension = ".jpg";
private const string ImageTiffFileExtension = ".tiff";
private const string ImagePngFileExtension = ".png";

Package _package = null;

private static BitmapEncoder GetBitmapEncoder(string imageContentType)
{
BitmapEncoder bitmapEncoder;

switch (imageContentType)
{
case ImageBmpContentType:
bitmapEncoder = new BmpBitmapEncoder();
break;
case ImageGifContentType:
bitmapEncoder = new GifBitmapEncoder();
break;
case ImageJpegContentType:
bitmapEncoder = new JpegBitmapEncoder();
//

break;
case ImageTiffContentType:
bitmapEncoder = new TiffBitmapEncoder();
break;
case ImagePngContentType:
bitmapEncoder = new PngBitmapEncoder();
break;
default:
bitmapEncoder = null;
break;
}
return bitmapEncoder;
}

// Returns a file extension corresponding to a given imageContentType
private static string GetImageFileExtension(string imageContentType)
{
string imageFileExtension;
switch (imageContentType)
{
case ImageBmpContentType:
imageFileExtension = ImageBmpFileExtension;
break;
case ImageGifContentType:
imageFileExtension = ImageGifFileExtension;
break;
case ImageJpegContentType:
imageFileExtension = ImageJpegFileExtension;
break;
case ImageTiffContentType:
imageFileExtension = ImageTiffFileExtension;
break;
case ImagePngContentType:
imageFileExtension = ImagePngFileExtension;
break;
default:
imageFileExtension = null;
break;
}
return imageFileExtension;
}

WpfPayload(Package p = null)
{
this._package = p;
}

private Package CreatePackage(Stream stream)
{
_package = Package.Open(stream, FileMode.Create, FileAccess.ReadWrite);
return _package;
}


// Generates a image part Uri for the given image index
private static string GetImageName(int imageIndex, string imageContentType)
{
string imageFileExtension = GetImageFileExtension(imageContentType);
return XamlImageName + (imageIndex + 1) + imageFileExtension;
}

// Generates a relative URL for using from within xaml Image tag.
private static string GetImageReference(string imageName)
{
return "." + imageName; // imageName is supposed to be created by GetImageName method
}

private PackagePart CreateWpfEntryPart()
{
// Define an entry part uri
Uri entryPartUri = new Uri(XamlPayloadDirectory + XamlEntryName, UriKind.Relative);

// Create the main xaml part
PackagePart part = _package.CreatePart(entryPartUri, XamlContentType, CompressionOption.Normal);
// Compression is turned off in this mode.
//NotCompressed = -1,
// Compression is optimized for a resonable compromise between size and performance.
//Normal = 0,
// Compression is optimized for size.
//Maximum = 1,
// Compression is optimized for performance.
//Fast = 2 ,
// Compression is optimized for super performance.
//SuperFast = 3,

// Create the relationship referring to the entry part
PackageRelationship entryRelationship = _package.CreateRelationship(entryPartUri, TargetMode.Internal, XamlRelationshipFromPackageToEntryPart);

return part;
}

private void CreateImagePart(PackagePart sourcePart, BitmapSource imageSource, string imageContentType, int imageIndex)
{
// Generate a new unique image part name
string imagePartUriString = GetImageName(imageIndex, imageContentType);

// Define an image part uri
Uri imagePartUri = new Uri(XamlPayloadDirectory + imagePartUriString, UriKind.Relative);

// Create a part for the image
PackagePart imagePart = _package.CreatePart(imagePartUri, imageContentType, CompressionOption.NotCompressed);

// Create the relationship referring from the enrty part to the image part
PackageRelationship componentRelationship = sourcePart.CreateRelationship(imagePartUri, TargetMode.Internal, XamlRelationshipFromXamlPartToComponentPart);

// Encode the image data
BitmapEncoder bitmapEncoder = GetBitmapEncoder(imageContentType);
bitmapEncoder.Frames.Add(BitmapFrame.Create(imageSource));

// Save encoded image data into the image part in the package
Stream imageStream = imagePart.GetStream();
using (imageStream)
{
bitmapEncoder.Save(imageStream);
}
}


internal PackagePart GetWpfEntryPart()
{
PackagePart wpfEntryPart = null;

// Find a relationship to entry part
PackageRelationshipCollection entryPartRelationships = _package.GetRelationshipsByType(XamlRelationshipFromPackageToEntryPart);
PackageRelationship entryPartRelationship = null;
foreach (PackageRelationship packageRelationship in entryPartRelationships)
{
entryPartRelationship = packageRelationship;
break;
}

// Get a part referred by this relationship
if (entryPartRelationship != null)
{
// Get entry part uri
Uri entryPartUri = entryPartRelationship.TargetUri;

// Get the enrty part
wpfEntryPart = _package.GetPart(entryPartUri);
}

return wpfEntryPart;
}



[System.Security.SecurityCritical]
internal static Stream SaveImage(BitmapSource bitmapSource, string imageContentType)
{
MemoryStream stream = new MemoryStream();
// Create the wpf package in the stream
WpfPayload wpfPayload = new WpfPayload();
using (wpfPayload.CreatePackage(stream))
{
PackagePart xamlEntryPart = wpfPayload.CreateWpfEntryPart();

Stream xamlPartStream = xamlEntryPart.GetStream();
using (xamlPartStream)
{
int imageIndex = 0;
string imageReference = GetImageReference(GetImageName(imageIndex, imageContentType));

StreamWriter xamlPartWriter = new StreamWriter(xamlPartStream);
using (xamlPartWriter)
{
string xamlText =
"<Span xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">" +
"<InlineUIContainer><Image " +
"Width=\"" +
bitmapSource.Width + "\" " +
"Height=\"" +
bitmapSource.Height + "\" " +
"><Image.Source><BitmapImage CacheOption=\"OnLoad\" UriSource=\"" +
imageReference +
"\"/></Image.Source></Image></InlineUIContainer></Span>";
xamlPartWriter.Write(xamlText);
}
wpfPayload.CreateImagePart(xamlEntryPart, bitmapSource, imageContentType, imageIndex);
}
}
return stream;
}

static int _wpfPayloadCount; // used to disambiguate between all acts of loading from different WPF payloads.


internal static object LoadElement(Stream stream)
{
Package package = Package.Open(stream, FileMode.Open, FileAccess.Read);
WpfPayload wpfPayload = new WpfPayload(package);

PackagePart xamlEntryPart = wpfPayload.GetWpfEntryPart();


int newWpfPayoutCount = _wpfPayloadCount++;
Uri payloadUri = new Uri("payload://wpf" + newWpfPayoutCount, UriKind.Absolute);
Uri entryPartUri = PackUriHelper.Create(payloadUri, xamlEntryPart.Uri); // gives an absolute uri of the entry part
Uri packageUri = PackUriHelper.GetPackageUri(entryPartUri); // extracts package uri from combined package+part uri
PackageStore.AddPackage(packageUri, wpfPayload.Package); // Register the package
ParserContext parserContext = new ParserContext();
parserContext.BaseUri = entryPartUri;

object xamlObject = XamlReader.Load(xamlEntryPart.GetStream(), parserContext);
// Remove the temporary uri from the PackageStore
PackageStore.RemovePackage(packageUri);

return xamlObject;
}

public Package Package
{
get { return _package; }
}

};
}

Next if we have below RichTextBox:

<RichTextBox x:Name="RtbCompose" Width="500" Height="300">
<FlowDocument x:Name="FdDocument">
<Paragraph x:Name="Para1"></Paragraph>
</FlowDocument>
</RichTextBox>
<Button Content="Paste image" HorizontalAlignment="Left" Margin="96,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="75" Click="Button_Click"/>

On Button_Click we can save our image from the ClipBoard. First I got the Stream of package and next we can convert this as XamlReader.Load().

 private void Button_Click(object sender, RoutedEventArgs e)
{
BitmapSource image = Clipboard.GetImage();
Stream packagedImage = WpfPayload.SaveImage(image, WpfPayload.ImageBmpContentType);
object element = WpfPayload.LoadElement(packagedImage);
Para1.Inlines.Add(element as Span);
}

Result we can save with XAMLPackage.

   public byte[] SaveAllContent(RichTextBox rtb)
{
var content = new TextRange(rtb.Document.ContentStart, rtb_Main.Document.ContentEnd);
using (MemoryStream ms = new MemoryStream())
{
content.Save(ms, DataFormats.XamlPackage, true);
return ms.ToArray();
}

}

public void LoadAllContent(byte [] bd, RichTextBox rtb)
{
var content = new TextRange(rtb.Document.ContentStart, rtb_Main.Document.ContentEnd);
MemoryStream ms = new MemoryStream(bd);
content.Load(ms, System.Windows.DataFormats.XamlPackage);
}

With this solution Undo() and Redo() works fine :)



Related Topics



Leave a reply



Submit