pdf.js.mirror/test/unit/pdf_spec.js
Nicolò Ribaudo 886c90d1a5
Add support for right-clicking on images
This patch adds right-click support for images in the PDF, allowing
users to download them. To minimize memory consumption, we:
- Do not store the images separately, and instead crop them out of the
  PDF page canvas
- Only extract the images when needed (i.e. when the user right-clicks
  on them), rather than eagery having all of them available.

To do so, we layer one empty 0x0 canvas per image, stretched to cover
the whole image, and only populate its contents on right click.
These images need to be inside the text layer: they cannot be _behind_
it, otherwise they would be covered by the text layer's container and
not be clickable, and they cannot be in front of it, otherwise they
would make the text spans unselectable.

This feature is managed by a new preference, `imagesRightClickMinSize`:
- when it's set to `-1`, right-click support is disabled
- when set to `0`, all images are available for right click
- when set to a positive integer, only images whose width and height are
  greater than or equal to that value (in the PDF page frame of
  reference) are available for right click.

This features is disabled by default outside of MOZCENTRAL, as it
significantly degrades the text selection experience in non-Firefox
browsers.
2026-03-10 14:51:03 +01:00

187 lines
4.9 KiB
JavaScript

/* Copyright 2023 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
AbortException,
AnnotationEditorParamsType,
AnnotationEditorType,
AnnotationMode,
AnnotationType,
createValidAbsoluteUrl,
FeatureTest,
getUuid,
ImageKind,
InvalidPDFException,
makeArr,
makeMap,
makeObj,
MathClamp,
normalizeUnicode,
OPS,
PasswordResponses,
PermissionFlag,
ResponseException,
shadow,
updateUrlHash,
Util,
VerbosityLevel,
} from "../../src/shared/util.js";
import {
applyOpacity,
CSSConstants,
fetchData,
findContrastColor,
getFilenameFromUrl,
getPdfFilenameFromUrl,
getRGB,
getXfaPageViewport,
isDataScheme,
isPdfFile,
noContextMenu,
OutputScale,
PDFDateString,
PixelsPerInch,
RenderingCancelledException,
renderRichText,
setLayerDimensions,
stopEvent,
SupportedImageMimeTypes,
} from "../../src/display/display_utils.js";
import {
build,
getDocument,
PDFDataRangeTransport,
PDFWorker,
version,
} from "../../src/display/api.js";
import { AnnotationEditorLayer } from "../../src/display/editor/annotation_editor_layer.js";
import { AnnotationEditorUIManager } from "../../src/display/editor/tools.js";
import { AnnotationLayer } from "../../src/display/annotation_layer.js";
import { ColorPicker } from "../../src/display/editor/color_picker.js";
import { DOMSVGFactory } from "../../src/display/svg_factory.js";
import { DrawLayer } from "../../src/display/draw_layer.js";
import { GlobalWorkerOptions } from "../../src/display/worker_options.js";
import { isValidExplicitDest } from "../../src/display/api_utils.js";
import { SignatureExtractor } from "../../src/display/editor/drawers/signaturedraw.js";
import { TextLayer } from "../../src/display/text_layer.js";
import { TextLayerImages } from "../../src/display/text_layer_images.js";
import { TouchManager } from "../../src/display/touch_manager.js";
import { XfaLayer } from "../../src/display/xfa_layer.js";
const expectedAPI = Object.freeze({
AbortException,
AnnotationEditorLayer,
AnnotationEditorParamsType,
AnnotationEditorType,
AnnotationEditorUIManager,
AnnotationLayer,
AnnotationMode,
AnnotationType,
applyOpacity,
build,
ColorPicker,
createValidAbsoluteUrl,
CSSConstants,
DOMSVGFactory,
DrawLayer,
FeatureTest,
fetchData,
findContrastColor,
getDocument,
getFilenameFromUrl,
getPdfFilenameFromUrl,
getRGB,
getUuid,
getXfaPageViewport,
GlobalWorkerOptions,
ImageKind,
InvalidPDFException,
isDataScheme,
isPdfFile,
isValidExplicitDest,
makeArr,
makeMap,
makeObj,
MathClamp,
noContextMenu,
normalizeUnicode,
OPS,
OutputScale,
PasswordResponses,
PDFDataRangeTransport,
PDFDateString,
PDFWorker,
PermissionFlag,
PixelsPerInch,
RenderingCancelledException,
renderRichText,
ResponseException,
setLayerDimensions,
shadow,
SignatureExtractor,
stopEvent,
SupportedImageMimeTypes,
TextLayer,
TextLayerImages,
TouchManager,
updateUrlHash,
Util,
VerbosityLevel,
version,
XfaLayer,
});
describe("pdfjs_api", function () {
it("checks that the *official* PDF.js API exposes the expected functionality", async function () {
// eslint-disable-next-line no-unsanitized/method
const pdfjsAPI = await import(
typeof PDFJSDev !== "undefined" && PDFJSDev.test("LIB")
? "../../pdf.js"
: "../../src/pdf.js"
);
// The imported Object contains an (automatically) inserted Symbol,
// hence we copy the data to allow using a simple comparison below.
expect({ ...pdfjsAPI }).toEqual(expectedAPI);
expect(Object.keys(globalThis.pdfjsLib).sort()).toEqual(
Object.keys(expectedAPI).sort()
);
});
});
describe("web_pdfjsLib", function () {
it("checks that the viewer re-exports the expected API functionality", async function () {
// Load the API globally, as the viewer does.
// eslint-disable-next-line no-unsanitized/method
await import(
typeof PDFJSDev !== "undefined" && PDFJSDev.test("LIB")
? "../../../generic-legacy/build/pdf.mjs"
: "../../build/generic/build/pdf.mjs"
);
// eslint-disable-next-line no-unsanitized/method
const webPdfjsLib = await import(
typeof PDFJSDev !== "undefined" && PDFJSDev.test("LIB")
? "../../../../web/pdfjs.js"
: "../../web/pdfjs.js"
);
expect(Object.keys(webPdfjsLib).sort()).toEqual(
Object.keys(expectedAPI).sort()
);
});
});