98 lines
3.9 KiB
PHP
98 lines
3.9 KiB
PHP
<?php
|
|
|
|
namespace App\Support;
|
|
|
|
use App\Models\Business\CourierAgreementSignature;
|
|
use DOMDocument;
|
|
use DOMXPath;
|
|
use Dompdf\FrameReflower\Text;
|
|
use Illuminate\Support\Carbon;
|
|
use RuntimeException;
|
|
|
|
final class CourierAgreementPdf
|
|
{
|
|
public function render(CourierAgreementSignature $signature): string
|
|
{
|
|
$payload = $signature->payload ?? [];
|
|
$signatureDate = (string) ($payload['signature_date'] ?? $payload['entity_proof_date'] ?? $payload['shared_proof_date'] ?? '');
|
|
try {
|
|
$signatureDate = $signatureDate !== '' ? Carbon::parse($signatureDate)->format('Y年n月j日') : '待填写';
|
|
} catch (\Throwable) {
|
|
$signatureDate = '待填写';
|
|
}
|
|
$supplierContract = app(SupplierCooperationTemplates::class)->isSupplierContract($signature);
|
|
$html = view($supplierContract ? 'business.pdf.supplier-cooperation-contract' : 'business.pdf.courier-agreement', [
|
|
'signature' => $signature,
|
|
'type' => $signature->document_type,
|
|
'title' => $this->title($signature),
|
|
'payload' => $payload,
|
|
'signatureDate' => $signatureDate,
|
|
'fontPath' => str_replace('\\', '/', resource_path('fonts/DroidSansFallback.ttf')),
|
|
])->render();
|
|
|
|
$pdf = app('dompdf.wrapper');
|
|
$pdf->setPaper('a4', 'portrait');
|
|
$pdf->loadHTML($html, 'UTF-8');
|
|
$this->applyLatinFont($pdf->getDomPDF()->getDom());
|
|
// Dompdf otherwise treats uninterrupted Chinese text as one word,
|
|
// moving a whole clause to the next line after its Latin number.
|
|
$wordBreakPattern = Text::$_wordbreak_pattern;
|
|
Text::$_wordbreak_pattern = '/([^\S\xA0\x{202F}\x{2007}\n]+|\R|\-+|\xAD+|(?<=[\p{Han}\x{3000}-\x{303F}\x{FF00}-\x{FFEF}])(?![,。、;:?!)】》]))/u';
|
|
try {
|
|
$bytes = $pdf->output();
|
|
} finally {
|
|
Text::$_wordbreak_pattern = $wordBreakPattern;
|
|
}
|
|
|
|
if (! is_string($bytes) || ! str_starts_with($bytes, '%PDF-')) {
|
|
throw new RuntimeException('协议 PDF 生成失败。');
|
|
}
|
|
|
|
return $bytes;
|
|
}
|
|
|
|
private function applyLatinFont(DOMDocument $document): void
|
|
{
|
|
// DroidSansFallback has no ASCII or typographic quote glyphs. Static
|
|
// clauses need the Latin font too, or glyph widths and layout diverge.
|
|
$nodes = (new DOMXPath($document))->query(
|
|
'//body//text()[not(ancestor::style or ancestor::script or ancestor::*[contains(concat(" ", normalize-space(@class), " "), " latin ")])]'
|
|
);
|
|
|
|
foreach ($nodes as $node) {
|
|
if (preg_match('/[\x21-\x7E\x{2018}-\x{201F}]/u', $node->nodeValue) !== 1) {
|
|
continue;
|
|
}
|
|
|
|
$parts = preg_split('/([\x20-\x7E\x{2018}-\x{201F}]+)/u', $node->nodeValue, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
|
|
$fragment = $document->createDocumentFragment();
|
|
|
|
foreach ($parts as $part) {
|
|
$text = $document->createTextNode($part);
|
|
if (preg_match('/^[\x20-\x7E\x{2018}-\x{201F}]+$/u', $part) === 1) {
|
|
$span = $document->createElement('span');
|
|
$span->setAttribute('class', 'latin');
|
|
$span->appendChild($text);
|
|
$fragment->appendChild($span);
|
|
} else {
|
|
$fragment->appendChild($text);
|
|
}
|
|
}
|
|
|
|
$node->parentNode->replaceChild($fragment, $node);
|
|
}
|
|
}
|
|
|
|
private function title(CourierAgreementSignature $signature): string
|
|
{
|
|
return match ($signature->document_type) {
|
|
'account-proof' => '收款帐户资料证明',
|
|
'account-change' => '收款帐户资料变更证明',
|
|
'entity-change' => '公司主体变更证明',
|
|
'shared-contract' => '证明',
|
|
'cooperation-contract' => '服务协议',
|
|
default => '授权委托书',
|
|
};
|
|
}
|
|
}
|