VB.NET printing images using a lot of memory - vb.net

I'm working with PDFs in VB.NET using a DLL I found on code project:
http://www.codeproject.com/Articles/37458/PDF-Viewer-Control-Without-Acrobat-Reader-Installe
My app allows you to select multiple files in a grid and print them. The files are stored in password-protected zip files, so the first step I do is extract each selected file to a memory stream that I pass to a new PDF wrapper object. Each object gets added to a queue. Then, each object in the queue is printed, page by page, as a system.drawing.image. The whole thing runs on a background worker.
Now, extracting the PDFs to the queue uses hardly any memory. But in the PrintPage event handler, when I extract the images and send them to the printer, something must be going wrong. My memory usage explodes. Each image, of course, is large because it's rendered at 300 dpi, but the memory used by each page isn't being returned to the OS and neither is it being garbage collected.
In the end, if I select enough files, I run out of memory. Why?

Ok, so I finally figured it out.
First, as far as the images go, the CLR apparently doesn't know how much memory is allocated for a Drawing.Image so when you dispose it, you have to tell it:
'It's 4 bytes per pixel with RGBA
'Use Drawing.Image.PixelFormat to get
'the number of bytes if you don't know
Dim countBytes as long = 4 * img.Width * img.Height
'Let the CLR know of the memory we want to free
if countBytes > 0 then GC.AddMemoryPressure(countBytes)
'Get rid of the image
img.Dispose()
img = Nothing
'Free up the unused memory
GC.Collect()
'Tell the CLR we took care of it
GC.RemoveMemoryPressure(countBytes)
Now, the PDF library from the CodeProject sample was quite a bit more difficult.
First of all, make sure you call the Dispose method on the PDFWrapper object in either the FormClosed event of the form that holds the wrapper, or in the Finalize method of the class that holds it.
But, the PDFWrapper actually seems to cache the images you retrieve from it. So as you page through a PDF, memory usage will grow until the images for entire PDF are cached. This is an even bigger problem if you use those images to print the PDF at 300DPI (I get out of memory errors toward the end of a 60+ page PDF at 1.5GB of memory used).
There is no 'Clear Cache' method for this object as far as I can tell. But the hack I used to get it working was to grab an image at 1DPI after I get the image I need, then perform garbage collection as above. This indirectly frees up the memory that was cached. However, like before, we must tell the CLR how many bytes we used. It's the same calculation as above.
BUT there is one more problem. The PDFWrapper object is actually grabbing the images on another thread, it seems. So, by requesting another 1DPI image after we request the 300DPI image, it gets confused and randomly spits out 1DPI images when it should be giving us 300DPI images to print. So, the workaround for this:
Dim img As System.Drawing.Image
img = AFPDFLibUtil.GetImageFromPDF(pdfWrapper, currentPage, DPI)
'Wait for PDFWrapper to finish rendering
Dim sw As New Stopwatch()
sw.Start()
While _pdfWrapper.IsBusy
If sw.ElapsedMilliseconds < TimeoutMS Then
System.Threading.Thread.Sleep(10)
Else
Throw New Exception("This page took too long to render.")
End If
End While
sw.Stop()
sw.Reset()
And there you go. Perhaps that's why in the CodeProject sample, he uses a different DLL to do the printing. However, the PDFWrapper object supports reading from a IO.MemoryStream, I don't think any of the other includes in that project do.
Happy coding to anyone who reads this!

Related

Printing in Zebra label printer

I'm trying to print images on a Zebra printer using ZPL commands.
This is the code:
^XA
^FO10,10^XGR:ICONE.GRF,3,3^FS
^XZ
My problem is that I can't print image from the Flash Memory. I only get image printed from DRAM memory. Could some one give me some tips?
Your ZPL sample relies on the printer having been pre-configured correctly by uploading the image to the printer memory (at the printer memory path R:ICONE.GRF). This is a little more brittle than just embedding the image directly in the ZPL, as you have found out.
If you're always embedding the same image (which appears to be the case), and if you're not worried about shaving milliseconds off of your print latency (most people aren't), then I'd recommend embedding the image directly in your ZPL using the ^GF command.
There's a little bit of black magic involved in getting the ^GF command right, but it's pretty easy if you have ZebraDesigner installed or if you just use Labelary to add the image to your label ZPL template.
Why don´t you create a memory bitmap loading the image from E: into it and printing from this memory image? You could do something like:
Dim image1 As Bitmap = CType(Image.FromFile("E:\ImageFile.bmp", True), Bitmap)
You may also put a PictureBox in your form, load the image into it and call your Zebra code on that control - it´s in memory too.
I just figured out my stupid error in the code:
^FO10,10^XGR:ICONE.GRF,3,3^FS
The solution was simply change de "R" by "E":
^FO10,10^XGE:ICONE.GRF,3,3^FS
Thanks for the ansswers.

Workaround for inlineshape.LinkFormat.Update

I am writing a Word 2007 document with a lot of images that are sure to change before the document is delivered. Therefore, I am inserting them in the document as links to PNG files. My problem is that if I select the image and execute:
Selection.InlineShapes(1).LinkFormat.Update
MsgBox Selection.InlineShapes(1).LinkFormat is Nothing
the message box displays "True". That is, the Update method broke the link.
I have tried using Selection.InlineShapes(1).Delete, followed by Selection.InlineShapes.AddPicture. This updates the image, but now I need to crop the image and that introduces its own set of problems. Before trying to deal with the cropping issues, I'm hoping that someone has a better way of updating the linked file.
BTW, closing the document and reopening it updates the image nicely as long as the filename has not changed. The point of the macro is to cope with filename changes, if necessary.

Printing a form in vb.net

I wrote a small program to store and hopefully print contact information (in vb.net). The program contains a few text fields and a picture box. Saves the Information and so on. Can anyone recommend a quick way to save the form to a format that can then be transferred to her work computer and printed (she can't install my program at work, taboo policy and so on). Using the printform control, printform.print() with the PrintAction set to PrintToFile is just giving me garbled junk. I guess I could print to an html file bit by bit, but I thought I'd ask if anyone knows a better way. Also, with the html route I'm not sure how I'd add the contents of the picture box. Thanks in advance.
You can try saving your Form to a Bitmap using the DrawToBitmap Method which could then be saved as an image then printed out later, the main problem you will run into with this method is that the DPI settings are different between the screen and a printer.
Dim bmp As Bitmap = New Bitmap(Me.Width, Me.Height)
Me.DrawToBitmap(bmp, New Rectangle(New Point(0, 0), Me.Size))
bmp.Save("C:\temp\123.bmp") 'Set your path and your filename here

Printscreen the screen and save the image(VB)

Can someone help me, how can I printscreen my screen that can be saved in gif or jpeg format
locally in VB.net
I know this question was asked a long time ago so I post this for posterity.
It's quite trivial to preform a screen capture operation using the CopyFromScreen method in the Graphics class. Also, the BitMap class ships with a Save method; making this even more trivial.
Using image As New Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)
Using surface As Graphics = Graphics.FromImage(image)
surface.CopyFromScreen(Screen.PrimaryScreen.Bounds.Location, Point.Empty, image.Size)
End Using
image.Save("C:\myimage.jpg", Imaging.ImageFormat.Jpeg)
End Using
One possible solution when dealing with multiple monitors is to iterate, capture and save each screen as individual images. Place the above code inside the following For Each Next statement and replace Screen.PrimaryScreen with monitor. Be sure that you set a unique file name for each image.
For Each monitor As Screen In Screen.AllScreens
'...
Next

picture is used by another process

in my windows application there is a image saving process.i can save different images.the images details will seen in a grid ,when i click the corresponding row in grid the image will shown in a picturebox.i want to delete the open picture by pressing the delete key.i used the
"deletefile(path)" code for this operation.but there is an error that "This file is used by another process."if anyone knows the solution for this problem please help me.thank you.
Do you have a reference to a Bitmap object created from that file ? If so, the Bitmap object is locking the file and will prevent you from deleting it.
The problem is not where you delete your file, it lies in how you open your image to display it. Could you maybe add some code showing how you load your image ?
When you load an image using something list Bitmap.FromFile, the Bitmap object keeps a lock on the file until it is disposed. So you could simply use the
using(Bitmap bmp = Bitmap.FromFile(path))
{
/* The code using the bitmap to display it goes here */
}
construct to force it to release the file once you do not need it. This will prevent it from locking. The reason it locks is that it does not load the whole bitmap in memory when you create the bitmap object, it loads it lazily on demand, so it needs to keep a lock on the file.
Open the imagefile using Image.FromStream and make sure you close the stream after the image is loaded. That way you sould have no locks on the file.
Added after comment.
I don't have Visual Studio at hand and I'm a c# guy but it should look something like this.
Dim stream As New FileStream(specified_path, FileMode.Open)
Dim image As Image = Image.FromStream(stream)
picturebox1.image = image
stream.Close()