Malformed PDF print doesn't catch RuntimeException

2.6k Views Asked by At

I try to print a PDF file and it works fine until I try to print a malformed PDF file. I don't know why the application crashes even though I used try / catch to prevent crashes. I checked and found out that PrintManager.java:1101 throws RuntimeException:

 case MSG_ON_KILL: {
     if (DEBUG) {
         Log.i(LOG_TAG, "onKill()");
     }

     String reason = (String) message.obj;
     throw new RuntimeException(reason);
 }

so code below shouldn't lead to crash:

public static void startPdfPrintProcedure(@NonNull Context context, @NonNull String filePath, @Nullable String jobName) {
    try {
        PrintManager printManager = (PrintManager) context.getSystemService(Context.PRINT_SERVICE);
        String jobName = formatDefaultJobName(context.getResources(), jobName);
        PrintDocumentAdapter pda = new SimplePrintDocumentAdapter(new File(filePath));
        if (printManager != null) {
            try {
                printManager.print(jobName, pda, null); // <- crash here even though there is a try/catch
            } catch (RuntimeException e) {
                showUnknownError();
            }
        } else {
            showUnknownError();
        }
    } catch (RuntimeException e) {
        showUnknownError();
    }
}

Exception that I get after try to print PDF. :

 java.lang.RuntimeException: Cannot print a malformed PDF file
    at android.print.PrintManager$PrintDocumentAdapterDelegate$MyHandler.handleMessage(PrintManager.java:1101)
    at android.os.Handler.dispatchMessage(Handler.java:112)
    at android.os.Looper.loop(Looper.java:216)
    at android.app.ActivityThread.main(ActivityThread.java:7625)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:524)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:987)

Why try/catch code doesn't catch this exception? How can I secure this piece of code from crashing?

2

There are 2 best solutions below

0
On

One way is to check if PDF file is corrupted or not by using PDF viewer library : https://github.com/voghDev/PdfViewPager

import library : implementation 'es.voghdev.pdfviewpager:library:1.1.2'

Use below code to check if PDF file is corrupted

BasePDFPagerAdapter adapter;
        PDFViewPager pdfViewPager;
        pdfViewPager = findViewById(R.id.pdfViewPager);
        adapter = new PDFPagerAdapter(this, path, new PdfErrorHandler() {
            @Override
            public void onPdfError(Throwable t) {
                Log.d("pdfcorrupt",">> yes");
                isPDFCorrupted = true;
            }
        });
        pdfViewPager.setAdapter(adapter);

When PDF file is not valid, onPdfError() method will be called.

If your file is corrupted, simply do not allow to print.

0
On

This happens because some "Genius" Google developer has come up with the "Great" idea of throwing an exception in the main thread causing your application to close. I have tried to solve the problem using reflection but the implementation is too closed. Unfortunately, you have to use it assuming the imminent closure of your application in case of incorrect files, unless you want to implement a library to check the PDF format before calling the API. Google never fails, you always have to mess around with its implementation.

The secondary thread ends up calling this handler.

    private final class MyHandler extends Handler {

            public static final int MSG_ON_KILL = 5;
            ...

            @Override
            public void handleMessage(Message message) {
                switch (message.what) {
                    ...
                    
                    case MSG_ON_KILL: {
                        if (DEBUG) {
                            Log.i(LOG_TAG, "onKill()");
                        }

                        String reason = (String) message.obj;
                        throw new RuntimeException(reason);<---------
                    }

                    default: {
                        throw new IllegalArgumentException("Unknown message: "
                                + message.what);
                    }
                }
            }
        }