/// <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>
    /// 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);
        }