new CourierQuoteVersion([ 'version_code' => 'QB-TEST', 'carrier_name' => '示例快递', 'quote_type' => '普通报价', 'effective_date' => '2026-11-01', ]), 'change' => null, 'values' => [['地区', '费用'], ['江苏', '6.50']], 'formulas' => [], 'partyA' => '示例甲方', 'expiryDate' => '长期', 'faceFee' => '0.50', 'bubbleCoefficient' => '1.2', 'billingRules' => "1、\u{00A0}计费重 > 0.5kg,费用6.50元。\t\t\t\r\n2、\u{00A0}轻抛比1:10000。\t\t\t\r3、保留末行。", 'signatureDate' => '2026年11月7日', 'generatedAt' => now(), 'fontPath' => str_replace('\\', '/', resource_path('fonts/DroidSansFallback.ttf')), ])->render(); $pdf = app('dompdf.wrapper'); $frames = []; $pdf->getDomPDF()->setCallbacks([[ 'event' => 'end_frame', 'f' => static function ($frame) use (&$frames): void { if (! $frame instanceof Text) { return; } $isSummary = false; for ($node = $frame->get_node()->parentNode; $node; $node = $node->parentNode) { if ($node instanceof DOMElement && $node->getAttribute('class') === 'summary') { $isSummary = true; break; } } $frames[] = [ 'text' => $frame->get_text(), 'font' => $frame->get_style()->font_family, 'y' => $frame->get_position()['y'], 'summary' => $isSummary, ]; }, ]]); $pdf->setPaper('a4', 'landscape'); $pdf->loadHTML($html, 'UTF-8'); $this->assertStringStartsWith('%PDF-', $pdf->output()); $this->assertNotEmpty($frames); $characterMaps = []; foreach ($frames as $frame) { if (! isset($characterMaps[$frame['font']])) { $font = Font::load($frame['font'] . '.ttf'); $font->parse(); $characterMaps[$frame['font']] = $font->getUnicodeCharMap(); $font->close(); } foreach (mb_str_split($frame['text']) as $character) { if ($character === "\n" || $character === "\r") { continue; } $codepoint = mb_ord($character); $this->assertNotEmpty( $characterMaps[$frame['font']][$codepoint] ?? null, sprintf('Quote PDF would display a missing glyph for U+%04X.', $codepoint) ); } } $ruleFrames = array_values(array_filter($frames, fn ($frame) => $frame['summary'])); $renderedText = implode('', array_column($ruleFrames, 'text')); $this->assertStringContainsString('计费重 > 0.5kg,费用6.50元。', $renderedText); $this->assertStringContainsString('轻抛比1:10000。', $renderedText); $this->assertStringContainsString('保留末行。', $renderedText); $lineY = []; foreach ($ruleFrames as $frame) { foreach (['1', '2', '3'] as $number) { if (str_starts_with($frame['text'], $number)) { $lineY[$number] ??= $frame['y']; } } } $this->assertCount(3, $lineY); $this->assertGreaterThan($lineY['1'], $lineY['2']); $this->assertGreaterThan($lineY['2'], $lineY['3']); } }