mirror of
https://github.com/alam00000/bentopdf.git
synced 2025-12-23 22:28:49 -05:00
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:
@@ -110,11 +110,13 @@ You can also watch the video on how to set it up 👉
|
||||
[BentoPDF Docker Setup](https://drive.google.com/file/d/1C4eJ2nqeaH__1Tlad-xuBHaF2Ha4fSBf/view?usp=drive_link)
|
||||
|
||||
**Using Docker Hub:**
|
||||
|
||||
```bash
|
||||
docker run -p 3000:8080 bentopdf/bentopdf:latest
|
||||
```
|
||||
|
||||
**Using GitHub Container Registry:**
|
||||
|
||||
```bash
|
||||
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf:latest
|
||||
```
|
||||
@@ -182,11 +184,13 @@ For detailed security configuration, see [SECURITY.md](SECURITY.md).
|
||||
BentoPDF supports semantic versioning with multiple Docker tags available on both Docker Hub and GitHub Container Registry:
|
||||
|
||||
**Docker Hub:**
|
||||
|
||||
- **Latest**: `bentopdf/bentopdf:latest`
|
||||
- **Specific Version**: `bentopdf/bentopdf:1.0.0`
|
||||
- **Version with Prefix**: `bentopdf/bentopdf:v1.0.0`
|
||||
|
||||
**GitHub Container Registry:**
|
||||
|
||||
- **Latest**: `ghcr.io/alam00000/bentopdf:latest`
|
||||
- **Specific Version**: `ghcr.io/alam00000/bentopdf:1.0.0`
|
||||
- **Version with Prefix**: `ghcr.io/alam00000/bentopdf:v1.0.0`
|
||||
|
||||
@@ -24,14 +24,17 @@ When enabled, Simple Mode will:
|
||||
Use the pre-built Simple Mode image directly:
|
||||
|
||||
**Using Docker Hub:**
|
||||
|
||||
```bash
|
||||
docker run -p 3000:8080 bentopdf/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
**Using GitHub Container Registry:**
|
||||
|
||||
```bash
|
||||
docker run -p 3000:8080 ghcr.io/alam00000/bentopdf-simple:latest
|
||||
```
|
||||
|
||||
Or with Docker Compose:
|
||||
|
||||
```yaml
|
||||
@@ -151,20 +154,24 @@ When Simple Mode is working correctly, you should see:
|
||||
### Normal Mode (Full Branding)
|
||||
|
||||
**Docker Hub:**
|
||||
|
||||
- `bentopdf/bentopdf:latest`
|
||||
- `bentopdf/bentopdf:v1.0.0` (versioned)
|
||||
|
||||
**GitHub Container Registry:**
|
||||
|
||||
- `ghcr.io/alam00000/bentopdf:latest`
|
||||
- `ghcr.io/alam00000/bentopdf:v1.0.0` (versioned)
|
||||
|
||||
### Simple Mode (Clean Interface)
|
||||
|
||||
**Docker Hub:**
|
||||
|
||||
- `bentopdf/bentopdf-simple:latest`
|
||||
- `bentopdf/bentopdf-simple:v1.0.0` (versioned)
|
||||
|
||||
**GitHub Container Registry:**
|
||||
|
||||
- `ghcr.io/alam00000/bentopdf-simple:latest`
|
||||
- `ghcr.io/alam00000/bentopdf-simple:v1.0.0` (versioned)
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
class="w-20 h-20 md:w-24 md:h-24 rounded-full bg-blue-600 flex items-center justify-center mb-4"
|
||||
>
|
||||
<img
|
||||
src="https://mkt-cf.pdffiller.com/mrk/350/images/_global/logos/security-badges/logo-gdpr.svg"
|
||||
src="/images/gdpr.svg"
|
||||
alt="GDPR compliance"
|
||||
class="w-full h-full"
|
||||
/>
|
||||
@@ -396,7 +396,7 @@
|
||||
class="w-20 h-20 md:w-24 md:h-24 rounded-full bg-blue-600 flex items-center justify-center mb-4"
|
||||
>
|
||||
<img
|
||||
src="https://mkt-cf.pdffiller.com/mrk/350/images/_global/logos/security-badges/logo-ccpa.svg"
|
||||
src="/images/ccpa.svg"
|
||||
alt="CCPA compliance"
|
||||
class="w-full h-full"
|
||||
/>
|
||||
@@ -417,7 +417,7 @@
|
||||
class="w-20 h-20 md:w-24 md:h-24 rounded-full bg-blue-600 flex items-center justify-center mb-4"
|
||||
>
|
||||
<img
|
||||
src="https://mkt-cf.pdffiller.com/mrk/350/images/_global/logos/security-badges/logo-hipaa.svg"
|
||||
src="/images/hipaa.svg"
|
||||
alt="HIPAA compliance"
|
||||
class="w-full h-full"
|
||||
/>
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "bento-pdf",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "bento-pdf",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@fontsource/cedarville-cursive": "^5.2.7",
|
||||
|
||||
6
public/images/ccpa.svg
Normal file
6
public/images/ccpa.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 26C0 11.6406 11.6406 0 26 0C40.3594 0 52 11.6406 52 26C52 40.3594 40.3594 52 26 52C11.6406 52 0 40.3594 0 26Z" fill="#0C4776"/>
|
||||
<path d="M26 46C37.0457 46 46 37.0457 46 26C46 14.9543 37.0457 6 26 6C14.9543 6 6 14.9543 6 26C6 37.0457 14.9543 46 26 46Z" fill="white" stroke="#007A7F" stroke-width="2"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.9398 16.7218L24.2279 18.9983L31.7583 11.5073L34.6951 14.4291L24.2279 24.8407L19.043 19.683L21.9398 16.7218Z" fill="#007A7F"/>
|
||||
<path d="M18.1522 35.9595C19.0079 35.9595 19.6653 35.8029 19.9574 35.6568L19.7174 34.4151C19.4044 34.5403 18.8722 34.6447 18.3922 34.6447C16.9731 34.6447 16.1383 33.7577 16.1383 32.349C16.1383 30.7838 17.1192 30.0221 18.3818 30.0221C18.9453 30.0221 19.394 30.1473 19.7174 30.2829L20.0409 29.0203C19.7592 28.8742 19.1331 28.7073 18.3087 28.7073C16.1801 28.7073 14.4688 30.0429 14.4688 32.4429C14.4688 34.4464 15.7209 35.9595 18.1522 35.9595ZM23.8989 35.9595C24.7545 35.9595 25.4119 35.8029 25.7041 35.6568L25.4641 34.4151C25.1511 34.5403 24.6189 34.6447 24.1389 34.6447C22.7198 34.6447 21.885 33.7577 21.885 32.349C21.885 30.7838 22.8658 30.0221 24.1285 30.0221C24.6919 30.0221 25.1406 30.1473 25.4641 30.2829L25.7876 29.0203C25.5059 28.8742 24.8798 28.7073 24.0554 28.7073C21.9267 28.7073 20.2154 30.0429 20.2154 32.4429C20.2154 34.4464 21.4676 35.9595 23.8989 35.9595ZM28.1743 35.8551V33.3403C28.3203 33.3612 28.5082 33.3716 28.7169 33.3716C29.656 33.3716 30.4595 33.1421 31.0021 32.6308C31.4195 32.2342 31.649 31.6499 31.649 30.9612C31.649 30.2725 31.3464 29.6881 30.8977 29.3334C30.4282 28.9577 29.729 28.7699 28.7482 28.7699C27.7777 28.7699 27.089 28.8325 26.5986 28.916V35.8551H28.1743ZM28.7064 32.1508C28.4769 32.1508 28.3099 32.1403 28.1743 32.109V30.0325C28.289 30.0012 28.5082 29.9699 28.8316 29.9699C29.6247 29.9699 30.0734 30.356 30.0734 31.0029C30.0734 31.7229 29.5516 32.1508 28.7064 32.1508ZM33.0235 35.8551L33.5244 34.0499H35.5383L36.0809 35.8551H37.7922L35.6114 28.8221H33.5244L31.3748 35.8551H33.0235ZM35.3087 32.8603H33.754L34.1714 31.3681C34.2861 30.9612 34.3905 30.429 34.4948 30.0116H34.5157C34.6201 30.429 34.7453 30.9508 34.8705 31.3681L35.3087 32.8603Z" fill="#0C4776"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
5
public/images/gdpr.svg
Normal file
5
public/images/gdpr.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg width="52" height="52" viewBox="0 0 52 52" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 26C0 11.6406 11.6406 0 26 0C40.3594 0 52 11.6406 52 26C52 40.3594 40.3594 52 26 52C11.6406 52 0 40.3594 0 26Z" fill="#0047BD"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.3149 11.2327L26.0039 9.98946L27.693 11.2327L27.0509 9.23793L28.733 7.99473H26.653L26.0109 6L25.3618 7.99473H23.2749L24.957 9.23793L24.3149 11.2327ZM34.6372 12.3214L32.9412 13.5646L33.5903 11.5698L31.9012 10.3266H33.9951L34.6372 8.33189L35.2863 10.3266H37.3593L35.6842 11.5698L36.3263 13.5646L34.6372 12.3214ZM39.2647 19.928L40.9607 18.6918L42.6498 19.928L42.0076 17.9332L43.6827 16.6971H41.6098L40.9607 14.7023L40.3186 16.6971H38.2247L39.9138 17.9332L39.2647 19.928ZM7.03298 28.6093L8.72902 27.3731L10.4181 28.6093L9.77596 26.6216L11.4511 25.3784H9.37812L8.736 23.3836L8.0869 25.3784H6L7.68208 26.6216L7.03298 28.6093ZM9.34325 37.3046L11.0393 36.0684L12.7283 37.3046L12.0862 35.3099L13.7683 34.0737H11.6884L11.0393 32.079L10.3972 34.0737H8.31027L9.99235 35.3099L9.34325 37.3046ZM17.3702 42.4249L15.6811 43.6681L16.3233 41.6734L14.6412 40.4302H16.7281L17.3772 38.4355L18.0193 40.4302H20.0992L18.4171 41.6734L19.0593 43.6681L17.3702 42.4249ZM24.3149 46L26.0039 44.7568L27.693 46L27.0509 44.0052L28.733 42.7691H26.653L26.0109 40.7743L25.3618 42.7691H23.2749L24.957 44.0052L24.3149 46ZM34.6372 42.4249L32.9412 43.6681L33.5903 41.6734L31.9012 40.4302H33.9951L34.6372 38.4355L35.2863 40.4302H37.3593L35.6842 41.6734L36.3263 43.6681L34.6372 42.4249ZM39.2647 37.3046L40.9607 36.0684L42.6498 37.3046L42.0076 35.3099L43.6827 34.0737H41.6098L40.9607 32.079L40.3186 34.0737H38.2247L39.9138 35.3099L39.2647 37.3046ZM43.2779 27.3731L41.5819 28.6093L42.231 26.6216L40.5489 25.3784H42.6358L43.2849 23.3836L43.927 25.3784H46L44.3249 26.6216L44.967 28.6093L43.2779 27.3731ZM17.3702 12.3214L15.6811 13.5646L16.3233 11.5698L14.6412 10.3266H16.7281L17.3772 8.33189L18.0193 10.3266H20.0992L18.4171 11.5698L19.0593 13.5646L17.3702 12.3214ZM9.34325 19.928L11.0393 18.6918L12.7283 19.928L12.0862 17.9332L13.7683 16.6971H11.6884L11.0393 14.7023L10.3972 16.6971H8.31027L9.99235 17.9332L9.34325 19.928Z" fill="#FFCC00"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.691 26.8367V25.8391H21.2723V28.0538C21.2723 29.1711 20.6973 29.8096 19.5883 29.8096C18.4793 29.8096 17.9043 29.1711 17.9043 28.0538V24.4225C17.9043 23.3052 18.4793 22.6667 19.5883 22.6667C20.6973 22.6667 21.2723 23.3052 21.2723 24.4225V25.1009H20.2044V24.3527C20.2044 23.8539 19.9785 23.6644 19.6191 23.6644C19.2597 23.6644 19.0338 23.8539 19.0338 24.3527V28.1237C19.0338 28.6225 19.2597 28.802 19.6191 28.802C19.9785 28.802 20.2044 28.6225 20.2044 28.1237V26.8367H19.691ZM22.3165 29.7297V22.7465H24.1032C25.2327 22.7465 25.7872 23.355 25.7872 24.4724V28.0039C25.7872 29.1212 25.2327 29.7297 24.1032 29.7297H22.3165ZM24.0832 23.7441H23.4465V28.7321H24.0832C24.4425 28.7321 24.6582 28.5526 24.6582 28.0538V24.4225C24.6582 23.9237 24.4425 23.7441 24.0832 23.7441ZM28.4852 22.7465C29.6147 22.7465 30.1692 23.355 30.1692 24.4724V25.3802C30.1692 26.4975 29.6147 27.106 28.4852 27.106H27.9512V29.7297H26.8217V22.7465H28.4852ZM28.4848 23.7441H27.9508V26.1084H28.4848C28.8441 26.1084 29.0392 25.9488 29.0392 25.45V24.4025C29.0392 23.9037 28.8441 23.7441 28.4848 23.7441ZM34.5708 29.7297H33.4208C33.3592 29.5502 33.3181 29.4404 33.3181 28.8718V27.7744C33.3181 27.126 33.0922 26.8866 32.5788 26.8866H32.1886V29.7297H31.0591V22.7465H32.7636C33.9342 22.7465 34.4373 23.2752 34.4373 24.3526V24.9013C34.4373 25.6196 34.2012 26.0885 33.698 26.3179C34.2628 26.5474 34.4476 27.0761 34.4476 27.8044V28.8818C34.4476 29.221 34.4579 29.4704 34.5708 29.7297ZM32.7333 23.7441H32.1891V25.889H32.6306C33.0516 25.889 33.3083 25.7094 33.3083 25.1507V24.4624C33.3083 23.9636 33.1338 23.7441 32.7333 23.7441Z" fill="white"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
9
public/images/hipaa.svg
Normal file
9
public/images/hipaa.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 11 KiB |
4
public/workers/table-of-contents.worker.d.ts
vendored
4
public/workers/table-of-contents.worker.d.ts
vendored
@@ -1,4 +1,4 @@
|
||||
declare const coherentpdf: typeof import("../../src/types/coherentpdf.global").coherentpdf;
|
||||
declare const coherentpdf: typeof import('../../src/types/coherentpdf.global').coherentpdf;
|
||||
|
||||
interface GenerateTOCMessage {
|
||||
command: 'generate-toc';
|
||||
@@ -19,4 +19,4 @@ interface TOCErrorResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
type TOCResponse = TOCSuccessResponse | TOCErrorResponse;
|
||||
type TOCResponse = TOCSuccessResponse | TOCErrorResponse;
|
||||
|
||||
@@ -19,7 +19,8 @@ function generateTableOfContentsInWorker(
|
||||
coherentpdf.deletePdf(pdf);
|
||||
self.postMessage({
|
||||
status: 'error',
|
||||
message: 'This PDF does not have any bookmarks. Please add bookmarks first using the Bookmark tool.',
|
||||
message:
|
||||
'This PDF does not have any bookmarks. Please add bookmarks first using the Bookmark tool.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -39,7 +40,10 @@ function generateTableOfContentsInWorker(
|
||||
} catch (error) {
|
||||
self.postMessage({
|
||||
status: 'error',
|
||||
message: error instanceof Error ? error.message : 'Unknown error occurred during table of contents generation.',
|
||||
message:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: 'Unknown error occurred during table of contents generation.',
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -54,4 +58,4 @@ self.onmessage = (e) => {
|
||||
e.data.addBookmark
|
||||
);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
@@ -44,7 +44,10 @@ export default defineConfig(({ mode }) => ({
|
||||
privacy: resolve(__dirname, 'privacy.html'),
|
||||
terms: resolve(__dirname, 'terms.html'),
|
||||
bookmark: resolve(__dirname, 'src/pages/bookmark.html'),
|
||||
'table-of-contents': resolve(__dirname, 'src/pages/table-of-contents.html'),
|
||||
'table-of-contents': resolve(
|
||||
__dirname,
|
||||
'src/pages/table-of-contents.html'
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -64,4 +67,4 @@ export default defineConfig(({ mode }) => ({
|
||||
],
|
||||
},
|
||||
},
|
||||
}));
|
||||
}));
|
||||
|
||||
Reference in New Issue
Block a user