feat: implement PDF page rotation feature

This commit is contained in:
RaktimaNXG
2024-08-16 14:10:29 +05:30
parent 3ef2eeef93
commit 1b4e5247ad
8 changed files with 242 additions and 40 deletions
+50 -3
View File
@@ -1,5 +1,6 @@
import React from "react";
import { useTranslation } from "react-i18next";
import { rotatePdfPage } from "../../constant/Utils";
function PdfZoom(props) {
const { t } = useTranslation();
@@ -17,9 +18,40 @@ function PdfZoom(props) {
props.setZoomPercent(props.zoomPercent - 10);
}
};
const handleReset = () => {
props.setScale(1);
props.setZoomPercent(0);
const handleRotationFun = async (rotateDegree) => {
const urlDetails = await rotatePdfPage(
props.file,
rotateDegree,
props.pageNumber - 1,
props.pdfRotateBase64
);
props.setPdfArrayBuffer(urlDetails.arrayBuffer);
props.setPdfRotatese64(urlDetails.base64);
};
const handleRotateRight = () => {
let rotateDegree;
if (props.rotateDegree < 360) {
rotateDegree = props.rotateDegree + 90;
props.setRotateDegree(rotateDegree);
handleRotationFun(rotateDegree);
} else {
rotateDegree = 90;
props.setRotateDegree(rotateDegree);
handleRotationFun(rotateDegree);
}
};
const handleRotateLeft = () => {
let rotateDegree;
if (props.rotateDegree > -360) {
rotateDegree = props.rotateDegree - 90;
props.setRotateDegree(rotateDegree);
handleRotationFun(rotateDegree);
} else {
rotateDegree = -90;
props.setRotateDegree(rotateDegree);
handleRotationFun(rotateDegree);
}
};
return (
// md:mt-[41px] xl:mt-[63px]mt=[]
@@ -32,11 +64,26 @@ function PdfZoom(props) {
<i className="fa-light fa-magnifying-glass-plus text-gray-500 2xl:text-[30px]"></i>
</span>
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
onClick={() => handleRotateRight()}
title={t("Rotate right")}
>
<i className="fa-light fa-rotate-right text-gray-500 2xl:text-[30px]"></i>
</span>
{/* <span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
onClick={handleReset}
title={t("reset")}
>
<i className="fa-light fa-magnifying-glass-arrows-rotate text-gray-500 2xl:text-[30px]"></i>
</span> */}
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px] cursor-pointer"
title={t("Rotate left")}
onClick={() => handleRotateLeft()}
>
<i className="fa-light fa-rotate-left text-gray-500 2xl:text-[30px]"></i>
</span>
<span
className="bg-gray-50 px-[4px] 2xl:px-[15px] 2xl:py-[10px]"
@@ -12,7 +12,9 @@ function RenderAllPdfPage({
pageNumber,
signerPos,
signerObjectId,
containerWH
containerWH,
setRotateDegree,
pdfRotateBase64
}) {
const { t } = useTranslation();
const [signPageNumber, setSignPageNumber] = useState([]);
@@ -66,6 +68,7 @@ function RenderAllPdfPage({
)
);
};
const pdfDataBase64 = `data:application/pdf;base64,${pdfRotateBase64}`;
return (
<div ref={pageContainer} className="hidden w-[20%] bg-base-100 md:block">
<div className="mx-2 pr-2 pt-2 pb-1 text-[15px] text-base-content font-semibold border-b-[1px] border-base-300">
@@ -78,7 +81,7 @@ function RenderAllPdfPage({
<Document
loading={t("loading-doc")}
onLoadSuccess={onDocumentLoad}
file={signPdfUrl}
file={(pdfRotateBase64 && pdfDataBase64) || signPdfUrl}
>
{Array.from(new Array(allPages), (el, index) => (
<div
@@ -88,6 +91,7 @@ function RenderAllPdfPage({
} border-2 m-[10px] flex justify-center items-center relative`}
onClick={() => {
setPageNumber(index + 1);
setRotateDegree(0);
if (setSignBtnPosition) {
setSignBtnPosition([]);
}
+15 -3
View File
@@ -61,7 +61,8 @@ function RenderPdf({
scale,
setIsSelectId,
ispublicTemplate,
handleUserDetails
handleUserDetails,
pdfRotateBase64
}) {
const { t } = useTranslation();
const isMobile = window.innerWidth < 767;
@@ -248,6 +249,8 @@ function RenderPdf({
);
}
};
const pdfDataBase64 = `data:application/pdf;base64,${pdfRotateBase64}`;
return (
<>
{successEmail && (
@@ -405,7 +408,11 @@ function RenderPdf({
setSelectWidgetId("");
}
}}
file={pdfDetails[0]?.SignedUrl || pdfDetails[0]?.URL}
file={
(pdfRotateBase64 && pdfDataBase64) ||
pdfDetails[0]?.SignedUrl ||
pdfDetails[0].URL
}
>
<Page
key={index}
@@ -586,9 +593,14 @@ function RenderPdf({
}
}}
// ref={pdfRef}
file={pdfDetails[0]?.SignedUrl || pdfDetails[0].URL}
file={
(pdfRotateBase64 && pdfDataBase64) ||
pdfDetails[0]?.SignedUrl ||
pdfDetails[0].URL
}
>
<Page
// rotate={rotateDegree}
key={index}
width={containerWH.width}
scale={scale || 1}
+49 -6
View File
@@ -1,7 +1,7 @@
import axios from "axios";
import moment from "moment";
import React from "react";
import { rgb } from "pdf-lib";
import { PDFDocument, rgb, degrees } from "pdf-lib";
import Parse from "parse";
import { appInfo } from "./appinfo";
import { saveAs } from "file-saver";
@@ -1629,8 +1629,8 @@ export const multiSignEmbed = async (
});
}
const pdfBytes = await pdfDoc.saveAsBase64({ useObjectStreams: false });
//console.log("pdf", pdfBytes);
return pdfBytes;
console.log("pdf", pdfBytes);
//return pdfBytes;
};
// function for validating URLs
@@ -2170,9 +2170,11 @@ function compensateRotation(
font,
page
) {
// Ensure pageRotation is between 0 and 360 degrees
pageRotation = ((pageRotation % 360) + 360) % 360;
let rotationRads = (pageRotation * Math.PI) / 180;
//These coords are now from bottom/left
// Coordinates are from bottom-left
let coordsFromBottomLeft = { x: x / scale };
if (pageRotation === 90 || pageRotation === 270) {
coordsFromBottomLeft.y = dimensions.width - (y + fontSize) / scale;
@@ -2182,6 +2184,7 @@ function compensateRotation(
let drawX = null;
let drawY = null;
if (pageRotation === 90) {
drawX =
coordsFromBottomLeft.x * Math.cos(rotationRads) -
@@ -2207,8 +2210,8 @@ function compensateRotation(
coordsFromBottomLeft.x * Math.sin(rotationRads) +
coordsFromBottomLeft.y * Math.cos(rotationRads) +
dimensions.height;
} else {
//no rotation
} else if (pageRotation === 0 || pageRotation === 360) {
// No rotation or full rotation
drawX = coordsFromBottomLeft.x;
drawY = coordsFromBottomLeft.y;
}
@@ -2312,3 +2315,43 @@ export const getDefaultSignature = async (objectId) => {
};
}
};
//function to rotate pdf page
export async function rotatePdfPage(
url,
rotateDegree,
pageNumber,
pdfRotateBase64
) {
let file;
if (pdfRotateBase64) {
file = base64ToArrayBuffer(pdfRotateBase64);
} else {
file = await convertPdfArrayBuffer(pdfRotateBase64 || url);
}
// Load the existing PDF
const pdfDoc = await PDFDocument.load(file);
// Get the page according to page number
const page = pdfDoc.getPage(pageNumber);
// Rotate the page
page.setRotation(degrees(rotateDegree));
// Serialize the PDFDocument to bytes (a Uint8Array)
const base64 = await pdfDoc.saveAsBase64({ useObjectStreams: false });
// console.log("pdfBytes",base64)
//convert base64 to arraybuffer
const arrayBuffer = base64ToArrayBuffer(base64);
return { arrayBuffer: arrayBuffer, base64: base64 };
}
function base64ToArrayBuffer(base64) {
// Decode the base64 string to a binary string
const binaryString = atob(base64);
// Create a new ArrayBuffer with the same length as the binary string
const len = binaryString.length;
const bytes = new Uint8Array(len);
// Convert the binary string to a byte array
for (let i = 0; i < len; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// Return the ArrayBuffer
return bytes.buffer;
}
+47 -22
View File
@@ -132,6 +132,9 @@ function PdfRequestFiles(props) {
const [res, setRes] = useState({});
const [documentId, setDocumentId] = useState("");
const [isPublicContact, setIsPublicContact] = useState(false);
const [rotateDegree, setRotateDegree] = useState(0);
const [pdfRotateBase64, setPdfRotatese64] = useState("");
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
const isHeader = useSelector((state) => state.showHeader);
const divRef = useRef(null);
@@ -298,6 +301,20 @@ function PdfRequestFiles(props) {
: [];
if (documentData && documentData.length > 0) {
const url =
documentData[0] &&
(documentData[0]?.SignedUrl || documentData[0]?.URL);
if (url) {
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError(t("something-went-wrong-mssg"));
} else {
setPdfArrayBuffer(arrayBuffer);
}
} else {
setHandleError(t("something-went-wrong-mssg"));
}
setIsPublicTemplate(true);
const getPublicRole = documentData[0]?.PublicRole[0];
const getUniqueIdDetails = documentData[0]?.Placeholders.find(
@@ -361,6 +378,20 @@ function PdfRequestFiles(props) {
//getting document details
const documentData = await contractDocument(documentId || docId);
if (documentData && documentData.length > 0) {
const url =
documentData[0] &&
(documentData[0]?.SignedUrl || documentData[0]?.URL);
if (url) {
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError(t("something-went-wrong-mssg"));
} else {
setPdfArrayBuffer(arrayBuffer);
}
} else {
setHandleError(t("something-went-wrong-mssg"));
}
setExtUserId(documentData[0]?.ExtUserPtr?.objectId);
const isCompleted =
documentData[0].IsCompleted && documentData[0].IsCompleted;
@@ -643,6 +674,7 @@ function PdfRequestFiles(props) {
}
};
console.log("base64", pdfRotateBase64);
//function for embed signature or image url in pdf
async function embedWidgetsData() {
//for emailVerified data checking first in localstorage
@@ -818,30 +850,9 @@ function PdfRequestFiles(props) {
setIsUiLoading(true);
// `widgets` is Used to return widgets details with page number of current user
const widgets = checkUser?.[0]?.placeHolder;
let pdfArrBuffer;
//`contractDocument` function used to get updated SignedUrl
//resolved issue of sign document by multiple signers simultaneously
const documentData = await contractDocument(documentId);
if (documentData && documentData.length > 0) {
const url = documentData[0]?.SignedUrl || documentData[0]?.URL;
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError(t("invalid-document"));
} else {
pdfArrBuffer = arrayBuffer;
}
} else if (
documentData === "Error: Something went wrong!" ||
(documentData.result && documentData.result.error)
) {
setHandleError(t("something-went-wrong-mssg"));
} else {
setHandleError(t("document-not-found"));
}
// Load a PDFDocument from the existing PDF bytes
const existingPdfBytes = pdfArrBuffer;
const existingPdfBytes = pdfArrayBuffer;
try {
const pdfDoc = await PDFDocument.load(existingPdfBytes);
const isSignYourSelfFlow = false;
@@ -1111,6 +1122,7 @@ function PdfRequestFiles(props) {
//function for change page
function changePage(offset) {
setPageNumber((prevPageNumber) => prevPageNumber + offset);
setRotateDegree(0);
}
//function for image upload or update
@@ -1911,6 +1923,8 @@ function PdfRequestFiles(props) {
setPageNumber={setPageNumber}
pageNumber={pageNumber}
containerWH={containerWH}
setRotateDegree={setRotateDegree}
pdfRotateBase64={pdfRotateBase64}
/>
{/* pdf render view */}
<div className=" w-full md:w-[57%] flex mr-4">
@@ -1920,6 +1934,16 @@ function PdfRequestFiles(props) {
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
setRotateDegree={setRotateDegree}
rotateDegree={rotateDegree}
file={
pdfDetails[0] &&
(pdfDetails[0].SignedUrl || pdfDetails[0].URL)
}
setPdfArrayBuffer={setPdfArrayBuffer}
setPdfRotatese64={setPdfRotatese64}
pageNumber={pageNumber}
pdfRotateBase64={pdfRotateBase64}
/>
<div className=" w-full md:w-[95%] ">
{/* this modal is used show this document is already sign */}
@@ -2108,6 +2132,7 @@ function PdfRequestFiles(props) {
uniqueId={uniqueId}
ispublicTemplate={isPublicTemplate}
handleUserDetails={handleUserDetails}
pdfRotateBase64={pdfRotateBase64}
/>
)}
</div>
+30 -3
View File
@@ -141,6 +141,8 @@ function PlaceHolderSign() {
const [isCustomize, setIsCustomize] = useState(false);
const [zoomPercent, setZoomPercent] = useState(0);
const [scale, setScale] = useState(1);
const [rotateDegree, setRotateDegree] = useState(0);
const [pdfRotateBase64, setPdfRotatese64] = useState("");
const isMobile = window.innerWidth < 767;
const [, drop] = useDrop({
accept: "BOX",
@@ -805,6 +807,7 @@ function PlaceHolderSign() {
function changePage(offset) {
setSignBtnPosition([]);
setPageNumber((prevPageNumber) => prevPageNumber + offset);
setRotateDegree(0);
}
//function for capture position on hover or touch widgets
@@ -853,7 +856,7 @@ function PlaceHolderSign() {
});
const isSignYourSelfFlow = false;
try {
const pdfBytes = await multiSignEmbed(
const pdfBase64 = await multiSignEmbed(
placeholder,
pdfDoc,
isSignYourSelfFlow,
@@ -863,18 +866,29 @@ function PlaceHolderSign() {
);
const fileName = sanitizeFileName(pdfDetails[0].Name) + ".pdf";
const pdfFile = new Parse.File(fileName, { base64: pdfBytes });
const pdfFile = new Parse.File(fileName, { base64: pdfBase64 });
// Save the Parse File if needed
const pdfData = await pdfFile.save();
const pdfUrl = pdfData.url();
const tenantId = localStorage.getItem("TenantId");
const buffer = atob(pdfBytes);
const buffer = atob(pdfBase64);
SaveFileSize(buffer.length, pdfUrl, tenantId);
return pdfUrl;
} catch (e) {
console.log("error", e);
}
} else if (pdfRotateBase64) {
const fileName = sanitizeFileName(pdfDetails[0].Name) + ".pdf";
const pdfFile = new Parse.File(fileName, { base64: pdfRotateBase64 });
// Save the Parse File if needed
const pdfData = await pdfFile.save();
const pdfUrl = pdfData.url();
const tenantId = localStorage.getItem("TenantId");
const buffer = atob(pdfRotateBase64);
SaveFileSize(buffer.length, pdfUrl, tenantId);
return pdfUrl;
} else {
return pdfDetails[0].URL;
}
@@ -1739,6 +1753,8 @@ function PlaceHolderSign() {
setPageNumber={setPageNumber}
setSignBtnPosition={setSignBtnPosition}
pageNumber={pageNumber}
setRotateDegree={setRotateDegree}
pdfRotateBase64={pdfRotateBase64}
/>
{/* pdf render view */}
<div className=" w-full md:w-[57%] flex mr-4">
@@ -1748,6 +1764,16 @@ function PlaceHolderSign() {
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
setRotateDegree={setRotateDegree}
rotateDegree={rotateDegree}
file={
pdfDetails[0] &&
(pdfDetails[0].SignedUrl || pdfDetails[0].URL)
}
setPdfArrayBuffer={setPdfArrayBuffer}
setPdfRotatese64={setPdfRotatese64}
pageNumber={pageNumber}
pdfRotateBase64={pdfRotateBase64}
/>
<div className=" w-full md:w-[95%] ">
{/* this modal is used show alert set placeholder for all signers before send mail */}
@@ -2025,6 +2051,7 @@ function PlaceHolderSign() {
setScale={setScale}
scale={scale}
setIsSelectId={setIsSelectId}
pdfRotateBase64={pdfRotateBase64}
/>
)}
</div>
@@ -123,6 +123,8 @@ function SignYourSelf() {
const [zoomPercent, setZoomPercent] = useState(0);
const isHeader = useSelector((state) => state.showHeader);
const [scale, setScale] = useState(1);
const [rotateDegree, setRotateDegree] = useState(0);
const [pdfRotateBase64, setPdfRotatese64] = useState("");
const divRef = useRef(null);
const nodeRef = useRef(null);
const [, drop] = useDrop({
@@ -862,6 +864,7 @@ function SignYourSelf() {
function changePage(offset) {
setSignBtnPosition([]);
setPageNumber((prevPageNumber) => prevPageNumber + offset);
setRotateDegree(0);
}
//function for image upload or update
const onImageChange = (event) => {
@@ -1213,6 +1216,8 @@ function SignYourSelf() {
setSignBtnPosition={setSignBtnPosition}
pageNumber={pageNumber}
containerWH={containerWH}
setRotateDegree={setRotateDegree}
pdfRotateBase64={pdfRotateBase64}
/>
<div className=" w-full md:w-[57%] flex mr-4">
<PdfZoom
@@ -1221,6 +1226,16 @@ function SignYourSelf() {
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
setRotateDegree={setRotateDegree}
rotateDegree={rotateDegree}
file={
pdfDetails[0] &&
(pdfDetails[0].SignedUrl || pdfDetails[0].URL)
}
setPdfArrayBuffer={setPdfArrayBuffer}
setPdfRotatese64={setPdfRotatese64}
pageNumber={pageNumber}
pdfRotateBase64={pdfRotateBase64}
/>
<div className="w-full md:w-[95%]">
<ModalUi
@@ -1368,6 +1383,7 @@ function SignYourSelf() {
handleTextSettingModal={handleTextSettingModal}
setScale={setScale}
scale={scale}
pdfRotateBase64={pdfRotateBase64}
/>
)}
</div>
+29 -1
View File
@@ -26,7 +26,8 @@ import {
radioButtonWidget,
fetchSubscription,
getContainerScale,
openInNewTab
openInNewTab,
convertPdfArrayBuffer
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import "../styles/AddUser.css";
@@ -93,6 +94,9 @@ const TemplatePlaceholder = () => {
const [isNameModal, setIsNameModal] = useState(false);
const [isTextSetting, setIsTextSetting] = useState(false);
const [pdfLoad, setPdfLoad] = useState(false);
const [rotateDegree, setRotateDegree] = useState(0);
const [pdfRotateBase64, setPdfRotatese64] = useState("");
const [pdfArrayBuffer, setPdfArrayBuffer] = useState("");
const color = [
"#93a3db",
"#e6c3db",
@@ -230,6 +234,18 @@ const TemplatePlaceholder = () => {
: [];
if (documentData && documentData.length > 0) {
const url = documentData[0] && documentData[0]?.URL;
if (url) {
//convert document url in array buffer format to use embed widgets in pdf using pdf-lib
const arrayBuffer = await convertPdfArrayBuffer(url);
if (arrayBuffer === "Error") {
setHandleError(t("something-went-wrong-mssg"));
} else {
setPdfArrayBuffer(arrayBuffer);
}
} else {
setHandleError(t("something-went-wrong-mssg"));
}
if (isEnableSubscription) {
checkIsSubscribed(documentData[0]?.ExtUserPtr?.Email);
}
@@ -691,6 +707,7 @@ const TemplatePlaceholder = () => {
function changePage(offset) {
setSignBtnPosition([]);
setPageNumber((prevPageNumber) => prevPageNumber + offset);
setRotateDegree(0);
}
//function for capture position on hover or touch widgets
@@ -1321,6 +1338,16 @@ const TemplatePlaceholder = () => {
containerWH={containerWH}
setZoomPercent={setZoomPercent}
zoomPercent={zoomPercent}
setRotateDegree={setRotateDegree}
rotateDegree={rotateDegree}
file={
pdfDetails[0] &&
(pdfDetails[0].SignedUrl || pdfDetails[0].URL)
}
setPdfArrayBuffer={setPdfArrayBuffer}
setPdfRotatese64={setPdfRotatese64}
pageNumber={pageNumber}
pdfRotateBase64={pdfRotateBase64}
/>
<div className="w-full md:w-[95%]">
{/* this modal is used show alert set placeholder for all signers before send mail */}
@@ -1496,6 +1523,7 @@ const TemplatePlaceholder = () => {
setScale={setScale}
scale={scale}
setIsSelectId={setIsSelectId}
pdfRotateBase64={pdfRotateBase64}
/>
)}
</div>