Merge pull request #16151 from Snuffleupagus/DefaultFilterFactory
[api-minor] Extend general transfer function support to browsers without `OffscreenCanvas`
This commit is contained in:
		
						commit
						5e4b3d13eb
					
				| @ -417,8 +417,6 @@ class Page { | ||||
|           this.resources, | ||||
|           this.nonBlendModesSet | ||||
|         ), | ||||
|         isOffscreenCanvasSupported: | ||||
|           this.evaluatorOptions.isOffscreenCanvasSupported, | ||||
|         pageIndex: this.pageIndex, | ||||
|         cacheKey, | ||||
|       }); | ||||
|  | ||||
| @ -46,8 +46,8 @@ import { | ||||
|   deprecated, | ||||
|   DOMCanvasFactory, | ||||
|   DOMCMapReaderFactory, | ||||
|   DOMFilterFactory, | ||||
|   DOMStandardFontDataFactory, | ||||
|   FilterFactory, | ||||
|   isDataScheme, | ||||
|   isValidFetchUrl, | ||||
|   loadScript, | ||||
| @ -71,17 +71,20 @@ const DELAYED_CLEANUP_TIMEOUT = 5000; // ms | ||||
| 
 | ||||
| let DefaultCanvasFactory = DOMCanvasFactory; | ||||
| let DefaultCMapReaderFactory = DOMCMapReaderFactory; | ||||
| let DefaultFilterFactory = DOMFilterFactory; | ||||
| let DefaultStandardFontDataFactory = DOMStandardFontDataFactory; | ||||
| 
 | ||||
| if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC") && isNodeJS) { | ||||
|   const { | ||||
|     NodeCanvasFactory, | ||||
|     NodeCMapReaderFactory, | ||||
|     NodeFilterFactory, | ||||
|     NodeStandardFontDataFactory, | ||||
|   } = require("./node_utils.js"); | ||||
| 
 | ||||
|   DefaultCanvasFactory = NodeCanvasFactory; | ||||
|   DefaultCMapReaderFactory = NodeCMapReaderFactory; | ||||
|   DefaultFilterFactory = NodeFilterFactory; | ||||
|   DefaultStandardFontDataFactory = NodeStandardFontDataFactory; | ||||
| } | ||||
| 
 | ||||
| @ -342,7 +345,7 @@ function getDocument(src) { | ||||
|   const canvasFactory = | ||||
|     src.canvasFactory || new DefaultCanvasFactory({ ownerDocument }); | ||||
|   const filterFactory = | ||||
|     src.filterFactory || new FilterFactory({ docId, ownerDocument }); | ||||
|     src.filterFactory || new DefaultFilterFactory({ docId, ownerDocument }); | ||||
| 
 | ||||
|   // Parameters only intended for development/testing purposes.
 | ||||
|   const styleElement = | ||||
| @ -784,6 +787,13 @@ class PDFDocumentProxy { | ||||
|     return this._transport.annotationStorage; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @type {Object} The filter factory instance. | ||||
|    */ | ||||
|   get filterFactory() { | ||||
|     return this._transport.filterFactory; | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
|    * @type {number} Total number of pages in the PDF file. | ||||
|    */ | ||||
| @ -1512,25 +1522,19 @@ class PDFPageProxy { | ||||
|       intentState.displayReadyCapability.promise, | ||||
|       optionalContentConfigPromise, | ||||
|     ]) | ||||
|       .then( | ||||
|         ([ | ||||
|           { transparency, isOffscreenCanvasSupported }, | ||||
|           optionalContentConfig, | ||||
|         ]) => { | ||||
|           if (this.#pendingCleanup) { | ||||
|             complete(); | ||||
|             return; | ||||
|           } | ||||
|           this._stats?.time("Rendering"); | ||||
| 
 | ||||
|           internalRenderTask.initializeGraphics({ | ||||
|             transparency, | ||||
|             isOffscreenCanvasSupported, | ||||
|             optionalContentConfig, | ||||
|           }); | ||||
|           internalRenderTask.operatorListChanged(); | ||||
|       .then(([transparency, optionalContentConfig]) => { | ||||
|         if (this.#pendingCleanup) { | ||||
|           complete(); | ||||
|           return; | ||||
|         } | ||||
|       ) | ||||
|         this._stats?.time("Rendering"); | ||||
| 
 | ||||
|         internalRenderTask.initializeGraphics({ | ||||
|           transparency, | ||||
|           optionalContentConfig, | ||||
|         }); | ||||
|         internalRenderTask.operatorListChanged(); | ||||
|       }) | ||||
|       .catch(complete); | ||||
| 
 | ||||
|     return renderTask; | ||||
| @ -1754,7 +1758,7 @@ class PDFPageProxy { | ||||
|   /** | ||||
|    * @private | ||||
|    */ | ||||
|   _startRenderPage(transparency, isOffscreenCanvasSupported, cacheKey) { | ||||
|   _startRenderPage(transparency, cacheKey) { | ||||
|     const intentState = this._intentStates.get(cacheKey); | ||||
|     if (!intentState) { | ||||
|       return; // Rendering was cancelled.
 | ||||
| @ -1763,10 +1767,7 @@ class PDFPageProxy { | ||||
| 
 | ||||
|     // TODO Refactor RenderPageRequest to separate rendering
 | ||||
|     // and operator list logic
 | ||||
|     intentState.displayReadyCapability?.resolve({ | ||||
|       transparency, | ||||
|       isOffscreenCanvasSupported, | ||||
|     }); | ||||
|     intentState.displayReadyCapability?.resolve(transparency); | ||||
|   } | ||||
| 
 | ||||
|   /** | ||||
| @ -2728,11 +2729,7 @@ class WorkerTransport { | ||||
|       } | ||||
| 
 | ||||
|       const page = this.#pageCache.get(data.pageIndex); | ||||
|       page._startRenderPage( | ||||
|         data.transparency, | ||||
|         data.isOffscreenCanvasSupported, | ||||
|         data.cacheKey | ||||
|       ); | ||||
|       page._startRenderPage(data.transparency, data.cacheKey); | ||||
|     }); | ||||
| 
 | ||||
|     messageHandler.on("commonobj", ([id, type, exportedData]) => { | ||||
| @ -3294,11 +3291,7 @@ class InternalRenderTask { | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   initializeGraphics({ | ||||
|     transparency = false, | ||||
|     isOffscreenCanvasSupported = false, | ||||
|     optionalContentConfig, | ||||
|   }) { | ||||
|   initializeGraphics({ transparency = false, optionalContentConfig }) { | ||||
|     if (this.cancelled) { | ||||
|       return; | ||||
|     } | ||||
| @ -3325,7 +3318,7 @@ class InternalRenderTask { | ||||
|       this.commonObjs, | ||||
|       this.objs, | ||||
|       this.canvasFactory, | ||||
|       isOffscreenCanvasSupported ? this.filterFactory : null, | ||||
|       this.filterFactory, | ||||
|       { optionalContentConfig }, | ||||
|       this.annotationCanvasMap, | ||||
|       this.pageColors | ||||
| @ -3430,6 +3423,7 @@ export { | ||||
|   build, | ||||
|   DefaultCanvasFactory, | ||||
|   DefaultCMapReaderFactory, | ||||
|   DefaultFilterFactory, | ||||
|   DefaultStandardFontDataFactory, | ||||
|   getDocument, | ||||
|   LoopbackPort, | ||||
|  | ||||
| @ -15,6 +15,20 @@ | ||||
| 
 | ||||
| import { CMapCompressionType, unreachable } from "../shared/util.js"; | ||||
| 
 | ||||
| class BaseFilterFactory { | ||||
|   constructor() { | ||||
|     if (this.constructor === BaseFilterFactory) { | ||||
|       unreachable("Cannot initialize BaseFilterFactory."); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   addFilter(maps) { | ||||
|     return "none"; | ||||
|   } | ||||
| 
 | ||||
|   destroy() {} | ||||
| } | ||||
| 
 | ||||
| class BaseCanvasFactory { | ||||
|   constructor() { | ||||
|     if (this.constructor === BaseCanvasFactory) { | ||||
| @ -179,6 +193,7 @@ class BaseSVGFactory { | ||||
| export { | ||||
|   BaseCanvasFactory, | ||||
|   BaseCMapReaderFactory, | ||||
|   BaseFilterFactory, | ||||
|   BaseStandardFontDataFactory, | ||||
|   BaseSVGFactory, | ||||
| }; | ||||
|  | ||||
| @ -491,7 +491,7 @@ class CanvasExtraState { | ||||
|     this.strokeAlpha = 1; | ||||
|     this.lineWidth = 1; | ||||
|     this.activeSMask = null; | ||||
|     this.transferMaps = null; | ||||
|     this.transferMaps = "none"; | ||||
| 
 | ||||
|     this.startNewPathAndClipBox([0, 0, width, height]); | ||||
|   } | ||||
| @ -588,7 +588,7 @@ class CanvasExtraState { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function putBinaryImageData(ctx, imgData, transferMaps = null) { | ||||
| function putBinaryImageData(ctx, imgData) { | ||||
|   if (typeof ImageData !== "undefined" && imgData instanceof ImageData) { | ||||
|     ctx.putImageData(imgData, 0, 0); | ||||
|     return; | ||||
| @ -618,24 +618,6 @@ function putBinaryImageData(ctx, imgData, transferMaps = null) { | ||||
|   const dest = chunkImgData.data; | ||||
|   let i, j, thisChunkHeight, elemsInThisChunk; | ||||
| 
 | ||||
|   let transferMapRed, transferMapGreen, transferMapBlue, transferMapGray; | ||||
|   if (transferMaps) { | ||||
|     switch (transferMaps.length) { | ||||
|       case 1: | ||||
|         transferMapRed = transferMaps[0]; | ||||
|         transferMapGreen = transferMaps[0]; | ||||
|         transferMapBlue = transferMaps[0]; | ||||
|         transferMapGray = transferMaps[0]; | ||||
|         break; | ||||
|       case 4: | ||||
|         transferMapRed = transferMaps[0]; | ||||
|         transferMapGreen = transferMaps[1]; | ||||
|         transferMapBlue = transferMaps[2]; | ||||
|         transferMapGray = transferMaps[3]; | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   // There are multiple forms in which the pixel data can be passed, and
 | ||||
|   // imgData.kind tells us which one this is.
 | ||||
|   if (imgData.kind === ImageKind.GRAYSCALE_1BPP) { | ||||
| @ -644,14 +626,8 @@ function putBinaryImageData(ctx, imgData, transferMaps = null) { | ||||
|     const dest32 = new Uint32Array(dest.buffer, 0, dest.byteLength >> 2); | ||||
|     const dest32DataLength = dest32.length; | ||||
|     const fullSrcDiff = (width + 7) >> 3; | ||||
|     let white = 0xffffffff; | ||||
|     let black = FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff; | ||||
| 
 | ||||
|     if (transferMapGray) { | ||||
|       if (transferMapGray[0] === 0xff && transferMapGray[0xff] === 0) { | ||||
|         [white, black] = [black, white]; | ||||
|       } | ||||
|     } | ||||
|     const white = 0xffffffff; | ||||
|     const black = FeatureTest.isLittleEndian ? 0xff000000 : 0x000000ff; | ||||
| 
 | ||||
|     for (i = 0; i < totalChunks; i++) { | ||||
|       thisChunkHeight = i < fullChunks ? FULL_CHUNK_HEIGHT : partialChunkHeight; | ||||
| @ -693,32 +669,12 @@ function putBinaryImageData(ctx, imgData, transferMaps = null) { | ||||
|     } | ||||
|   } else if (imgData.kind === ImageKind.RGBA_32BPP) { | ||||
|     // RGBA, 32-bits per pixel.
 | ||||
|     const hasTransferMaps = !!( | ||||
|       transferMapRed || | ||||
|       transferMapGreen || | ||||
|       transferMapBlue | ||||
|     ); | ||||
| 
 | ||||
|     j = 0; | ||||
|     elemsInThisChunk = width * FULL_CHUNK_HEIGHT * 4; | ||||
|     for (i = 0; i < fullChunks; i++) { | ||||
|       dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); | ||||
|       srcPos += elemsInThisChunk; | ||||
| 
 | ||||
|       if (hasTransferMaps) { | ||||
|         for (let k = 0; k < elemsInThisChunk; k += 4) { | ||||
|           if (transferMapRed) { | ||||
|             dest[k + 0] = transferMapRed[dest[k + 0]]; | ||||
|           } | ||||
|           if (transferMapGreen) { | ||||
|             dest[k + 1] = transferMapGreen[dest[k + 1]]; | ||||
|           } | ||||
|           if (transferMapBlue) { | ||||
|             dest[k + 2] = transferMapBlue[dest[k + 2]]; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       ctx.putImageData(chunkImgData, 0, j); | ||||
|       j += FULL_CHUNK_HEIGHT; | ||||
|     } | ||||
| @ -726,30 +682,10 @@ function putBinaryImageData(ctx, imgData, transferMaps = null) { | ||||
|       elemsInThisChunk = width * partialChunkHeight * 4; | ||||
|       dest.set(src.subarray(srcPos, srcPos + elemsInThisChunk)); | ||||
| 
 | ||||
|       if (hasTransferMaps) { | ||||
|         for (let k = 0; k < elemsInThisChunk; k += 4) { | ||||
|           if (transferMapRed) { | ||||
|             dest[k + 0] = transferMapRed[dest[k + 0]]; | ||||
|           } | ||||
|           if (transferMapGreen) { | ||||
|             dest[k + 1] = transferMapGreen[dest[k + 1]]; | ||||
|           } | ||||
|           if (transferMapBlue) { | ||||
|             dest[k + 2] = transferMapBlue[dest[k + 2]]; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       ctx.putImageData(chunkImgData, 0, j); | ||||
|     } | ||||
|   } else if (imgData.kind === ImageKind.RGB_24BPP) { | ||||
|     // RGB, 24-bits per pixel.
 | ||||
|     const hasTransferMaps = !!( | ||||
|       transferMapRed || | ||||
|       transferMapGreen || | ||||
|       transferMapBlue | ||||
|     ); | ||||
| 
 | ||||
|     thisChunkHeight = FULL_CHUNK_HEIGHT; | ||||
|     elemsInThisChunk = width * thisChunkHeight; | ||||
|     for (i = 0; i < totalChunks; i++) { | ||||
| @ -766,20 +702,6 @@ function putBinaryImageData(ctx, imgData, transferMaps = null) { | ||||
|         dest[destPos++] = 255; | ||||
|       } | ||||
| 
 | ||||
|       if (hasTransferMaps) { | ||||
|         for (let k = 0; k < destPos; k += 4) { | ||||
|           if (transferMapRed) { | ||||
|             dest[k + 0] = transferMapRed[dest[k + 0]]; | ||||
|           } | ||||
|           if (transferMapGreen) { | ||||
|             dest[k + 1] = transferMapGreen[dest[k + 1]]; | ||||
|           } | ||||
|           if (transferMapBlue) { | ||||
|             dest[k + 2] = transferMapBlue[dest[k + 2]]; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       ctx.putImageData(chunkImgData, 0, i * FULL_CHUNK_HEIGHT); | ||||
|     } | ||||
|   } else { | ||||
| @ -865,7 +787,10 @@ function resetCtxToDefault(ctx, foregroundColor) { | ||||
|     ctx.setLineDash([]); | ||||
|     ctx.lineDashOffset = 0; | ||||
|   } | ||||
|   if (!isNodeJS) { | ||||
|   if ( | ||||
|     (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || | ||||
|     !isNodeJS | ||||
|   ) { | ||||
|     ctx.filter = "none"; | ||||
|   } | ||||
| } | ||||
| @ -1591,12 +1516,8 @@ class CanvasGraphics { | ||||
|           this.checkSMaskState(); | ||||
|           break; | ||||
|         case "TR": | ||||
|           if (this.filterFactory) { | ||||
|             this.ctx.filter = this.current.transferMaps = | ||||
|               this.filterFactory.addFilter(value); | ||||
|           } else { | ||||
|             this.current.transferMaps = value; | ||||
|           } | ||||
|           this.ctx.filter = this.current.transferMaps = | ||||
|             this.filterFactory.addFilter(value); | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
| @ -3042,8 +2963,25 @@ class CanvasGraphics { | ||||
|     this.paintInlineImageXObjectGroup(imgData, map); | ||||
|   } | ||||
| 
 | ||||
|   applyTransferMapsToCanvas(ctx) { | ||||
|     if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { | ||||
|       if (this.current.transferMaps !== "none") { | ||||
|         warn("Ignoring transferMaps - `OffscreenCanvas` support is disabled."); | ||||
|       } | ||||
|       return ctx.canvas; | ||||
|     } | ||||
|     if (this.current.transferMaps === "none") { | ||||
|       return ctx.canvas; | ||||
|     } | ||||
|     ctx.filter = this.current.transferMaps; | ||||
|     ctx.drawImage(ctx.canvas, 0, 0); | ||||
|     ctx.filter = "none"; | ||||
| 
 | ||||
|     return ctx.canvas; | ||||
|   } | ||||
| 
 | ||||
|   applyTransferMapsToBitmap(imgData) { | ||||
|     if (!this.current.transferMaps || this.current.transferMaps === "none") { | ||||
|     if (this.current.transferMaps === "none") { | ||||
|       return imgData.bitmap; | ||||
|     } | ||||
|     const { bitmap, width, height } = imgData; | ||||
| @ -3070,7 +3008,10 @@ class CanvasGraphics { | ||||
| 
 | ||||
|     this.save(); | ||||
| 
 | ||||
|     if (!isNodeJS) { | ||||
|     if ( | ||||
|       (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) || | ||||
|       !isNodeJS | ||||
|     ) { | ||||
|       // The filter, if any, will be applied in applyTransferMapsToBitmap.
 | ||||
|       // It must be applied to the image before rescaling else some artifacts
 | ||||
|       // could appear.
 | ||||
| @ -3097,8 +3038,8 @@ class CanvasGraphics { | ||||
|         height | ||||
|       ); | ||||
|       const tmpCtx = tmpCanvas.context; | ||||
|       putBinaryImageData(tmpCtx, imgData, this.current.transferMaps); | ||||
|       imgToPaint = tmpCanvas.canvas; | ||||
|       putBinaryImageData(tmpCtx, imgData); | ||||
|       imgToPaint = this.applyTransferMapsToCanvas(tmpCtx); | ||||
|     } | ||||
| 
 | ||||
|     const scaled = this._scaleImage( | ||||
| @ -3140,8 +3081,8 @@ class CanvasGraphics { | ||||
| 
 | ||||
|       const tmpCanvas = this.cachedCanvases.getCanvas("inlineImage", w, h); | ||||
|       const tmpCtx = tmpCanvas.context; | ||||
|       putBinaryImageData(tmpCtx, imgData, this.current.transferMaps); | ||||
|       imgToPaint = tmpCanvas.canvas; | ||||
|       putBinaryImageData(tmpCtx, imgData); | ||||
|       imgToPaint = this.applyTransferMapsToCanvas(tmpCtx); | ||||
|     } | ||||
| 
 | ||||
|     for (const entry of map) { | ||||
|  | ||||
| @ -16,6 +16,7 @@ | ||||
| import { | ||||
|   BaseCanvasFactory, | ||||
|   BaseCMapReaderFactory, | ||||
|   BaseFilterFactory, | ||||
|   BaseStandardFontDataFactory, | ||||
|   BaseSVGFactory, | ||||
| } from "./base_factory.js"; | ||||
| @ -48,7 +49,7 @@ class PixelsPerInch { | ||||
|  * an image without the need to apply them on the pixel arrays: the renderer | ||||
|  * does the magic for us. | ||||
|  */ | ||||
| class FilterFactory { | ||||
| class DOMFilterFactory extends BaseFilterFactory { | ||||
|   #_cache; | ||||
| 
 | ||||
|   #_defs; | ||||
| @ -60,6 +61,7 @@ class FilterFactory { | ||||
|   #id = 0; | ||||
| 
 | ||||
|   constructor({ docId, ownerDocument = globalThis.document } = {}) { | ||||
|     super(); | ||||
|     this.#docId = docId; | ||||
|     this.#document = ownerDocument; | ||||
|   } | ||||
| @ -823,9 +825,9 @@ export { | ||||
|   deprecated, | ||||
|   DOMCanvasFactory, | ||||
|   DOMCMapReaderFactory, | ||||
|   DOMFilterFactory, | ||||
|   DOMStandardFontDataFactory, | ||||
|   DOMSVGFactory, | ||||
|   FilterFactory, | ||||
|   getColorValues, | ||||
|   getCurrentTransform, | ||||
|   getCurrentTransformInverse, | ||||
|  | ||||
| @ -17,6 +17,7 @@ | ||||
| import { | ||||
|   BaseCanvasFactory, | ||||
|   BaseCMapReaderFactory, | ||||
|   BaseFilterFactory, | ||||
|   BaseStandardFontDataFactory, | ||||
| } from "./base_factory.js"; | ||||
| 
 | ||||
| @ -39,6 +40,8 @@ const fetchData = function (url) { | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| class NodeFilterFactory extends BaseFilterFactory {} | ||||
| 
 | ||||
| class NodeCanvasFactory extends BaseCanvasFactory { | ||||
|   /** | ||||
|    * @ignore | ||||
| @ -72,5 +75,6 @@ class NodeStandardFontDataFactory extends BaseStandardFontDataFactory { | ||||
| export { | ||||
|   NodeCanvasFactory, | ||||
|   NodeCMapReaderFactory, | ||||
|   NodeFilterFactory, | ||||
|   NodeStandardFontDataFactory, | ||||
| }; | ||||
|  | ||||
| @ -51,7 +51,6 @@ import { | ||||
|   version, | ||||
| } from "./display/api.js"; | ||||
| import { | ||||
|   FilterFactory, | ||||
|   getFilenameFromUrl, | ||||
|   getPdfFilenameFromUrl, | ||||
|   getXfaPageViewport, | ||||
| @ -91,7 +90,6 @@ export { | ||||
|   createPromiseCapability, | ||||
|   createValidAbsoluteUrl, | ||||
|   FeatureTest, | ||||
|   FilterFactory, | ||||
|   getDocument, | ||||
|   getFilenameFromUrl, | ||||
|   getPdfFilenameFromUrl, | ||||
|  | ||||
| @ -469,6 +469,8 @@ class Driver { | ||||
|             .getElementsByTagName("head")[0] | ||||
|             .append(xfaStyleElement); | ||||
|         } | ||||
|         const isOffscreenCanvasSupported = | ||||
|           task.isOffscreenCanvasSupported === false ? false : undefined; | ||||
| 
 | ||||
|         const loadingTask = getDocument({ | ||||
|           url: new URL(task.file, window.location), | ||||
| @ -480,6 +482,7 @@ class Driver { | ||||
|           useSystemFonts: task.useSystemFonts, | ||||
|           useWorkerFetch: task.useWorkerFetch, | ||||
|           enableXfa: task.enableXfa, | ||||
|           isOffscreenCanvasSupported, | ||||
|           styleElement: xfaStyleElement, | ||||
|         }); | ||||
|         let promise = loadingTask.promise; | ||||
|  | ||||
| @ -6281,6 +6281,13 @@ | ||||
|        "rounds": 1, | ||||
|        "type": "eq" | ||||
|     }, | ||||
|     {  "id": "issue6931-disable-isOffscreenCanvasSupported", | ||||
|        "file": "pdfs/issue6931_reduced.pdf", | ||||
|        "md5": "e61388913821a5e044bf85a5846d6d9a", | ||||
|        "rounds": 1, | ||||
|        "type": "eq", | ||||
|        "isOffscreenCanvasSupported": false | ||||
|     }, | ||||
|     {  "id": "annotation-button-widget-annotations", | ||||
|        "file": "pdfs/annotation-button-widget.pdf", | ||||
|        "md5": "5cf23adfff84256d9cfe261bea96dade", | ||||
| @ -7474,6 +7481,15 @@ | ||||
|       "link": true, | ||||
|       "type": "eq" | ||||
|    }, | ||||
|    { | ||||
|       "id": "issue16114-disable-isOffscreenCanvasSupported", | ||||
|       "file": "pdfs/issue16114.pdf", | ||||
|       "md5": "c04827ea33692e0f94a5e51716d9aa2e", | ||||
|       "rounds": 1, | ||||
|       "link": true, | ||||
|       "type": "eq", | ||||
|       "isOffscreenCanvasSupported": false | ||||
|    }, | ||||
|    { | ||||
|       "id": "bug1820909", | ||||
|       "file": "pdfs/bug1820909.pdf", | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user