@@ -31,6 +31,7 @@ import {
3131} from "./core_utils.js" ;
3232import { BaseStream } from "./base_stream.js" ;
3333import { CipherTransformFactory } from "./crypto.js" ;
34+ import { NameTree } from "./name_number_tree.js" ;
3435
3536class XRef {
3637 #firstXRefStmPos = null ;
@@ -118,22 +119,6 @@ class XRef {
118119 }
119120 warn ( `XRef.parse - Invalid "Encrypt" reference: "${ ex } ".` ) ;
120121 }
121- if ( encrypt instanceof Dict ) {
122- const ids = trailerDict . get ( "ID" ) ;
123- const fileId = ids ?. length ? ids [ 0 ] : "" ;
124- // The 'Encrypt' dictionary itself should not be encrypted, and by
125- // setting `suppressEncryption` we can prevent an infinite loop inside
126- // of `XRef_fetchUncompressed` if the dictionary contains indirect
127- // objects (fixes issue7665.pdf).
128- encrypt . suppressEncryption = true ;
129- this . encrypt = new CipherTransformFactory (
130- encrypt ,
131- fileId ,
132- this . pdfManager . password
133- ) ;
134- }
135-
136- // Get the root dictionary (catalog) object, and do some basic validation.
137122 let root ;
138123 try {
139124 root = trailerDict . get ( "Root" ) ;
@@ -143,6 +128,52 @@ class XRef {
143128 }
144129 warn ( `XRef.parse - Invalid "Root" reference: "${ ex } ".` ) ;
145130 }
131+
132+ if ( encrypt instanceof Dict ) {
133+ const ids = trailerDict . get ( "ID" ) ;
134+ const fileId = ids ?. length ? ids [ 0 ] : "" ;
135+
136+ const stmF = encrypt . get ( "StmF" ) ?. name ?? "Identity" ;
137+ const strF = encrypt . get ( "StrF" ) ?. name ?? "Identity" ;
138+ const eff = encrypt . has ( "EFF" ) ? encrypt . get ( "EFF" ) ?. name : strF ;
139+ const cryptFilters = encrypt . get ( "CF" ) || Dict . empty ;
140+ // Check if only the file attachments are encrypted.
141+ if (
142+ stmF === "Identity" &&
143+ strF === "Identity" &&
144+ eff !== "Identity" &&
145+ cryptFilters . has ( eff ) &&
146+ cryptFilters . get ( eff ) ?. get ( "CFM" ) !== "None"
147+ ) {
148+ let hasEncryptedAttachments = false ;
149+ if ( root instanceof Dict ) {
150+ const names = root . get ( "Names" ) ;
151+ if ( names instanceof Dict && names . has ( "EmbeddedFiles" ) ) {
152+ const nameTree = new NameTree ( names . getRaw ( "EmbeddedFiles" ) , this ) ;
153+ const attachments = nameTree . getAll ( ) ;
154+ if ( attachments . size > 0 ) {
155+ hasEncryptedAttachments = true ;
156+ }
157+ }
158+ }
159+ if ( ! hasEncryptedAttachments ) {
160+ // If there are no encrypted attachments, encrypt dictionary is
161+ // not needed.
162+ encrypt = null ;
163+ }
164+ } else {
165+ // The 'Encrypt' dictionary itself should not be encrypted, and by
166+ // setting `suppressEncryption` we can prevent an infinite loop inside
167+ // of `XRef_fetchUncompressed` if the dictionary contains indirect
168+ // objects (fixes issue7665.pdf).
169+ encrypt . suppressEncryption = true ;
170+ this . encrypt = new CipherTransformFactory (
171+ encrypt ,
172+ fileId ,
173+ this . pdfManager . password
174+ ) ;
175+ }
176+ }
146177 if ( root instanceof Dict ) {
147178 try {
148179 const pages = root . get ( "Pages" ) ;
0 commit comments