summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-10-30 09:52:25 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-10-30 09:52:25 +0000
commit0f9976751134107dfa53f68dd361f0d66836d37e (patch)
treef55c7bb1c966c99ae7d54c25d74fd56d53dfdad2
parent9462d93d01ad51d58b22921a1a0a509cf9768299 (diff)
(임수민) 기본계약문서 자동 서명란 수정
-rw-r--r--lib/basic-contract/viewer/basic-contract-sign-viewer.tsx227
1 files changed, 103 insertions, 124 deletions
diff --git a/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx b/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx
index b6024b29..246c5200 100644
--- a/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx
+++ b/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx
@@ -127,89 +127,63 @@ class AutoSignatureFieldDetector {
private async createSignatureFieldAtText(searchText: string): Promise<string | null> {
const { Core } = this.instance;
- const { documentViewer, annotationManager } = Core;
- const document = documentViewer.getDocument();
-
- if (!document) return null;
-
+ const { documentViewer, annotationManager, Annotations } = Core;
+ const doc = documentViewer.getDocument();
+ if (!doc) return null;
+
try {
- // 모든 페이지에서 텍스트 검색
- const searchMode = Core.Search.Mode.PAGE_STOP | Core.Search.Mode.HIGHLIGHT;
- const searchOptions = {
- fullSearch: true,
- onResult: null,
- };
-
- // 텍스트 검색 시작
- const textSearchIterator = await document.getTextSearchIterator();
- textSearchIterator.begin(searchText, searchMode);
-
- let searchResult = await textSearchIterator.next();
-
- // 검색 결과가 있는 경우
- if (searchResult && searchResult.resultCode === Core.Search.ResultCode.FOUND) {
- const pageNumber = searchResult.pageNum;
- const quads = searchResult.quads;
-
- if (quads && quads.length > 0) {
- // 첫 번째 검색 결과의 위치 가져오기
- const quad = quads[0];
-
- // 쿼드의 좌표를 기반으로 서명 필드 위치 계산
- const x = Math.min(quad.x1, quad.x2, quad.x3, quad.x4);
- const y = Math.min(quad.y1, quad.y2, quad.y3, quad.y4);
- const textWidth = Math.abs(quad.x2 - quad.x1);
- const textHeight = Math.abs(quad.y3 - quad.y1);
-
- // 서명 필드 생성
- const fieldName = `signature_at_text_${Date.now()}`;
- const flags = new Core.Annotations.WidgetFlags();
- flags.set('Required', true);
-
- const field = new Core.Annotations.Forms.Field(fieldName, {
- type: 'Sig',
- flags
- });
-
- const widget = new Core.Annotations.SignatureWidgetAnnotation(field, {
- Width: 150,
- Height: 50
- });
-
- widget.setPageNumber(pageNumber);
-
- // 텍스트 바로 아래 또는 오른쪽에 서명 필드 배치
- // 옵션 1: 텍스트 바로 아래
- widget.setX(x);
- widget.setY(y + textHeight + 5); // 텍스트 아래 5픽셀 간격
-
- // 옵션 2: 텍스트 오른쪽 (필요시 아래 주석 해제)
- // widget.setX(x + textWidth + 10); // 텍스트 오른쪽 10픽셀 간격
- // widget.setY(y);
-
- widget.setWidth(150);
- widget.setHeight(50);
-
- // 필드 매니저에 추가
- const fm = annotationManager.getFieldManager();
- fm.addField(field);
- annotationManager.addAnnotation(widget);
- annotationManager.drawAnnotationsFromList([widget]);
-
- console.log(`📌 서명 필드를 페이지 ${pageNumber}의 "${searchText}" 위치에 생성`);
-
- return fieldName;
- }
+ const pageCount = documentViewer.getPageCount();
+
+ for (let pageNumber = 1; pageNumber <= pageCount; pageNumber++) {
+ // 1) 페이지 텍스트 로드
+ const pageText = await doc.loadPageText(pageNumber);
+
+ // 2) 해당 페이지에서 검색어 위치 찾기 (첫 매치만 사용)
+ const startIndex = pageText.indexOf(searchText);
+ if (startIndex === -1) continue;
+
+ const endIndex = startIndex + searchText.length;
+
+ // 3) 검색어의 문자 단위 Quads 얻기
+ const quads = await doc.getTextPosition(pageNumber, startIndex, endIndex);
+ if (!quads || quads.length === 0) continue;
+
+ // 첫 글자의 quad만 사용해 대략적인 위치 산출
+ const q = quads[0];
+ const x = Math.min(q.x1, q.x2, q.x3, q.x4);
+ const y = Math.min(q.y1, q.y2, q.y3, q.y4);
+ const textHeight = Math.abs(q.y3 - q.y1);
+
+ // 4) 서명 필드 생성
+ const fieldName = `signature_at_text_${Date.now()}`;
+ const flags = new Annotations.WidgetFlags();
+ flags.set('Required', true);
+
+ const field = new Core.Annotations.Forms.Field(fieldName, { type: 'Sig', flags });
+ const widget = new Annotations.SignatureWidgetAnnotation(field, { Width: 150, Height: 50 });
+
+ widget.setPageNumber(pageNumber);
+ // 텍스트 바로 아래에 배치 (필요하면 오른쪽 배치로 바꿀 수 있음)
+ widget.setX(x);
+ widget.setY(y + textHeight + 5);
+ widget.setWidth(150);
+ widget.setHeight(50);
+
+ const fm = annotationManager.getFieldManager();
+ fm.addField(field);
+ annotationManager.addAnnotation(widget);
+ annotationManager.drawAnnotationsFromList([widget]);
+
+ return fieldName;
}
-
- console.log(`⚠️ "${searchText}" 텍스트를 찾을 수 없음`);
+
+ // 모든 페이지에서 못 찾으면 null
return null;
-
- } catch (error) {
- console.error(`📛 텍스트 검색 중 오류: ${error}`);
+ } catch (e) {
+ console.error('텍스트 기반 서명 필드 생성 중 오류', e);
return null;
}
- }
+ }
private async createSimpleSignatureField(): Promise<string> {
const { Core } = this.instance;
@@ -259,56 +233,61 @@ class AutoSignatureFieldDetector {
const applyBuyerSignatureAutomatically = async (instance: WebViewerInstance) => {
const { Core } = instance;
- const { documentViewer, annotationManager } = Core;
- const document = documentViewer.getDocument();
-
- if (!document) return;
+ const { documentViewer, annotationManager, Annotations } = Core;
+ const doc = documentViewer.getDocument();
+ if (!doc) return;
try {
- console.log('🔍 구매자 서명란 자동 서명 시작...');
-
- // "삼성중공업_서명란" 텍스트 검색
- const searchText = '삼성중공업_서명란';
- const textSearchIterator = await document.getTextSearchIterator();
- textSearchIterator.begin(searchText, Core.Search.Mode.PAGE_STOP | Core.Search.Mode.HIGHLIGHT);
-
- let searchResult = await textSearchIterator.next();
-
- if (searchResult && searchResult.resultCode === Core.Search.ResultCode.FOUND) {
- const pageNumber = searchResult.pageNum;
- const quads = searchResult.quads;
-
- if (quads && quads.length > 0) {
- const quad = quads[0];
- const x = Math.min(quad.x1, quad.x2, quad.x3, quad.x4);
- const y = Math.min(quad.y1, quad.y2, quad.y3, quad.y4);
- const textHeight = Math.abs(quad.y3 - quad.y1);
-
- // 구매자 서명 이미지 가져오기
- const buyerSignature = await getBuyerSignatureFileWithFallback();
-
- if (buyerSignature) {
- // 스탬프 어노테이션 생성
- const stamp = new Core.Annotations.StampAnnotation();
- stamp.PageNumber = pageNumber;
- stamp.X = x;
- stamp.Y = y + textHeight + 5; // 텍스트 아래 5픽셀
- stamp.Width = 150;
- stamp.Height = 50;
-
- await stamp.setImageData(buyerSignature.data.dataUrl);
-
- // 어노테이션 추가
- annotationManager.addAnnotation(stamp);
- annotationManager.drawAnnotationsFromList([stamp]);
-
- console.log('✅ 구매자 서명 자동 적용 완료');
- toast.info('삼성중공업 서명이 자동으로 적용되었습니다.', {
- duration: 3000
- });
- }
+ console.log('🔍 구매자 자동 서명: "삼성중공업_서명란"만 검색');
+
+ const TARGET = '삼성중공업_서명란'; // ✅ 정확히 이 문자열만 허용
+ const pageCount = documentViewer.getPageCount();
+
+ for (let pageNumber = 1; pageNumber <= pageCount; pageNumber++) {
+ const pageText = await doc.loadPageText(pageNumber);
+ if (!pageText) continue;
+
+ const startIndex = pageText.indexOf(TARGET);
+ if (startIndex === -1) continue;
+
+ const endIndex = startIndex + TARGET.length;
+
+ // 문자 쿼드 → 바운딩 박스
+ const quads = await doc.getTextPosition(pageNumber, startIndex, endIndex);
+ if (!quads?.length) continue;
+
+ const xs: number[] = [], ys: number[] = [];
+ quads.forEach(q => { xs.push(q.x1, q.x2, q.x3, q.x4); ys.push(q.y1, q.y2, q.y3, q.y4); });
+ const minX = Math.min(...xs), maxY = Math.max(...ys);
+
+ // 텍스트 바로 아래에 스탬프(서명 이미지) 배치
+ const widgetX = minX;
+ const widgetY = maxY + 5;
+ const widgetW = 150;
+ const widgetH = 50;
+
+ const buyerSignature = await getBuyerSignatureFileWithFallback(); // 기존 프로젝트 함수 그대로 사용
+ if (!buyerSignature?.data?.dataUrl) {
+ console.warn('⚠️ 구매자 서명 이미지가 없습니다.');
+ return;
}
+
+ const stamp = new Annotations.StampAnnotation();
+ stamp.PageNumber = pageNumber;
+ stamp.X = widgetX;
+ stamp.Y = widgetY;
+ stamp.Width = widgetW;
+ stamp.Height = widgetH;
+ await stamp.setImageData(buyerSignature.data.dataUrl);
+
+ annotationManager.addAnnotation(stamp);
+ annotationManager.drawAnnotationsFromList([stamp]);
+
+ console.log('✅ "삼성중공업_서명란" 위치에 자동 서명 적용 완료');
+ return; // 첫 매치만 처리
}
+
+ console.warn('⚠️ 문서에서 "삼성중공업_서명란"을 찾지 못했습니다.');
} catch (error) {
console.error('구매자 자동 서명 처리 실패:', error);
}