mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-08-23 23:10:17 +05:30
feat(pdf): add extract text, page numbers, crop, extract images operations
Adds four new PDFOperations: pdfExtractText (pdfjs-dist getTextContent), pdfAddPageNumbers (reuses pdfWatermark's position-mapping logic, extracted into a shared resolvePosition helper), pdfCrop (page.setCropBox against the existing MediaBox), and pdfExtractImages (pdfjs-dist operator list + paintImageXObject + sharp). Wired into executeOperation's switch and the PDF editor dialog UI (4 new sections/toolbar buttons/menu items) with no new IPC channel needed. pdfjs-dist v5 is ESM-only, so it's loaded via dynamic import() of its Node-friendly legacy build; Jest needs --experimental-vm-modules to support that, so the test scripts now set NODE_OPTIONS accordingly via cross-env. Amit Haridas
This commit is contained in:
+153
@@ -3862,6 +3862,38 @@ function showPDFEditorDialog(operation, openedFilePath = null) {
|
||||
if (permInput) permInput.value = openedFilePath;
|
||||
}
|
||||
break;
|
||||
case 'extractText':
|
||||
sectionId = 'pdf-extract-text-section';
|
||||
titleText = 'Extract Text';
|
||||
if (openedFilePath) {
|
||||
const extractTextInput = document.getElementById('extract-text-input-path');
|
||||
if (extractTextInput) extractTextInput.value = openedFilePath;
|
||||
}
|
||||
break;
|
||||
case 'pageNumbers':
|
||||
sectionId = 'pdf-page-numbers-section';
|
||||
titleText = 'Add Page Numbers';
|
||||
if (openedFilePath) {
|
||||
const pageNumbersInput = document.getElementById('page-numbers-input-path');
|
||||
if (pageNumbersInput) pageNumbersInput.value = openedFilePath;
|
||||
}
|
||||
break;
|
||||
case 'crop':
|
||||
sectionId = 'pdf-crop-section';
|
||||
titleText = 'Crop Pages';
|
||||
if (openedFilePath) {
|
||||
const cropInput = document.getElementById('crop-input-path');
|
||||
if (cropInput) cropInput.value = openedFilePath;
|
||||
}
|
||||
break;
|
||||
case 'extractImages':
|
||||
sectionId = 'pdf-extract-images-section';
|
||||
titleText = 'Extract Images';
|
||||
if (openedFilePath) {
|
||||
const extractImagesInput = document.getElementById('extract-images-input-path');
|
||||
if (extractImagesInput) extractImagesInput.value = openedFilePath;
|
||||
}
|
||||
break;
|
||||
}
|
||||
title.textContent = titleText;
|
||||
document.getElementById(sectionId).classList.remove('hidden');
|
||||
@@ -4040,6 +4072,46 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
inputId: 'permissions-output-path',
|
||||
saveDialog: true,
|
||||
},
|
||||
{
|
||||
id: 'browse-extract-text-input',
|
||||
inputId: 'extract-text-input-path',
|
||||
saveDialog: false,
|
||||
},
|
||||
{
|
||||
id: 'browse-extract-text-output',
|
||||
inputId: 'extract-text-output-path',
|
||||
saveDialog: true,
|
||||
},
|
||||
{
|
||||
id: 'browse-page-numbers-input',
|
||||
inputId: 'page-numbers-input-path',
|
||||
saveDialog: false,
|
||||
},
|
||||
{
|
||||
id: 'browse-page-numbers-output',
|
||||
inputId: 'page-numbers-output-path',
|
||||
saveDialog: true,
|
||||
},
|
||||
{
|
||||
id: 'browse-crop-input',
|
||||
inputId: 'crop-input-path',
|
||||
saveDialog: false,
|
||||
},
|
||||
{
|
||||
id: 'browse-crop-output',
|
||||
inputId: 'crop-output-path',
|
||||
saveDialog: true,
|
||||
},
|
||||
{
|
||||
id: 'browse-extract-images-input',
|
||||
inputId: 'extract-images-input-path',
|
||||
saveDialog: false,
|
||||
},
|
||||
{
|
||||
id: 'browse-extract-images-output',
|
||||
inputId: 'extract-images-output-folder',
|
||||
folder: true,
|
||||
},
|
||||
];
|
||||
browseButtons.forEach((button) => {
|
||||
const btn = document.getElementById(button.id);
|
||||
@@ -4151,6 +4223,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
checkbox: 'permissions-overwrite',
|
||||
section: 'permissions-saveas-section',
|
||||
},
|
||||
{
|
||||
checkbox: 'page-numbers-overwrite',
|
||||
section: 'page-numbers-saveas-section',
|
||||
},
|
||||
{
|
||||
checkbox: 'crop-overwrite',
|
||||
section: 'crop-saveas-section',
|
||||
},
|
||||
];
|
||||
overwriteCheckboxes.forEach((item) => {
|
||||
const checkbox = document.getElementById(item.checkbox);
|
||||
@@ -4432,6 +4512,67 @@ function processPDFOperation() {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'extractText':
|
||||
operationData.inputPath = document.getElementById('extract-text-input-path').value.trim();
|
||||
operationData.outputPath = document.getElementById('extract-text-output-path').value.trim();
|
||||
if (!operationData.inputPath || !operationData.outputPath) {
|
||||
showPDFValidationMessage(
|
||||
'Select an input PDF and where to save the extracted text.',
|
||||
'#extract-text-input-path'
|
||||
);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'pageNumbers':
|
||||
operationData.inputPath = document.getElementById('page-numbers-input-path').value.trim();
|
||||
operationData.overwrite = document.getElementById('page-numbers-overwrite').checked;
|
||||
operationData.outputPath = operationData.overwrite
|
||||
? operationData.inputPath
|
||||
: document.getElementById('page-numbers-output-path').value.trim();
|
||||
operationData.position = document.getElementById('page-numbers-position').value;
|
||||
operationData.startNumber =
|
||||
parseInt(document.getElementById('page-numbers-start').value) || 1;
|
||||
if (!operationData.inputPath || !operationData.outputPath) {
|
||||
showPDFValidationMessage(
|
||||
'Select an input PDF' + (operationData.overwrite ? '.' : ' and an output file path.'),
|
||||
'#page-numbers-input-path'
|
||||
);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'crop':
|
||||
operationData.inputPath = document.getElementById('crop-input-path').value.trim();
|
||||
operationData.overwrite = document.getElementById('crop-overwrite').checked;
|
||||
operationData.outputPath = operationData.overwrite
|
||||
? operationData.inputPath
|
||||
: document.getElementById('crop-output-path').value.trim();
|
||||
operationData.margins = {
|
||||
top: parseFloat(document.getElementById('crop-margin-top').value) || 0,
|
||||
bottom: parseFloat(document.getElementById('crop-margin-bottom').value) || 0,
|
||||
left: parseFloat(document.getElementById('crop-margin-left').value) || 0,
|
||||
right: parseFloat(document.getElementById('crop-margin-right').value) || 0,
|
||||
};
|
||||
if (!operationData.inputPath || !operationData.outputPath) {
|
||||
showPDFValidationMessage(
|
||||
'Select an input PDF' + (operationData.overwrite ? '.' : ' and an output file path.'),
|
||||
'#crop-input-path'
|
||||
);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 'extractImages':
|
||||
operationData.inputPath = document.getElementById('extract-images-input-path').value.trim();
|
||||
operationData.outputDir = document
|
||||
.getElementById('extract-images-output-folder')
|
||||
.value.trim();
|
||||
if (!operationData.inputPath || !operationData.outputDir) {
|
||||
showPDFValidationMessage(
|
||||
'Select an input PDF and an output folder.',
|
||||
'#extract-images-input-path'
|
||||
);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
clearPDFStatus();
|
||||
// Show progress
|
||||
@@ -5924,6 +6065,18 @@ document.getElementById('pdf-tb-encrypt')?.addEventListener('click', () => {
|
||||
document.getElementById('pdf-tb-decrypt')?.addEventListener('click', () => {
|
||||
openPdfEditorDialog('decrypt');
|
||||
});
|
||||
document.getElementById('pdf-tb-extract-text')?.addEventListener('click', () => {
|
||||
openPdfEditorDialog('extractText');
|
||||
});
|
||||
document.getElementById('pdf-tb-page-numbers')?.addEventListener('click', () => {
|
||||
openPdfEditorDialog('pageNumbers');
|
||||
});
|
||||
document.getElementById('pdf-tb-crop')?.addEventListener('click', () => {
|
||||
openPdfEditorDialog('crop');
|
||||
});
|
||||
document.getElementById('pdf-tb-extract-images')?.addEventListener('click', () => {
|
||||
openPdfEditorDialog('extractImages');
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// DYNAMIC PANE RESIZER
|
||||
|
||||
Reference in New Issue
Block a user