Compare commits
16 Commits
b14f696071
...
2a68724b53
Author | SHA1 | Date | |
---|---|---|---|
|
2a68724b53 | ||
|
65d618635c | ||
|
30e69956db | ||
|
e650b95253 | ||
|
a7d47af474 | ||
|
b54887cfab | ||
|
b4267cd294 | ||
|
eb160726ee | ||
|
70b6ddc5d9 | ||
|
3c78ff5fb0 | ||
|
e647311a89 | ||
|
f676c2c0c8 | ||
|
dab8a2eaa4 | ||
|
db2849cc17 | ||
|
38004b65b1 | ||
|
90b2664622 |
@ -72,6 +72,10 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
|
"enableHighlightFloatingButton": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"highlightEditorColors": {
|
"highlightEditorColors": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F"
|
"default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F"
|
||||||
|
28
gulpfile.mjs
28
gulpfile.mjs
@ -863,11 +863,17 @@ async function parseDefaultPreferences(dir) {
|
|||||||
"./" + DEFAULT_PREFERENCES_DIR + dir + "app_options.mjs"
|
"./" + DEFAULT_PREFERENCES_DIR + dir + "app_options.mjs"
|
||||||
);
|
);
|
||||||
|
|
||||||
const browserPrefs = AppOptions.getAll(OptionKind.BROWSER);
|
const browserPrefs = AppOptions.getAll(
|
||||||
|
OptionKind.BROWSER,
|
||||||
|
/* defaultOnly = */ true
|
||||||
|
);
|
||||||
if (Object.keys(browserPrefs).length === 0) {
|
if (Object.keys(browserPrefs).length === 0) {
|
||||||
throw new Error("No browser preferences found.");
|
throw new Error("No browser preferences found.");
|
||||||
}
|
}
|
||||||
const prefs = AppOptions.getAll(OptionKind.PREFERENCE);
|
const prefs = AppOptions.getAll(
|
||||||
|
OptionKind.PREFERENCE,
|
||||||
|
/* defaultOnly = */ true
|
||||||
|
);
|
||||||
if (Object.keys(prefs).length === 0) {
|
if (Object.keys(prefs).length === 0) {
|
||||||
throw new Error("No default preferences found.");
|
throw new Error("No default preferences found.");
|
||||||
}
|
}
|
||||||
@ -2222,25 +2228,15 @@ gulp.task(
|
|||||||
])
|
])
|
||||||
.pipe(gulp.dest(DIST_DIR + "legacy/build/")),
|
.pipe(gulp.dest(DIST_DIR + "legacy/build/")),
|
||||||
gulp
|
gulp
|
||||||
.src(MINIFIED_DIR + "build/pdf.min.mjs")
|
.src(MINIFIED_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.min.mjs")
|
||||||
.pipe(gulp.dest(DIST_DIR + "build/")),
|
|
||||||
gulp
|
|
||||||
.src(MINIFIED_DIR + "build/pdf.worker.min.mjs")
|
|
||||||
.pipe(gulp.dest(DIST_DIR + "build/")),
|
|
||||||
gulp
|
|
||||||
.src(MINIFIED_DIR + "build/pdf.sandbox.min.mjs")
|
|
||||||
.pipe(gulp.dest(DIST_DIR + "build/")),
|
.pipe(gulp.dest(DIST_DIR + "build/")),
|
||||||
gulp
|
gulp
|
||||||
.src(MINIFIED_DIR + "image_decoders/pdf.image_decoders.min.mjs")
|
.src(MINIFIED_DIR + "image_decoders/pdf.image_decoders.min.mjs")
|
||||||
.pipe(gulp.dest(DIST_DIR + "image_decoders/")),
|
.pipe(gulp.dest(DIST_DIR + "image_decoders/")),
|
||||||
gulp
|
gulp
|
||||||
.src(MINIFIED_LEGACY_DIR + "build/pdf.min.mjs")
|
.src(
|
||||||
.pipe(gulp.dest(DIST_DIR + "legacy/build/")),
|
MINIFIED_LEGACY_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.min.mjs"
|
||||||
gulp
|
)
|
||||||
.src(MINIFIED_LEGACY_DIR + "build/pdf.worker.min.mjs")
|
|
||||||
.pipe(gulp.dest(DIST_DIR + "legacy/build/")),
|
|
||||||
gulp
|
|
||||||
.src(MINIFIED_LEGACY_DIR + "build/pdf.sandbox.min.mjs")
|
|
||||||
.pipe(gulp.dest(DIST_DIR + "legacy/build/")),
|
.pipe(gulp.dest(DIST_DIR + "legacy/build/")),
|
||||||
gulp
|
gulp
|
||||||
.src(
|
.src(
|
||||||
|
@ -318,6 +318,8 @@ pdfjs-editor-stamp-button-label = Add or edit images
|
|||||||
pdfjs-editor-highlight-button =
|
pdfjs-editor-highlight-button =
|
||||||
.title = Highlight
|
.title = Highlight
|
||||||
pdfjs-editor-highlight-button-label = Highlight
|
pdfjs-editor-highlight-button-label = Highlight
|
||||||
|
pdfjs-highlight-floating-button =
|
||||||
|
.title = Highlight
|
||||||
|
|
||||||
## Remove button for the various kind of editor.
|
## Remove button for the various kind of editor.
|
||||||
|
|
||||||
|
@ -445,20 +445,10 @@ class Catalog {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
groupRefs.put(groupRef);
|
groupRefs.put(groupRef);
|
||||||
const group = this.xref.fetch(groupRef);
|
|
||||||
groups.push({
|
groups.push(this.#readOptionalContentGroup(groupRef));
|
||||||
id: groupRef.toString(),
|
|
||||||
name:
|
|
||||||
typeof group.get("Name") === "string"
|
|
||||||
? stringToPDFString(group.get("Name"))
|
|
||||||
: null,
|
|
||||||
intent:
|
|
||||||
typeof group.get("Intent") === "string"
|
|
||||||
? stringToPDFString(group.get("Intent"))
|
|
||||||
: null,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
config = this._readOptionalContentConfig(defaultConfig, groupRefs);
|
config = this.#readOptionalContentConfig(defaultConfig, groupRefs);
|
||||||
config.groups = groups;
|
config.groups = groups;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex instanceof MissingDataException) {
|
if (ex instanceof MissingDataException) {
|
||||||
@ -469,7 +459,65 @@ class Catalog {
|
|||||||
return shadow(this, "optionalContentConfig", config);
|
return shadow(this, "optionalContentConfig", config);
|
||||||
}
|
}
|
||||||
|
|
||||||
_readOptionalContentConfig(config, contentGroupRefs) {
|
#readOptionalContentGroup(groupRef) {
|
||||||
|
const group = this.xref.fetch(groupRef);
|
||||||
|
const obj = {
|
||||||
|
id: groupRef.toString(),
|
||||||
|
name: null,
|
||||||
|
intent: null,
|
||||||
|
usage: {
|
||||||
|
print: null,
|
||||||
|
view: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const name = group.get("Name");
|
||||||
|
if (typeof name === "string") {
|
||||||
|
obj.name = stringToPDFString(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
let intent = group.getArray("Intent");
|
||||||
|
if (!Array.isArray(intent)) {
|
||||||
|
intent = [intent];
|
||||||
|
}
|
||||||
|
if (intent.every(i => i instanceof Name)) {
|
||||||
|
obj.intent = intent.map(i => i.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
const usage = group.get("Usage");
|
||||||
|
if (!(usage instanceof Dict)) {
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
const usageObj = obj.usage;
|
||||||
|
|
||||||
|
const print = usage.get("Print");
|
||||||
|
if (print instanceof Dict) {
|
||||||
|
const printState = print.get("PrintState");
|
||||||
|
if (printState instanceof Name) {
|
||||||
|
switch (printState.name) {
|
||||||
|
case "ON":
|
||||||
|
case "OFF":
|
||||||
|
usageObj.print = { printState: printState.name };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const view = usage.get("View");
|
||||||
|
if (view instanceof Dict) {
|
||||||
|
const viewState = view.get("ViewState");
|
||||||
|
if (viewState instanceof Name) {
|
||||||
|
switch (viewState.name) {
|
||||||
|
case "ON":
|
||||||
|
case "OFF":
|
||||||
|
usageObj.view = { viewState: viewState.name };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
#readOptionalContentConfig(config, contentGroupRefs) {
|
||||||
function parseOnOff(refs) {
|
function parseOnOff(refs) {
|
||||||
const onParsed = [];
|
const onParsed = [];
|
||||||
if (Array.isArray(refs)) {
|
if (Array.isArray(refs)) {
|
||||||
|
@ -949,12 +949,26 @@ class PDFDocumentProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @typedef {Object} GetOptionalContentConfigParameters
|
||||||
|
* @property {string} [intent] - Determines the optional content groups that
|
||||||
|
* are visible by default; valid values are:
|
||||||
|
* - 'display' (viewable groups).
|
||||||
|
* - 'print' (printable groups).
|
||||||
|
* - 'any' (all groups).
|
||||||
|
* The default value is 'display'.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {GetOptionalContentConfigParameters} [params] - Optional content
|
||||||
|
* config parameters.
|
||||||
* @returns {Promise<OptionalContentConfig>} A promise that is resolved with
|
* @returns {Promise<OptionalContentConfig>} A promise that is resolved with
|
||||||
* an {@link OptionalContentConfig} that contains all the optional content
|
* an {@link OptionalContentConfig} that contains all the optional content
|
||||||
* groups (assuming that the document has any).
|
* groups (assuming that the document has any).
|
||||||
*/
|
*/
|
||||||
getOptionalContentConfig() {
|
getOptionalContentConfig({ intent = "display" } = {}) {
|
||||||
return this._transport.getOptionalContentConfig();
|
const { renderingIntent } = this._transport.getRenderingIntent(intent);
|
||||||
|
|
||||||
|
return this._transport.getOptionalContentConfig(renderingIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1340,17 +1354,14 @@ class PDFPageProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {GetAnnotationsParameters} params - Annotation parameters.
|
* @param {GetAnnotationsParameters} [params] - Annotation parameters.
|
||||||
* @returns {Promise<Array<any>>} A promise that is resolved with an
|
* @returns {Promise<Array<any>>} A promise that is resolved with an
|
||||||
* {Array} of the annotation objects.
|
* {Array} of the annotation objects.
|
||||||
*/
|
*/
|
||||||
getAnnotations({ intent = "display" } = {}) {
|
getAnnotations({ intent = "display" } = {}) {
|
||||||
const intentArgs = this._transport.getRenderingIntent(intent);
|
const { renderingIntent } = this._transport.getRenderingIntent(intent);
|
||||||
|
|
||||||
return this._transport.getAnnotations(
|
return this._transport.getAnnotations(this._pageIndex, renderingIntent);
|
||||||
this._pageIndex,
|
|
||||||
intentArgs.renderingIntent
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1411,20 +1422,20 @@ class PDFPageProxy {
|
|||||||
annotationMode,
|
annotationMode,
|
||||||
printAnnotationStorage
|
printAnnotationStorage
|
||||||
);
|
);
|
||||||
|
const { renderingIntent, cacheKey } = intentArgs;
|
||||||
// If there was a pending destroy, cancel it so no cleanup happens during
|
// If there was a pending destroy, cancel it so no cleanup happens during
|
||||||
// this call to render...
|
// this call to render...
|
||||||
this.#pendingCleanup = false;
|
this.#pendingCleanup = false;
|
||||||
// ... and ensure that a delayed cleanup is always aborted.
|
// ... and ensure that a delayed cleanup is always aborted.
|
||||||
this.#abortDelayedCleanup();
|
this.#abortDelayedCleanup();
|
||||||
|
|
||||||
if (!optionalContentConfigPromise) {
|
optionalContentConfigPromise ||=
|
||||||
optionalContentConfigPromise = this._transport.getOptionalContentConfig();
|
this._transport.getOptionalContentConfig(renderingIntent);
|
||||||
}
|
|
||||||
|
|
||||||
let intentState = this._intentStates.get(intentArgs.cacheKey);
|
let intentState = this._intentStates.get(cacheKey);
|
||||||
if (!intentState) {
|
if (!intentState) {
|
||||||
intentState = Object.create(null);
|
intentState = Object.create(null);
|
||||||
this._intentStates.set(intentArgs.cacheKey, intentState);
|
this._intentStates.set(cacheKey, intentState);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that a pending `streamReader` cancel timeout is always aborted.
|
// Ensure that a pending `streamReader` cancel timeout is always aborted.
|
||||||
@ -1433,9 +1444,7 @@ class PDFPageProxy {
|
|||||||
intentState.streamReaderCancelTimeout = null;
|
intentState.streamReaderCancelTimeout = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const intentPrint = !!(
|
const intentPrint = !!(renderingIntent & RenderingIntentFlag.PRINT);
|
||||||
intentArgs.renderingIntent & RenderingIntentFlag.PRINT
|
|
||||||
);
|
|
||||||
|
|
||||||
// If there's no displayReadyCapability yet, then the operatorList
|
// If there's no displayReadyCapability yet, then the operatorList
|
||||||
// was never requested before. Make the request and create the promise.
|
// was never requested before. Make the request and create the promise.
|
||||||
@ -1512,6 +1521,12 @@ class PDFPageProxy {
|
|||||||
}
|
}
|
||||||
this._stats?.time("Rendering");
|
this._stats?.time("Rendering");
|
||||||
|
|
||||||
|
if (!(optionalContentConfig.renderingIntent & renderingIntent)) {
|
||||||
|
throw new Error(
|
||||||
|
"Must use the same `intent`-argument when calling the `PDFPageProxy.render` " +
|
||||||
|
"and `PDFDocumentProxy.getOptionalContentConfig` methods."
|
||||||
|
);
|
||||||
|
}
|
||||||
internalRenderTask.initializeGraphics({
|
internalRenderTask.initializeGraphics({
|
||||||
transparency,
|
transparency,
|
||||||
optionalContentConfig,
|
optionalContentConfig,
|
||||||
@ -2994,10 +3009,10 @@ class WorkerTransport {
|
|||||||
return this.messageHandler.sendWithPromise("GetOutline", null);
|
return this.messageHandler.sendWithPromise("GetOutline", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
getOptionalContentConfig() {
|
getOptionalContentConfig(renderingIntent) {
|
||||||
return this.messageHandler
|
return this.#cacheSimpleMethod("GetOptionalContentConfig").then(
|
||||||
.sendWithPromise("GetOptionalContentConfig", null)
|
data => new OptionalContentConfig(data, renderingIntent)
|
||||||
.then(results => new OptionalContentConfig(results));
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPermissions() {
|
getPermissions() {
|
||||||
|
@ -66,6 +66,7 @@ class DrawLayer {
|
|||||||
#createSVG(box) {
|
#createSVG(box) {
|
||||||
const svg = DrawLayer._svgFactory.create(1, 1, /* skipDimensions = */ true);
|
const svg = DrawLayer._svgFactory.create(1, 1, /* skipDimensions = */ true);
|
||||||
this.#parent.append(svg);
|
this.#parent.append(svg);
|
||||||
|
svg.setAttribute("aria-hidden", true);
|
||||||
DrawLayer.#setBox(svg, box);
|
DrawLayer.#setBox(svg, box);
|
||||||
|
|
||||||
return svg;
|
return svg;
|
||||||
|
@ -93,6 +93,7 @@ class ColorPicker {
|
|||||||
button.addEventListener("keydown", this.#boundKeyDown);
|
button.addEventListener("keydown", this.#boundKeyDown);
|
||||||
const swatch = (this.#buttonSwatch = document.createElement("span"));
|
const swatch = (this.#buttonSwatch = document.createElement("span"));
|
||||||
swatch.className = "swatch";
|
swatch.className = "swatch";
|
||||||
|
swatch.setAttribute("aria-hidden", true);
|
||||||
swatch.style.backgroundColor = this.#defaultColor;
|
swatch.style.backgroundColor = this.#defaultColor;
|
||||||
button.append(swatch);
|
button.append(swatch);
|
||||||
return button;
|
return button;
|
||||||
|
@ -61,6 +61,8 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
|
|
||||||
#outlineId = null;
|
#outlineId = null;
|
||||||
|
|
||||||
|
#text = "";
|
||||||
|
|
||||||
#thickness;
|
#thickness;
|
||||||
|
|
||||||
#methodOfCreation = "";
|
#methodOfCreation = "";
|
||||||
@ -104,6 +106,7 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
this.#opacity = params.opacity || HighlightEditor._defaultOpacity;
|
this.#opacity = params.opacity || HighlightEditor._defaultOpacity;
|
||||||
this.#boxes = params.boxes || null;
|
this.#boxes = params.boxes || null;
|
||||||
this.#methodOfCreation = params.methodOfCreation || "";
|
this.#methodOfCreation = params.methodOfCreation || "";
|
||||||
|
this.#text = params.text || "";
|
||||||
this._isDraggable = false;
|
this._isDraggable = false;
|
||||||
|
|
||||||
if (params.highlightId > -1) {
|
if (params.highlightId > -1) {
|
||||||
@ -558,6 +561,13 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const div = super.render();
|
const div = super.render();
|
||||||
|
if (this.#text) {
|
||||||
|
const mark = document.createElement("mark");
|
||||||
|
div.append(mark);
|
||||||
|
mark.append(document.createTextNode(this.#text));
|
||||||
|
// The text is invisible but it's still visible by a screen reader.
|
||||||
|
mark.className = "visuallyHidden";
|
||||||
|
}
|
||||||
if (this.#isFreeHighlight) {
|
if (this.#isFreeHighlight) {
|
||||||
div.classList.add("free");
|
div.classList.add("free");
|
||||||
} else {
|
} else {
|
||||||
@ -565,6 +575,7 @@ class HighlightEditor extends AnnotationEditor {
|
|||||||
}
|
}
|
||||||
const highlightDiv = (this.#highlightDiv = document.createElement("div"));
|
const highlightDiv = (this.#highlightDiv = document.createElement("div"));
|
||||||
div.append(highlightDiv);
|
div.append(highlightDiv);
|
||||||
|
highlightDiv.setAttribute("aria-hidden", "true");
|
||||||
highlightDiv.className = "internal";
|
highlightDiv.className = "internal";
|
||||||
highlightDiv.style.clipPath = this.#clipPathId;
|
highlightDiv.style.clipPath = this.#clipPathId;
|
||||||
const [parentWidth, parentHeight] = this.parentDimensions;
|
const [parentWidth, parentHeight] = this.parentDimensions;
|
||||||
|
@ -31,6 +31,7 @@ class EditorToolbar {
|
|||||||
render() {
|
render() {
|
||||||
const editToolbar = (this.#toolbar = document.createElement("div"));
|
const editToolbar = (this.#toolbar = document.createElement("div"));
|
||||||
editToolbar.className = "editToolbar";
|
editToolbar.className = "editToolbar";
|
||||||
|
editToolbar.setAttribute("role", "toolbar");
|
||||||
editToolbar.addEventListener("contextmenu", noContextMenu);
|
editToolbar.addEventListener("contextmenu", noContextMenu);
|
||||||
editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown);
|
editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown);
|
||||||
|
|
||||||
@ -134,4 +135,80 @@ class EditorToolbar {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { EditorToolbar };
|
class HighlightToolbar {
|
||||||
|
#buttons = null;
|
||||||
|
|
||||||
|
#toolbar = null;
|
||||||
|
|
||||||
|
#uiManager;
|
||||||
|
|
||||||
|
constructor(uiManager) {
|
||||||
|
this.#uiManager = uiManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
#render() {
|
||||||
|
const editToolbar = (this.#toolbar = document.createElement("div"));
|
||||||
|
editToolbar.className = "editToolbar";
|
||||||
|
editToolbar.setAttribute("role", "toolbar");
|
||||||
|
editToolbar.addEventListener("contextmenu", noContextMenu);
|
||||||
|
|
||||||
|
const buttons = (this.#buttons = document.createElement("div"));
|
||||||
|
buttons.className = "buttons";
|
||||||
|
editToolbar.append(buttons);
|
||||||
|
|
||||||
|
this.#addHighlightButton();
|
||||||
|
|
||||||
|
return editToolbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
#getLastPoint(boxes, isLTR) {
|
||||||
|
let lastY = 0;
|
||||||
|
let lastX = 0;
|
||||||
|
for (const box of boxes) {
|
||||||
|
const y = box.y + box.height;
|
||||||
|
if (y < lastY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const x = box.x + (isLTR ? box.width : 0);
|
||||||
|
if (y > lastY) {
|
||||||
|
lastX = x;
|
||||||
|
lastY = y;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (isLTR) {
|
||||||
|
if (x > lastX) {
|
||||||
|
lastX = x;
|
||||||
|
}
|
||||||
|
} else if (x < lastX) {
|
||||||
|
lastX = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return [isLTR ? 1 - lastX : lastX, lastY];
|
||||||
|
}
|
||||||
|
|
||||||
|
show(parent, boxes, isLTR) {
|
||||||
|
const [x, y] = this.#getLastPoint(boxes, isLTR);
|
||||||
|
const { style } = (this.#toolbar ||= this.#render());
|
||||||
|
parent.append(this.#toolbar);
|
||||||
|
style.insetInlineEnd = `${100 * x}%`;
|
||||||
|
style.top = `calc(${100 * y}% + var(--editor-toolbar-vert-offset))`;
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.#toolbar.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
#addHighlightButton() {
|
||||||
|
const button = document.createElement("button");
|
||||||
|
button.className = "highlightButton";
|
||||||
|
button.tabIndex = 0;
|
||||||
|
button.setAttribute("data-l10n-id", `pdfjs-highlight-floating-button`);
|
||||||
|
button.addEventListener("contextmenu", noContextMenu);
|
||||||
|
button.addEventListener("click", () => {
|
||||||
|
this.#uiManager.highlightSelection("floating_button");
|
||||||
|
});
|
||||||
|
this.#buttons.append(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { EditorToolbar, HighlightToolbar };
|
||||||
|
@ -33,6 +33,7 @@ import {
|
|||||||
getRGB,
|
getRGB,
|
||||||
PixelsPerInch,
|
PixelsPerInch,
|
||||||
} from "../display_utils.js";
|
} from "../display_utils.js";
|
||||||
|
import { HighlightToolbar } from "./toolbar.js";
|
||||||
|
|
||||||
function bindEvents(obj, element, names) {
|
function bindEvents(obj, element, names) {
|
||||||
for (const name of names) {
|
for (const name of names) {
|
||||||
@ -555,6 +556,8 @@ class AnnotationEditorUIManager {
|
|||||||
|
|
||||||
#editorsToRescale = new Set();
|
#editorsToRescale = new Set();
|
||||||
|
|
||||||
|
#enableHighlightFloatingButton = false;
|
||||||
|
|
||||||
#filterFactory = null;
|
#filterFactory = null;
|
||||||
|
|
||||||
#focusMainContainerTimeoutId = null;
|
#focusMainContainerTimeoutId = null;
|
||||||
@ -563,6 +566,8 @@ class AnnotationEditorUIManager {
|
|||||||
|
|
||||||
#highlightWhenShiftUp = false;
|
#highlightWhenShiftUp = false;
|
||||||
|
|
||||||
|
#highlightToolbar = null;
|
||||||
|
|
||||||
#idManager = new IdManager();
|
#idManager = new IdManager();
|
||||||
|
|
||||||
#isEnabled = false;
|
#isEnabled = false;
|
||||||
@ -771,6 +776,7 @@ class AnnotationEditorUIManager {
|
|||||||
pdfDocument,
|
pdfDocument,
|
||||||
pageColors,
|
pageColors,
|
||||||
highlightColors,
|
highlightColors,
|
||||||
|
enableHighlightFloatingButton,
|
||||||
mlManager
|
mlManager
|
||||||
) {
|
) {
|
||||||
this.#container = container;
|
this.#container = container;
|
||||||
@ -782,10 +788,12 @@ class AnnotationEditorUIManager {
|
|||||||
this._eventBus._on("scalechanging", this.#boundOnScaleChanging);
|
this._eventBus._on("scalechanging", this.#boundOnScaleChanging);
|
||||||
this._eventBus._on("rotationchanging", this.#boundOnRotationChanging);
|
this._eventBus._on("rotationchanging", this.#boundOnRotationChanging);
|
||||||
this.#addSelectionListener();
|
this.#addSelectionListener();
|
||||||
|
this.#addKeyboardManager();
|
||||||
this.#annotationStorage = pdfDocument.annotationStorage;
|
this.#annotationStorage = pdfDocument.annotationStorage;
|
||||||
this.#filterFactory = pdfDocument.filterFactory;
|
this.#filterFactory = pdfDocument.filterFactory;
|
||||||
this.#pageColors = pageColors;
|
this.#pageColors = pageColors;
|
||||||
this.#highlightColors = highlightColors || null;
|
this.#highlightColors = highlightColors || null;
|
||||||
|
this.#enableHighlightFloatingButton = enableHighlightFloatingButton;
|
||||||
this.#mlManager = mlManager || null;
|
this.#mlManager = mlManager || null;
|
||||||
this.viewParameters = {
|
this.viewParameters = {
|
||||||
realScale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
realScale: PixelsPerInch.PDF_TO_CSS_UNITS,
|
||||||
@ -821,6 +829,8 @@ class AnnotationEditorUIManager {
|
|||||||
this.#selectedEditors.clear();
|
this.#selectedEditors.clear();
|
||||||
this.#commandManager.destroy();
|
this.#commandManager.destroy();
|
||||||
this.#altTextManager?.destroy();
|
this.#altTextManager?.destroy();
|
||||||
|
this.#highlightToolbar?.hide();
|
||||||
|
this.#highlightToolbar = null;
|
||||||
if (this.#focusMainContainerTimeoutId) {
|
if (this.#focusMainContainerTimeoutId) {
|
||||||
clearTimeout(this.#focusMainContainerTimeoutId);
|
clearTimeout(this.#focusMainContainerTimeoutId);
|
||||||
this.#focusMainContainerTimeoutId = null;
|
this.#focusMainContainerTimeoutId = null;
|
||||||
@ -946,24 +956,32 @@ class AnnotationEditorUIManager {
|
|||||||
this.viewParameters.rotation = pagesRotation;
|
this.viewParameters.rotation = pagesRotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#getAnchorElementForSelection({ anchorNode }) {
|
||||||
|
return anchorNode.nodeType === Node.TEXT_NODE
|
||||||
|
? anchorNode.parentElement
|
||||||
|
: anchorNode;
|
||||||
|
}
|
||||||
|
|
||||||
highlightSelection(methodOfCreation = "") {
|
highlightSelection(methodOfCreation = "") {
|
||||||
const selection = document.getSelection();
|
const selection = document.getSelection();
|
||||||
if (!selection || selection.isCollapsed) {
|
if (!selection || selection.isCollapsed) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { anchorNode, anchorOffset, focusNode, focusOffset } = selection;
|
const { anchorNode, anchorOffset, focusNode, focusOffset } = selection;
|
||||||
const anchorElement =
|
const text = selection.toString();
|
||||||
anchorNode.nodeType === Node.TEXT_NODE
|
const anchorElement = this.#getAnchorElementForSelection(selection);
|
||||||
? anchorNode.parentElement
|
|
||||||
: anchorNode;
|
|
||||||
const textLayer = anchorElement.closest(".textLayer");
|
const textLayer = anchorElement.closest(".textLayer");
|
||||||
const boxes = this.getSelectionBoxes(textLayer);
|
const boxes = this.getSelectionBoxes(textLayer);
|
||||||
|
if (!boxes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
selection.empty();
|
selection.empty();
|
||||||
if (this.#mode === AnnotationEditorType.NONE) {
|
if (this.#mode === AnnotationEditorType.NONE) {
|
||||||
this._eventBus.dispatch("showannotationeditorui", {
|
this._eventBus.dispatch("showannotationeditorui", {
|
||||||
source: this,
|
source: this,
|
||||||
mode: AnnotationEditorType.HIGHLIGHT,
|
mode: AnnotationEditorType.HIGHLIGHT,
|
||||||
});
|
});
|
||||||
|
this.showAllEditors("highlight", true, /* updateButton = */ true);
|
||||||
}
|
}
|
||||||
for (const layer of this.#allLayers.values()) {
|
for (const layer of this.#allLayers.values()) {
|
||||||
if (layer.hasTextLayer(textLayer)) {
|
if (layer.hasTextLayer(textLayer)) {
|
||||||
@ -974,12 +992,28 @@ class AnnotationEditorUIManager {
|
|||||||
anchorOffset,
|
anchorOffset,
|
||||||
focusNode,
|
focusNode,
|
||||||
focusOffset,
|
focusOffset,
|
||||||
|
text,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#displayHighlightToolbar() {
|
||||||
|
const selection = document.getSelection();
|
||||||
|
if (!selection || selection.isCollapsed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const anchorElement = this.#getAnchorElementForSelection(selection);
|
||||||
|
const textLayer = anchorElement.closest(".textLayer");
|
||||||
|
const boxes = this.getSelectionBoxes(textLayer);
|
||||||
|
if (!boxes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.#highlightToolbar ||= new HighlightToolbar(this);
|
||||||
|
this.#highlightToolbar.show(textLayer, boxes, this.direction === "ltr");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an editor in the annotation storage.
|
* Add an editor in the annotation storage.
|
||||||
* @param {AnnotationEditor} editor
|
* @param {AnnotationEditor} editor
|
||||||
@ -998,6 +1032,7 @@ class AnnotationEditorUIManager {
|
|||||||
const selection = document.getSelection();
|
const selection = document.getSelection();
|
||||||
if (!selection || selection.isCollapsed) {
|
if (!selection || selection.isCollapsed) {
|
||||||
if (this.#selectedTextNode) {
|
if (this.#selectedTextNode) {
|
||||||
|
this.#highlightToolbar?.hide();
|
||||||
this.#selectedTextNode = null;
|
this.#selectedTextNode = null;
|
||||||
this.#dispatchUpdateStates({
|
this.#dispatchUpdateStates({
|
||||||
hasSelectedText: false,
|
hasSelectedText: false,
|
||||||
@ -1010,12 +1045,11 @@ class AnnotationEditorUIManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const anchorElement =
|
const anchorElement = this.#getAnchorElementForSelection(selection);
|
||||||
anchorNode.nodeType === Node.TEXT_NODE
|
const textLayer = anchorElement.closest(".textLayer");
|
||||||
? anchorNode.parentElement
|
if (!textLayer) {
|
||||||
: anchorNode;
|
|
||||||
if (!anchorElement.closest(".textLayer")) {
|
|
||||||
if (this.#selectedTextNode) {
|
if (this.#selectedTextNode) {
|
||||||
|
this.#highlightToolbar?.hide();
|
||||||
this.#selectedTextNode = null;
|
this.#selectedTextNode = null;
|
||||||
this.#dispatchUpdateStates({
|
this.#dispatchUpdateStates({
|
||||||
hasSelectedText: false,
|
hasSelectedText: false,
|
||||||
@ -1023,16 +1057,22 @@ class AnnotationEditorUIManager {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.#highlightToolbar?.hide();
|
||||||
this.#selectedTextNode = anchorNode;
|
this.#selectedTextNode = anchorNode;
|
||||||
this.#dispatchUpdateStates({
|
this.#dispatchUpdateStates({
|
||||||
hasSelectedText: true,
|
hasSelectedText: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.#mode !== AnnotationEditorType.HIGHLIGHT) {
|
if (
|
||||||
|
this.#mode !== AnnotationEditorType.HIGHLIGHT &&
|
||||||
|
this.#mode !== AnnotationEditorType.NONE
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showAllEditors("highlight", true, /* updateButton = */ true);
|
if (this.#mode === AnnotationEditorType.HIGHLIGHT) {
|
||||||
|
this.showAllEditors("highlight", true, /* updateButton = */ true);
|
||||||
|
}
|
||||||
|
|
||||||
this.#highlightWhenShiftUp = this.isShiftKeyDown;
|
this.#highlightWhenShiftUp = this.isShiftKeyDown;
|
||||||
if (!this.isShiftKeyDown) {
|
if (!this.isShiftKeyDown) {
|
||||||
@ -1044,7 +1084,7 @@ class AnnotationEditorUIManager {
|
|||||||
window.removeEventListener("pointerup", pointerup);
|
window.removeEventListener("pointerup", pointerup);
|
||||||
window.removeEventListener("blur", pointerup);
|
window.removeEventListener("blur", pointerup);
|
||||||
if (e.type === "pointerup") {
|
if (e.type === "pointerup") {
|
||||||
this.highlightSelection("main_toolbar");
|
this.#onSelectEnd("main_toolbar");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
window.addEventListener("pointerup", pointerup);
|
window.addEventListener("pointerup", pointerup);
|
||||||
@ -1052,6 +1092,14 @@ class AnnotationEditorUIManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#onSelectEnd(methodOfCreation = "") {
|
||||||
|
if (this.#mode === AnnotationEditorType.HIGHLIGHT) {
|
||||||
|
this.highlightSelection(methodOfCreation);
|
||||||
|
} else if (this.#enableHighlightFloatingButton) {
|
||||||
|
this.#displayHighlightToolbar();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#addSelectionListener() {
|
#addSelectionListener() {
|
||||||
document.addEventListener("selectionchange", this.#boundSelectionChange);
|
document.addEventListener("selectionchange", this.#boundSelectionChange);
|
||||||
}
|
}
|
||||||
@ -1074,7 +1122,7 @@ class AnnotationEditorUIManager {
|
|||||||
this.isShiftKeyDown = false;
|
this.isShiftKeyDown = false;
|
||||||
if (this.#highlightWhenShiftUp) {
|
if (this.#highlightWhenShiftUp) {
|
||||||
this.#highlightWhenShiftUp = false;
|
this.#highlightWhenShiftUp = false;
|
||||||
this.highlightSelection("main_toolbar");
|
this.#onSelectEnd("main_toolbar");
|
||||||
}
|
}
|
||||||
if (!this.hasSelection) {
|
if (!this.hasSelection) {
|
||||||
return;
|
return;
|
||||||
@ -1250,7 +1298,10 @@ class AnnotationEditorUIManager {
|
|||||||
if (!this.isShiftKeyDown && event.key === "Shift") {
|
if (!this.isShiftKeyDown && event.key === "Shift") {
|
||||||
this.isShiftKeyDown = true;
|
this.isShiftKeyDown = true;
|
||||||
}
|
}
|
||||||
if (!this.isEditorHandlingKeyboard) {
|
if (
|
||||||
|
this.#mode !== AnnotationEditorType.NONE &&
|
||||||
|
!this.isEditorHandlingKeyboard
|
||||||
|
) {
|
||||||
AnnotationEditorUIManager._keyboardManager.exec(this, event);
|
AnnotationEditorUIManager._keyboardManager.exec(this, event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1264,7 +1315,7 @@ class AnnotationEditorUIManager {
|
|||||||
this.isShiftKeyDown = false;
|
this.isShiftKeyDown = false;
|
||||||
if (this.#highlightWhenShiftUp) {
|
if (this.#highlightWhenShiftUp) {
|
||||||
this.#highlightWhenShiftUp = false;
|
this.#highlightWhenShiftUp = false;
|
||||||
this.highlightSelection("main_toolbar");
|
this.#onSelectEnd("main_toolbar");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1333,7 +1384,6 @@ class AnnotationEditorUIManager {
|
|||||||
setEditingState(isEditing) {
|
setEditingState(isEditing) {
|
||||||
if (isEditing) {
|
if (isEditing) {
|
||||||
this.#addFocusManager();
|
this.#addFocusManager();
|
||||||
this.#addKeyboardManager();
|
|
||||||
this.#addCopyPasteListeners();
|
this.#addCopyPasteListeners();
|
||||||
this.#dispatchUpdateStates({
|
this.#dispatchUpdateStates({
|
||||||
isEditing: this.#mode !== AnnotationEditorType.NONE,
|
isEditing: this.#mode !== AnnotationEditorType.NONE,
|
||||||
@ -1344,7 +1394,6 @@ class AnnotationEditorUIManager {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.#removeFocusManager();
|
this.#removeFocusManager();
|
||||||
this.#removeKeyboardManager();
|
|
||||||
this.#removeCopyPasteListeners();
|
this.#removeCopyPasteListeners();
|
||||||
this.#dispatchUpdateStates({
|
this.#dispatchUpdateStates({
|
||||||
isEditing: false,
|
isEditing: false,
|
||||||
|
@ -424,21 +424,22 @@ class PDFNodeStreamFsFullReader extends BaseFullReader {
|
|||||||
path = path.replace(/^\//, "");
|
path = path.replace(/^\//, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
fs.lstat(path, (error, stat) => {
|
fs.promises.lstat(path).then(
|
||||||
if (error) {
|
stat => {
|
||||||
|
// Setting right content length.
|
||||||
|
this._contentLength = stat.size;
|
||||||
|
|
||||||
|
this._setReadableStream(fs.createReadStream(path));
|
||||||
|
this._headersCapability.resolve();
|
||||||
|
},
|
||||||
|
error => {
|
||||||
if (error.code === "ENOENT") {
|
if (error.code === "ENOENT") {
|
||||||
error = new MissingPDFException(`Missing PDF "${path}".`);
|
error = new MissingPDFException(`Missing PDF "${path}".`);
|
||||||
}
|
}
|
||||||
this._storedError = error;
|
this._storedError = error;
|
||||||
this._headersCapability.reject(error);
|
this._headersCapability.reject(error);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Setting right content length.
|
);
|
||||||
this._contentLength = stat.size;
|
|
||||||
|
|
||||||
this._setReadableStream(fs.createReadStream(path));
|
|
||||||
this._headersCapability.resolve();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,15 +71,7 @@ if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("SKIP_BABEL")) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fetchData = function (url) {
|
const fetchData = function (url) {
|
||||||
return new Promise((resolve, reject) => {
|
return fs.promises.readFile(url).then(data => new Uint8Array(data));
|
||||||
fs.readFile(url, (error, data) => {
|
|
||||||
if (error || !data) {
|
|
||||||
reject(new Error(error));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
resolve(new Uint8Array(data));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class NodeFilterFactory extends BaseFilterFactory {}
|
class NodeFilterFactory extends BaseFilterFactory {}
|
||||||
|
@ -13,33 +13,63 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { info, objectFromMap, unreachable, warn } from "../shared/util.js";
|
import {
|
||||||
|
info,
|
||||||
|
objectFromMap,
|
||||||
|
RenderingIntentFlag,
|
||||||
|
unreachable,
|
||||||
|
warn,
|
||||||
|
} from "../shared/util.js";
|
||||||
import { MurmurHash3_64 } from "../shared/murmurhash3.js";
|
import { MurmurHash3_64 } from "../shared/murmurhash3.js";
|
||||||
|
|
||||||
const INTERNAL = Symbol("INTERNAL");
|
const INTERNAL = Symbol("INTERNAL");
|
||||||
|
|
||||||
class OptionalContentGroup {
|
class OptionalContentGroup {
|
||||||
|
#isDisplay = false;
|
||||||
|
|
||||||
|
#isPrint = false;
|
||||||
|
|
||||||
|
#userSet = false;
|
||||||
|
|
||||||
#visible = true;
|
#visible = true;
|
||||||
|
|
||||||
constructor(name, intent) {
|
constructor(renderingIntent, { name, intent, usage }) {
|
||||||
|
this.#isDisplay = !!(renderingIntent & RenderingIntentFlag.DISPLAY);
|
||||||
|
this.#isPrint = !!(renderingIntent & RenderingIntentFlag.PRINT);
|
||||||
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.intent = intent;
|
this.intent = intent;
|
||||||
|
this.usage = usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {boolean}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
get visible() {
|
get visible() {
|
||||||
return this.#visible;
|
if (this.#userSet) {
|
||||||
|
return this.#visible;
|
||||||
|
}
|
||||||
|
if (!this.#visible) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const { print, view } = this.usage;
|
||||||
|
|
||||||
|
if (this.#isDisplay) {
|
||||||
|
return view?.viewState !== "OFF";
|
||||||
|
} else if (this.#isPrint) {
|
||||||
|
return print?.printState !== "OFF";
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
_setVisible(internal, visible) {
|
_setVisible(internal, visible, userSet = false) {
|
||||||
if (internal !== INTERNAL) {
|
if (internal !== INTERNAL) {
|
||||||
unreachable("Internal method `_setVisible` called.");
|
unreachable("Internal method `_setVisible` called.");
|
||||||
}
|
}
|
||||||
|
this.#userSet = userSet;
|
||||||
this.#visible = visible;
|
this.#visible = visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,7 +83,9 @@ class OptionalContentConfig {
|
|||||||
|
|
||||||
#order = null;
|
#order = null;
|
||||||
|
|
||||||
constructor(data) {
|
constructor(data, renderingIntent = RenderingIntentFlag.DISPLAY) {
|
||||||
|
this.renderingIntent = renderingIntent;
|
||||||
|
|
||||||
this.name = null;
|
this.name = null;
|
||||||
this.creator = null;
|
this.creator = null;
|
||||||
|
|
||||||
@ -66,7 +98,7 @@ class OptionalContentConfig {
|
|||||||
for (const group of data.groups) {
|
for (const group of data.groups) {
|
||||||
this.#groups.set(
|
this.#groups.set(
|
||||||
group.id,
|
group.id,
|
||||||
new OptionalContentGroup(group.name, group.intent)
|
new OptionalContentGroup(renderingIntent, group)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,11 +230,44 @@ class OptionalContentConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setVisibility(id, visible = true) {
|
setVisibility(id, visible = true) {
|
||||||
if (!this.#groups.has(id)) {
|
const group = this.#groups.get(id);
|
||||||
|
if (!group) {
|
||||||
warn(`Optional content group not found: ${id}`);
|
warn(`Optional content group not found: ${id}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#groups.get(id)._setVisible(INTERNAL, !!visible);
|
group._setVisible(INTERNAL, !!visible, /* userSet = */ true);
|
||||||
|
|
||||||
|
this.#cachedGetHash = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOCGState({ state, preserveRB }) {
|
||||||
|
let operator;
|
||||||
|
|
||||||
|
for (const elem of state) {
|
||||||
|
switch (elem) {
|
||||||
|
case "ON":
|
||||||
|
case "OFF":
|
||||||
|
case "Toggle":
|
||||||
|
operator = elem;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = this.#groups.get(elem);
|
||||||
|
if (!group) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (operator) {
|
||||||
|
case "ON":
|
||||||
|
group._setVisible(INTERNAL, true);
|
||||||
|
break;
|
||||||
|
case "OFF":
|
||||||
|
group._setVisible(INTERNAL, false);
|
||||||
|
break;
|
||||||
|
case "Toggle":
|
||||||
|
group._setVisible(INTERNAL, !group.visible);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.#cachedGetHash = null;
|
this.#cachedGetHash = null;
|
||||||
}
|
}
|
||||||
|
@ -684,7 +684,9 @@ class Driver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task.pdfDoc = doc;
|
task.pdfDoc = doc;
|
||||||
task.optionalContentConfigPromise = doc.getOptionalContentConfig();
|
task.optionalContentConfigPromise = doc.getOptionalContentConfig({
|
||||||
|
intent: task.print ? "print" : "display",
|
||||||
|
});
|
||||||
|
|
||||||
if (task.optionalContent) {
|
if (task.optionalContent) {
|
||||||
const entries = Object.entries(task.optionalContent),
|
const entries = Object.entries(task.optionalContent),
|
||||||
|
@ -35,6 +35,7 @@ async function runTests(results) {
|
|||||||
"scripting_spec.mjs",
|
"scripting_spec.mjs",
|
||||||
"stamp_editor_spec.mjs",
|
"stamp_editor_spec.mjs",
|
||||||
"text_field_spec.mjs",
|
"text_field_spec.mjs",
|
||||||
|
"viewer_spec.mjs",
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -46,8 +46,11 @@ const getXY = (page, selector) =>
|
|||||||
return `${bbox.x}::${bbox.y}`;
|
return `${bbox.x}::${bbox.y}`;
|
||||||
}, selector);
|
}, selector);
|
||||||
|
|
||||||
const getSpanRectFromText = (page, pageNumber, text) =>
|
const getSpanRectFromText = async (page, pageNumber, text) => {
|
||||||
page.evaluate(
|
await page.waitForSelector(
|
||||||
|
`.page[data-page-number="${pageNumber}"] > .textLayer .endOfContent`
|
||||||
|
);
|
||||||
|
return page.evaluate(
|
||||||
(number, content) => {
|
(number, content) => {
|
||||||
for (const el of document.querySelectorAll(
|
for (const el of document.querySelectorAll(
|
||||||
`.page[data-page-number="${number}"] > .textLayer > span`
|
`.page[data-page-number="${number}"] > .textLayer > span`
|
||||||
@ -62,6 +65,7 @@ const getSpanRectFromText = (page, pageNumber, text) =>
|
|||||||
pageNumber,
|
pageNumber,
|
||||||
text
|
text
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
|
||||||
describe("Highlight Editor", () => {
|
describe("Highlight Editor", () => {
|
||||||
describe("Editor must be removed without exception", () => {
|
describe("Editor must be removed without exception", () => {
|
||||||
@ -1510,4 +1514,46 @@ describe("Highlight Editor", () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("Highlight from floating highlight button", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
pages = await loadAndWait(
|
||||||
|
"tracemonkey.pdf",
|
||||||
|
".annotationEditorLayer",
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
{ highlightEditorColors: "red=#AB0000" }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that clicking on the highlight floating button triggers an highlight", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
const rect = await getSpanRectFromText(page, 1, "Abstract");
|
||||||
|
const x = rect.x + rect.width / 2;
|
||||||
|
const y = rect.y + rect.height / 2;
|
||||||
|
await page.mouse.click(x, y, { count: 2, delay: 100 });
|
||||||
|
|
||||||
|
await page.waitForSelector(".textLayer .highlightButton");
|
||||||
|
await page.click(".textLayer .highlightButton");
|
||||||
|
|
||||||
|
await page.waitForSelector(getEditorSelector(0));
|
||||||
|
const usedColor = await page.evaluate(() => {
|
||||||
|
const highlight = document.querySelector(
|
||||||
|
`.page[data-page-number = "1"] .canvasWrapper > svg.highlight`
|
||||||
|
);
|
||||||
|
return highlight.getAttribute("fill");
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(usedColor).withContext(`In ${browserName}`).toEqual("#AB0000");
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
58
test/integration/viewer_spec.mjs
Normal file
58
test/integration/viewer_spec.mjs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/* Copyright 2024 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 { closePages, loadAndWait } from "./test_utils.mjs";
|
||||||
|
|
||||||
|
describe("PDF viewer", () => {
|
||||||
|
describe("Zoom with the mouse wheel", () => {
|
||||||
|
let pages;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
pages = await loadAndWait("empty.pdf", ".textLayer .endOfContent", 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await closePages(pages);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("must check that we can zoom with the mouse wheel and pressed control key", async () => {
|
||||||
|
await Promise.all(
|
||||||
|
pages.map(async ([browserName, page]) => {
|
||||||
|
if (browserName === "firefox") {
|
||||||
|
// Skip this test for Firefox, as it's not working correctly.
|
||||||
|
// See https://github.com/puppeteer/puppeteer/issues/12093.
|
||||||
|
// TODO: Remove this check once the issue is resolved.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await page.keyboard.down("Control");
|
||||||
|
let zoom = 10;
|
||||||
|
const zoomGetter = () =>
|
||||||
|
page.evaluate(
|
||||||
|
() => window.PDFViewerApplication.pdfViewer.currentScale
|
||||||
|
);
|
||||||
|
while (zoom > 0.1) {
|
||||||
|
await page.mouse.wheel({ deltaY: 100 });
|
||||||
|
zoom = await zoomGetter();
|
||||||
|
}
|
||||||
|
while (zoom < 10) {
|
||||||
|
await page.mouse.wheel({ deltaY: -100 });
|
||||||
|
zoom = await zoomGetter();
|
||||||
|
}
|
||||||
|
await page.keyboard.up("Control");
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
1
test/pdfs/bug1826783.pdf.link
Normal file
1
test/pdfs/bug1826783.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
https://bugzilla.mozilla.org/attachment.cgi?id=9327375
|
@ -4016,6 +4016,23 @@
|
|||||||
"lastPage": 5,
|
"lastPage": 5,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "bug1826783-display",
|
||||||
|
"file": "pdfs/bug1826783.pdf",
|
||||||
|
"md5": "93e706efee15dd7b32d32d66f15a3ea2",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bug1826783-print",
|
||||||
|
"file": "pdfs/bug1826783.pdf",
|
||||||
|
"md5": "93e706efee15dd7b32d32d66f15a3ea2",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq",
|
||||||
|
"print": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "issue8586",
|
"id": "issue8586",
|
||||||
"file": "pdfs/issue8586.pdf",
|
"file": "pdfs/issue8586.pdf",
|
||||||
|
41
test/unit/app_options_spec.js
Normal file
41
test/unit/app_options_spec.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* Copyright 2024 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 { AppOptions, OptionKind } from "../../web/app_options.js";
|
||||||
|
import { objectSize } from "../../src/shared/util.js";
|
||||||
|
|
||||||
|
describe("AppOptions", function () {
|
||||||
|
it("checks that getAll returns data, for every OptionKind", function () {
|
||||||
|
const KIND_NAMES = ["BROWSER", "VIEWER", "API", "WORKER", "PREFERENCE"];
|
||||||
|
|
||||||
|
for (const name of KIND_NAMES) {
|
||||||
|
const kind = OptionKind[name];
|
||||||
|
expect(typeof kind).toEqual("number");
|
||||||
|
|
||||||
|
const options = AppOptions.getAll(kind);
|
||||||
|
expect(objectSize(options)).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('checks that the number of "PREFERENCE" options does *not* exceed the maximum in mozilla-central', function () {
|
||||||
|
// If the following constant is updated then you *MUST* make the same change
|
||||||
|
// in mozilla-central as well to ensure that preference-fetching works; see
|
||||||
|
// https://searchfox.org/mozilla-central/source/toolkit/components/pdfjs/content/PdfStreamConverter.sys.mjs
|
||||||
|
const MAX_NUMBER_OF_PREFS = 50;
|
||||||
|
|
||||||
|
const options = AppOptions.getAll(OptionKind.PREFERENCE);
|
||||||
|
expect(objectSize(options)).toBeLessThanOrEqual(MAX_NUMBER_OF_PREFS);
|
||||||
|
});
|
||||||
|
});
|
@ -7,6 +7,7 @@
|
|||||||
"annotation_spec.js",
|
"annotation_spec.js",
|
||||||
"annotation_storage_spec.js",
|
"annotation_storage_spec.js",
|
||||||
"api_spec.js",
|
"api_spec.js",
|
||||||
|
"app_options_spec.js",
|
||||||
"bidi_spec.js",
|
"bidi_spec.js",
|
||||||
"cff_parser_spec.js",
|
"cff_parser_spec.js",
|
||||||
"cmap_spec.js",
|
"cmap_spec.js",
|
||||||
|
@ -50,6 +50,7 @@ async function initializePDFJS(callback) {
|
|||||||
"pdfjs-test/unit/annotation_spec.js",
|
"pdfjs-test/unit/annotation_spec.js",
|
||||||
"pdfjs-test/unit/annotation_storage_spec.js",
|
"pdfjs-test/unit/annotation_storage_spec.js",
|
||||||
"pdfjs-test/unit/api_spec.js",
|
"pdfjs-test/unit/api_spec.js",
|
||||||
|
"pdfjs-test/unit/app_options_spec.js",
|
||||||
"pdfjs-test/unit/bidi_spec.js",
|
"pdfjs-test/unit/bidi_spec.js",
|
||||||
"pdfjs-test/unit/cff_parser_spec.js",
|
"pdfjs-test/unit/cff_parser_spec.js",
|
||||||
"pdfjs-test/unit/cmap_spec.js",
|
"pdfjs-test/unit/cmap_spec.js",
|
||||||
|
@ -48,6 +48,19 @@
|
|||||||
pointer;
|
pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The following class is used to hide an element but keep it available to
|
||||||
|
* for screen readers. */
|
||||||
|
.visuallyHidden {
|
||||||
|
position: absolute;
|
||||||
|
border: 0;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.textLayer.highlighting {
|
.textLayer.highlighting {
|
||||||
cursor: var(--editorFreeHighlight-editing-cursor);
|
cursor: var(--editorFreeHighlight-editing-cursor);
|
||||||
|
|
||||||
@ -182,10 +195,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.annotationEditorLayer
|
.annotationEditorLayer
|
||||||
:is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor) {
|
:is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),
|
||||||
|
.textLayer {
|
||||||
.editToolbar {
|
.editToolbar {
|
||||||
--editor-toolbar-delete-image: url(images/editor-toolbar-delete.svg);
|
--editor-toolbar-delete-image: url(images/editor-toolbar-delete.svg);
|
||||||
--editor-toolbar-bg-color: #f0f0f4;
|
--editor-toolbar-bg-color: #f0f0f4;
|
||||||
|
--editor-toolbar-highlight-image: url(images/toolbarButton-editorHighlight.svg);
|
||||||
--editor-toolbar-fg-color: #2e2e56;
|
--editor-toolbar-fg-color: #2e2e56;
|
||||||
--editor-toolbar-border-color: #8f8f9d;
|
--editor-toolbar-border-color: #8f8f9d;
|
||||||
--editor-toolbar-hover-border-color: var(--editor-toolbar-border-color);
|
--editor-toolbar-hover-border-color: var(--editor-toolbar-border-color);
|
||||||
@ -271,6 +286,25 @@
|
|||||||
margin-inline: 2px;
|
margin-inline: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.highlightButton {
|
||||||
|
width: var(--editor-toolbar-height);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
mask-image: var(--editor-toolbar-highlight-image);
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
mask-position: center;
|
||||||
|
display: inline-block;
|
||||||
|
background-color: var(--editor-toolbar-fg-color);
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover::before {
|
||||||
|
background-color: var(--editor-toolbar-hover-fg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.delete {
|
.delete {
|
||||||
width: var(--editor-toolbar-height);
|
width: var(--editor-toolbar-height);
|
||||||
|
|
||||||
|
@ -424,6 +424,9 @@ const PDFViewerApplication = {
|
|||||||
annotationMode: AppOptions.get("annotationMode"),
|
annotationMode: AppOptions.get("annotationMode"),
|
||||||
annotationEditorMode,
|
annotationEditorMode,
|
||||||
annotationEditorHighlightColors: AppOptions.get("highlightEditorColors"),
|
annotationEditorHighlightColors: AppOptions.get("highlightEditorColors"),
|
||||||
|
enableHighlightFloatingButton: AppOptions.get(
|
||||||
|
"enableHighlightFloatingButton"
|
||||||
|
),
|
||||||
imageResourcesPath: AppOptions.get("imageResourcesPath"),
|
imageResourcesPath: AppOptions.get("imageResourcesPath"),
|
||||||
enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"),
|
enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"),
|
||||||
maxCanvasPixels: AppOptions.get("maxCanvasPixels"),
|
maxCanvasPixels: AppOptions.get("maxCanvasPixels"),
|
||||||
@ -1796,7 +1799,6 @@ const PDFViewerApplication = {
|
|||||||
pagesOverview: this.pdfViewer.getPagesOverview(),
|
pagesOverview: this.pdfViewer.getPagesOverview(),
|
||||||
printContainer: this.appConfig.printContainer,
|
printContainer: this.appConfig.printContainer,
|
||||||
printResolution: AppOptions.get("printResolution"),
|
printResolution: AppOptions.get("printResolution"),
|
||||||
optionalContentConfigPromise: this.pdfViewer.optionalContentConfigPromise,
|
|
||||||
printAnnotationStoragePromise: this._printAnnotationStoragePromise,
|
printAnnotationStoragePromise: this._printAnnotationStoragePromise,
|
||||||
});
|
});
|
||||||
this.forceRendering();
|
this.forceRendering();
|
||||||
@ -2022,8 +2024,9 @@ const PDFViewerApplication = {
|
|||||||
});
|
});
|
||||||
const scroll = (_boundEvents.mainContainerScroll = () => {
|
const scroll = (_boundEvents.mainContainerScroll = () => {
|
||||||
if (
|
if (
|
||||||
this._lastScrollTop === mainContainer.scrollTop &&
|
this._isCtrlKeyDown ||
|
||||||
this._lastScrollLeft === mainContainer.scrollLeft
|
(this._lastScrollTop === mainContainer.scrollTop &&
|
||||||
|
this._lastScrollLeft === mainContainer.scrollLeft)
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -143,6 +143,14 @@ const defaultOptions = {
|
|||||||
value: typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING"),
|
value: typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING"),
|
||||||
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
|
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
|
||||||
},
|
},
|
||||||
|
enableHighlightFloatingButton: {
|
||||||
|
// We'll probably want to make some experiments before enabling this
|
||||||
|
// in Firefox release, but it has to be temporary.
|
||||||
|
// TODO: remove it when unnecessary.
|
||||||
|
/** @type {boolean} */
|
||||||
|
value: typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING"),
|
||||||
|
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
|
||||||
|
},
|
||||||
enableML: {
|
enableML: {
|
||||||
/** @type {boolean} */
|
/** @type {boolean} */
|
||||||
value: false,
|
value: false,
|
||||||
@ -409,57 +417,64 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
|||||||
|
|
||||||
const userOptions = Object.create(null);
|
const userOptions = Object.create(null);
|
||||||
|
|
||||||
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING || LIB")) {
|
||||||
|
// Ensure that the `defaultOptions` are correctly specified.
|
||||||
|
for (const name in defaultOptions) {
|
||||||
|
const { value, kind } = defaultOptions[name];
|
||||||
|
|
||||||
|
if (kind & OptionKind.PREFERENCE) {
|
||||||
|
if (kind === OptionKind.PREFERENCE) {
|
||||||
|
throw new Error(`Cannot use only "PREFERENCE" kind: ${name}`);
|
||||||
|
}
|
||||||
|
if (kind & OptionKind.BROWSER) {
|
||||||
|
throw new Error(`Cannot mix "PREFERENCE" and "BROWSER" kind: ${name}`);
|
||||||
|
}
|
||||||
|
if (compatibilityParams[name] !== undefined) {
|
||||||
|
throw new Error(
|
||||||
|
`Should not have compatibility-value for "PREFERENCE" kind: ${name}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Only "simple" preference-values are allowed.
|
||||||
|
if (
|
||||||
|
typeof value !== "boolean" &&
|
||||||
|
typeof value !== "string" &&
|
||||||
|
!Number.isInteger(value)
|
||||||
|
) {
|
||||||
|
throw new Error(`Invalid value for "PREFERENCE" kind: ${name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AppOptions {
|
class AppOptions {
|
||||||
constructor() {
|
constructor() {
|
||||||
throw new Error("Cannot initialize AppOptions.");
|
throw new Error("Cannot initialize AppOptions.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static get(name) {
|
static getCompat(name) {
|
||||||
const userOption = userOptions[name];
|
return compatibilityParams[name] ?? undefined;
|
||||||
if (userOption !== undefined) {
|
|
||||||
return userOption;
|
|
||||||
}
|
|
||||||
const defaultOption = defaultOptions[name];
|
|
||||||
if (defaultOption !== undefined) {
|
|
||||||
return compatibilityParams[name] ?? defaultOption.value;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getAll(kind = null) {
|
static get(name) {
|
||||||
|
return (
|
||||||
|
userOptions[name] ??
|
||||||
|
compatibilityParams[name] ??
|
||||||
|
defaultOptions[name]?.value ??
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getAll(kind = null, defaultOnly = false) {
|
||||||
const options = Object.create(null);
|
const options = Object.create(null);
|
||||||
for (const name in defaultOptions) {
|
for (const name in defaultOptions) {
|
||||||
const defaultOption = defaultOptions[name];
|
const defaultOption = defaultOptions[name];
|
||||||
if (kind) {
|
|
||||||
if (!(kind & defaultOption.kind)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
(typeof PDFJSDev === "undefined" || PDFJSDev.test("LIB")) &&
|
|
||||||
kind === OptionKind.PREFERENCE
|
|
||||||
) {
|
|
||||||
if (defaultOption.kind & OptionKind.BROWSER) {
|
|
||||||
throw new Error(`Invalid kind for preference: ${name}`);
|
|
||||||
}
|
|
||||||
const value = defaultOption.value,
|
|
||||||
valueType = typeof value;
|
|
||||||
|
|
||||||
if (
|
if (kind && !(kind & defaultOption.kind)) {
|
||||||
valueType === "boolean" ||
|
continue;
|
||||||
valueType === "string" ||
|
|
||||||
(valueType === "number" && Number.isInteger(value))
|
|
||||||
) {
|
|
||||||
options[name] = value;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
throw new Error(`Invalid type for preference: ${name}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const userOption = userOptions[name];
|
options[name] = defaultOnly
|
||||||
options[name] =
|
? defaultOption.value
|
||||||
userOption !== undefined
|
: userOptions[name] ?? compatibilityParams[name] ?? defaultOption.value;
|
||||||
? userOption
|
|
||||||
: compatibilityParams[name] ?? defaultOption.value;
|
|
||||||
}
|
}
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
@ -493,4 +508,4 @@ class AppOptions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { AppOptions, compatibilityParams, OptionKind };
|
export { AppOptions, OptionKind };
|
||||||
|
@ -119,15 +119,15 @@ class FirefoxPrintService {
|
|||||||
pagesOverview,
|
pagesOverview,
|
||||||
printContainer,
|
printContainer,
|
||||||
printResolution,
|
printResolution,
|
||||||
optionalContentConfigPromise = null,
|
|
||||||
printAnnotationStoragePromise = null,
|
printAnnotationStoragePromise = null,
|
||||||
}) {
|
}) {
|
||||||
this.pdfDocument = pdfDocument;
|
this.pdfDocument = pdfDocument;
|
||||||
this.pagesOverview = pagesOverview;
|
this.pagesOverview = pagesOverview;
|
||||||
this.printContainer = printContainer;
|
this.printContainer = printContainer;
|
||||||
this._printResolution = printResolution || 150;
|
this._printResolution = printResolution || 150;
|
||||||
this._optionalContentConfigPromise =
|
this._optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
|
||||||
optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
|
intent: "print",
|
||||||
|
});
|
||||||
this._printAnnotationStoragePromise =
|
this._printAnnotationStoragePromise =
|
||||||
printAnnotationStoragePromise || Promise.resolve();
|
printAnnotationStoragePromise || Promise.resolve();
|
||||||
}
|
}
|
||||||
|
@ -258,14 +258,8 @@ if (PDFJSDev.test("GECKOVIEW")) {
|
|||||||
const hasWillPrint =
|
const hasWillPrint =
|
||||||
pdfViewer.enableScripting &&
|
pdfViewer.enableScripting &&
|
||||||
!!(await pdfDocument.getJSActions())?.WillPrint;
|
!!(await pdfDocument.getJSActions())?.WillPrint;
|
||||||
const hasUnchangedOptionalContent = (
|
|
||||||
await pdfViewer.optionalContentConfigPromise
|
|
||||||
).hasInitialVisibility;
|
|
||||||
|
|
||||||
result =
|
result = hasUnchangedAnnotations && !hasWillPrint;
|
||||||
hasUnchangedAnnotations &&
|
|
||||||
!hasWillPrint &&
|
|
||||||
hasUnchangedOptionalContent;
|
|
||||||
} catch {
|
} catch {
|
||||||
console.warn("Unable to check if the document can be downloaded.");
|
console.warn("Unable to check if the document can be downloaded.");
|
||||||
}
|
}
|
||||||
|
@ -182,7 +182,7 @@ class PDFLayerViewer extends BaseTreeViewer {
|
|||||||
}
|
}
|
||||||
const pdfDocument = this._pdfDocument;
|
const pdfDocument = this._pdfDocument;
|
||||||
const optionalContentConfig = await (promise ||
|
const optionalContentConfig = await (promise ||
|
||||||
pdfDocument.getOptionalContentConfig());
|
pdfDocument.getOptionalContentConfig({ intent: "display" }));
|
||||||
|
|
||||||
if (pdfDocument !== this._pdfDocument) {
|
if (pdfDocument !== this._pdfDocument) {
|
||||||
return; // The document was closed while the optional content resolved.
|
return; // The document was closed while the optional content resolved.
|
||||||
|
@ -517,31 +517,7 @@ class PDFLinkService {
|
|||||||
if (pdfDocument !== this.pdfDocument) {
|
if (pdfDocument !== this.pdfDocument) {
|
||||||
return; // The document was closed while the optional content resolved.
|
return; // The document was closed while the optional content resolved.
|
||||||
}
|
}
|
||||||
let operator;
|
optionalContentConfig.setOCGState(action);
|
||||||
|
|
||||||
for (const elem of action.state) {
|
|
||||||
switch (elem) {
|
|
||||||
case "ON":
|
|
||||||
case "OFF":
|
|
||||||
case "Toggle":
|
|
||||||
operator = elem;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switch (operator) {
|
|
||||||
case "ON":
|
|
||||||
optionalContentConfig.setVisibility(elem, true);
|
|
||||||
break;
|
|
||||||
case "OFF":
|
|
||||||
optionalContentConfig.setVisibility(elem, false);
|
|
||||||
break;
|
|
||||||
case "Toggle":
|
|
||||||
const group = optionalContentConfig.getGroup(elem);
|
|
||||||
if (group) {
|
|
||||||
optionalContentConfig.setVisibility(elem, !group.visible);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.pdfViewer.optionalContentConfigPromise = Promise.resolve(
|
this.pdfViewer.optionalContentConfigPromise = Promise.resolve(
|
||||||
optionalContentConfig
|
optionalContentConfig
|
||||||
|
@ -41,7 +41,7 @@ import {
|
|||||||
} from "./ui_utils.js";
|
} from "./ui_utils.js";
|
||||||
import { AnnotationEditorLayerBuilder } from "./annotation_editor_layer_builder.js";
|
import { AnnotationEditorLayerBuilder } from "./annotation_editor_layer_builder.js";
|
||||||
import { AnnotationLayerBuilder } from "./annotation_layer_builder.js";
|
import { AnnotationLayerBuilder } from "./annotation_layer_builder.js";
|
||||||
import { compatibilityParams } from "./app_options.js";
|
import { AppOptions } from "./app_options.js";
|
||||||
import { DrawLayerBuilder } from "./draw_layer_builder.js";
|
import { DrawLayerBuilder } from "./draw_layer_builder.js";
|
||||||
import { GenericL10n } from "web-null_l10n";
|
import { GenericL10n } from "web-null_l10n";
|
||||||
import { SimpleLinkService } from "./pdf_link_service.js";
|
import { SimpleLinkService } from "./pdf_link_service.js";
|
||||||
@ -83,8 +83,6 @@ import { XfaLayerBuilder } from "./xfa_layer_builder.js";
|
|||||||
* the necessary layer-properties.
|
* the necessary layer-properties.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const MAX_CANVAS_PIXELS = compatibilityParams.maxCanvasPixels || 16777216;
|
|
||||||
|
|
||||||
const DEFAULT_LAYER_PROPERTIES =
|
const DEFAULT_LAYER_PROPERTIES =
|
||||||
typeof PDFJSDev === "undefined" || !PDFJSDev.test("COMPONENTS")
|
typeof PDFJSDev === "undefined" || !PDFJSDev.test("COMPONENTS")
|
||||||
? null
|
? null
|
||||||
@ -152,7 +150,9 @@ class PDFPageView {
|
|||||||
this.#annotationMode =
|
this.#annotationMode =
|
||||||
options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
|
options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
|
||||||
this.imageResourcesPath = options.imageResourcesPath || "";
|
this.imageResourcesPath = options.imageResourcesPath || "";
|
||||||
this.maxCanvasPixels = options.maxCanvasPixels ?? MAX_CANVAS_PIXELS;
|
this.maxCanvasPixels =
|
||||||
|
options.maxCanvasPixels ??
|
||||||
|
(AppOptions.getCompat("maxCanvasPixels") || 16777216);
|
||||||
this.pageColors = options.pageColors || null;
|
this.pageColors = options.pageColors || null;
|
||||||
|
|
||||||
this.eventBus = options.eventBus;
|
this.eventBus = options.eventBus;
|
||||||
|
@ -13,7 +13,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AnnotationMode, PixelsPerInch, shadow } from "pdfjs-lib";
|
import {
|
||||||
|
AnnotationMode,
|
||||||
|
PixelsPerInch,
|
||||||
|
RenderingCancelledException,
|
||||||
|
shadow,
|
||||||
|
} from "pdfjs-lib";
|
||||||
import { getXfaHtmlForPrinting } from "./print_utils.js";
|
import { getXfaHtmlForPrinting } from "./print_utils.js";
|
||||||
|
|
||||||
let activeService = null;
|
let activeService = null;
|
||||||
@ -58,7 +63,14 @@ function renderPage(
|
|||||||
optionalContentConfigPromise,
|
optionalContentConfigPromise,
|
||||||
printAnnotationStorage,
|
printAnnotationStorage,
|
||||||
};
|
};
|
||||||
return pdfPage.render(renderContext).promise;
|
const renderTask = pdfPage.render(renderContext);
|
||||||
|
|
||||||
|
return renderTask.promise.catch(reason => {
|
||||||
|
if (!(reason instanceof RenderingCancelledException)) {
|
||||||
|
console.error(reason);
|
||||||
|
}
|
||||||
|
throw reason;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,15 +80,15 @@ class PDFPrintService {
|
|||||||
pagesOverview,
|
pagesOverview,
|
||||||
printContainer,
|
printContainer,
|
||||||
printResolution,
|
printResolution,
|
||||||
optionalContentConfigPromise = null,
|
|
||||||
printAnnotationStoragePromise = null,
|
printAnnotationStoragePromise = null,
|
||||||
}) {
|
}) {
|
||||||
this.pdfDocument = pdfDocument;
|
this.pdfDocument = pdfDocument;
|
||||||
this.pagesOverview = pagesOverview;
|
this.pagesOverview = pagesOverview;
|
||||||
this.printContainer = printContainer;
|
this.printContainer = printContainer;
|
||||||
this._printResolution = printResolution || 150;
|
this._printResolution = printResolution || 150;
|
||||||
this._optionalContentConfigPromise =
|
this._optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
|
||||||
optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
|
intent: "print",
|
||||||
|
});
|
||||||
this._printAnnotationStoragePromise =
|
this._printAnnotationStoragePromise =
|
||||||
printAnnotationStoragePromise || Promise.resolve();
|
printAnnotationStoragePromise || Promise.resolve();
|
||||||
this.currentPage = -1;
|
this.currentPage = -1;
|
||||||
|
@ -189,7 +189,9 @@ class PDFThumbnailViewer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const firstPagePromise = pdfDocument.getPage(1);
|
const firstPagePromise = pdfDocument.getPage(1);
|
||||||
const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig();
|
const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
|
||||||
|
intent: "display",
|
||||||
|
});
|
||||||
|
|
||||||
firstPagePromise
|
firstPagePromise
|
||||||
.then(firstPdfPage => {
|
.then(firstPdfPage => {
|
||||||
|
@ -214,6 +214,8 @@ class PDFViewer {
|
|||||||
|
|
||||||
#copyCallbackBound = null;
|
#copyCallbackBound = null;
|
||||||
|
|
||||||
|
#enableHighlightFloatingButton = false;
|
||||||
|
|
||||||
#enablePermissions = false;
|
#enablePermissions = false;
|
||||||
|
|
||||||
#mlManager = null;
|
#mlManager = null;
|
||||||
@ -282,6 +284,8 @@ class PDFViewer {
|
|||||||
options.annotationEditorMode ?? AnnotationEditorType.NONE;
|
options.annotationEditorMode ?? AnnotationEditorType.NONE;
|
||||||
this.#annotationEditorHighlightColors =
|
this.#annotationEditorHighlightColors =
|
||||||
options.annotationEditorHighlightColors || null;
|
options.annotationEditorHighlightColors || null;
|
||||||
|
this.#enableHighlightFloatingButton =
|
||||||
|
options.enableHighlightFloatingButton === true;
|
||||||
this.imageResourcesPath = options.imageResourcesPath || "";
|
this.imageResourcesPath = options.imageResourcesPath || "";
|
||||||
this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
|
this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
|
||||||
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
|
||||||
@ -781,7 +785,9 @@ class PDFViewer {
|
|||||||
const pagesCount = pdfDocument.numPages;
|
const pagesCount = pdfDocument.numPages;
|
||||||
const firstPagePromise = pdfDocument.getPage(1);
|
const firstPagePromise = pdfDocument.getPage(1);
|
||||||
// Rendering (potentially) depends on this, hence fetching it immediately.
|
// Rendering (potentially) depends on this, hence fetching it immediately.
|
||||||
const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig();
|
const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
|
||||||
|
intent: "display",
|
||||||
|
});
|
||||||
const permissionsPromise = this.#enablePermissions
|
const permissionsPromise = this.#enablePermissions
|
||||||
? pdfDocument.getPermissions()
|
? pdfDocument.getPermissions()
|
||||||
: Promise.resolve();
|
: Promise.resolve();
|
||||||
@ -861,6 +867,7 @@ class PDFViewer {
|
|||||||
pdfDocument,
|
pdfDocument,
|
||||||
this.pageColors,
|
this.pageColors,
|
||||||
this.#annotationEditorHighlightColors,
|
this.#annotationEditorHighlightColors,
|
||||||
|
this.#enableHighlightFloatingButton,
|
||||||
this.#mlManager
|
this.#mlManager
|
||||||
);
|
);
|
||||||
this.eventBus.dispatch("annotationeditoruimanager", {
|
this.eventBus.dispatch("annotationeditoruimanager", {
|
||||||
@ -1822,7 +1829,7 @@ class PDFViewer {
|
|||||||
console.error("optionalContentConfigPromise: Not initialized yet.");
|
console.error("optionalContentConfigPromise: Not initialized yet.");
|
||||||
// Prevent issues if the getter is accessed *before* the `onePageRendered`
|
// Prevent issues if the getter is accessed *before* the `onePageRendered`
|
||||||
// promise has resolved; won't (normally) happen in the default viewer.
|
// promise has resolved; won't (normally) happen in the default viewer.
|
||||||
return this.pdfDocument.getOptionalContentConfig();
|
return this.pdfDocument.getOptionalContentConfig({ intent: "display" });
|
||||||
}
|
}
|
||||||
return this._optionalContentConfigPromise;
|
return this._optionalContentConfigPromise;
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import { AppOptions, OptionKind } from "./app_options.js";
|
|||||||
class BasePreferences {
|
class BasePreferences {
|
||||||
#defaults = Object.freeze(
|
#defaults = Object.freeze(
|
||||||
typeof PDFJSDev === "undefined"
|
typeof PDFJSDev === "undefined"
|
||||||
? AppOptions.getAll(OptionKind.PREFERENCE)
|
? AppOptions.getAll(OptionKind.PREFERENCE, /* defaultOnly = */ true)
|
||||||
: PDFJSDev.eval("DEFAULT_PREFERENCES")
|
: PDFJSDev.eval("DEFAULT_PREFERENCES")
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ class BasePreferences {
|
|||||||
({ browserPrefs, prefs }) => {
|
({ browserPrefs, prefs }) => {
|
||||||
const BROWSER_PREFS =
|
const BROWSER_PREFS =
|
||||||
typeof PDFJSDev === "undefined"
|
typeof PDFJSDev === "undefined"
|
||||||
? AppOptions.getAll(OptionKind.BROWSER)
|
? AppOptions.getAll(OptionKind.BROWSER, /* defaultOnly = */ true)
|
||||||
: PDFJSDev.eval("BROWSER_PREFERENCES");
|
: PDFJSDev.eval("BROWSER_PREFERENCES");
|
||||||
const options = Object.create(null);
|
const options = Object.create(null);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user