Is it possible to view multi page .Tif files in vb.net application? - vb.net

I am hoping to be able to view .Tif files in my vb.net application - is there a component that can be used to do that. I've been messing around with Tiff Viewer but for some reason the file is stretched beyond anything - you can barely make out what it says. I tried to adjust the width of the viewer but it did not do much, it is still stretched horizontally.

Based on https://stackoverflow.com/a/401579/741136, this will load a multiframe tif into a collection of single frames:
Function LoadTif(filename As String) As List(Of Image)
Dim lstTif As New List(Of Image)
Dim bmp As Bitmap = DirectCast(Image.FromFile(filename), Bitmap)
For i As Integer = 0 To bmp.GetFrameCount(Imaging.FrameDimension.Page) - 1
bmp.SelectActiveFrame(Imaging.FrameDimension.Page, i)
Dim ms As New System.IO.MemoryStream
bmp.Save(ms, Imaging.ImageFormat.Tiff)
lstTif.Add(Image.FromStream(ms))
ms.Dispose()
Next i
Return lstTif
End Function

Related

VB.NET Display Image in PictureBox from Binary DB Value

I am trying to take a database binary entry where images are stored and convert it back to display in a PictureBox on a Windows Form. I have looked around and tried to piece a couple methods together however i get no errors and also no image in the PictureBox when trying to use the below code.
Dim MyByte = varReader("BinaryData")
Dim MyImg As Image
If MyByte IsNot Nothing Then
MyImg = bytesToImage(MyByte)
PictureBox1.Image = MyImg
End If
Public Function bytesToImage(ByVal byteArrayIn As Byte()) As Image
Dim ms As MemoryStream = New MemoryStream(byteArrayIn)
Dim returnImage As Image = Image.FromStream(ms)
Return returnImage
End Function
I know the Binary data is good as i have a different piece of code that has created it in the first place to store in the DB and the image displays fine on the Website which uses this data.
Any help or advice where i have gone wrong would be much appreciated!!
Many Thanks
EDITED::
The database column holding the information is varbinary(MAX)
This is the code i use to convert the image to the binary (I won't include the DB update part as this is just a basic update to the DB Column)
Dim varPictureBinary As Byte()
Dim filePath As String = "ImageFilePath"
Dim fStream As FileStream = New FileStream(filePath, FileMode.Open, FileAccess.Read)
Dim br As BinaryReader = New BinaryReader(fStream)
Dim fileInfo As FileInfo = New FileInfo(filePath)
varPictureBinary = br.ReadBytes(fileInfo.Length)
I then want to take a binary entry from this column and turn it back to an image and display it in a picture box in my program.

How do I make an image not selectable

I have added an image to my iTextSharp PDF document like this:
Public Sub CreatePDFFromBitmap(ByVal uPath As String, ByVal uBitmap As Bitmap)
Dim nFs As System.IO.FileStream = New FileStream(uPath, FileMode.Create)
Dim nDocument As iTextSharp.text.Document
Dim nWriter As iTextSharp.text.pdf.PdfWriter
Dim nCb As iTextSharp.text.pdf.PdfContentByte
Dim nImgFromBitmap As System.Drawing.Image = DirectCast(uBitmap, System.Drawing.Image)
Dim nImg As iTextSharp.text.Image = iTextSharp.text.Image.GetInstance(nImgFromBitmap, Imaging.ImageFormat.Png)
Dim bLandscape As Boolean = (nImg.Width > nImg.Height)
'rotation needs to be set before document is being opened
If bLandscape Then
nDocument = New iTextSharp.text.Document(PageSize.A4.Rotate, 0, 0, 0, 0)
Else
nDocument = New iTextSharp.text.Document(PageSize.A4, 0, 0, 0, 0)
End If
'if an exception is raised here, the following will help: https://stackoverflow.com/questions/15833285/pdfwriter-getinstance-throws-system-nullreferenceexception
nWriter = iTextSharp.text.pdf.PdfWriter.GetInstance(nDocument, nFs)
nDocument.Open()
nCb = nWriter.DirectContent
nImg.ScaleToFit(nDocument.PageSize.Width, nDocument.PageSize.Height) 'raises dpi size :-)))
'X-Y-Koordinatensystem 0,0 startet also unten links, nicht oben-links
nImg.SetAbsolutePosition(0, nDocument.PageSize.Height - nImg.ScaledHeight)
nCb.AddImage(nImg)
nDocument.Close()
nWriter.Close()
nFs.Close()
End Sub
It works fine.
However, when I click the image in the PDF, it gets selected.
This is not what I want.
If I click the image in the PDF, it should not be selected.
This is what it looks like: The image becomes blue:
I want to add editable fields to the PDF, so I need to make the image not selectable, else it would confuse the user.
As Abdel-Rahman Al-Qawasmi mentions in his answer, it is completely up to the PDF viewer which entities it makes selectable and which not. Thus, there is no guaranteed way to get what you want.
Nonetheless, there are ways to put an image into a PDF which dissuade current versions of most PDF viewers from making it selectable. These ways either transform the bitmap image into a non-bitmap entity (e.g. by iterating over the pixels of the bitmap and drawing a little rectangle per pixel using vector graphics) or wrap the bitmap image into something that usually is not selectable.
Let's take the latter approach and wrap the image into a page-size PDF pattern with which we then fill the actual page. You can do that by replacing your
nCb.AddImage(nImg)
by
Dim painter As iTextSharp.text.pdf.PdfPatternPainter = nCb.CreatePattern(nDocument.PageSize.Width, nDocument.PageSize.Height)
painter.AddImage(nImg)
nCb.SetColorFill(New iTextSharp.text.pdf.PatternColor(painter))
nCb.Rectangle(0, 0, nDocument.PageSize.Width, nDocument.PageSize.Height)
nCb.Fill()
(This essentially is the VB/iTextSharp pendant of the Java/iText code from this answer.)
This is a pdf program specifications and not related to asp.net or vb.net programming. you need to have control of the pdf reader settings. Or try to use another format.

Resizing multiple images and saving them to a separate folder

I need to resize and compress 200 images that I have stored in a folder.
I am getting these images in a list using this code that I got from another question:
Dim dir = New IO.DirectoryInfo("C:\\Users\\Charbel\\Desktop\\Images")
Dim images = dir.GetFiles("*.jpg", IO.SearchOption.AllDirectories).ToList
Dim pictures As New List(Of PictureBox)
For Each img In images
Dim picture As New PictureBox
picture.Image = Image.FromFile(img.FullName)
pictures.Add(picture)
Next
Now, I need to compress and reduce each image to (500x374) and then save them in another folder on my PC.
Well, let me first point out a couple of points about your code:
PictureBox doesn't serve any purpose here. You shouldn't create a PictureBox to use the Image.
Always remember to dispose the Image object (e.g., by wrapping it in a Using block) so you don't run into memory issues.
Unlike C#, VB.NET doesn't require escaping the \ character, therefore, you can write your path like this "C:\Users...".
Now, for resizing the image, you can simply create an instance of the Bitmap class with the constructor that takes an image and a size argument: Bitmap(Image, Size) or Bitmap(Image, Int32, Int32).
Here:
Dim sourcePath As String = "C:\Users\Charbel\Desktop\Images"
Dim outputPath As String = "C:\Users\Charbel\Desktop\Images\Resized"
IO.Directory.CreateDirectory(outputPath)
Dim dir = New IO.DirectoryInfo(sourcePath)
Dim files As IO.FileInfo() = dir.GetFiles("*.jpg", IO.SearchOption.AllDirectories)
For Each fInfo In files
Using img As Bitmap = Image.FromFile(fInfo.FullName)
Using resizedImg As New Bitmap(img, 500, 374)
resizedImg.Save(IO.Path.Combine(outputPath, fInfo.Name),
Imaging.ImageFormat.Jpeg)
End Using
End Using
Next

Print Bitmap image over multiple pages using PDFSharp, Vb.net

I have a panel which draws a diagram based on user input. Unfortunately the diagrams can get really really big, and when I print them it doesn't entirely fit on a single page without losing readability.
I'm using PDFsharp within VB.net to create the PDF. The current method is the turn the panel in which the diagram is drawn on to a bitmap image, and then place the image in the pdf document.
1 diagram can easily make the panel 1500,3000 in size.
Example of Diagram
Example of Diagram in PDF
Any help is greatly appreciated
Dim pdfdoc As New PdfDocument
Dim page As PdfPage = pdfdoc.AddPage
Dim Bitmap As Bitmap = New Bitmap(pnl_Draw.Width, pnl_Draw.Height)
Dim BXImage As XImage
Dim GFX As XGraphics
Me.pnl_Draw.DrawToBitmap(Bitmap, New Rectangle(0, 0, Bitmap.Width, Height))
Dim pbx As New PictureBox
pbx.Image = Bitmap
BXImage = XImage.FromGdiPlusImage(pbx.Image)
GFX = XGraphics.FromPdfPage(page)
GFX.ScaleTransform(0.82)
GFX.DrawImage(BXImage, 0, 0)
GFX.Dispose()
pdfdoc.Save("G:\test.pdf")
pdfdoc.Close()
Try
Process.Start("G:\test.pdf")
Catch ex As Exception
End Try
Simple solution: in the Adobe Reader Print dialog click on "Poster" to print the image on more than 1 page. Increase the Zoom factor to distribute the poster on as many pages as you need.
You can also increase the page size proportionally to the bitmap size.
BTW: You don't need GFX.ScaleTransform(0.82) if you specify the destination size in the call to GFX.DrawImage.

how to prevent the Image.FromFile() method to lock the file

I am using following code to put JPG's into a DataGridView's Image cell.
If strFileName.ToLower.EndsWith(".jpg") Then
Dim inImg As Image = Image.FromFile(strFileName)
DataGridView4.Rows.Add()
DataGridView4.Rows(DataGridView4.Rows().Count - 1).Cells(0).Value = inImg
End If
The problem is that I need to save this file from within the program, but i get the message that the file is beeing used by another program.
So i tried to add inImg.Dispose() before the end if, but then the program doesnt display the images anymore in the DataGridView.
How can i add images in the DataGridView without locking them?
thanks
When you use the Image.FromFile(strFileName) method to create the Image, the method locks the file until you release the Image. The exact reason is explained below. And it's why you can't access more than one time to the same image file with this method.
You could instead:
use the Image.FromStream(stream) method.
that you use with a New FileStream or a MemoryStream that you create from the image file.
Here are possible implementation of a custom SafeImageFromFile method that doesn't lock the image file:
Public Shared Function SafeImageFromFile(path As String) As Image
Using fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim img = Image.FromStream(fs)
Return img
End using
End Function
Or
Public Shared Function SafeImageFromFile(path As String) As Image
Dim bytes = File.ReadAllBytes(path)
Using ms As New MemoryStream(bytes)
Dim img = Image.FromStream(ms)
Return img
End Using
End Function
Usage
If strFileName.ToLower.EndsWith(".jpg") Then
Dim inImg As Image = SafeImageFromFile(strFileName)
Dim index as integer = DataGridView4.Rows.Add()
DataGridView4.Rows(index).Cells(0).Value = inImg
End If
Important note
Here I create the FileStream or a MemoryStream using a Using statement to make sure the stream is released. It works fine on my system and it seems it work for you too, though MSDN says about Image.FromStream(stream) method:
You must keep the stream open for the lifetime of the Image.
The reason of this sentence is explain here: KB814675 Bitmap and Image constructor dependencies
GDI+, and therefore the System.Drawing namespace, may defer the
decoding of raw image bits until the bits are required by the image.
Additionally, even after the image has been decoded, GDI+ may
determine that it is more efficient to discard the memory for a large
Bitmap and to re-decode later. Therefore, GDI+ must have access to the
source bits for the image for the life of the Bitmap or the Image
object.
To retain access to the source bits, GDI+ locks any source file, and
forces the application to maintain the life of any source stream, for
the life of the Bitmap or the Image object.
So know the code above could generate GDIexceptions because of releasing the stream using Using. It could happen when you save the image from the file or during the image creation. From this thread Loading an image from a stream without keeping the stream open and Hans Passant's comment they fixed several problems with indexed pixel formats in the Vista version of gdiplus.dll., it would happen only on XP.
To avoid this you need to keep the stream open. The methods would be:
Public Shared Function SafeImageFromFile(path As String) As Image
Dim fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim img = Image.FromStream(fs)
Return img
End Function
Or
Public Shared Function SafeImageFromFile(path As String) As Image
Dim bytes = File.ReadAllBytes(path)
Dim ms = New MemoryStream(bytes)
Dim img = Image.FromStream(ms)
Return img
End Function
But those last methods have some disadvantage like not releasing the stream (memory issue) and they violate rule CA2000 Dispose objects before losing scope .
The KB article gives some workarounds:
Create a Non-Indexed Image
This approach requires that the new image be in a non-indexed pixel
format (more than 8 bits-per-pixel), even if the original image was in
an indexed format. This workaround uses the Graphics.DrawImage()
method to copy the image to a new Bitmap object:
Construct the original Bitmap from the stream, from the memory, or from the file.
Create a new Bitmap of the same size, with a pixel format of more than 8 bits-per-pixel (BPP).
Use the Graphics.FromImage() method to obtain a Graphics object for the second Bitmap.
Use Graphics.DrawImage() to draw the first Bitmap onto the second Bitmap.
Use Graphics.Dispose() to dispose of the Graphics.
Use Bitmap.Dispose() to dispose of the first Bitmap.
Create an Indexed Image
This workaround creates a Bitmap object in an indexed format:
Construct the original Bitmap from the stream, from the memory, or from the file.
Create a new Bitmap with the same size and pixel format as the first Bitmap.
Use the Bitmap.LockBits() method to lock the whole image for both Bitmap objects in their native pixel format.
Use either the Marshal.Copy function or another memory copying function to copy the image bits from the first Bitmap to the second Bitmap.
Use the Bitmap.UnlockBits() method to unlock both Bitmap objects.
Use Bitmap.Dispose() to dispose of the first Bitmap.
Here is an implementation of Non-Indexed Image creation, based on KB article and this answer https://stackoverflow.com/a/7972963/2387010 Your best bet is creating a pixel-perfect replica of the image -- though YMMV (with certain types of images there may be more than one frame, or you may have to copy palette data as well.) But for most images, this works:
Private Shared Function SafeImageFromFile(path As String) As Bitmap
Dim img As Bitmap = Nothing
Using fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fs)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
End Using
Return img
End Function
Someone indicated that what is important is that the FileStream is opened in read mode (FileAccess.Read).
True, but it makes more sens if you don't use Using statement and so you don't release the stream, or in multi threads context: FileAccess.Write is inappropriate, and FileAccess.ReadWrite is not required, but open the stream with FileAccess.Read mode won't prevent to have an IO.Exception if another program (or yours in multi threads context) has opened the file with another mode than FileAccess.Read.
If you want to be able to display the image and at the same time be able to save data to the file, Since you don't lock the file with those methods, you should be able to save the image (delete/overwrite the previous file) using the Image.Save method.
# Chris: Opening approximately 100 large (3400x2200) images with your final code, I was receiving an invalid argument crash on [img = new bitmap(...], I have seen this before opening an image of zero size, but that was not the case here. I added fs.dispose and successfully opened thousands of images of the same size of the same set as the first test without issue. I'm interested in your comments on this.
Private Function SafeImageFromFile(FilePath As String) As Image
Dim img As Bitmap = Nothing
Using fs As New FileStream(FilePath, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fs)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
fs.Dispose()
End Using
Return img
End Function
This works without issue, ran 4189 images 3400x2200 through it (twice) without issue, this moves the filestream outside of the function and re-uses it. Im closing the file to release the write lock. Im pointing a picturebox at this image in a loop for my test.
Private fsIMG As FileStream
Private Function SafeImageFromFile(FilePath As String) As Image
'Ref: http://stackoverflow.com/questions/18250848/how-to-prevent-the-image-fromfile-method-to-lock-the-file
Dim img As Bitmap = Nothing
fsIMG = New FileStream(FilePath, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fsIMG)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
fsIMG.Close()
Return img
End Function
After searching the internet for long time I found out I can use this code without any error.
Private fsIMG As FileStream
Private Function SafeImageFromFile(FilePath As String) As Image
'Ref: http://stackoverflow.com/questions/18250848/how-to-prevent-the-image-fromfile-method-to-lock-the-file
Dim img As Bitmap = Nothing
fsIMG = New FileStream(FilePath, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fsIMG)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
fsIMG.Close()
Return img
End Function
I encountered the same situation and used this code:
' Create memory stream from file
Dim ms As New MemoryStream()
' Open image file
Using fs As New FileStream(.FileName, FileMode.Open, FileAccess.Read)
' Save to memory stream
fs.CopyTo(ms)
End Using
' Create image from the file's copy in memory
Dim img = Image.FromStream(ms)
I didn't dispose the memory stream because it allows to save the image later using exactly the same encoding as the original file, using this code:
img.Save(someOtherStream, img.RawFormat)