Skip to content

Commit deb7e77

Browse files
committed
feat(new tool): Pdf Unlocker
Pdf Unlocker using QPDF-Wasm (modularized) Fix #696
1 parent 06640f9 commit deb7e77

File tree

3 files changed

+99
-1
lines changed

3 files changed

+99
-1
lines changed

Diff for: src/tools/index.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,25 @@ import { tool as uuidGenerator } from './uuid-generator';
8181
import { tool as macAddressLookup } from './mac-address-lookup';
8282
import { tool as xmlFormatter } from './xml-formatter';
8383
import { tool as yamlViewer } from './yaml-viewer';
84+
import { tool as pdfUnlock } from './pdf-unlock';
8485

8586
export const toolsByCategory: ToolCategory[] = [
8687
{
8788
name: 'Crypto',
88-
components: [tokenGenerator, hashText, bcrypt, uuidGenerator, ulidGenerator, cypher, bip39, hmacGenerator, rsaKeyPairGenerator, passwordStrengthAnalyser, pdfSignatureChecker],
89+
components: [
90+
tokenGenerator,
91+
hashText,
92+
bcrypt,
93+
uuidGenerator,
94+
ulidGenerator,
95+
cypher,
96+
bip39,
97+
hmacGenerator,
98+
rsaKeyPairGenerator,
99+
passwordStrengthAnalyser,
100+
pdfSignatureChecker,
101+
pdfUnlock,
102+
],
89103
},
90104
{
91105
name: 'Converter',

Diff for: src/tools/pdf-unlock/index.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { LockOff } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Pdf Decrypt and Unlock',
6+
path: '/pdf-unlock',
7+
description: 'Decrypt a PDF and unlock (remove security permissions)',
8+
keywords: ['pdf', 'unlock', 'decrypt'],
9+
component: () => import('./pdf-unlock.vue'),
10+
icon: LockOff,
11+
createdAt: new Date('2024-01-09'),
12+
});

Diff for: src/tools/pdf-unlock/pdf-unlock.vue

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<script setup lang="ts">
2+
import { Base64 } from 'js-base64';
3+
import createQPDFModule from '@/libs/qpdf/qpdf';
4+
import { useDownloadFileFromBase64Refs } from '@/composable/downloadBase64';
5+
6+
const status = ref<'idle' | 'done' | 'error' | 'processing'>('idle');
7+
const file = ref<File | null>(null);
8+
9+
const base64OutputPDF = ref('');
10+
const fileName = ref('');
11+
const fileExtension = ref('pdf');
12+
const { download } = useDownloadFileFromBase64Refs(
13+
{
14+
source: base64OutputPDF,
15+
filename: fileName,
16+
extension: fileExtension,
17+
});
18+
19+
async function onProcessClicked(uploadedFile: File) {
20+
file.value = uploadedFile;
21+
const fileBuffer = await uploadedFile.arrayBuffer();
22+
23+
fileName.value = `decrypted_${uploadedFile.name}`;
24+
status.value = 'processing';
25+
try {
26+
const outPdfBuffer = await callMainWithInOutPdf(fileBuffer,
27+
[
28+
'--decrypt',
29+
'in.pdf',
30+
'out.pdf',
31+
],
32+
0);
33+
base64OutputPDF.value = `data:application/pdf;base64,${Base64.fromUint8Array(outPdfBuffer)}`;
34+
status.value = 'done';
35+
36+
download();
37+
}
38+
catch (e) {
39+
status.value = 'error';
40+
}
41+
}
42+
43+
async function callMainWithInOutPdf(data: ArrayBuffer, args: string[], expected_exitcode: number) {
44+
const mod = await createQPDFModule();
45+
mod.FS.writeFile('in.pdf', new Uint8Array(data));
46+
const ret = mod.callMain(args);
47+
if (expected_exitcode !== ret) {
48+
throw new Error('Process run failed');
49+
}
50+
return mod.FS.readFile('out.pdf');
51+
}
52+
</script>
53+
54+
<template>
55+
<div>
56+
<div style="flex: 0 0 100%">
57+
<div mx-auto max-w-600px>
58+
<c-file-upload title="Drag and drop a PDF file here, or click to select a file" accept=".pdf" @file-upload="onProcessClicked" />
59+
</div>
60+
</div>
61+
62+
<div mt-3 flex justify-center>
63+
<c-alert v-if="status === 'error'" type="error">
64+
An error occured processing {{ fileName }}
65+
</c-alert>
66+
<n-spin
67+
v-if="status === 'processing'"
68+
size="small"
69+
/>
70+
</div>
71+
</div>
72+
</template>

0 commit comments

Comments
 (0)