ismaelw / laratex

A Laravel package for creating PDF files using LaTeX
MIT License
105 stars 13 forks source link

Can not create pdf on windows #20

Closed zillion42 closed 1 year ago

zillion42 commented 1 year ago

Hi,

I have texlive installed in C:\texlive\2023\bin\windows I also added this to my path environment variable, running pdflatex in the cmd prompt works.

If I run return (new LaraTeX)->dryRun();

with default config/laratex.php:

<?php

return [
    // bin path to your pdflatex installation | use 'which pdflatex' on a linux system to find out which is the path to your pdflatex installation
    'binPath' => 'pdflatex',

    // Folder in your storage folder where you would like to store the temp files created by LaraTeX
    'tempPath' => 'app',

    // boolean to define if log, aux and tex files should be deleted after generating PDF
    'teardown' => false,
];

I get a Str::random(10) file 1 x vr0E5B8 1 x vr0E5B8.log 1 x vr0E5B8.aux

in my app directory

The log reads:

Output written on D:\Apache24_htdocs\project\storage
\app/vr0E5B8.pdf (1 page, 35744 bytes).
PDF statistics:
 29 PDF objects out of 1000 (max. 8388607)
 17 compressed objects within 1 object stream
 0 named destinations out of 1000 (max. 500000)
 1 words of extra memory for PDF output out of 10000 (max. 10000000)

but the file is not there, it's also not downloaded automatically.

I already tried all combinations of

    // Folder in your storage folder where you would like to store the temp files created by LaraTeX
    'tempPath' => 'app/',
        'tempPath' => 'app//',
    'tempPath' => 'app\',
        'tempPath' => 'app\\',
    'tempPath' => 'app',

None seems to work.

If I rename vr0E5B8 into vr0E5B8.tex, I can double click open it with texworks and compile it just fine.

What am I doing wrong?

Running Win10 Pro, 22H2, 19045.2728 with Apache 2.4 (Xampp), PHP Version => 7.4.7

zillion42 commented 1 year ago

EDIT: I step by step debugged the whole process now.

The Pdf is in fact created, but it won't download in the response. It shows and empty chrome tab, later the:

register_shutdown_function(function () use ($tmpfname) {
            if (File::exists($tmpfname . '.pdf')) {
                File::delete($tmpfname . '.pdf');
            }
        });

deletes the file... Just tried with Firefox, same result.

zillion42 commented 1 year ago

I also created the tex blade and tried:

return (new LaraTeX('latex.tex'))->with([
            'Name' => 'John Doe',
            'Dob' => '01/01/1990',
            'SpecialCharacters' => '$ (a < b) $',
            'languages' => [
                'English',
                'Spanish',
                'Italian'
            ]
        ])->download('test.pdf');

I also tried

->inline('test.pdf'); 
->content();

neither works.

If I try ->savePdf('C:\Users\User\Desktop') I get:

ErrorException
rename(D:\Apache24_htdocs\projects\storage\app\qnp1882.pdf,C:\Users\User\Desktop): Access denied (code: 5) 
zillion42 commented 1 year ago

This works:

    /**
     * Download file as a response
     *
     * @param  string|null $fileName
     * @return Illuminate\Http\Response
     */
    public function download($fileName = null) {
        if (!$this->isRaw) {
            $this->render();
        }

        $pdfPath = $this->generate();
        if (!$fileName) {
            $fileName = basename($pdfPath);
        }

        if (file_exists($pdfPath)) {
            header('Content-type: application/pdf');
            header('Content-Disposition: attachment; filename="' . $fileName . '"');
            header('Content-Transfer-Encoding: binary');
            header('Content-Length: ' . filesize($pdfPath));
            header('Accept-Ranges: bytes');
            @readfile($pdfPath);
        }
    }

but not using response. 👎

ismaelw commented 1 year ago

I also created the tex blade and tried:

return (new LaraTeX('latex.tex'))->with([
            'Name' => 'John Doe',
            'Dob' => '01/01/1990',
            'SpecialCharacters' => '$ (a < b) $',
            'languages' => [
                'English',
                'Spanish',
                'Italian'
            ]
        ])->download('test.pdf');

I also tried

->inline('test.pdf'); 
->content();

neither works.

If I try ->savePdf('C:\Users\User\Desktop') I get:

ErrorException
rename(D:\Apache24_htdocs\projects\storage\app\qnp1882.pdf,C:\Users\User\Desktop): Access denied (code: 5) 

Saving a file outside of your laravel app is not working out of the box and security-wise also not recommended. The savePdf method can be used if you want each pdf saved somewhere inside of your laravel app storage. Note: This comment is just a side-note. I am trying to think about other reasons why your PDF generation doesn't work in Windows

ismaelw commented 1 year ago

Hi @zillion42

I prepared my windows system now in the following way:

First I was able to figure out that after adding the texlive path to my PATH environment variable did not help, as phpinfo() didn't recognized the change. So inside some log files on my system I was able to find out that "pdflatex" was not recognized as a command.

Using the absolute path C:\texlive\2023\bin\windows\pdflatex.exe in the config binPath did the trick in that moment.

After some research I got tired and restarted my machine. After that phpinfo() did actually have the texlive path from my PATH system environment variable in the PHP Path variable.

Changing back the binPath to pdflatex then made me beeing able to run dryrun and a PDF has been downloaded successfully.

So my question to you would be:

Thank you so much for your feedback.

Kind regards, Ismael

zillion42 commented 1 year ago

Hi,

Did you restart your computer after changing your PATH environment variable? Sure, more than once, but actually in windows it is enough to restart the console (cmd prompt) after adding the path. I don't quite understand why you were searching the texlive path in phpinfo, but I guess the php PATH should be the same as the system PATH. I never had any issues with PHP finding pdlatex.exe, that was never a problem. But just for testing purposes I also added the complete path which is C:\texlive\2023\bin\windows\pdflatex.exe - That works just the same.

Also like I said, laratex actually generates the pdf, so it definitely works. I'm sorry the title of this issue is a bit misleading, I only found out it works after setting some breakpoints and using the debugger.

The problem is that the response does not download the file, before it gets deleted by the shutdown function. So the pdf is there for a brief moment, then gets deleted again. The response however does not trigger a file download.

I'm suspecting more something not working with:

\Event::dispatch(new LaratexPdfWasGenerated($fileName, 'download', $this->metadata));

        return \Response::download($pdfPath, $fileName, [
            'Content-Type' => 'application/pdf',
        ]);

EDIT: Another possible issue might be a problem with the forward and backslash notation, it is always troublesome. Note the pdlatex logs writes: Output written on D:\Apache24_htdocs\project\storage\app/vr0E5B8.pdf (1 page, 35744 bytes).

Anyway Thank you so much for testing this so quick on a windows machine. Please note, I am not using PHP 8, but 7.4 together with laravel framework 8.75

"require": {
        "php": "^7.3|^8.0",
        "directorytree/ldaprecord-laravel": "^2.5",
        "doctrine/dbal": "^3.3",
        "guzzlehttp/guzzle": "^7.0.1",
        "illuminate/support": "^8.83",
        "ismaelw/laratex": "^1.0",
        "laravel/framework": "^8.75",
        "laravel/sanctum": "^2.11",
        "laravel/tinker": "^2.5",
        "phpoffice/phpspreadsheet": "^1.23"
    },

EDIT2:

Saving a file outside of your laravel app is not working out of the box and security-wise also not recommended. The savePdf method can be used if you want each pdf saved somewhere inside of your laravel app storage. Note: This comment is just a side-note. I am trying to think about other reasons why your PDF generation doesn't work in Windows

That is true, but since the whole project runs only inside the intranet of our organization, security issues are thankfully not so relevant. This Hack that I used now, although not very secure at least works for the moment.

ismaelw commented 1 year ago

My apologies. You are right. You did mention the PDF gets generated.

What I couldn't read from your messages is if any exception is thrown?

Or everything seems to be fine but nothing happens?

If you wish I can continue the troubleshooting or we try to troubleshoot it on your end together?

I remember having someone that was able to use laratex using windows and PHP 7.4 so that should also be no issue here.

zillion42 commented 1 year ago

Since unfortunately you can not reproduce the issue, it would make sense to debug it on my end.., There is no exception thrown unfortunately, I will provide some screenshots.

ismaelw commented 1 year ago

Also I did try using a mix of slashes in windows. So in the same path a mix of back- and forwardslashes. Windows is actually pretty good in handling this. But to try it would be maybe an option to replace everything with forwardslashes.

zillion42 commented 1 year ago

2023-03-22 18_21_30-Krankenhilfe

pressing the export button routes directly to the named route clerk_export which triggers the export function in the clerkController:

    Route::prefix('sachbearbeiter')->controller(ClerkController::class)->group(function () {
        Route::get('/', 'index')->name('clerks_index');
        Route::get('erstellen', 'create')->name('clerk_create');
        Route::get('export', 'export')->name('clerk_export');

clerkController.php:

    public function download(){

        return (new LaraTeX('latex.tex'))->with([
            'Name' => 'John Doe',
            'Dob' => '01/01/1990',
            'SpecialCharacters' => '$ (a < b) $',
            'languages' => [
                'English',
                'Spanish',
                'Italian'
            ]
        ])->download('test.pdf');
    }

    public function export(Request $request) {

        $this->download();

    }

This then leads me to a blank white tab, nothing happens there.

2023-03-22 18_22_01-localhost_krankenhilfe_soz_public_index php_sachbearbeiter_export_clerkID=1 Rech

zillion42 commented 1 year ago

using:

    // Folder in your storage folder where you would like to store the temp files created by LaraTeX
    'tempPath' => "app\/",

leads to:

Output written on D:\Webappsrv_c_httpd_Apache24_htdocs\krankenhilfe_soz\storage
\app\//6RE505B.pdf (1 page, 35547 bytes).

using:

    // Folder in your storage folder where you would like to store the temp files created by LaraTeX
    'tempPath' => "app//",

leads to:

Output written on D:\Webappsrv_c_httpd_Apache24_htdocs\krankenhilfe_soz\storage
\app///h8S3112.pdf (1 page, 35547 bytes).

using:

    // Folder in your storage folder where you would like to store the temp files created by LaraTeX
    'tempPath' => "app\\",

leads to:

Output written on D:\Webappsrv_c_httpd_Apache24_htdocs\krankenhilfe_soz\storage
\app\/POyA89B.pdf (1 page, 35547 bytes).

using:

    // Folder in your storage folder where you would like to store the temp files created by LaraTeX
    'tempPath' => "app/\\",

leads to:

Output written on D:\Webappsrv_c_httpd_Apache24_htdocs\krankenhilfe_soz\storage
\app/\/xaG198C.pdf (1 page, 35547 bytes).

:-) any more suggestions...

zillion42 commented 1 year ago

It looks totally fine after generation: 2023-03-22 18_57_05-Info-Center

ismaelw commented 1 year ago

If all this seems to work just fine I am thinking if we should focus on the response which is Laravel specific and not LaraTeX specific. I am just thinking about that because on Line 203 everything seems to be fine and as to my understanding a PDF file exists inside your storage/app folder.

Maybe a stupid question. but did you ever try to put a random file into your storage path and download it using the Response class?

zillion42 commented 1 year ago

Now that you say it, I haven't actually. I just looked at how I download PhpOffice Excel files. Edit: Note, this is ancient code, that I copied from older projects, without thinking to much about it

        // Redirect output to a client’s web browser (Xlsx)
        header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
        header('Content-Disposition: attachment;filename="' . $filename . '.xlsx"');
        header('Cache-Control: max-age=0');
        // If you're serving to IE 9, then the following may be needed
        header('Cache-Control: max-age=1');

        // If you're serving to IE over SSL, then the following may be needed
        header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified
        header('Cache-Control: cache, must-revalidate'); // HTTP/1.1
        header('Pragma: public'); // HTTP/1.0

        $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
        $writer->save('php://output');
        exit;

But I tested it now, and using the Response class also works without any problems. I'm unsure about the header here, but it works.

        $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
        $writer->save($path = storage_path('download.xlsx'));
        return response()->download($path, $filename . '.xlsx')->deleteFileAfterSend();

$writer belongs to PhpOffice

namespace PhpOffice\PhpSpreadsheet;

use PhpOffice\PhpSpreadsheet\Reader\IReader;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Writer\IWriter;
ismaelw commented 1 year ago

This is actually getting super weird. So at the moment everything seems to works and should be working but it just doesn't. How I cannot replicate the issue on my side it is quiet hard to think of something without having a hands-on.

Things I would like to test would be

If it is an option for you we can maybe even arrange a Teams call to get this working for you.

zillion42 commented 1 year ago

This is dd of the download

Symfony\Component\HttpFoundation\BinaryFileResponse {#1364 ▼
  #file: Symfony\Component\HttpFoundation\File\File {#1366 ▼
    path: "D:\Webappsrv_c_httpd_Apache24_htdocs\krankenhilfe_soz\storage\app"
    filename: "Bf682FC.pdf"
    basename: "Bf682FC.pdf"
    pathname: "D:\Webappsrv_c_httpd_Apache24_htdocs\krankenhilfe_soz\storage\app\Bf682FC.pdf"
    extension: "pdf"
    realPath: "D:\Webappsrv_c_httpd_Apache24_htdocs\krankenhilfe_soz\storage\app\Bf682FC.pdf"
    aTime: 2023-03-23 09:54:39
    mTime: 2023-03-23 09:54:39
    cTime: 2023-03-23 09:54:39
    inode: 4222124650748109
    size: 35547
    perms: 0100666
    owner: 0
    group: 0
    type: "file"
    writable: true
    readable: true
    executable: false
    file: true
    dir: false
    link: false
    linkTarget: "D:\Webappsrv_c_httpd_Apache24_htdocs\krankenhilfe_soz\storage\app\Bf682FC.pdf"
  }
  #offset: 0
  #maxlen: -1
  #deleteFileAfterSend: false
  #chunkSize: 8192
  +headers: Symfony\Component\HttpFoundation\ResponseHeaderBag {#1365 ▼
    #computedCacheControl: array:1 [▼
      "public" => true
    ]
    #cookies: []
    #headerNames: array:5 [▼
      "content-type" => "Content-Type"
      "cache-control" => "Cache-Control"
      "date" => "Date"
      "last-modified" => "Last-Modified"
      "content-disposition" => "Content-Disposition"
    ]
    #headers: array:5 [▼
      "content-type" => array:1 [▼
        0 => "application/pdf"
      ]
      "cache-control" => array:1 [▼
        0 => "public"
      ]
      "date" => array:1 [▼
        0 => "Thu, 23 Mar 2023 09:54:39 GMT"
      ]
      "last-modified" => array:1 [▼
        0 => "Thu, 23 Mar 2023 09:54:39 GMT"
      ]
      "content-disposition" => array:1 [▼
        0 => "attachment; filename=test.pdf"
      ]
    ]
    #cacheControl: array:1 [▼
      "public" => true
    ]
  }
  #content: null
  #version: "1.0"
  #statusCode: 200
  #statusText: "OK"
  #charset: null
}

Teams is not so easy here because of the proxy we use, but we have a jitsi where I could share my screen, I Just don't want to post addresses here.

ismaelw commented 1 year ago

Your response looks exactly the same like mine on my windows machine.

Another question: do you have the php_fileinfo PHP extension enabled? I guess probably yes but excluding always helps. Or in general I am thinking about uncatched exceptions from the apache and php front.

So maybe you could also check things like the "max_execution_time" runtime setting in PHP, and check the apache error_log in your xampp setup. I am just trying to think of all the issues I had in all my time and come up with a possible cause

ismaelw commented 1 year ago

Wait a minute.

After checking your code I realized one thing.

public function download(){

        return (new LaraTeX('latex.tex'))->with([
            'Name' => 'John Doe',
            'Dob' => '01/01/1990',
            'SpecialCharacters' => '$ (a < b) $',
            'languages' => [
                'English',
                'Spanish',
                'Italian'
            ]
        ])->download('test.pdf');
    }

    public function export(Request $request) {

        $this->download();

    }

Your download method returns a file but your export method doesn't actually return anything.

Can you try changing $this->download();

to return $this->download();

EDIT: I was able to replicate the white empty tab when I removed the return on my end. In that case I am not actually returning a file.

zillion42 commented 1 year ago

Hi, I actually have quite some work ahead of me at the moment, I also think it's weird by now.

max_execution_time=1440
extension=fileinfo

WOW

Your download method returns a file but your export method doesn't actually return anything.

Can you try changing $this->download();

to return $this->download();

EDIT: I was able to replicate the white empty tab when I removed the return on my end. In that case I am not actually returning a file.

That was it... omg, thx for all the trouble. I was beginning to suspect it must be something really stupid on my part. I think I'll donate you a coffee for this.

You sponsored my next beer (or two)!

Thanks so much again.

Edit: I guess I can close this issue now 🤦

ismaelw commented 1 year ago

haha amazing. Well that happens. Whenever I have an issue like this I end up face-palming myself for wasting valuable time on a syntax or thinking error.

You are very welcome - and I am here if there is anything else I can help you with! 🥳

Oh and thank you so much for the donation! Much appreciated!

zillion42 commented 1 year ago

I'm sorry to bother you so much, but I have another question real quick. Should I open another issue?

I have this kind of array each entry has a model and a collection of models:

2023-03-23 11_52_55-Start

I'm passing it to the tex blade like so: return (new LaraTeX('clerk.clerk_export_tex'))->with($daten)->download('test.pdf');

and I get 2023-03-23 11_53_35-🧨 Undefined variable_ daten (View_ D__Webappsrv_c_httpd_Apache24_htdocs_kranken

ismaelw commented 1 year ago

@zillion42 no worries.

Please reference this in a new issue and we can look at it together :)