I'm inserting a TIFF file into a PDF using PDFSharp. That process works fine, but it's leaving a lock on the TIFF file. The TIFF file is on a SMB share. I am using the WPF version because the GDI version does not support CMYK TIFFs.
var output = new PdfDocument();
var input = PdfReader.Open(template_path, PdfDocumentOpenMode.Import);
var page = input.Pages[0];
output.AddPage(page);
page = output.Pages[0];
var gfx = XGraphics.FromPdfPage(page);
var image = XImage.FromFile(tiff_path);
gfx.DrawImage(image, 500, 200, 400, 400);
output.Save(destination_path);
output.Close();
Update: Simply doing this leaves the TIFF locked. No document opened or XGraphics or anything.
using (var image = XImage.FromFile(path))
{}
Update: This works, and is what I am going with for now.
using (var fsImage = File.Open(tiffPath, FileMode.Open, FileAccess.Read, FileShare.None))
{
var bitmapSource = new BitmapImage();
bitmapSource.BeginInit();
bitmapSource.StreamSource = fsImage;
bitmapSource.EndInit();
using (var image = XImage.FromBitmapSource(bitmapSource))
{
}
}
Indecently, this nasty piece of code works also :-)
using (var image = XImage.FromFile(tiffPath))
{
}
GC.Collect();
With WPF BitmapSource, there is no deterministic disposal of the underlying stream, so you can end up with locks for as long as there is a reference.
You --> XImage --> BitmapSource --> Stream
If you call dispose on the XImage, it will release its reference on the BitmapSource, which will allow it to be finalized when the GC feels like it.
You can control when the file is closed by providing stream in lieu of a path and closing it explicitly. Doing so prematurely will cause exceptions in BitmapSource, however, so be sure you are not using the BitmapSource after you close the stream.
using (var fsImage = File.Open(tiff_path, FileMode.Open, FileAccess.Read, FileShare.None))
{
var output = new PdfDocument();
var input = PdfReader.Open(template_path, PdfDocumentOpenMode.Import);
var page = input.Pages[0];
output.AddPage(page);
page = output.Pages[0];
var gfx = XGraphics.FromPdfPage(page);
var bitmapSource = new BitmapImage();
bitmapSource.BeginInit();
bitmapSource.StreamSource = fsImage;
bitmapSource.EndInit();
using (var image = XImage.FromBitmapSource(bitmapSource))
{
gfx.DrawImage(image, 500, 200, 400, 400);
}
output.Save(destination_path);
output.Close();
}
If your image is small enough, you could skip the stream and just use the BitmapCacheOption of OnLoad to close the source after opening, but this will cause the entire image to be loaded into memory.
Related
My application is used to store scanned images, with additional information, to an SQL Database. Because I use a picturebox to accomplish this there is an issue I've become aware of with the picturebox holding open resources. This is preventing me from doing anything with the original file until I close my form. I have tried various ways to dispose of the picturebox with no success. Need help with the following code to release the resources held by the picturebox.
using (OpenFileDialog GetPhoto = new OpenFileDialog())
{
GetPhoto.Filter = "images | *.jpg";
if (GetPhoto.ShowDialog() == DialogResult.OK)
{
pbPhoto.Image = Image.FromFile(GetPhoto.FileName);
txtPath.Text = GetPhoto.FileName;
txtTitle.Text = System.IO.Path.GetFileNameWithoutExtension(GetPhoto.FileName);
((MainPage)MdiParent).tsStatus.Text = txtPath.Text;
//GetPhoto.Dispose(); Tried this
//GetPhoto.Reset(); Tried this
//GC.Collect(): Tried this
}
}
Saving the image to my database uses the following:
MemoryStream stream = new MemoryStream();
pbPhoto.Image.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
byte[] pic = stream.ToArray();
It's usually FromFile() that causes locking issues: (not the PictureBox itself)
The file remains locked until the Image is disposed.
Change:
pbPhoto.Image = Image.FromFile(GetPhoto.FileName);
To:
using (FileStream fs = new FileStream(GetPhoto.FileName, FileMode.Open))
{
if (pbPhoto.Image != null)
{
Image tmp = pbPhoto.Image;
pbPhoto.Image = null;
tmp.Dispose();
}
using (Image img = Image.FromStream(fs))
{
Bitmap bmp = new Bitmap(img);
pbPhoto.Image = bmp;
}
}
This should make a copy of the image for use in the PictureBox and release the file itself from any locks.
I'm trying to "copy" an image from another image, reduce it's height and width, and return it as stream to retrieve in another class and show as an Image. But when I get the stream in the other class, and exception is thrown; "cannot access to closed stream".
This method gets the image path, reduces its size and returns as a stream.
public Stream getImagenCopia (string dataImagen)
{
Bitmap ImageOrig = BitmapFactory.DecodeFile (dataImagen);
var ImagenCopia = Bitmap.CreateScaledBitmap (ImageOrig, 80, 80, false);
using (MemoryStream ms = new MemoryStream ())
{
ImagenCopia.Compress (Bitmap.CompressFormat.Jpeg, 40, ms);
return ms;
}
}
This method receives the stream and sets it in Image source
var cim = auxFotos.getImagenCopia(path);
setImagen(img, cim);
void setImagen (Image img, Stream strm)
{
img.Source = ImageSource.FromStream (() =>
{
return strm;
});
}
You're disposing of the MemoryStream because you're using a using block. By the time you try to use the return value later in your code, it's unavailable.
using (MemoryStream ms = new MemoryStream ())
{
ImagenCopia.Compress (Bitmap.CompressFormat.Jpeg, 40, ms);
return ms;
}
Remove the using statement:
Bitmap ImageOrig = BitmapFactory.DecodeFile(dataImagen);
var ImagenCopia = Bitmap.CreateScaledBitmap(ImageOrig, 80, 80, false);
var ms = new MemoryStream());
ImagenCopia.Compress(Bitmap.CompressFormat.Jpeg, 40, ms);
return ms;
If you see memory consumption spike because resources aren't being garbage collected, you may have to clean it up manually after you're done using it, perhaps by calling .Dispose() on it.
I am trying to add some text on top of a photo take by the camera, and here is the method I am using, but unfortunately I either get a closedStream error, or that there is cross-thread access when I try use the dispatcher. Could someone please explain me what is going wrong?
void cam_CaptureImageAvailable(object sender, Microsoft.Devices.ContentReadyEventArgs e)
{
DateTime dt = DateTime.Now;
string fileName = dt.Year.ToString() + dt.Month.ToString() + dt.Day.ToString() + dt.Hour.ToString() + dt.Minute.ToString() + dt.Second.ToString() + ".jpg";
try
{
// Save picture to the library camera roll.
library.SavePictureToCameraRoll(fileName, e.ImageStream);
// Set the position of the stream back to start
e.ImageStream.Seek(0, SeekOrigin.Begin);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
// load photo to writable bitmap
WriteableBitmap writeableBitmap = PictureDecoder.DecodeJpeg(e.ImageStream);
writeableBitmap.Invalidate();
var renderText = new TextBlock
{
Text = "Hello World",
FontSize = 72,
Foreground = new SolidColorBrush(Colors.White),
FontWeight = FontWeights.Black,
Width = 500,
Height = 100
};
writeableBitmap.Render(renderText, new TranslateTransform() { X = 100, Y = 300 });
writeableBitmap.Invalidate();
using (var ms = new MemoryStream())
{
writeableBitmap.SaveJpeg(ms, 1024, 768, 0, 100);
ms.Seek(0, SeekOrigin.Begin);
library.SavePicture("x" + fileName, ms);
}
// e.ImageStream.Close();
});
// Save picture as JPEG to isolated storage.
using (IsolatedStorageFile isStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream targetStream = isStore.OpenFile(fileName, FileMode.Create, FileAccess.Write))
{
// Initialize the buffer for 4KB disk pages.
byte[] readBuffer = new byte[4096];
int bytesRead = -1;
// Copy the image to isolated storage.
while ((bytesRead = e.ImageStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
targetStream.Write(readBuffer, 0, bytesRead);
}
}
}
}
finally
{
// Close image stream
e.ImageStream.Close();
}
}
With the code above I get the following error: Cannot access a closed Stream.
If i remove the Dispatcher, I get this error: Invalid cross-thread access.
Thanks.
place ui less code outside dispatcher scope.
or save your e.ImageStream to other stream can be gotted in dispatcher scope.
First, what is occurring?
Dispatcher.BeginInvoke postpones your code and tells the UI thread to execute it whenever it's available. Therefore, your e.ImageStream.Close(); line is executed before the code inside of the BeginInvoke. So when you're trying to read the contents of the stream, it's already closed.
Two ways to solve that:
Remove the e.ImageStream.Close(); from the finally block. But I don't know if the stream will remain open anyway.
If 1. doesn't work, copy the contents of the stream to a MemoryStream, then use this stream to create the WriteableBitmap:
var stream = new MemoryStream();
e.ImageStream.CopyTo(stream);
In both case, don't forget to close the stream when you're done creating the WriteableBitmap.
I am trying to save a PNG image that has been copied to the clipboard, but it is either turning out as a solid black, or black around the areas that should be transparent.
Here is the code I am using to capture and save the Image
var clipboardImage = (InteropBitmap)Clipboard.GetImage();
Image.SaveImage(clipboardImage, Path.Combine(Config.App.ApplicationDataImagesPath, string.Format("{0}.{1}", imageId, "png")));
public static void SaveImage(BitmapSource bitmapImage, string filename)
{
using (var fileStream = new FileStream(filename, FileMode.Create))
{
var pngBitmapEncoder = new PngBitmapEncoder();
pngBitmapEncoder.Frames.Add(BitmapFrame.Create(bitmapImage));
pngBitmapEncoder.Save(fileStream);
fileStream.Close();
fileStream.Dispose();
}
}
Does anyone have any ideas why it won't persrve the alpha channels of a PNG?
Thanks
Dan
Edit: I should of mentioned that black images were happening when copying an image from Internet Explorer 9. Works perfectly when copying an image from either Chrome or Firefox. Any workarounds for IE9 issue?
What happens if just do this:
Clipboard.GetImage().Save ("XXX.png", System.Drawing.Imaging.ImageFormat.Png);
EDIT - for WPF try this:
public static void SaveClipboardImageToFile(string filePath)
{
var image = Clipboard.GetImage();
using (var fileStream = new FileStream(filePath, FileMode.Create))
{
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(image));
encoder.Save(fileStream);
}
}
My ASP.NET application has an image cropping and resizing features. This requires that the uploaded temporary image be deleted. Everything works fine, but when I try to delete an image larger than 80px by 80px I get a "File is locked by another process..." error, even though I've released all resources.
Here's a snippet:
System.Drawing.Image tempimg = System.Drawing.Image.FromFile(temppath);
System.Drawing.Image img = (System.Drawing.Image) tempimg.Clone(); //advice from another forum
tempimg.Dispose();
img = resizeImage(img, 200, 200); //delete only works if it's 80, 80
img.Save(newpath);
img.Dispose();
File.Delete(temppath);
I think you are not disposing the first Image instance assigned to the img variable.
Consider this instead:
System.Drawing.Image tempimg = System.Drawing.Image.FromFile(temppath);
System.Drawing.Image img = (System.Drawing.Image) tempimg.Clone();
tempimg.Dispose();
System.Drawing.Image img2 = resizeImage(img, 200, 200);
img2.Save(newpath);
img2.Dispose();
img.Dispose();
File.Delete(temppath);
If you create the image this way, it won't be locked:
using (FileStream fs = new FileStream(info.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
byte[] data = new byte[fs.Length];
int read = fs.Read(data, 0, (int)fs.Length);
MemoryStream ms = new MemoryStream(data, false);
return Image.FromStream(ms, false, false); // prevent GDI from holding image file open
}