Getting MetaData from an image file - wpf

We have images stored on a DB and they are being used to replace an image within a Word document - that bit works perfectly, except where the replacement image is portrait and it's replacing a landscape one, so I'm trying to get the metadata to determine how the image is orientated using this function
Public Function GetImageTags(ImageFile() As Byte) As String()
Try
Dim vReturnArray() As String = Nothing
Using MS As New System.IO.MemoryStream(ImageFile)
Dim vDecoder As BitmapDecoder = BitmapDecoder.Create(MS, BitmapCreateOptions.None, BitmapCacheOption.Default)
Dim vFrame As BitmapFrame = vDecoder.Frames(0)
Dim vMetadata As BitmapMetadata = TryCast(vFrame.Metadata, BitmapMetadata)
If vMetadata IsNot Nothing And vMetadata.Keywords IsNot Nothing Then
vReturnArray = vMetadata.Keywords.ToArray()
End If
End Using
Return vReturnArray
Catch ex As Exception
EmailError(ex)
Return Nothing
End Try
End Function
...but it throws the toys out with...
This codec does not support the specified property.
at System.Windows.Media.Imaging.BitmapMetadata.GetQuery(String query)
at System.Windows.Media.Imaging.BitmapMetadata.get_Keywords()
...at BitMapMetadata.Keywords. Any idea how I can overcome this and get the keywords?
Thank you
================ UPDATE ================
It appears that the error, and I also tried...
vReturnArray = TryCast(vMetadata.GetQuery("System.Keywords"), String())
... is only returned for some images, but all that I tried returned Nothing for the String()

There is a really good EXIF class on Code Project that is easy to implement, either with a string link to the file
Dim vEXIF As New ImageEXIF(ImagePath)
Dim vOrientation As Integer = vEXIF.Orientation
or as BitMap
Dim vOrientation As Integer = 0
Using vBitmap As System.Drawing.Image = System.Drawing.Image.FromStream(New IO.MemoryStream(ImageFile))
Dim vEXIF As New ImageEXIF(vBitmap)
vOrientation = vEXIF.Orientation
End Using
It would not be difficult to add another Sub to the class for Byte(), but the above conversion is quite straightforward and the class should work with all image types.

You could use MetadataExtractor to access the image metadata.
Check for the presence of ExifDirectoryBase.TagOrientation on any of the contained Exif directories.
Something like this (sorry it's C# as I don't know VB.NET):
var orientation = ImageMetadataReader.ReadMetadata(imagePath)
.OfType<ExifSubIfdDirectory>()
.Select(d => d.GetObject(ExifDirectoryBase.TagOrientation))
.First(o => o != null);

Related

Comma-separated string to data table error: input array is longer

I am trying to put data received from an API call that is comma-delimited into a datatable.
The data comes in something like this:
Name,ID,Date,Supervisior CRLF
Joe,123,1/1/2020,George CRLF
Mike,456,2/1/2020,George CRLF
Dan,789,4/1/2021,George
If there is only one row of data then my code works the data displays on screen just fine.
If there is more than one row I get an error "Input array is longer than the number of columns in this table."
I tried doing a split on comma and environment new line (also vbCRLF); none of those resolved the issue.
Any ideas on how I can resolve this?
Here is my code:
Dim vartable As DataTable = New DataTable()
vartable.Columns.Add("Name", GetType(String))
vartable.Columns.Add("ID", GetType(String))
vartable.Columns.Add("Date", GetType(String))
vartable.Columns.Add("Supervisior", GetType(String))
Dim inputstring As String
inputstring = (apiresponse) 'redacted API code as it works fine If I just display 'raw data to text field
Dim rowData As String() = inputstring.Split(New Char() {",",Environment.NewLine})
vartable.Rows.Add(rowData) 'this is where I get the input array error if 'more than one row of 'data
GridView1.DataSource = vartable
GridView1.DataBind()
It looks like you're expecting the Split() function to act on each of the delimiters separately, so you get an array of arrays, with each element in the outer array holding one line/row. This is not how it works.
You need to separate the lines first, and then in a loop for each line separate the contents by comma. The test way to do this is NOT by calling Split(). Instead, you can use a StringReader (which is different from StreamReader):
Using rdr As New StringReader(apiresponse)
Dim line As String = rdr.ReadLine()
While line IsNot Nothing
Dim rowData As String() = line.Split(","c)
vartable.Rows.Add(rowData)
line = rdr.ReadLine()
End While
End Using
But I would be surprised to learn the code to access the API doesn't also need to deal with streams at some point, even if you don't see it directly, meaning there exists a possible version of this that is even more efficient from using StreamReader connected to the api response directly.
For fun, since it's been a while since I've had to do this in VB, I made this extension module:
Public Module TextExt
<Extension()>
Public Iterator Function AsLines(data As TextReader) As IEnumerable(Of String)
Dim line As String = data.ReadLine()
While line IsNot Nothing
Yield line
line = data.ReadLine()
End While
End Function
<Extension()>
Public Iterator Function AsLines(data As String) As IEnumerable(Of String)
Using rdr As New StringReader(data)
For Each line As String In AsLines(rdr)
Yield line
Next
End Using
End Function
End Module
Which would let me write it like this:
For Each row As String() In apiresponse.AsLines().Select(Function(ln) ln.Split(","c))
vartable.Rows.Add(row)
Next
Finally, I need to add my customary warning about how it's a really bad idea to use .Split() as a CSV parser.

Error reading JArray from JsonReader VB.net

where does it go wrong?
my coding
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Imports System.Net
Public Class DigiposAJA
Private Sub CekPaket()
Dim json As String = (New WebClient).DownloadString("http://192.168.101.1:100/list_product?username=SIP12&category=ROAMING&to=0811&payment_method=LINKAJA&json=1")
Dim jarr As JArray = Linq.JArray.Parse(json)
Dim sKatagori As String
For Each jtk As JToken In jarr
sKatagori = jtk.SelectToken("kategori")
DgvDigipos.Rows.Add()
DgvDigipos.Rows(DgvDigipos.Rows.Count - 1).Cells("DgvKategori").Value = sKatagori
Next
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
CekPaket()
End Sub
End Class
after I debug the result is an error like this.
Newtonsoft.Json.JsonReaderException: 'Error reading JArray from JsonReader. Current JsonReader item is not an array: StartObject. Path '', line 1, position 1.'
Can you help me to get a great result
Most likely this is a result of your call to the web service not returning the result you expect.
This is actually a good example of the benefits of separation of concerns and strongly typed objects. Your sub CekPaket should be broken down into 3 parts. 1) get the string. This should be a function and should use some sort of configuration to get the end point and have appropriate guards for failure, 2) parse the string into a strongly typed object (IEnumerable of whatever), this should validation to make sure that the input is good. You might want to make this function public for easy testing. And finally 3) bind your results to your UI. It looks like you are doing this part by hand, whenever possible you should allow the frame work to do this for you by providing a data source and a template for the display.

Decode base64 to image in vb.net

I've been searching on the internet and have not found an answer. Would you like to tell me, how to decode from base64 to be Image like line graph? I've been trying to convert from base64 to Byte array first and from Byte array to Image.
Private Function convertbytetoimage(ByVal BA As Byte())
Dim ms As MemoryStream = New MemoryStream(BA)
image = Image.FromStream(ms) 'I always get wrong in this line.
Return image
End Function
Looking at your code, your problem could be using the variable name image instead of something like _image.
Keep in mind that VB is not case sensitive like C# and other programming languages.
In your code, I assume you defined your image variable as Image.
To use the static Image.FromStream(ms), you either need to use the fully qualified name of Image or change your variable name.
Here's how you can fix your code:
Private Function convertbytetoimage(ByVal BA As Byte())
Dim ms As MemoryStream = New MemoryStream(BA)
image = System.Drawing.Image.FromStream(ms)
Return image
End Function
Or you can do this by changing your variable name, such as:
Dim _image as Image
Private Function convertbytetoimage(ByVal BA As Byte())
Dim ms As MemoryStream = New MemoryStream(BA)
_image = Image.FromStream(ms)
Return _image
End Function
#Update:
You can try to convert the Byte array to Image also by using ImageConvertor:
Private Function convertbytetoimage(ByVal BA As Byte())
Dim converter As ImageConverter = New ImageConverter()
_image = CType(converter.ConvertFrom(BA), Image)
Return _image
End Function
#Update 2:
Since it looks like that the main problem is with the base64 string. Please have a look at my small demo that convert an Image from inside a PictureBox to base64 string, then to Byte array, and at the end, back to an Image.
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim base64String = ConvertImageToBase64String() 'Using Functions To Make the code tidier
Dim byteArray = ConvertBase64ToByteArray(base64String) 'Using Functions To Make the code tidier
Dim image = convertbytetoimage(byteArray) 'Using Functions To Make the code tidier
PictureBox2.Image = image 'since we're using a small windows form app, we'll set back the image to a second picture box.
End Sub
Public Function ConvertImageToBase64String() As String
Using ms As New MemoryStream()
PictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Png) 'We load the image from first PictureBox in the MemoryStream
Dim obyte = ms.ToArray() 'We tranform it to byte array..
Return Convert.ToBase64String(obyte) 'We then convert the byte array to base 64 string.
End Using
End Function
Public Function ConvertBase64ToByteArray(base64 As String) As Byte()
Return Convert.FromBase64String(base64) 'Convert the base64 back to byte array.
End Function
'Here's the part of your code (which works)
Private Function convertbytetoimage(ByVal BA As Byte())
Dim ms As MemoryStream = New MemoryStream(BA)
Dim image = System.Drawing.Image.FromStream(ms)
Return image
End Function
Note that after converting the Image to base64 string, it looks something like that (keep in mind that each image is different, hence you won't get the same string):
/9j/4AAQSkZJRgABAQEAYABgAAD/4QBaRXhpZgAATU0AKgAAAAgABQMBAAUAAAABAAAASgMDAAEAAAABAAAAAFEQAAEAAAABAQAAAFERAAQAAAABAAAAAFESAAQAAAABAAAAAAAAAAAAAYagAACxj//bAEMACAYGBwYFCAcHBwkJCAoMFA0MCwsMGRITDxQdGh8eHRocHCAkLicgIiwjHBwoNyksMDE0NDQfJzk9ODI8LjM0Mv/bAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIADAAMAMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/APDdH01tY1a3sElWIzMQZGBIUAEk4HXgGvfvC/hPTbDSIzZabLcjnMyxoC54zlzgtyo46A5xjNeC6BfR6brtpdTcRI+HPPyhgVJ45OM5x7V9kaFYpYeH7W1keNpIU2O0ZypIPOD6ZrhxSlKXLfSx14eUYx5utzy3U7C7uL1bZLZoI26tIm3Ht715/wCNdK0p45JbRf3yIWaXHJKjv7YGOc4r6C1qIS2VxGOrIQPrjivn7xNbSWVtdW8wAkEbE4Of4a4sPN+0sddVKVO7R5tRRRXtnkhX1d4N8V2+seGrOcTw+e8CSTRxlvkY5VuG5ALq+O3HFfPngHwmPFeueTMSLWHDSY/jJPC5HIyAxz/s4yCQa+iItJtdIt7a0tYIVjV0QIiBV5YA8DA5yT9ea5cVQdWK5XZo3oVVTeuzDV9XRYHKsMAcmvn/AMdXV214wIJhm+ZpMdDk4X26Z96+i/EHhm2htWubpgiL8scaMcu3+FedalotjOhSVM57nkfjXJgcLOMnUqHTisRBwUIHg1FdR4n8LNpcbXttG32VZBFIAGIjYgleffaep7H8OXr1Tzz2D4OExWU829wBO42hztJ2pyR0JHY9sn1NesQT/atTsoyes6E/gc/0r5p8LeK5/DlwAQ8lqzF2SMqrbtuM5IOR0JHGdo5Fer6N4+0rUfLKXSw3G0nY3BB289fQE8+xoA7zxPq/9pagyo3+jw5VPf1NcXqMw55qefUoSnySKR6g1z9/fKc/OPzoA5vx0TJpgYOw5UkA/e7YP8/wrzuuz8VajDcWQgEqhwBwTyef/rVxlAH/2Q==
If you look at the first 5 characters of the string, you notice it's equal to /9j/4 which means that the file represented by this string is a PNG file, you may look at my old answer to see how to validate a base64 string.
Another note, I used a small PNG image, hence I used the next format:
PictureBox1.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Png)
If you have another Format and you want to use same code, make sure to change the format.

Deserializing byte to image from sql through memory stream

I have convert an image to be saved in SQL Server Database as Binary with column name as "img". I have PictureBox1 ready to show the image.
Now I want to import the binary data back into image, and I'm trying this code in VB.net for example:
Dim queries As String
queries = "SELECT * from StudentData where Std_fname='" & ComboBox1.Text & "'"
Dim com As New SqlCommand(queries, sqlconn)
sqlconn.Open()
Dim ds As New SqlDataAdapter(queries, sqlconn)
Dim dr As SqlDataReader
dr = com.ExecuteReader()
While dr.Read
Std_fnameTextBox.Text = dr("Std_fname")
Std_lnameTextBox.Text = dr("Std_lname")
AgeTextBox.Text = dr("age")
AddressTextBox.Text = dr("address")
StateTextBox.Text = dr("state")
CityTextBox.Text = dr("city")
CountryTextBox.Text = dr("country")
Ic_passportTextBox.Text = dr("ic_passport")
DobDateTimePicker.Text = dr("dob")
PictureBox1.Image = dr("img") 'Here is the problem. If I run it, it ask me to convert Binary to Image first.
End While
sqlconn.Close()
The problem is, I don't know how to convert binary to image in this situation. And yes, I've been googling for it, but can't seem to get the right answer.
dr("img") returns either DBNull.Value, or a byte[]. You can use the stream overload of the Bitmap constructor to load this. In C# (should be easy to translate to VB), you can do it like this:
var imageData = (byte[])dr["img"];
using (var ms = new MemoryStream(imageData))
{
var bmp = new Bitmap(ms);
// Work with bmp
}
As Mark correctly noted, you're supposed to keep the stream open for the whole life-time of the Bitmap - this is actually a bit trickier than it seems, because the Bitmap doesn't even keep a reference to the stream.
The easiest way to handle this is to clone the bitmap after you create it, to remove the dependency on the stream. Unless you can do whatever work you need to do within the using - unlikely if you want to display it in a PictureBox.
You can also use ImageConverter.ConvertFrom directly, but all it does is create the MemoryStream if used on raw byte[] data :)

XPS from FlowDocument Rendering bug in Images

This will be little longer post
Goal
Render XPS document from FlowDocument while maintaining original Images (no transformation)
Existing Scenarios
Image is added correctly (keeps format), but only first one. Cache is then broken, only 1 image is embedded in XPS and used for all images
All images are added and correct, but converted to PNG.
Difference occurs based on commenting / uncommenting of single line in method GetImage (see comments)
Minimal showcase / problem recreation code
Imports System.Windows.Documents
Imports System.Windows.Documents.Serialization
Imports System.Windows.Xps.Packaging
Imports System.Windows.Xps
Imports System.IO
Imports System.IO.Packaging
Imports System.Windows.Markup
Imports System.Windows.Media.Imaging
Imports System.Windows.Media
Imports System.Windows.Controls
Module Module1
Sub Main()
Render()
End Sub
Sub Render()
Using image1 = IO.File.OpenRead("image1.jpg"),
image2 = IO.File.OpenRead("image2.png"),
file = IO.File.Create("asdf.xps"),
pack = Package.Open(file, FileMode.Create),
d As New XpsDocument(pack)
Dim writer As XpsDocumentWriter = XpsDocument.CreateXpsDocumentWriter(d)
Dim xpsVisWriter As SerializerWriterCollator = writer.CreateVisualsCollator()
Dim x As New FlowDocument
x.PageWidth = 100
x.ColumnWidth = x.PageWidth
Dim s As New Section
DirectCast(x, IAddChild).AddChild(s)
s.BreakPageBefore = True
Dim p As New Paragraph()
DirectCast(s, IAddChild).AddChild(p)
Dim i As New Image
DirectCast(p, IAddChild).AddChild(i)
i.Source = GetImage(image1)
Dim i2 As New Image
DirectCast(p, IAddChild).AddChild(i2)
i2.Source = GetImage(image2)
Dim paginator = DirectCast(x, IDocumentPaginatorSource).DocumentPaginator
Dim pageIndex As Integer = 0
While Not paginator.IsPageCountValid OrElse paginator.PageCount > pageIndex
Dim page As DocumentPage = paginator.GetPage(pageIndex)
xpsVisWriter.Write(page.Visual)
pageIndex += 1
End While
xpsVisWriter.EndBatchWrite()
End Using
End Sub
Function GetImage(stream As Stream) As ImageSource
Dim result As ImageSource = BitmapFrame.Create(
stream,
BitmapCreateOptions.PreservePixelFormat Or BitmapCreateOptions.IgnoreImageCache,
BitmapCacheOption.None)
'If this Line Runs, scenario 2 occures, otherwise scenario 1
'result = BitmapFrame.Create(result)
Return result
End Function
End Module
I no longer have any idea what to do with this. All possible cache enabling / disabling was tried.
I have not done any visual basic, but I ran into similar problem with c#. Try returning a BitmapImage instead of ImageSource in your GetImage method. Using PngBitmapDecoder, MemoryStream, FlowDocument, XPSDocument to Preview Images

Resources