C#: How do I convert a multi-page TIFF via MemoryS

2019-01-19 08:52发布

问题:

So I have been able to take a multi-page TIFF file and convert it to a single jpeg image but it flattens the TIFF. By flatten it, I mean it only returns the first page. The goal is to retrieve the TIFF (via memory stream), open each page of the TIFF and append it to a new jpeg (or any web image). Thus creating one long image to view on the web without the aid of a plugin. I do have the MODI.dll installed but I am not sure how to use it in this instance but it is an option.

  • Source Code (using a FileHandler):

    #region multi-page tiff to single page jpeg
    var byteFiles = dfSelectedDocument.File.FileBytes; // <-- FileBytes is a byte[] or byte array source.
    
    byte[] jpegBytes;
    using( var inStream = new MemoryStream( byteFiles ) )
    using( var outStream = new MemoryStream() ) {
    System.Drawing.Image.FromStream( inStream ).Save( outStream, ImageFormat.Jpeg );
    jpegBytes = outStream.ToArray();
    }
    
     context.Response.ContentType = "image/JPEG";
     context.Response.AddHeader( "content-disposition", 
        string.Format( "attachment;filename=\"{0}\"",
        dfSelectedDocument.File.FileName.Replace( ".tiff", ".jpg" ) ) 
     );
     context.Response.Buffer = true;
     context.Response.BinaryWrite( jpegBytes );
    #endregion
    

回答1:

have you compressed the jpeg? https://msdn.microsoft.com/en-us/library/bb882583(v=vs.110).aspx



回答2:

I'm guessing that you'll have to loop over each frame in the TIFF.

Here's an excerpt from Split multi page tiff file:

private void Split(string pstrInputFilePath, string pstrOutputPath) 
    { 
        //Get the frame dimension list from the image of the file and 
        Image tiffImage = Image.FromFile(pstrInputFilePath); 
        //get the globally unique identifier (GUID) 
        Guid objGuid = tiffImage.FrameDimensionsList[0]; 
        //create the frame dimension 
        FrameDimension dimension = new FrameDimension(objGuid); 
        //Gets the total number of frames in the .tiff file 
        int noOfPages = tiffImage.GetFrameCount(dimension); 

        ImageCodecInfo encodeInfo = null; 
        ImageCodecInfo[] imageEncoders = ImageCodecInfo.GetImageEncoders(); 
        for (int j = 0; j < imageEncoders.Length; j++) 
        { 
            if (imageEncoders[j].MimeType == "image/tiff") 
            { 
                encodeInfo = imageEncoders[j]; 
                break; 
            } 
        } 

        // Save the tiff file in the output directory. 
        if (!Directory.Exists(pstrOutputPath)) 
            Directory.CreateDirectory(pstrOutputPath); 

        foreach (Guid guid in tiffImage.FrameDimensionsList) 
        { 
            for (int index = 0; index < noOfPages; index++) 
            { 
                FrameDimension currentFrame = new FrameDimension(guid); 
                tiffImage.SelectActiveFrame(currentFrame, index); 
                tiffImage.Save(string.Concat(pstrOutputPath, @"\", index, ".TIF"), encodeInfo, null); 
            } 
        } 
    } 

You should be able to adapt the logic above to append onto your JPG rather than create separate files.



回答3:

In case you get the dreadful "A generic error occurred in GDI+" error (which is arguably the Rickroll of all errors) when using the SelectActiveFrame method suggested in the other answers, I strongly suggest to use the System.Windows.Media.Imaging.TiffBitmapDecoder class instead (you will need to add a Reference to the PresentationCore.dll framework library).

Here's an example code that does just that (it puts all the TIFF frames into a list of standard Bitmaps):

List<System.Drawing.Bitmap> bmpLst = new List<System.Drawing.Bitmap>();

using (var msTemp = new MemoryStream(data))
{
    TiffBitmapDecoder decoder = new TiffBitmapDecoder(msTemp, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default);
    int totFrames = decoder.Frames.Count;

    for (int i = 0; i < totFrames; ++i)
    {
        // Create bitmap to hold the single frame
        System.Drawing.Bitmap bmpSingleFrame = BitmapFromSource(decoder.Frames[i]);
        // add the frame (as a bitmap) to the bitmap list
        bmpLst.Add(bmpSingleFrame);
    }
}

And here's the BitmapFromSource helper method:

public static Bitmap BitmapFromSource(BitmapSource bitmapsource)
{
    Bitmap bitmap;
    using (var outStream = new MemoryStream())
    {
        BitmapEncoder enc = new BmpBitmapEncoder();
        enc.Frames.Add(BitmapFrame.Create(bitmapsource));
        enc.Save(outStream);
        bitmap = new Bitmap(outStream);
    }
    return bitmap;
}

For further info regarding this workaround, I also suggest to read this blog post I wrote.