Skip to content

Commit 92b5570

Browse files
committed
Add decryption support
1 parent 9e3db91 commit 92b5570

11 files changed

+2076
-26
lines changed

src/api/PDFDocument.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
JpegEmbedder,
1919
PageBoundingBox,
2020
PageEmbeddingMismatchedContextError,
21+
PDFArray,
2122
PDFCatalog,
2223
PDFContext,
2324
PDFDict,
@@ -66,6 +67,7 @@ import FileEmbedder, { AFRelationship } from '../core/embedders/FileEmbedder';
6667
import PDFEmbeddedFile from './PDFEmbeddedFile';
6768
import PDFJavaScript from './PDFJavaScript';
6869
import JavaScriptEmbedder from '../core/embedders/JavaScriptEmbedder';
70+
import { CipherTransformFactory } from '../core/crypto';
6971

7072
/**
7173
* Represents a PDF document.
@@ -147,7 +149,20 @@ export default class PDFDocument {
147149
throwOnInvalidObject,
148150
capNumbers,
149151
).parseDocument();
150-
return new PDFDocument(context, ignoreEncryption, updateMetadata);
152+
const pdfDoc = new PDFDocument(context, ignoreEncryption, updateMetadata, false);
153+
154+
if (pdfDoc.isEncrypted) {
155+
// Decrypt
156+
const context = await PDFParser.forBytesWithOptions(
157+
bytes,
158+
parseSpeed,
159+
throwOnInvalidObject,
160+
capNumbers,
161+
pdfDoc.cryptoFactory
162+
).parseDocument();
163+
return new PDFDocument(context, true, updateMetadata, true);
164+
}
165+
return pdfDoc;
151166
}
152167

153168
/**
@@ -163,7 +178,7 @@ export default class PDFDocument {
163178
const catalog = PDFCatalog.withContextAndPages(context, pageTreeRef);
164179
context.trailerInfo.Root = context.register(catalog);
165180

166-
return new PDFDocument(context, false, updateMetadata);
181+
return new PDFDocument(context, false, updateMetadata, false);
167182
}
168183

169184
/** The low-level context of this document. */
@@ -175,6 +190,8 @@ export default class PDFDocument {
175190
/** Whether or not this document is encrypted. */
176191
readonly isEncrypted: boolean;
177192

193+
readonly cryptoFactory?: CipherTransformFactory;
194+
178195
/** The default word breaks used in PDFPage.drawText */
179196
defaultWordBreaks: string[] = [' '];
180197

@@ -193,6 +210,7 @@ export default class PDFDocument {
193210
context: PDFContext,
194211
ignoreEncryption: boolean,
195212
updateMetadata: boolean,
213+
isDecrypted: boolean
196214
) {
197215
assertIs(context, 'context', [[PDFContext, 'PDFContext']]);
198216
assertIs(ignoreEncryption, 'ignoreEncryption', ['boolean']);
@@ -201,6 +219,15 @@ export default class PDFDocument {
201219
this.catalog = context.lookup(context.trailerInfo.Root) as PDFCatalog;
202220
this.isEncrypted = !!context.lookup(context.trailerInfo.Encrypt);
203221

222+
if (this.isEncrypted && !isDecrypted) {
223+
const encryptDict = context.lookup(context.trailerInfo.Encrypt, PDFDict);
224+
const fileIds = context.lookup(context.trailerInfo.ID, PDFArray);
225+
this.cryptoFactory = new CipherTransformFactory(encryptDict, (fileIds.get(0) as PDFHexString).asBytes())
226+
} else if (this.isEncrypted) {
227+
// context.delete(context.trailerInfo.Encrypt);
228+
delete context.trailerInfo.Encrypt;
229+
}
230+
204231
this.pageCache = Cache.populatedBy(this.computePages);
205232
this.pageMap = new Map();
206233
this.formCache = Cache.populatedBy(this.getOrCreateForm);

0 commit comments

Comments
 (0)