1
|
|
|
<?php declare(strict_types=1); |
2
|
|
|
|
3
|
|
|
|
4
|
|
|
namespace Sprain\SwissQrBill\PaymentPart\Output\FpdfOutput; |
5
|
|
|
|
6
|
|
|
use Fpdf\Fpdf; |
7
|
|
|
use Sprain\SwissQrBill\Exception\InvalidFpdfImageFormat; |
8
|
|
|
use Sprain\SwissQrBill\PaymentPart\Output\AbstractOutput; |
9
|
|
|
use Sprain\SwissQrBill\PaymentPart\Output\Element\OutputElementInterface; |
10
|
|
|
use Sprain\SwissQrBill\PaymentPart\Output\Element\Placeholder; |
11
|
|
|
use Sprain\SwissQrBill\PaymentPart\Output\Element\Text; |
12
|
|
|
use Sprain\SwissQrBill\PaymentPart\Output\Element\Title; |
13
|
|
|
use Sprain\SwissQrBill\PaymentPart\Output\OutputInterface; |
14
|
|
|
use Sprain\SwissQrBill\PaymentPart\Translation\Translation; |
15
|
|
|
use Sprain\SwissQrBill\QrBill; |
16
|
|
|
use Sprain\SwissQrBill\QrCode\QrCode; |
17
|
|
|
|
18
|
|
|
final class FpdfOutput extends AbstractOutput implements OutputInterface |
19
|
|
|
{ |
20
|
|
|
// FPDF |
21
|
|
|
private const BORDER = 0; |
22
|
|
|
private const ALIGN_LEFT = 'L'; |
23
|
|
|
private const ALIGN_RIGHT = 'R'; |
24
|
|
|
private const ALIGN_CENTER = 'C'; |
25
|
|
|
private const FONT = 'Helvetica'; |
26
|
|
|
|
27
|
|
|
// Location |
28
|
|
|
private const CURRENCY_AMOUNT_Y = 259.3; |
29
|
|
|
private const AMOUNT_LINE_SPACING = 1.2; |
30
|
|
|
private const AMOUNT_LINE_SPACING_RCPT = 0.6; |
31
|
|
|
private const LEFT_PART_X = 4; |
32
|
|
|
private const RIGHT_PART_X = 66; |
33
|
|
|
private const RIGHT_PART_X_INFO = 117; |
34
|
|
|
private const TITLE_Y = 195.2; |
35
|
|
|
|
36
|
|
|
// Font size |
37
|
|
|
private const FONT_SIZE_MAIN_TITLE = 11; |
38
|
|
|
private const FONT_SIZE_TITLE_RECEIPT = 6; |
39
|
|
|
private const FONT_SIZE_RECEIPT = 8; |
40
|
|
|
private const FONT_SIZE_TITLE_PAYMENT_PART = 8; |
41
|
|
|
private const FONT_SIZE_PAYMENT_PART = 10; |
42
|
|
|
private const FONT_SIZE_FURTHER_INFORMATION = 7; |
43
|
|
|
|
44
|
|
|
// Line spacing |
45
|
|
|
private const LINE_SPACING_RECEIPT = 3.4; |
46
|
|
|
private const LINE_SPACING_PAYMENT_PART = 4.8; |
47
|
|
|
private float $amountLS = 0; |
48
|
|
|
|
49
|
|
|
private Fpdf $fpdf; |
50
|
|
|
private float $offsetX; |
51
|
|
|
private float $offsetY; |
52
|
|
|
|
53
|
|
|
public function __construct( |
54
|
|
|
QrBill $qrBill, |
55
|
|
|
string $language, |
56
|
|
|
Fpdf $fpdf, |
57
|
|
|
float $offsetX = 0, |
58
|
|
|
float $offsetY = 0 |
59
|
|
|
) { |
60
|
|
|
parent::__construct($qrBill, $language); |
61
|
|
|
$this->fpdf = $fpdf; |
62
|
|
|
$this->offsetX = $offsetX; |
63
|
|
|
$this->offsetY = $offsetY; |
64
|
|
|
$this->setQrCodeImageFormat(QrCode::FILE_FORMAT_PNG); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
public function getPaymentPart(): void |
68
|
|
|
{ |
69
|
|
|
$this->fpdf->SetAutoPageBreak(false); |
70
|
|
|
|
71
|
|
|
$this->addSeparatorContentIfNotPrintable(); |
72
|
|
|
|
73
|
|
|
$this->addInformationContentReceipt(); |
74
|
|
|
$this->addCurrencyContentReceipt(); |
75
|
|
|
$this->addAmountContentReceipt(); |
76
|
|
|
|
77
|
|
|
$this->addSwissQrCodeImage(); |
78
|
|
|
$this->addInformationContent(); |
79
|
|
|
$this->addCurrencyContent(); |
80
|
|
|
$this->addAmountContent(); |
81
|
|
|
$this->addFurtherInformationContent(); |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
public function setQrCodeImageFormat(string $fileExtension): AbstractOutput |
85
|
|
|
{ |
86
|
|
|
if ($fileExtension === 'svg') { |
87
|
|
|
throw new InvalidFpdfImageFormat('SVG images are not allowed by FPDF.'); |
88
|
|
|
} |
89
|
|
|
|
90
|
|
|
$this->qrCodeImageFormat = $fileExtension; |
91
|
|
|
|
92
|
|
|
return $this; |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
private function addSwissQrCodeImage(): void |
96
|
|
|
{ |
97
|
|
|
$qrCode = $this->getQrCode(); |
98
|
|
|
|
99
|
|
|
$yPosQrCode = 209.5 + $this->offsetY; |
100
|
|
|
$xPosQrCode = 67 + $this->offsetX; |
101
|
|
|
|
102
|
|
|
$this->fpdf->Image($qrCode->getDataUri($this->getQrCodeImageFormat()), $xPosQrCode, $yPosQrCode, 46, 46, 'png'); |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
private function addInformationContentReceipt(): void |
106
|
|
|
{ |
107
|
|
|
// Title |
108
|
|
|
$this->fpdf->SetFont(self::FONT, 'B', self::FONT_SIZE_MAIN_TITLE); |
109
|
|
|
$this->SetXY(self::LEFT_PART_X, self::TITLE_Y); |
110
|
|
|
$this->fpdf->MultiCell(0, 7, utf8_decode(Translation::get('receipt', $this->language))); |
|
|
|
|
111
|
|
|
|
112
|
|
|
// Elements |
113
|
|
|
$this->setY(204); |
114
|
|
|
foreach ($this->getInformationElementsOfReceipt() as $receiptInformationElement) { |
115
|
|
|
$this->setX(self::LEFT_PART_X); |
116
|
|
|
$this->setContentElement($receiptInformationElement, true); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
// Acceptance section |
120
|
|
|
$this->fpdf->SetFont(self::FONT, 'B', self::FONT_SIZE_TITLE_RECEIPT); |
121
|
|
|
$this->SetXY(self::LEFT_PART_X, 274.3); |
122
|
|
|
$this->fpdf->Cell(54, 0, utf8_decode(Translation::get('acceptancePoint', $this->language)), self::BORDER, '', self::ALIGN_RIGHT); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
private function addInformationContent(): void |
126
|
|
|
{ |
127
|
|
|
// Title |
128
|
|
|
$this->fpdf->SetFont(self::FONT, 'B', self::FONT_SIZE_MAIN_TITLE); |
129
|
|
|
$this->SetXY(self::RIGHT_PART_X, 195.2); |
130
|
|
|
$this->fpdf->MultiCell(48, 7, utf8_decode(Translation::get('paymentPart', $this->language))); |
|
|
|
|
131
|
|
|
|
132
|
|
|
// Elements |
133
|
|
|
$this->setY(197.3); |
134
|
|
|
foreach ($this->getInformationElements() as $informationElement) { |
135
|
|
|
$this->setX(self::RIGHT_PART_X_INFO); |
136
|
|
|
$this->setContentElement($informationElement, false); |
137
|
|
|
} |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
private function addCurrencyContentReceipt(): void |
141
|
|
|
{ |
142
|
|
|
$this->setY(self::CURRENCY_AMOUNT_Y); |
143
|
|
|
foreach ($this->getCurrencyElements() as $receiptCurrencyElement) { |
144
|
|
|
$this->amountLS = self::AMOUNT_LINE_SPACING_RCPT; |
145
|
|
|
$this->setX(self::LEFT_PART_X); |
146
|
|
|
$this->setContentElement($receiptCurrencyElement, true); |
147
|
|
|
$this->amountLS = 0; |
148
|
|
|
} |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
private function addAmountContentReceipt(): void |
152
|
|
|
{ |
153
|
|
|
$this->setY(self::CURRENCY_AMOUNT_Y); |
154
|
|
|
foreach ($this->getAmountElementsReceipt() as $receiptAmountElement) { |
155
|
|
|
$this->amountLS = self::AMOUNT_LINE_SPACING_RCPT; |
156
|
|
|
$this->setX(16); |
157
|
|
|
$this->setContentElement($receiptAmountElement, true); |
158
|
|
|
$this->amountLS = 0; |
159
|
|
|
} |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
private function addCurrencyContent(): void |
163
|
|
|
{ |
164
|
|
|
$this->setY(self::CURRENCY_AMOUNT_Y); |
165
|
|
|
foreach ($this->getCurrencyElements() as $currencyElement) { |
166
|
|
|
$this->amountLS = self::AMOUNT_LINE_SPACING; |
167
|
|
|
$this->setX(self::RIGHT_PART_X); |
168
|
|
|
$this->setContentElement($currencyElement, false); |
169
|
|
|
$this->amountLS = 0; |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
private function addAmountContent(): void |
174
|
|
|
{ |
175
|
|
|
$this->setY(self::CURRENCY_AMOUNT_Y); |
176
|
|
|
foreach ($this->getAmountElements() as $amountElement) { |
177
|
|
|
$this->amountLS = self::AMOUNT_LINE_SPACING; |
178
|
|
|
$this->setX(80); |
179
|
|
|
$this->setContentElement($amountElement, false); |
180
|
|
|
$this->amountLS = 0; |
181
|
|
|
} |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
private function addFurtherInformationContent(): void |
185
|
|
|
{ |
186
|
|
|
$this->SetXY(self::RIGHT_PART_X, 286); |
187
|
|
|
$this->fpdf->SetFont(self::FONT, '', self::FONT_SIZE_FURTHER_INFORMATION); |
188
|
|
|
|
189
|
|
|
foreach ($this->getFurtherInformationElements() as $furtherInformationElement) { |
190
|
|
|
$this->setX(self::RIGHT_PART_X); |
191
|
|
|
$this->setContentElement($furtherInformationElement, true); |
192
|
|
|
} |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
private function addSeparatorContentIfNotPrintable(): void |
196
|
|
|
{ |
197
|
|
|
if (!$this->isPrintable()) { |
198
|
|
|
$this->fpdf->SetLineWidth(0.1); |
199
|
|
|
$this->fpdf->Line(2 + $this->offsetX, 193 + $this->offsetY, 208 + $this->offsetX, 193 + $this->offsetY); |
200
|
|
|
$this->fpdf->Line(62 + $this->offsetX, 193 + $this->offsetY, 62 + $this->offsetX, 296 + $this->offsetY); |
201
|
|
|
$this->fpdf->SetFont(self::FONT, '', self::FONT_SIZE_FURTHER_INFORMATION); |
202
|
|
|
$this->setY(189.6); |
203
|
|
|
$this->fpdf->MultiCell(0, 0, utf8_decode(Translation::get('separate', $this->language)), self::BORDER, self::ALIGN_CENTER); |
|
|
|
|
204
|
|
|
} |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
private function setContentElement(OutputElementInterface $element, bool $isReceiptPart): void |
208
|
|
|
{ |
209
|
|
|
if ($element instanceof Title) { |
210
|
|
|
$this->setTitleElement($element, $isReceiptPart); |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
if ($element instanceof Text) { |
214
|
|
|
$this->setTextElement($element, $isReceiptPart); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
if ($element instanceof Placeholder) { |
218
|
|
|
$this->setPlaceholderElement($element); |
219
|
|
|
} |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
private function setTitleElement(Title $element, bool $isReceiptPart): void |
223
|
|
|
{ |
224
|
|
|
$this->fpdf->SetFont(self::FONT, 'B', $isReceiptPart ? self::FONT_SIZE_TITLE_RECEIPT : self::FONT_SIZE_TITLE_PAYMENT_PART); |
225
|
|
|
$this->fpdf->MultiCell(0, 2.8, utf8_decode( |
226
|
|
|
Translation::get(str_replace("text.", "", $element->getTitle()), $this->language) |
|
|
|
|
227
|
|
|
)); |
228
|
|
|
$this->fpdf->Ln($this->amountLS); |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
private function setTextElement(Text $element, bool $isReceiptPart): void |
232
|
|
|
{ |
233
|
|
|
$this->fpdf->SetFont(self::FONT, '', $isReceiptPart ? self::FONT_SIZE_RECEIPT : self::FONT_SIZE_PAYMENT_PART); |
234
|
|
|
$this->fpdf->MultiCell( |
235
|
|
|
$isReceiptPart ? 54 : 0, |
236
|
|
|
$isReceiptPart ? 3.3 : 4, |
237
|
|
|
str_replace("text.", "", utf8_decode($element->getText())), |
238
|
|
|
self::BORDER, |
239
|
|
|
self::ALIGN_LEFT |
240
|
|
|
); |
241
|
|
|
$this->fpdf->Ln($isReceiptPart ? self::LINE_SPACING_RECEIPT : self::LINE_SPACING_PAYMENT_PART); |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
private function setPlaceholderElement(Placeholder $element): void |
245
|
|
|
{ |
246
|
|
|
$type = $element->getType(); |
247
|
|
|
|
248
|
|
|
switch ($type) { |
249
|
|
|
case Placeholder::PLACEHOLDER_TYPE_AMOUNT['type']: |
250
|
|
|
$y = $this->fpdf->GetY() + 1; |
251
|
|
|
$x = $this->fpdf->GetX() - 2; |
252
|
|
|
break; |
253
|
|
|
case Placeholder::PLACEHOLDER_TYPE_AMOUNT_RECEIPT['type']: |
254
|
|
|
$y = $this->fpdf->GetY() - 2; |
255
|
|
|
$x = $this->fpdf->GetX() + 11; |
256
|
|
|
break; |
257
|
|
|
case Placeholder::PLACEHOLDER_TYPE_PAYABLE_BY['type']: |
258
|
|
|
case Placeholder::PLACEHOLDER_TYPE_PAYABLE_BY_RECEIPT['type']: |
259
|
|
|
default: |
260
|
|
|
$y = $this->fpdf->GetY() + 1; |
261
|
|
|
$x = $this->fpdf->GetX() + 1; |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
$this->fpdf->Image( |
265
|
|
|
$element->getFile(Placeholder::FILE_TYPE_PNG), |
266
|
|
|
$x, |
267
|
|
|
$y, |
268
|
|
|
$element->getWidth(), |
269
|
|
|
$element->getHeight() |
270
|
|
|
); |
271
|
|
|
} |
272
|
|
|
|
273
|
|
|
private function setX(float $x): void |
274
|
|
|
{ |
275
|
|
|
$this->fpdf->SetX($x + $this->offsetX); |
276
|
|
|
} |
277
|
|
|
|
278
|
|
|
private function setY(float $y): void |
279
|
|
|
{ |
280
|
|
|
$this->fpdf->SetY($y + $this->offsetY); |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
private function SetXY(float $x, float $y): void |
284
|
|
|
{ |
285
|
|
|
$this->fpdf->SetXY($x + $this->offsetX, $y + $this->offsetY); |
286
|
|
|
} |
287
|
|
|
} |
288
|
|
|
|