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(`