diff --git a/app/Http/Controllers/ESign/EsignPublicController.php b/app/Http/Controllers/ESign/EsignPublicController.php index c9c96b1f..485684e0 100644 --- a/app/Http/Controllers/ESign/EsignPublicController.php +++ b/app/Http/Controllers/ESign/EsignPublicController.php @@ -388,9 +388,18 @@ public function downloadDocument(string $token): StreamedResponse|JsonResponse } // 서명 완료된 PDF가 있으면 우선 제공 - $filePath = $contract->signed_file_path && Storage::disk('local')->exists($contract->signed_file_path) - ? $contract->signed_file_path - : $contract->original_file_path; + if ($contract->signed_file_path && Storage::disk('local')->exists($contract->signed_file_path)) { + $filePath = $contract->signed_file_path; + } else { + // 서명 전: 텍스트/날짜/체크박스 필드가 합성된 미리보기 PDF 생성 + try { + $pdfService = new PdfSignatureService(); + $filePath = $pdfService->generatePreview($contract); + } catch (\Throwable $e) { + Log::warning('미리보기 PDF 생성 실패, 원본 제공', ['error' => $e->getMessage()]); + $filePath = $contract->original_file_path; + } + } if (! Storage::disk('local')->exists($filePath)) { return response()->json(['success' => false, 'message' => '문서 파일이 존재하지 않습니다.'], 404); diff --git a/app/Services/ESign/PdfSignatureService.php b/app/Services/ESign/PdfSignatureService.php index 840ff871..bedf35c5 100644 --- a/app/Services/ESign/PdfSignatureService.php +++ b/app/Services/ESign/PdfSignatureService.php @@ -99,6 +99,63 @@ public function mergeSignatures(EsignContract $contract): string return $signedRelPath; } + /** + * 서명/도장을 제외한 필드(텍스트, 날짜, 체크박스)만 합성한 미리보기 PDF를 생성한다. + * 서명 전 문서 확인 시 사용된다. + */ + public function generatePreview(EsignContract $contract): string + { + $disk = Storage::disk('local'); + $originalPath = $disk->path($contract->original_file_path); + + if (! file_exists($originalPath)) { + throw new \RuntimeException("원본 PDF 파일이 존재하지 않습니다: {$contract->original_file_path}"); + } + + $pdf = new Fpdi(); + $pdf->setPrintHeader(false); + $pdf->setPrintFooter(false); + + $pageCount = $pdf->setSourceFile($originalPath); + + // 서명/도장을 제외한 필드만 조회 + $signFields = EsignSignField::withoutGlobalScopes() + ->where('contract_id', $contract->id) + ->whereNotIn('field_type', ['signature', 'stamp']) + ->with('signer') + ->orderBy('page_number') + ->orderBy('sort_order') + ->get() + ->groupBy('page_number'); + + for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) { + $templateId = $pdf->importPage($pageNo); + $size = $pdf->getTemplateSize($templateId); + + $pdf->AddPage($size['orientation'], [$size['width'], $size['height']]); + $pdf->useTemplate($templateId, 0, 0, $size['width'], $size['height']); + + if ($signFields->has($pageNo)) { + foreach ($signFields[$pageNo] as $field) { + $this->overlayField($pdf, $field, $size['width'], $size['height']); + } + } + } + + // 미리보기 PDF 저장 + $previewDir = "esign/{$contract->tenant_id}/preview"; + $previewRelPath = "{$previewDir}/{$contract->id}_preview.pdf"; + $previewAbsPath = $disk->path($previewRelPath); + + if (! is_dir(dirname($previewAbsPath))) { + mkdir(dirname($previewAbsPath), 0755, true); + } + + $pdf->Output($previewAbsPath, 'F'); + + return $previewRelPath; + } + /** * 개별 필드를 PDF 위에 오버레이한다. */