diff --git a/src/pages/Watermark/Watermark.jsx b/src/pages/Watermark/Watermark.jsx index 22574e5..f7a2b21 100644 --- a/src/pages/Watermark/Watermark.jsx +++ b/src/pages/Watermark/Watermark.jsx @@ -24,7 +24,11 @@ export function Watermark() { fontSize: 60, rotation: 45, offsetX: 0, - offsetY: 0 + offsetY: 0, + tile: false, + tileSpacingX: 200, + tileSpacingY: 150, + tileStagger: false, }); const [watermarkMode, setWatermarkMode] = useState("text"); @@ -239,20 +243,22 @@ export function Watermark() {

Style & Position

-
- - -
+ {!options.tile && ( +
+ + +
+ )}
{watermarkMode === "text" && ( @@ -301,6 +307,41 @@ export function Watermark() { />
+
+
+ Tile Watermark + +
+ + {options.tile && ( +
+
+
+ + updateOption("tileSpacingX", parseInt(e.target.value))} + className="w-full h-1.5 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-white" /> +
+
+ + updateOption("tileSpacingY", parseInt(e.target.value))} + className="w-full h-1.5 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-white" /> +
+
+
+ Stagger Rows + +
+
+ )} +
)} diff --git a/src/services/pdf.service.js b/src/services/pdf.service.js index 2114f30..941a828 100644 --- a/src/services/pdf.service.js +++ b/src/services/pdf.service.js @@ -97,6 +97,10 @@ export const addWatermark = async (file, watermarkText = "CONFIDENTIAL", options offsetX = 0, offsetY = 0, imageScale = 0.4, + tile = false, + tileSpacingX = 200, + tileSpacingY = 150, + tileStagger = false, } = options; const arrayBuffer = await file.arrayBuffer(); @@ -128,80 +132,120 @@ export const addWatermark = async (file, watermarkText = "CONFIDENTIAL", options const scale = targetW / imgW; const targetH = imgH * scale; - const margin = 40; - let x, y; + if (tile) { + const cols = Math.ceil(pageW / tileSpacingX) + 1; + const rows = Math.ceil(pageH / tileSpacingY) + 1; + + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + const staggerOffset = tileStagger && row % 2 !== 0 ? tileSpacingX / 2 : 0; + page.drawImage(embeddedImage, { + x: col * tileSpacingX + staggerOffset + Number(offsetX), + y: row * tileSpacingY + Number(offsetY), + width: targetW, + height: targetH, + opacity: parseFloat(opacity), + }); + } + } + } else { + const margin = 40; + let x, y; + + switch (position) { + case "top-left": + x = margin; y = pageH - margin - targetH; break; + case "top-right": + x = pageW - margin - targetW; y = pageH - margin - targetH; break; + case "bottom-left": + x = margin; y = margin; break; + case "bottom-right": + x = pageW - margin - targetW; y = margin; break; + case "center": + default: + x = (pageW - targetW) / 2; + y = (pageH - targetH) / 2; + break; + } + + page.drawImage(embeddedImage, { + x: x + Number(offsetX), + y: y + Number(offsetY), + width: targetW, + height: targetH, + opacity: parseFloat(opacity), + }) + } + + }) + + } else { + + pages.forEach((page) => { + const { width, height } = page.getSize(); + if (tile) { + const cols = Math.ceil(width / tileSpacingX) + 1; + const rows = Math.ceil(height / tileSpacingY) + 1; + + for (let row = 0; row < rows; row++) { + for (let col = 0; col < cols; col++) { + const staggerOffset = tileStagger && row % 2 !== 0 ? tileSpacingX / 2 : 0; + page.drawText(watermarkText, { + x: col * tileSpacingX + staggerOffset + Number(offsetX), + y: height - (row * tileSpacingY) + Number(offsetY), + size: fontSize, + font: helveticaFont, + color: rgb(0.5, 0.5, 0.5), + opacity: parseFloat(opacity), + rotate: degrees(rotation), + }); + } + } + } else { + const textWidth = helveticaFont.widthOfTextAtSize(watermarkText, fontSize); + const textHeight = helveticaFont.heightAtSize(fontSize); + + // Convert degrees to radians for math + const rad = (rotation * Math.PI) / 180; + + let x, y; + const margin = 40; + switch (position) { case "top-left": - x = margin; y = pageH - margin - targetH; break; + x = margin; + y = height - margin - textHeight; + break; case "top-right": - x = pageW - margin - targetW; y = pageH - margin - targetH; break; + x = width - margin - textWidth; + y = height - margin - textHeight; + break; case "bottom-left": - x = margin; y = margin; break; + x = margin; + y = margin; + break; case "bottom-right": - x = pageW - margin - targetW; y = margin; break; + x = width - margin - textWidth; + y = margin; + break; case "center": default: - x = (pageW - targetW) / 2; - y = (pageH - targetH) / 2; + x = (width / 2) - (Math.cos(rad) * textWidth / 2) + (Math.sin(rad) * textHeight / 2); + y = (height / 2) - (Math.sin(rad) * textWidth / 2) - (Math.cos(rad) * textHeight / 2); break; - } - - page.drawImage(embeddedImage, { - x: x + Number(offsetX), - y: y + Number(offsetY), - width: targetW, - height: targetH, - opacity: parseFloat(opacity), - }) - }) - - } else { - - pages.forEach((page) => { - const { width, height } = page.getSize(); - const textWidth = helveticaFont.widthOfTextAtSize(watermarkText, fontSize); - const textHeight = helveticaFont.heightAtSize(fontSize); - - // Convert degrees to radians for math - const rad = (rotation * Math.PI) / 180; - - let x, y; - const margin = 40; - - switch (position) { - case "top-left": - x = margin; - y = height - margin - textHeight; - break; - case "top-right": - x = width - margin - textWidth; - y = height - margin - textHeight; - break; - case "bottom-left": - x = margin; - y = margin; - break; - case "bottom-right": - x = width - margin - textWidth; - y = margin; - break; - case "center": - default: - x = (width / 2) - (Math.cos(rad) * textWidth / 2) + (Math.sin(rad) * textHeight / 2); - y = (height / 2) - (Math.sin(rad) * textWidth / 2) - (Math.cos(rad) * textHeight / 2); - break; + } + page.drawText(watermarkText, { + x: x + Number(offsetX), + y: y + Number(offsetY), + size: fontSize, + font: helveticaFont, + color: rgb(0.5, 0.5, 0.5), + opacity: parseFloat(opacity), + rotate: degrees(rotation), + }); } - page.drawText(watermarkText, { - x: x + Number(offsetX), - y: y + Number(offsetY), - size: fontSize, - font: helveticaFont, - color: rgb(0.5, 0.5, 0.5), - opacity: parseFloat(opacity), - rotate: degrees(rotation), - }); }); }