Question

On certain images, when I call:

PdfImageObject pimg = new PdfImageObject(stream);
Image bmp = pimg.GetDrawingImage();

The Image that is returned is twisted. I've seen this before and it usually has to do with byte alignment but I'm not sure how to get around this.

The /DecodeParms for this object are /EndOfLine true /K 0 /Columns 3300.

I have tried using the GetStreamBytesRaw() with BitMiracle.LibTiff and with it I can get the data formatted properly although the image is rotated. I'd prefer for GetDrawingImage() to decode the data properly if possible, assuming that is the problem.

I could provide the PDF via email if requested.

Thanks, Darren

Was it helpful?

Solution

For anyone else that runs across this scenario here is my solution. The key to this puzzle was understanding that /K 0 is G3, /K -1 (or anything less than 0) is G4 /K 1 (or anything greater than 0) is G3-2D.

The twisting happens when you try to make G3 compressed data fit into a G4 image which it appears that is what iTextSharp may be doing. I know it definitely does not work with how I have iTextSharp implemented in my project. I confess that I cannot decipher all the decoding stuff that iTextSharp is doing so it could be something I'm missing too.

EndOfLine didn't have any part in this puzzle but I still think putting line feeds in binary data is a strange practice.

99% of this code came from BitMiracle.LibTiff.Net - Thank you.

int nK = 0;// Default to 0 like the PDF Spec
PdfObject oDecodeParms = stream.Get(PdfName.DECODEPARMS);
if (oDecodeParms is PdfDictionary)
{
    PdfObject oK0 = ((PdfDictionary)oDecodeParms).Get(PdfName.K);
    if (oK0 != null)
        nK = ((PdfNumber)oK0).IntValue;
}

using (MemoryStream ms = new MemoryStream())
{
    using (Tiff tiff = Tiff.ClientOpen("custom", "w", ms, new TiffStream()))
    {
        tiff.SetField(TiffTag.IMAGEWIDTH, width);
        tiff.SetField(TiffTag.IMAGELENGTH, height);
        if (nK == 0 || nK > 0) // 0 = Group 3, > 0 = Group 3 2D
            tiff.SetField(TiffTag.COMPRESSION, Compression.CCITTFAX3);
        else if (nK < 0) // < 0 = Group 4
            tiff.SetField(TiffTag.COMPRESSION, Compression.CCITTFAX4);
        tiff.SetField(TiffTag.BITSPERSAMPLE, bpc);
        tiff.SetField(TiffTag.SAMPLESPERPIXEL, 1);
        tiff.WriteRawStrip(0, rawBytes, rawBytes.Length); //saving the tiff file using the raw bytes retrieved from the PDF.
        tiff.Close();
    }

    TiffStreamForBytes byteStream = new TiffStreamForBytes(ms.ToArray());

    using (Tiff input = Tiff.ClientOpen("bytes", "r", null, byteStream))
    {
        int stride = input.ScanlineSize();

        Bitmap result = new Bitmap(width, height, pixelFormat);
        ColorPalette palette = result.Palette;
        palette.Entries[0] = System.Drawing.Color.White;
        palette.Entries[1] = System.Drawing.Color.Black;
        result.Palette = palette;

        for (int i = 0; i < height; i++)
        {
            Rectangle imgRect = new Rectangle(0, i, width, 1);
            BitmapData imgData = result.LockBits(imgRect, ImageLockMode.WriteOnly, pixelFormat);

            byte[] buffer = new byte[stride];
            input.ReadScanline(buffer, i);

            System.Runtime.InteropServices.Marshal.Copy(buffer, 0, imgData.Scan0, buffer.Length);
            result.UnlockBits(imgData);
        }
    }
}

/// <summary>
/// Custom read-only stream for byte buffer that can be used
/// with Tiff.ClientOpen method.
/// </summary>
public class TiffStreamForBytes : TiffStream
{
    private byte[] m_bytes;
    private int m_position;

    public TiffStreamForBytes(byte[] bytes)
    {
        m_bytes = bytes;
        m_position = 0;
    }

    public override int Read(object clientData, byte[] buffer, int offset, int count)
    {
        if ((m_position + count) > m_bytes.Length)
            return -1;

        Buffer.BlockCopy(m_bytes, m_position, buffer, offset, count);
        m_position += count;
        return count;
    }

    public override void Write(object clientData, byte[] buffer, int offset, int count)
    {
        throw new InvalidOperationException("This stream is read-only");
    }

    public override long Seek(object clientData, long offset, SeekOrigin origin)
    {
        switch (origin)
        {
            case SeekOrigin.Begin:
                if (offset > m_bytes.Length)
                    return -1;

                m_position = (int)offset;
                return m_position;

            case SeekOrigin.Current:
                if ((offset + m_position) > m_bytes.Length)
                    return -1;

                m_position += (int)offset;
                return m_position;

            case SeekOrigin.End:
                if ((m_bytes.Length - offset) < 0)
                    return -1;

                m_position = (int)(m_bytes.Length - offset);
                return m_position;
        }

        return -1;
    }

    public override void Close(object clientData)
    {
        // nothing to do
        return;
    }

    public override long Size(object clientData)
    {
        return m_bytes.Length;
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top