Passed
Push — master ( 5801ae...f9bb79 )
by Manuel
01:12 queued 11s
created

FpdfOutput::setQrCodeImageFormat()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 2
eloc 4
c 2
b 1
f 0
nc 2
nop 1
dl 0
loc 9
rs 10
1
<?php
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 constants
21
    private const FPDF_BORDER = 0;
22
    private const FPDF_ALIGN_LEFT = 'L';
23
    private const FPDF_ALIGN_RIGHT = 'R';
24
    private const FPDF_ALIGN_CENTER = 'C';
25
    private const FPDF_FONT = 'Helvetica';
26
27
    // Location constants
28
    private const FPDF_CURRENCY_AMOUNT_Y = 259.3;
29
    private const FPDF_AMOUNT_LINE_SPACING = 1.2;
30
    private const FPDF_AMOUNT_LINE_SPACING_RCPT = 0.6;
31
    private const FPDF_LEFT_PART_X = 4;
32
    private const FPDF_RIGHT_PART_X = 66;
33
    private const FPDF_RIGHT_PAR_X_INFO = 117;
34
    private const FPDF_TITLE_Y = 195.2;
35
36
    // Line spacing constants
37
    private const FPDF_9PT = 3.4;
38
    private const FPDF_11PT = 4.8;
39
    
40
    /** @var Fpdf */
41
    private $fpdf;
42
43
    /** @var float */
44
    private $amountLS = 0;
45
46
    /* @var int $offsetX */
47
    private $offsetX;
48
49
    /* @var int $offsetY */
50
    private $offsetY;
51
52
    public function __construct(
53
        QrBill $qrBill,
54
        string $language,
55
        Fpdf $fpdf,
56
        int $offsetX = 0,
57
        int $offsetY = 0
58
    ) {
59
        parent::__construct($qrBill, $language);
60
        $this->fpdf = $fpdf;
61
        $this->offsetX = $offsetX;
62
        $this->offsetY = $offsetY;
63
        $this->setQrCodeImageFormat(QrCode::FILE_FORMAT_PNG);
64
    }
65
66
    public function getPaymentPart()
67
    {
68
        $this->fpdf->SetAutoPageBreak(false);
69
70
        $this->addSeparatorContentIfNotPrintable();
71
72
        $this->addReceiptPart();
73
        $this->addPaymentPart();
74
    }
75
76
    /**
77
     * @param string $fileExtension
78
     * @return $this
79
     * @throws InvalidFpdfImageFormat
80
     */
81
    public function setQrCodeImageFormat(string $fileExtension): AbstractOutput
82
    {
83
        if ($fileExtension === 'svg') {
84
            throw new InvalidFpdfImageFormat('SVG images are not allowed by FPDF.');
85
        }
86
87
        $this->qrCodeImageFormat = $fileExtension;
88
89
        return $this;
90
    }
91
92
    private function addSeparatorContentIfNotPrintable()
93
    {
94
        if (!$this->isPrintable()) {
95
            $this->fpdf->SetLineWidth(0.1);
96
            $this->fpdf->Line(2, 193, 208, 193);
97
            $this->fpdf->Line(62, 193, 62, 296);
98
            $this->fpdf->SetFont(self::FPDF_FONT, '', 7);
99
            $this->SetY(189.6);
100
            $this->fpdf->MultiCell(0, 0, utf8_decode(Translation::get('separate', $this->language)), self::FPDF_BORDER, self::FPDF_ALIGN_CENTER);
101
        }
102
    }
103
104
    private function addReceiptPart()
105
    {
106
        // Title
107
        $this->fpdf->SetFont(self::FPDF_FONT, 'B', 11);
108
        $this->SetXY(self::FPDF_LEFT_PART_X, self::FPDF_TITLE_Y);
109
        $this->fpdf->MultiCell(0, 7, utf8_decode(Translation::get('receipt', $this->language)));
110
111
        // Elements
112
        $this->SetY(204);
113
        foreach ($this->getInformationElementsOfReceipt() as $receiptInformationElement) {
114
            $this->SetX(self::FPDF_LEFT_PART_X);
115
            $this->setContentElement($receiptInformationElement, true);
116
        }
117
118
        // Amount
119
        $this->SetY(self::FPDF_CURRENCY_AMOUNT_Y);
120
        foreach ($this->getCurrencyElements() as $receiptCurrencyElement) {
121
            $this->amountLS = self::FPDF_AMOUNT_LINE_SPACING_RCPT;
122
            $this->SetX(self::FPDF_LEFT_PART_X);
123
            $this->setContentElement($receiptCurrencyElement, true);
124
            $this->amountLS = 0;
125
        }
126
        $this->SetY(self::FPDF_CURRENCY_AMOUNT_Y);
127
        foreach ($this->getAmountElementsReceipt() as $receiptAmountElement) {
128
            $this->amountLS = self::FPDF_AMOUNT_LINE_SPACING_RCPT;
129
            $this->SetX(16);
130
            $this->setContentElement($receiptAmountElement, true);
131
            $this->amountLS = 0;
132
        }
133
134
        // Acceptance section
135
        $this->fpdf->SetFont(self::FPDF_FONT, 'B', 6);
136
        $this->SetXY(self::FPDF_LEFT_PART_X, 274.3);
137
        $this->fpdf->Cell(54, 0, utf8_decode(Translation::get('acceptancePoint', $this->language)), self::FPDF_BORDER, '', self::FPDF_ALIGN_RIGHT);
138
    }
139
140
    private function addPaymentPart()
141
    {
142
        // Title
143
        $this->fpdf->SetFont(self::FPDF_FONT, 'B', 11);
144
        $this->SetXY(self::FPDF_RIGHT_PART_X, 195.2);
145
        $this->fpdf->MultiCell(48, 7, utf8_decode(Translation::get('paymentPart', $this->language)));
146
147
        // QRCode
148
        $image = $this->getPngImage();
149
        $this->fpdf->Image($image[0], 67, 209.5, 46, 46, $image[1]);
150
151
        // Information Section
152
        $this->SetY(197.3);
153
        foreach ($this->getInformationElements() as $informationElement) {
154
            $this->SetX(self::FPDF_RIGHT_PAR_X_INFO);
155
            $this->setContentElement($informationElement, false);
156
        }
157
158
        // Amount section
159
        $this->SetY(self::FPDF_CURRENCY_AMOUNT_Y);
160
        foreach ($this->getCurrencyElements() as $currencyElement) {
161
            $this->amountLS = self::FPDF_AMOUNT_LINE_SPACING;
162
            $this->SetX(self::FPDF_RIGHT_PART_X);
163
            $this->setContentElement($currencyElement, false);
164
            $this->amountLS = 0;
165
        }
166
        $this->SetY(self::FPDF_CURRENCY_AMOUNT_Y);
167
        foreach ($this->getAmountElements() as $amountElement) {
168
            $this->amountLS = self::FPDF_AMOUNT_LINE_SPACING;
169
            $this->SetX(80);
170
            $this->setContentElement($amountElement, false);
171
            $this->amountLS = 0;
172
        }
173
174
        // Further Information Section
175
        $this->SetY(286);
176
        $this->fpdf->SetFont(self::FPDF_FONT, '', 7);
177
        foreach ($this->getFurtherInformationElements() as $furtherInformationElement) {
178
            $this->SetX(self::FPDF_RIGHT_PART_X);
179
            $this->setContentElement($furtherInformationElement, true);
180
        }
181
    }
182
183
    private function setContentElement(OutputElementInterface $element, bool $isReceiptPart): void
184
    {
185
        if ($element instanceof Title) {
186
            $this->setTitleElement($element, $isReceiptPart);
187
        }
188
189
        if ($element instanceof Text) {
190
            $this->setTextElement($element, $isReceiptPart);
191
        }
192
193
        if ($element instanceof Placeholder) {
194
            $this->setPlaceholderElement($element);
195
        }
196
    }
197
198
    private function setTitleElement(Title $element, bool $isReceiptPart): void
199
    {
200
        $this->fpdf->SetFont(self::FPDF_FONT, 'B', $isReceiptPart ? 6 : 8);
201
        $this->fpdf->MultiCell(0, 2.8, utf8_decode(
202
            Translation::get(str_replace("text.", "", $element->getTitle()), $this->language)
203
        ));
204
        $this->fpdf->Ln($this->amountLS);
205
    }
206
207
    private function setTextElement(Text $element, bool $isReceiptPart): void
208
    {
209
        $this->fpdf->SetFont(self::FPDF_FONT, '', $isReceiptPart ? 8 : 10);
210
        $this->fpdf->MultiCell(
211
            $isReceiptPart ? 54 : 0,
212
            $isReceiptPart ? 3.3 : 4,
213
            str_replace("text.", "", utf8_decode($element->getText())),
214
            self::FPDF_BORDER,
215
            self::FPDF_ALIGN_LEFT
216
        );
217
        $this->fpdf->Ln($isReceiptPart ? self::FPDF_9PT : self::FPDF_11PT);
218
    }
219
220
    private function setPlaceholderElement(Placeholder $element): void
221
    {
222
        $type = $element->getType();
223
224
        switch ($type) {
225
            case Placeholder::PLACEHOLDER_TYPE_AMOUNT['type']:
226
                $y = $this->fpdf->GetY() + 1;
227
                $x = $this->fpdf->GetX() - 2;
228
                break;
229
            case Placeholder::PLACEHOLDER_TYPE_AMOUNT_RECEIPT['type']:
230
                $y = $this->fpdf->GetY() - 2;
231
                $x = $this->fpdf->GetX() + 11;
232
                break;
233
            case Placeholder::PLACEHOLDER_TYPE_PAYABLE_BY['type']:
234
            case Placeholder::PLACEHOLDER_TYPE_PAYABLE_BY_RECEIPT['type']:
235
            default:
236
                $y = $this->fpdf->GetY() + 1;
237
                $x = $this->fpdf->GetX() + 1;
238
        }
239
240
        $this->fpdf->Image(
241
            $element->getFile(Placeholder::FILE_TYPE_PNG),
242
            $x,
243
            $y,
244
            $element->getWidth(),
245
            $element->getHeight()
246
        );
247
    }
248
249
    private function setX(float $x) : void
250
    {
251
        $this->fpdf->SetX($x + $this->offsetX);
252
    }
253
254
    private function setY(float $y) : void
255
    {
256
        $this->fpdf->SetY($y + $this->offsetY);
257
    }
258
259
    private function SetXY(float $x, float $y) : void
260
    {
261
        $this->fpdf->SetXY($x + $this->offsetX, $y + $this->offsetY);
262
    }
263
264
    private function getPngImage()
265
    {
266
        $qrCode = $this->getQrCode();
267
        $format = QrCode::FILE_FORMAT_PNG;
268
        $qrCode->setWriterByExtension($format);
269
        $image64 = explode(',', $qrCode->writeDataUri(), 2);
270
        $image = 'data://text/plain;base64,' . $image64[1];
271
        $type = 'png';
272
273
        return [$image, $type];
274
    }
275
}
276