/// <summary> /// Prepends a proper TIFF image header to the the CCITTFaxDecode image data. /// </summary> /// <param name="imageData">The metadata about the image.</param> /// <param name="image">The original compressed image.</param> /// <returns>A properly formatted TIFF header and the compressed image data.</returns> private static byte[] GetTiffImageBufferFromCCITTFaxDecode(PdfDictionaryImageMetaData imageData, byte[] image) { const short TIFF_BIGENDIAN = 0x4d4d; const short TIFF_LITTLEENDIAN = 0x4949; const int ifd_length = 10; const int header_length = 10 + (ifd_length * 12 + 4); using (MemoryStream buffer = new MemoryStream(header_length + image.Length)) { // TIFF Header buffer.Write(BitConverter.GetBytes(BitConverter.IsLittleEndian ? TIFF_LITTLEENDIAN : TIFF_BIGENDIAN), 0, 2); // tiff_magic (big/little endianness) buffer.Write(BitConverter.GetBytes((uint)42), 0, 2); // tiff_version buffer.Write(BitConverter.GetBytes((uint)8), 0, 4); // first_ifd (Image file directory) / offset buffer.Write(BitConverter.GetBytes((uint)ifd_length), 0, 2); // ifd_length, number of tags (ifd entries) // Dictionary should be in order based on the TiffTag value WriteTiffTag(buffer, TiffTag.SUBFILETYPE, TiffType.LONG, 1, 0); WriteTiffTag(buffer, TiffTag.IMAGEWIDTH, TiffType.LONG, 1, (uint)imageData.Width); WriteTiffTag(buffer, TiffTag.IMAGELENGTH, TiffType.LONG, 1, (uint)imageData.Height); WriteTiffTag(buffer, TiffTag.BITSPERSAMPLE, TiffType.SHORT, 1, (uint)imageData.BitsPerPixel); WriteTiffTag(buffer, TiffTag.COMPRESSION, TiffType.SHORT, 1, (uint)imageData.Compression); WriteTiffTag(buffer, TiffTag.PHOTOMETRIC, TiffType.SHORT, 1, 0); // WhiteIsZero WriteTiffTag(buffer, TiffTag.STRIPOFFSETS, TiffType.LONG, 1, header_length); WriteTiffTag(buffer, TiffTag.SAMPLESPERPIXEL, TiffType.SHORT, 1, 1); WriteTiffTag(buffer, TiffTag.ROWSPERSTRIP, TiffType.LONG, 1, (uint)imageData.Height); WriteTiffTag(buffer, TiffTag.STRIPBYTECOUNTS, TiffType.LONG, 1, (uint)image.Length); // Next IFD Offset buffer.Write(BitConverter.GetBytes((uint)0), 0, 4); buffer.Write(image, 0, image.Length); return(buffer.GetBuffer()); } }
/// <summary> /// Retrieves the specifed dictionary object as an object encoded with FlateDecode filter. /// </summary> /// <remarks> /// FlateDecode a commonly used filter based on the zlib/deflate algorithm (a.k.a. gzip, but not zip) /// defined in RFC 1950 and RFC 1951; introduced in PDF 1.2; it can use one of two groups of predictor /// functions for more compact zlib/deflate compression: Predictor 2 from the TIFF 6.0 specification /// and predictors (filters) from the PNG specification (RFC 2083) /// </remarks> /// <param name="dictionary">The dictionary to extract the object from.</param> /// <returns>The image retrieve from the dictionary. If not found or an invalid image, then null is returned.</returns> private static Image ImageFromFlateDecode(PdfDictionary dictionary) { PdfDictionaryImageMetaData imageData = new PdfDictionaryImageMetaData(dictionary); // FlateDecode can be either indexed or a traditional ColorSpace bool isIndexed = imageData.ColorSpace.IsIndexed; PixelFormat format = GetPixelFormat(imageData.ColorSpace, imageData.BitsPerPixel, isIndexed); Bitmap bitmap = new Bitmap(imageData.Width, imageData.Height, format); // If indexed, retrieve and assign the color palette for the item. if ((isIndexed) && (imageData.ColorSpace.IsRGB)) bitmap.Palette = ((PdfIndexedRGBColorSpace) imageData.ColorSpace).ToColorPalette(); else if (imageData.ColorSpace is PdfGrayColorSpace) bitmap.Palette = ((PdfGrayColorSpace)imageData.ColorSpace).ToColorPalette(imageData.BitsPerPixel); // If not an indexed color, the .NET image component expects pixels to be in BGR order. However, our PDF stream is in RGB order. byte[] stream = (format == PixelFormat.Format24bppRgb) ? ConvertRGBStreamToBGR(dictionary.Stream.UnfilteredValue) : dictionary.Stream.UnfilteredValue; BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, imageData.Width, imageData.Height), ImageLockMode.WriteOnly, format); // We can't just copy the bytes directly; the BitmapData .NET class has a stride (padding) associated with it. int bitsPerPixel = ((((int)format >> 8) & 0xFF)); int length = (int)Math.Ceiling(bitmapData.Width * bitsPerPixel / 8.0); for (int y = 0, height = bitmapData.Height; y < height; y++) { int offset = y * length; Marshal.Copy(stream, offset, bitmapData.Scan0 + (y * bitmapData.Stride), length); } bitmap.UnlockBits(bitmapData); return (bitmap); }
/// <summary> /// Retrieves the specifed dictionary object as an object encoded with CCITTFaxDecode filter (TIFF). /// </summary> /// <param name="dictionary">The dictionary to extract the object from.</param> /// <returns>The image retrieve from the dictionary. If not found or an invalid image, then null is returned.</returns> private static Image ImageFromCCITTFaxDecode(PdfDictionary dictionary) { Image image = null; PdfDictionaryImageMetaData imageData = new PdfDictionaryImageMetaData(dictionary); PixelFormat format = GetPixelFormat(imageData.ColorSpace, imageData.BitsPerPixel, true); Bitmap bitmap = new Bitmap(imageData.Width, imageData.Height, format); // Determine if BLACK=1, create proper indexed color palette. CCITTFaxDecodeParameters ccittFaxDecodeParameters = new CCITTFaxDecodeParameters(dictionary.Elements["/DecodeParms"].Get() as PdfDictionary); if (ccittFaxDecodeParameters.BlackIs1) bitmap.Palette = PdfIndexedColorSpace.CreateColorPalette(Color.Black, Color.White); else bitmap.Palette = PdfIndexedColorSpace.CreateColorPalette(Color.White, Color.Black); using (MemoryStream stream = new MemoryStream(GetTiffImageBufferFromCCITTFaxDecode(imageData, dictionary.Stream.Value))) { using (Tiff tiff = Tiff.ClientOpen("<INLINE>", "r", stream, new TiffStream())) { if (tiff == null) return (null); int stride = tiff.ScanlineSize(); byte[] buffer = new byte[stride]; for (int i = 0; i < imageData.Height; i++) { tiff.ReadScanline(buffer, i); Rectangle imgRect = new Rectangle(0, i, imageData.Width, 1); BitmapData imgData = bitmap.LockBits(imgRect, ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed); Marshal.Copy(buffer, 0, imgData.Scan0, buffer.Length); bitmap.UnlockBits(imgData); } } } return (bitmap); }
/// <summary> /// Prepends a proper TIFF image header to the the CCITTFaxDecode image data. /// </summary> /// <param name="imageData">The metadata about the image.</param> /// <param name="image">The original compressed image.</param> /// <returns>A properly formatted TIFF header and the compressed image data.</returns> private static byte[] GetTiffImageBufferFromCCITTFaxDecode(PdfDictionaryImageMetaData imageData, byte[] image) { const short TIFF_BIGENDIAN = 0x4d4d; const short TIFF_LITTLEENDIAN = 0x4949; const int ifd_length = 10; const int header_length = 10 + (ifd_length * 12 + 4); using (MemoryStream buffer = new MemoryStream(header_length + image.Length)) { // TIFF Header buffer.Write(BitConverter.GetBytes(BitConverter.IsLittleEndian ? TIFF_LITTLEENDIAN : TIFF_BIGENDIAN), 0, 2); // tiff_magic (big/little endianness) buffer.Write(BitConverter.GetBytes((uint)42), 0, 2); // tiff_version buffer.Write(BitConverter.GetBytes((uint)8), 0, 4); // first_ifd (Image file directory) / offset buffer.Write(BitConverter.GetBytes((uint)ifd_length), 0, 2); // ifd_length, number of tags (ifd entries) // Dictionary should be in order based on the TiffTag value WriteTiffTag(buffer, TiffTag.SUBFILETYPE, TiffType.LONG, 1, 0); WriteTiffTag(buffer, TiffTag.IMAGEWIDTH, TiffType.LONG, 1, (uint)imageData.Width); WriteTiffTag(buffer, TiffTag.IMAGELENGTH, TiffType.LONG, 1, (uint)imageData.Height); WriteTiffTag(buffer, TiffTag.BITSPERSAMPLE, TiffType.SHORT, 1, (uint)imageData.BitsPerPixel); WriteTiffTag(buffer, TiffTag.COMPRESSION, TiffType.SHORT, 1, (uint) Compression.CCITTFAX4); // CCITT Group 4 fax encoding. WriteTiffTag(buffer, TiffTag.PHOTOMETRIC, TiffType.SHORT, 1, 0); // WhiteIsZero WriteTiffTag(buffer, TiffTag.STRIPOFFSETS, TiffType.LONG, 1, header_length); WriteTiffTag(buffer, TiffTag.SAMPLESPERPIXEL, TiffType.SHORT, 1, 1); WriteTiffTag(buffer, TiffTag.ROWSPERSTRIP, TiffType.LONG, 1, (uint)imageData.Height); WriteTiffTag(buffer, TiffTag.STRIPBYTECOUNTS, TiffType.LONG, 1, (uint)image.Length); // Next IFD Offset buffer.Write(BitConverter.GetBytes((uint)0), 0, 4); buffer.Write(image, 0, image.Length); return(buffer.GetBuffer()); } }
/// <summary> /// Retrieves the specifed dictionary object as an object encoded with CCITTFaxDecode filter (TIFF). /// </summary> /// <param name="dictionary">The dictionary to extract the object from.</param> /// <returns>The image retrieve from the dictionary. If not found or an invalid image, then null is returned.</returns> private static Image ImageFromCCITTFaxDecode(PdfDictionary dictionary) { PdfDictionaryImageMetaData imageData = new PdfDictionaryImageMetaData(dictionary); PixelFormat format = GetPixelFormat(imageData.ColorSpace, imageData.BitsPerPixel, true); Bitmap bitmap = new Bitmap(imageData.Width, imageData.Height, format); // Determine if BLACK=1, create proper indexed color palette. PdfDictionary decodeParams; var decodeParamsObject = dictionary.Elements["/DecodeParms"].Get(); if (decodeParamsObject is PdfArray) { decodeParams = (decodeParamsObject as PdfArray).First() as PdfDictionary; } else if (decodeParamsObject is PdfDictionary) { decodeParams = decodeParamsObject as PdfDictionary; } else { throw new NotSupportedException("Unknown format of CCITTFaxDecode params."); } CCITTFaxDecodeParameters ccittFaxDecodeParameters = new CCITTFaxDecodeParameters(decodeParams); if (ccittFaxDecodeParameters.BlackIs1) { bitmap.Palette = PdfIndexedColorSpace.CreateColorPalette(Color.Black, Color.White); } else { bitmap.Palette = PdfIndexedColorSpace.CreateColorPalette(Color.White, Color.Black); } if (ccittFaxDecodeParameters.K == 0 || ccittFaxDecodeParameters.K > 0) { imageData.Compression = Compression.CCITTFAX3; } else if (ccittFaxDecodeParameters.K < 0) { imageData.Compression = Compression.CCITTFAX4; } using (MemoryStream stream = new MemoryStream(GetTiffImageBufferFromCCITTFaxDecode(imageData, dictionary.Stream.Value))) { using (Tiff tiff = Tiff.ClientOpen("<INLINE>", "r", stream, new TiffStream())) { if (tiff == null) { return(null); } int stride = tiff.ScanlineSize(); byte[] buffer = new byte[stride]; for (int i = 0; i < imageData.Height; i++) { tiff.ReadScanline(buffer, i); Rectangle imgRect = new Rectangle(0, i, imageData.Width, 1); BitmapData imgData = bitmap.LockBits(imgRect, ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed); Marshal.Copy(buffer, 0, imgData.Scan0, buffer.Length); bitmap.UnlockBits(imgData); } } } return(bitmap); }