Skip to content
Advertisement

Importing large number of pdfs into mPDF is causing /fopen to fail

Edit: Updated after assistance from Jan

Edit: The code dos not fail until $mpdf->Output($max . ‘ imports .pdf’, ‘D’); is called

PHP Version 7.1

MPDF Version 8.0.10

Setasign fpdi_pdf-parser Version 2.0.4 Error

Failed to open file (fopen) Compnay Induction (checklist) th v2.pdf Failed to open file (fopen) Information Register for Employees.pdf Failed to open file (fopen) Coshh listings v7.pdf Failed to open file (fopen) Emp Competency Form.pdf

Below is a snippet of my code which produces the error

function addCustomForms($forms, $mpdf, $Customer)
{
    if ($forms) {
        $handles = [];
        foreach ($forms as $CustomPolicyForm) {
            if (is_file('../cdata/' . $Customer->reference . '/policy-forms/' . $CustomPolicyForm->form)) {
                try {
                    $h = fopen('../cdata/' . $Customer->reference . '/policy-forms/' . $CustomPolicyForm->form, 'rb');
                    // File not Opened
                    if ($h === false) {
                            echo 'Failed to open file (fopen) ' . $CustomPolicyForm->form . '<br/>';
                            return false;
                        } else {

                        $stream = new setasignFpdiPdfParserStreamReader($h, false);
                        $pagecount = $mpdf->setSourceFile($stream);
                        if ($pagecount > 0) {
                            for ($i = 1; $i <= ($pagecount); $i++) {
                                $mpdf->AddPage($CustomPolicyForm->orientation);
                                if ($i == 1) {
                                    $html = '<h3 style="color: #ffffff;">' . sd($CustomPolicyForm->title) . '</h3>';
                                    $mpdf->WriteHTML($html);
                                }
                                try {
                                    $import_page = $mpdf->importPage($i);
                                    try {
                                        $mpdf->useTemplate($import_page);

                                    } catch (Exception $e) {
                                        echo 'Not worked for ' . $CustomPolicyForm->title .'<br/>';
                                        return false;
                                    }
                                } catch (Exception $e) {
                                    echo 'importPage failed for ' . $CustomPolicyForm->title . ' Page ' . $i .'<br/>';
                                    return false;
                                }
                            }
                            $handles[] = $h;

                        } else {
                            echo 'Page count is less than 1 for ' . $CustomPolicyForm->form .'<br/>';
                            return false;

                        }
                    }
                } catch
                (Exception $e) {
                    echo 'Failed to set source file for ' . $CustomPolicyForm->form . '<br/>';
                    return false;
                }
            } else {
                echo 'File not found: ' . $CustomPolicyForm->form .'<br/>';
                return false;
            }
            $pagecount = null;
        }
        return $handles;

    } else {
        // No forms found
        echo 'No Forms Found';
        return false;
    }
}

$mpdf->Output(Customer::find_by_id($Policy->customer_id)->customer . '.pdf', 'D');
        fclose($handles);

Changing TOCuseLinking’ => false hides/resolves the issue but I am not sure what is causing the issue in the first place.

The test file I am using is https://1drv.ms/b/s!ApXTTaxD_QQPj7kwpxX3G4a5-2Ir2Q?e=WJRMi3 (This is a blank PDF)

Does anyone have any pointers?

Advertisement

Answer

The TOC functionalitiy of mPDF tries to do some magic by cloning the whole instance and resetting it… I didn’t digged deeper into it. But by this the references to the stream readers are unset. Due to the amount of objects the garbadge collector is triggered later which triggers the __destruct() method which closes the stream handle then.

To bypass this issue you need to get control over the file handle. Your example can be rewritten like this:

$h = fopen('BLANK PDF.pdf', 'rb');
$stream = new setasignFpdiPdfParserStreamReader($h, false);

for ($count = 0; $count < $max; $count++) {
    $pagecount = $mpdf->setSourceFile($stream);
    if ($pagecount > 0) {
        $mpdf->AddPage();
        for ($i = 1; $i <= ($pagecount); $i++) {
            if ($i == 1) {
                $html = '<h3 style="color: #000;">' . htmlentities('Blank PDF ' . $count) . '</h3>';
                $mpdf->WriteHTML($html);
            }
            $import_page = $mpdf->importPage($i);
            $mpdf->useTemplate($import_page);

        }
    } else {
        return false;
    }
}
$mpdf->Output($max . ' imports .pdf', 'F');

fclose($h);

If you need to work with different files, store the handles in an array to keep their references.

You should also notice that the operation system have a limit on open file handles. FPDI is bound to this limit.

User contributions licensed under: CC BY-SA
6 People found this is helpful
Advertisement