summaryrefslogtreecommitdiff
path: root/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/basic-contract/viewer/basic-contract-sign-viewer.tsx')
-rw-r--r--lib/basic-contract/viewer/basic-contract-sign-viewer.tsx202
1 files changed, 116 insertions, 86 deletions
diff --git a/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx b/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx
index d1492fdb..eeebae4e 100644
--- a/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx
+++ b/lib/basic-contract/viewer/basic-contract-sign-viewer.tsx
@@ -114,12 +114,12 @@ class AutoSignatureFieldDetector {
console.log(`πŸ“„ "${searchText}" ν…μŠ€νŠΈ 검색 쀑...`);
- // ν…μŠ€νŠΈ 검색 및 μ„œλͺ… ν•„λ“œ 생성
- const fieldName = await this.createSignatureFieldAtText(searchText);
+ // ν…μŠ€νŠΈ 검색 및 μ„œλͺ… ν•„λ“œ 생성 (λͺ¨λ“  맀치 μ°ΎκΈ°)
+ const fieldNames = await this.createSignatureFieldAtText(searchText);
- if (fieldName) {
- console.log(`βœ… ν…μŠ€νŠΈ μœ„μΉ˜μ— μ„œλͺ… ν•„λ“œ 생성 μ™„λ£Œ: ${searchText}`);
- return [fieldName];
+ if (fieldNames.length > 0) {
+ console.log(`βœ… ${fieldNames.length}개의 "${searchText}" μœ„μΉ˜μ— μ„œλͺ… ν•„λ“œ 생성 μ™„λ£Œ`);
+ return fieldNames;
} else {
// ν…μŠ€νŠΈλ₯Ό μ°Ύμ§€ λͺ»ν•œ 경우 κΈ°λ³Έ μœ„μΉ˜μ— 생성
console.log(`⚠️ "${searchText}" ν…μŠ€νŠΈλ₯Ό μ°Ύμ§€ λͺ»ν•΄ κΈ°λ³Έ μœ„μΉ˜μ— 생성`);
@@ -140,74 +140,88 @@ class AutoSignatureFieldDetector {
}
}
- private async createSignatureFieldAtText(searchText: string): Promise<string | null> {
+ private async createSignatureFieldAtText(searchText: string): Promise<string[]> {
const { Core } = this.instance;
const { documentViewer, annotationManager, Annotations } = Core;
const doc = documentViewer.getDocument();
- if (!doc) return null;
+ if (!doc) return [];
try {
const pageCount = documentViewer.getPageCount();
+ const fieldNames: string[] = [];
+ // λͺ¨λ“  νŽ˜μ΄μ§€μ—μ„œ λͺ¨λ“  맀치 μ°ΎκΈ°
for (let pageNumber = 1; pageNumber <= pageCount; pageNumber++) {
// 1) νŽ˜μ΄μ§€ ν…μŠ€νŠΈ λ‘œλ“œ
const pageText = await doc.loadPageText(pageNumber);
+ if (!pageText) continue;
- // 2) ν•΄λ‹Ή νŽ˜μ΄μ§€μ—μ„œ 검색어 μœ„μΉ˜ μ°ΎκΈ° (첫 맀치만 μ‚¬μš©)
- const startIndex = pageText.indexOf(searchText);
- if (startIndex === -1) continue;
+ // 2) ν•΄λ‹Ή νŽ˜μ΄μ§€μ—μ„œ λͺ¨λ“  검색어 μœ„μΉ˜ μ°ΎκΈ°
+ let searchIndex = 0;
+ while (true) {
+ const startIndex = pageText.indexOf(searchText, searchIndex);
+ if (startIndex === -1) break; // 더 이상 찾을 수 μ—†μœΌλ©΄ λ‹€μŒ νŽ˜μ΄μ§€λ‘œ
- const endIndex = startIndex + searchText.length;
+ 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] as any; // PDFTron의 Quad νƒ€μž…
- 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 signatureY = y + textHeight + 5;
-
- // μœ„μΉ˜ 정보 μ €μž₯
- this.location = {
- pageNumber,
- x,
- y: signatureY,
- fieldName,
- searchText
- };
-
- 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(signatureY);
- widget.setWidth(150);
- widget.setHeight(50);
+ // 3) κ²€μƒ‰μ–΄μ˜ 문자 λ‹¨μœ„ Quads μ–»κΈ°
+ const quads = await doc.getTextPosition(pageNumber, startIndex, endIndex);
+ if (!quads || quads.length === 0) {
+ searchIndex = startIndex + 1;
+ continue;
+ }
- const fm = annotationManager.getFieldManager();
- fm.addField(field);
- annotationManager.addAnnotation(widget);
- annotationManager.drawAnnotationsFromList([widget]);
+ // 첫 κΈ€μžμ˜ quad만 μ‚¬μš©ν•΄ λŒ€λž΅μ μΈ μœ„μΉ˜ μ‚°μΆœ
+ const q = quads[0] as any; // PDFTron의 Quad νƒ€μž…
+ 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);
- return fieldName;
+ // 4) μ„œλͺ… ν•„λ“œ 생성
+ const fieldName = `signature_at_text_${Date.now()}_${fieldNames.length}`;
+ const signatureY = y + textHeight + 5;
+
+ // 첫 번째 ν•„λ“œμ˜ μœ„μΉ˜ μ •λ³΄λ§Œ μ €μž₯ (μ„œλͺ…λž€μœΌλ‘œ 이동 κΈ°λŠ₯용)
+ if (fieldNames.length === 0) {
+ this.location = {
+ pageNumber,
+ x,
+ y: signatureY,
+ fieldName,
+ searchText
+ };
+ }
+
+ 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(signatureY);
+ widget.setWidth(150);
+ widget.setHeight(50);
+
+ const fm = annotationManager.getFieldManager();
+ fm.addField(field);
+ annotationManager.addAnnotation(widget);
+ annotationManager.drawAnnotationsFromList([widget]);
+
+ fieldNames.push(fieldName);
+ console.log(`βœ… νŽ˜μ΄μ§€ ${pageNumber}의 "${searchText}" μœ„μΉ˜μ— μ„œλͺ… ν•„λ“œ 생성 (${fieldNames.length}번째)`);
+
+ // λ‹€μŒ 검색을 μœ„ν•΄ 인덱슀 이동
+ searchIndex = startIndex + 1;
+ }
}
- // λͺ¨λ“  νŽ˜μ΄μ§€μ—μ„œ λͺ» 찾으면 null
- return null;
+ return fieldNames;
} catch (e) {
console.error('ν…μŠ€νŠΈ 기반 μ„œλͺ… ν•„λ“œ 생성 쀑 였λ₯˜', e);
- return null;
+ return [];
}
}
@@ -269,56 +283,72 @@ const applyBuyerSignatureAutomatically = async (instance: WebViewerInstance) =>
if (!doc) return;
try {
- console.log('πŸ” ꡬ맀자 μžλ™ μ„œλͺ…: "삼성쀑곡업_μ„œλͺ…λž€"만 검색');
+ console.log('πŸ” ꡬ맀자 μžλ™ μ„œλͺ…: "삼성쀑곡업_μ„œλͺ…λž€" 전체 검색');
const TARGET = '삼성쀑곡업_μ„œλͺ…λž€'; // βœ… μ •ν™•νžˆ 이 λ¬Έμžμ—΄λ§Œ ν—ˆμš©
const pageCount = documentViewer.getPageCount();
+ let signatureCount = 0;
+ const buyerSignature = await getBuyerSignatureFileWithFallback(); // κΈ°μ‘΄ ν”„λ‘œμ νŠΈ ν•¨μˆ˜ κ·ΈλŒ€λ‘œ μ‚¬μš©
+ if (!buyerSignature?.data?.dataUrl) {
+ console.warn('⚠️ ꡬ맀자 μ„œλͺ… 이미지가 μ—†μŠ΅λ‹ˆλ‹€.');
+ return;
+ }
+
+ // λͺ¨λ“  νŽ˜μ΄μ§€μ—μ„œ λͺ¨λ“  "삼성쀑곡업_μ„œλͺ…λž€" μ°ΎκΈ°
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;
+ // ν•œ νŽ˜μ΄μ§€μ—μ„œ μ—¬λŸ¬ 개의 맀치λ₯Ό μ°ΎκΈ° μœ„ν•΄ 반볡 검색
+ let searchIndex = 0;
+ while (true) {
+ const startIndex = pageText.indexOf(TARGET, searchIndex);
+ if (startIndex === -1) break; // 더 이상 찾을 수 μ—†μœΌλ©΄ λ‹€μŒ νŽ˜μ΄μ§€λ‘œ
- const endIndex = startIndex + TARGET.length;
+ const endIndex = startIndex + TARGET.length;
- // 문자 μΏΌλ“œ β†’ λ°”μš΄λ”© λ°•μŠ€
- const quads = await doc.getTextPosition(pageNumber, startIndex, endIndex);
- if (!quads?.length) continue;
+ // 문자 μΏΌλ“œ β†’ λ°”μš΄λ”© λ°•μŠ€
+ const quads = await doc.getTextPosition(pageNumber, startIndex, endIndex);
+ if (!quads?.length) {
+ searchIndex = startIndex + 1;
+ 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 xs: number[] = [], ys: number[] = [];
+ quads.forEach((q: any) => { 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 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);
- 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]);
- annotationManager.addAnnotation(stamp);
- annotationManager.drawAnnotationsFromList([stamp]);
+ signatureCount++;
+ console.log(`βœ… νŽ˜μ΄μ§€ ${pageNumber}의 "삼성쀑곡업_μ„œλͺ…λž€" μœ„μΉ˜μ— μžλ™ μ„œλͺ… 적용 μ™„λ£Œ (${signatureCount}번째)`);
- console.log('βœ… "삼성쀑곡업_μ„œλͺ…λž€" μœ„μΉ˜μ— μžλ™ μ„œλͺ… 적용 μ™„λ£Œ');
- return; // 첫 맀치만 처리
+ // λ‹€μŒ 검색을 μœ„ν•΄ 인덱슀 이동
+ searchIndex = startIndex + 1;
+ }
}
- console.warn('⚠️ λ¬Έμ„œμ—μ„œ "삼성쀑곡업_μ„œλͺ…λž€"을 μ°Ύμ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€.');
+ if (signatureCount > 0) {
+ console.log(`βœ… 총 ${signatureCount}개의 "삼성쀑곡업_μ„œλͺ…λž€" μœ„μΉ˜μ— μžλ™ μ„œλͺ… 적용 μ™„λ£Œ`);
+ } else {
+ console.warn('⚠️ λ¬Έμ„œμ—μ„œ "삼성쀑곡업_μ„œλͺ…λž€"을 μ°Ύμ§€ λͺ»ν–ˆμŠ΅λ‹ˆλ‹€.');
+ }
} catch (error) {
console.error('ꡬ맀자 μžλ™ μ„œλͺ… 처리 μ‹€νŒ¨:', error);
}