function removeElementsTemporarily(selectors, callback) { const elements = document.querySelectorAll(selectors); const originalStyles = []; elements.forEach(el => { originalStyles.push(el.style.display); el.style.display = "none"; }); return callback().finally(() => { elements.forEach((el, index) => { el.style.display = originalStyles[index]; }); }); } function addMarginToCanvas(canvas, marginSize = 50) { const newCanvas = document.createElement("canvas"); const ctx = newCanvas.getContext("2d"); newCanvas.width = canvas.width + marginSize * 2; newCanvas.height = canvas.height + marginSize * 2; ctx.fillStyle = "white"; ctx.fillRect(0, 0, newCanvas.width, newCanvas.height); ctx.drawImage(canvas, marginSize, marginSize); return newCanvas; } function captureEditor() { return new Promise((resolve, reject) => { const editor = document.getElementById("editor"); if (!editor) { console.error("Error: `#editor` 요소를 찾을 수 없음"); return reject("Missing `#editor` element"); } const resizableElements = document.querySelectorAll(".resizable"); // `.selected` 클래스 제거 resizableElements.forEach(resizable => resizable.classList.remove("selected")); return html2canvas(editor, { useCORS: true, scale: window.devicePixelRatio * 3, allowTaint: true, backgroundColor: null }).then(editorCanvas => { // `.resizable`이 하나도 없으면 `#editor`만 저장 if (resizableElements.length === 0) { resolve(editorCanvas); return; } // `.resizable`이 있을 경우 개별적으로 필터 적용 const newCanvas = document.createElement("canvas"); const ctx = newCanvas.getContext("2d"); newCanvas.width = editorCanvas.width; newCanvas.height = editorCanvas.height; ctx.fillStyle = "white"; ctx.fillRect(0, 0, newCanvas.width, newCanvas.height); ctx.drawImage(editorCanvas, 0, 0); const imagePromises = []; resizableElements.forEach(resizable => { const img = resizable.querySelector("img"); if (!img) return; const imgObj = new Image(); imgObj.crossOrigin = "anonymous"; imgObj.src = img.src; const imgStyle = getComputedStyle(img); const resizableRect = resizable.getBoundingClientRect(); const editorRect = editor.getBoundingClientRect(); const imgLeft = resizableRect.left - editorRect.left; const imgTop = resizableRect.top - editorRect.top; const resizableWidth = resizable.offsetWidth; const resizableHeight = resizable.offsetHeight; const borderRadius = parseFloat(imgStyle.borderRadius) * 3; const borderWidth = parseFloat(imgStyle.borderWidth) * 3; const borderColor = imgStyle.borderColor; const boxShadow = imgStyle.boxShadow.match(/(-?\d+px)/g); const shadowOffsetX = boxShadow ? parseFloat(boxShadow[0]) * 3 : 0; const shadowOffsetY = boxShadow ? parseFloat(boxShadow[1]) * 3 : 0; const shadowBlur = boxShadow ? parseFloat(boxShadow[2]) * 3 : 0; const shadowColor = imgStyle.boxShadow.match(/rgba?\([\d,.\s]+\)/); const promise = new Promise((imgResolve) => { imgObj.onload = function () { ctx.save(); if (shadowColor) { ctx.shadowColor = shadowColor[0]; ctx.shadowBlur = shadowBlur; ctx.shadowOffsetX = shadowOffsetX; ctx.shadowOffsetY = shadowOffsetY; } ctx.filter = imgStyle.filter; ctx.globalAlpha = imgStyle.opacity; const imgRatio = imgObj.width / imgObj.height; const canvasRatio = resizableWidth / resizableHeight; let sx, sy, sWidth, sHeight; if (imgRatio > canvasRatio) { sHeight = imgObj.height; sWidth = sHeight * canvasRatio; sx = (imgObj.width - sWidth) / 2; sy = 0; } else { sWidth = imgObj.width; sHeight = sWidth / canvasRatio; sx = 0; sy = (imgObj.height - sHeight) / 2; } ctx.beginPath(); ctx.roundRect(imgLeft * 3, imgTop * 3, resizableWidth * 3, resizableHeight * 3, borderRadius); ctx.closePath(); ctx.clip(); ctx.drawImage(imgObj, sx, sy, sWidth, sHeight, imgLeft * 3, imgTop * 3, resizableWidth * 3, resizableHeight * 3); if (borderWidth > 0) { ctx.strokeStyle = borderColor; ctx.lineWidth = borderWidth; ctx.stroke(); } ctx.restore(); imgResolve(); }; imgObj.onerror = function () { console.error("Error: 이미지 로드 실패"); imgResolve(); }; }); imagePromises.push(promise); }); Promise.all(imagePromises).then(() => { resolve(newCanvas); }); }).catch(error => { console.error("html2canvas 캡처 오류:", error); reject(error); }); }); } /* 고도화 후 오픈예정 document.getElementById("save-pdf-btn").addEventListener("click", function () { removeElementsTemporarily(".resize-handle, .delete-preview, .rb-video-container", () => { return captureEditor().then(canvas => { const finalCanvas = addMarginToCanvas(canvas, 50); const imgData = finalCanvas.toDataURL("image/png"); const { jsPDF } = window.jspdf; const pdf = new jsPDF("p", "mm", "a4"); const pdfWidth = pdf.internal.pageSize.getWidth(); // A4 너비 (mm) const pdfHeight = pdf.internal.pageSize.getHeight(); // A4 높이 (mm) const canvasWidth = finalCanvas.width; const canvasHeight = finalCanvas.height; // PDF에 맞는 비율 계산 const scaleFactor = pdfWidth / canvasWidth; const imgHeight = canvasHeight * scaleFactor; let positionY = 0; while (positionY < imgHeight) { pdf.addImage(imgData, "PNG", 0, -positionY, pdfWidth, imgHeight, "FAST"); positionY += pdfHeight; // 한 페이지 크기만큼 이동 if (positionY < imgHeight) { pdf.addPage(); } } pdf.save("editor.pdf"); }); }); }); document.getElementById("save-image-btn").addEventListener("click", function () { removeElementsTemporarily(".resize-handle, .delete-preview, .rb-video-container", () => { return captureEditor().then(canvas => { const finalCanvas = addMarginToCanvas(canvas, 50); const link = document.createElement("a"); link.download = "editor.png"; link.href = finalCanvas.toDataURL("image/png"); link.click(); }); }); }); document.getElementById("save-print-btn").addEventListener("click", function () { removeElementsTemporarily(".resize-handle, .delete-preview, .rb-video-container", () => { return captureEditor().then(canvas => { const finalCanvas = addMarginToCanvas(canvas, 50); const imageData = finalCanvas.toDataURL("image/png"); const printWindow = window.open("", "_blank"); printWindow.document.write(` 인쇄 `); printWindow.document.close(); }); }); }); */ window.addEventListener("message", function (event) { if (event.data.type === "rbeditor-submit") { const editorContent = document.getElementById("editor").innerHTML; const iframe = window.frameElement; const editorId = iframe.getAttribute("data-editor-id"); window.parent.postMessage({ type: "rbeditor-content", content: editorContent, editorId: editorId }, "*"); } if (event.data.type === "rbeditor-set-content") { const editorId = event.data.editorId; // 부모 창에서 전달받은 editorId let content = event.data.content; if (content) { // 1. 엔티티를 디코딩하여 원래 HTML로 변환 const tempDiv = document.createElement("div"); tempDiv.innerHTML = content; // 자동 디코딩 수행 // 2. 에디터에 변환된 HTML 삽입 const editor = document.getElementById("editor"); if (editor) { editor.innerHTML = tempDiv.textContent || tempDiv.innerText; } else { //console.error(`Editor with ID ${editorId} not found`); } } } /* 자동저장 전송 */ if (event.data.type === "rbeditor-insert-content") { const content = event.data.content; // HTML 엔티티 디코딩 (태그 및 스타일 유지) const tempDiv = document.createElement("div"); tempDiv.innerHTML = content; const decodedContent = tempDiv.innerHTML; // 강제로 `#regular-mode-btn` 클릭 효과 실행 function triggerRegularMode() { var regularModeBtn = document.getElementById("regular-mode-btn"); if (regularModeBtn) { //console.log("#regular-mode-btn 클릭 트리거 실행"); regularModeBtn.click(); // 클릭 효과 발생 } else { //console.error("#regular-mode-btn을 찾을 수 없습니다."); } } // RB 에디터에 내용 삽입 function insertContent() { var editor = document.getElementById("editor"); if (!editor) { console.error("에디터를 찾을 수 없습니다."); return; } editor.innerHTML = decodedContent; // 기존 내용 교체 editor.focus(); // 포커스 유지 //console.log("에디터 내용이 정상적으로 업데이트됨"); } // `#regular-mode-btn` 클릭 효과 실행 후 내용 삽입 triggerRegularMode(); setTimeout(insertContent, 100); // 클릭 효과 후 약간의 지연 적용 } if (event.data.type === "rbeditor-get-content") { //console.log("autosave 요청 수신"); // 로그 추가 const editorContent = document.getElementById("editor").innerHTML; // 부모 창으로 에디터 내용 전송 event.source.postMessage({ type: "rbeditor-content", content: editorContent }, "*"); } }); /* 고도화 후 오픈예정 document.getElementById("btn_rb_autosave").addEventListener("click", function () { //console.log("아이프레임에서 부모 창으로 autosave-trigger 요청 전송"); // 부모 창으로 자동 저장 실행 요청 window.parent.postMessage({ type: "autosave-trigger" }, "*"); }); document.getElementById("btn_tb_autosave_popup").addEventListener("click", function () { //console.log("아이프레임에서 부모 창으로 autosave 요청 전송"); // 부모 창으로 메시지 전송 window.parent.postMessage({ type: "trigger-autosave-popup" }, "*"); }); */