@@ -31,13 +31,6 @@ const DRAW_UPSCALE_FACTOR = 2; // See comment in `PDFThumbnailView.draw` below.
3131const MAX_NUM_SCALING_STEPS = 3 ;
3232const THUMBNAIL_WIDTH = 98 ; // px
3333
34- function zeroCanvas ( c ) {
35- // Zeroing the width and height causes Firefox to release graphics
36- // resources immediately, which can greatly reduce memory consumption.
37- c . width = 0 ;
38- c . height = 0 ;
39- }
40-
4134/**
4235 * @typedef {Object } PDFThumbnailViewOptions
4336 * @property {HTMLDivElement } container - The viewer element.
@@ -61,12 +54,8 @@ function zeroCanvas(c) {
6154 */
6255
6356class TempImageFactory {
64- static #tempCanvas = null ;
65-
6657 static getCanvas ( width , height ) {
67- const tempCanvas = ( this . #tempCanvas ||= document . createElement ( "canvas" ) ) ;
68- tempCanvas . width = width ;
69- tempCanvas . height = height ;
58+ const tempCanvas = new OffscreenCanvas ( width , height ) ;
7059
7160 // Since this is a temporary canvas, we need to fill it with a white
7261 // background ourselves. `#getPageDrawContext` uses CSS rules for this.
@@ -75,14 +64,7 @@ class TempImageFactory {
7564 ctx . fillStyle = "rgb(255, 255, 255)" ;
7665 ctx . fillRect ( 0 , 0 , width , height ) ;
7766 ctx . restore ( ) ;
78- return [ tempCanvas , tempCanvas . getContext ( "2d" ) ] ;
79- }
80-
81- static destroyCanvas ( ) {
82- if ( this . #tempCanvas) {
83- zeroCanvas ( this . #tempCanvas) ;
84- }
85- this . #tempCanvas = null ;
67+ return [ tempCanvas , ctx ] ;
8668 }
8769}
8870
@@ -126,27 +108,24 @@ class PDFThumbnailView {
126108 this . renderingState = RenderingStates . INITIAL ;
127109 this . resume = null ;
128110
129- const anchor = document . createElement ( "a" ) ;
130- anchor . href = linkService . getAnchorUrl ( " #page=" + id ) ;
111+ const anchor = ( this . anchor = document . createElement ( "a" ) ) ;
112+ anchor . href = linkService . getAnchorUrl ( ` #page=${ id } ` ) ;
131113 anchor . setAttribute ( "data-l10n-id" , "pdfjs-thumb-page-title" ) ;
132114 anchor . setAttribute ( "data-l10n-args" , this . #pageL10nArgs) ;
133- anchor . onclick = function ( ) {
115+ anchor . onclick = ( ) => {
134116 linkService . goToPage ( id ) ;
135117 return false ;
136118 } ;
137- this . anchor = anchor ;
138119
139- const div = document . createElement ( "div" ) ;
140- div . className = "thumbnail" ;
120+ const div = ( this . div = document . createElement ( "div" ) ) ;
121+ div . classList . add ( "thumbnail" , "missingThumbnailImage" ) ;
141122 div . setAttribute ( "data-page-number" , this . id ) ;
142- this . div = div ;
143123 this . #updateDims( ) ;
144124
145- const img = document . createElement ( "div" ) ;
146- img . className = "thumbnailImage" ;
147- this . _placeholderImg = img ;
125+ const image = ( this . image = document . createElement ( "img" ) ) ;
126+ image . className = "thumbnailImage" ;
148127
149- div . append ( img ) ;
128+ div . append ( image ) ;
150129 anchor . append ( div ) ;
151130 container . append ( anchor ) ;
152131 }
@@ -155,13 +134,11 @@ class PDFThumbnailView {
155134 const { width, height } = this . viewport ;
156135 const ratio = width / height ;
157136
158- this . canvasWidth = THUMBNAIL_WIDTH ;
159- this . canvasHeight = ( this . canvasWidth / ratio ) | 0 ;
160- this . scale = this . canvasWidth / width ;
137+ const canvasWidth = ( this . canvasWidth = THUMBNAIL_WIDTH ) ;
138+ const canvasHeight = ( this . canvasHeight = ( canvasWidth / ratio ) | 0 ) ;
139+ this . scale = canvasWidth / width ;
161140
162- const { style } = this . div ;
163- style . setProperty ( "--thumbnail-width" , `${ this . canvasWidth } px` ) ;
164- style . setProperty ( "--thumbnail-height" , `${ this . canvasHeight } px` ) ;
141+ this . div . style . height = `${ canvasHeight } px` ;
165142 }
166143
167144 setPdfPage ( pdfPage ) {
@@ -175,14 +152,16 @@ class PDFThumbnailView {
175152 reset ( ) {
176153 this . cancelRendering ( ) ;
177154 this . renderingState = RenderingStates . INITIAL ;
178-
179- this . div . removeAttribute ( "data-loaded" ) ;
180- this . image ?. replaceWith ( this . _placeholderImg ) ;
181155 this . #updateDims( ) ;
182156
183- if ( this . image ) {
184- this . image . removeAttribute ( "src" ) ;
185- delete this . image ;
157+ const { image } = this ;
158+ const url = image . src ;
159+ if ( url ) {
160+ URL . revokeObjectURL ( url ) ;
161+ image . removeAttribute ( "data-l10n-id" ) ;
162+ image . removeAttribute ( "data-l10n-args" ) ;
163+ image . src = "" ;
164+ this . div . classList . add ( "missingThumbnailImage" ) ;
186165 }
187166 }
188167
@@ -213,7 +192,6 @@ class PDFThumbnailView {
213192 #getPageDrawContext( upscaleFactor = 1 ) {
214193 // Keep the no-thumbnail outline visible, i.e. `data-loaded === false`,
215194 // until rendering/image conversion is complete, to avoid display issues.
216- const canvas = document . createElement ( "canvas" ) ;
217195 const outputScale = new OutputScale ( ) ;
218196 const width = upscaleFactor * this . canvasWidth ,
219197 height = upscaleFactor * this . canvasHeight ;
@@ -224,8 +202,10 @@ class PDFThumbnailView {
224202 this . maxCanvasPixels ,
225203 this . maxCanvasDim
226204 ) ;
227- canvas . width = ( width * outputScale . sx ) | 0 ;
228- canvas . height = ( height * outputScale . sy ) | 0 ;
205+ const canvas = new OffscreenCanvas (
206+ ( width * outputScale . sx ) | 0 ,
207+ ( height * outputScale . sy ) | 0
208+ ) ;
229209
230210 const transform = outputScale . scaled
231211 ? [ outputScale . sx , 0 , 0 , outputScale . sy , 0 , 0 ]
@@ -239,18 +219,13 @@ class PDFThumbnailView {
239219 throw new Error ( "#convertCanvasToImage: Rendering has not finished." ) ;
240220 }
241221 const reducedCanvas = this . #reduceImage( canvas ) ;
242-
243- const image = document . createElement ( "img" ) ;
244- image . className = "thumbnailImage" ;
245- image . setAttribute ( "data-l10n-id" , "pdfjs-thumb-page-canvas" ) ;
246- image . setAttribute ( "data-l10n-args" , this . #pageL10nArgs) ;
247- image . src = reducedCanvas . toDataURL ( ) ;
248- this . image = image ;
249-
250- this . div . setAttribute ( "data-loaded" , true ) ;
251- this . _placeholderImg . replaceWith ( image ) ;
252-
253- zeroCanvas ( reducedCanvas ) ;
222+ reducedCanvas . convertToBlob ( ) . then ( blob => {
223+ const { image } = this ;
224+ image . src = URL . createObjectURL ( blob ) ;
225+ image . setAttribute ( "data-l10n-id" , "pdfjs-thumb-page-canvas" ) ;
226+ image . setAttribute ( "data-l10n-args" , this . #pageL10nArgs) ;
227+ this . div . classList . remove ( "missingThumbnailImage" ) ;
228+ } ) ;
254229 }
255230
256231 async draw ( ) {
@@ -303,7 +278,6 @@ class PDFThumbnailView {
303278 await renderTask . promise ;
304279 } catch ( e ) {
305280 if ( e instanceof RenderingCancelledException ) {
306- zeroCanvas ( canvas ) ;
307281 return ;
308282 }
309283 error = e ;
@@ -318,7 +292,6 @@ class PDFThumbnailView {
318292 this . renderingState = RenderingStates . FINISHED ;
319293
320294 this . #convertCanvasToImage( canvas ) ;
321- zeroCanvas ( canvas ) ;
322295
323296 this . eventBus . dispatch ( "thumbnailrendered" , {
324297 source : this ,
@@ -449,14 +422,9 @@ class PDFThumbnailView {
449422 */
450423 setPageLabel ( label ) {
451424 this . pageLabel = typeof label === "string" ? label : null ;
452-
453425 this . anchor . setAttribute ( "data-l10n-args" , this . #pageL10nArgs) ;
454-
455- if ( this . renderingState !== RenderingStates . FINISHED ) {
456- return ;
457- }
458- this . image ?. setAttribute ( "data-l10n-args" , this . #pageL10nArgs) ;
426+ this . image . setAttribute ( "data-l10n-args" , this . #pageL10nArgs) ;
459427 }
460428}
461429
462- export { PDFThumbnailView , TempImageFactory } ;
430+ export { PDFThumbnailView } ;
0 commit comments