fix pdf creation for very large exports (#1533)
This commit is contained in:
@@ -34,7 +34,21 @@ class MPdfConverter implements HtmlToPdfConverter
|
|||||||
{
|
{
|
||||||
$mpdf = new Mpdf(['tempDir' => $this->cacheDirectory]);
|
$mpdf = new Mpdf(['tempDir' => $this->cacheDirectory]);
|
||||||
$mpdf->creator = Constants::SOFTWARE;
|
$mpdf->creator = Constants::SOFTWARE;
|
||||||
$mpdf->WriteHTML($html);
|
|
||||||
|
// some OS do not follow the PHP default settings
|
||||||
|
if ((int) ini_get('pcre.backtrack_limit') < 1000000) {
|
||||||
|
@ini_set('pcre.backtrack_limit', 1000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reduce the size of content parts that are passed to MPDF, to prevent
|
||||||
|
// https://mpdf.github.io/troubleshooting/known-issues.html#blank-pages-or-some-sections-missing
|
||||||
|
$parts = explode('<pagebreak>', $html);
|
||||||
|
for ($i = 0; $i < count($parts); $i++) {
|
||||||
|
$mpdf->WriteHTML($parts[$i]);
|
||||||
|
if ($i < count($parts) - 1) {
|
||||||
|
$mpdf->WriteHTML('<pagebreak>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $mpdf->Output('', Destination::STRING_RETURN);
|
return $mpdf->Output('', Destination::STRING_RETURN);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,20 +10,32 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<style>
|
<style>
|
||||||
body {font-family: sans-serif;
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
}
|
}
|
||||||
p { margin: 0pt; }
|
p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
table.items {
|
table.items {
|
||||||
border: 0.1mm solid #000000;
|
border: 0.1mm solid #000000;
|
||||||
|
width: 100%;
|
||||||
|
font-size: 9pt;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
td, th {
|
||||||
|
padding: 7px;
|
||||||
|
}
|
||||||
|
td {
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
td { vertical-align: top; }
|
|
||||||
.items td {
|
.items td {
|
||||||
border-left: 0.1mm solid #000000;
|
border-left: 0.1mm solid #000000;
|
||||||
border-right: 0.1mm solid #000000;
|
border-right: 0.1mm solid #000000;
|
||||||
}
|
}
|
||||||
.items tr.even {
|
.items tr.even {
|
||||||
background-color: #e0ebff;
|
/*background-color: #e0ebff;*/
|
||||||
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
.items tr.summary {
|
.items tr.summary {
|
||||||
background-color: #efefef;
|
background-color: #efefef;
|
||||||
@@ -33,22 +45,25 @@
|
|||||||
border-top: 0.1mm solid #000000;
|
border-top: 0.1mm solid #000000;
|
||||||
border-bottom: 0.1mm solid #000000;
|
border-bottom: 0.1mm solid #000000;
|
||||||
}
|
}
|
||||||
table thead td {
|
table thead th {
|
||||||
background-color: #ececec;
|
background-color: #ececec;
|
||||||
text-align: center;
|
|
||||||
border: 0.1mm solid #000000;
|
border: 0.1mm solid #000000;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 11pt;
|
font-size: 10pt;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
.items td.totals {
|
.items td.totals {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-align: right;
|
|
||||||
border: 0.1mm solid #000000;
|
border: 0.1mm solid #000000;
|
||||||
}
|
}
|
||||||
|
.items .center,
|
||||||
.items td.duration,
|
.items td.duration,
|
||||||
.items td.cost {
|
.items td.cost {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
.text-nowrap {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@@ -82,14 +97,14 @@ mpdf-->
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>{{ 'export.summary'|trans }}</h3>
|
<h3>{{ 'export.summary'|trans }}</h3>
|
||||||
<table class="items" width="100%" style="font-size: 9pt; border-collapse: collapse; " cellpadding="8">
|
<table class="items">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ 'label.customer'|trans }}</td>
|
<th>{{ 'label.customer'|trans }}</th>
|
||||||
<td>{{ 'label.project'|trans }}</td>
|
<th>{{ 'label.project'|trans }}</th>
|
||||||
<td>{{ 'label.duration'|trans }}</td>
|
<th class="center">{{ 'label.duration'|trans }}</th>
|
||||||
{% if showRateColumn %}
|
{% if showRateColumn %}
|
||||||
<td>{{ 'label.rate'|trans }}</td>
|
<th class="center">{{ 'label.rate'|trans }}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -149,17 +164,17 @@ mpdf-->
|
|||||||
{% set duration = 0 %}
|
{% set duration = 0 %}
|
||||||
{% set rate = 0 %}
|
{% set rate = 0 %}
|
||||||
{% set currency = false %}
|
{% set currency = false %}
|
||||||
<table class="items" width="100%" style="font-size: 9pt; border-collapse: collapse; " cellpadding="8">
|
<table class="items">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ 'label.date'|trans }}</td>
|
<th>{{ 'label.date'|trans }}</th>
|
||||||
{% if showUserColumn %}
|
{% if showUserColumn %}
|
||||||
<td>{{ 'label.user'|trans }}</td>
|
<th>{{ 'label.user'|trans }}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td width="">{{ 'label.description'|trans }}</td>
|
<th>{{ 'label.description'|trans }}</th>
|
||||||
<td>{{ 'label.duration'|trans }}</td>
|
<th class="center">{{ 'label.duration'|trans }}</th>
|
||||||
{% if showRateColumn %}
|
{% if showRateColumn %}
|
||||||
<td>{{ 'label.rate'|trans }}</td>
|
<th class="center">{{ 'label.rate'|trans }}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -173,7 +188,7 @@ mpdf-->
|
|||||||
{% set currency = null %}
|
{% set currency = null %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<tr class="{{ cycle(['odd', 'even'], loop.index0) }}">
|
<tr class="{{ cycle(['odd', 'even'], loop.index0) }}">
|
||||||
<td>
|
<td class="text-nowrap">
|
||||||
{{ entry.begin|date_time }}
|
{{ entry.begin|date_time }}
|
||||||
{% if entry.end %}
|
{% if entry.end %}
|
||||||
<br>
|
<br>
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ class ExportControllerTest extends ControllerBaseTest
|
|||||||
|
|
||||||
// poor mans assertions ;-)
|
// poor mans assertions ;-)
|
||||||
$this->assertStringContainsString('export_print', $node->getIterator()[0]->getAttribute('class'));
|
$this->assertStringContainsString('export_print', $node->getIterator()[0]->getAttribute('class'));
|
||||||
$this->assertStringContainsString('<h2>List of expenses</h2>', $response->getContent());
|
$this->assertStringContainsString('<h2>', $response->getContent());
|
||||||
$this->assertStringContainsString('<h3>Summary</h3>', $response->getContent());
|
$this->assertStringContainsString('<h3>Summary</h3>', $response->getContent());
|
||||||
|
|
||||||
$node = $client->getCrawler()->filter('section.export div#export-records table.dataTable tbody tr');
|
$node = $client->getCrawler()->filter('section.export div#export-records table.dataTable tbody tr');
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class HtmlRendererTest extends AbstractRendererTest
|
|||||||
|
|
||||||
$content = $response->getContent();
|
$content = $response->getContent();
|
||||||
|
|
||||||
$this->assertStringContainsString('<h2>List of expenses</h2>', $content);
|
$this->assertStringContainsString('<h2>', $content);
|
||||||
$this->assertStringContainsString('<h3>Summary</h3>', $content);
|
$this->assertStringContainsString('<h3>Summary</h3>', $content);
|
||||||
$this->assertEquals(1, substr_count($content, 'id="export-summary"'));
|
$this->assertEquals(1, substr_count($content, 'id="export-summary"'));
|
||||||
$this->assertEquals(1, substr_count($content, 'id="export-records"'));
|
$this->assertEquals(1, substr_count($content, 'id="export-records"'));
|
||||||
|
|||||||
@@ -970,7 +970,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="export.document_title">
|
<trans-unit id="export.document_title">
|
||||||
<source>export.document_title</source>
|
<source>export.document_title</source>
|
||||||
<target>Aufstellung zu Aufwänden</target>
|
<target>Export von Zeiten</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="export.full_list">
|
<trans-unit id="export.full_list">
|
||||||
<source>export.full_list</source>
|
<source>export.full_list</source>
|
||||||
|
|||||||
@@ -970,7 +970,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="export.document_title">
|
<trans-unit id="export.document_title">
|
||||||
<source>export.document_title</source>
|
<source>export.document_title</source>
|
||||||
<target>List of expenses</target>
|
<target>Export of timesheets</target>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="export.full_list">
|
<trans-unit id="export.full_list">
|
||||||
<source>export.full_list</source>
|
<source>export.full_list</source>
|
||||||
|
|||||||
Reference in New Issue
Block a user