• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LibreSign / libresign / 20040103294

08 Dec 2025 07:23PM UTC coverage: 44.146%. First build
20040103294

Pull #6021

github

web-flow
Merge 6275726ed into eb7183cb5
Pull Request #6021: feat: docmdp implementation

66 of 113 new or added lines in 10 files covered. (58.41%)

5678 of 12862 relevant lines covered (44.15%)

5.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.84
/lib/Handler/SignEngine/JSignPdfHandler.php
1
<?php
2

3
declare(strict_types=1);
4
/**
5
 * SPDX-FileCopyrightText: 2020-2024 LibreCode coop and contributors
6
 * SPDX-License-Identifier: AGPL-3.0-or-later
7
 */
8

9
namespace OCA\Libresign\Handler\SignEngine;
10

11
use Imagick;
12
use ImagickPixel;
13
use OCA\Libresign\AppInfo\Application;
14
use OCA\Libresign\Exception\LibresignException;
15
use OCA\Libresign\Handler\CertificateEngine\CertificateEngineFactory;
16
use OCA\Libresign\Helper\JavaHelper;
17
use OCA\Libresign\Service\DocMdpConfigService;
18
use OCA\Libresign\Service\Install\InstallService;
19
use OCA\Libresign\Service\SignatureBackgroundService;
20
use OCA\Libresign\Service\SignatureTextService;
21
use OCA\Libresign\Service\SignerElementsService;
22
use OCA\Libresign\Vendor\Jeidison\JSignPDF\JSignPDF;
23
use OCA\Libresign\Vendor\Jeidison\JSignPDF\Sign\JSignParam;
24
use OCP\Files\File;
25
use OCP\IAppConfig;
26
use OCP\ITempManager;
27
use Psr\Log\LoggerInterface;
28

29
class JSignPdfHandler extends Pkcs12Handler {
30
        /** @var JSignPDF */
31
        private $jSignPdf;
32
        /** @var JSignParam */
33
        private $jSignParam;
34
        private array $parsedSignatureText = [];
35

36
        public function __construct(
37
                private IAppConfig $appConfig,
38
                private LoggerInterface $logger,
39
                private SignatureTextService $signatureTextService,
40
                private ITempManager $tempManager,
41
                private SignatureBackgroundService $signatureBackgroundService,
42
                protected CertificateEngineFactory $certificateEngineFactory,
43
                protected JavaHelper $javaHelper,
44
                private DocMdpConfigService $docMdpConfigService,
45
        ) {
46
        }
49✔
47

48
        public function setJSignPdf(JSignPDF $jSignPdf): void {
49
                $this->jSignPdf = $jSignPdf;
11✔
50
        }
51

52
        public function getJSignPdf(): JSignPDF {
53
                if (!$this->jSignPdf) {
11✔
54
                        // @codeCoverageIgnoreStart
55
                        $this->setJSignPdf(new JSignPDF());
56
                        // @codeCoverageIgnoreEnd
57
                }
58
                return $this->jSignPdf;
11✔
59
        }
60

61
        /**
62
         * @psalm-suppress MixedReturnStatement
63
         */
64
        public function getJSignParam(): JSignParam {
65
                if (!$this->jSignParam) {
19✔
66
                        $javaPath = $this->javaHelper->getJavaPath();
19✔
67
                        $tempPath = $this->appConfig->getValueString(Application::APP_ID, 'jsignpdf_temp_path', sys_get_temp_dir() . DIRECTORY_SEPARATOR);
19✔
68
                        if (!is_writable($tempPath)) {
19✔
69
                                throw new \Exception('The path ' . $tempPath . ' is not writtable. Fix this or change the LibreSign app setting jsignpdf_temp_path to a writtable path');
2✔
70
                        }
71
                        $jSignPdfJarPath = $this->appConfig->getValueString(Application::APP_ID, 'jsignpdf_jar_path', '/opt/jsignpdf-' . InstallService::JSIGNPDF_VERSION . '/JSignPdf.jar');
17✔
72
                        if (!file_exists($jSignPdfJarPath)) {
17✔
73
                                throw new \Exception('Invalid JSignPdf jar path. Run occ libresign:install --jsignpdf');
1✔
74
                        }
75
                        $this->jSignParam = (new JSignParam())
16✔
76
                                ->setTempPath($tempPath)
16✔
77
                                ->setIsUseJavaInstalled(empty($javaPath))
16✔
78
                                ->setJavaDownloadUrl('')
16✔
79
                                ->setJSignPdfDownloadUrl('')
16✔
80
                                ->setjSignPdfJarPath($jSignPdfJarPath);
16✔
81
                        if (!empty($javaPath)) {
16✔
82
                                if (!file_exists($javaPath)) {
4✔
83
                                        throw new \Exception('Invalid Java binary. Run occ libresign:install --java');
2✔
84
                                }
85
                                $this->jSignParam->setJavaPath(
2✔
86
                                        $this->getEnvironments()
2✔
87
                                        . $javaPath
2✔
88
                                        . ' -Duser.home=' . escapeshellarg($this->getHome()) . ' '
2✔
89
                                );
2✔
90
                        }
91

92
                        $certificationLevel = $this->getCertificationLevel();
14✔
93
                        if ($certificationLevel !== null) {
14✔
NEW
94
                                $this->jSignParam->setJSignParameters(' -cl ' . $certificationLevel);
×
95
                        }
96
                }
97
                return $this->jSignParam;
14✔
98
        }
99

100
        private function getEnvironments(): string {
101
                return 'JSIGNPDF_HOME=' . escapeshellarg($this->getHome()) . ' ';
2✔
102
        }
103

104
        /**
105
         * It's a workaround to create the folder structure that JSignPdf needs. Without
106
         * this, the JSignPdf will return the follow message to all commands:
107
         * > FINE Config file conf/conf.properties doesn't exists.
108
         * > FINE Default property file /root/.JSignPdf doesn't exists.
109
         */
110
        private function getHome(): string {
111
                $jSignPdfHome = $this->appConfig->getValueString(Application::APP_ID, 'jsignpdf_home', '');
2✔
112
                if ($jSignPdfHome) {
2✔
113
                        return $jSignPdfHome;
2✔
114
                }
115
                $jsignpdfTempFolder = $this->tempManager->getTemporaryFolder('jsignpdf');
×
116
                if (!$jsignpdfTempFolder) {
×
117
                        throw new \Exception('Temporary file not accessible');
×
118
                }
119
                mkdir(
×
120
                        directory: $jsignpdfTempFolder . '/conf',
×
121
                        recursive: true
×
122
                );
×
123
                $configFile = fopen($jsignpdfTempFolder . '/conf/conf.properties', 'w');
×
124
                fclose($configFile);
×
125
                $propertyFile = fopen($jsignpdfTempFolder . '/.JSignPdf', 'w');
×
126
                fclose($propertyFile);
×
127
                return $jsignpdfTempFolder;
×
128
        }
129

130
        private function getHashAlgorithm(): string {
131
                $hashAlgorithm = $this->appConfig->getValueString(Application::APP_ID, 'signature_hash_algorithm', 'SHA256');
25✔
132
                /**
133
                 * Need to respect the follow code:
134
                 * https://github.com/intoolswetrust/jsignpdf/blob/JSignPdf_2_2_2/jsignpdf/src/main/java/net/sf/jsignpdf/types/HashAlgorithm.java#L46-L47
135
                 */
136
                $content = $this->getInputFile()->getContent();
25✔
137
                preg_match('/^%PDF-(?<version>\d+(\.\d+)?)/', $content, $match);
25✔
138
                if (isset($match['version'])) {
25✔
139
                        $version = (float)$match['version'];
24✔
140
                        if ($version < 1.6) {
24✔
141
                                return 'SHA1';
4✔
142
                        }
143
                        if ($version < 1.7) {
20✔
144
                                return 'SHA256';
15✔
145
                        }
146
                        if ($version >= 1.7 && $hashAlgorithm === 'SHA1') {
5✔
147
                                return 'SHA256';
×
148
                        }
149
                }
150

151
                if (in_array($hashAlgorithm, ['SHA1', 'SHA256', 'SHA384', 'SHA512', 'RIPEMD160'])) {
6✔
152
                        return $hashAlgorithm;
2✔
153
                }
154
                return 'SHA256';
4✔
155
        }
156

157
        private function getCertificationLevel(): ?string {
158
                if (!$this->docMdpConfigService->isEnabled()) {
14✔
159
                        return null;
14✔
160
                }
161

NEW
162
                return $this->docMdpConfigService->getLevel()->name;
×
163
        }
164

165
        #[\Override]
166
        public function sign(): File {
167
                $this->beforeSign();
×
168

169
                $signedContent = $this->getSignedContent();
×
170
                $this->getInputFile()->putContent($signedContent);
×
171
                return $this->getInputFile();
×
172
        }
173

174
        #[\Override]
175
        public function getSignedContent(): string {
176
                $param = $this->getJSignParam()
11✔
177
                        ->setCertificate($this->getCertificate())
11✔
178
                        ->setPdf($this->getInputFile()->getContent())
11✔
179
                        ->setPassword($this->getPassword());
11✔
180

181
                $signed = $this->signUsingVisibleElements();
11✔
182
                if ($signed) {
11✔
183
                        return $signed;
10✔
184
                }
185
                $jSignPdf = $this->getJSignPdf();
1✔
186
                $jSignPdf->setParam($param);
1✔
187
                return $this->signWrapper($jSignPdf);
1✔
188
        }
189

190
        private function signUsingVisibleElements(): string {
191
                $visibleElements = $this->getVisibleElements();
11✔
192
                if ($visibleElements) {
11✔
193
                        $jSignPdf = $this->getJSignPdf();
10✔
194

195
                        $renderMode = $this->signatureTextService->getRenderMode();
10✔
196

197
                        $params = [
10✔
198
                                '--l2-text' => $this->getSignatureText(),
10✔
199
                                '-V' => null,
10✔
200
                        ];
10✔
201

202
                        $fontSize = $this->parseSignatureText()['templateFontSize'];
10✔
203
                        if ($fontSize === 10.0 || !$fontSize || $params['--l2-text'] === '""') {
10✔
204
                                $fontSize = 0;
9✔
205
                        }
206

207
                        $backgroundType = $this->signatureBackgroundService->getSignatureBackgroundType();
10✔
208
                        if ($backgroundType !== 'deleted') {
10✔
209
                                $backgroundPath = $this->signatureBackgroundService->getImagePath();
7✔
210
                        } else {
211
                                $backgroundPath = '';
3✔
212
                        }
213

214
                        $param = $this->getJSignParam();
10✔
215
                        $originalParam = clone $param;
10✔
216

217
                        foreach ($visibleElements as $element) {
10✔
218
                                $params['-pg'] = $element->getFileElement()->getPage();
10✔
219
                                if ($params['-pg'] <= 1) {
10✔
220
                                        unset($params['-pg']);
1✔
221
                                }
222
                                $params['-llx'] = $element->getFileElement()->getLlx();
10✔
223
                                $params['-lly'] = $element->getFileElement()->getLly();
10✔
224
                                $params['-urx'] = $element->getFileElement()->getUrx();
10✔
225
                                $params['-ury'] = $element->getFileElement()->getUry();
10✔
226

227
                                $scaleFactor = $this->getScaleFactor($params['-urx'] - $params['-llx']);
10✔
228
                                if ($fontSize) {
10✔
229
                                        $params['--font-size'] = $fontSize * $scaleFactor;
1✔
230
                                }
231

232
                                $signatureImagePath = $element->getTempFile();
10✔
233
                                if ($backgroundType === 'deleted') {
10✔
234
                                        if ($renderMode === SignerElementsService::RENDER_MODE_SIGNAME_AND_DESCRIPTION) {
3✔
235
                                                $params['--render-mode'] = SignerElementsService::RENDER_MODE_GRAPHIC_AND_DESCRIPTION;
2✔
236
                                                $params['--img-path'] = $this->createTextImage(
2✔
237
                                                        width: ($params['-urx'] - $params['-llx']),
2✔
238
                                                        height: ($params['-ury'] - $params['-lly']),
2✔
239
                                                        fontSize: $this->signatureTextService->getSignatureFontSize() * $scaleFactor,
2✔
240
                                                        scaleFactor: $scaleFactor < 5 ? 5 : $scaleFactor,
2✔
241
                                                );
2✔
242
                                        } elseif ($signatureImagePath) {
1✔
243
                                                $params['--bg-path'] = $signatureImagePath;
1✔
244
                                        }
245
                                } elseif ($params['--l2-text'] === '""') {
7✔
246
                                        if ($backgroundPath) {
3✔
247
                                                $params['--bg-path'] = $this->mergeBackgroundWithSignature(
3✔
248
                                                        $backgroundPath,
3✔
249
                                                        $signatureImagePath,
3✔
250
                                                        $scaleFactor < 5 ? 5 : $scaleFactor
3✔
251
                                                );
3✔
252
                                        } else {
253
                                                $params['--bg-path'] = $signatureImagePath;
×
254
                                        }
255
                                } else {
256
                                        if ($renderMode === SignerElementsService::RENDER_MODE_GRAPHIC_AND_DESCRIPTION) {
4✔
257
                                                $params['--render-mode'] = SignerElementsService::RENDER_MODE_GRAPHIC_AND_DESCRIPTION;
1✔
258
                                                $params['--bg-path'] = $backgroundPath;
1✔
259
                                                $params['--img-path'] = $signatureImagePath;
1✔
260
                                        } elseif ($renderMode === SignerElementsService::RENDER_MODE_SIGNAME_AND_DESCRIPTION) {
3✔
261
                                                $params['--render-mode'] = SignerElementsService::RENDER_MODE_GRAPHIC_AND_DESCRIPTION;
1✔
262
                                                $params['--bg-path'] = $backgroundPath;
1✔
263
                                                $params['--img-path'] = $this->createTextImage(
1✔
264
                                                        width: (int)(($params['-urx'] - $params['-llx']) / 2),
1✔
265
                                                        height: $params['-ury'] - $params['-lly'],
1✔
266
                                                        fontSize: $this->signatureTextService->getSignatureFontSize() * $scaleFactor,
1✔
267
                                                        scaleFactor: $scaleFactor < 5 ? 5 : $scaleFactor,
1✔
268
                                                );
1✔
269

270
                                        } else {
271
                                                // --render-mode DESCRIPTION_ONLY, this is the default
272
                                                // render-mode, because this, is unecessary to set here
273
                                                $params['--bg-path'] = $backgroundPath;
2✔
274
                                        }
275
                                }
276

277
                                $param->setJSignParameters(
10✔
278
                                        $originalParam->getJSignParameters()
10✔
279
                                        . $this->listParamsToString($params)
10✔
280
                                );
10✔
281
                                $jSignPdf->setParam($param);
10✔
282
                                $signed = $this->signWrapper($jSignPdf);
10✔
283
                                $param->setPdf($signed);
10✔
284
                        }
285
                        return $signed;
10✔
286
                }
287
                return '';
1✔
288
        }
289

290
        private function getScaleFactor(float $width): float {
291
                $systemWidth = $this->signatureTextService->getFullSignatureWidth();
10✔
292
                if (!$systemWidth) {
10✔
293
                        return 1;
×
294
                }
295
                $widthScale = $width / $systemWidth;
10✔
296
                return $widthScale;
10✔
297
        }
298

299

300
        #[\Override]
301
        public function readCertificate(): array {
302
                $result = $this->certificateEngineFactory
3✔
303
                        ->getEngine()
3✔
304
                        ->readCertificate(
3✔
305
                                $this->getCertificate(),
3✔
306
                                $this->getPassword()
3✔
307
                        );
3✔
308

309
                if (!is_array($result)) {
3✔
310
                        throw new \RuntimeException('Failed to read certificate data');
×
311
                }
312

313
                return $result;
3✔
314
        }
315

316
        private function createTextImage(int $width, int $height, float $fontSize, float $scaleFactor): string {
317
                $params = $this->getSignatureParams();
3✔
318
                if (!empty($params['SignerCommonName'])) {
3✔
319
                        $commonName = $params['SignerCommonName'];
×
320
                } else {
321
                        $certificateData = $this->readCertificate();
3✔
322
                        $commonName = $certificateData['subject']['CN'] ?? throw new \RuntimeException('Certificate must have a Common Name (CN) in subject field');
3✔
323
                }
324
                $content = $this->signatureTextService->signerNameImage(
3✔
325
                        width: $width,
3✔
326
                        height: $height,
3✔
327
                        text: $commonName,
3✔
328
                        fontSize: $fontSize,
3✔
329
                        scale: $scaleFactor,
3✔
330
                );
3✔
331

332
                $tmpPath = $this->tempManager->getTemporaryFile('_text_image.png');
3✔
333
                if (!$tmpPath) {
3✔
334
                        throw new \Exception('Temporary file not accessible');
×
335
                }
336
                file_put_contents($tmpPath, $content);
3✔
337
                return $tmpPath;
3✔
338
        }
339

340
        private function mergeBackgroundWithSignature(string $backgroundPath, string $signaturePath, float $scaleFactor): string {
341
                if (!extension_loaded('imagick')) {
3✔
342
                        throw new \Exception('Extension imagick is not loaded.');
×
343
                }
344
                $baseWidth = $this->signatureTextService->getFullSignatureWidth();
3✔
345
                $baseHeight = $this->signatureTextService->getFullSignatureHeight();
3✔
346

347
                $canvasWidth = round($baseWidth * $scaleFactor);
3✔
348
                $canvasHeight = round($baseHeight * $scaleFactor);
3✔
349

350
                $background = new Imagick($backgroundPath);
3✔
351
                $signature = new Imagick($signaturePath);
3✔
352

353
                $background->setImageFormat('png');
3✔
354
                $signature->setImageFormat('png');
3✔
355

356
                $background->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE);
3✔
357
                $signature->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE);
3✔
358

359
                $background->resizeImage(
3✔
360
                        (int)round($background->getImageWidth() * $scaleFactor),
3✔
361
                        (int)round($background->getImageHeight() * $scaleFactor),
3✔
362
                        Imagick::FILTER_LANCZOS,
3✔
363
                        1
3✔
364
                );
3✔
365

366
                $signature->resizeImage(
3✔
367
                        (int)round($signature->getImageWidth() * $scaleFactor),
3✔
368
                        (int)round($signature->getImageHeight() * $scaleFactor),
3✔
369
                        Imagick::FILTER_LANCZOS,
3✔
370
                        1
3✔
371
                );
3✔
372

373
                $canvas = new Imagick();
3✔
374
                $canvas->newImage((int)$canvasWidth, (int)$canvasHeight, new ImagickPixel('transparent'));
3✔
375
                $canvas->setImageFormat('png32');
3✔
376
                $canvas->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE);
3✔
377

378
                $bgX = (int)(($canvasWidth - $background->getImageWidth()) / 2);
3✔
379
                $bgY = (int)(($canvasHeight - $background->getImageHeight()) / 2);
3✔
380
                $canvas->compositeImage($background, Imagick::COMPOSITE_OVER, $bgX, $bgY);
3✔
381

382
                $sigX = (int)(($canvasWidth - $signature->getImageWidth()) / 2);
3✔
383
                $sigY = (int)(($canvasHeight - $signature->getImageHeight()) / 2);
3✔
384
                $canvas->compositeImage($signature, Imagick::COMPOSITE_OVER, $sigX, $sigY);
3✔
385

386
                $tmpPath = $this->tempManager->getTemporaryFile('_merged.png');
3✔
387
                if (!$tmpPath) {
3✔
388
                        throw new \Exception('Temporary file not accessible');
×
389
                }
390
                $canvas->writeImage($tmpPath);
3✔
391

392
                $canvas->clear();
3✔
393
                $background->clear();
3✔
394
                $signature->clear();
3✔
395

396
                return $tmpPath;
3✔
397
        }
398

399
        private function parseSignatureText(): array {
400
                if (!$this->parsedSignatureText) {
15✔
401
                        $params = $this->getSignatureParams();
15✔
402
                        $params['ServerSignatureDate'] = '${timestamp}';
15✔
403
                        $this->parsedSignatureText = $this->signatureTextService->parse(context: $params);
15✔
404
                }
405
                return $this->parsedSignatureText;
15✔
406
        }
407

408
        public function getSignatureText(): string {
409
                $renderMode = $this->signatureTextService->getRenderMode();
20✔
410
                if ($renderMode !== 'GRAPHIC_ONLY') {
20✔
411
                        $data = $this->parseSignatureText();
15✔
412
                        $signatureText = '"' . str_replace(
15✔
413
                                ['"', '$'],
15✔
414
                                ['\"', '\$'],
15✔
415
                                $data['parsed']
15✔
416
                        ) . '"';
15✔
417
                } else {
418
                        $signatureText = '""';
5✔
419
                }
420

421
                return $signatureText;
20✔
422
        }
423

424
        private function listParamsToString(array $params): string {
425
                $paramString = '';
11✔
426
                foreach ($params as $flag => $value) {
11✔
427
                        $paramString .= ' ' . $flag;
11✔
428
                        if ($value !== null && $value !== '') {
11✔
429
                                $paramString .= ' ' . $value;
11✔
430
                        }
431
                }
432
                return $paramString;
11✔
433
        }
434

435
        private function getTsaParameters(): array {
436
                $tsaUrl = $this->appConfig->getValueString(Application::APP_ID, 'tsa_url', '');
11✔
437
                if (empty($tsaUrl)) {
11✔
438
                        return [];
11✔
439
                }
440

441
                $params = [
×
442
                        '--tsa-server-url' => $tsaUrl,
×
443
                        '--tsa-policy-oid' => $this->appConfig->getValueString(Application::APP_ID, 'tsa_policy_oid', ''),
×
444
                ];
×
445

446
                if (!$params['--tsa-policy-oid']) {
×
447
                        unset($params['--tsa-policy-oid']);
×
448
                }
449

450
                $tsaAuthType = $this->appConfig->getValueString(Application::APP_ID, 'tsa_auth_type', 'none');
×
451
                if ($tsaAuthType === 'basic') {
×
452
                        $tsaUsername = $this->appConfig->getValueString(Application::APP_ID, 'tsa_username', '');
×
453
                        $tsaPassword = $this->appConfig->getValueString(Application::APP_ID, 'tsa_password', '');
×
454

455
                        if (!empty($tsaUsername) && !empty($tsaPassword)) {
×
456
                                $params['--tsa-authentication'] = 'PASSWORD';
×
457
                                $params['--tsa-user'] = $tsaUsername;
×
458
                                $params['--tsa-password'] = $tsaPassword;
×
459
                        }
460
                }
461

462
                return $params;
×
463
        }
464

465
        private function signWrapper(JSignPDF $jSignPDF): string {
466
                try {
467
                        $params = [
11✔
468
                                '--hash-algorithm' => $this->getHashAlgorithm(),
11✔
469
                        ];
11✔
470

471
                        $params = array_merge($params, $this->getTsaParameters());
11✔
472
                        $param = $this->getJSignParam();
11✔
473
                        $param
11✔
474
                                ->setJSignParameters(
11✔
475
                                        $this->jSignParam->getJSignParameters()
11✔
476
                                        . $this->listParamsToString($params)
11✔
477
                                );
11✔
478
                        $jSignPDF->setParam($param);
11✔
479
                        return $jSignPDF->sign();
11✔
480
                } catch (\Throwable $th) {
×
481
                        $errorMessage = $th->getMessage();
×
482
                        $rows = str_getcsv($errorMessage);
×
483

484
                        $tsaError = array_filter($rows, fn ($r) => str_contains((string)$r, 'Invalid TSA'));
×
485
                        if (!empty($tsaError)) {
×
486
                                $tsaErrorMsg = current($tsaError);
×
487
                                if (preg_match("/Invalid TSA '([^']+)'/", $tsaErrorMsg, $matches)) {
×
488
                                        $friendlyMessage = 'Timestamp Authority (TSA) service is unavailable or misconfigured.' . "\n"
×
489
                                                . 'Please check the TSA configuration.';
×
490
                                } else {
491
                                        $friendlyMessage = 'Timestamp Authority (TSA) service error.' . "\n"
×
492
                                                . 'Please check the TSA configuration.';
×
493
                                }
494
                                throw new LibresignException($friendlyMessage);
×
495
                        }
496

497
                        // Check for hash algorithm errors
498
                        $hashAlgorithm = array_filter($rows, fn ($r) => str_contains((string)$r, 'The chosen hash algorithm'));
×
499
                        if (!empty($hashAlgorithm)) {
×
500
                                $hashAlgorithm = current($hashAlgorithm);
×
501
                                $hashAlgorithm = trim((string)$hashAlgorithm, 'INFO ');
×
502
                                $hashAlgorithm = str_replace('\"', '"', $hashAlgorithm);
×
503
                                $hashAlgorithm = preg_replace('/\.( )/', ".\n", $hashAlgorithm);
×
504
                                throw new LibresignException($hashAlgorithm);
×
505
                        }
506

507
                        $this->logger->error('Error at JSignPdf side. LibreSign can not do nothing. Follow the error message: ' . $errorMessage);
×
508
                        throw new \Exception($errorMessage);
×
509
                }
510
        }
511
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc