How can I read file properties/metadata? - c#

I wanted read system file properties like the same shown in the picture below, specifically the Title and Copyright properties. How can I do this?

Generally you can use System.Diagnostics,
FileVersionInfo info = FileVersionInfo.GetVersionInfo("path\to\file");
Then examine the FileDescription and LegalCopyright properties. However for images the case is different, you need to extract the bitmap metadata explicitly. Consider,
using (Stream fs = File.Open("path\to\file", FileMode.Open, FileAccess.ReadWrite))
{
BitmapDecoder decoder =
BitmapDecoder.Create(
fs, BitmapCreateOptions.None, BitmapCacheOption.Default);
BitmapFrame frame = decoder.Frames[0]; // the first frame with the metadata
BitmapMetadata metadata = frame.Metadata as BitmapMetadata;
if (metadata != null)
{
// examine metadata.Title, metadata.Copyright
}
fs.Close();
}
You can find all the properties listed at the BitmapMetadata Class documentation on MSDN.

You would need to load the image into a Bitmap, and access the PropertyItems.
Bitmap image = new Bitmap("YOUR IMAGE PATH HERE");
PropertyItem[] propItems = image.PropertyItems;
The following would get the manufacturer...
System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
string manufacturer = encoding.GetString(propItems[1].Value);
You need to do similar conversions depending on what details you are after.
foreach (PropertyItem item in propItems)
{
Console.WriteLine("ID : " + item.Id + " , VALUE : " + encoding.GetString(item.Value));
}

Related

How to change default image folder on Xamarin forms (android)

I am having the issue of "Canvas Drawing too large bitmaps". After a quick search, I found the following thread, which promptly helped me know what is the issue.
The solution is to put the image in drawable-xxhdpi/ instead of simply drawable/. And here lies the issue: the image is not static, it is imported when I need it. As such, I do not chose where the image ends up stored. It store itself in drawable. Is there 1) A solution to chose which folder to use, or 2) a way to tell it not get the image if it's too heavy?
var file = new SmbFile(path, auth);
try
{
if (file.Exists())
{
// Get readable stream.
var readStream = file.GetInputStream();
//Create reading buffer.
MemoryStream memStream = new MemoryStream();
//Get bytes.
((Stream)readStream).CopyTo(memStream);
var stream1 = new MemoryStream(memStream.ToArray());
if (stream1.Length < 120188100)
{
//Save image
ProductImage = ImageSource.FromStream(() => stream1);
//Dispose readable stream.
readStream.Dispose();
InfoColSpan = 1;
}
else
{
Common.AlertError("Image trop lourde pour l'affichage");
}
}
}

BitmapMetadata.GetQuery() threw "Metadata query request is not valid"

I am trying to extract time information from a tiff file using metadata class; Here is the part where time information is in the tiff file:
<Plane TheZ="0" TheT="0" TheC="0" DeltaT="0.2345"/>
where 0.2345 is the information I am trying to extract.
And here is my code that tried to get it out:
string searchtext = "DeltaT=";
FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
TiffBitmapDecoder tbd = new TiffBitmapDecoder(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
if (tbd.Frames[0] != null && tbd.Frames[0].Metadata != null)
{
//BitmapMetadata bmd = new BitmapMetadata("tiff");
BitmapMetadata bmd = tbd.Frames[0].Metadata as BitmapMetadata;
bmd.GetQuery(#searchText);
}
However, the line bmd.GetQuery(#searchText); threw an exception "Metadata query request is not valid"; I am not sure how to change it to make it get the 0.2345 value. Anyone has any idea? Thanks alot.
Here is the tiff file so that you can take a look: http://dl.dropbox.com/u/105139407/ChanA_0001_0001_0001_0003.tif
Apparently the XML you're looking for is in "/ifd/{ushort=270}" (don't ask me why - I don't know anything about how TIFF metadata is supposed to look like...). Note that it returns the whole XML document, so you still have to parse it. This code retrieves the value of DeltaT:
var decoder = new TiffBitmapDecoder(new Uri(fileName), BitmapCreateOptions.None, BitmapCacheOption.Default);
var metadata = (BitmapMetadata)decoder.Frames[0].Metadata;
string xml = (string)metadata.GetQuery("/ifd/{ushort=270}");
var doc = XDocument.Parse(xml);
var ns = doc.Root.GetDefaultNamespace();
var plane = doc.Root.Element(ns + "Image")
.Element(ns + "Pixels")
.Element(ns + "Plane");
double deltaT = (double)plane.Attribute("DeltaT");
EDIT: here's a LINQPad script I use to have a quick look at the metadata of an image: http://pastebin.com/daBTdW33. Feel free to use it or adapt it to your needs ;)

Loading Album art with TagLib sharp and then saving it to same/different file in C# causes memory error

My application lists all MP3's in a directory and when the user selects a file it loads the tag info, including album art. The art is loaded into a variable to be used when the user saves the data. The art is also loaded into a picture frame for the user to see.
// Global to all methods
System.Drawing.Image currentImage = null;
// In method onclick of the listbox showing all mp3's
TagLib.File f = new TagLib.Mpeg.AudioFile(file);
if (f.Tag.Pictures.Length > 0)
{
TagLib.IPicture pic = f.Tag.Pictures[0];
MemoryStream ms = new MemoryStream(pic.Data.Data);
if (ms != null && ms.Length > 4096)
{
currentImage = System.Drawing.Image.FromStream(ms);
// Load thumbnail into PictureBox
AlbumArt.Image = currentImage.GetThumbnailImage(100,100, null, System.IntPtr.Zero);
}
ms.Close();
}
// Method to save album art
TagLib.Picture pic = new TagLib.Picture();
pic.Type = TagLib.PictureType.FrontCover;
pic.MimeType = System.Net.Mime.MediaTypeNames.Image.Jpeg;
pic.Description = "Cover";
MemoryStream ms = new MemoryStream();
currentImage.Save(ms, ImageFormat.Jpeg); // <-- Error occurs on this line
ms.Position = 0;
pic.Data = TagLib.ByteVector.FromStream(ms);
f.Tag.Pictures = new TagLib.IPicture[1] { pic };
f.save();
ms.Close();
If I load the image and try to save it right away I get this "Attempted to read or write protected memory. This is often an indication that other memory is corrupt." If I try to save currentImage as a ImageFormat.Bmp I get this "A generic error occurred in GDI+."
My save method works correctly if I load an image from a url like this:
WebRequest req = WebRequest.Create(urlToImg);
WebResponse response = req.GetResponse();
Stream stream = response.GetResponseStream();
currentImage = Image.FromStream(stream);
stream.Close();
So I'm guessing there is an issue with the way that I am loading the image into currentImage when the user selects an MP3 from the listbox.
I have found a lot of examples of loading and saving images to mp3's but no one seems to be having this issue when they try to save the are immediately after loading it.
Thanks for the help Jim but I couldn't really get it working using the 'using' blocks so I'm guessing the stream was still closing somewhere. I found another way to do what I was looking for by storing/saving a byte[] instead of an Image. And then just saving it using:
using (MemoryStream ms = new MemoryStream(currentImageBytes))
{
pic.Data = TagLib.ByteVector.FromStream(ms);
f.Tag.Pictures = new TagLib.IPicture[1] { pic };
if (save)
f.Save();
}
Your stream stuff should be in using blocks, which will dispose of your goods automagically and close them too. Not terribly important, but easier to manage.
Your generic GDI+ error is likely because you are trying to perform an operation or call a method on a file for which the stream is already closed.
Check it out...
your method isn't really false, only a few things had to be changed:
// Method to save album art
TagLib.Picture pic = new TagLib.Picture();
pic.Type = TagLib.PictureType.FrontCover;
pic.MimeType = System.Net.Mime.MediaTypeNames.Image.Jpeg;
pic.Description = "Cover";
MemoryStream ms = new MemoryStream();
currentImage.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); // <-- Error doesn't occur anymore
ms.Position = 0;
pic.Data = TagLib.ByteVector.FromStream(ms);
f.Tag.Pictures = new TagLib.IPicture[1] { pic };
f.save();
ms.Close();

C#: Retrieve JPEG Comment (not Exif)

I've been stumped trying to figure this one out.
I'm trying to retrieve the "Jpeg Comment" out of a jpg file via C#.
The code below works but I need the basic comment NOT the Exif comment.
I'm using FastStone Image Viewer to set the basic comment. Help me retrieve it.
I can use the commandline program exiv2 to verify that the comment is there.
exiv2 -pc c:\test.jpg (it spits out the basic comment).
exiv2 -pa c:\test.jpg (it spits out the EXIF comment)
I've used several C# libs to get at it but they get the EXIF data.
Image x = Image.FromFile(#"c:\test.jpg");
PropertyItem prop;
prop = x.GetPropertyItem(0x9286);
string Comment = Encoding.ASCII.GetString(prop.Value);
You could refer to this link.
(Thanks for those who already have answered the same question, although the answer was quite right but not 100% to solve this problem.)
Here are three steps you need to do:
Be aware that you should have the Jpeg file cloned.
Set the comment of the cloned file.
Replace the file by deleting the original jpeg file.
Here is the sample code:
public void addImageComment(string imageFlePath, string comments)
{
BitmapDecoder decoder = null;
BitmapFrame bitmapFrame = null;
BitmapMetadata metadata = null;
FileInfo originalImage = new FileInfo(imageFlePath);
if (File.Exists(imageFlePath))
{
// load the jpg file with a JpegBitmapDecoder
using (Stream jpegStreamIn = File.Open(imageFlePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
decoder = new JpegBitmapDecoder(jpegStreamIn, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
}
bitmapFrame = decoder.Frames[0];
metadata = (BitmapMetadata)bitmapFrame.Metadata;
if (bitmapFrame != null)
{
BitmapMetadata metaData = (BitmapMetadata)bitmapFrame.Metadata.Clone();
if (metaData != null)
{
// modify the metadata
metaData.Comment = comments;
// get an encoder to create a new jpg file with the new metadata.
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapFrame, bitmapFrame.Thumbnail, metaData, bitmapFrame.ColorContexts));
//string jpegNewFileName = Path.Combine(jpegDirectory, "JpegTemp.jpg");
// Delete the original
originalImage.Delete();
// Save the new image
using (Stream jpegStreamOut = File.Open(imageFlePath, FileMode.CreateNew, FileAccess.ReadWrite))
{
encoder.Save(jpegStreamOut);
}
}
}
}
}
You can do this quite simply with the MetadataExtractor library (available via NuGet):
JpegCommentDirectory jpegCommentDirectory = ImageMetadataReader.ReadMetadata(imagePath)
.OfType<JpegCommentDirectory>()
.FirstOrDefault();
string comment = jpegCommentDirectory?.GetDescription(JpegCommentDirectory.TagComment);

InPlaceBitmapMetadataWriter.TrySave() returns true but does nothing

On some .JPG files (EPS previews, generated by Adobe Illustrator) in Windows 7 InPlaceBitmapMetadataWriter.TrySave() returns true after some SetQuery() calls, but does nothing.
Code sample:
BitmapDecoder decoder;
BitmapFrame frame;
BitmapMetadata metadata;
InPlaceBitmapMetadataWriter writer;
decoder = BitmapDecoder.Create(s, BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile, BitmapCacheOption.Default);
frame = decoder.Frames[0];
metadata = frame.Metadata as BitmapMetadata;
writer = frame.CreateInPlaceBitmapMetadataWriter();
try {
writer.SetQuery("System.Title", title);
writer.SetQuery(#"/app1/ifd/{ushort=" + exiftagids[0] + "} ", (title + '\0').ToCharArray());
writer.SetQuery(#"/app13/irb/8bimiptc/iptc/object name", title);
return writer.TrySave();
}
catch {
return false;
}
Image sample
You can reproduce problem (if you have Windows 7) by downloading image sample and using this code sample to set title on this image.
Image has enough room for metadata - and this code sample works fine on my WinXP.
Same code works fine on Win7 with other .JPG files.
Any ideas are welcome :)
Two things:
I don't think you will be able to write to your metadata variable just like that, as it will be Frozen. So, you will have to clone it:
BitmapMetadata metadata = frame.Metadata.Clone() as BitmapMetadata;
Padding, you need padding. I found this out after about a day's worth of tinkering around trying to make some code (similar to yours) work. InPlaceBitmapMetadataWriter will not work if there is no metadata padding in your image file. So you need something like:
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
if(frame != null && metadata != null) {
metadata.SetQuery("/app1/ifd/PaddingSchema:Padding", padding);
encoder.Frames.Add(BitmapFrame.Create(frame, frame.Thumbnail, metadata, frame.ColorContexts));
using (Stream outputFile = File.Open(_myoutputpath, FileMode.Create, FileAccess.ReadWrite)) {
encoder.Save(outputFile);
}
}
Now you can use the file located at _myoutputpath which has added metadata padding for your InPlaceBitmapMetadataWriter operations.
This article and attached code should help you out.
Hi I found this article about InPlaceBitmapMetadataWriter where the guy said that TrySave() might corrupt the image and that's why he advised to do TrySave() on the copy of the original file and if this doesn't work, add padding to the copy of original file and than TrySave() again and if it works, delete the original and rename the copy.
I scratched my head and asked myself why I should bother with InPlaceBitmapMetadataWriter and writing 3x original file to the disk in case TrySave() doesn't work because there is not enough padding, if I can clone metadata, write whatever into them and assemble jpeg file right away.
Then I started to think that maybe thanks to InPlaceBitmapMetadataWriter I can edit metadata without losing quality, but it looks like it "just" helps you to write metadata more quickly if there is enough padding.
I wrote a small test where I compress one file many times to see the quality degradation and you can see it in the third-fourth compression, which is very bad.
But luckily, if you always use same QualityLevel with JpegBitmapEncoder there is no degradation.
In this example I rewrite keywords 100x in metadata and the quality seems not to change.
private void LosslessJpegTest() {
var original = "d:\\!test\\TestInTest\\20150205_123011.jpg";
var copy = original;
const BitmapCreateOptions createOptions = BitmapCreateOptions.PreservePixelFormat | BitmapCreateOptions.IgnoreColorProfile;
for (int i = 0; i < 100; i++) {
using (Stream originalFileStream = File.Open(copy, FileMode.Open, FileAccess.Read)) {
BitmapDecoder decoder = BitmapDecoder.Create(originalFileStream, createOptions, BitmapCacheOption.None);
if (decoder.CodecInfo == null || !decoder.CodecInfo.FileExtensions.Contains("jpg") || decoder.Frames[0] == null)
continue;
BitmapMetadata metadata = decoder.Frames[0].Metadata == null
? new BitmapMetadata("jpg")
: decoder.Frames[0].Metadata.Clone() as BitmapMetadata;
if (metadata == null) continue;
var keywords = metadata.Keywords == null ? new List<string>() : new List<string>(metadata.Keywords);
keywords.Add($"Keyword {i:000}");
metadata.Keywords = new ReadOnlyCollection<string>(keywords);
JpegBitmapEncoder encoder = new JpegBitmapEncoder {QualityLevel = 80};
encoder.Frames.Add(BitmapFrame.Create(decoder.Frames[0], decoder.Frames[0].Thumbnail, metadata,
decoder.Frames[0].ColorContexts));
copy = original.Replace(".", $"_{i:000}.");
using (Stream newFileStream = File.Open(copy, FileMode.Create, FileAccess.ReadWrite)) {
encoder.Save(newFileStream);
}
}
}
}
I still didn't find the answer and has to write wrapper for exiftool instead of using WPF's way to work with metadata...
May be som1 will find it useful.

Categories

Resources