Hi,
1.5 days of trying to solve this and I can't.... I now ask the pros for help
:0)
I'm working on an image viewing module for our office application. We will
need to display multi pages tiff (faxes). I've created a sample application
to familiarize myself with working with tiffs. Things were going OK until I
tried to change to a different page and I got the dreaded "Generic GDI+
Error" exception.
You will see some methods in the below code that might not "smell right" -
things like ImageFromBytes and BytesFromImage, etc. These are all helper
methods I've been using for years to work with DB blob data. I suspect very
much the problem is somewhere in those methods, but I don't see it.
Since there is no error to really run with, I hoped I could paste the whole
application (Console app, very small) in the hopes that someone with
experience could give it a run. It doesn't require any files or anything,
the TIF is created by the application for testing. It does save the tiff it
creates to the drive, but deletes it after it's done in a try/finally
This is just a test app, it's a bit ugly I know.
<code>
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Collections.Generic;
using System.Text;
namespace AccessTiffPageProblem
{
class Program
{
static void Main(string[] args)
{
// Create a multi page TIFF, save to disk (I think we have to)
// then read the saved tiff as a byte[], then convert to Image
object
// Finally pull the page count and activate different frames.
//
// A good chunk of this code was pulled from Bob Powell's FAQ.
// I've proceeded to massacre it to my usual standards ;0)
try
{
byte[] multiPageTiffData = CreateMultiPageDummyTiffFile(7);
Image image = ImageFromBytes(multiPageTiffData);
// Get the page count
int numPages = image.GetFrameCount(FrameDimension.Page);
Console.WriteLine("Tiff image has {0} pages (frames)",
numPages);
// activate the frames
for (int i = 0; i < numPages; i++)
{
image.SelectActiveFrame(FrameDimension.Page, i);
Console.WriteLine("Changed to frame {0} without
trouble", i);
}
}
catch(Exception e)
{
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("--------------------------------------------------------");
Console.WriteLine(e.ToString());
}
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
private static byte[] CreateMultiPageDummyTiffFile(int pageCount)
{
// Make sure that no more than 10 pages are created!
pageCount = Math.Max(10, pageCount);
string rootPath =
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string path = rootPath + @"\" + Guid.NewGuid().ToString() +
".tif";
try
{
//get the codec for tiff files
ImageCodecInfo info = null;
foreach (ImageCodecInfo ice in
ImageCodecInfo.GetImageEncoders())
if (ice.MimeType == "image/tiff")
info = ice;
//use the save encoder
System.Drawing.Imaging.Encoder enc =
System.Drawing.Imaging.Encoder.SaveFlag;
EncoderParameters ep = new EncoderParameters(1);
ep.Param[0] = new EncoderParameter(enc,
(long)EncoderValue.MultiFrame);
Bitmap pages = null;
int frame = 0;
for (int i = 0; i < pageCount; i++)
{
if (frame == 0)
{
pages = (Bitmap)CreateDummyTifPage(i + 1);
//save the first frame
pages.Save(path, info, ep);
}
else
{
//save the intermediate frames
ep.Param[0] = new EncoderParameter(enc,
(long)EncoderValue.FrameDimensionPage);
Bitmap bm = (Bitmap)CreateDummyTifPage(i + 1);
pages.SaveAdd(bm, ep);
}
if (frame == (pageCount - 1))
{
//flush and close.
ep.Param[0] = new EncoderParameter(enc,
(long)EncoderValue.Flush);
pages.SaveAdd(ep);
}
frame++;
}
// Load up the saved tiff file, convert to byte[] and
delete the temp file
byte[] data = ReadWholeFileBytes(path);
// If you comment out the above line and uncomment
// this line things fail in a different way - it reports
// only one frame in the loaded tif
//
//byte[] data = BytesFromImage(pages, ImageFormat.Tiff);
System.IO.File.Delete(path);
return data;
}
catch(Exception e)
{
Console.WriteLine(e.ToString());
}
finally
{
if (System.IO.File.Exists(path))
System.IO.File.Delete(path);
}
return null;
}
private static Image CreateDummyTifPage(int randomNumber)
{
// Create an 8.5" x 11" B&W tif image
// assume 96 dpi
const int dpi = 96;
int width = (int)(dpi * 8.5 );
int height = (int)(dpi * 11.0);
Bitmap image = new Bitmap(width, height);
Graphics g = Graphics.FromImage(image);
string msg = string.Format("This is a dummy tif fax created at
runtime.\n" +
"It's random seed is {0}", randomNumber);
Font font = new Font(FontFamily.GenericSerif, 20F);
SizeF msgSize = g.MeasureString(msg, font);
g.FillRectangle(Brushes.White, 0, 0, image.Width, image.Height);
g.DrawString(msg, font, Brushes.Black, (width / 2) -
(msgSize.Width / 2), 100);
return image;
}
public static byte[] ReadWholeStream(Stream stream)
{
byte[] buffer = new byte[stream.Length];
int offset = 0;
int remaining = (int)stream.Length;
while (remaining > 0)
{
int read = stream.Read(buffer, offset, remaining);
if (read <= 0)
{
throw new EndOfStreamException
(String.Format("End of stream reached with {0} bytes
left to read", remaining));
}
remaining -= read;
offset += read;
}
return buffer;
}
public static byte[] ReadWholeFileBytes(string filename)
{
using (Stream stream = new FileStream(filename, FileMode.Open))
{
return ReadWholeStream(stream);
}
}
public static Image ImageFromBytes(byte[] data)
{
using (MemoryStream ms = new MemoryStream(data, 0, data.Length))
{
ms.Write(data, 0, data.Length);
return Image.FromStream(ms, true);
}
}
public static byte[] BytesFromImage(Image image, ImageFormat format)
{
byte[] Ret;
using (MemoryStream ms = new MemoryStream())
{
image.Save(ms, format);
Ret = ms.ToArray();
}
return Ret;
}
}
}
</code>
I will REALLY appreciate if anyone can point out the error... I can't find
it without a better error message from GDI.
Thanks,
Steve
Bob Powell [MVP] - 08 Jan 2008 10:43 GMT
Have you seen my article on how to insert pages into multi-frame tiffs?
http://www.bobpowell.net/addframes.htm

Signature
--
Bob Powell [MVP]
Visual C#, System.Drawing
Ramuseco Limited .NET consulting
http://www.ramuseco.com
Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm
Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm
All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.
> Hi,
>
[quoted text clipped - 246 lines]
> Thanks,
> Steve
Steve K. - 08 Jan 2008 13:31 GMT
Hi Bob,
yes, I have seen that page. 99% of my tiff related code I have in this
thread is from your site, you are even mentioned in the comments! ;0)
However, I suspect the problem could be with the IO utils I'm using to
convert Image/Files to byte[] and back again.
The Tiff that is produced from my application (your code) appears to be
valid and work fine.
I'm still stuck.
> Have you seen my article on how to insert pages into multi-frame tiffs?
>
[quoted text clipped - 260 lines]
>> Thanks,
>> Steve
Bob Powell [MVP] - 08 Jan 2008 15:26 GMT
Let me cast an eye over it...

Signature
--
Bob Powell [MVP]
Visual C#, System.Drawing
Ramuseco Limited .NET consulting
http://www.ramuseco.com
Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm
Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm
All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.
> Hi Bob,
>
[quoted text clipped - 277 lines]
>>> Thanks,
>>> Steve
Steve K. - 09 Jan 2008 00:14 GMT
That would be great Bob, thanks for any help you can offer.
-Steve
> Let me cast an eye over it...
>
[quoted text clipped - 283 lines]
>>>> Thanks,
>>>> Steve
Bob Powell [MVP] - 09 Jan 2008 12:26 GMT
Hmm, after the image is read in from the bytes, the frame dimension list is
1 so it's not surprising that you cannot select the frame.
As to why I don't know. The file opens nicely in the image viewer and all
the frames do indeed exist.
I will continue to look.

Signature
--
Bob Powell [MVP]
Visual C#, System.Drawing
Ramuseco Limited .NET consulting
http://www.ramuseco.com
Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm
Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm
All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.
> That would be great Bob, thanks for any help you can offer.
>
[quoted text clipped - 292 lines]
>>>>> Thanks,
>>>>> Steve
Steve K. - 10 Jan 2008 08:44 GMT
I think I've got the answer to this one. After doing some reading and
speaking with some other on a different NG I've learned the following:
A multi frame tiff needs access to it's source stream. So although I
thought Image.FromFile/Stream was loading the ENTIRE tiff structure into
memory... it wasn't. The frame count is in the header, so that was read in,
but attempts to access the frames fail because the stream is closed.
A temporary solution I'm come up with is to load the image, break the frames
out into an array, dispose the source image and delete the source file.
<code>
public static Image[] MultiFrameImageToArray(Image source, FrameDimension
frameDimension)
{
int numFrames = source.GetFrameCount(frameDimension);
Image[] frames = new Image[numFrames];
for(int i = 0; i < numFrames; i++)
{
source.SelectActiveFrame(frameDimension, i);
frames[i] = new Bitmap(source);
}
return frames;
}
</code>
-Steve
> Hmm, after the image is read in from the bytes, the frame dimension list
> is 1 so it's not surprising that you cannot select the frame.
[quoted text clipped - 304 lines]
>>>>>> Thanks,
>>>>>> Steve