skip to Main Content

I am using wkhtmltopdf to generate a pdf for my content within a Laravel app.

Controller.php

<?php

/**
 * @todo
 */
public function pdf($regionCode, $langCode, Model $model)
{
    $locale = LocalisationServiceProvider::getLocale($regionCode, $langCode);

    $pdfFileName = $this->buildPdfFileName($model->id, $locale);

    if (Storage::disk('root')->exists('public/storage/wkhtmltopdf/' . $pdfFileName)) {
        return response()->file(public_path('/storage/wkhtmltopdf/' . $pdfFileName));
    }

    $view = view('model.pdf', compact('model', 'locale'));
    $viewRendered = $view->render();

    $pdf = new Pdf([
        'binary' => env('WKHTMLTOPDF_BINARY_PATH'),
        'ignoreWarnings' => ('production' === env('APP_ENV')),
        'disable-smart-shrinking',
        'title' => $model->title . ' - Site.pdf',
        'encoding' => 'utf-8',
    ]);

    $pdf->addPage($viewRendered);

    if (!$pdf->saveAs(public_path('/storage/wkhtmltopdf/' . $pdfFileName))) {
        $error = $pdf->getError();
        // @todo log
        return response('An error occured', 500);
    }

    if (!$pdf->send($model->title . ' - Site.pdf', true)) {
        $error = $pdf->getError();
        return response('An error occured', 500);
    }
}

/**
 * @todo
 */
public function buildPdfFileName($id, $locale)
{
    return 'foobar_' . $id . '_' . $locale . '.pdf';
}

pdf.blade.php

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <meta charset="UTF-8">
    </head>
    <body>
        <div id="canvas" class="position-relative">

            @hasSection('header')
                <header id="header">
                    @yield('header')
                </header>
            @endif

            @hasSection('main')
                <main role="main" id="main-content">
                    @yield('main')
                </main>
            @endif

        </div>
    </body>
</html>

The PDF looks fine (except CSS issues, different matter). On my local machine, the PDF that gets send to the browser tab, also shows the correct browser tab title, meaning special chars are displayed correctly in the browser tab (like umlauts). When I want to save the file, the suggested file name in the OS’ save prompt is the same and also looks fine.

On production however, the title in the browser tab misses all special chars while when saving the file it suggests the correct file name again. So "können" for instance is displayed as "knnen" in the browser tab but the save prompt shows "können".

On both machines runs wkhtmltopdf 0.12.6 (with patched qt).

All I found in previous threads is adding utf encoding as meta and adding it as option, which I both did and locally it does work.

PS: buildPdfFileName is only used for the internal pdf name that gets saved and loaded locally, it is not the title/ filename suggestion.

4

Answers


  1. Chosen as BEST ANSWER

    After going through all suggestions, the only thing that worked for me was to set the title via <title> tag within the base template and remove the title option when creating a new pdf class:

    <?php
    
    $pdf = new Pdf([
        'binary' => env('WKHTMLTOPDF_BINARY_PATH'),
        'ignoreWarnings' => ('production' === env('APP_ENV')),
        'disable-smart-shrinking',
        'encoding' => 'utf-8',
    ]);
    
    $pdf->addPage($viewRendered);
    
    if (!$pdf->send($model->title . ' - Sitename.pdf', true)) {
        $error = $pdf->getError();
        return response('An error occured', 500);
    }
    

    This correctly shows the title in the browser tab and when saving has the correct filename suggestion.

    What didnt work:

    1.) Set the content type via htaccess:

    <If "%{REQUEST_URI} =~ m#/pdf$#"> Header always set Content-Type "application/pdf; charset=UTF-8" </If>
    This didnt even show in my response headers and had no effect at all. I probably did it wrong.

    2.) This lead to a server error:

    // 'encoding' => 'utf-8',
    'options' => array(
        'encoding' => 'utf-8'
    ),
    

    3.) putenv('LANG=de_DE.UTF-8'); had no effect (probably it already is utf8).

    4.) All my strings were already utf-8 encoded. Using urlencode within the title option did show urlencoded strings instead of umlauts.


  2. Maybe the problem is an encoding issue of the production environment on which your code runs.

    In particular, also because you said that your OS handles properly the file name when downloading it from production, on production you should check if the script that render the webpage/pdf is set with the correct encoding for the output to send to the browser (in your case the pdf).

    Usually, the browser uses the encoding delivered by the web page.

    You can try these attempts:

    • in addition to set the Content-Type in the rendered HTML, try to set it also in the PHP script, before sending any output to the browser.
    • try to encode the title: 'title' => urlencode($model->title . ' - Site.pdf'),
    • set it as wkhtmltopdf option:
      $pdf = new Pdf([
          'binary' => env('WKHTMLTOPDF_BINARY_PATH'),
          'ignoreWarnings' => ('production' === env('APP_ENV')),
          'disable-smart-shrinking',
          'title' => $model->title . ' - Site.pdf',
          // 'encoding' => 'utf-8',
          'options' => array(
              'encoding' => 'utf-8'
          ),
      ]);
      
    Login or Signup to reply.
  3. I dont think its related to

    • PDF or
    • user language or
    • means of serving

    Its traditional NOT to use non latin UTF characters in URLS because they need conversion into "safe" ranges for different OS, locality, applications, etc, and Latin-American is historically Universally Resource Location friendly. Here same file 2 different browsers 2 different results

    enter image description here

    When found locally still 2 different outcomes

    enter image description here

    Each browser is responsible for choosing tab titles , Note here the tab in the middle is the same as all the others, but browser asked if I wished to change it from German to English. the last example shows the Tab is "Untitled" without an HTML carrier until after the downloaded PDF is returned to the browser.

    enter image description here

    So towards your answer, the OS handles filenames during production, Here all is well, but Browsers decide for them selves based of loaded html title or 2nd any other over-ride like users choice of tab name or a hint from RE-loaded.PDF, where the PDF string will have been correctly injected as PDF BOM-UTF-16BE by wkHTML as say /Title (þÿ T i t e l k ö n n e n f u n k t i o n i e r e n) then its up to the browser to convert that into a semblance of human readable string English, German UTF-8 URL encoded or whatever.

    enter image description here
    enter image description here

    Login or Signup to reply.
  4. When displaying a PDF, most browsers show the PDF title (when defined) in the tab title.

    To set the PDF title, you are using the title option, which is passed to wkhtmltopdf on the command line.

    First, you should check if $model->title is encoded in UTF-8 or not.

    Another possible explanation for the issue is that the PHP process has no LANG environment variable defined. In that case,
    the default locale is C, which supports only the ASCII encoding. All non-ASCII characters are considered invalid. A way to fix it is to add this before running wkhtmltopdf :

    putenv('LANG=de_DE.UTF-8');
    

    Alternatively, you can define the PDF title via the <title> HTML tag. Just remove the title option and add the <title> tag to your HTML page. This is the most reliable solution because it avoids transmitting non-ASCII characters on the command line.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search