I have code to import Images from Clipboard to RichtTextBox.
Image i = new Image();
i.Source = Clipboard.GetImage();
paragraph.Inlines.Add(i);
When I try to delete image and press Undo() i have exception.
No matching constructor found on type
'System.Windows.Interop.InteropBitmap'. You can use the Arguments or
FactoryMethod directives to construct this type.' Line number '1' and
line position '226'.
This is because XAML generated by RichTextBox look like below:
<Image.Source><swi:InteropBitmap /></Image.Source>
I try to change type of BitmapSource to BitmapImage. But in this situation i have XAML:
<Image.Source><BitmapImage BaseUri="{x:Null}" /></Image.Source></Image>
And after delete,Undo i have exception:
Exception thrown: 'System.Windows.Markup.XamlParseException' in
PresentationFramework.dll
Additional information: 'Initialization of 'System.Windows.Media.Imaging.BitmapImage' threw an exception.' Line
number '1' and line position '243'.
I even try the InlineImage from: http://wpftutorial.net/InlineImagesXaml.html
<InlineImage Width="100" Height="100" Stretch="Fill">
<![CDATA[iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAB3RJTUUH2AQP
SFlzAAALEgAACxIB0t1+/AAAAARnQU1BAACxjwv8YQUAAAnOSURBVHjaxVcLcBvVFT1vV
ki3Hju3GCQnGjkObONQkJkxCSIHQQGnIdEr5TFs+LaGl7RRCSUvDp8nglH4mDGQ6EwZIm=]]>
</InlineImage>
Even in this situation i have exception in Undo/Redo operation. Is there any possibility without writing own Undo/Redo operation to handle this situation.
I am confused weather to edit old answer or add new one. So I am going for a new one.
Using below approach I can now copy an existing image file and paste it in RTB, and also I can now copy some unsaved image data from MSPaint, Photoshop and paste that too. After pressing Save button, rtf file is saved and opens nicely in MSWord as expected.
Ctrl+Z is not working, as image data is in stream. I am working on it. Ctrl+Z is not a problem when image is copied as file.
Any more doubts are most welcome. Code below is complete can be used as is.
ImageCode.cs for getting image stored in clipboard
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Runtime.InteropServices;
namespace WpfRichTextBox._32648134
{
public class ImageCode
{
public static ImageSource ImageFromClipboardDibAsSource()
{
MemoryStream ms = Clipboard.GetData("DeviceIndependentBitmap") as MemoryStream;
if (ms != null)
{
byte[] dibBuffer = new byte[ms.Length];
ms.Read(dibBuffer, 0, dibBuffer.Length);
BITMAPINFOHEADER infoHeader =
BinaryStructConverter.FromByteArray<BITMAPINFOHEADER>(dibBuffer);
int fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
int infoHeaderSize = infoHeader.biSize;
int fileSize = fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage;
BITMAPFILEHEADER fileHeader = new BITMAPFILEHEADER();
fileHeader.bfType = BITMAPFILEHEADER.BM;
fileHeader.bfSize = fileSize;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4;
byte[] fileHeaderBytes =
BinaryStructConverter.ToByteArray<BITMAPFILEHEADER>(fileHeader);
MemoryStream msBitmap = new MemoryStream();
msBitmap.Write(fileHeaderBytes, 0, fileHeaderSize);
msBitmap.Write(dibBuffer, 0, dibBuffer.Length);
msBitmap.Seek(0, SeekOrigin.Begin);
BitmapImage img = new BitmapImage();
img.BeginInit();
img.CacheOption = BitmapCacheOption.OnDemand;
img.CreateOptions = BitmapCreateOptions.DelayCreation;
img.StreamSource = msBitmap;
img.EndInit();
return img;
}
return null;
}
public static MemoryStream ImageFromClipboardDibAsStream()
{
MemoryStream ms = Clipboard.GetData("DeviceIndependentBitmap") as MemoryStream;
if (ms != null)
{
byte[] dibBuffer = new byte[ms.Length];
ms.Read(dibBuffer, 0, dibBuffer.Length);
BITMAPINFOHEADER infoHeader =
BinaryStructConverter.FromByteArray<BITMAPINFOHEADER>(dibBuffer);
int fileHeaderSize = Marshal.SizeOf(typeof(BITMAPFILEHEADER));
int infoHeaderSize = infoHeader.biSize;
int fileSize = fileHeaderSize + infoHeader.biSize + infoHeader.biSizeImage;
BITMAPFILEHEADER fileHeader = new BITMAPFILEHEADER();
fileHeader.bfType = BITMAPFILEHEADER.BM;
fileHeader.bfSize = fileSize;
fileHeader.bfReserved1 = 0;
fileHeader.bfReserved2 = 0;
fileHeader.bfOffBits = fileHeaderSize + infoHeaderSize + infoHeader.biClrUsed * 4;
byte[] fileHeaderBytes =
BinaryStructConverter.ToByteArray<BITMAPFILEHEADER>(fileHeader);
MemoryStream msBitmap = new MemoryStream();
msBitmap.Write(fileHeaderBytes, 0, fileHeaderSize);
msBitmap.Write(dibBuffer, 0, dibBuffer.Length);
msBitmap.Seek(0, SeekOrigin.Begin);
return msBitmap;
}
return null;
}
}
public static class BinaryStructConverter
{
public static T FromByteArray<T>(byte[] bytes) where T : struct
{
IntPtr ptr = IntPtr.Zero;
try
{
int size = Marshal.SizeOf(typeof(T));
ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(bytes, 0, ptr, size);
object obj = Marshal.PtrToStructure(ptr, typeof(T));
return (T)obj;
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
}
}
public static byte[] ToByteArray<T>(T obj) where T : struct
{
IntPtr ptr = IntPtr.Zero;
try
{
int size = Marshal.SizeOf(typeof(T));
ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(obj, ptr, true);
byte[] bytes = new byte[size];
Marshal.Copy(ptr, bytes, 0, size);
return bytes;
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
}
}
}
[StructLayout(LayoutKind.Sequential, Pack = 2)]
struct BITMAPFILEHEADER
{
public static readonly short BM = 0x4d42; // BM
public short bfType;
public int bfSize;
public short bfReserved1;
public short bfReserved2;
public int bfOffBits;
}
[StructLayout(LayoutKind.Sequential)]
struct BITMAPINFOHEADER
{
public int biSize;
public int biWidth;
public int biHeight;
public short biPlanes;
public short biBitCount;
public int biCompression;
public int biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant;
}
}
MainWindow.xaml
<Window x:Class="WpfRichTextBox._32648134.Win32648134"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Win32648134" Height="600" Width="700">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="41*"/>
<RowDefinition Height="5*"/>
<RowDefinition Height="21*"/>
</Grid.RowDefinitions>
<RichTextBox x:Name="RtbCompose" Width="500" Height="300" ScrollViewer.VerticalScrollBarVisibility="Visible">
<FlowDocument x:Name="FdDocument">
<Paragraph x:Name="Para1"></Paragraph>
</FlowDocument>
</RichTextBox>
<Button x:Name="BtnCopyImgFile" Content="Paste image" HorizontalAlignment="Left" Margin="96,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="75" Click="BtnCopyImgFile_Click"/>
<Button x:Name="BtnSave" Content="Save" HorizontalAlignment="Left" Grid.Row="1" VerticalAlignment="Top" Width="75" Margin="521,10,0,0" Click="BtnSave_Click"/>
<Button x:Name="BtnCopyImgData" Content="Paste image data" HorizontalAlignment="Left" Margin="190,11,0,0" Grid.Row="1" VerticalAlignment="Top" Click="BtnCopyImgData_Click"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Diagnostics;
using System.IO;
namespace WpfRichTextBox._32648134
{
/// <summary>
/// Interaction logic for Win32648134.xaml
/// </summary>
public partial class Win32648134 : Window
{
public Win32648134()
{
InitializeComponent();
}
private void BtnCopyImgFile_Click(object sender, RoutedEventArgs e)
{
Image i = new Image();
if (Clipboard.ContainsFileDropList())
{
StringCollection fileNames = Clipboard.GetFileDropList();
BitmapImage img = new BitmapImage(new Uri(fileNames[0], UriKind.Absolute));
i.Source = img;
Para1.Inlines.Add(i);
}
Para1.Inlines.Add(new Run("first rtb app"));
}
private void BtnSave_Click(object sender, RoutedEventArgs e)
{
TextRange allText = new TextRange(RtbCompose.Document.ContentStart, RtbCompose.Document.ContentEnd);
FileStream stream = new FileStream(#"I:\RTB.rtf", FileMode.Create);
allText.Save(stream, DataFormats.Rtf);
if (stream != null)
stream.Close();
}
private void BtnCopyImgData_Click(object sender, RoutedEventArgs e)
{
bool hasImgData = Clipboard.ContainsImage();
Image i = new Image();
if (hasImgData)
{
BitmapSource imgData = (BitmapSource)ImageCode.ImageFromClipboardDibAsSource();
i.Source = imgData;
Para1.Inlines.Add(i);
}
Para1.Inlines.Add(new Run("rtb app, image comes from image data instead of file"));
}
}
}
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
I am attempting to save an embedded shape as an image using C#.
If the object is embedded as an actual image (WMF/JPEG) I can retrieve the image without issue but when the object is an embedded shape or an OLE Object that displays as an image in Word I cannot seem to extract or retrieve said object to then either copy to the clipboard or save said image.
Here is my current code sample; either the object is empty or I get the following error:
System.Runtime.InteropServices.ExternalException: 'A generic error occurred in GDI+.'
Any help is appreciated. Thank you
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace ImageMagickSandboxWinForms
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
public static BitmapSource ConvertBitmap(Bitmap source)
{
return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
source.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
}
public static Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
Bitmap bitmap;
using (var outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapsource));
enc.Save(outStream);
bitmap = new Bitmap(outStream);
}
return bitmap;
}
private void button1_Click(object sender, EventArgs e)
{
string physicsDocLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
physicsDocLocation += #"\[Doc path Here].docx";
var wordApp = new Microsoft.Office.Interop.Word.Application();
var wordDoc = wordApp.Documents.Open(physicsDocLocation);
int iCount = wordDoc.InlineShapes.Count;
for (int i = 1; i < (wordDoc.InlineShapes.Count + 1); i++)
{
var currentInlineShape = wordDoc.InlineShapes[i];
currentInlineShape.Range.Select();
wordDoc.ActiveWindow.Selection.Range.Copy();
BitmapSource clipBoardImage = System.Windows.Clipboard.GetImage();
Bitmap bmpClipImage = BitmapFromSource(clipBoardImage);
string finalPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), #"TestConversions");
finalPath += #"\" + Guid.NewGuid().ToString() + ".jpg";
using (MemoryStream memory = new MemoryStream())
{
using (FileStream fs = new FileStream(finalPath, FileMode.Create, FileAccess.ReadWrite))
{
bmpClipImage.Save(memory, ImageFormat.Jpeg); <<<---- Error happens here.
byte[] bytes = memory.ToArray();
fs.Write(bytes, 0, bytes.Length);
}
}
}
wordDoc.Close();
wordApp.Quit();
}
}
}
i have these code in my library, dunno where i have found that but hope you do the job for you: i am using Clippboard to trap the different images, jus t dont forget, Thread is needed to access Clipboard
for (var i = 1; i <= wordApplication.ActiveDocument.InlineShapes.Count; i++)
{
var inlineShapeId = i;
var thread = new Thread(() => SaveInlineShapeToFile(inlineShapeId, wordApplication));
// STA is needed in order to access the clipboard
// https://stackoverflow.com/a/518724/700926
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
}
// General idea is based on: https://stackoverflow.com/a/7937590/700926
protected static void SaveInlineShapeToFile(int inlineShapeId, Application wordApplication)
{
// Get the shape, select, and copy it to the clipboard
var inlineShape = wordApplication.ActiveDocument.InlineShapes[inlineShapeId];
inlineShape.Select();
wordApplication.Selection.Copy();
// Check data is in the clipboard
if (Clipboard.GetDataObject() != null)
{
var data = Clipboard.GetDataObject();
// Check if the data conforms to a bitmap format
if (data != null && data.GetDataPresent(DataFormats.Bitmap))
{
// Fetch the image and convert it to a Bitmap
var image = (Image) data.GetData(DataFormats.Bitmap, true);
var currentBitmap = new Bitmap(image);
// Save the bitmap to a file
currentBitmap.Save(#"C:\Users\Username\Documents\" + String.Format("img_{0}.png", inlineShapeId));
}
}
}
following if you are using Winform or WPF the clipboard acts differently for an image:
if (Clipboard.ContainsImage())
{
// ImageUIElement.Source = Clipboard.GetImage(); // does not work
System.Windows.Forms.IDataObject clipboardData = System.Windows.Forms.Clipboard.GetDataObject();
if (clipboardData != null)
{
if (clipboardData.GetDataPresent(System.Windows.Forms.DataFormats.Bitmap))
{
System.Drawing.Bitmap bitmap = (System.Drawing.Bitmap)clipboardData.GetData(System.Windows.Forms.DataFormats.Bitmap);
ImageUIElement.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(bitmap.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty,BitmapSizeOptions.FromEmptyOptions());
Console.WriteLine("Clipboard copied to UIElement");
}
}
}
after if its not functionam due to a bug in translation of format, there is this solution . So its infrecnh but its easily to understand the logic of the using of "DeviceIndependentBitmap"
I am stuck. Currently I am trying to simulate pulling a binary blob from a database that is supposed to be a TIFF image. I use this gist image.tif in the image variable to do so. I am pretty sure I am close to making this happen. It's that the issue probably has to do with how I am converting the string to byte array or something. Basically this application throws an exception stating that it can't create a PDF with 0 frames. At this point I must admit that I may be in over my head on this one. Could someone be so kind and help me the rest of the way with this one?
The code is included below:
using System;
using System.Drawing;
using PdfSharp.Pdf;
using PdfSharp.Drawing;
using System.IO;
using System.Drawing.Imaging;
using System.Text;
namespace ConvertTifToPDFFile
{
class Program
{
static void Main(string[] args)
{
string image = "";
byte[] imageAsByteStream = Encoding.ASCII.GetBytes(image);
int imageByteStreamLength = imageAsByteStream.Length;
string base64EncodedImage = Convert.ToBase64String(imageAsByteStream);
imageAsByteStream = Encoding.ASCII.GetBytes(base64EncodedImage);
Stream imageStream = TiffImageSplitter.ByteArrayToMemoryStream(imageAsByteStream);
// Image splitImage = TiffImageSplitter.getTiffImage(imageStream, 1);
TiffImageSplitter.tiff2PDF(imageStream);
}
}
public class TiffImageSplitter
{
private static TiffImageSplitter tiff = new TiffImageSplitter();
public static void tiff2PDF(Stream imageByteStream)
{
PdfDocument doc = new PdfDocument();
int pageCount = getPageCount(imageByteStream);
for (int i = 0; i < pageCount; i++)
{
PdfPage page = new PdfPage();
Image img = getTiffImage(imageByteStream, 1);
XImage imgFrame = XImage.FromGdiPlusImage(img);
page.Width = imgFrame.PointWidth;
page.Height = imgFrame.PointHeight;
doc.Pages.Add(page);
XGraphics xgr = XGraphics.FromPdfPage(doc.Pages[i]);
xgr.DrawImage(img, 0, 0);
}
doc.Save("C:/temp/test.pdf");
doc.Close();
}
public static Image getTiffImage(Stream imageStream, int pageNumber)
{
MemoryStream ms = null;
Image returnImage = null;
try
{
ms = new MemoryStream();
Image sourceImage = Image.FromStream(imageStream, true, true);
Guid objGuid = sourceImage.FrameDimensionsList[0];
FrameDimension objDimension = new FrameDimension(objGuid);
sourceImage.SelectActiveFrame(objDimension, pageNumber);
sourceImage.Save(ms, ImageFormat.Tiff);
returnImage = Image.FromStream(ms);
}
catch (Exception ex)
{
Console.WriteLine("{0} Exception caught.", ex);
returnImage = null;
}
return returnImage;
}
public static MemoryStream ByteArrayToMemoryStream(byte[] bytestream)
{
MemoryStream stream = new MemoryStream();
stream.Write(bytestream, 0, bytestream.Length);
return stream;
}
public static int getPageCount(Stream imageStream)
{
int pageCount = -1;
try
{
Image img = Image.FromStream(imageStream, true, true);
pageCount = img.GetFrameCount(FrameDimension.Page);
img.Dispose();
}
catch (Exception ex)
{
Console.WriteLine("{0} Exception caught.", ex);
pageCount = 0;
}
return pageCount;
}
}
}
IMPORTANT!!!! First of all, your example tiff isn't valid at all. It can't be read by any file editor. I have had to take these ones for testing.
Then, the code has couple errors:
1) I don't understand what've done with strings but reading files and BLOBs are same:
static void Main(string[] args)
{
//string image = "";
//byte[] imageAsByteStream = Encoding.ASCII.GetBytes(image);
byte[] imageAsByteStream = File.ReadAllBytes("../../../MARBIBM.TIF");
//int imageByteStreamLength = imageAsByteStream.Length;
//string base64EncodedImage = Convert.ToBase64String(imageAsByteStream);
//imageAsByteStream = Encoding.ASCII.GetBytes(base64EncodedImage);
Stream imageStream = TiffImageSplitter.ByteArrayToMemoryStream(imageAsByteStream);
// Image splitImage = TiffImageSplitter.getTiffImage(imageStream, 1);
TiffImageSplitter.tiff2PDF(imageStream);
}
2) method tiff2PDF should be like that
public static void tiff2PDF(Stream imageByteStream)
{
PdfDocument doc = new PdfDocument();
int pageCount = getPageCount(imageByteStream);
for (int i = 0; i < pageCount; i++)
{
PdfPage page = new PdfPage();
Image img = getTiffImage(imageByteStream, i); //<---HERE WAS ANOTHER ERROR, LOOK AT i
XImage imgFrame = XImage.FromGdiPlusImage(img);
3)
public static MemoryStream ByteArrayToMemoryStream(byte[] bytestream)
{
MemoryStream stream = new MemoryStream(bytestream);
//stream.Write(bytestream, 0, bytestream.Length);
return stream;
}
I am trying to create a function that will load a PNG from a URL into memory and then add an iTXt chunk with the keyword "openbadges" and some json data. I have every part working except when I execute
metadata.SetQuery("/iTXt/openbadges", "");
I get an exception:
Value does not fall within the expected range.
Here is the function:
private static byte[] CreateOpenBadge(BadgeAssertionEntity assertion)
{
using (var image = LoadImage(new Uri(assertion.Badge.ImageUrl)))
using (var imageStream = new MemoryStream())
{
image.Save(imageStream, ImageFormat.Png);
var pngDecoder = new PngBitmapDecoder(imageStream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
using (var badgeStream = new MemoryStream())
{
var pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(pngDecoder.Frames[0]));
var metadata = pngEncoder.Frames[0].Metadata as BitmapMetadata;
if (metadata == null)
throw new ApplicationException();
metadata.SetQuery("/iTXt/openbadges", "");
pngEncoder.Save(badgeStream);
return badgeStream.ToArray();
}
}
}
Any ideas what I am doing wrong?
The trick is to setquery() "/iTXt/Keyword" and "/iTXt/TextEntry", but you must use a char[] for the value of Keyword.
This is because internally "/iTXt/Keyword" wants a VT_LPSTR as the value type, and only a char[] will correctly convert into that.
Here's a sample that uses WIC to write a checkerboard png, including some iTXt metadata.
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace PNGEncoder
{
class Program
{
static void Main(string[] args)
{
var width = 256;
var height = 256;
var pngMetadata = new BitmapMetadata("png");
pngMetadata.SetQuery("/iTXt/Keyword", "keyword0".ToCharArray());
pngMetadata.SetQuery("/iTXt/TextEntry", "textentry0");
pngMetadata.SetQuery("/[1]iTXt/Keyword", "keyword1".ToCharArray());
pngMetadata.SetQuery("/[1]iTXt/TextEntry", "textentry1");
var bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Gray8, null);
var pixels = new byte[width * height];
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
pixels[y * width + x] = (byte)(255 * (((x >> 4) ^ (y >> 4)) & 1));
}
}
bitmap.WritePixels(new Int32Rect(0, 0, width, height), pixels, width, 0);
var encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap, null, pngMetadata, null));
using (var stream = File.Create("checkerBoard.png"))
{
encoder.Save(stream);
}
}
}
}
I gave up on using PngBitmapEncoder. Instead I will just modify the png bytes directly. I have attached a class I made for this purpose incase others find it useful.
This class is heavily inspired by AShelly's response at Using Chunks in a PNG
I also use http://damieng.com/blog/2006/08/08/calculating_crc32_in_c_and_net for the crc hash.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Badger.Libraries.Hashing;
namespace Badger.Libraries.Images
{
public class Png
{
private readonly byte[] _header;
private readonly IList<Chunk> _chunks;
public Png(Uri imageUri)
{
_header = new byte[8];
_chunks = new List<Chunk>();
var webResponse = WebRequest.Create(imageUri).GetResponse();
using (var webResponseStream = webResponse.GetResponseStream())
using (var memoryStream = new MemoryStream())
{
if (webResponseStream == null)
throw new ArgumentException("invalid uri");
webResponseStream.CopyTo(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
memoryStream.Read(_header, 0, _header.Length);
while (memoryStream.Position < memoryStream.Length)
_chunks.Add(ChunkFromStream(memoryStream));
memoryStream.Close();
}
}
public void AddInternationalText(string keyword, string text)
{
// 1-79 (keyword)
// 1 (null character)
// 1 (compression flag)
// 1 (compression method)
// 0+ (language)
// 1 (null character)
// 0+ (translated keyword)
// 1 (null character)
// 0+ (text)
var typeBytes = Encoding.UTF8.GetBytes("iTXt");
var keywordBytes = Encoding.UTF8.GetBytes(keyword);
var textBytes = Encoding.UTF8.GetBytes(text);
var nullByte = BitConverter.GetBytes('\0')[0];
var zeroByte = BitConverter.GetBytes(0)[0];
var data = new List<byte>();
data.AddRange(keywordBytes);
data.Add(nullByte);
data.Add(zeroByte);
data.Add(zeroByte);
data.Add(nullByte);
data.Add(nullByte);
data.AddRange(textBytes);
var chunk = new Chunk(typeBytes, data.ToArray());
_chunks.Insert(1, chunk);
}
public byte[] ToBytes()
{
using (var stream = new MemoryStream())
{
stream.Write(_header, 0, _header.Length);
foreach (var chunk in _chunks)
chunk.WriteToStream(stream);
var bytes = stream.ToArray();
stream.Close();
return bytes;
}
}
private static Chunk ChunkFromStream(Stream stream)
{
var length = ReadBytes(stream, 4);
var type = ReadBytes(stream, 4);
var data = ReadBytes(stream, Convert.ToInt32(BitConverter.ToUInt32(length.Reverse().ToArray(), 0)));
stream.Seek(4, SeekOrigin.Current);
return new Chunk(type, data);
}
private static byte[] ReadBytes(Stream stream, int n)
{
var buffer = new byte[n];
stream.Read(buffer, 0, n);
return buffer;
}
private static void WriteBytes(Stream stream, byte[] bytes)
{
stream.Write(bytes, 0, bytes.Length);
}
private class Chunk
{
public Chunk(byte[] type, byte[] data)
{
_type = type;
_data = data;
}
public void WriteToStream(Stream stream)
{
WriteBytes(stream, BitConverter.GetBytes(Convert.ToUInt32(_data.Length)).Reverse().ToArray());
WriteBytes(stream, _type);
WriteBytes(stream, _data);
WriteBytes(stream, CalculateCrc(_type, _data));
}
private static byte[] CalculateCrc(IEnumerable<byte> type, IEnumerable<byte> data)
{
var bytes = new List<byte>();
bytes.AddRange(type);
bytes.AddRange(data);
var hasher = new Crc32();
using (var stream = new MemoryStream(bytes.ToArray()))
return hasher.ComputeHash(stream);
}
private readonly byte[] _type;
private readonly byte[] _data;
}
}
}
Whenever I retrieve an image using my Generic Handler, I retrieve either an empty image or a broken image.
Here is my code.
aspx File:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
//imports
using DHELTASSys.Modules;
using System.Data;
using DHELTASSys.AuditTrail;
namespace DHELTASSys
{
public partial class EvaluateOffense : System.Web.UI.Page
{
DisciplineModuleBL discipline = new DisciplineModuleBL();
DHELTASSysAuditTrail audit = new DHELTASSysAuditTrail();
protected void Page_Load(object sender, EventArgs e)
{
string position = Session["Position"].ToString();
if (Session["EmployeeID"] == null)
{
Response.Redirect("LogIn.aspx");
} else if(position != "HR Manager")
{
Response.Redirect("AccessDenied.aspx");
}
discipline.Offense_emp_id = int.Parse(Session["OffenseID"].ToString());
DataTable dt = discipline.GetProof();
if (dt.Rows == null)
{
Label9.Visible = false;
Image1.Visible = false;
}
}
protected void btnEvaluate_Click(object sender, EventArgs e)
{
discipline.Offense_emp_id = int.Parse(Session["OffenseID"].ToString());
discipline.Decision = drpDecision.Text;
discipline.AddOffenseDecision();
audit.Emp_id = int.Parse(Session["EmployeeID"].ToString());
audit.AddAuditTrail(drpDecision.Text + "ed Employee's offense.");
}
}
}
Here is the handler:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.IO;
//imports
using System.Data;
using DHELTASSys.Modules;
using DHELTASSys.DataAccess;
namespace DHELTASSys
{
public class ShowImage : IHttpHandler, System.Web.SessionState.IRequiresSessionState
{
DisciplineModuleBL discipline = new DisciplineModuleBL();
public void ProcessRequest(HttpContext context)
{
if (context.Session["OffenseID"].ToString() == null) return;
int offense_emp_id = int.Parse(context.Session["OffenseID"].ToString());
discipline.Offense_emp_id = offense_emp_id;
DataTable dt = discipline.GetProof();
if (dt.Rows == null) return;
int id = 1;
string image = dt.Rows[0][1].ToString() + id;
string FileName = dt.Rows[0][0].ToString();
string FileContentType = dt.Rows[0][2].ToString();
Byte[] bytes = (Byte[])dt.Rows[0][1];
string imageBase64 = Convert.ToBase64String(bytes);
context.Response.ContentType = "image/" + FileContentType;
if (context.Request.QueryString["id"] == "1")
{
MemoryStream ms = new MemoryStream();
ms.Write(bytes, 0, bytes.Length);
context.Response.Buffer = true;
System.Drawing.Image imagen = System.Drawing.Image.FromStream(ms);
context.Response.BinaryWrite(bytes);
ms.Dispose();
}
else
{
return;
}
}
public bool IsReusable
{
get
{
return true;
}
}
}
}
And adding to that, here is my image object.
<asp:Image ID="Image1" runat="server" ImageUrl="~/ShowImage.ashx" />
I already tweaked my code in so many ways.
The image file is stored in SQL Server using the data type "Image"
As you can see, I'm using the session to retrieve the specified image from the Database.
I have no problem in accessing the session whatsoever.
Thanks in advance.
Your code seems a bit more complicated than necessary; you can write the binary data to the outputstream without needing to load it into a memory stream, and you're doing nothing with your System.Drawing.Image object. Try this:
context.Response.OutputStream.Write(bytes, 0, bytes.Length);
First, I don't see anything wrong with the way you are writing the image to the response stream. There's a few useless lines of code but I understand that as you said, you were tweaking your code in desperation. Basically, this should be good enough...
Byte[] bytes = (Byte[])dt.Rows[0][1];
context.Response.ContentType = "image/" + FileContentType;
if (context.Request.QueryString["id"] == "1")
{
context.Response.BinaryWrite(bytes);
context.ApplicationInstance.CompleteRequest(); //just to make sure the ASP.NET pipeline completes the request
}
else
{
return;
}
Now, that "should" work given that bytes has been correctly casted to a byte array. Make sure to add some proper exception handling and logging because I'm a bit suspicious that the problem it's somewhere else. So, follow these steps:
Clean up your code for better clarity
Debug the app
Implement some exception handling
Implement some sort of error logging
You should be able to get to the bottom of this issue since there's nothing wrong in sending an array of bytes using the BinaryWrite method
This is real example that pickup from my project
into page.cs set image url dynamically:
// for diffrent url using guid in url
imgProfilePic.ImageUrl = "GenericHandler_ShowImage.ashx?Ref=" + Guid.NewGuid().ToString() + "&n=" + lngEmployeeID;
into handler, get image by table adapter
// in handler
public void ProcessRequest(HttpContext context)
{
long EmployeeID = -1;
if (context.Request.QueryString["n"] != null)
EmployeeID = long.Parse(context.Request.QueryString["n"].ToString());
//else
// throw new ArgumentException("No parameter specified");
if (context.Request.QueryString["actid"] == "2")
{
ShowThumbPic(context, EmployeeID);
return;
}
//else ...
}
private void ShowThumbPic(HttpContext context, long EmployeeID)
{
context.Response.ContentType = "image/jpeg";
Stream myStream = GetEmpImage(EmployeeID);
byte[] myImgByteArray;
using (BinaryReader br = new BinaryReader(myStream))
{
myImgByteArray = br.ReadBytes((int)myStream.Length);
}
MemoryStream ms = new MemoryStream(byteArrayIn);
System.Drawing.Image img = System.Drawing.Image.FromStream(ms);
if (img.Height > 50 || img.Width > 50)
{
System.Drawing.Size siz = GetScaledSize(img.Size, new System.Drawing.Size(50, 50));
img = (System.Drawing.Image)ResizeImage(img, siz);
}
MemoryStream ms = new MemoryStream();
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
byte[] myBuffer = ms.ToArray();
int byteSeq = myBuffer.Length; //myStream.Read(myBuffer, 0, 4096);
if (byteSeq > 0)
context.Response.OutputStream.Write(myBuffer, 0, byteSeq);
}
private System.Drawing.Image GetEmployeeImage(long EmployeeID)
{
System.Drawing.Image img = null;
try
{
using (DAL.dstEmployeeTableAdapters.tbl_Employee_InfoTableAdapter ta = new DAL.dstEmployeeTableAdapters.tbl_Employee_InfoTableAdapter())
{
object obj = ta.spr_Employee_Info_GetPicture(EmployeeID);
if (obj != null)
{
MemoryStream ms = new MemoryStream((byte[])obj);
img = System.Drawing.Image.FromStream(ms);
}
}
}
catch (Exception x)
{
throw new Exception(Msg.Error_InDownloadPicture + x.Message);
}
return img;
}
public static Size GetScaledSize(Size ImageSize, Size FrameSize)
{
int newWidth = ImageSize.Width;
int newHeight = ImageSize.Height;
double ratioX = (double)FrameSize.Width / ImageSize.Width;
double ratioY = (double)FrameSize.Height / ImageSize.Height;
double ratio = Math.Min(ratioX, ratioY);
if (ratio < 1.0f) // if Frame is greater than image, resize it.
{
newWidth = (int)(ImageSize.Width * ratio);
newHeight = (int)(ImageSize.Height * ratio);
}
return new Size(newWidth, newHeight);
}
public static System.Drawing.Bitmap ResizeImage(System.Drawing.Image image, Size siz)
{
//a holder for the result
Bitmap result = new Bitmap(siz.Width, siz.Height);
// set the resolutions the same to avoid cropping due to resolution differences
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
//use a graphics object to draw the resized image into the bitmap
using (Graphics graphics = Graphics.FromImage(result))
{
//set the resize quality modes to high quality
graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
//draw the image into the target bitmap
graphics.DrawImage(image, 0, 0, result.Width, result.Height);
}
//return the resulting bitmap
return result;
}
I create thumbnails with the following page, Thumbnail.ashx:
<%# WebHandler Language="C#" Class="Thumbnail" %>
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
public class Thumbnail : IHttpHandler {
private int _thumbnailSize = 150;
public void ProcessRequest(HttpContext context) {
string photoName = context.Request.QueryString["p"];
string cachePath = Path.Combine(HttpRuntime.CodegenDir, photoName + ".png");
if (File.Exists(cachePath)) {
OutputCacheResponse(context, File.GetLastWriteTime(cachePath));
context.Response.WriteFile(cachePath);
return;
}
string photoPath = context.Server.MapPath("../uploads/originals/" + photoName);
Bitmap photo;
try {
photo = new Bitmap(photoPath);
}
catch (ArgumentException) {
throw new HttpException(404, "Photo not found.");
}
context.Response.ContentType = "image/png";
int width, height;
if (photo.Width > photo.Height) {
width = _thumbnailSize;
height = photo.Height * _thumbnailSize / photo.Width;
}
else {
width = photo.Width * _thumbnailSize / photo.Height;
height = _thumbnailSize;
}
Bitmap target = new Bitmap(width, height);
using (Graphics graphics = Graphics.FromImage(target)) {
graphics.CompositingQuality = CompositingQuality.HighSpeed;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.DrawImage(photo, 0, 0, width, height);
using (MemoryStream memoryStream = new MemoryStream()) {
target.Save(memoryStream, ImageFormat.Png);
OutputCacheResponse(context, File.GetLastWriteTime(photoPath));
using (FileStream diskCacheStream = new FileStream(cachePath, FileMode.CreateNew)) {
memoryStream.WriteTo(diskCacheStream);
}
memoryStream.WriteTo(context.Response.OutputStream);
}
}
}
private static void OutputCacheResponse(HttpContext context, DateTime lastModified) {
HttpCachePolicy cachePolicy = context.Response.Cache;
cachePolicy.SetCacheability(HttpCacheability.Public);
cachePolicy.VaryByParams["p"] = true;
cachePolicy.SetOmitVaryStar(true);
cachePolicy.SetExpires(DateTime.Now + TimeSpan.FromDays(365));
cachePolicy.SetValidUntilExpires(true);
cachePolicy.SetLastModified(lastModified);
}
public bool IsReusable {
get {
return true;
}
}
}
When I try to delete the picture file physically or through code, I get a 'cannot access file, being used by another process" error.
Is this caused by the caching of the thumbnail? Or is the file not being closed somewhere I can't spot?
Could it be my upload file script is causing this?
You don't appear to be closing your MemoryStream anywhere? Garbage collection alone won't remove a file lock, would it? Tossing in a memoryStream.Close() at the end of ProcessRequest couldn't hurt.