Writing a multipaged tif file from memorystream vs filestream? - c#

I am trying to use a LibTiff.Net library and rewriting a merge tool TiffCP api to use memory streams.
This library has a Tiff class and by passing a stream to this class, it can merge tiff images into this stream.
For testing, I passed on a Filestream and I got what i wanted - it merged and I was able to see multipage tif.
But when I pass a MemoryStream, I am able to verify that the page data is being added to the stream as I loop through but when I write it to the file at the end, I could see only 1st page.
var mso = new MemoryStream();
var fso = new FileStream(#"C:\test\ttest.tif",FileMode.OpenOrCreate); //This works
using (Tiff outImage = Tiff.ClientOpen("custom", "w", mso, tso))
{
//...
//..
System.Drawing.Image tiffImg = System.Drawing.Image.FromStream(mso, true);
tiffImg.Save(#"C:\test\test2.tiff", System.Drawing.Imaging.ImageFormat.Tiff);
tiffImg.Dispose();
//..
//..
}
P.S: I need it in memorystream because, of some folder permissions on servers + vendor API reasons.

You probably using the memory stream before data is actually written into the stream.
Please use Tiff.Flush() method before accessing data in the memory stream. And please make sure you call Tiff.WriteDirectory() method for each page you create.
EDIT:
Please also take a look at Bob Powell's article on Generating Multi-Page TIFF files. The article shows how to use EncoderParameters to actually generate a multipage TIFF.
Using
tiffImg.Save(#"C:\test\test2.tiff", System.Drawing.Imaging.ImageFormat.Tiff);
you are probably save only first frame.

Related

Decompressing a MemoryStream using Zlib

I am writing software that deals with a large collection of files that contain zlib compressed data in different sections of the file rather than the entire file itself. I know how to grab the section(s) I need. However I was having trouble getting the documented zlib stream classes to work properly. I googled and tried several different solutions and could not get them to work except one that uses a static method. The following code works just fine but does not work directly with the Zlib stream class as I would prefer:
// reader is the BinaryReader for the original file and...
// The "Data" section consists of a UInt32 and the compressed data
byte[] compressedStream = reader.ReadBytes((int)Size - 4); // name was kept for code compatibility
MemoryStream deflatedStream = new MemoryStream(ZlibStream.UncompressBuffer(compressedStream), true);
I don't really have any issues with using the code above since it gives me the decompressed data I need. However, I am baffled as to why my original code which instances the zlib stream class directly did not work (since they use the same basic API):
MemoryStream compressedStream = new MemoryStream(reader.ReadBytes((int)Size - 4));
ZlibStream deflatedStream = new ZlibStream(compressedStream, CompressionMode.Decompress, true);
Accessing almost any property of "deflatedStream" results in an error. I assume this means it did not work. It might be worth noting that I have not yet used the DotNetZip lib and used Zlib.Portable instead (the second most popular library). However, the API seems to be the same.

How to attach .docx and .doc files to the email from memory stream?

I'm doing the file attachment through memory stream because temporary storage is not an option.
Here I did a Jpeg image attachment. I looked at other file types with which you could do the same by switching the MediaTypeNames, and unfortunately .doc and .docx is not among them.
I was wondering whether any of you know of any package and how to use it for this particular occasion?
//...set up MailMessage, add a bunch of non-file content to it
MemoryStream jpgStream = new MemoryStream();
string filename = uploadedFile.FileName;
System.Drawing.Image theImage = System.Drawing.Image.FromStream(uploadedFile.InputStream);
theImage.Save(jpgStream, System.Drawing.Imaging.ImageFormat.Jpeg);
jpgStream.Seek(0L, SeekOrigin.Begin);
emailMessage.Attachments.Add(new Attachment(jpgStream, filename, System.Net.Mime.MediaTypeNames.Image.Jpeg));
//something extra and send email...
You should use MediaTypeNames.Application.Octet as mime type.
From : https://learn.microsoft.com/fr-fr/dotnet/api/system.net.mime.mediatypenames.application.octet?view=netframework-4.7.2
Thanks Benoit Gidon for his answer. I was proved once again that your assumptions are your worst enemy.
Apparently it's as simple as that and you don't need any other special methods as long as you put the file.InputStream in directly into attachment, unlike other S.O. posts make you believe:
https://stackoverflow.com/questions/9095880/attach-multiple-image-formats-not-just-jpg-to-email
https://stackoverflow.com/questions/5336239/attach-a-file-from-memorystream-to-a-mailmessage-in-c-sharp
In case anyone is actually struggling with it, here is the code:
foreach (HttpPostedFileBase file in fullViewModel.filesCollection)
{
string filename = file.FileName;
msg.Attachments.Add(new Attachment(file.InputStream, filename, MediaTypeNames.Application.Octet));
}
I tested this with a .docx document that had 5000 words of content in it, as well as tables and pictures, and it gets reconstructed the way it was in my gmail client.
Just tested that, it also works the same way with .png, so no need for PngBitmapDecoder.

How do I save an in memory Bitmap to a ZipArchive without saving the Bitmap to the file system first?

So I have some code that takes a capture of the screen and saves it to a jpeg file. This works fine, however I want to instead save the jpeg encoded capture to a new ZipArchive without writing the Bitmap to the file system first.
Here is what I have so far:
FileInfo zipArchive = new FileInfo(fileToZip.FullName + ".zip");
using (ZipArchive zipFile = ZipFile.Open(zipArchive.FullName, ZipArchiveMode.Create)))
{
ZipArchiveEntry zae = zipFile.CreateEntry(fileToZip.FullName, CompressionLevel.Optimal);
using (Stream zipStream = zae.Open())
bmp.Save(zipStream, ImageFormat.Jpeg);
}
The problem is that on the bmp.Save() line a System.NotSupportedException is thrown
This stream from ZipArchiveEntry does not support seeking.
I've seen a lot of examples that write directly to the Stream returned from zae.Open() so I am not sure why this doesn't work because I figured that all bmp.Save() would need to do is write, not seek. I don't know if this would work but I don't want to have to save the Bitmap to a MemoryStream and the copy that stream to the Stream returned from zae.Open() because it feels like unnecessary extra work. Am I missing something obvious?
Many file formats have pointers to other parts of the file, or length values, which may not be known beforehand. The simplest way is to just write zeros first, then the data and then seek to change the value. If this way is used, there is no way to get by this, so you will need to first write the data into a MemoryStream and then write the resulting data into the ZipStream, as you mentioned.
This doesn't really add that much code and is a simple fix for the problem.

How to modify tiff creation date in .Net

I am writing a pdf comparison utility. After some investigation it seems like the best way to do this is to convert to tiff and compare from there.
I managed to do this with Ghostscript but am getting a difference in the embedded creation date metadata.
How do I use .Net to modify this?
You can use LibTiff.NET. It is open source. Using this library, you can use the SetField method to modify any one of the many tags in the Tiff file, including the TiffTag.DATETIME flag.
After more investigation, it seems Microsoft does provide a TIFF library with multi-image support. It's in System.Windows.Media.Imaging. To get this namespace reference PresentationCore.
To access the TIFF metadata use this site as a reference: http://www.awaresystems.be/imaging/tiff/tifftags/baseline.html
This code accesses the date field after the GhostScript name you were interested in:
FileInfo fi = new FileInfo(#"C:\Users\Chris\Downloads\PdfVerificationTests.can_use_image_approval_mode.approved.tiff");
FileStream stream = fi.Open(FileMode.Open, FileAccess.ReadWrite,FileShare.None);
TiffBitmapDecoder decoder = new TiffBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapMetadata bmd = (BitmapMetadata) decoder.Frames[0].Metadata;
string thedateval = (string) bmd.GetQuery("/ifd/{ushort=306}");
BitmapMetadata bmd2 = bmd.Clone();
bmd2.SetQuery("/ifd/{ushort=306}", "2013:05:30 20:07:52");
This code does not write out a modified TIFF, but is all the info you need to do so. Hope this helps as I feel I'm beating a dead horse.
This code will strip all the attributes from a multipage TIFF and leave the image content intact:
FileInfo fi = new FileInfo(#"C:\Users\Chris\Downloads\PdfVerificationTests.can_use_image_approval_mode.approved.tiff");
FileStream stream = fi.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
TiffBitmapDecoder decoder = new TiffBitmapDecoder(stream, BitmapCreateOptions.None, BitmapCacheOption.None);
FileStream stream2 = new FileStream("empty.tif", FileMode.Create);
TiffBitmapEncoder encoder = new TiffBitmapEncoder();
for (int i = 0; i < decoder.Frames.Count(); i++)
{
BitmapSource source = decoder.Frames[i];
int stride = source.PixelWidth * (source.Format.BitsPerPixel / 8);
byte[] data = new byte[stride * source.PixelHeight];
source.CopyPixels(data, stride, 0);
CachedBitmap theSource = (CachedBitmap)BitmapSource.Create(source.PixelWidth, source.PixelHeight, source.DpiX, source.DpiY, source.Format, source.Palette, data, stride);
encoder.Frames.Add(BitmapFrame.Create(theSource));
}
try
{
encoder.Save(stream2);
stream2.Close();
stream.Close();
}
catch
{
}
If the date stamps are fixed size, a fun workaround for this type of problem is to write a FileStream which simply detects and blanks out such date stamps. In fact I've done this before for PDF comparison, on a project I worked on in school. The checksum comparison worked fine with just that, without any conversion to tiff, though in our specific case we were sure all compared PDFs were generated by the same system, so that simplified things a bit.
The basic method is to make a subclass of FileStream with overridden ReadByte and Read functions, which contains the length and expected format of the date stamps. Whenever a read is performed the following happens:
The code reads an extra piece, the size of the datestamp length minus 1 byte, both before and behind the requested data.
Inside that block, a search and replace is performed to clear any found date stamps.
Finally, the original requested piece is returned.
The source code I wrote for the project back in the day is here.
It seams that this ghostscript behavior could be supressed.
-dTIFFDateTime=false
https://www.ghostscript.com/doc/9.22/Devices.htm
... but for this situation I would recommend some diffpdf tools (http://soft.rubypdf.com/software/diffpdf)
D

7zip compress network stream

I will like to compress a file before sending it through the network. I think the best approach is 7zip because it is free and open source.
How I use 7zip with .net?
I know that 7zip is free and that they have the source code in c# but for some reason it is very slow on c# so I rather call the dll 7z.dll that comes when installing 7zip for performance reasons. So the way I am able to eassily marshal and call the methods in 7z.dll is with the help of the library called sevenzipsharp . For example adding that dll to my project will enable me to do:
// if you installed 7zip 64bit version then make sure you change plataform target
// like on the picture I showed above!
SevenZip.SevenZipCompressor.SetLibraryPath(#"C:\Program Files\7-Zip\7z.dll");
var stream = System.IO.File.OpenRead(#"SomeFileToCompress.txt");
var outputStream = System.IO.File.Create("Output.7z");
SevenZip.SevenZipCompressor compressor = new SevenZip.SevenZipCompressor();
compressor.CompressionMethod = SevenZip.CompressionMethod.Lzma2;
compressor.CompressionLevel = SevenZip.CompressionLevel.Ultra;
compressor.CompressStream(stream, outputStream);
that's how I use 7zip within c#.
Now my question is:
I will like to send a compressed file over the network. I know I could compress it first then send it. The file is 4GB so I will have to wait a long time for it to compress. I will be wasting a lot of space on hard drive. then I will finally be able to send it. I think that is to complicated. I was wondering how it will be possible to send the file meanwhile it is being compressed.
It seems to be a problem with SevenZipSharp:
Have you considered an alternate library - one that doesn't even require 7-Zip to be installed / available?
From the description posted at http://dotnetzip.codeplex.com/ :
creating zip files from stream content, saving to a stream, extracting
to a stream, reading from a stream
Unlike 7-Zip, DotNetZip is designed to work with C# / .Net.
Plenty of examples - including streaming, are available at http://dotnetzip.codeplex.com/wikipage?title=CS-Examples&referringTitle=Examples .
Another option is to use the 7-Zip Command Line Version (7z.exe), and write to/read from standard in/out. This would allow you to use the 7-Zip file format, while also keeping all of the core work in native code (though there likely won't be much of a significant difference).
Looking back at SevenZipSharp:
Since the 0.29 release, streaming is supported.
Looking at http://sevenzipsharp.codeplex.com/SourceControl/changeset/view/59007#364711 :
it seems you'd want this method:
public void CompressStream(Stream inStream, Stream outStream)
Thank you for considering performance here! I think way too many people would do exactly what you're trying to avoid: compress to a temp file, then do something with the temp file.
CompressStream threw an exception. My code is as follows:
public void TestCompress()
{
string fileToCompress = #"C:\Users\gary\Downloads\BD01.DAT";
byte[] inputBytes = File.ReadAllBytes(fileToCompress);
var inputStream = new MemoryStream(inputBytes);
byte[] zipBytes = new byte[38000000]; // this memory size is large enough.
MemoryStream outStream = new MemoryStream(zipBytes);
string compressorEnginePath = #"C:\Engine\7z.dll";
SevenZipCompressor.SetLibraryPath(compressorEnginePath);
compressor = new SevenZip.SevenZipCompressor();
compressor.CompressionLevel = CompressionLevel.Fast;
compressor.CompressionMethod = CompressionMethod.Lzma2;
compressor.CompressStream(inputStream, outputStream);
inputStream.Close();
outputStream.Close();
The exception messages:
Message: Test method Test7zip.UnitTest1.TestCompress threw exception:
SevenZip.SevenZipException: The execution has failed due to the bug in the SevenZipSharp.
Please report about it to http://sevenzipsharp.codeplex.com/WorkItem/List.aspx, post the release number and attach the archive

Categories

Resources