Files
bentopdf/src/js/logic/remove-annotations.ts

142 lines
4.6 KiB
TypeScript
Raw Normal View History

import { showLoader, hideLoader, showAlert } from '../ui.js';
import { downloadFile } from '../utils/helpers.js';
import { state } from '../state.js';
import { PDFName } from 'pdf-lib';
export function setupRemoveAnnotationsTool() {
if (state.pdfDoc) {
document.getElementById('total-pages').textContent =
state.pdfDoc.getPageCount();
}
const pageScopeRadios = document.querySelectorAll('input[name="page-scope"]');
const pageRangeWrapper = document.getElementById('page-range-wrapper');
pageScopeRadios.forEach((radio) => {
radio.addEventListener('change', () => {
// @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'Element'.
pageRangeWrapper.classList.toggle('hidden', radio.value !== 'specific');
});
});
const selectAllCheckbox = document.getElementById('select-all-annotations');
const allAnnotCheckboxes = document.querySelectorAll('.annot-checkbox');
selectAllCheckbox.addEventListener('change', () => {
allAnnotCheckboxes.forEach((checkbox) => {
// @ts-expect-error TS(2339) FIXME: Property 'checked' does not exist on type 'Element... Remove this comment to see the full error message
checkbox.checked = selectAllCheckbox.checked;
});
});
}
export function removeAnnotationsFromDoc(
pdfDoc,
pageIndices = null,
annotationTypes = null
) {
const pages = pdfDoc.getPages();
const targetPages =
pageIndices || Array.from({ length: pages.length }, (_, i) => i);
for (const pageIndex of targetPages) {
const page = pages[pageIndex];
const annotRefs = page.node.Annots()?.asArray() || [];
if (!annotationTypes) {
if (annotRefs.length > 0) {
page.node.delete(PDFName.of('Annots'));
}
} else {
const annotsToKeep = [];
for (const ref of annotRefs) {
const annot = pdfDoc.context.lookup(ref);
const subtype = annot
.get(PDFName.of('Subtype'))
?.toString()
.substring(1);
if (!subtype || !annotationTypes.has(subtype)) {
annotsToKeep.push(ref);
}
}
if (annotsToKeep.length > 0) {
const newAnnotsArray = pdfDoc.context.obj(annotsToKeep);
page.node.set(PDFName.of('Annots'), newAnnotsArray);
} else {
page.node.delete(PDFName.of('Annots'));
}
}
}
}
export async function removeAnnotations() {
showLoader('Removing annotations...');
try {
const totalPages = state.pdfDoc.getPageCount();
let targetPageIndices = [];
const pageScope = (
document.querySelector(
'input[name="page-scope"]:checked'
) as HTMLInputElement
).value;
if (pageScope === 'all') {
targetPageIndices = Array.from({ length: totalPages }, (_, i) => i);
} else {
// @ts-expect-error TS(2339) FIXME: Property 'value' does not exist on type 'HTMLEleme... Remove this comment to see the full error message
const rangeInput = document.getElementById('page-range-input').value;
if (!rangeInput.trim()) throw new Error('Please enter a page range.');
const ranges = rangeInput.split(',');
for (const range of ranges) {
const trimmedRange = range.trim();
if (trimmedRange.includes('-')) {
const [start, end] = trimmedRange.split('-').map(Number);
if (
isNaN(start) ||
isNaN(end) ||
start < 1 ||
end > totalPages ||
start > end
)
continue;
for (let i = start; i <= end; i++) targetPageIndices.push(i - 1);
} else {
const pageNum = Number(trimmedRange);
if (isNaN(pageNum) || pageNum < 1 || pageNum > totalPages) continue;
targetPageIndices.push(pageNum - 1);
}
}
targetPageIndices = [...new Set(targetPageIndices)];
}
if (targetPageIndices.length === 0)
throw new Error('No valid pages were selected.');
const typesToRemove = new Set(
Array.from(document.querySelectorAll('.annot-checkbox:checked')).map(
(cb) => (cb as HTMLInputElement).value
)
);
if (typesToRemove.size === 0)
throw new Error('Please select at least one annotation type to remove.');
removeAnnotationsFromDoc(state.pdfDoc, targetPageIndices, typesToRemove);
const newPdfBytes = await state.pdfDoc.save();
downloadFile(
new Blob([newPdfBytes], { type: 'application/pdf' }),
'annotations-removed.pdf'
);
} catch (e) {
console.error(e);
showAlert(
'Error',
e.message || 'Could not remove annotations. Please check your page range.'
);
} finally {
hideLoader();
}
}