Files
anxinyan/server-api/app/support/ReportPdfGenerator.php
wushumin 9aac78b8da first
2026-05-11 15:28:27 +08:00

173 lines
6.2 KiB
PHP

<?php
namespace app\support;
class ReportPdfGenerator
{
public function generate(array $payload): string
{
$content = $this->buildContentStream($payload);
$objects = [
1 => '<< /Type /Catalog /Pages 2 0 R >>',
2 => '<< /Type /Pages /Kids [3 0 R] /Count 1 >>',
3 => '<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 5 0 R >> >> /Contents 4 0 R >>',
4 => sprintf("<< /Length %d >>\nstream\n%s\nendstream", strlen($content), $content),
5 => '<< /Type /Font /Subtype /Type0 /BaseFont /STSong-Light /Encoding /UniGB-UCS2-H /DescendantFonts [6 0 R] >>',
6 => '<< /Type /Font /Subtype /CIDFontType0 /BaseFont /STSong-Light /CIDSystemInfo << /Registry (Adobe) /Ordering (GB1) /Supplement 4 >> /DW 1000 >>',
];
return $this->renderPdf($objects);
}
private function buildContentStream(array $payload): string
{
$title = $this->normalizeText((string)($payload['report_title'] ?? '鉴定报告'));
$serviceProviderText = $this->normalizeText((string)($payload['service_provider_text'] ?? '-'));
$institutionName = $this->normalizeText((string)($payload['institution_name'] ?? '-'));
$reportNo = $this->normalizeText((string)($payload['report_no'] ?? '-'));
$publishTime = $this->normalizeText((string)($payload['publish_time'] ?? '-'));
$resultText = $this->normalizeText((string)($payload['result_text'] ?? '-'));
$resultDesc = $this->normalizeText((string)($payload['result_desc'] ?? '-'));
$productName = $this->normalizeText((string)($payload['product_name'] ?? '-'));
$categoryBrand = $this->normalizeText((string)($payload['category_brand'] ?? '-'));
$specInfo = $this->normalizeText((string)($payload['spec_info'] ?? '-'));
$appraisers = $this->normalizeText((string)($payload['appraisers'] ?? '-'));
$conditionGrade = $this->normalizeText((string)($payload['condition_grade'] ?? '-'));
$valuationRange = $this->normalizeText((string)($payload['valuation_range'] ?? '-'));
$verifyInfo = $this->normalizeText((string)($payload['verify_info'] ?? '-'));
$riskNotice = $this->normalizeText((string)($payload['risk_notice_text'] ?? '-'));
$blocks = [];
$y = 790;
$blocks[] = $this->textBlock($title, 52, $y, 20);
$y -= 32;
$blocks[] = $this->textBlock('正式报告凭证,请以编号验真结果为准。', 52, $y, 10);
$y -= 30;
foreach ([
sprintf('报告编号:%s', $reportNo),
sprintf('出具机构:%s', $institutionName),
sprintf('出具时间:%s', $publishTime),
sprintf('服务类型:%s', $serviceProviderText),
] as $line) {
$blocks[] = $this->textBlock($line, 52, $y, 12);
$y -= 22;
}
$y -= 8;
$blocks[] = $this->textBlock(sprintf('鉴定结论:%s', $resultText), 52, $y, 16);
$y -= 26;
foreach ($this->wrapText('结果说明:' . $resultDesc, 30) as $line) {
$blocks[] = $this->textBlock($line, 52, $y, 11);
$y -= 18;
}
$y -= 8;
foreach ([
sprintf('商品名称:%s', $productName),
sprintf('品类 / 品牌:%s', $categoryBrand),
sprintf('颜色 / 规格:%s', $specInfo),
sprintf('鉴定师:%s', $appraisers),
sprintf('成色评级:%s', $conditionGrade),
sprintf('估值区间:%s', $valuationRange),
sprintf('验真信息:%s', $verifyInfo),
] as $line) {
$blocks[] = $this->textBlock($line, 52, $y, 11);
$y -= 20;
}
$y -= 8;
foreach ($this->wrapText('风险说明:' . $riskNotice, 30) as $line) {
$blocks[] = $this->textBlock($line, 52, $y, 10);
$y -= 17;
}
if ($y > 48) {
$blocks[] = $this->textBlock('安心验鉴定平台', 52, 42, 9);
}
return implode("\n", array_filter($blocks));
}
private function wrapText(string $text, int $maxUnits): array
{
$normalized = $this->normalizeText($text);
if ($normalized === '') {
return ['-'];
}
$chars = preg_split('//u', $normalized, -1, PREG_SPLIT_NO_EMPTY) ?: [];
$lines = [];
$current = '';
$width = 0.0;
foreach ($chars as $char) {
$charWidth = strlen($char) === 1 && ord($char) < 128 ? 0.5 : 1.0;
if ($current !== '' && $width + $charWidth > $maxUnits) {
$lines[] = $current;
$current = '';
$width = 0.0;
}
$current .= $char;
$width += $charWidth;
}
if ($current !== '') {
$lines[] = $current;
}
return $lines ?: ['-'];
}
private function textBlock(string $text, int $x, int $y, int $fontSize): string
{
if ($text === '') {
return '';
}
return sprintf(
"BT\n/F1 %d Tf\n1 0 0 1 %d %d Tm\n<%s> Tj\nET",
$fontSize,
$x,
$y,
strtoupper(bin2hex(mb_convert_encoding($text, 'UCS-2BE', 'UTF-8')))
);
}
private function normalizeText(string $text): string
{
$text = trim(str_replace(["\r\n", "\r", "\n", "\t"], [' ', ' ', ' ', ' '], $text));
return preg_replace('/\s+/u', ' ', $text) ?: '';
}
private function renderPdf(array $objects): string
{
$pdf = "%PDF-1.4\n%\xE2\xE3\xCF\xD3\n";
$offsets = [];
foreach ($objects as $id => $body) {
$offsets[$id] = strlen($pdf);
$pdf .= sprintf("%d 0 obj\n%s\nendobj\n", $id, $body);
}
$xrefPosition = strlen($pdf);
$pdf .= sprintf("xref\n0 %d\n", count($objects) + 1);
$pdf .= "0000000000 65535 f \n";
foreach ($objects as $id => $_body) {
$pdf .= sprintf("%010d 00000 n \n", $offsets[$id]);
}
$pdf .= sprintf(
"trailer\n<< /Size %d /Root 1 0 R >>\nstartxref\n%d\n%%%%EOF",
count($objects) + 1,
$xrefPosition
);
return $pdf;
}
}