워드 export 레이아웃 및 바코드/서명 렌더링 개선
This commit is contained in:
parent
da195200a8
commit
e1567d3f77
|
|
@ -30,6 +30,7 @@ import {
|
|||
Header,
|
||||
Footer,
|
||||
HeadingLevel,
|
||||
TableLayoutType,
|
||||
} from "docx";
|
||||
import { WatermarkConfig } from "../types/report";
|
||||
import bwipjs from "bwip-js";
|
||||
|
|
@ -592,8 +593,12 @@ export class ReportController {
|
|||
|
||||
// mm를 twip으로 변환
|
||||
const mmToTwip = (mm: number) => convertMillimetersToTwip(mm);
|
||||
// px를 twip으로 변환 (1px = 15twip at 96DPI)
|
||||
const pxToTwip = (px: number) => Math.round(px * 15);
|
||||
|
||||
// 프론트엔드와 동일한 MM_TO_PX 상수 (캔버스에서 mm를 px로 변환할 때 사용하는 값)
|
||||
const MM_TO_PX = 4;
|
||||
// 1mm = 56.692913386 twip (docx 라이브러리 기준)
|
||||
// px를 twip으로 변환: px -> mm -> twip
|
||||
const pxToTwip = (px: number) => Math.round((px / MM_TO_PX) * 56.692913386);
|
||||
|
||||
// 쿼리 결과 맵
|
||||
const queryResultsMap: Record<
|
||||
|
|
@ -726,6 +731,9 @@ export class ReportController {
|
|||
const base64Data =
|
||||
component.imageBase64.split(",")[1] || component.imageBase64;
|
||||
const imageBuffer = Buffer.from(base64Data, "base64");
|
||||
// 서명 이미지 크기: 라벨 옆에 인라인으로 표시될 수 있도록 적절한 크기로 조정
|
||||
const sigImageHeight = 30; // 고정 높이 (약 40px)
|
||||
const sigImageWidth = Math.round((component.width / component.height) * sigImageHeight) || 80;
|
||||
result.push(
|
||||
new ParagraphRef({
|
||||
children: [
|
||||
|
|
@ -733,8 +741,8 @@ export class ReportController {
|
|||
new ImageRunRef({
|
||||
data: imageBuffer,
|
||||
transformation: {
|
||||
width: Math.round(component.width * 0.75),
|
||||
height: Math.round(component.height * 0.75),
|
||||
width: sigImageWidth,
|
||||
height: sigImageHeight,
|
||||
},
|
||||
type: "png",
|
||||
}),
|
||||
|
|
@ -1443,7 +1451,11 @@ export class ReportController {
|
|||
try {
|
||||
const barcodeType = component.barcodeType || "CODE128";
|
||||
const barcodeColor = (component.barcodeColor || "#000000").replace("#", "");
|
||||
const barcodeBackground = (component.barcodeBackground || "#ffffff").replace("#", "");
|
||||
// transparent는 bwip-js에서 지원하지 않으므로 흰색으로 변환
|
||||
let barcodeBackground = (component.barcodeBackground || "#ffffff").replace("#", "");
|
||||
if (barcodeBackground === "transparent" || barcodeBackground === "") {
|
||||
barcodeBackground = "ffffff";
|
||||
}
|
||||
|
||||
// 바코드 값 결정 (쿼리 바인딩 또는 고정값)
|
||||
let barcodeValue = component.barcodeValue || "SAMPLE123";
|
||||
|
|
@ -1739,6 +1751,7 @@ export class ReportController {
|
|||
const rowTable = new Table({
|
||||
rows: [new TableRow({ children: cells })],
|
||||
width: { size: 100, type: WidthType.PERCENTAGE },
|
||||
layout: TableLayoutType.FIXED, // 셀 너비 고정
|
||||
borders: {
|
||||
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
bottom: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
|
|
@ -1821,6 +1834,7 @@ export class ReportController {
|
|||
const textTable = new Table({
|
||||
rows: [new TableRow({ children: [textCell] })],
|
||||
width: { size: pxToTwip(component.width), type: WidthType.DXA },
|
||||
layout: TableLayoutType.FIXED, // 셀 너비 고정
|
||||
indent: { size: indentLeft, type: WidthType.DXA },
|
||||
borders: {
|
||||
top: { style: BorderStyle.NONE, size: 0, color: "FFFFFF" },
|
||||
|
|
@ -1970,6 +1984,10 @@ export class ReportController {
|
|||
component.imageBase64.split(",")[1] || component.imageBase64;
|
||||
const imageBuffer = Buffer.from(base64Data, "base64");
|
||||
|
||||
// 서명 이미지 크기: 라벨 옆에 인라인으로 표시될 수 있도록 적절한 크기로 조정
|
||||
const sigImageHeight = 30; // 고정 높이
|
||||
const sigImageWidth = Math.round((component.width / component.height) * sigImageHeight) || 80;
|
||||
|
||||
const paragraph = new Paragraph({
|
||||
spacing: { before: spacingBefore, after: 0 },
|
||||
indent: { left: indentLeft },
|
||||
|
|
@ -1978,8 +1996,8 @@ export class ReportController {
|
|||
new ImageRun({
|
||||
data: imageBuffer,
|
||||
transformation: {
|
||||
width: Math.round(component.width * 0.75),
|
||||
height: Math.round(component.height * 0.75),
|
||||
width: sigImageWidth,
|
||||
height: sigImageHeight,
|
||||
},
|
||||
type: "png",
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -1052,7 +1052,7 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||
description: "WORD 파일을 생성하고 있습니다...",
|
||||
});
|
||||
|
||||
// 이미지를 Base64로 변환하여 컴포넌트 데이터에 포함
|
||||
// 이미지 및 바코드를 Base64로 변환하여 컴포넌트 데이터에 포함
|
||||
const pagesWithBase64 = await Promise.all(
|
||||
layoutConfig.pages.map(async (page) => {
|
||||
const componentsWithBase64 = await Promise.all(
|
||||
|
|
@ -1066,12 +1066,21 @@ export function ReportPreviewModal({ isOpen, onClose }: ReportPreviewModalProps)
|
|||
return component;
|
||||
}
|
||||
}
|
||||
// 바코드/QR코드 컴포넌트는 이미지로 변환
|
||||
if (component.type === "barcode") {
|
||||
try {
|
||||
const barcodeImage = await generateBarcodeImage(component);
|
||||
return { ...component, barcodeImageBase64: barcodeImage };
|
||||
} catch {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
return component;
|
||||
}),
|
||||
);
|
||||
})
|
||||
);
|
||||
return { ...page, components: componentsWithBase64 };
|
||||
}),
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
// 쿼리 결과 수집
|
||||
const queryResults: Record<string, { fields: string[]; rows: Record<string, unknown>[] }> = {};
|
||||
|
|
|
|||
Loading…
Reference in New Issue