chore: update image sources and version bump to 1.2.0
- Changed image sources for GDPR, CCPA, and HIPAA compliance logos to local paths. - Updated package version to 1.2.0 in package-lock.json. - Enhanced README and SIMPLE_MODE documentation with Docker usage instructions. - Improved table-of-contents worker and related TypeScript definitions for better clarity and functionality. - Refactored CSS styles for consistency and improved UI elements.
This commit is contained in:
@@ -129,4 +129,3 @@ body {
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
|
||||
@@ -246,10 +246,10 @@ function showInputModal(title, fields = [], defaultValues = {}) {
|
||||
// Store modal references
|
||||
savedModalOverlay = overlay;
|
||||
savedModal = modal;
|
||||
|
||||
|
||||
// Hide modal completely
|
||||
overlay.style.display = 'none';
|
||||
|
||||
|
||||
startDestinationPicking((page, pdfX, pdfY) => {
|
||||
const destPageInput = modal.querySelector('#modal-dest-page');
|
||||
const destXInput = modal.querySelector('#modal-dest-x');
|
||||
@@ -258,10 +258,10 @@ function showInputModal(title, fields = [], defaultValues = {}) {
|
||||
if (destPageInput) destPageInput.value = page;
|
||||
if (destXInput) destXInput.value = Math.round(pdfX);
|
||||
if (destYInput) destYInput.value = Math.round(pdfY);
|
||||
|
||||
|
||||
// Restore modal
|
||||
overlay.style.display = '';
|
||||
|
||||
|
||||
// Update preview to show the destination after a short delay to ensure modal is visible
|
||||
setTimeout(() => {
|
||||
updateDestinationPreview();
|
||||
@@ -269,7 +269,7 @@ function showInputModal(title, fields = [], defaultValues = {}) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Add validation for page input
|
||||
const destPageInput = modal.querySelector('#modal-dest-page');
|
||||
if (destPageInput) {
|
||||
@@ -285,7 +285,7 @@ function showInputModal(title, fields = [], defaultValues = {}) {
|
||||
}
|
||||
updateDestinationPreview();
|
||||
});
|
||||
|
||||
|
||||
destPageInput.addEventListener('blur', (e) => {
|
||||
const value = parseInt(e.target.value);
|
||||
const maxPages = parseInt(e.target.max) || 1;
|
||||
@@ -299,32 +299,34 @@ function showInputModal(title, fields = [], defaultValues = {}) {
|
||||
updateDestinationPreview();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Function to update destination preview
|
||||
function updateDestinationPreview() {
|
||||
if (!pdfJsDoc) return;
|
||||
|
||||
|
||||
const destPageInput = modal.querySelector('#modal-dest-page');
|
||||
const destXInput = modal.querySelector('#modal-dest-x');
|
||||
const destYInput = modal.querySelector('#modal-dest-y');
|
||||
const destZoomSelect = modal.querySelector('#modal-dest-zoom');
|
||||
|
||||
const pageNum = destPageInput ? parseInt(destPageInput.value) : currentPage;
|
||||
|
||||
const pageNum = destPageInput
|
||||
? parseInt(destPageInput.value)
|
||||
: currentPage;
|
||||
const x = destXInput ? parseFloat(destXInput.value) : null;
|
||||
const y = destYInput ? parseFloat(destYInput.value) : null;
|
||||
const zoom = destZoomSelect ? destZoomSelect.value : null;
|
||||
|
||||
|
||||
if (pageNum >= 1 && pageNum <= pdfJsDoc.numPages) {
|
||||
// Render the page with zoom if specified
|
||||
renderPageWithDestination(pageNum, x, y, zoom);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add listeners for X, Y, and zoom changes
|
||||
const destXInput = modal.querySelector('#modal-dest-x');
|
||||
const destYInput = modal.querySelector('#modal-dest-y');
|
||||
const destZoomSelect = modal.querySelector('#modal-dest-zoom');
|
||||
|
||||
|
||||
if (destXInput) {
|
||||
destXInput.addEventListener('input', updateDestinationPreview);
|
||||
}
|
||||
@@ -434,7 +436,7 @@ function cancelDestinationPicking() {
|
||||
destinationMarker.remove();
|
||||
destinationMarker = null;
|
||||
}
|
||||
|
||||
|
||||
// Remove coordinate display
|
||||
const coordDisplay = document.getElementById('destination-coord-display');
|
||||
if (coordDisplay) {
|
||||
@@ -497,20 +499,22 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const page = await pdfJsDoc.getPage(currentPage);
|
||||
viewport = page.getViewport({ scale: currentZoom });
|
||||
}
|
||||
|
||||
|
||||
// Convert canvas pixel coordinates to PDF coordinates
|
||||
// The canvas CSS size matches viewport dimensions, so coordinates map directly
|
||||
// PDF uses bottom-left origin, canvas uses top-left
|
||||
const scaleX = viewport.width / rect.width;
|
||||
const scaleY = viewport.height / rect.height;
|
||||
const pdfX = canvasX * scaleX;
|
||||
const pdfY = viewport.height - (canvasY * scaleY);
|
||||
const pdfY = viewport.height - canvasY * scaleY;
|
||||
|
||||
// Remove old marker and coordinate display
|
||||
if (destinationMarker) {
|
||||
destinationMarker.remove();
|
||||
}
|
||||
const oldCoordDisplay = document.getElementById('destination-coord-display');
|
||||
const oldCoordDisplay = document.getElementById(
|
||||
'destination-coord-display'
|
||||
);
|
||||
if (oldCoordDisplay) {
|
||||
oldCoordDisplay.remove();
|
||||
}
|
||||
@@ -528,16 +532,21 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const canvasRect = canvas.getBoundingClientRect();
|
||||
const wrapperRect = canvasWrapper.getBoundingClientRect();
|
||||
destinationMarker.style.position = 'absolute';
|
||||
destinationMarker.style.left = (canvasX + canvasRect.left - wrapperRect.left) + 'px';
|
||||
destinationMarker.style.top = (canvasY + canvasRect.top - wrapperRect.top) + 'px';
|
||||
destinationMarker.style.left =
|
||||
canvasX + canvasRect.left - wrapperRect.left + 'px';
|
||||
destinationMarker.style.top =
|
||||
canvasY + canvasRect.top - wrapperRect.top + 'px';
|
||||
canvasWrapper.appendChild(destinationMarker);
|
||||
|
||||
|
||||
// Create persistent coordinate display
|
||||
const coordDisplay = document.createElement('div');
|
||||
coordDisplay.id = 'destination-coord-display';
|
||||
coordDisplay.className = 'absolute bg-blue-500 text-white px-2 py-1 rounded text-xs font-mono z-50 pointer-events-none';
|
||||
coordDisplay.style.left = (canvasX + canvasRect.left - wrapperRect.left + 20) + 'px';
|
||||
coordDisplay.style.top = (canvasY + canvasRect.top - wrapperRect.top - 30) + 'px';
|
||||
coordDisplay.className =
|
||||
'absolute bg-blue-500 text-white px-2 py-1 rounded text-xs font-mono z-50 pointer-events-none';
|
||||
coordDisplay.style.left =
|
||||
canvasX + canvasRect.left - wrapperRect.left + 20 + 'px';
|
||||
coordDisplay.style.top =
|
||||
canvasY + canvasRect.top - wrapperRect.top - 30 + 'px';
|
||||
coordDisplay.textContent = `X: ${Math.round(pdfX)}, Y: ${Math.round(pdfY)}`;
|
||||
canvasWrapper.appendChild(coordDisplay);
|
||||
|
||||
@@ -639,6 +648,12 @@ const jsonInput = document.getElementById('json-input');
|
||||
const autoExtractCheckbox = document.getElementById('auto-extract-checkbox');
|
||||
const appEl = document.getElementById('app');
|
||||
const uploaderEl = document.getElementById('uploader');
|
||||
const fileDisplayArea = document.getElementById(
|
||||
'file-display-area'
|
||||
) as HTMLElement;
|
||||
const backToToolsBtn = document.getElementById(
|
||||
'back-to-tools'
|
||||
) as HTMLButtonElement;
|
||||
const canvas = document.getElementById('pdf-canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const pageIndicator = document.getElementById('page-indicator');
|
||||
@@ -974,6 +989,37 @@ collapseAllBtn.addEventListener('click', () => {
|
||||
renderBookmarkTree();
|
||||
});
|
||||
|
||||
// Format bytes helper
|
||||
function formatBytes(bytes: number): string {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// Render file display
|
||||
function renderFileDisplay(file: File) {
|
||||
if (!fileDisplayArea) return;
|
||||
fileDisplayArea.innerHTML = '';
|
||||
fileDisplayArea.classList.remove('hidden');
|
||||
|
||||
const fileDiv = document.createElement('div');
|
||||
fileDiv.className =
|
||||
'flex items-center justify-between bg-gray-700 p-3 rounded-lg text-sm';
|
||||
|
||||
const nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'truncate font-medium text-gray-200';
|
||||
nameSpan.textContent = file.name;
|
||||
|
||||
const sizeSpan = document.createElement('span');
|
||||
sizeSpan.className = 'flex-shrink-0 ml-4 text-gray-400';
|
||||
sizeSpan.textContent = formatBytes(file.size);
|
||||
|
||||
fileDiv.append(nameSpan, sizeSpan);
|
||||
fileDisplayArea.appendChild(fileDiv);
|
||||
}
|
||||
|
||||
fileInput.addEventListener('change', loadPDF);
|
||||
|
||||
async function loadPDF(e) {
|
||||
@@ -982,6 +1028,7 @@ async function loadPDF(e) {
|
||||
|
||||
originalFileName = file.name.replace('.pdf', '');
|
||||
filenameDisplay.textContent = originalFileName;
|
||||
renderFileDisplay(file);
|
||||
const arrayBuffer = await file.arrayBuffer();
|
||||
|
||||
currentPage = 1;
|
||||
@@ -1057,47 +1104,47 @@ async function renderPage(num, zoom = null, destX = null, destY = null) {
|
||||
if (!pdfJsDoc) return;
|
||||
|
||||
const page = await pdfJsDoc.getPage(num);
|
||||
|
||||
|
||||
let zoomScale = currentZoom;
|
||||
if (zoom !== null && zoom !== '' && zoom !== '0') {
|
||||
zoomScale = parseFloat(zoom) / 100;
|
||||
}
|
||||
|
||||
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
|
||||
|
||||
let viewport = page.getViewport({ scale: zoomScale });
|
||||
currentViewport = viewport;
|
||||
|
||||
canvas.height = viewport.height * dpr;
|
||||
canvas.width = viewport.width * dpr;
|
||||
|
||||
|
||||
// Set CSS size to maintain aspect ratio (this is what the browser displays)
|
||||
canvas.style.width = viewport.width + 'px';
|
||||
canvas.style.height = viewport.height + 'px';
|
||||
|
||||
|
||||
// Scale the canvas context to match device pixel ratio
|
||||
ctx.scale(dpr, dpr);
|
||||
|
||||
await page.render({ canvasContext: ctx, viewport: viewport }).promise;
|
||||
|
||||
|
||||
// Draw destination marker if coordinates are provided
|
||||
if (destX !== null && destY !== null) {
|
||||
const canvasX = destX;
|
||||
const canvasY = viewport.height - destY; // Flip Y axis (PDF bottom-left, canvas top-left)
|
||||
|
||||
|
||||
// Draw marker on canvas with animation effect
|
||||
ctx.save();
|
||||
ctx.strokeStyle = '#3b82f6';
|
||||
ctx.fillStyle = '#3b82f6';
|
||||
ctx.lineWidth = 3;
|
||||
|
||||
|
||||
ctx.shadowBlur = 10;
|
||||
ctx.shadowColor = 'rgba(59, 130, 246, 0.5)';
|
||||
ctx.beginPath();
|
||||
ctx.arc(canvasX, canvasY, 12, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
ctx.shadowBlur = 0;
|
||||
|
||||
|
||||
// Draw crosshair
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(canvasX - 15, canvasY);
|
||||
@@ -1105,26 +1152,26 @@ async function renderPage(num, zoom = null, destX = null, destY = null) {
|
||||
ctx.moveTo(canvasX, canvasY - 15);
|
||||
ctx.lineTo(canvasX, canvasY + 15);
|
||||
ctx.stroke();
|
||||
|
||||
|
||||
// Draw inner circle
|
||||
ctx.beginPath();
|
||||
ctx.arc(canvasX, canvasY, 6, 0, 2 * Math.PI);
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
|
||||
|
||||
// Draw coordinate text background
|
||||
const text = `X: ${Math.round(destX)}, Y: ${Math.round(destY)}`;
|
||||
ctx.font = 'bold 12px monospace';
|
||||
const textMetrics = ctx.measureText(text);
|
||||
const textWidth = textMetrics.width;
|
||||
const textHeight = 18;
|
||||
|
||||
|
||||
ctx.fillStyle = 'rgba(59, 130, 246, 0.95)';
|
||||
ctx.fillRect(canvasX + 18, canvasY - 25, textWidth + 10, textHeight);
|
||||
|
||||
|
||||
ctx.fillStyle = 'white';
|
||||
ctx.fillText(text, canvasX + 23, canvasY - 10);
|
||||
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
@@ -1424,8 +1471,13 @@ function createNodeElement(node, level = 0) {
|
||||
// Check if bookmark has a custom destination
|
||||
if (node.destX !== null || node.destY !== null || node.zoom !== null) {
|
||||
// Render page with destination highlighted and zoom applied
|
||||
await renderPageWithDestination(node.page, node.destX, node.destY, node.zoom);
|
||||
|
||||
await renderPageWithDestination(
|
||||
node.page,
|
||||
node.destX,
|
||||
node.destY,
|
||||
node.zoom
|
||||
);
|
||||
|
||||
// Highlight the destination briefly (2 seconds)
|
||||
setTimeout(() => {
|
||||
// Re-render without highlight but keep the zoom if it was set
|
||||
@@ -1769,7 +1821,6 @@ extractExistingBtn.addEventListener('click', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
async function extractExistingBookmarks(doc) {
|
||||
try {
|
||||
const outlines = doc.catalog.lookup(PDFName.of('Outlines'));
|
||||
@@ -1792,7 +1843,9 @@ async function extractExistingBookmarks(doc) {
|
||||
try {
|
||||
function addNamePair(nameObj, destObj) {
|
||||
try {
|
||||
const key = nameObj.decodeText ? nameObj.decodeText() : String(nameObj);
|
||||
const key = nameObj.decodeText
|
||||
? nameObj.decodeText()
|
||||
: String(nameObj);
|
||||
namedDests.set(key, resolveRef(destObj));
|
||||
} catch (_) {
|
||||
// ignore malformed entry
|
||||
@@ -1804,7 +1857,9 @@ async function extractExistingBookmarks(doc) {
|
||||
node = resolveRef(node);
|
||||
if (!node) return;
|
||||
|
||||
const namesArray = node.lookup ? node.lookup(PDFName.of('Names')) : null;
|
||||
const namesArray = node.lookup
|
||||
? node.lookup(PDFName.of('Names'))
|
||||
: null;
|
||||
if (namesArray && namesArray.array) {
|
||||
for (let i = 0; i < namesArray.array.length; i += 2) {
|
||||
const n = namesArray.array[i];
|
||||
@@ -1848,7 +1903,8 @@ async function extractExistingBookmarks(doc) {
|
||||
|
||||
if (pageRef.numberValue !== undefined) {
|
||||
const numericIndex = pageRef.numberValue | 0;
|
||||
if (numericIndex >= 0 && numericIndex < pages.length) return numericIndex;
|
||||
if (numericIndex >= 0 && numericIndex < pages.length)
|
||||
return numericIndex;
|
||||
}
|
||||
|
||||
if (pageRef.objectNumber !== undefined) {
|
||||
@@ -1860,7 +1916,9 @@ async function extractExistingBookmarks(doc) {
|
||||
|
||||
if (pageRef.toString) {
|
||||
const target = pageRef.toString();
|
||||
const idxByString = pages.findIndex((p) => p.ref.toString() === target);
|
||||
const idxByString = pages.findIndex(
|
||||
(p) => p.ref.toString() === target
|
||||
);
|
||||
if (idxByString !== -1) return idxByString;
|
||||
}
|
||||
|
||||
@@ -1884,7 +1942,7 @@ async function extractExistingBookmarks(doc) {
|
||||
|
||||
// Try Dest entry first
|
||||
let dest = item.lookup(PDFName.of('Dest'));
|
||||
|
||||
|
||||
// If no Dest, try Action/D
|
||||
if (!dest) {
|
||||
const action = resolveRef(item.lookup(PDFName.of('A')));
|
||||
@@ -1902,7 +1960,10 @@ async function extractExistingBookmarks(doc) {
|
||||
} else if (dest.lookup) {
|
||||
// Some named destinations resolve to a dictionary with 'D' entry
|
||||
const maybeDict = resolveRef(dest);
|
||||
const dictD = maybeDict && maybeDict.lookup ? maybeDict.lookup(PDFName.of('D')) : null;
|
||||
const dictD =
|
||||
maybeDict && maybeDict.lookup
|
||||
? maybeDict.lookup(PDFName.of('D'))
|
||||
: null;
|
||||
if (dictD) dest = resolveRef(dictD);
|
||||
}
|
||||
} catch (_) {
|
||||
@@ -1975,7 +2036,7 @@ async function extractExistingBookmarks(doc) {
|
||||
style,
|
||||
destX,
|
||||
destY,
|
||||
zoom
|
||||
zoom,
|
||||
};
|
||||
|
||||
// Process children (make sure to resolve refs)
|
||||
@@ -1986,7 +2047,6 @@ async function extractExistingBookmarks(doc) {
|
||||
child = resolveRef(child.lookup(PDFName.of('Next')));
|
||||
}
|
||||
|
||||
|
||||
if (pageIndex === 0 && bookmark.children.length > 0) {
|
||||
const firstChild = bookmark.children[0];
|
||||
if (firstChild) {
|
||||
@@ -2015,6 +2075,13 @@ async function extractExistingBookmarks(doc) {
|
||||
}
|
||||
}
|
||||
|
||||
// Back to tools button
|
||||
if (backToToolsBtn) {
|
||||
backToToolsBtn.addEventListener('click', () => {
|
||||
window.location.href = '../../index.html#tools-header';
|
||||
});
|
||||
}
|
||||
|
||||
downloadBtn.addEventListener('click', async () => {
|
||||
const pages = pdfLibDoc.getPages();
|
||||
const outlinesDict = pdfLibDoc.context.obj({});
|
||||
|
||||
@@ -2,17 +2,29 @@ const worker = new Worker('/workers/table-of-contents.worker.js');
|
||||
|
||||
let pdfFile: File | null = null;
|
||||
|
||||
// Get DOM elements
|
||||
const dropZone = document.getElementById('drop-zone') as HTMLElement;
|
||||
const fileInput = document.getElementById('file-input') as HTMLInputElement;
|
||||
const generateBtn = document.getElementById('generate-btn') as HTMLButtonElement;
|
||||
const generateBtn = document.getElementById(
|
||||
'generate-btn'
|
||||
) as HTMLButtonElement;
|
||||
const tocTitleInput = document.getElementById('toc-title') as HTMLInputElement;
|
||||
const fontSizeSelect = document.getElementById('font-size') as HTMLSelectElement;
|
||||
const fontFamilySelect = document.getElementById('font-family') as HTMLSelectElement;
|
||||
const addBookmarkCheckbox = document.getElementById('add-bookmark') as HTMLInputElement;
|
||||
const fontSizeSelect = document.getElementById(
|
||||
'font-size'
|
||||
) as HTMLSelectElement;
|
||||
const fontFamilySelect = document.getElementById(
|
||||
'font-family'
|
||||
) as HTMLSelectElement;
|
||||
const addBookmarkCheckbox = document.getElementById(
|
||||
'add-bookmark'
|
||||
) as HTMLInputElement;
|
||||
const statusMessage = document.getElementById('status-message') as HTMLElement;
|
||||
const fileDisplayArea = document.getElementById(
|
||||
'file-display-area'
|
||||
) as HTMLElement;
|
||||
const backToToolsBtn = document.getElementById(
|
||||
'back-to-tools'
|
||||
) as HTMLButtonElement;
|
||||
|
||||
// Type definitions for the worker messages
|
||||
interface GenerateTOCMessage {
|
||||
command: 'generate-toc';
|
||||
pdfData: ArrayBuffer;
|
||||
@@ -35,14 +47,17 @@ interface TOCErrorResponse {
|
||||
type TOCWorkerResponse = TOCSuccessResponse | TOCErrorResponse;
|
||||
|
||||
// Show status message
|
||||
function showStatus(message: string, type: 'success' | 'error' | 'info' = 'info') {
|
||||
function showStatus(
|
||||
message: string,
|
||||
type: 'success' | 'error' | 'info' = 'info'
|
||||
) {
|
||||
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'
|
||||
? 'bg-red-900 text-red-200'
|
||||
: 'bg-blue-900 text-blue-200'
|
||||
}`;
|
||||
statusMessage.classList.remove('hidden');
|
||||
}
|
||||
@@ -52,6 +67,36 @@ function hideStatus() {
|
||||
statusMessage.classList.add('hidden');
|
||||
}
|
||||
|
||||
// Format bytes helper
|
||||
function formatBytes(bytes: number): string {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
// Render file display
|
||||
function renderFileDisplay(file: File) {
|
||||
fileDisplayArea.innerHTML = '';
|
||||
fileDisplayArea.classList.remove('hidden');
|
||||
|
||||
const fileDiv = document.createElement('div');
|
||||
fileDiv.className =
|
||||
'flex items-center justify-between bg-gray-700 p-3 rounded-lg text-sm';
|
||||
|
||||
const nameSpan = document.createElement('span');
|
||||
nameSpan.className = 'truncate font-medium text-gray-200';
|
||||
nameSpan.textContent = file.name;
|
||||
|
||||
const sizeSpan = document.createElement('span');
|
||||
sizeSpan.className = 'flex-shrink-0 ml-4 text-gray-400';
|
||||
sizeSpan.textContent = formatBytes(file.size);
|
||||
|
||||
fileDiv.append(nameSpan, sizeSpan);
|
||||
fileDisplayArea.appendChild(fileDiv);
|
||||
}
|
||||
|
||||
// Handle file selection
|
||||
function handleFileSelect(file: File) {
|
||||
if (file.type !== 'application/pdf') {
|
||||
@@ -61,6 +106,7 @@ function handleFileSelect(file: File) {
|
||||
|
||||
pdfFile = file;
|
||||
generateBtn.disabled = false;
|
||||
renderFileDisplay(file);
|
||||
showStatus(`File selected: ${file.name}`, 'success');
|
||||
}
|
||||
|
||||
@@ -103,7 +149,7 @@ async function generateTableOfContents() {
|
||||
|
||||
const arrayBuffer = await pdfFile.arrayBuffer();
|
||||
|
||||
showStatus('Offloading table of contents generation to background Worker...', 'info');
|
||||
showStatus('Generating table of contents...', 'info');
|
||||
|
||||
const title = tocTitleInput.value || 'Table of Contents';
|
||||
const fontSize = parseInt(fontSizeSelect.value, 10);
|
||||
@@ -142,20 +188,24 @@ worker.onmessage = (e: MessageEvent<TOCWorkerResponse>) => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = pdfFile?.name.replace('.pdf', '_with_toc.pdf') || 'output_with_toc.pdf';
|
||||
a.download =
|
||||
pdfFile?.name.replace('.pdf', '_with_toc.pdf') || 'output_with_toc.pdf';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
showStatus('Table of contents generated successfully! Download started.', 'success');
|
||||
showStatus(
|
||||
'Table of contents generated successfully! Download started.',
|
||||
'success'
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
hideStatus();
|
||||
pdfFile = null;
|
||||
fileInput.value = '';
|
||||
generateBtn.disabled = true;
|
||||
}, 3000);
|
||||
hideStatus();
|
||||
pdfFile = null;
|
||||
fileInput.value = '';
|
||||
fileDisplayArea.innerHTML = '';
|
||||
fileDisplayArea.classList.add('hidden');
|
||||
generateBtn.disabled = true;
|
||||
} else if (e.data.status === 'error') {
|
||||
const errorMessage = e.data.message || 'Unknown error occurred in worker.';
|
||||
console.error('Worker Error:', errorMessage);
|
||||
@@ -169,4 +219,11 @@ worker.onerror = (error) => {
|
||||
generateBtn.disabled = false;
|
||||
};
|
||||
|
||||
generateBtn.addEventListener('click', generateTableOfContents);
|
||||
// Back to tools button
|
||||
if (backToToolsBtn) {
|
||||
backToToolsBtn.addEventListener('click', () => {
|
||||
window.location.href = '../../index.html#tools-header';
|
||||
});
|
||||
}
|
||||
|
||||
generateBtn.addEventListener('click', generateTableOfContents);
|
||||
|
||||
@@ -103,8 +103,15 @@
|
||||
class="min-h-screen flex items-center justify-center p-4 bg-gray-900"
|
||||
>
|
||||
<div
|
||||
class="bg-gray-900 rounded-xl shadow-xl p-8 max-w-md w-full text-gray-200"
|
||||
class="bg-gray-800 rounded-xl shadow-xl p-8 max-w-md w-full text-gray-200 border border-gray-700"
|
||||
>
|
||||
<button
|
||||
id="back-to-tools"
|
||||
class="flex items-center gap-2 text-indigo-400 hover:text-indigo-300 mb-6 font-semibold"
|
||||
>
|
||||
<i data-lucide="arrow-left" class="cursor-pointer"></i>
|
||||
<span class="cursor-pointer"> Back to Tools </span>
|
||||
</button>
|
||||
<p class="text-gray-400 mb-6">
|
||||
Upload a PDF to begin editing bookmarks
|
||||
</p>
|
||||
@@ -112,7 +119,7 @@
|
||||
<!-- Drop Zone for Main PDF Upload -->
|
||||
<div
|
||||
id="drop-zone"
|
||||
class="relative flex flex-col items-center justify-center w-full h-48 border-2 border-dashed border-gray-600 rounded-xl cursor-pointer bg-gray-800 hover:bg-gray-700 transition-colors duration-300"
|
||||
class="relative flex flex-col items-center justify-center w-full h-48 border-2 border-dashed border-gray-600 rounded-xl cursor-pointer bg-gray-700 hover:bg-gray-600 transition-colors duration-300"
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center pt-5 pb-6">
|
||||
<i
|
||||
@@ -136,6 +143,9 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- File Display Area -->
|
||||
<div id="file-display-area" class="mt-4 hidden"></div>
|
||||
|
||||
<!-- Auto Extract Checkbox -->
|
||||
<div class="mt-4">
|
||||
<label class="flex items-center gap-2 text-sm text-gray-300">
|
||||
@@ -163,10 +173,7 @@
|
||||
class="relative flex flex-col items-center justify-center w-full h-32 mt-2 border-2 border-dashed border-gray-600 rounded-xl cursor-pointer bg-gray-800 hover:bg-gray-700 transition-colors duration-300"
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center pt-4 pb-4">
|
||||
<i
|
||||
data-lucide="sheet"
|
||||
class="w-8 h-8 mb-2 text-gray-400"
|
||||
></i>
|
||||
<i data-lucide="sheet" class="w-8 h-8 mb-2 text-gray-400"></i>
|
||||
<p class="text-xs text-gray-400">
|
||||
<span class="font-semibold">Click to upload CSV</span> or drag
|
||||
here
|
||||
@@ -207,7 +214,9 @@
|
||||
</div>
|
||||
|
||||
<div id="app" class="hidden bg-gray-900 min-h-screen">
|
||||
<header class="bg-gray-800 border-b border-gray-700 shadow-sm sticky top-0 z-50">
|
||||
<header
|
||||
class="bg-gray-800 border-b border-gray-700 shadow-sm sticky top-0 z-50"
|
||||
>
|
||||
<div class="max-w-7xl mx-auto px-4 py-3">
|
||||
<div class="flex items-center justify-between flex-wrap gap-2">
|
||||
<h1 class="text-xl font-bold text-white" id="filename-display">
|
||||
|
||||
@@ -1,241 +1,369 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Generate Table of Contents - BentoPDF</title>
|
||||
<link rel="icon" type="image/png" href="../../images/favicon.svg" />
|
||||
<link href="../../src/css/styles.css" rel="stylesheet" />
|
||||
</head>
|
||||
</head>
|
||||
|
||||
<body class="antialiased bg-gray-900">
|
||||
<body class="antialiased bg-gray-900">
|
||||
<nav class="bg-gray-800 border-b border-gray-700 sticky top-0 z-30">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="flex justify-between items-center h-16">
|
||||
<div class="flex-shrink-0 flex items-center cursor-pointer" id="home-logo">
|
||||
<img src="../../public/images/favicon.svg" alt="Bento PDF Logo" class="h-8 w-8" />
|
||||
<span class="text-white font-bold text-xl ml-2">
|
||||
<a href="../../index.html">BentoPDF</a>
|
||||
</span>
|
||||
</div>
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="flex justify-between items-center h-16">
|
||||
<div
|
||||
class="flex-shrink-0 flex items-center cursor-pointer"
|
||||
id="home-logo"
|
||||
>
|
||||
<img
|
||||
src="../../public/images/favicon.svg"
|
||||
alt="Bento PDF Logo"
|
||||
class="h-8 w-8"
|
||||
/>
|
||||
<span class="text-white font-bold text-xl ml-2">
|
||||
<a href="../../index.html">BentoPDF</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Desktop Navigation -->
|
||||
<div class="hidden md:flex items-center space-x-8 text-white">
|
||||
<a href="../../index.html" class="nav-link">Home</a>
|
||||
<a href="../../about.html" class="nav-link">About</a>
|
||||
<a href="../../contact.html" class="nav-link">Contact</a>
|
||||
<a href="../../index.html#tools-header" class="nav-link">All Tools</a>
|
||||
</div>
|
||||
<!-- Desktop Navigation -->
|
||||
<div class="hidden md:flex items-center space-x-8 text-white">
|
||||
<a href="../../index.html" class="nav-link">Home</a>
|
||||
<a href="../../about.html" class="nav-link">About</a>
|
||||
<a href="../../contact.html" class="nav-link">Contact</a>
|
||||
<a href="../../index.html#tools-header" class="nav-link"
|
||||
>All Tools</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Hamburger Button -->
|
||||
<div class="md:hidden flex items-center">
|
||||
<button id="mobile-menu-button" type="button"
|
||||
class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500 transition-colors"
|
||||
aria-controls="mobile-menu" aria-expanded="false">
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<!-- Hamburger Icon -->
|
||||
<svg id="menu-icon" class="block h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
<!-- Close Icon -->
|
||||
<svg id="close-icon" class="hidden h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none"
|
||||
viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Mobile Hamburger Button -->
|
||||
<div class="md:hidden flex items-center">
|
||||
<button
|
||||
id="mobile-menu-button"
|
||||
type="button"
|
||||
class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500 transition-colors"
|
||||
aria-controls="mobile-menu"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="sr-only">Open main menu</span>
|
||||
<!-- Hamburger Icon -->
|
||||
<svg
|
||||
id="menu-icon"
|
||||
class="block h-6 w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4 6h16M4 12h16M4 18h16"
|
||||
/>
|
||||
</svg>
|
||||
<!-- Close Icon -->
|
||||
<svg
|
||||
id="close-icon"
|
||||
class="hidden h-6 w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Dropdown -->
|
||||
<div id="mobile-menu" class="hidden md:hidden bg-gray-800 border-t border-gray-700">
|
||||
<div class="px-2 pt-2 pb-3 space-y-1 text-center">
|
||||
<a href="../../index.html" class="mobile-nav-link">Home</a>
|
||||
<a href="../../about.html" class="mobile-nav-link">About</a>
|
||||
<a href="../../contact.html" class="mobile-nav-link">Contact</a>
|
||||
<a href="../../index.html#tools-header" class="mobile-nav-link">All Tools</a>
|
||||
</div>
|
||||
<!-- Mobile Menu Dropdown -->
|
||||
<div
|
||||
id="mobile-menu"
|
||||
class="hidden md:hidden bg-gray-800 border-t border-gray-700"
|
||||
>
|
||||
<div class="px-2 pt-2 pb-3 space-y-1 text-center">
|
||||
<a href="../../index.html" class="mobile-nav-link">Home</a>
|
||||
<a href="../../about.html" class="mobile-nav-link">About</a>
|
||||
<a href="../../contact.html" class="mobile-nav-link">Contact</a>
|
||||
<a href="../../index.html#tools-header" class="mobile-nav-link"
|
||||
>All Tools</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="uploader" class="min-h-screen flex items-center justify-center p-4 bg-gray-900">
|
||||
<div class="bg-gray-900 rounded-xl shadow-xl p-8 max-w-2xl w-full text-gray-200">
|
||||
<h1 class="text-2xl font-bold text-white mb-2">
|
||||
Generate Table of Contents
|
||||
</h1>
|
||||
<p class="text-gray-400 mb-6">
|
||||
Upload a PDF with bookmarks to generate a table of contents page
|
||||
<div
|
||||
id="uploader"
|
||||
class="min-h-screen flex items-center justify-center p-4 bg-gray-900"
|
||||
>
|
||||
<div
|
||||
class="bg-gray-800 rounded-xl shadow-xl p-8 max-w-2xl w-full text-gray-200 border border-gray-700"
|
||||
>
|
||||
<button
|
||||
id="back-to-tools"
|
||||
class="flex items-center gap-2 text-indigo-400 hover:text-indigo-300 mb-6 font-semibold"
|
||||
>
|
||||
<i data-lucide="arrow-left" class="cursor-pointer"></i>
|
||||
<span class="cursor-pointer"> Back to Tools </span>
|
||||
</button>
|
||||
<h1 class="text-2xl font-bold text-white mb-2">
|
||||
Generate Table of Contents
|
||||
</h1>
|
||||
<p class="text-gray-400 mb-6">
|
||||
Upload a PDF with bookmarks to generate a table of contents page
|
||||
</p>
|
||||
|
||||
<!-- Drop Zone for Main PDF Upload -->
|
||||
<div
|
||||
id="drop-zone"
|
||||
class="relative flex flex-col items-center justify-center w-full h-48 border-2 border-dashed border-gray-600 rounded-xl cursor-pointer bg-gray-700 hover:bg-gray-600 transition-colors duration-300"
|
||||
>
|
||||
<div class="flex flex-col items-center justify-center pt-5 pb-6">
|
||||
<i
|
||||
data-lucide="upload-cloud"
|
||||
class="w-10 h-10 mb-3 text-gray-400"
|
||||
></i>
|
||||
<p class="mb-2 text-sm text-gray-300">
|
||||
<span class="font-semibold">Click to select a file</span> or drag
|
||||
and drop
|
||||
</p>
|
||||
|
||||
<!-- Drop Zone for Main PDF Upload -->
|
||||
<div id="drop-zone"
|
||||
class="relative flex flex-col items-center justify-center w-full h-48 border-2 border-dashed border-gray-600 rounded-xl cursor-pointer bg-gray-800 hover:bg-gray-700 transition-colors duration-300">
|
||||
<div class="flex flex-col items-center justify-center pt-5 pb-6">
|
||||
<i data-lucide="upload-cloud" class="w-10 h-10 mb-3 text-gray-400"></i>
|
||||
<p class="mb-2 text-sm text-gray-300">
|
||||
<span class="font-semibold">Click to select a file</span> or drag
|
||||
and drop
|
||||
</p>
|
||||
<p class="text-xs text-gray-500">A single PDF file</p>
|
||||
<p class="text-xs text-gray-500">
|
||||
Your files never leave your device.
|
||||
</p>
|
||||
</div>
|
||||
<input type="file" id="file-input" accept=".pdf"
|
||||
class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer" />
|
||||
</div>
|
||||
|
||||
<!-- Options Section -->
|
||||
<div class="mt-6 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
TOC Title
|
||||
</label>
|
||||
<input type="text" id="toc-title" value="Table of Contents"
|
||||
class="w-full px-4 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="Table of Contents" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
Font Size
|
||||
</label>
|
||||
<select id="font-size"
|
||||
class="w-full px-4 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<option value="10">10pt</option>
|
||||
<option value="11">11pt</option>
|
||||
<option value="12" selected>12pt</option>
|
||||
<option value="14">14pt</option>
|
||||
<option value="16">16pt</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
Font Family
|
||||
</label>
|
||||
<select id="font-family"
|
||||
class="w-full px-4 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500">
|
||||
<option value="0">Times Roman</option>
|
||||
<option value="1">Times Bold</option>
|
||||
<option value="2">Times Italic</option>
|
||||
<option value="3">Times Bold Italic</option>
|
||||
<option value="4" selected>Helvetica</option>
|
||||
<option value="5">Helvetica Bold</option>
|
||||
<option value="6">Helvetica Oblique</option>
|
||||
<option value="7">Helvetica Bold Oblique</option>
|
||||
<option value="8">Courier</option>
|
||||
<option value="9">Courier Bold</option>
|
||||
<option value="10">Courier Oblique</option>
|
||||
<option value="11">Courier Bold Oblique</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="checkbox" id="add-bookmark" checked class="w-4 h-4 accent-blue-500" />
|
||||
<label for="add-bookmark" class="text-sm text-gray-300">
|
||||
Add bookmark for TOC page
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Generate Button -->
|
||||
<button id="generate-btn" disabled
|
||||
class="mt-6 w-full px-4 py-3 bg-gradient-to-b from-blue-600 to-blue-700 hover:from-blue-700 hover:to-blue-800 disabled:from-gray-600 disabled:to-gray-700 disabled:cursor-not-allowed text-white rounded-lg font-medium flex items-center justify-center gap-2 focus:ring-2 focus:ring-blue-400 transition duration-200">
|
||||
<i data-lucide="file-text" class="w-5 h-5"></i>
|
||||
<span>Generate Table of Contents</span>
|
||||
</button>
|
||||
|
||||
<!-- Status Message -->
|
||||
<div id="status-message" class="mt-4 hidden p-3 rounded-lg text-sm"></div>
|
||||
<p class="text-xs text-gray-500">A single PDF file</p>
|
||||
<p class="text-xs text-gray-500">
|
||||
Your files never leave your device.
|
||||
</p>
|
||||
</div>
|
||||
<input
|
||||
type="file"
|
||||
id="file-input"
|
||||
accept=".pdf"
|
||||
class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- File Display Area -->
|
||||
<div id="file-display-area" class="mt-4 hidden"></div>
|
||||
|
||||
<!-- Options Section -->
|
||||
<div class="mt-6 space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
TOC Title
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id="toc-title"
|
||||
value="Table of Contents"
|
||||
class="w-full px-4 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
placeholder="Table of Contents"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
Font Size
|
||||
</label>
|
||||
<select
|
||||
id="font-size"
|
||||
class="w-full px-4 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="10">10pt</option>
|
||||
<option value="11">11pt</option>
|
||||
<option value="12" selected>12pt</option>
|
||||
<option value="14">14pt</option>
|
||||
<option value="16">16pt</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-300 mb-2">
|
||||
Font Family
|
||||
</label>
|
||||
<select
|
||||
id="font-family"
|
||||
class="w-full px-4 py-2 bg-gray-800 border border-gray-600 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
>
|
||||
<option value="0">Times Roman</option>
|
||||
<option value="1">Times Bold</option>
|
||||
<option value="2">Times Italic</option>
|
||||
<option value="3">Times Bold Italic</option>
|
||||
<option value="4" selected>Helvetica</option>
|
||||
<option value="5">Helvetica Bold</option>
|
||||
<option value="6">Helvetica Oblique</option>
|
||||
<option value="7">Helvetica Bold Oblique</option>
|
||||
<option value="8">Courier</option>
|
||||
<option value="9">Courier Bold</option>
|
||||
<option value="10">Courier Oblique</option>
|
||||
<option value="11">Courier Bold Oblique</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="add-bookmark"
|
||||
checked
|
||||
class="w-4 h-4 accent-blue-500"
|
||||
/>
|
||||
<label for="add-bookmark" class="text-sm text-gray-300">
|
||||
Add bookmark for TOC page
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Generate Button -->
|
||||
<button id="generate-btn" disabled class="btn-gradient w-full mt-6">
|
||||
<span id="generate-btn-text">Generate Table of Contents</span>
|
||||
</button>
|
||||
|
||||
<!-- Status Message -->
|
||||
<div
|
||||
id="status-message"
|
||||
class="mt-4 hidden p-3 rounded-lg text-sm"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="mt-16 border-t-2 border-gray-700 py-8 bg-[#111827]">
|
||||
<div class="container mx-auto px-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-4 gap-8 text-center md:text-left">
|
||||
<div class="mb-8 md:mb-0">
|
||||
<div class="flex items-center justify-center md:justify-start mb-4">
|
||||
<img src="../../public/images/favicon.svg" alt="Bento PDF Logo" class="h-10 w-10 mr-3" />
|
||||
<span class="text-xl font-bold text-white">BentoPDF</span>
|
||||
</div>
|
||||
<p class="text-gray-400 text-sm">
|
||||
© 2025 BentoPDF. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-bold text-white mb-4">Company</h3>
|
||||
<ul class="space-y-2 text-gray-400">
|
||||
<li>
|
||||
<a href="../../about.html" class="hover:text-indigo-400">About Us</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../../faq.html" class="hover:text-indigo-400">FAQ</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../../contact.html" class="hover:text-indigo-400">Contact Us</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-bold text-white mb-4">Legal</h3>
|
||||
<ul class="space-y-2 text-gray-400">
|
||||
<li>
|
||||
<a href="../../terms.html" class="hover:text-indigo-400">Terms and Conditions</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../../privacy.html" class="hover:text-indigo-400">Privacy Policy</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-bold text-white mb-4">Follow Us</h3>
|
||||
<div class="flex justify-center md:justify-start space-x-4">
|
||||
<a href="https://github.com/alam00000/bentopdf" target="_blank" rel="noopener noreferrer"
|
||||
class="text-gray-400 hover:text-indigo-400" title="GitHub">
|
||||
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path fill-rule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
||||
clip-rule="evenodd" />
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://discord.gg/q42xWQmJ" target="_blank" rel="noopener noreferrer"
|
||||
class="text-gray-400 hover:text-indigo-400" title="Discord">
|
||||
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028a14.09 14.09 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418z" />
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://www.instagram.com/thebentopdf/" class="text-gray-400 hover:text-indigo-400"
|
||||
title="Instagram">
|
||||
<i data-lucide="instagram"></i>
|
||||
</a>
|
||||
<a href="https://www.linkedin.com/company/bentopdf/" class="text-gray-400 hover:text-indigo-400"
|
||||
title="LinkedIn">
|
||||
<i data-lucide="linkedin"></i>
|
||||
</a>
|
||||
<a href="https://x.com/BentoPDF" class="text-gray-400 hover:text-indigo-400"
|
||||
title="X (Twitter)">
|
||||
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path
|
||||
d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z" />
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container mx-auto px-4">
|
||||
<div
|
||||
class="grid grid-cols-1 md:grid-cols-4 gap-8 text-center md:text-left"
|
||||
>
|
||||
<div class="mb-8 md:mb-0">
|
||||
<div class="flex items-center justify-center md:justify-start mb-4">
|
||||
<img
|
||||
src="../../public/images/favicon.svg"
|
||||
alt="Bento PDF Logo"
|
||||
class="h-10 w-10 mr-3"
|
||||
/>
|
||||
<span class="text-xl font-bold text-white">BentoPDF</span>
|
||||
</div>
|
||||
<p class="text-gray-400 text-sm">
|
||||
© 2025 BentoPDF. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-bold text-white mb-4">Company</h3>
|
||||
<ul class="space-y-2 text-gray-400">
|
||||
<li>
|
||||
<a href="../../about.html" class="hover:text-indigo-400"
|
||||
>About Us</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../../faq.html" class="hover:text-indigo-400">FAQ</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../../contact.html" class="hover:text-indigo-400"
|
||||
>Contact Us</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-bold text-white mb-4">Legal</h3>
|
||||
<ul class="space-y-2 text-gray-400">
|
||||
<li>
|
||||
<a href="../../terms.html" class="hover:text-indigo-400"
|
||||
>Terms and Conditions</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../../privacy.html" class="hover:text-indigo-400"
|
||||
>Privacy Policy</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-bold text-white mb-4">Follow Us</h3>
|
||||
<div class="flex justify-center md:justify-start space-x-4">
|
||||
<a
|
||||
href="https://github.com/alam00000/bentopdf"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-gray-400 hover:text-indigo-400"
|
||||
title="GitHub"
|
||||
>
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="https://discord.gg/q42xWQmJ"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-gray-400 hover:text-indigo-400"
|
||||
title="Discord"
|
||||
>
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028a14.09 14.09 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418z"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.instagram.com/thebentopdf/"
|
||||
class="text-gray-400 hover:text-indigo-400"
|
||||
title="Instagram"
|
||||
>
|
||||
<i data-lucide="instagram"></i>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.linkedin.com/company/bentopdf/"
|
||||
class="text-gray-400 hover:text-indigo-400"
|
||||
title="LinkedIn"
|
||||
>
|
||||
<i data-lucide="linkedin"></i>
|
||||
</a>
|
||||
<a
|
||||
href="https://x.com/BentoPDF"
|
||||
class="text-gray-400 hover:text-indigo-400"
|
||||
title="X (Twitter)"
|
||||
>
|
||||
<svg
|
||||
class="w-6 h-6"
|
||||
fill="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script type="module" src="../js/utils/lucide-init.ts"></script>
|
||||
<script type="module" src="../js/logic/table-of-contents.ts"></script>
|
||||
<script type="module" src="../js/mobileMenu.ts"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
72
src/types/coherentpdf.global.d.ts
vendored
72
src/types/coherentpdf.global.d.ts
vendored
@@ -19,8 +19,37 @@ declare global {
|
||||
// --- Type Aliases from Constants ---
|
||||
type CpdfPermission = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
type CpdfEncryptionMethod = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
||||
type CpdfPaperSize = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15;
|
||||
type CpdfPositionAnchor = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
|
||||
type CpdfPaperSize =
|
||||
| 0
|
||||
| 1
|
||||
| 2
|
||||
| 3
|
||||
| 4
|
||||
| 5
|
||||
| 6
|
||||
| 7
|
||||
| 8
|
||||
| 9
|
||||
| 10
|
||||
| 11
|
||||
| 12
|
||||
| 13
|
||||
| 14
|
||||
| 15;
|
||||
type CpdfPositionAnchor =
|
||||
| 0
|
||||
| 1
|
||||
| 2
|
||||
| 3
|
||||
| 4
|
||||
| 5
|
||||
| 6
|
||||
| 7
|
||||
| 8
|
||||
| 9
|
||||
| 10
|
||||
| 11
|
||||
| 12;
|
||||
type CpdfFont = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11;
|
||||
type CpdfJustification = 0 | 1 | 2;
|
||||
type CpdfLayout = 0 | 1 | 2 | 3 | 4 | 5;
|
||||
@@ -36,25 +65,25 @@ declare const coherentpdf: {
|
||||
* @returns The version number.
|
||||
*/
|
||||
version(): string;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the global operation mode to 'fast'. The default is 'slow' mode, which works
|
||||
* even on old-fashioned files.
|
||||
*/
|
||||
setFast(): void;
|
||||
|
||||
|
||||
/**
|
||||
* Sets the global operation mode to 'slow'. The default is 'slow' mode, which works
|
||||
* even on old-fashioned files.
|
||||
*/
|
||||
setSlow(): void;
|
||||
|
||||
|
||||
/**
|
||||
* Delete a PDF so the memory representing it may be recovered. Must be called for every loaded PDF.
|
||||
* @param pdf PDF document to delete.
|
||||
*/
|
||||
deletePdf(pdf: CoherentPdf): void;
|
||||
|
||||
|
||||
/**
|
||||
* A debug function which prints some information about resource usage.
|
||||
* Can be used to detect if PDFs or ranges are being deallocated properly.
|
||||
@@ -69,7 +98,7 @@ declare const coherentpdf: {
|
||||
* @returns The loaded PDF document instance.
|
||||
*/
|
||||
fromFile(filename: string, userpw: string): CoherentPdf;
|
||||
|
||||
|
||||
/**
|
||||
* Loads a PDF from a file, doing only minimal parsing (lazily).
|
||||
* @param filename File name.
|
||||
@@ -77,7 +106,7 @@ declare const coherentpdf: {
|
||||
* @returns The loaded PDF document instance.
|
||||
*/
|
||||
fromFileLazy(filename: string, userpw: string): CoherentPdf;
|
||||
|
||||
|
||||
/**
|
||||
* Loads a file from memory given any user password.
|
||||
* @param data PDF document as an array of bytes.
|
||||
@@ -85,7 +114,7 @@ declare const coherentpdf: {
|
||||
* @returns The loaded PDF document instance.
|
||||
*/
|
||||
fromMemory(data: Uint8Array, userpw: string): CoherentPdf;
|
||||
|
||||
|
||||
/**
|
||||
* Loads a file from memory, but lazily (minimal parsing).
|
||||
* @param data PDF document as an array of bytes.
|
||||
@@ -93,7 +122,7 @@ declare const coherentpdf: {
|
||||
* @returns The loaded PDF document instance.
|
||||
*/
|
||||
fromMemoryLazy(data: Uint8Array, userpw: string): CoherentPdf;
|
||||
|
||||
|
||||
/**
|
||||
* Writes the PDF document to memory as a byte array.
|
||||
* @param pdf The PDF document.
|
||||
@@ -102,7 +131,7 @@ declare const coherentpdf: {
|
||||
* @returns The PDF document as a byte array.
|
||||
*/
|
||||
toMemory(pdf: CoherentPdf, linearize: boolean, make_id: boolean): Uint8Array;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the total number of pages in the PDF document.
|
||||
* @param pdf The PDF document.
|
||||
@@ -116,20 +145,20 @@ declare const coherentpdf: {
|
||||
* @param pdf The PDF document.
|
||||
*/
|
||||
startGetBookmarkInfo(pdf: CoherentPdf): void;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the total number of bookmarks available after calling `startGetBookmarkInfo`.
|
||||
* @returns The number of bookmarks.
|
||||
*/
|
||||
numberBookmarks(): number;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the nesting level (0-based) for the bookmark at index `n`.
|
||||
* @param n The bookmark index (0-based).
|
||||
* @returns The bookmark level.
|
||||
*/
|
||||
getBookmarkLevel(n: number): number;
|
||||
|
||||
|
||||
/**
|
||||
* Gets the target page number (1-based) for the bookmark at index `n`.
|
||||
* @param pdf The PDF document.
|
||||
@@ -137,21 +166,21 @@ declare const coherentpdf: {
|
||||
* @returns The target page number.
|
||||
*/
|
||||
getBookmarkPage(pdf: CoherentPdf, n: number): number;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the text title of the bookmark at index `n`.
|
||||
* @param n The bookmark index (0-based).
|
||||
* @returns The bookmark text.
|
||||
*/
|
||||
getBookmarkText(n: number): string;
|
||||
|
||||
|
||||
/**
|
||||
* Returns the open/closed status for the bookmark at index `n`.
|
||||
* @param n The bookmark index (0-based).
|
||||
* @returns True if the bookmark is open.
|
||||
*/
|
||||
getBookmarkOpenStatus(n: number): boolean;
|
||||
|
||||
|
||||
/**
|
||||
* Ends the bookmark retrieval process and cleans up resources.
|
||||
*/
|
||||
@@ -165,7 +194,13 @@ declare const coherentpdf: {
|
||||
* @param title The title for the TOC page.
|
||||
* @param bookmark If true, the TOC page itself gets a bookmark.
|
||||
*/
|
||||
tableOfContents(pdf: CoherentPdf, font: CpdfFont, fontsize: number, title: string, bookmark: boolean): void;
|
||||
tableOfContents(
|
||||
pdf: CoherentPdf,
|
||||
font: CpdfFont,
|
||||
fontsize: number,
|
||||
title: string,
|
||||
bookmark: boolean
|
||||
): void;
|
||||
|
||||
/** Times Roman font constant (0) */
|
||||
readonly timesRoman: CpdfFont;
|
||||
@@ -194,4 +229,3 @@ declare const coherentpdf: {
|
||||
};
|
||||
|
||||
export { coherentpdf };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user