mirror of
https://github.com/alam00000/bentopdf.git
synced 2026-11-05 20:02:47 +01:00
Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
900196879f | ||
|
|
bd985f06f8 | ||
|
|
98467f4809 | ||
|
|
151060204d | ||
|
|
3019195fe5 | ||
|
|
3673b6e434 | ||
|
|
ad915b7527 | ||
|
|
bbafb11246 | ||
|
|
e33e79ef4d | ||
|
|
1e557d5b2a |
@@ -20,7 +20,7 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: "20"
|
node-version: "20"
|
||||||
|
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ The easiest way to self-host is to download the pre-built distribution file from
|
|||||||
**Navigate to the extracted Folder:**
|
**Navigate to the extracted Folder:**
|
||||||
```bash
|
```bash
|
||||||
# Navigate to the extracted folder
|
# Navigate to the extracted folder
|
||||||
cd dist-1.6.2 # Replace with your version
|
cd dist-1.7.3 # Replace with your version
|
||||||
```
|
```
|
||||||
|
|
||||||
**Serve with Python:**
|
**Serve with Python:**
|
||||||
|
|||||||
+2
-2
@@ -261,7 +261,7 @@
|
|||||||
© 2025 BentoPDF. All rights reserved.
|
© 2025 BentoPDF. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-500 text-xs mt-2">
|
<p class="text-gray-500 text-xs mt-2">
|
||||||
Version <span id="app-version">1.6.2</span>
|
Version <span id="app-version"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -331,7 +331,7 @@
|
|||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
||||||
|
<script type="module" src="src/version.ts"></script>
|
||||||
<script type="module" src="src/js/mobileMenu.ts"></script>
|
<script type="module" src="src/js/mobileMenu.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+2
-2
@@ -133,7 +133,7 @@
|
|||||||
© 2025 BentoPDF. All rights reserved.
|
© 2025 BentoPDF. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-500 text-xs mt-2">
|
<p class="text-gray-500 text-xs mt-2">
|
||||||
Version <span id="app-version">1.6.2</span>
|
Version <span id="app-version"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -203,7 +203,7 @@
|
|||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
||||||
|
<script type="module" src="src/version.ts"></script>
|
||||||
<script type="module" src="src/js/mobileMenu.ts"></script>
|
<script type="module" src="src/js/mobileMenu.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -319,7 +319,7 @@
|
|||||||
© 2025 BentoPDF. All rights reserved.
|
© 2025 BentoPDF. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-500 text-xs mt-2">
|
<p class="text-gray-500 text-xs mt-2">
|
||||||
Version <span id="app-version">1.6.2</span>
|
Version <span id="app-version"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -389,7 +389,7 @@
|
|||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
||||||
|
<script type="module" src="src/version.ts"></script>
|
||||||
<script type="module" src="src/js/mobileMenu.ts"></script>
|
<script type="module" src="src/js/mobileMenu.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+2
-1
@@ -594,7 +594,7 @@
|
|||||||
© 2025 BentoPDF. All rights reserved.
|
© 2025 BentoPDF. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-500 text-xs mt-2">
|
<p class="text-gray-500 text-xs mt-2">
|
||||||
Version <span id="app-version">1.6.2</span>
|
Version <span id="app-version"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -664,6 +664,7 @@
|
|||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
||||||
|
<script type="module" src="src/version.ts"></script>
|
||||||
<script type="module" src="src/js/main.ts"></script>
|
<script type="module" src="src/js/main.ts"></script>
|
||||||
<script type="module" src="src/js/mobileMenu.ts"></script>
|
<script type="module" src="src/js/mobileMenu.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -34,5 +34,7 @@ http {
|
|||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
add_header X-XSS-Protection "1; mode=block" always;
|
add_header X-XSS-Protection "1; mode=block" always;
|
||||||
|
add_header Cross-Origin-Opener-Policy "same-origin" always;
|
||||||
|
add_header Cross-Origin-Resource-Policy "cross-origin" always;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "bento-pdf",
|
"name": "bento-pdf",
|
||||||
"version": "1.6.2",
|
"version": "1.7.3",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "bento-pdf",
|
"name": "bento-pdf",
|
||||||
"version": "1.6.2",
|
"version": "1.7.3",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource/cedarville-cursive": "^5.2.7",
|
"@fontsource/cedarville-cursive": "^5.2.7",
|
||||||
|
|||||||
+4
-2
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "bento-pdf",
|
"name": "bento-pdf",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.7.1",
|
"version": "1.7.5",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -16,10 +16,12 @@
|
|||||||
"test:watch": "vitest watch",
|
"test:watch": "vitest watch",
|
||||||
"build:docker": "vite build",
|
"build:docker": "vite build",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
|
"update-version": "node scripts/update-version.js",
|
||||||
"release": "node scripts/release.js patch",
|
"release": "node scripts/release.js patch",
|
||||||
"release:minor": "node scripts/release.js minor",
|
"release:minor": "node scripts/release.js minor",
|
||||||
"release:major": "node scripts/release.js major",
|
"release:major": "node scripts/release.js major",
|
||||||
"serve:simple": "SIMPLE_MODE=true npm run build && npx serve dist -p 3000",
|
"serve:simple": "SIMPLE_MODE=true npm run build && npm run preview -- --port 3000",
|
||||||
|
"serve": "npm run build && npm run preview -- --port 3000",
|
||||||
"package": "node scripts/package-dist.js"
|
"package": "node scripts/package-dist.js"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
+2
-2
@@ -244,7 +244,7 @@
|
|||||||
© 2025 BentoPDF. All rights reserved.
|
© 2025 BentoPDF. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-500 text-xs mt-2">
|
<p class="text-gray-500 text-xs mt-2">
|
||||||
Version <span id="app-version">1.6.2</span>
|
Version <span id="app-version"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -313,7 +313,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
||||||
|
<script type="module" src="src/version.ts"></script>
|
||||||
<script type="module" src="src/js/mobileMenu.ts"></script>
|
<script type="module" src="src/js/mobileMenu.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -12372,7 +12372,10 @@ const PDFViewerApplication = {
|
|||||||
let file;
|
let file;
|
||||||
const queryString = document.location.search.substring(1);
|
const queryString = document.location.search.substring(1);
|
||||||
const params = parseQueryString(queryString);
|
const params = parseQueryString(queryString);
|
||||||
file = params.get("file") ?? AppOptions.get("defaultUrl");
|
file = params.get("file");
|
||||||
|
if (!file) {
|
||||||
|
file = "";
|
||||||
|
}
|
||||||
validateFileURL(file);
|
validateFileURL(file);
|
||||||
const fileInput = this._openFileInput = document.createElement("input");
|
const fileInput = this._openFileInput = document.createElement("input");
|
||||||
fileInput.id = "fileInput";
|
fileInput.id = "fileInput";
|
||||||
|
|||||||
@@ -207,7 +207,7 @@
|
|||||||
viewer: document.getElementById('viewer'),
|
viewer: document.getElementById('viewer'),
|
||||||
eventBus,
|
eventBus,
|
||||||
linkService,
|
linkService,
|
||||||
annotationEditorMode: 0,
|
annotationEditorMode: 1,
|
||||||
enableScripting: true,
|
enableScripting: true,
|
||||||
renderer: 'canvas'
|
renderer: 'canvas'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1512,7 +1512,7 @@ class BaseExternalServices {
|
|||||||
class BasePreferences {
|
class BasePreferences {
|
||||||
#defaults = Object.freeze({
|
#defaults = Object.freeze({
|
||||||
altTextLearnMoreUrl: "",
|
altTextLearnMoreUrl: "",
|
||||||
annotationEditorMode: 0,
|
annotationEditorMode: 1,
|
||||||
annotationMode: 2,
|
annotationMode: 2,
|
||||||
capCanvasAreaFactor: 200,
|
capCanvasAreaFactor: 200,
|
||||||
commentLearnMoreUrl: "",
|
commentLearnMoreUrl: "",
|
||||||
@@ -16429,7 +16429,10 @@ const PDFViewerApplication = {
|
|||||||
let file;
|
let file;
|
||||||
const queryString = document.location.search.substring(1);
|
const queryString = document.location.search.substring(1);
|
||||||
const params = parseQueryString(queryString);
|
const params = parseQueryString(queryString);
|
||||||
file = params.get("file") ?? AppOptions.get("defaultUrl");
|
file = params.get("file");
|
||||||
|
if (!file) {
|
||||||
|
file = "";
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
file = new URL(decodeURIComponent(file)).href;
|
file = new URL(decodeURIComponent(file)).href;
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
declare const coherentpdf: typeof import('../../src/types/coherentpdf.global').coherentpdf;
|
||||||
|
|
||||||
|
interface AddAttachmentsMessage {
|
||||||
|
command: 'add-attachments';
|
||||||
|
pdfBuffer: ArrayBuffer;
|
||||||
|
attachmentBuffers: ArrayBuffer[];
|
||||||
|
attachmentNames: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddAttachmentsSuccessResponse {
|
||||||
|
status: 'success';
|
||||||
|
modifiedPDF: ArrayBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AddAttachmentsErrorResponse {
|
||||||
|
status: 'error';
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type AddAttachmentsResponse = AddAttachmentsSuccessResponse | AddAttachmentsErrorResponse;
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
self.importScripts('/coherentpdf.browser.min.js');
|
||||||
|
|
||||||
|
function parsePageRange(rangeString, totalPages) {
|
||||||
|
const pages = new Set();
|
||||||
|
const parts = rangeString.split(',').map(s => s.trim());
|
||||||
|
|
||||||
|
for (const part of parts) {
|
||||||
|
if (part.includes('-')) {
|
||||||
|
const [start, end] = part.split('-').map(s => parseInt(s.trim(), 10));
|
||||||
|
if (isNaN(start) || isNaN(end)) continue;
|
||||||
|
for (let i = Math.max(1, start); i <= Math.min(totalPages, end); i++) {
|
||||||
|
pages.add(i);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const pageNum = parseInt(part, 10);
|
||||||
|
if (!isNaN(pageNum) && pageNum >= 1 && pageNum <= totalPages) {
|
||||||
|
pages.add(pageNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(pages).sort((a, b) => a - b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addAttachmentsToPDFInWorker(pdfBuffer, attachmentBuffers, attachmentNames, attachmentLevel, pageRange) {
|
||||||
|
try {
|
||||||
|
const uint8Array = new Uint8Array(pdfBuffer);
|
||||||
|
|
||||||
|
let pdf;
|
||||||
|
try {
|
||||||
|
pdf = coherentpdf.fromMemory(uint8Array, '');
|
||||||
|
} catch (error) {
|
||||||
|
const errorMsg = error.message || error.toString();
|
||||||
|
|
||||||
|
if (errorMsg.includes('Failed to read PDF') ||
|
||||||
|
errorMsg.includes('Could not read object') ||
|
||||||
|
errorMsg.includes('No /Root entry') ||
|
||||||
|
errorMsg.includes('PDFError')) {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: 'The PDF file has structural issues and cannot be processed. The file may be corrupted, incomplete, or created with non-standard tools. Please try:\n\n• Opening and re-saving the PDF in another PDF viewer\n• Using a different PDF file\n• Repairing the PDF with a PDF repair tool'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: `Failed to load PDF: ${errorMsg}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalPages = coherentpdf.pages(pdf);
|
||||||
|
|
||||||
|
let targetPages = [];
|
||||||
|
if (attachmentLevel === 'page') {
|
||||||
|
if (!pageRange) {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: 'Page range is required for page-level attachments.'
|
||||||
|
});
|
||||||
|
coherentpdf.deletePdf(pdf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
targetPages = parsePageRange(pageRange, totalPages);
|
||||||
|
if (targetPages.length === 0) {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: 'Invalid page range specified.'
|
||||||
|
});
|
||||||
|
coherentpdf.deletePdf(pdf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < attachmentBuffers.length; i++) {
|
||||||
|
try {
|
||||||
|
const attachmentData = new Uint8Array(attachmentBuffers[i]);
|
||||||
|
const attachmentName = attachmentNames[i];
|
||||||
|
|
||||||
|
if (attachmentLevel === 'document') {
|
||||||
|
coherentpdf.attachFileFromMemory(attachmentData, attachmentName, pdf);
|
||||||
|
} else {
|
||||||
|
for (const pageNum of targetPages) {
|
||||||
|
coherentpdf.attachFileToPageFromMemory(attachmentData, attachmentName, pdf, pageNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`Failed to attach file ${attachmentNames[i]}:`, error);
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: `Failed to attach file ${attachmentNames[i]}: ${error.message || error}`
|
||||||
|
});
|
||||||
|
coherentpdf.deletePdf(pdf);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the modified PDF
|
||||||
|
const modifiedBytes = coherentpdf.toMemory(pdf, false, false);
|
||||||
|
coherentpdf.deletePdf(pdf);
|
||||||
|
|
||||||
|
const buffer = modifiedBytes.buffer.slice(
|
||||||
|
modifiedBytes.byteOffset,
|
||||||
|
modifiedBytes.byteOffset + modifiedBytes.byteLength
|
||||||
|
);
|
||||||
|
|
||||||
|
self.postMessage({
|
||||||
|
status: 'success',
|
||||||
|
modifiedPDF: buffer
|
||||||
|
}, [buffer]);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
self.postMessage({
|
||||||
|
status: 'error',
|
||||||
|
message: error instanceof Error
|
||||||
|
? error.message
|
||||||
|
: 'Unknown error occurred while adding attachments.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.onmessage = (e) => {
|
||||||
|
if (e.data.command === 'add-attachments') {
|
||||||
|
addAttachmentsToPDFInWorker(
|
||||||
|
e.data.pdfBuffer,
|
||||||
|
e.data.attachmentBuffers,
|
||||||
|
e.data.attachmentNames,
|
||||||
|
e.data.attachmentLevel || 'document',
|
||||||
|
e.data.pageRange || ''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
+9
-5
@@ -75,20 +75,24 @@ function main() {
|
|||||||
const newVersion = updateVersion(type);
|
const newVersion = updateVersion(type);
|
||||||
console.log(`📦 Updated version to ${newVersion}`);
|
console.log(`📦 Updated version to ${newVersion}`);
|
||||||
|
|
||||||
// 2. Add and commit changes
|
// 2. Update version in HTML files
|
||||||
execSync('git add package.json', { stdio: 'inherit' });
|
console.log(`📝 Updating version in HTML files...`);
|
||||||
|
execSync('npm run update-version', { stdio: 'inherit' });
|
||||||
|
|
||||||
|
// 3. Add and commit changes
|
||||||
|
execSync('git add package.json *.html src/pages/*.html', { stdio: 'inherit' });
|
||||||
execSync(`git commit -m "Release v${newVersion}"`, { stdio: 'inherit' });
|
execSync(`git commit -m "Release v${newVersion}"`, { stdio: 'inherit' });
|
||||||
console.log(`💾 Committed version change`);
|
console.log(`💾 Committed version change`);
|
||||||
|
|
||||||
// 3. Create git tag
|
// 4. Create git tag
|
||||||
const tagName = createGitTag(newVersion);
|
const tagName = createGitTag(newVersion);
|
||||||
|
|
||||||
// 4. Build and package the distribution files
|
// 5. Build and package the distribution files
|
||||||
console.log(`📦 Building and packaging distribution files...`);
|
console.log(`📦 Building and packaging distribution files...`);
|
||||||
execSync('npm run package', { stdio: 'inherit' });
|
execSync('npm run package', { stdio: 'inherit' });
|
||||||
console.log(`📦 Distribution files packaged successfully`);
|
console.log(`📦 Distribution files packaged successfully`);
|
||||||
|
|
||||||
// 5. Push everything to main
|
// 6. Push everything to main
|
||||||
console.log(`📤 Pushing to main...`);
|
console.log(`📤 Pushing to main...`);
|
||||||
execSync('git push origin main', { stdio: 'inherit' });
|
execSync('git push origin main', { stdio: 'inherit' });
|
||||||
execSync(`git push origin ${tagName}`, { stdio: 'inherit' });
|
execSync(`git push origin ${tagName}`, { stdio: 'inherit' });
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Script to update version numbers in HTML files from package.json
|
||||||
|
* Run this script whenever you need to sync HTML versions with package.json
|
||||||
|
*/
|
||||||
|
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
// Read version from package.json
|
||||||
|
const packageJson = JSON.parse(
|
||||||
|
fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8')
|
||||||
|
);
|
||||||
|
const version = packageJson.version;
|
||||||
|
|
||||||
|
// HTML files to update
|
||||||
|
const htmlFiles = [
|
||||||
|
'index.html',
|
||||||
|
'about.html',
|
||||||
|
'contact.html',
|
||||||
|
'faq.html',
|
||||||
|
'privacy.html',
|
||||||
|
'terms.html',
|
||||||
|
'src/pages/add-stamps.html',
|
||||||
|
'src/pages/bookmark.html',
|
||||||
|
'src/pages/json-to-pdf.html',
|
||||||
|
'src/pages/pdf-multi-tool.html',
|
||||||
|
'src/pages/pdf-to-json.html',
|
||||||
|
'src/pages/table-of-contents.html',
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log(`Updating version to ${version} in HTML files...`);
|
||||||
|
|
||||||
|
let updatedCount = 0;
|
||||||
|
|
||||||
|
htmlFiles.forEach((file) => {
|
||||||
|
const filePath = path.join(__dirname, '..', file);
|
||||||
|
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
console.log(`⚠️ Skipping ${file} (not found)`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = fs.readFileSync(filePath, 'utf8');
|
||||||
|
|
||||||
|
// Replace version in <span id="app-version">X.X.X</span>
|
||||||
|
const regex = /(<span id="app-version">)[^<]+(<\/span>)/g;
|
||||||
|
const newContent = content.replace(regex, `$1${version}$2`);
|
||||||
|
|
||||||
|
if (content !== newContent) {
|
||||||
|
fs.writeFileSync(filePath, newContent, 'utf8');
|
||||||
|
console.log(`✓ Updated ${file}`);
|
||||||
|
updatedCount++;
|
||||||
|
} else {
|
||||||
|
console.log(`- ${file} (already up to date)`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`\nDone! Updated ${updatedCount} file(s) to version ${version}`);
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"source": "**/*",
|
||||||
|
"headers": [
|
||||||
|
{
|
||||||
|
"key": "Cross-Origin-Opener-Policy",
|
||||||
|
"value": "same-origin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "Cross-Origin-Embedder-Policy",
|
||||||
|
"value": "require-corp"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "X-Frame-Options",
|
||||||
|
"value": "SAMEORIGIN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "X-Content-Type-Options",
|
||||||
|
"value": "nosniff"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+114
-27
@@ -1,10 +1,40 @@
|
|||||||
import { showLoader, hideLoader, showAlert } from '../ui';
|
import { showLoader, hideLoader, showAlert } from '../ui';
|
||||||
import { readFileAsArrayBuffer, downloadFile } from '../utils/helpers';
|
import { readFileAsArrayBuffer, downloadFile } from '../utils/helpers';
|
||||||
import { state } from '../state';
|
import { state } from '../state';
|
||||||
|
|
||||||
|
const worker = new Worker('/workers/add-attachments.worker.js');
|
||||||
|
|
||||||
let attachments: File[] = [];
|
let attachments: File[] = [];
|
||||||
|
|
||||||
|
worker.onmessage = (e) => {
|
||||||
|
const data = e.data;
|
||||||
|
|
||||||
|
if (data.status === 'success' && data.modifiedPDF !== undefined) {
|
||||||
|
hideLoader();
|
||||||
|
|
||||||
|
downloadFile(
|
||||||
|
new Blob([new Uint8Array(data.modifiedPDF)], { type: 'application/pdf' }),
|
||||||
|
`attached-${state.files[0].name}`
|
||||||
|
);
|
||||||
|
|
||||||
|
showAlert('Success', `${attachments.length} file(s) attached successfully.`);
|
||||||
|
clearAttachments();
|
||||||
|
} else if (data.status === 'error') {
|
||||||
|
hideLoader();
|
||||||
|
showAlert('Error', data.message || 'Unknown error occurred.');
|
||||||
|
clearAttachments();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
worker.onerror = (error) => {
|
||||||
|
hideLoader();
|
||||||
|
console.error('Worker error:', error);
|
||||||
|
showAlert('Error', 'Worker error occurred. Check console for details.');
|
||||||
|
clearAttachments();
|
||||||
|
};
|
||||||
|
|
||||||
export async function addAttachments() {
|
export async function addAttachments() {
|
||||||
if (!state.pdfDoc) {
|
if (!state.files || state.files.length === 0) {
|
||||||
showAlert('Error', 'Main PDF is not loaded.');
|
showAlert('Error', 'Main PDF is not loaded.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -13,39 +43,57 @@ export async function addAttachments() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const attachmentLevel = (
|
||||||
|
document.querySelector('input[name="attachment-level"]:checked') as HTMLInputElement
|
||||||
|
)?.value || 'document';
|
||||||
|
|
||||||
|
let pageRange: string = '';
|
||||||
|
|
||||||
|
if (attachmentLevel === 'page') {
|
||||||
|
const pageRangeInput = document.getElementById('attachment-page-range') as HTMLInputElement;
|
||||||
|
pageRange = pageRangeInput?.value?.trim() || '';
|
||||||
|
|
||||||
|
if (!pageRange) {
|
||||||
|
showAlert('Error', 'Please specify a page range for page-level attachments.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
showLoader('Embedding files into PDF...');
|
showLoader('Embedding files into PDF...');
|
||||||
try {
|
try {
|
||||||
const pdfDoc = state.pdfDoc;
|
const pdfFile = state.files[0];
|
||||||
|
const pdfBuffer = (await readFileAsArrayBuffer(pdfFile)) as ArrayBuffer;
|
||||||
|
|
||||||
|
const attachmentBuffers: ArrayBuffer[] = [];
|
||||||
|
const attachmentNames: string[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < attachments.length; i++) {
|
for (let i = 0; i < attachments.length; i++) {
|
||||||
const file = attachments[i];
|
const file = attachments[i];
|
||||||
showLoader(`Attaching ${file.name} (${i + 1}/${attachments.length})...`);
|
showLoader(`Reading ${file.name} (${i + 1}/${attachments.length})...`);
|
||||||
|
|
||||||
const fileBytes = await readFileAsArrayBuffer(file);
|
const fileBuffer = (await readFileAsArrayBuffer(file)) as ArrayBuffer;
|
||||||
|
attachmentBuffers.push(fileBuffer);
|
||||||
await pdfDoc.attach(fileBytes as ArrayBuffer, file.name, {
|
attachmentNames.push(file.name);
|
||||||
mimeType: file.type || 'application/octet-stream',
|
|
||||||
description: `Attached file: ${file.name}`,
|
|
||||||
creationDate: new Date(),
|
|
||||||
modificationDate: new Date(file.lastModified),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const pdfBytes = await pdfDoc.save();
|
showLoader('Attaching files to PDF...');
|
||||||
downloadFile(
|
|
||||||
new Blob([pdfBytes], { type: 'application/pdf' }),
|
const message = {
|
||||||
`attached-${state.files[0].name}`
|
command: 'add-attachments',
|
||||||
);
|
pdfBuffer: pdfBuffer,
|
||||||
|
attachmentBuffers: attachmentBuffers,
|
||||||
|
attachmentNames: attachmentNames,
|
||||||
|
attachmentLevel: attachmentLevel,
|
||||||
|
pageRange: pageRange
|
||||||
|
};
|
||||||
|
|
||||||
|
const transferables = [pdfBuffer, ...attachmentBuffers];
|
||||||
|
worker.postMessage(message, transferables);
|
||||||
|
|
||||||
showAlert(
|
|
||||||
'Success',
|
|
||||||
`${attachments.length} file(s) attached successfully.`
|
|
||||||
);
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('Error attaching files:', error);
|
console.error('Error attaching files:', error);
|
||||||
showAlert('Error', `Failed to attach files: ${error.message}`);
|
|
||||||
} finally {
|
|
||||||
hideLoader();
|
hideLoader();
|
||||||
|
showAlert('Error', `Failed to attach files: ${error.message}`);
|
||||||
clearAttachments();
|
clearAttachments();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,6 +107,8 @@ function clearAttachments() {
|
|||||||
const processBtn = document.getElementById(
|
const processBtn = document.getElementById(
|
||||||
'process-btn'
|
'process-btn'
|
||||||
) as HTMLButtonElement;
|
) as HTMLButtonElement;
|
||||||
|
const attachmentLevelOptions = document.getElementById('attachment-level-options');
|
||||||
|
const pageRangeWrapper = document.getElementById('page-range-wrapper');
|
||||||
|
|
||||||
if (fileListDiv) fileListDiv.innerHTML = '';
|
if (fileListDiv) fileListDiv.innerHTML = '';
|
||||||
if (attachmentInput) attachmentInput.value = '';
|
if (attachmentInput) attachmentInput.value = '';
|
||||||
@@ -66,14 +116,20 @@ function clearAttachments() {
|
|||||||
processBtn.disabled = true;
|
processBtn.disabled = true;
|
||||||
processBtn.classList.add('hidden');
|
processBtn.classList.add('hidden');
|
||||||
}
|
}
|
||||||
|
if (attachmentLevelOptions) {
|
||||||
|
attachmentLevelOptions.classList.add('hidden');
|
||||||
|
}
|
||||||
|
if (pageRangeWrapper) {
|
||||||
|
pageRangeWrapper.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
const documentRadio = document.querySelector('input[name="attachment-level"][value="document"]') as HTMLInputElement;
|
||||||
|
if (documentRadio) {
|
||||||
|
documentRadio.checked = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let isSetup = false; // Prevent duplicate setup
|
|
||||||
|
|
||||||
export function setupAddAttachmentsTool() {
|
export function setupAddAttachmentsTool() {
|
||||||
if (isSetup) return; // Already set up
|
|
||||||
isSetup = true;
|
|
||||||
|
|
||||||
const optionsDiv = document.getElementById('attachment-options');
|
const optionsDiv = document.getElementById('attachment-options');
|
||||||
const attachmentInput = document.getElementById(
|
const attachmentInput = document.getElementById(
|
||||||
'attachment-files-input'
|
'attachment-files-input'
|
||||||
@@ -82,14 +138,29 @@ export function setupAddAttachmentsTool() {
|
|||||||
const processBtn = document.getElementById(
|
const processBtn = document.getElementById(
|
||||||
'process-btn'
|
'process-btn'
|
||||||
) as HTMLButtonElement;
|
) as HTMLButtonElement;
|
||||||
|
const attachmentLevelOptions = document.getElementById('attachment-level-options');
|
||||||
|
const pageRangeWrapper = document.getElementById('page-range-wrapper');
|
||||||
|
const totalPagesSpan = document.getElementById('attachment-total-pages');
|
||||||
|
|
||||||
if (!optionsDiv || !attachmentInput || !fileListDiv || !processBtn) {
|
if (!optionsDiv || !attachmentInput || !fileListDiv || !processBtn) {
|
||||||
console.error('Attachment tool UI elements not found.');
|
console.error('Attachment tool UI elements not found.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!state.files || state.files.length === 0) {
|
||||||
|
console.error('No PDF file loaded for adding attachments.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
optionsDiv.classList.remove('hidden');
|
optionsDiv.classList.remove('hidden');
|
||||||
|
|
||||||
|
if (totalPagesSpan && state.pdfDoc) {
|
||||||
|
totalPagesSpan.textContent = state.pdfDoc.getPageCount().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attachmentInput.dataset.listenerAttached) return;
|
||||||
|
attachmentInput.dataset.listenerAttached = 'true';
|
||||||
|
|
||||||
attachmentInput.addEventListener('change', (e) => {
|
attachmentInput.addEventListener('change', (e) => {
|
||||||
const files = (e.target as HTMLInputElement).files;
|
const files = (e.target as HTMLInputElement).files;
|
||||||
if (files && files.length > 0) {
|
if (files && files.length > 0) {
|
||||||
@@ -114,6 +185,10 @@ export function setupAddAttachmentsTool() {
|
|||||||
fileListDiv.appendChild(div);
|
fileListDiv.appendChild(div);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (attachmentLevelOptions) {
|
||||||
|
attachmentLevelOptions.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
processBtn.disabled = false;
|
processBtn.disabled = false;
|
||||||
processBtn.classList.remove('hidden');
|
processBtn.classList.remove('hidden');
|
||||||
} else {
|
} else {
|
||||||
@@ -121,5 +196,17 @@ export function setupAddAttachmentsTool() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const attachmentLevelRadios = document.querySelectorAll('input[name="attachment-level"]');
|
||||||
|
attachmentLevelRadios.forEach((radio) => {
|
||||||
|
radio.addEventListener('change', (e) => {
|
||||||
|
const value = (e.target as HTMLInputElement).value;
|
||||||
|
if (value === 'page' && pageRangeWrapper) {
|
||||||
|
pageRangeWrapper.classList.remove('hidden');
|
||||||
|
} else if (pageRangeWrapper) {
|
||||||
|
pageRangeWrapper.classList.add('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
processBtn.onclick = addAttachments;
|
processBtn.onclick = addAttachments;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,11 +58,23 @@ async function loadPdfInViewer(file: File) {
|
|||||||
const blob = new Blob([arrayBuffer as BlobPart], { type: 'application/pdf' })
|
const blob = new Blob([arrayBuffer as BlobPart], { type: 'application/pdf' })
|
||||||
currentBlobUrl = URL.createObjectURL(blob)
|
currentBlobUrl = URL.createObjectURL(blob)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const existingPrefsRaw = localStorage.getItem('pdfjs.preferences')
|
||||||
|
const existingPrefs = existingPrefsRaw ? JSON.parse(existingPrefsRaw) : {}
|
||||||
|
delete (existingPrefs as any).annotationEditorMode
|
||||||
|
const newPrefs = {
|
||||||
|
...existingPrefs,
|
||||||
|
enablePermissions: false,
|
||||||
|
}
|
||||||
|
localStorage.setItem('pdfjs.preferences', JSON.stringify(newPrefs))
|
||||||
|
} catch {}
|
||||||
|
|
||||||
const iframe = document.createElement('iframe')
|
const iframe = document.createElement('iframe')
|
||||||
iframe.className = 'w-full h-full border-0'
|
iframe.className = 'w-full h-full border-0'
|
||||||
iframe.allowFullscreen = true
|
iframe.allowFullscreen = true
|
||||||
|
|
||||||
iframe.src = `/pdfjs-annotation-viewer/web/viewer.html?file=${encodeURIComponent(currentBlobUrl)}`
|
const viewerUrl = new URL('/pdfjs-annotation-viewer/web/viewer.html', window.location.origin)
|
||||||
|
iframe.src = `${viewerUrl.toString()}?file=${encodeURIComponent(currentBlobUrl)}`
|
||||||
|
|
||||||
iframe.addEventListener('load', () => {
|
iframe.addEventListener('load', () => {
|
||||||
setupAnnotationViewer(iframe)
|
setupAnnotationViewer(iframe)
|
||||||
@@ -85,10 +97,14 @@ function setupAnnotationViewer(iframe: HTMLIFrameElement) {
|
|||||||
await app.initializedPromise
|
await app.initializedPromise
|
||||||
}
|
}
|
||||||
|
|
||||||
const stampBtn = doc.getElementById('editorStamp') as HTMLButtonElement | null
|
const eventBus = app?.eventBus
|
||||||
if (stampBtn) {
|
if (eventBus && typeof eventBus._on === 'function') {
|
||||||
stampBtn.classList.remove('hidden')
|
eventBus._on('annotationeditoruimanager', () => {
|
||||||
stampBtn.disabled = false
|
try {
|
||||||
|
const stampBtn = doc.getElementById('editorStampButton') as HTMLButtonElement | null
|
||||||
|
stampBtn?.click()
|
||||||
|
} catch {}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const root = doc.querySelector('.PdfjsAnnotationExtension') as HTMLElement | null
|
const root = doc.querySelector('.PdfjsAnnotationExtension') as HTMLElement | null
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { downloadFile, formatBytes } from '../utils/helpers.js';
|
import { downloadFile, formatBytes } from '../utils/helpers.js';
|
||||||
import { state } from '../state.js';
|
import { state } from '../state.js';
|
||||||
|
import { showAlert } from '../ui.js';
|
||||||
import JSZip from 'jszip';
|
import JSZip from 'jszip';
|
||||||
|
|
||||||
const worker = new Worker('/workers/extract-attachments.worker.js');
|
const worker = new Worker('/workers/extract-attachments.worker.js');
|
||||||
@@ -66,6 +67,30 @@ worker.onmessage = (e: MessageEvent<ExtractAttachmentResponse>) => {
|
|||||||
if (e.data.status === 'success') {
|
if (e.data.status === 'success') {
|
||||||
const attachments = e.data.attachments;
|
const attachments = e.data.attachments;
|
||||||
|
|
||||||
|
if (attachments.length === 0) {
|
||||||
|
showAlert('No Attachments', 'The PDF file(s) do not contain any attachments to extract.');
|
||||||
|
|
||||||
|
state.files = [];
|
||||||
|
state.pdfDoc = null;
|
||||||
|
|
||||||
|
const fileDisplayArea = document.getElementById('file-display-area');
|
||||||
|
if (fileDisplayArea) {
|
||||||
|
fileDisplayArea.innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileInput = document.getElementById('file-input') as HTMLInputElement;
|
||||||
|
if (fileInput) {
|
||||||
|
fileInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileControls = document.getElementById('file-controls');
|
||||||
|
if (fileControls) {
|
||||||
|
fileControls.classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
let totalSize = 0;
|
let totalSize = 0;
|
||||||
|
|
||||||
@@ -76,29 +101,60 @@ worker.onmessage = (e: MessageEvent<ExtractAttachmentResponse>) => {
|
|||||||
|
|
||||||
zip.generateAsync({ type: 'blob' }).then((zipBlob) => {
|
zip.generateAsync({ type: 'blob' }).then((zipBlob) => {
|
||||||
downloadFile(zipBlob, 'extracted-attachments.zip');
|
downloadFile(zipBlob, 'extracted-attachments.zip');
|
||||||
|
|
||||||
|
showAlert('Success', `${attachments.length} attachment(s) extracted successfully!`);
|
||||||
|
|
||||||
showStatus(
|
showStatus(
|
||||||
`Extraction completed! ${attachments.length} attachment(s) in zip file (${formatBytes(totalSize)}). Download started.`,
|
`Extraction completed! ${attachments.length} attachment(s) in zip file (${formatBytes(totalSize)}). Download started.`,
|
||||||
'success'
|
'success'
|
||||||
);
|
);
|
||||||
|
|
||||||
state.files = [];
|
state.files = [];
|
||||||
|
state.pdfDoc = null;
|
||||||
|
|
||||||
const fileDisplayArea = document.getElementById('file-display-area');
|
const fileDisplayArea = document.getElementById('file-display-area');
|
||||||
if (fileDisplayArea) {
|
if (fileDisplayArea) {
|
||||||
fileDisplayArea.innerHTML = '';
|
fileDisplayArea.innerHTML = '';
|
||||||
fileDisplayArea.classList.add('hidden');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileInput = document.getElementById('file-input') as HTMLInputElement;
|
const fileInput = document.getElementById('file-input') as HTMLInputElement;
|
||||||
if (fileInput) {
|
if (fileInput) {
|
||||||
fileInput.value = '';
|
fileInput.value = '';
|
||||||
}
|
}
|
||||||
document.getElementById('process-btn')?.classList.add('opacity-50', 'cursor-not-allowed');
|
|
||||||
document.getElementById('process-btn')?.setAttribute('disabled', 'true');
|
const fileControls = document.getElementById('file-controls');
|
||||||
|
if (fileControls) {
|
||||||
|
fileControls.classList.add('hidden');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else if (e.data.status === 'error') {
|
} else if (e.data.status === 'error') {
|
||||||
const errorMessage = e.data.message || 'Unknown error occurred in worker.';
|
const errorMessage = e.data.message || 'Unknown error occurred in worker.';
|
||||||
console.error('Worker Error:', errorMessage);
|
console.error('Worker Error:', errorMessage);
|
||||||
|
|
||||||
|
if (errorMessage.includes('No attachments were found')) {
|
||||||
|
showAlert('No Attachments', 'The PDF file(s) do not contain any attachments to extract.');
|
||||||
|
|
||||||
|
state.files = [];
|
||||||
|
state.pdfDoc = null;
|
||||||
|
|
||||||
|
const fileDisplayArea = document.getElementById('file-display-area');
|
||||||
|
if (fileDisplayArea) {
|
||||||
|
fileDisplayArea.innerHTML = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileInput = document.getElementById('file-input') as HTMLInputElement;
|
||||||
|
if (fileInput) {
|
||||||
|
fileInput.value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileControls = document.getElementById('file-controls');
|
||||||
|
if (fileControls) {
|
||||||
|
fileControls.classList.add('hidden');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
showStatus(`Error: ${errorMessage}`, 'error');
|
showStatus(`Error: ${errorMessage}`, 'error');
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
worker.onerror = (error) => {
|
worker.onerror = (error) => {
|
||||||
@@ -113,8 +169,7 @@ function showStatus(message: string, type: 'success' | 'error' | 'info' = 'info'
|
|||||||
if (!statusMessage) return;
|
if (!statusMessage) return;
|
||||||
|
|
||||||
statusMessage.textContent = message;
|
statusMessage.textContent = message;
|
||||||
statusMessage.className = `mt-4 p-3 rounded-lg text-sm ${
|
statusMessage.className = `mt-4 p-3 rounded-lg text-sm ${type === 'success'
|
||||||
type === 'success'
|
|
||||||
? 'bg-green-900 text-green-200'
|
? 'bg-green-900 text-green-200'
|
||||||
: type === 'error'
|
: type === 'error'
|
||||||
? 'bg-red-900 text-red-200'
|
? 'bg-red-900 text-red-200'
|
||||||
|
|||||||
@@ -40,9 +40,21 @@ export async function setupSignTool() {
|
|||||||
const blob = new Blob([pdfBytes as BlobPart], { type: 'application/pdf' });
|
const blob = new Blob([pdfBytes as BlobPart], { type: 'application/pdf' });
|
||||||
const blobUrl = URL.createObjectURL(blob);
|
const blobUrl = URL.createObjectURL(blob);
|
||||||
|
|
||||||
const viewerBase = '/pdfjs-viewer/viewer.html';
|
try {
|
||||||
|
const existingPrefsRaw = localStorage.getItem('pdfjs.preferences');
|
||||||
|
const existingPrefs = existingPrefsRaw ? JSON.parse(existingPrefsRaw) : {};
|
||||||
|
delete (existingPrefs as any).annotationEditorMode;
|
||||||
|
const newPrefs = {
|
||||||
|
...existingPrefs,
|
||||||
|
enableSignatureEditor: true,
|
||||||
|
enablePermissions: false,
|
||||||
|
};
|
||||||
|
localStorage.setItem('pdfjs.preferences', JSON.stringify(newPrefs));
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
const viewerUrl = new URL('/pdfjs-viewer/viewer.html', window.location.origin);
|
||||||
const query = new URLSearchParams({ file: blobUrl });
|
const query = new URLSearchParams({ file: blobUrl });
|
||||||
iframe.src = `${viewerBase}?${query.toString()}`;
|
iframe.src = `${viewerUrl.toString()}?${query.toString()}`;
|
||||||
|
|
||||||
iframe.onload = () => {
|
iframe.onload = () => {
|
||||||
hideLoader();
|
hideLoader();
|
||||||
@@ -52,23 +64,27 @@ export async function setupSignTool() {
|
|||||||
if (viewerWindow && viewerWindow.PDFViewerApplication) {
|
if (viewerWindow && viewerWindow.PDFViewerApplication) {
|
||||||
const app = viewerWindow.PDFViewerApplication;
|
const app = viewerWindow.PDFViewerApplication;
|
||||||
const doc = viewerWindow.document;
|
const doc = viewerWindow.document;
|
||||||
|
const eventBus = app.eventBus;
|
||||||
|
eventBus?._on('annotationeditoruimanager', () => {
|
||||||
const editorModeButtons = doc.getElementById('editorModeButtons');
|
const editorModeButtons = doc.getElementById('editorModeButtons');
|
||||||
editorModeButtons?.classList.remove('hidden');
|
editorModeButtons?.classList.remove('hidden');
|
||||||
|
|
||||||
const editorSignature = doc.getElementById('editorSignature');
|
const editorSignature = doc.getElementById('editorSignature');
|
||||||
editorSignature?.removeAttribute('hidden');
|
editorSignature?.removeAttribute('hidden');
|
||||||
const editorSignatureButton = doc.getElementById('editorSignatureButton') as HTMLButtonElement | null;
|
const editorSignatureButton = doc.getElementById('editorSignatureButton') as HTMLButtonElement | null;
|
||||||
if (editorSignatureButton) {
|
if (editorSignatureButton) {
|
||||||
editorSignatureButton.disabled = false;
|
editorSignatureButton.disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const editorStamp = doc.getElementById('editorStamp');
|
const editorStamp = doc.getElementById('editorStamp');
|
||||||
editorStamp?.removeAttribute('hidden');
|
editorStamp?.removeAttribute('hidden');
|
||||||
const editorStampButton = doc.getElementById('editorStampButton') as HTMLButtonElement | null;
|
const editorStampButton = doc.getElementById('editorStampButton') as HTMLButtonElement | null;
|
||||||
if (editorStampButton) {
|
if (editorStampButton) {
|
||||||
editorStampButton.disabled = false;
|
editorStampButton.disabled = false;
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
const highlightBtn = doc.getElementById('editorHighlightButton') as HTMLButtonElement | null;
|
||||||
|
highlightBtn?.click();
|
||||||
|
} catch {}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Could not initialize base PDF.js viewer for signing:', e);
|
console.error('Could not initialize base PDF.js viewer for signing:', e);
|
||||||
|
|||||||
+6
-3
@@ -5,6 +5,7 @@ import { createIcons, icons } from 'lucide';
|
|||||||
import * as pdfjsLib from 'pdfjs-dist';
|
import * as pdfjsLib from 'pdfjs-dist';
|
||||||
import '../css/styles.css';
|
import '../css/styles.css';
|
||||||
import { formatStars } from './utils/helpers.js';
|
import { formatStars } from './utils/helpers.js';
|
||||||
|
import { APP_VERSION, injectVersion } from '../version.js';
|
||||||
|
|
||||||
const init = () => {
|
const init = () => {
|
||||||
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
|
pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
|
||||||
@@ -28,9 +29,11 @@ const init = () => {
|
|||||||
simpleNav.innerHTML = `
|
simpleNav.innerHTML = `
|
||||||
<div class="container mx-auto px-4">
|
<div class="container mx-auto px-4">
|
||||||
<div class="flex justify-start items-center h-16">
|
<div class="flex justify-start items-center h-16">
|
||||||
<div class="flex-shrink-0 flex items-center">
|
<div class="flex-shrink-0 flex items-center cursor-pointer" id="home-logo">
|
||||||
<img src="images/favicon.svg" alt="Bento PDF Logo" class="h-8 w-8">
|
<img src="images/favicon.svg" alt="Bento PDF Logo" class="h-8 w-8">
|
||||||
<span class="text-white font-bold text-xl ml-2">BentoPDF</span>
|
<span class="text-white font-bold text-xl ml-2">
|
||||||
|
<a href="index.html">BentoPDF</a>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,7 +97,7 @@ const init = () => {
|
|||||||
© 2025 BentoPDF. All rights reserved.
|
© 2025 BentoPDF. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-500 text-xs mt-2">
|
<p class="text-gray-500 text-xs mt-2">
|
||||||
Version <span id="app-version-simple">1.6.2</span>
|
Version <span id="app-version-simple">${APP_VERSION}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
+29
-2
@@ -251,8 +251,7 @@ const createFileInputHTML = (options = {}) => {
|
|||||||
<input id="file-input" type="file" class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer" ${multiple} accept="${acceptedFiles}">
|
<input id="file-input" type="file" class="absolute top-0 left-0 w-full h-full opacity-0 cursor-pointer" ${multiple} accept="${acceptedFiles}">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
${
|
${showControls
|
||||||
showControls
|
|
||||||
? `
|
? `
|
||||||
<!-- NEW: Add control buttons for multi-file uploads -->
|
<!-- NEW: Add control buttons for multi-file uploads -->
|
||||||
<div id="file-controls" class="hidden mt-4 flex gap-3">
|
<div id="file-controls" class="hidden mt-4 flex gap-3">
|
||||||
@@ -1984,6 +1983,34 @@ export const toolTemplates = {
|
|||||||
|
|
||||||
<div id="attachment-file-list" class="mt-4 space-y-2"></div>
|
<div id="attachment-file-list" class="mt-4 space-y-2"></div>
|
||||||
|
|
||||||
|
<div id="attachment-level-options" class="hidden mt-6 space-y-4">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold text-white mb-2">Attachment Level</h3>
|
||||||
|
<div class="flex gap-4 p-2 rounded-lg bg-gray-900">
|
||||||
|
<label class="flex-1 flex items-center gap-2 p-3 rounded-md hover:bg-gray-700 cursor-pointer">
|
||||||
|
<input type="radio" name="attachment-level" value="document" checked class="w-4 h-4 text-indigo-600 bg-gray-700 border-gray-600 focus:ring-indigo-500">
|
||||||
|
<div>
|
||||||
|
<span class="font-semibold text-white">Document Level</span>
|
||||||
|
<p class="text-xs text-gray-400">Attach to the entire document</p>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<label class="flex-1 flex items-center gap-2 p-3 rounded-md hover:bg-gray-700 cursor-pointer">
|
||||||
|
<input type="radio" name="attachment-level" value="page" class="w-4 h-4 text-indigo-600 bg-gray-700 border-gray-600 focus:ring-indigo-500">
|
||||||
|
<div>
|
||||||
|
<span class="font-semibold text-white">Page Level</span>
|
||||||
|
<p class="text-xs text-gray-400">Attach to specific pages</p>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="page-range-wrapper" class="hidden">
|
||||||
|
<label for="attachment-page-range" class="block mb-2 text-sm font-medium text-gray-300">Page Range</label>
|
||||||
|
<input type="text" id="attachment-page-range" class="w-full bg-gray-700 border border-gray-600 text-white rounded-lg p-2.5" placeholder="e.g., 1-3, 5, 8">
|
||||||
|
<p class="text-xs text-gray-400 mt-1">Attachments will be added to each page in this range. Total Pages: <span id="attachment-total-pages"></span></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<button id="process-btn" class="hidden btn-gradient w-full mt-6" disabled>Embed Files & Download</button>
|
<button id="process-btn" class="hidden btn-gradient w-full mt-6" disabled>Embed Files & Download</button>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
|
|||||||
@@ -117,7 +117,7 @@
|
|||||||
© 2025 BentoPDF. All rights reserved.
|
© 2025 BentoPDF. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-500 text-xs mt-2">
|
<p class="text-gray-500 text-xs mt-2">
|
||||||
Version <span id="app-version">1.6.2</span>
|
Version <span id="app-version"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -186,6 +186,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script type="module" src="../js/utils/lucide-init.ts"></script>
|
<script type="module" src="../js/utils/lucide-init.ts"></script>
|
||||||
|
<script type="module" src="../version.ts"></script>
|
||||||
<script type="module" src="../js/logic/add-stamps.ts"></script>
|
<script type="module" src="../js/logic/add-stamps.ts"></script>
|
||||||
<script type="module" src="../js/mobileMenu.ts"></script>
|
<script type="module" src="../js/mobileMenu.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -420,7 +420,7 @@
|
|||||||
© 2025 BentoPDF. All rights reserved.
|
© 2025 BentoPDF. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-500 text-xs mt-2">
|
<p class="text-gray-500 text-xs mt-2">
|
||||||
Version <span id="app-version">1.6.2</span>
|
Version <span id="app-version"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -489,6 +489,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script type="module" src="../js/utils/lucide-init.ts"></script>
|
<script type="module" src="../js/utils/lucide-init.ts"></script>
|
||||||
|
<script type="module" src="../version.ts"></script>
|
||||||
<script type="module" src="../js/logic/bookmark-pdf.ts"></script>
|
<script type="module" src="../js/logic/bookmark-pdf.ts"></script>
|
||||||
<script type="module" src="../js/mobileMenu.ts"></script>
|
<script type="module" src="../js/mobileMenu.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -116,7 +116,7 @@
|
|||||||
© 2025 BentoPDF. All rights reserved.
|
© 2025 BentoPDF. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-500 text-xs mt-2">
|
<p class="text-gray-500 text-xs mt-2">
|
||||||
Version <span id="app-version">1.6.2</span>
|
Version <span id="app-version"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -186,6 +186,7 @@
|
|||||||
</footer>
|
</footer>
|
||||||
<script type="module" src="../js/logic/json-to-pdf.ts"></script>
|
<script type="module" src="../js/logic/json-to-pdf.ts"></script>
|
||||||
<script type="module" src="../js/utils/lucide-init.ts"></script>
|
<script type="module" src="../js/utils/lucide-init.ts"></script>
|
||||||
|
<script type="module" src="../version.ts"></script>
|
||||||
<script type="module" src="../js/mobileMenu.ts"></script>
|
<script type="module" src="../js/mobileMenu.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@
|
|||||||
© 2025 BentoPDF. All rights reserved.
|
© 2025 BentoPDF. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-500 text-xs mt-2">
|
<p class="text-gray-500 text-xs mt-2">
|
||||||
Version <span id="app-version">1.6.2</span>
|
Version <span id="app-version"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -180,6 +180,7 @@
|
|||||||
</footer>
|
</footer>
|
||||||
<script type="module" src="../js/logic/pdf-to-json.ts"></script>
|
<script type="module" src="../js/logic/pdf-to-json.ts"></script>
|
||||||
<script type="module" src="../js/utils/lucide-init.ts"></script>
|
<script type="module" src="../js/utils/lucide-init.ts"></script>
|
||||||
|
<script type="module" src="../version.ts"></script>
|
||||||
<script type="module" src="../js/mobileMenu.ts"></script>
|
<script type="module" src="../js/mobileMenu.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import packageJson from '../package.json';
|
||||||
|
|
||||||
|
export const APP_VERSION = packageJson.version;
|
||||||
|
|
||||||
|
export function injectVersion() {
|
||||||
|
const versionElements = document.querySelectorAll('#app-version, #app-version-simple');
|
||||||
|
versionElements.forEach((element) => {
|
||||||
|
element.textContent = APP_VERSION;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof document !== 'undefined') {
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', injectVersion);
|
||||||
|
} else {
|
||||||
|
injectVersion();
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -234,7 +234,7 @@
|
|||||||
© 2025 BentoPDF. All rights reserved.
|
© 2025 BentoPDF. All rights reserved.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-gray-500 text-xs mt-2">
|
<p class="text-gray-500 text-xs mt-2">
|
||||||
Version <span id="app-version">1.6.2</span>
|
Version <span id="app-version"></span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -303,7 +303,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
<script type="module" src="src/js/utils/lucide-init.ts"></script>
|
||||||
|
<script type="module" src="src/version.ts"></script>
|
||||||
<script type="module" src="src/js/mobileMenu.ts"></script>
|
<script type="module" src="src/js/mobileMenu.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
|
||||||
/* Path aliases (optional but helpful) */
|
/* Path aliases (optional but helpful) */
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ export default defineConfig(({ mode }) => ({
|
|||||||
'Cross-Origin-Embedder-Policy': 'require-corp',
|
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
preview: {
|
||||||
|
headers: {
|
||||||
|
'Cross-Origin-Opener-Policy': 'same-origin',
|
||||||
|
'Cross-Origin-Embedder-Policy': 'require-corp',
|
||||||
|
},
|
||||||
|
},
|
||||||
build: {
|
build: {
|
||||||
rollupOptions: {
|
rollupOptions: {
|
||||||
input: {
|
input: {
|
||||||
@@ -51,6 +57,7 @@ export default defineConfig(({ mode }) => ({
|
|||||||
'pdf-to-json': resolve(__dirname, 'src/pages/pdf-to-json.html'),
|
'pdf-to-json': resolve(__dirname, 'src/pages/pdf-to-json.html'),
|
||||||
'json-to-pdf': resolve(__dirname, 'src/pages/json-to-pdf.html'),
|
'json-to-pdf': resolve(__dirname, 'src/pages/json-to-pdf.html'),
|
||||||
'pdf-multi-tool': resolve(__dirname, 'src/pages/pdf-multi-tool.html'),
|
'pdf-multi-tool': resolve(__dirname, 'src/pages/pdf-multi-tool.html'),
|
||||||
|
'add-stamps': resolve(__dirname, 'src/pages/add-stamps.html'),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user