mirror of
https://github.com/alam00000/bentopdf.git
synced 2026-11-11 09:23:36 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18ecaf4228 | ||
|
|
fe10335fde | ||
|
|
842d940801 | ||
|
|
85ce0c97b5 |
No files matched your search
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "bento-pdf",
|
"name": "bento-pdf",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.6.0",
|
"version": "1.6.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
declare const coherentpdf: typeof import('../../src/types/coherentpdf.global').coherentpdf;
|
||||||
|
|
||||||
|
interface GetAttachmentsMessage {
|
||||||
|
command: 'get-attachments';
|
||||||
|
fileBuffer: ArrayBuffer;
|
||||||
|
fileName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditAttachmentsMessage {
|
||||||
|
command: 'edit-attachments';
|
||||||
|
fileBuffer: ArrayBuffer;
|
||||||
|
fileName: string;
|
||||||
|
attachmentsToRemove: number[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type EditAttachmentsWorkerMessage = GetAttachmentsMessage | EditAttachmentsMessage;
|
||||||
|
|
||||||
|
interface GetAttachmentsSuccessResponse {
|
||||||
|
status: 'success';
|
||||||
|
attachments: Array<{ index: number; name: string; page: number; data: ArrayBuffer }>;
|
||||||
|
fileName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetAttachmentsErrorResponse {
|
||||||
|
status: 'error';
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditAttachmentsSuccessResponse {
|
||||||
|
status: 'success';
|
||||||
|
modifiedPDF: ArrayBuffer;
|
||||||
|
fileName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditAttachmentsErrorResponse {
|
||||||
|
status: 'error';
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetAttachmentsResponse = GetAttachmentsSuccessResponse | GetAttachmentsErrorResponse;
|
||||||
|
type EditAttachmentsResponse = EditAttachmentsSuccessResponse | EditAttachmentsErrorResponse;
|
||||||
|
type EditAttachmentsWorkerResponse = GetAttachmentsResponse | EditAttachmentsResponse;
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
self.importScripts('/coherentpdf.browser.min.js');
|
||||||
|
|
||||||
|
function getAttachmentsFromPDFInWorker(fileBuffer, fileName) {
|
||||||
|
try {
|
||||||
|
const uint8Array = new Uint8Array(fileBuffer);
|
||||||
|
|
||||||
|
let pdf;
|
||||||
|
try {
|
||||||
|
pdf = coherentpdf.fromMemory(uint8Array, '');
|
||||||
|
} catch (error) {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: `Failed to load PDF: ${fileName}. Error: ${error.message || error}`
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
coherentpdf.startGetAttachments(pdf);
|
||||||
|
const attachmentCount = coherentpdf.numberGetAttachments();
|
||||||
|
|
||||||
|
if (attachmentCount === 0) {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'success',
|
||||||
|
attachments: [],
|
||||||
|
fileName: fileName
|
||||||
|
});
|
||||||
|
coherentpdf.deletePdf(pdf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attachments = [];
|
||||||
|
for (let i = 0; i < attachmentCount; i++) {
|
||||||
|
try {
|
||||||
|
const name = coherentpdf.getAttachmentName(i);
|
||||||
|
const page = coherentpdf.getAttachmentPage(i);
|
||||||
|
const attachmentData = coherentpdf.getAttachmentData(i);
|
||||||
|
|
||||||
|
const dataArray = new Uint8Array(attachmentData);
|
||||||
|
const buffer = dataArray.buffer.slice(dataArray.byteOffset, dataArray.byteOffset + dataArray.byteLength);
|
||||||
|
|
||||||
|
attachments.push({
|
||||||
|
index: i,
|
||||||
|
name: String(name),
|
||||||
|
page: Number(page),
|
||||||
|
data: buffer
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to get attachment ${i} from ${fileName}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coherentpdf.endGetAttachments();
|
||||||
|
coherentpdf.deletePdf(pdf);
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
status: 'success',
|
||||||
|
attachments: attachments,
|
||||||
|
fileName: fileName
|
||||||
|
};
|
||||||
|
|
||||||
|
const transferBuffers = attachments.map(att => att.data);
|
||||||
|
self.postMessage(response, transferBuffers);
|
||||||
|
} catch (error) {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: 'Unknown error occurred during attachment listing.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function editAttachmentsInPDFInWorker(fileBuffer, fileName, attachmentsToRemove) {
|
||||||
|
try {
|
||||||
|
const uint8Array = new Uint8Array(fileBuffer);
|
||||||
|
|
||||||
|
let pdf;
|
||||||
|
try {
|
||||||
|
pdf = coherentpdf.fromMemory(uint8Array, '');
|
||||||
|
} catch (error) {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: `Failed to load PDF: ${fileName}. Error: ${error.message || error}`
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attachmentsToRemove && attachmentsToRemove.length > 0) {
|
||||||
|
coherentpdf.startGetAttachments(pdf);
|
||||||
|
const attachmentCount = coherentpdf.numberGetAttachments();
|
||||||
|
const attachmentsToKeep = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < attachmentCount; i++) {
|
||||||
|
if (!attachmentsToRemove.includes(i)) {
|
||||||
|
const name = coherentpdf.getAttachmentName(i);
|
||||||
|
const page = coherentpdf.getAttachmentPage(i);
|
||||||
|
const data = coherentpdf.getAttachmentData(i);
|
||||||
|
|
||||||
|
const dataCopy = new Uint8Array(data.length);
|
||||||
|
dataCopy.set(new Uint8Array(data));
|
||||||
|
|
||||||
|
attachmentsToKeep.push({
|
||||||
|
name: String(name),
|
||||||
|
page: Number(page),
|
||||||
|
data: dataCopy
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coherentpdf.endGetAttachments();
|
||||||
|
|
||||||
|
coherentpdf.removeAttachedFiles(pdf);
|
||||||
|
|
||||||
|
for (const attachment of attachmentsToKeep) {
|
||||||
|
if (attachment.page === 0) {
|
||||||
|
coherentpdf.attachFileFromMemory(attachment.data, attachment.name, pdf);
|
||||||
|
} else {
|
||||||
|
coherentpdf.attachFileToPageFromMemory(attachment.data, attachment.name, pdf, attachment.page);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const modifiedBytes = coherentpdf.toMemory(pdf, false, true);
|
||||||
|
coherentpdf.deletePdf(pdf);
|
||||||
|
|
||||||
|
const buffer = modifiedBytes.buffer.slice(modifiedBytes.byteOffset, modifiedBytes.byteOffset + modifiedBytes.byteLength);
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
status: 'success',
|
||||||
|
modifiedPDF: buffer,
|
||||||
|
fileName: fileName
|
||||||
|
};
|
||||||
|
|
||||||
|
self.postMessage(response, [response.modifiedPDF]);
|
||||||
|
} catch (error) {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: 'Unknown error occurred during attachment editing.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onmessage = (e) => {
|
||||||
|
if (e.data.command === 'get-attachments') {
|
||||||
|
getAttachmentsFromPDFInWorker(e.data.fileBuffer, e.data.fileName);
|
||||||
|
} else if (e.data.command === 'edit-attachments') {
|
||||||
|
editAttachmentsInPDFInWorker(e.data.fileBuffer, e.data.fileName, e.data.attachmentsToRemove);
|
||||||
|
}
|
||||||
|
};
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
declare const coherentpdf: typeof import('../../src/types/coherentpdf.global').coherentpdf;
|
||||||
|
|
||||||
|
interface ExtractAttachmentsMessage {
|
||||||
|
command: 'extract-attachments';
|
||||||
|
fileBuffers: ArrayBuffer[];
|
||||||
|
fileNames: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExtractAttachmentSuccessResponse {
|
||||||
|
status: 'success';
|
||||||
|
attachments: Array<{ name: string; data: ArrayBuffer }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExtractAttachmentErrorResponse {
|
||||||
|
status: 'error';
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ExtractAttachmentResponse = ExtractAttachmentSuccessResponse | ExtractAttachmentErrorResponse;
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
self.importScripts('/coherentpdf.browser.min.js');
|
||||||
|
|
||||||
|
function extractAttachmentsFromPDFsInWorker(fileBuffers, fileNames) {
|
||||||
|
try {
|
||||||
|
const allAttachments = [];
|
||||||
|
const totalFiles = fileBuffers.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < totalFiles; i++) {
|
||||||
|
const buffer = fileBuffers[i];
|
||||||
|
const fileName = fileNames[i];
|
||||||
|
const uint8Array = new Uint8Array(buffer);
|
||||||
|
|
||||||
|
let pdf;
|
||||||
|
try {
|
||||||
|
pdf = coherentpdf.fromMemory(uint8Array, '');
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to load PDF: ${fileName}`, error);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
coherentpdf.startGetAttachments(pdf);
|
||||||
|
const attachmentCount = coherentpdf.numberGetAttachments();
|
||||||
|
|
||||||
|
if (attachmentCount === 0) {
|
||||||
|
console.warn(`No attachments found in ${fileName}`);
|
||||||
|
coherentpdf.deletePdf(pdf);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseName = fileName.replace(/\.pdf$/i, '');
|
||||||
|
for (let j = 0; j < attachmentCount; j++) {
|
||||||
|
try {
|
||||||
|
const attachmentName = coherentpdf.getAttachmentName(j);
|
||||||
|
const attachmentPage = coherentpdf.getAttachmentPage(j);
|
||||||
|
const attachmentData = coherentpdf.getAttachmentData(j);
|
||||||
|
|
||||||
|
let uniqueName = attachmentName;
|
||||||
|
let counter = 1;
|
||||||
|
while (allAttachments.some(att => att.name === uniqueName)) {
|
||||||
|
const nameParts = attachmentName.split('.');
|
||||||
|
if (nameParts.length > 1) {
|
||||||
|
const extension = nameParts.pop();
|
||||||
|
uniqueName = `${nameParts.join('.')}_${counter}.${extension}`;
|
||||||
|
} else {
|
||||||
|
uniqueName = `${attachmentName}_${counter}`;
|
||||||
|
}
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attachmentPage > 0) {
|
||||||
|
uniqueName = `${baseName}_page${attachmentPage}_${uniqueName}`;
|
||||||
|
} else {
|
||||||
|
uniqueName = `${baseName}_${uniqueName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
allAttachments.push({
|
||||||
|
name: uniqueName,
|
||||||
|
data: attachmentData.buffer.slice(0)
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to extract attachment ${j} from ${fileName}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
coherentpdf.endGetAttachments();
|
||||||
|
coherentpdf.deletePdf(pdf);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allAttachments.length === 0) {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: 'No attachments were found in the selected PDF(s).'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
status: 'success',
|
||||||
|
attachments: []
|
||||||
|
};
|
||||||
|
|
||||||
|
const transferBuffers = [];
|
||||||
|
for (const attachment of allAttachments) {
|
||||||
|
response.attachments.push({
|
||||||
|
name: attachment.name,
|
||||||
|
data: attachment.data
|
||||||
|
});
|
||||||
|
transferBuffers.push(attachment.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.postMessage(response, transferBuffers);
|
||||||
|
} catch (error) {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: 'Unknown error occurred during attachment extraction.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onmessage = (e) => {
|
||||||
|
if (e.data.command === 'extract-attachments') {
|
||||||
|
extractAttachmentsFromPDFsInWorker(e.data.fileBuffers, e.data.fileNames);
|
||||||
|
}
|
||||||
|
};
|
||||||
+12
-13
@@ -318,19 +318,18 @@ export const categories = [
|
|||||||
icon: 'paperclip',
|
icon: 'paperclip',
|
||||||
subtitle: 'Embed one or more files into your PDF.',
|
subtitle: 'Embed one or more files into your PDF.',
|
||||||
},
|
},
|
||||||
// TODO@ALAM - MAKE THIS LATER, ONCE INTEGERATED WITH CPDF
|
{
|
||||||
// {
|
id: 'extract-attachments',
|
||||||
// id: 'extract-attachments',
|
name: 'Extract Attachments',
|
||||||
// name: 'Extract Attachments',
|
icon: 'download',
|
||||||
// icon: 'download',
|
subtitle: 'Extract all embedded files from PDF(s) as a ZIP.',
|
||||||
// subtitle: 'Extract all embedded files from PDF(s) as a ZIP.',
|
},
|
||||||
// },
|
{
|
||||||
// {
|
id: 'edit-attachments',
|
||||||
// id: 'edit-attachments',
|
name: 'Edit Attachments',
|
||||||
// name: 'Edit Attachments',
|
icon: 'file-edit',
|
||||||
// icon: 'file-edit',
|
subtitle: 'View or remove attachments in your PDF.',
|
||||||
// subtitle: 'View, remove, or replace attachments in your PDF.',
|
},
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
href: '/src/pages/pdf-multi-tool.html',
|
href: '/src/pages/pdf-multi-tool.html',
|
||||||
name: 'PDF Multi Tool',
|
name: 'PDF Multi Tool',
|
||||||
|
|||||||
+195
-184
@@ -1,207 +1,218 @@
|
|||||||
// TODO@ALAM - USE CPDF HERE
|
import { showLoader, hideLoader, showAlert } from '../ui.js';
|
||||||
|
import { downloadFile, readFileAsArrayBuffer } from '../utils/helpers.js';
|
||||||
|
import { state } from '../state.js';
|
||||||
|
|
||||||
// import { showLoader, hideLoader, showAlert } from '../ui.js';
|
const worker = new Worker('/workers/edit-attachments.worker.js');
|
||||||
// import { downloadFile, readFileAsArrayBuffer } from '../utils/helpers.js';
|
|
||||||
// import { state } from '../state.js';
|
|
||||||
// import { PDFDocument as PDFLibDocument } from 'pdf-lib';
|
|
||||||
|
|
||||||
// let currentAttachments: Array<{ name: string; index: number; size: number }> = [];
|
let allAttachments: Array<{ index: number; name: string; page: number; data: Uint8Array }> = [];
|
||||||
// let attachmentsToRemove: Set<number> = new Set();
|
let attachmentsToRemove: Set<number> = new Set();
|
||||||
// let attachmentsToReplace: Map<number, File> = new Map();
|
|
||||||
|
|
||||||
// export async function setupEditAttachmentsTool() {
|
export async function setupEditAttachmentsTool() {
|
||||||
// const optionsDiv = document.getElementById('edit-attachments-options');
|
const optionsDiv = document.getElementById('edit-attachments-options');
|
||||||
// if (!optionsDiv || !state.pdfDoc) return;
|
if (!optionsDiv || !state.files || state.files.length === 0) return;
|
||||||
|
|
||||||
// optionsDiv.classList.remove('hidden');
|
optionsDiv.classList.remove('hidden');
|
||||||
// await loadAttachmentsList();
|
await loadAttachmentsList();
|
||||||
// }
|
}
|
||||||
|
|
||||||
// async function loadAttachmentsList() {
|
async function loadAttachmentsList() {
|
||||||
// const attachmentsList = document.getElementById('attachments-list');
|
const attachmentsList = document.getElementById('attachments-list');
|
||||||
// if (!attachmentsList || !state.pdfDoc) return;
|
if (!attachmentsList || !state.files || state.files.length === 0) return;
|
||||||
|
|
||||||
// attachmentsList.innerHTML = '';
|
attachmentsList.innerHTML = '';
|
||||||
// currentAttachments = [];
|
attachmentsToRemove.clear();
|
||||||
// attachmentsToRemove.clear();
|
allAttachments = [];
|
||||||
// attachmentsToReplace.clear();
|
|
||||||
|
|
||||||
// try {
|
try {
|
||||||
// // Get embedded files from PDF
|
showLoader('Loading attachments...');
|
||||||
// const embeddedFiles = state.pdfDoc.context.enumerateIndirectObjects()
|
|
||||||
// .filter(([ref, obj]: any) => {
|
|
||||||
// const dict = obj instanceof PDFLibDocument.context.dict ? obj : null;
|
|
||||||
// return dict && dict.get('Type')?.toString() === '/Filespec';
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (embeddedFiles.length === 0) {
|
const file = state.files[0];
|
||||||
// attachmentsList.innerHTML = '<p class="text-gray-400 text-center py-4">No attachments found in this PDF.</p>';
|
const fileBuffer = await readFileAsArrayBuffer(file);
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// let index = 0;
|
const message = {
|
||||||
// for (const [ref, fileSpec] of embeddedFiles) {
|
command: 'get-attachments',
|
||||||
// try {
|
fileBuffer: fileBuffer,
|
||||||
// const fileSpecDict = fileSpec as any;
|
fileName: file.name
|
||||||
// const fileName = fileSpecDict.get('UF')?.decodeText() ||
|
};
|
||||||
// fileSpecDict.get('F')?.decodeText() ||
|
|
||||||
// `attachment-${index + 1}`;
|
|
||||||
|
|
||||||
// const ef = fileSpecDict.get('EF');
|
|
||||||
// let fileSize = 0;
|
|
||||||
// if (ef) {
|
|
||||||
// const fRef = ef.get('F') || ef.get('UF');
|
|
||||||
// if (fRef) {
|
|
||||||
// const fileStream = state.pdfDoc.context.lookup(fRef);
|
|
||||||
// if (fileStream) {
|
|
||||||
// fileSize = (fileStream as any).getContents().length;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// currentAttachments.push({ name: fileName, index, size: fileSize });
|
worker.postMessage(message, [fileBuffer]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading attachments:', error);
|
||||||
|
hideLoader();
|
||||||
|
showAlert('Error', 'Failed to load attachments from PDF.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// const attachmentDiv = document.createElement('div');
|
|
||||||
// attachmentDiv.className = 'flex items-center justify-between p-3 bg-gray-800 rounded-lg border border-gray-700';
|
|
||||||
// attachmentDiv.dataset.attachmentIndex = index.toString();
|
|
||||||
|
|
||||||
// const infoDiv = document.createElement('div');
|
worker.onmessage = (e) => {
|
||||||
// infoDiv.className = 'flex-1';
|
const data = e.data;
|
||||||
// const nameSpan = document.createElement('span');
|
|
||||||
// nameSpan.className = 'text-white font-medium block';
|
|
||||||
// nameSpan.textContent = fileName;
|
|
||||||
// const sizeSpan = document.createElement('span');
|
|
||||||
// sizeSpan.className = 'text-gray-400 text-sm';
|
|
||||||
// sizeSpan.textContent = `${Math.round(fileSize / 1024)} KB`;
|
|
||||||
// infoDiv.append(nameSpan, sizeSpan);
|
|
||||||
|
|
||||||
// const actionsDiv = document.createElement('div');
|
if (data.status === 'success' && data.attachments !== undefined) {
|
||||||
// actionsDiv.className = 'flex items-center gap-2';
|
const attachments = data.attachments;
|
||||||
|
allAttachments = attachments.map(att => ({
|
||||||
|
...att,
|
||||||
|
data: new Uint8Array(att.data)
|
||||||
|
}));
|
||||||
|
|
||||||
// // Remove button
|
displayAttachments(attachments);
|
||||||
// const removeBtn = document.createElement('button');
|
hideLoader();
|
||||||
// removeBtn.className = 'btn bg-red-600 hover:bg-red-700 text-white px-3 py-1 rounded text-sm';
|
} else if (data.status === 'success' && data.modifiedPDF !== undefined) {
|
||||||
// removeBtn.innerHTML = '<i data-lucide="trash-2" class="w-4 h-4"></i>';
|
hideLoader();
|
||||||
// removeBtn.title = 'Remove attachment';
|
|
||||||
// removeBtn.onclick = () => {
|
|
||||||
// attachmentsToRemove.add(index);
|
|
||||||
// attachmentDiv.classList.add('opacity-50', 'line-through');
|
|
||||||
// removeBtn.disabled = true;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // Replace button
|
downloadFile(
|
||||||
// const replaceBtn = document.createElement('button');
|
new Blob([new Uint8Array(data.modifiedPDF)], { type: 'application/pdf' }),
|
||||||
// replaceBtn.className = 'btn bg-indigo-600 hover:bg-indigo-700 text-white px-3 py-1 rounded text-sm';
|
`edited-attachments-${data.fileName}`
|
||||||
// replaceBtn.innerHTML = '<i data-lucide="refresh-cw" class="w-4 h-4"></i>';
|
);
|
||||||
// replaceBtn.title = 'Replace attachment';
|
|
||||||
// replaceBtn.onclick = () => {
|
|
||||||
// const input = document.createElement('input');
|
|
||||||
// input.type = 'file';
|
|
||||||
// input.onchange = async (e) => {
|
|
||||||
// const file = (e.target as HTMLInputElement).files?.[0];
|
|
||||||
// if (file) {
|
|
||||||
// attachmentsToReplace.set(index, file);
|
|
||||||
// nameSpan.textContent = `${fileName} → ${file.name}`;
|
|
||||||
// nameSpan.classList.add('text-yellow-400');
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// input.click();
|
|
||||||
// };
|
|
||||||
|
|
||||||
// actionsDiv.append(replaceBtn, removeBtn);
|
showAlert('Success', 'Attachments updated successfully!');
|
||||||
// attachmentDiv.append(infoDiv, actionsDiv);
|
} else if (data.status === 'error') {
|
||||||
// attachmentsList.appendChild(attachmentDiv);
|
hideLoader();
|
||||||
// index++;
|
showAlert('Error', data.message || 'Unknown error occurred.');
|
||||||
// } catch (e) {
|
}
|
||||||
// console.warn(`Failed to process attachment ${index}:`, e);
|
};
|
||||||
// index++;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch (e) {
|
|
||||||
// console.error('Error loading attachments:', e);
|
|
||||||
// showAlert('Error', 'Failed to load attachments from PDF.');
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// export async function editAttachments() {
|
worker.onerror = (error) => {
|
||||||
// if (!state.pdfDoc) {
|
hideLoader();
|
||||||
// showAlert('Error', 'PDF is not loaded.');
|
console.error('Worker error:', error);
|
||||||
// return;
|
showAlert('Error', 'Worker error occurred. Check console for details.');
|
||||||
// }
|
};
|
||||||
|
|
||||||
// showLoader('Updating attachments...');
|
function displayAttachments(attachments) {
|
||||||
// try {
|
const attachmentsList = document.getElementById('attachments-list');
|
||||||
// // Create a new PDF document
|
if (!attachmentsList) return;
|
||||||
// const newPdfDoc = await PDFLibDocument.create();
|
|
||||||
|
|
||||||
// // Copy all pages
|
|
||||||
// const pages = await newPdfDoc.copyPages(state.pdfDoc, state.pdfDoc.getPageIndices());
|
|
||||||
// pages.forEach((page: any) => newPdfDoc.addPage(page));
|
|
||||||
|
|
||||||
// // Handle attachments
|
const existingControls = attachmentsList.querySelector('.attachments-controls');
|
||||||
// const embeddedFiles = state.pdfDoc.context.enumerateIndirectObjects()
|
attachmentsList.innerHTML = '';
|
||||||
// .filter(([ref, obj]: any) => {
|
if (existingControls) {
|
||||||
// const dict = obj instanceof PDFLibDocument.context.dict ? obj : null;
|
attachmentsList.appendChild(existingControls);
|
||||||
// return dict && dict.get('Type')?.toString() === '/Filespec';
|
}
|
||||||
// });
|
|
||||||
|
|
||||||
// let attachmentIndex = 0;
|
if (attachments.length === 0) {
|
||||||
// for (const [ref, fileSpec] of embeddedFiles) {
|
const noAttachments = document.createElement('p');
|
||||||
// if (attachmentsToRemove.has(attachmentIndex)) {
|
noAttachments.className = 'text-gray-400 text-center py-4';
|
||||||
// attachmentIndex++;
|
noAttachments.textContent = 'No attachments found in this PDF.';
|
||||||
// continue; // Skip removed attachments
|
attachmentsList.appendChild(noAttachments);
|
||||||
// }
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// if (attachmentsToReplace.has(attachmentIndex)) {
|
const controlsContainer = document.createElement('div');
|
||||||
// // Replace attachment
|
controlsContainer.className = 'attachments-controls mb-4 flex justify-end';
|
||||||
// const replacementFile = attachmentsToReplace.get(attachmentIndex)!;
|
const removeAllBtn = document.createElement('button');
|
||||||
// const fileBytes = await readFileAsArrayBuffer(replacementFile);
|
removeAllBtn.className = 'btn bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded text-sm';
|
||||||
// await newPdfDoc.attach(fileBytes as ArrayBuffer, replacementFile.name, {
|
removeAllBtn.textContent = 'Remove All Attachments';
|
||||||
// mimeType: replacementFile.type || 'application/octet-stream',
|
removeAllBtn.onclick = () => {
|
||||||
// description: `Attached file: ${replacementFile.name}`,
|
if (allAttachments.length === 0) return;
|
||||||
// creationDate: new Date(),
|
|
||||||
// modificationDate: new Date(replacementFile.lastModified),
|
|
||||||
// });
|
|
||||||
// } else {
|
|
||||||
// // Keep existing attachment - copy it
|
|
||||||
// try {
|
|
||||||
// const fileSpecDict = fileSpec as any;
|
|
||||||
// const fileName = fileSpecDict.get('UF')?.decodeText() ||
|
|
||||||
// fileSpecDict.get('F')?.decodeText() ||
|
|
||||||
// `attachment-${attachmentIndex + 1}`;
|
|
||||||
|
|
||||||
// const ef = fileSpecDict.get('EF');
|
|
||||||
// if (ef) {
|
|
||||||
// const fRef = ef.get('F') || ef.get('UF');
|
|
||||||
// if (fRef) {
|
|
||||||
// const fileStream = state.pdfDoc.context.lookup(fRef);
|
|
||||||
// if (fileStream) {
|
|
||||||
// const fileData = (fileStream as any).getContents();
|
|
||||||
// await newPdfDoc.attach(fileData, fileName, {
|
|
||||||
// mimeType: 'application/octet-stream',
|
|
||||||
// description: `Attached file: ${fileName}`,
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch (e) {
|
|
||||||
// console.warn(`Failed to copy attachment ${attachmentIndex}:`, e);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// attachmentIndex++;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const pdfBytes = await newPdfDoc.save();
|
const allSelected = allAttachments.every(attachment => attachmentsToRemove.has(attachment.index));
|
||||||
// downloadFile(
|
|
||||||
// new Blob([new Uint8Array(pdfBytes)], { type: 'application/pdf' }),
|
|
||||||
// `edited-attachments-${state.files[0].name}`
|
|
||||||
// );
|
|
||||||
// showAlert('Success', 'Attachments updated successfully!');
|
|
||||||
// } catch (e) {
|
|
||||||
// console.error(e);
|
|
||||||
// showAlert('Error', 'Failed to edit attachments.');
|
|
||||||
// } finally {
|
|
||||||
// hideLoader();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
if (allSelected) {
|
||||||
|
allAttachments.forEach(attachment => {
|
||||||
|
attachmentsToRemove.delete(attachment.index);
|
||||||
|
const element = document.querySelector(`[data-attachment-index="${attachment.index}"]`);
|
||||||
|
if (element) {
|
||||||
|
element.classList.remove('opacity-50', 'line-through');
|
||||||
|
const removeBtn = element.querySelector('button');
|
||||||
|
if (removeBtn) {
|
||||||
|
removeBtn.classList.remove('bg-gray-600');
|
||||||
|
removeBtn.classList.add('bg-red-600');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
removeAllBtn.textContent = 'Remove All Attachments';
|
||||||
|
} else {
|
||||||
|
allAttachments.forEach(attachment => {
|
||||||
|
attachmentsToRemove.add(attachment.index);
|
||||||
|
const element = document.querySelector(`[data-attachment-index="${attachment.index}"]`);
|
||||||
|
if (element) {
|
||||||
|
element.classList.add('opacity-50', 'line-through');
|
||||||
|
const removeBtn = element.querySelector('button');
|
||||||
|
if (removeBtn) {
|
||||||
|
removeBtn.classList.add('bg-gray-600');
|
||||||
|
removeBtn.classList.remove('bg-red-600');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
removeAllBtn.textContent = 'Deselect All';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
controlsContainer.appendChild(removeAllBtn);
|
||||||
|
attachmentsList.appendChild(controlsContainer);
|
||||||
|
|
||||||
|
for (const attachment of attachments) {
|
||||||
|
const attachmentDiv = document.createElement('div');
|
||||||
|
attachmentDiv.className = 'flex items-center justify-between p-3 bg-gray-800 rounded-lg border border-gray-700';
|
||||||
|
attachmentDiv.dataset.attachmentIndex = attachment.index.toString();
|
||||||
|
|
||||||
|
const infoDiv = document.createElement('div');
|
||||||
|
infoDiv.className = 'flex-1';
|
||||||
|
|
||||||
|
const nameSpan = document.createElement('span');
|
||||||
|
nameSpan.className = 'text-white font-medium block';
|
||||||
|
nameSpan.textContent = attachment.name;
|
||||||
|
|
||||||
|
const levelSpan = document.createElement('span');
|
||||||
|
levelSpan.className = 'text-gray-400 text-sm block';
|
||||||
|
if (attachment.page === 0) {
|
||||||
|
levelSpan.textContent = 'Document-level attachment';
|
||||||
|
} else {
|
||||||
|
levelSpan.textContent = `Page ${attachment.page} attachment`;
|
||||||
|
}
|
||||||
|
|
||||||
|
infoDiv.append(nameSpan, levelSpan);
|
||||||
|
|
||||||
|
const actionsDiv = document.createElement('div');
|
||||||
|
actionsDiv.className = 'flex items-center gap-2';
|
||||||
|
|
||||||
|
const removeBtn = document.createElement('button');
|
||||||
|
removeBtn.className = `btn ${attachmentsToRemove.has(attachment.index) ? 'bg-gray-600' : 'bg-red-600'} hover:bg-red-700 text-white px-3 py-1 rounded text-sm`;
|
||||||
|
removeBtn.innerHTML = '<i data-lucide="trash-2" class="w-4 h-4"></i>';
|
||||||
|
removeBtn.title = 'Remove attachment';
|
||||||
|
removeBtn.onclick = () => {
|
||||||
|
if (attachmentsToRemove.has(attachment.index)) {
|
||||||
|
attachmentsToRemove.delete(attachment.index);
|
||||||
|
attachmentDiv.classList.remove('opacity-50', 'line-through');
|
||||||
|
removeBtn.classList.remove('bg-gray-600');
|
||||||
|
removeBtn.classList.add('bg-red-600');
|
||||||
|
} else {
|
||||||
|
attachmentsToRemove.add(attachment.index);
|
||||||
|
attachmentDiv.classList.add('opacity-50', 'line-through');
|
||||||
|
removeBtn.classList.add('bg-gray-600');
|
||||||
|
removeBtn.classList.remove('bg-red-600');
|
||||||
|
}
|
||||||
|
const allSelected = allAttachments.every(attachment => attachmentsToRemove.has(attachment.index));
|
||||||
|
removeAllBtn.textContent = allSelected ? 'Deselect All' : 'Remove All Attachments';
|
||||||
|
};
|
||||||
|
|
||||||
|
actionsDiv.append(removeBtn);
|
||||||
|
attachmentDiv.append(infoDiv, actionsDiv);
|
||||||
|
attachmentsList.appendChild(attachmentDiv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function editAttachments() {
|
||||||
|
if (!state.files || state.files.length === 0) {
|
||||||
|
showAlert('Error', 'No PDF file loaded.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showLoader('Processing attachments...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const file = state.files[0];
|
||||||
|
const fileBuffer = await readFileAsArrayBuffer(file);
|
||||||
|
|
||||||
|
const message = {
|
||||||
|
command: 'edit-attachments',
|
||||||
|
fileBuffer: fileBuffer,
|
||||||
|
fileName: file.name,
|
||||||
|
attachmentsToRemove: Array.from(attachmentsToRemove)
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.postMessage(message, [fileBuffer]);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error editing attachments:', error);
|
||||||
|
hideLoader();
|
||||||
|
showAlert('Error', 'Failed to edit attachments.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,88 +1,130 @@
|
|||||||
// TODO@ALAM - USE CPDF HERE
|
import { downloadFile, formatBytes } from '../utils/helpers.js';
|
||||||
|
import { state } from '../state.js';
|
||||||
|
import JSZip from 'jszip';
|
||||||
|
|
||||||
// import { showLoader, hideLoader, showAlert } from '../ui.js';
|
const worker = new Worker('/workers/extract-attachments.worker.js');
|
||||||
// import { downloadFile, readFileAsArrayBuffer } from '../utils/helpers.js';
|
|
||||||
// import { state } from '../state.js';
|
|
||||||
// import { PDFDocument as PDFLibDocument } from 'pdf-lib';
|
|
||||||
// import JSZip from 'jszip';
|
|
||||||
|
|
||||||
// export async function extractAttachments() {
|
interface ExtractAttachmentSuccessResponse {
|
||||||
// if (state.files.length === 0) {
|
status: 'success';
|
||||||
// showAlert('No Files', 'Please select at least one PDF file.');
|
attachments: Array<{ name: string; data: ArrayBuffer }>;
|
||||||
// return;
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// showLoader('Extracting attachments...');
|
interface ExtractAttachmentErrorResponse {
|
||||||
// try {
|
status: 'error';
|
||||||
// const zip = new JSZip();
|
message: string;
|
||||||
// let totalAttachments = 0;
|
}
|
||||||
|
|
||||||
// for (const file of state.files) {
|
type ExtractAttachmentResponse = ExtractAttachmentSuccessResponse | ExtractAttachmentErrorResponse;
|
||||||
// const pdfBytes = await readFileAsArrayBuffer(file);
|
|
||||||
// const pdfDoc = await PDFLibDocument.load(pdfBytes as ArrayBuffer, {
|
|
||||||
// ignoreEncryption: true,
|
|
||||||
// });
|
|
||||||
|
|
||||||
// const embeddedFiles = pdfDoc.context.enumerateIndirectObjects()
|
export async function extractAttachments() {
|
||||||
// .filter(([ref, obj]: any) => {
|
if (state.files.length === 0) {
|
||||||
// // obj must be a PDFDict
|
showStatus('No Files', 'error');
|
||||||
// if (obj && typeof obj.get === 'function') {
|
return;
|
||||||
// const type = obj.get('Type');
|
}
|
||||||
// return type && type.toString() === '/Filespec';
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (embeddedFiles.length === 0) {
|
document.getElementById('process-btn')?.classList.add('opacity-50', 'cursor-not-allowed');
|
||||||
// console.warn(`No attachments found in ${file.name}`);
|
document.getElementById('process-btn')?.setAttribute('disabled', 'true');
|
||||||
// continue;
|
|
||||||
// }
|
showStatus('Reading files (Main Thread)...', 'info');
|
||||||
|
|
||||||
// // Extract attachments
|
try {
|
||||||
// const baseName = file.name.replace(/\.pdf$/i, '');
|
const fileBuffers: ArrayBuffer[] = [];
|
||||||
// for (let i = 0; i < embeddedFiles.length; i++) {
|
const fileNames: string[] = [];
|
||||||
// try {
|
|
||||||
// const [ref, fileSpec] = embeddedFiles[i];
|
|
||||||
// const fileSpecDict = fileSpec as any;
|
|
||||||
|
|
||||||
// // Get attachment name
|
|
||||||
// const fileName = fileSpecDict.get('UF')?.decodeText() ||
|
|
||||||
// fileSpecDict.get('F')?.decodeText() ||
|
|
||||||
// `attachment-${i + 1}`;
|
|
||||||
|
|
||||||
// // Get embedded file stream
|
|
||||||
// const ef = fileSpecDict.get('EF');
|
|
||||||
// if (ef) {
|
|
||||||
// const fRef = ef.get('F') || ef.get('UF');
|
|
||||||
// if (fRef) {
|
|
||||||
// const fileStream = pdfDoc.context.lookup(fRef);
|
|
||||||
// if (fileStream) {
|
|
||||||
// const fileData = (fileStream as any).getContents();
|
|
||||||
// zip.file(`${baseName}_${fileName}`, fileData);
|
|
||||||
// totalAttachments++;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch (e) {
|
|
||||||
// console.warn(`Failed to extract attachment ${i} from ${file.name}:`, e);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (totalAttachments === 0) {
|
for (const file of state.files) {
|
||||||
// showAlert('No Attachments', 'No attachments were found in the selected PDF(s).');
|
const buffer = await file.arrayBuffer();
|
||||||
// hideLoader();
|
fileBuffers.push(buffer);
|
||||||
// return;
|
fileNames.push(file.name);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// const zipBlob = await zip.generateAsync({ type: 'blob' });
|
showStatus(`Extracting attachments from ${state.files.length} file(s)...`, 'info');
|
||||||
// downloadFile(zipBlob, 'extracted-attachments.zip');
|
|
||||||
// showAlert('Success', `Extracted ${totalAttachments} attachment(s) successfully!`);
|
|
||||||
// } catch (e) {
|
|
||||||
// console.error(e);
|
|
||||||
// showAlert('Error', 'Failed to extract attachments. The PDF may not contain attachments or may be corrupted.');
|
|
||||||
// } finally {
|
|
||||||
// hideLoader();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
const message: ExtractAttachmentsMessage = {
|
||||||
|
command: 'extract-attachments',
|
||||||
|
fileBuffers,
|
||||||
|
fileNames,
|
||||||
|
};
|
||||||
|
|
||||||
|
const transferables = fileBuffers.map(buf => buf);
|
||||||
|
worker.postMessage(message, transferables);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading files:', error);
|
||||||
|
showStatus(
|
||||||
|
`Error reading files: ${error instanceof Error ? error.message : 'Unknown error occurred'}`,
|
||||||
|
'error'
|
||||||
|
);
|
||||||
|
document.getElementById('process-btn')?.classList.remove('opacity-50', 'cursor-not-allowed');
|
||||||
|
document.getElementById('process-btn')?.removeAttribute('disabled');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
worker.onmessage = (e: MessageEvent<ExtractAttachmentResponse>) => {
|
||||||
|
document.getElementById('process-btn')?.classList.remove('opacity-50', 'cursor-not-allowed');
|
||||||
|
document.getElementById('process-btn')?.removeAttribute('disabled');
|
||||||
|
|
||||||
|
if (e.data.status === 'success') {
|
||||||
|
const attachments = e.data.attachments;
|
||||||
|
|
||||||
|
const zip = new JSZip();
|
||||||
|
let totalSize = 0;
|
||||||
|
|
||||||
|
for (const attachment of attachments) {
|
||||||
|
zip.file(attachment.name, new Uint8Array(attachment.data));
|
||||||
|
totalSize += attachment.data.byteLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
zip.generateAsync({ type: 'blob' }).then((zipBlob) => {
|
||||||
|
downloadFile(zipBlob, 'extracted-attachments.zip');
|
||||||
|
showStatus(
|
||||||
|
`Extraction completed! ${attachments.length} attachment(s) in zip file (${formatBytes(totalSize)}). Download started.`,
|
||||||
|
'success'
|
||||||
|
);
|
||||||
|
|
||||||
|
state.files = [];
|
||||||
|
const fileDisplayArea = document.getElementById('file-display-area');
|
||||||
|
if (fileDisplayArea) {
|
||||||
|
fileDisplayArea.innerHTML = '';
|
||||||
|
fileDisplayArea.classList.add('hidden');
|
||||||
|
}
|
||||||
|
const fileInput = document.getElementById('file-input') as HTMLInputElement;
|
||||||
|
if (fileInput) {
|
||||||
|
fileInput.value = '';
|
||||||
|
}
|
||||||
|
document.getElementById('process-btn')?.classList.add('opacity-50', 'cursor-not-allowed');
|
||||||
|
document.getElementById('process-btn')?.setAttribute('disabled', 'true');
|
||||||
|
});
|
||||||
|
} else if (e.data.status === 'error') {
|
||||||
|
const errorMessage = e.data.message || 'Unknown error occurred in worker.';
|
||||||
|
console.error('Worker Error:', errorMessage);
|
||||||
|
showStatus(`Error: ${errorMessage}`, 'error');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.onerror = (error) => {
|
||||||
|
console.error('Worker error:', error);
|
||||||
|
showStatus('Worker error occurred. Check console for details.', 'error');
|
||||||
|
document.getElementById('process-btn')?.classList.remove('opacity-50', 'cursor-not-allowed');
|
||||||
|
document.getElementById('process-btn')?.removeAttribute('disabled');
|
||||||
|
};
|
||||||
|
|
||||||
|
function showStatus(message: string, type: 'success' | 'error' | 'info' = 'info') {
|
||||||
|
const statusMessage = document.getElementById('status-message') as HTMLElement;
|
||||||
|
if (!statusMessage) return;
|
||||||
|
|
||||||
|
statusMessage.textContent = message;
|
||||||
|
statusMessage.className = `mt-4 p-3 rounded-lg text-sm ${
|
||||||
|
type === 'success'
|
||||||
|
? 'bg-green-900 text-green-200'
|
||||||
|
: type === 'error'
|
||||||
|
? 'bg-red-900 text-red-200'
|
||||||
|
: 'bg-blue-900 text-blue-200'
|
||||||
|
}`;
|
||||||
|
statusMessage.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExtractAttachmentsMessage {
|
||||||
|
command: 'extract-attachments';
|
||||||
|
fileBuffers: ArrayBuffer[];
|
||||||
|
fileNames: string[];
|
||||||
|
}
|
||||||
@@ -63,8 +63,8 @@ import {
|
|||||||
import { alternateMerge, setupAlternateMergeTool } from './alternate-merge.js';
|
import { alternateMerge, setupAlternateMergeTool } from './alternate-merge.js';
|
||||||
import { linearizePdf } from './linearize.js';
|
import { linearizePdf } from './linearize.js';
|
||||||
import { addAttachments, setupAddAttachmentsTool } from './add-attachments.js';
|
import { addAttachments, setupAddAttachmentsTool } from './add-attachments.js';
|
||||||
// import { extractAttachments } from './extract-attachments.js';
|
import { extractAttachments } from './extract-attachments.js';
|
||||||
// import { editAttachments, setupEditAttachmentsTool } from './edit-attachments.js';
|
import { editAttachments, setupEditAttachmentsTool } from './edit-attachments.js';
|
||||||
import { sanitizePdf } from './sanitize-pdf.js';
|
import { sanitizePdf } from './sanitize-pdf.js';
|
||||||
import { removeRestrictions } from './remove-restrictions.js';
|
import { removeRestrictions } from './remove-restrictions.js';
|
||||||
|
|
||||||
@@ -140,10 +140,10 @@ export const toolLogic = {
|
|||||||
process: addAttachments,
|
process: addAttachments,
|
||||||
setup: setupAddAttachmentsTool,
|
setup: setupAddAttachmentsTool,
|
||||||
},
|
},
|
||||||
// 'extract-attachments': extractAttachments,
|
'extract-attachments': extractAttachments,
|
||||||
// 'edit-attachments': {
|
'edit-attachments': {
|
||||||
// process: editAttachments,
|
process: editAttachments,
|
||||||
// setup: setupEditAttachmentsTool,
|
setup: setupEditAttachmentsTool,
|
||||||
// },
|
},
|
||||||
'sanitize-pdf': sanitizePdf,
|
'sanitize-pdf': sanitizePdf,
|
||||||
};
|
};
|
||||||
Reference in new issue
Block a user