@@ -283,24 +283,20 @@ class PartialEvaluator {
283283 return newEvaluator ;
284284 }
285285
286- hasBlendModes ( resources , nonBlendModesSet ) {
287- if ( ! ( resources instanceof Dict ) ) {
288- return false ;
289- }
290- if ( resources . objId && nonBlendModesSet . has ( resources . objId ) ) {
291- return false ;
292- }
293-
294- const processed = new RefSet ( nonBlendModesSet ) ;
295- if ( resources . objId ) {
296- processed . put ( resources . objId ) ;
286+ _traverseExtGState (
287+ resources ,
288+ processed ,
289+ {
290+ checkGraphicState,
291+ onExtGStateFetchError = null ,
292+ onXObjectFetchError = null ,
293+ onResourceNode = null ,
297294 }
298-
295+ ) {
299296 const nodes = [ resources ] ,
300297 xref = this . xref ;
301298 while ( nodes . length ) {
302299 const node = nodes . shift ( ) ;
303- // First check the current resources for blend modes.
304300 const graphicStates = node . get ( "ExtGState" ) ;
305301 if ( graphicStates instanceof Dict ) {
306302 for ( let graphicState of graphicStates . getRawValues ( ) ) {
@@ -311,10 +307,11 @@ class PartialEvaluator {
311307 try {
312308 graphicState = xref . fetch ( graphicState ) ;
313309 } catch ( ex ) {
310+ if ( onExtGStateFetchError ?. ( ex ) ) {
311+ return true ;
312+ }
314313 // Avoid parsing a corrupt ExtGState more than once.
315314 processed . put ( graphicState ) ;
316-
317- info ( `hasBlendModes - ignoring ExtGState: "${ ex } ".` ) ;
318315 continue ;
319316 }
320317 }
@@ -324,66 +321,100 @@ class PartialEvaluator {
324321 if ( graphicState . objId ) {
325322 processed . put ( graphicState . objId ) ;
326323 }
324+ if ( checkGraphicState ( graphicState ) ) {
325+ return true ;
326+ }
327+ }
328+ }
327329
328- const bm = graphicState . get ( "BM" ) ;
329- if ( bm instanceof Name ) {
330- if ( bm . name !== "Normal" ) {
331- return true ;
330+ const xObjects = node . get ( "XObject" ) ;
331+ if ( xObjects instanceof Dict ) {
332+ for ( let xObject of xObjects . getRawValues ( ) ) {
333+ if ( xObject instanceof Ref ) {
334+ if ( processed . has ( xObject ) ) {
335+ // The XObject has already been processed, and by avoiding a
336+ // redundant `xref.fetch` we can *significantly* reduce the load
337+ // time for badly generated PDF files (fixes issue6961.pdf).
338+ continue ;
332339 }
333- continue ;
334- }
335- if ( bm !== undefined && Array . isArray ( bm ) ) {
336- for ( const element of bm ) {
337- if ( element instanceof Name && element . name !== "Normal" ) {
340+ try {
341+ xObject = xref . fetch ( xObject ) ;
342+ } catch ( ex ) {
343+ if ( onXObjectFetchError ?. ( ex ) ) {
338344 return true ;
339345 }
346+ // Avoid parsing a corrupt XObject more than once.
347+ processed . put ( xObject ) ;
348+ continue ;
340349 }
341350 }
342- }
343- }
344- // Descend into the XObjects to look for more resources and blend modes.
345- const xObjects = node . get ( "XObject" ) ;
346- if ( ! ( xObjects instanceof Dict ) ) {
347- continue ;
348- }
349- for ( let xObject of xObjects . getRawValues ( ) ) {
350- if ( xObject instanceof Ref ) {
351- if ( processed . has ( xObject ) ) {
352- // The XObject has already been processed, and by avoiding a
353- // redundant `xref.fetch` we can *significantly* reduce the load
354- // time for badly generated PDF files (fixes issue6961.pdf).
351+ if ( ! ( xObject instanceof BaseStream ) ) {
355352 continue ;
356353 }
357- try {
358- xObject = xref . fetch ( xObject ) ;
359- } catch ( ex ) {
360- // Avoid parsing a corrupt XObject more than once.
361- processed . put ( xObject ) ;
362-
363- info ( `hasBlendModes - ignoring XObject: "${ ex } ".` ) ;
354+ if ( xObject . dict . objId ) {
355+ processed . put ( xObject . dict . objId ) ;
356+ }
357+ const xResources = xObject . dict . get ( "Resources" ) ;
358+ if ( ! ( xResources instanceof Dict ) ) {
359+ continue ;
360+ }
361+ // Checking objId to detect an infinite loop.
362+ if ( xResources . objId && processed . has ( xResources . objId ) ) {
364363 continue ;
365364 }
366- }
367- if ( ! ( xObject instanceof BaseStream ) ) {
368- continue ;
369- }
370- if ( xObject . dict . objId ) {
371- processed . put ( xObject . dict . objId ) ;
372- }
373- const xResources = xObject . dict . get ( "Resources" ) ;
374- if ( ! ( xResources instanceof Dict ) ) {
375- continue ;
376- }
377- // Checking objId to detect an infinite loop.
378- if ( xResources . objId && processed . has ( xResources . objId ) ) {
379- continue ;
380- }
381365
382- nodes . push ( xResources ) ;
383- if ( xResources . objId ) {
384- processed . put ( xResources . objId ) ;
366+ nodes . push ( xResources ) ;
367+ if ( xResources . objId ) {
368+ processed . put ( xResources . objId ) ;
369+ }
385370 }
386371 }
372+ if ( onResourceNode ?. ( node , nodes ) ) {
373+ return true ;
374+ }
375+ }
376+ return false ;
377+ }
378+
379+ hasBlendModes ( resources , nonBlendModesSet ) {
380+ if ( ! ( resources instanceof Dict ) ) {
381+ return false ;
382+ }
383+ if ( resources . objId && nonBlendModesSet . has ( resources . objId ) ) {
384+ return false ;
385+ }
386+
387+ const processed = new RefSet ( nonBlendModesSet ) ;
388+ if ( resources . objId ) {
389+ processed . put ( resources . objId ) ;
390+ }
391+ if (
392+ this . _traverseExtGState ( resources , processed , {
393+ checkGraphicState ( graphicState ) {
394+ const bm = graphicState . get ( "BM" ) ;
395+ if ( bm instanceof Name ) {
396+ return bm . name !== "Normal" ;
397+ }
398+ if ( bm !== undefined && Array . isArray ( bm ) ) {
399+ for ( const element of bm ) {
400+ if ( element instanceof Name && element . name !== "Normal" ) {
401+ return true ;
402+ }
403+ }
404+ }
405+ return false ;
406+ } ,
407+ onExtGStateFetchError ( ex ) {
408+ info ( `hasBlendModes - ignoring ExtGState: "${ ex } ".` ) ;
409+ return false ;
410+ } ,
411+ onXObjectFetchError ( ex ) {
412+ info ( `hasBlendModes - ignoring XObject: "${ ex } ".` ) ;
413+ return false ;
414+ } ,
415+ } )
416+ ) {
417+ return true ;
387418 }
388419
389420 // When no blend modes exist, there's no need re-fetch/re-parse any of the
@@ -449,126 +480,77 @@ class PartialEvaluator {
449480 if ( resources . objId ) {
450481 processed . put ( resources . objId ) ;
451482 }
452-
453- const nodes = [ resources ] ,
454- xref = this . xref ;
455- while ( nodes . length ) {
456- const node = nodes . shift ( ) ;
457- const graphicStates = node . get ( "ExtGState" ) ;
458- if ( graphicStates instanceof Dict ) {
459- for ( let graphicState of graphicStates . getRawValues ( ) ) {
460- if ( graphicState instanceof Ref ) {
461- if ( processed . has ( graphicState ) ) {
483+ const xref = this . xref ;
484+ return this . _traverseExtGState ( resources , processed , {
485+ checkGraphicState : graphicState => {
486+ try {
487+ return this . _hasCanvasFiltersInGState ( graphicState ) ;
488+ } catch ( ex ) {
489+ info ( `hasCanvasFilters - failed to inspect filter data: "${ ex } ".` ) ;
490+ return true ;
491+ }
492+ } ,
493+ onExtGStateFetchError ( ex ) {
494+ info ( `hasCanvasFilters - failed to fetch ExtGState: "${ ex } ".` ) ;
495+ return true ;
496+ } ,
497+ onXObjectFetchError ( ex ) {
498+ info ( `hasCanvasFilters - failed to fetch XObject: "${ ex } ".` ) ;
499+ return true ;
500+ } ,
501+ onResourceNode ( node , nodes ) {
502+ const patterns = node . get ( "Pattern" ) ;
503+ if ( ! ( patterns instanceof Dict ) ) {
504+ return false ;
505+ }
506+ for ( let pattern of patterns . getRawValues ( ) ) {
507+ if ( pattern instanceof Ref ) {
508+ if ( processed . has ( pattern ) ) {
462509 continue ;
463510 }
464511 try {
465- graphicState = xref . fetch ( graphicState ) ;
512+ pattern = xref . fetch ( pattern ) ;
466513 } catch ( ex ) {
467- info ( `hasCanvasFilters - failed to fetch ExtGState : "${ ex } ".` ) ;
514+ info ( `hasCanvasFilters - failed to fetch Pattern : "${ ex } ".` ) ;
468515 return true ;
469516 }
470517 }
471- if ( ! ( graphicState instanceof Dict ) ) {
472- continue ;
473- }
474- if ( graphicState . objId ) {
475- processed . put ( graphicState . objId ) ;
476- }
477- try {
478- if ( this . _hasCanvasFiltersInGState ( graphicState ) ) {
479- return true ;
518+ if ( pattern instanceof BaseStream ) {
519+ if ( pattern . dict . objId ) {
520+ processed . put ( pattern . dict . objId ) ;
480521 }
481- } catch ( ex ) {
482- info ( `hasCanvasFilters - failed to inspect filter data: "${ ex } ".` ) ;
483- return true ;
484- }
485- }
486- }
487-
488- const xObjects = node . get ( "XObject" ) ;
489- if ( xObjects instanceof Dict ) {
490- for ( let xObject of xObjects . getRawValues ( ) ) {
491- if ( xObject instanceof Ref ) {
492- if ( processed . has ( xObject ) ) {
522+ const patternResources = pattern . dict . get ( "Resources" ) ;
523+ if ( ! ( patternResources instanceof Dict ) ) {
493524 continue ;
494525 }
495- try {
496- xObject = xref . fetch ( xObject ) ;
497- } catch ( ex ) {
498- info ( `hasCanvasFilters - failed to fetch XObject: " ${ ex } ".` ) ;
499- return true ;
526+ if (
527+ patternResources . objId &&
528+ processed . has ( patternResources . objId )
529+ ) {
530+ continue ;
500531 }
501- }
502- if ( ! ( xObject instanceof BaseStream ) ) {
503- continue ;
504- }
505- if ( xObject . dict . objId ) {
506- processed . put ( xObject . dict . objId ) ;
507- }
508- const xResources = xObject . dict . get ( "Resources" ) ;
509- if ( ! ( xResources instanceof Dict ) ) {
510- continue ;
511- }
512- if ( xResources . objId && processed . has ( xResources . objId ) ) {
513- continue ;
514- }
515-
516- nodes . push ( xResources ) ;
517- if ( xResources . objId ) {
518- processed . put ( xResources . objId ) ;
519- }
520- }
521- }
522532
523- const patterns = node . get ( "Pattern" ) ;
524- if ( ! ( patterns instanceof Dict ) ) {
525- continue ;
526- }
527- for ( let pattern of patterns . getRawValues ( ) ) {
528- if ( pattern instanceof Ref ) {
529- if ( processed . has ( pattern ) ) {
533+ nodes . push ( patternResources ) ;
534+ if ( patternResources . objId ) {
535+ processed . put ( patternResources . objId ) ;
536+ }
530537 continue ;
531538 }
532- try {
533- pattern = xref . fetch ( pattern ) ;
534- } catch ( ex ) {
535- info ( `hasCanvasFilters - failed to fetch Pattern: "${ ex } ".` ) ;
536- return true ;
537- }
538- }
539- if ( pattern instanceof BaseStream ) {
540- if ( pattern . dict . objId ) {
541- processed . put ( pattern . dict . objId ) ;
542- }
543- const patternResources = pattern . dict . get ( "Resources" ) ;
544- if ( ! ( patternResources instanceof Dict ) ) {
539+ if ( ! ( pattern instanceof Dict ) ) {
545540 continue ;
546541 }
547- if ( patternResources . objId && processed . has ( patternResources . objId ) ) {
542+ if ( pattern . objId && processed . has ( pattern . objId ) ) {
548543 continue ;
549544 }
550545
551- nodes . push ( patternResources ) ;
552- if ( patternResources . objId ) {
553- processed . put ( patternResources . objId ) ;
546+ nodes . push ( pattern ) ;
547+ if ( pattern . objId ) {
548+ processed . put ( pattern . objId ) ;
554549 }
555- continue ;
556550 }
557- if ( ! ( pattern instanceof Dict ) ) {
558- continue ;
559- }
560- if ( pattern . objId && processed . has ( pattern . objId ) ) {
561- continue ;
562- }
563-
564- nodes . push ( pattern ) ;
565- if ( pattern . objId ) {
566- processed . put ( pattern . objId ) ;
567- }
568- }
569- }
570-
571- return false ;
551+ return false ;
552+ } ,
553+ } ) ;
572554 }
573555
574556 async fetchBuiltInCMap ( name ) {
0 commit comments