New to Telerik UI for WinForms? Download free 30-day trial

Use a custom JPXDecode filter with RadPdfViewer

Product Version Product Author Last modified
2014_3_1104 RadPdfViewer for WinForms Dimitar Karamfilov December 25, 2014

Problem

Currently, RadPdfViewer does not support the JPXDecode filter and documents that uses this filter will display a blank pages.

Solution

Use a custom filter to view such documents. The following example uses the OpenJPEG library to convert the images that are using this particular filter. To use this library with RadPdfViewer you should create a custom class that implements the IPdfFilter interface. Then you can perform the conversion in the Decode method:  

class JpxFilterCodec : IPdfFilter
{
    private readonly string OpenJpegPath = @"..\..\openjpeg-2.1.0-win32-x86\bin\opj_decompress";

    public byte[] Encode(PdfObject encodedObject, byte[] inputData)
    {
        throw new NotImplementedException();
    }

    public byte[] Decode(PdfObject decodedObject, byte[] inputData, DecodeParameters decodeParameters)
    {
        string filename = Guid.NewGuid().ToString();

        File.WriteAllBytes(filename + ".j2k", inputData);
        ProcessStartInfo processInfo = new ProcessStartInfo(OpenJpegPath, " -i " + filename + ".j2k -o " + filename + ".bmp");
        processInfo.WorkingDirectory = Directory.GetCurrentDirectory();
        processInfo.WindowStyle = ProcessWindowStyle.Hidden;
        processInfo.CreateNoWindow = true;
        var process = Process.Start(processInfo);
        process.WaitForExit();
        System.Drawing.Bitmap bitmap = System.Drawing.Image.FromFile(filename + ".bmp") as Bitmap;
        if (bitmap == null)
        {
            return new byte[0];
        }

        BitmapData bitmapData = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, bitmap.PixelFormat);

        int length = bitmapData.Stride * bitmapData.Height;
        int stride = bitmapData.Stride;
        byte[] bytes = new byte[length];

        System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, bytes, 0, length);
        bitmap.UnlockBits(bitmapData);

        byte[] bytePixels = new byte[bitmapData.Width * bitmapData.Height * 3];

        int resLength = bytePixels.Length;
        for (int i = 0; i < resLength; i++)
        {
            int row = i / (bitmapData.Width * 3);
            int col = i % (bitmapData.Width * 3);
            bytePixels[i] = bytes[row * stride + col];
        }

        bitmap.Dispose();
        File.Delete(filename + ".j2k");
        File.Delete(filename + ".bmp");

        return bytePixels;
    }

    public string Name
    {
        get { return "JPXDecode"; }
    }
}

Friend Class JpxFilterCodec
    Implements IPdfFilter

    Private ReadOnly OpenJpegPath As String = "..\..\openjpeg-2.1.0-win32-x86\bin\opj_decompress"

    Public Function Encode(ByVal encodedObject As PdfObject, ByVal inputData() As Byte) As Byte() Implements IPdfFilter.Encode
        Throw New NotImplementedException()
    End Function

    Public Function Decode(ByVal decodedObject As PdfObject, ByVal inputData() As Byte, ByVal decodeParameters As DecodeParameters) As Byte() Implements IPdfFilter.Decode
        Dim filename As String = Guid.NewGuid().ToString()

        File.WriteAllBytes(filename & ".j2k", inputData)
        Dim processInfo As New ProcessStartInfo(OpenJpegPath, " -i " & filename & ".j2k -o " & filename & ".bmp")
        processInfo.WorkingDirectory = Directory.GetCurrentDirectory()
        processInfo.WindowStyle = ProcessWindowStyle.Hidden
        processInfo.CreateNoWindow = True
        Dim process = System.Diagnostics.Process.Start(processInfo)
        process.WaitForExit()
        Dim bitmap As System.Drawing.Bitmap = TryCast(System.Drawing.Image.FromFile(filename & ".bmp"), Bitmap)
        If bitmap Is Nothing Then
            Return New Byte(){}
        End If

        Dim bitmapData As BitmapData = bitmap.LockBits(New Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, bitmap.PixelFormat)

        Dim length As Integer = bitmapData.Stride * bitmapData.Height
        Dim stride As Integer = bitmapData.Stride
        Dim bytes(length - 1) As Byte

        System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, bytes, 0, length)
        bitmap.UnlockBits(bitmapData)

        Dim bytePixels((bitmapData.Width * bitmapData.Height * 3) - 1) As Byte

        Dim resLength As Integer = bytePixels.Length
        For i As Integer = 0 To resLength - 1
            Dim row As Integer = i \ (bitmapData.Width * 3)
            Dim col As Integer = i Mod (bitmapData.Width * 3)
            bytePixels(i) = bytes(row * stride + col)
        Next i

        bitmap.Dispose()
        File.Delete(filename & ".j2k")
        File.Delete(filename & ".bmp")

        Return bytePixels
    End Function

    Public ReadOnly Property Name() As String Implements IPdfFilter.Name
        Get
            Return "JPXDecode"
        End Get
    End Property
End Class

Currently, you can use the above class by calling the RegisterFilter method with reflection:

Telerik.Windows.Pdf.Documents.Fixed.FormatProviders.Old.Pdf.Filters.FiltersManager.RegisterFilter(new JpxFilterCodec());

Telerik.Windows.Pdf.Documents.Fixed.FormatProviders.Old.Pdf.Filters.FiltersManager.RegisterFilter(New JpxFilterCodec())

You can download a VB and C# project from the following link.

In this article