Kyle Hawk
Kyle Hawk

Reputation: 449

MPDF - feof(), ftell(), fseek(), and fread() Error - Table of Contents

I am using the following library to generator pdfs -> https://mpdf.github.io/

MPDF Version -> 7.1.7 (upgraded to 8.0.0)

I generated quite a few PDFs and then wrote a script to combine them all into one compiled PDF. All went well, and the PDF is 655 pages long.

The problem comes whenever I try to add the TOC to the pdf. The program runs into an infinite error saying that feof(), ftell(), fseek(), and fread() have the error "supplied resource is not a valid stream resource". This only happens if I try to add in the table of contents.

enter image description here

The following is the merge code. It scans a specific directory, determines if it is a file or sub-directory, then grabs the pdf and imports it.

    function import_page($pdf, $path) {
        $page_count = $pdf -> SetSourceFile($path);
        for($j = 1; $j <= $page_count; $j++) {
            $page_id = $pdf -> ImportPage($j);
            $pdf -> UseTemplate($page_id);
            if($j < $page_count) {
                $pdf -> AddPage();
            }
        }
    }
    $pdf = new \Mpdf\Mpdf([
        'mode' => 'utf-8',
        'format' => 'Letter-P'
    ]);
    $pdf -> SetImportUse();

    $pdf -> WriteHTML('<tocpagebreak links="1" toc-preHTML="&lt;h2&gt;Table of Contents&lt;/h2&gt;" toc-resetpagenum="1" />');

    $files = scandir('../../output/');
    natsort($files);

    foreach($files as $file) {
        if(strpos($file, ".pdf") !== false) { // File
            $title = substr($file, strpos($file, '.') + 2, -4);
            $pdf -> TOC_Entry(htmlspecialchars($title, ENT_QUOTES), 0);

            import_page($pdf, '../../output/' . $file);
            $pdf -> WriteHTML('<pagebreak>');
        } elseif($file !== "." && $file !== "..") { // Directory
            $title = substr($file, strpos($file, '.') + 2);
            $pdf -> TOC_Entry(htmlspecialchars($title, ENT_QUOTES), 0);

            $sub_files = scandir('../../output/' . $file . '/');
            natsort($sub_files);

            foreach($sub_files as $sub_file) {
                if(strpos($sub_file, ".pdf") !== false) { // File
                    $sub_title = substr($sub_file, strpos($sub_file, '.') + 2, -4);
                    $pdf -> TOC_Entry(htmlspecialchars($sub_title, ENT_QUOTES), 1);

                    import_page($pdf, '../../output/' . $file . '/' . $sub_file);
                    $pdf -> WriteHTML('<pagebreak>');
                }
            }
        }
    }

    $pdf -> Output();

This code runs perfectly so long as the tocpagebreak line is commented out. As soon as I try and add that, all hell breaks loose.

Fix Attempts

-Moving the writehtml TOC to the end of the document, or adding a page before. Result: Fail.

-Removing all settings from tocpagebreak, trying methods tocpagebreak and tocpagebreakarray. Result: Fail.

-Commenting out either one section of the if block or the other (single file or sub-directories). Result: Pass.

-Break the loop after a single sub directory iteration. Result: Pass.

-Save compiled pdf. Write quick import of just that single page and attempt to add a TOC. Result: Fail.

-Upgraded MPDF version to 8.0.0. Result: Fail.

Upvotes: 1

Views: 399

Answers (1)

miks
miks

Reputation: 66

By default StreamReader object is created with closeStream flag enabled when you are setting PDF source file as filename string.

    $page_count = $pdf -> SetSourceFile($path);

Problem here is in deep object cloning during TOC generation, MPDF object is cloned as well as all StreamReader objects.

When cloned TOC object goes out of scope, its garbage collected, and destructor of cloned StreamReader class is called and file handle is closed, causing invalid stream in original object.

Solution for this, is to set PDF source file as StreamReader object with closeStream flag disabled.

Using your example it should look something like this:

    function import_page($pdf, $path) {
        $fh = fopen($path, 'rb');
        $sr = new StreamReader($fh, false);
        $page_count = $pdf -> SetSourceFile(sr);
        for($j = 1; $j <= $page_count; $j++) {
            $page_id = $pdf -> ImportPage($j);
            $pdf -> UseTemplate($page_id);
            if($j < $page_count) {
                $pdf -> AddPage();
            }
        }
    }

Upvotes: 1

Related Questions