Product:
Product Version:“pdftron/webviewer”: “^10.4.0”
Brief summary of your issue:
Creating a project on react js
issue is i want to apply custom user selection in which i have to open a modal for user selection all user i have select its shown in my custom drop down
but know i wan to apply every form changes like add remove update delete should be add into a custom json
etc
const json = {
user1: “”,
user2: “”,
// Add more users as needed
};
like if i select user one so every form action i have performed its save its XFDF into user1 and when i have changes user to user2 it should apply after that action of form perform save its XFDF in to user2 Jason if i switch to user1 its import XFDF of user 1 if previously XFDF exists init and again start update it without removing the previously data added into a user1 and start to add more XFDF data init
how can i achive this
Please describe your issue and provide steps to reproduce it:
currently i am tring to achive this goal using addEventListener annotationChanged
but on using exportAnnotCommand() its generate error
TypeError: annotationManager.exportAnnotCommand is not a function
or on using annotationManager.exportAnnotations()
its give me whole XFDF which i didnt want
Please provide a link to a minimal sample where the issue is reproducible:
const updateXFDF = (action, xfdf, userSelect) => {
setUserAnnotations((prevState) => {
// Check if the userSelect already has some data
if (prevState[userSelect]) {
// If userSelect already has data, concatenate the new xfdf data to it
let newXfdf = prevState[userSelect] + xfdf;
// Split the concatenated string by the annotation start tag "<"
// and filter out duplicates
newXfdf = newXfdf
.split("<")
.filter((value, index, self) => {
return self.indexOf(value) === index;
})
.join("<");
return {
...prevState,
[userSelect]: newXfdf,
};
} else {
// If userSelect does not have any data, simply set the xfdf as its value
return {
...prevState,
[userSelect]: xfdf,
};
}
});
};
// if using a class, equivalent of componentDidMount
useEffect(() => {
if (webViewer.attachmentBlob) {
WebViewer(
{
path: "/webviewer/lib",
showLocalFilePicker: true,
fullAPI: true,
licenseKey:
"1693909073058:7c3***", // sign up to get a free trial key at https://dev.apryse.com
},
viewer.current
).then(async (instance) => {
instance.UI.loadDocument(base64ToBlob(webViewer.attachmentBlob), {
filename: fileName,
});
const { documentViewer, annotationManager } = instance.Core;
let userSelect = selectedUser;
//======================================== disable header =====================================//
instance.UI.disableElements([
"outlinesPanelButton",
"comboBoxFieldToolGroupButton",
"listBoxFieldToolGroupButton",
"toolsOverlay",
"toolbarGroup-Shapes",
"toolbarGroup-Edit",
"toolbarGroup-Insert",
"shapeToolGroupButton",
"menuButton",
"freeHandHighlightToolGroupButton",
"underlineToolGroupButton",
"freeHandToolGroupButton",
"stickyToolGroupButton",
"squigglyToolGroupButton",
"strikeoutToolGroupButton",
"notesPanel",
"viewControlsButton",
"selectToolButton",
"toggleNotesButton",
"searchButton",
"freeTextToolGroupButton",
"crossStampToolButton",
"checkStampToolButton",
"dotStampToolButton",
"rubberStampToolGroupButton",
"dateFreeTextToolButton",
"eraserToolButton",
"panToolButton",
"signatureToolGroupButton",
"viewControlsOverlay",
"contextMenuPopup",
]);
//======================================== disable header =====================================//
//======================================== for cutome side bar =====================================//
// Example handler for when the dropdown value changes
const handleDropdownChange = (selectedValue) => {
setSelectedUser(selectedValue);
userSelect = selectedValue;
};
const openCustomModal = () => {
setOpenAddParticipentModal(true); // Open the custom modal
};
// Create a render function for the custom panel
const renderCustomPanel = () => {
return (
<div>
<label htmlFor="participantDropdown">{t("Participant")}</label>
<select
id="participantDropdown"
name="Participant"
onChange={(e) => handleDropdownChange(e.target.value)}
>
<option value="user1">user1</option>
<option value="user2">user2</option>
{/* Add more options as needed */}
</select>
<button onClick={openCustomModal}>Open Custom Modal</button>
</div>
);
};
var myCustomPanel = {
tab: {
dataElement: "customPanelTab",
title: "customPanelTab",
img: "/favicon-32x32.png",
},
panel: {
dataElement: "customPanel",
render: renderCustomPanel,
},
};
instance.UI.setCustomPanel(myCustomPanel);
//======================================== for cutome side bar =====================================//
//======================================== header save button =====================================//
async function generateBase64FromBlob(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = function () {
const base64String = reader.result.split(",")[1];
resolve(base64String);
};
reader.onerror = function (error) {
reject(error);
};
reader.readAsDataURL(blob);
});
}
instance.UI.setHeaderItems((header) => {
header.push({
type: "actionButton",
img: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/></svg>',
onClick: async () => {
// save the annotations
const xfdfString = await annotationManager.exportAnnotations({
widgets: false,
links: false,
//fields: false
});
console.log("xfdfStringxfdf String", xfdfString);
const doc = documentViewer.getDocument();
const data = await doc.getFileData({
// saves the document with annotations in it
xfdfString,
});
const arr = new Uint8Array(data);
const blob = new Blob([arr], { type: "application/pdf" });
generateBase64FromBlob(blob)
.then((base64String) => {
console.log("xfdfStringxfdf Base64 String:", base64String);
})
.catch((error) => {
console.error("Error generating base64 string:", error);
});
// Dispatch your Redux action to send the data to the API
// if (Number(commingFrom) === 1) {
// const apiData = {
// TaskID: taskId, // Assuming taskId is defined in your component
// TaskAttachementID: attachmentID, // Assuming attachmentID is defined in your component
// AnnotationString: xfdfString, // Pass the annotations data here
// };
// // for todo
// dispatch(addAnnotationsOnToDoAttachement(navigate, t, apiData));
// } else if (Number(commingFrom) === 2) {
// let notesData = {
// NoteID: taskId,
// NoteAttachementID: attachmentID,
// AnnotationString: xfdfString,
// };
// dispatch(
// addAnnotationsOnNotesAttachement(navigate, t, notesData)
// );
// // for notes
// } else if (Number(commingFrom) === 3) {
// let resolutionData = {
// ResolutionID: taskId,
// ResolutionAttachementID: attachmentID,
// AnnotationString: xfdfString,
// };
// dispatch(
// addAnnotationsOnResolutionAttachement(
// navigate,
// t,
// resolutionData
// )
// );
// // for resultion
// } else if (Number(commingFrom) === 4) {
// // for data room
// let dataRoomData = {
// FileID: attachmentID,
// AnnotationString: xfdfString,
// };
// dispatch(
// addAnnotationsOnDataroomAttachement(navigate, t, dataRoomData)
// );
// }
},
});
header.push({
type: "actionButton",
img: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"/></svg>',
onClick: async () => {},
});
});
//======================================== header save button =====================================//
//======================================== for documentLoaded =====================================//
await documentViewer.getAnnotationsLoadedPromise();
documentViewer.addEventListener("documentLoaded", async () => {
// annotManager.setCurrentUser(name);
if (webViewer.xfdfData !== "" && annotationManager) {
try {
await annotationManager.importAnnotations(webViewer.xfdfData);
} catch (error) {
console.log("importAnnotations", error);
}
}
});
//======================================== for documentLoaded =====================================//
//======================================== for every single line anotation =====================================//
annotationManager.addEventListener(
"annotationChanged",
async (annotations, action, { imported }) => {
if (imported) {
return;
}
try {
// Export annotations to XFDF format using `exportAnnotations`
const xfdf = await annotationManager.exportAnnotations();
// Update the user's annotations based on the action
updateXFDF(action, xfdf, userSelect);
} catch (error) {
console.error("Error in annotationChanged event:", error);
}
}
);
//======================================== for every single line anotation =====================================//
});
}
}, [webViewer.attachmentBlob]);
currently i have set