Compare commits

..

No commits in common. "2a68724b5342f8ac8e038f6ed2e1a39b2132d7d8" and "b14f6960710127adccba3a5fca5906c333259da5" have entirely different histories.

34 changed files with 190 additions and 663 deletions

View File

@ -72,10 +72,6 @@
"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"

View File

@ -863,17 +863,11 @@ async function parseDefaultPreferences(dir) {
"./" + DEFAULT_PREFERENCES_DIR + dir + "app_options.mjs" "./" + DEFAULT_PREFERENCES_DIR + dir + "app_options.mjs"
); );
const browserPrefs = AppOptions.getAll( const browserPrefs = AppOptions.getAll(OptionKind.BROWSER);
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( const prefs = AppOptions.getAll(OptionKind.PREFERENCE);
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.");
} }
@ -2228,15 +2222,25 @@ gulp.task(
]) ])
.pipe(gulp.dest(DIST_DIR + "legacy/build/")), .pipe(gulp.dest(DIST_DIR + "legacy/build/")),
gulp gulp
.src(MINIFIED_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.min.mjs") .src(MINIFIED_DIR + "build/pdf.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( .src(MINIFIED_LEGACY_DIR + "build/pdf.min.mjs")
MINIFIED_LEGACY_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.min.mjs" .pipe(gulp.dest(DIST_DIR + "legacy/build/")),
) 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(

View File

@ -318,8 +318,6 @@ 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.

View File

@ -445,10 +445,20 @@ class Catalog {
continue; continue;
} }
groupRefs.put(groupRef); groupRefs.put(groupRef);
const group = this.xref.fetch(groupRef);
groups.push(this.#readOptionalContentGroup(groupRef)); groups.push({
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) {
@ -459,65 +469,7 @@ class Catalog {
return shadow(this, "optionalContentConfig", config); return shadow(this, "optionalContentConfig", config);
} }
#readOptionalContentGroup(groupRef) { _readOptionalContentConfig(config, contentGroupRefs) {
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)) {

View File

@ -949,26 +949,12 @@ 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({ intent = "display" } = {}) { getOptionalContentConfig() {
const { renderingIntent } = this._transport.getRenderingIntent(intent); return this._transport.getOptionalContentConfig();
return this._transport.getOptionalContentConfig(renderingIntent);
} }
/** /**
@ -1354,14 +1340,17 @@ 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 { renderingIntent } = this._transport.getRenderingIntent(intent); const intentArgs = this._transport.getRenderingIntent(intent);
return this._transport.getAnnotations(this._pageIndex, renderingIntent); return this._transport.getAnnotations(
this._pageIndex,
intentArgs.renderingIntent
);
} }
/** /**
@ -1422,20 +1411,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();
optionalContentConfigPromise ||= if (!optionalContentConfigPromise) {
this._transport.getOptionalContentConfig(renderingIntent); optionalContentConfigPromise = this._transport.getOptionalContentConfig();
}
let intentState = this._intentStates.get(cacheKey); let intentState = this._intentStates.get(intentArgs.cacheKey);
if (!intentState) { if (!intentState) {
intentState = Object.create(null); intentState = Object.create(null);
this._intentStates.set(cacheKey, intentState); this._intentStates.set(intentArgs.cacheKey, intentState);
} }
// Ensure that a pending `streamReader` cancel timeout is always aborted. // Ensure that a pending `streamReader` cancel timeout is always aborted.
@ -1444,7 +1433,9 @@ class PDFPageProxy {
intentState.streamReaderCancelTimeout = null; intentState.streamReaderCancelTimeout = null;
} }
const intentPrint = !!(renderingIntent & RenderingIntentFlag.PRINT); const intentPrint = !!(
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.
@ -1521,12 +1512,6 @@ 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,
@ -3009,10 +2994,10 @@ class WorkerTransport {
return this.messageHandler.sendWithPromise("GetOutline", null); return this.messageHandler.sendWithPromise("GetOutline", null);
} }
getOptionalContentConfig(renderingIntent) { getOptionalContentConfig() {
return this.#cacheSimpleMethod("GetOptionalContentConfig").then( return this.messageHandler
data => new OptionalContentConfig(data, renderingIntent) .sendWithPromise("GetOptionalContentConfig", null)
); .then(results => new OptionalContentConfig(results));
} }
getPermissions() { getPermissions() {

View File

@ -66,7 +66,6 @@ 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;

View File

@ -93,7 +93,6 @@ 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;

View File

@ -61,8 +61,6 @@ class HighlightEditor extends AnnotationEditor {
#outlineId = null; #outlineId = null;
#text = "";
#thickness; #thickness;
#methodOfCreation = ""; #methodOfCreation = "";
@ -106,7 +104,6 @@ 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) {
@ -561,13 +558,6 @@ 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 {
@ -575,7 +565,6 @@ 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;

View File

@ -31,7 +31,6 @@ 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);
@ -135,80 +134,4 @@ class EditorToolbar {
} }
} }
class HighlightToolbar { export { EditorToolbar };
#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 };

View File

@ -33,7 +33,6 @@ 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) {
@ -556,8 +555,6 @@ class AnnotationEditorUIManager {
#editorsToRescale = new Set(); #editorsToRescale = new Set();
#enableHighlightFloatingButton = false;
#filterFactory = null; #filterFactory = null;
#focusMainContainerTimeoutId = null; #focusMainContainerTimeoutId = null;
@ -566,8 +563,6 @@ class AnnotationEditorUIManager {
#highlightWhenShiftUp = false; #highlightWhenShiftUp = false;
#highlightToolbar = null;
#idManager = new IdManager(); #idManager = new IdManager();
#isEnabled = false; #isEnabled = false;
@ -776,7 +771,6 @@ class AnnotationEditorUIManager {
pdfDocument, pdfDocument,
pageColors, pageColors,
highlightColors, highlightColors,
enableHighlightFloatingButton,
mlManager mlManager
) { ) {
this.#container = container; this.#container = container;
@ -788,12 +782,10 @@ 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,
@ -829,8 +821,6 @@ 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;
@ -956,32 +946,24 @@ 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 text = selection.toString(); const anchorElement =
const anchorElement = this.#getAnchorElementForSelection(selection); anchorNode.nodeType === Node.TEXT_NODE
? 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)) {
@ -992,28 +974,12 @@ 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
@ -1032,7 +998,6 @@ 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,
@ -1045,11 +1010,12 @@ class AnnotationEditorUIManager {
return; return;
} }
const anchorElement = this.#getAnchorElementForSelection(selection); const anchorElement =
const textLayer = anchorElement.closest(".textLayer"); anchorNode.nodeType === Node.TEXT_NODE
if (!textLayer) { ? anchorNode.parentElement
: 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,
@ -1057,22 +1023,16 @@ class AnnotationEditorUIManager {
} }
return; return;
} }
this.#highlightToolbar?.hide();
this.#selectedTextNode = anchorNode; this.#selectedTextNode = anchorNode;
this.#dispatchUpdateStates({ this.#dispatchUpdateStates({
hasSelectedText: true, hasSelectedText: true,
}); });
if ( if (this.#mode !== AnnotationEditorType.HIGHLIGHT) {
this.#mode !== AnnotationEditorType.HIGHLIGHT &&
this.#mode !== AnnotationEditorType.NONE
) {
return; return;
} }
if (this.#mode === AnnotationEditorType.HIGHLIGHT) {
this.showAllEditors("highlight", true, /* updateButton = */ true); this.showAllEditors("highlight", true, /* updateButton = */ true);
}
this.#highlightWhenShiftUp = this.isShiftKeyDown; this.#highlightWhenShiftUp = this.isShiftKeyDown;
if (!this.isShiftKeyDown) { if (!this.isShiftKeyDown) {
@ -1084,7 +1044,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.#onSelectEnd("main_toolbar"); this.highlightSelection("main_toolbar");
} }
}; };
window.addEventListener("pointerup", pointerup); window.addEventListener("pointerup", pointerup);
@ -1092,14 +1052,6 @@ 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);
} }
@ -1122,7 +1074,7 @@ class AnnotationEditorUIManager {
this.isShiftKeyDown = false; this.isShiftKeyDown = false;
if (this.#highlightWhenShiftUp) { if (this.#highlightWhenShiftUp) {
this.#highlightWhenShiftUp = false; this.#highlightWhenShiftUp = false;
this.#onSelectEnd("main_toolbar"); this.highlightSelection("main_toolbar");
} }
if (!this.hasSelection) { if (!this.hasSelection) {
return; return;
@ -1298,10 +1250,7 @@ class AnnotationEditorUIManager {
if (!this.isShiftKeyDown && event.key === "Shift") { if (!this.isShiftKeyDown && event.key === "Shift") {
this.isShiftKeyDown = true; this.isShiftKeyDown = true;
} }
if ( if (!this.isEditorHandlingKeyboard) {
this.#mode !== AnnotationEditorType.NONE &&
!this.isEditorHandlingKeyboard
) {
AnnotationEditorUIManager._keyboardManager.exec(this, event); AnnotationEditorUIManager._keyboardManager.exec(this, event);
} }
} }
@ -1315,7 +1264,7 @@ class AnnotationEditorUIManager {
this.isShiftKeyDown = false; this.isShiftKeyDown = false;
if (this.#highlightWhenShiftUp) { if (this.#highlightWhenShiftUp) {
this.#highlightWhenShiftUp = false; this.#highlightWhenShiftUp = false;
this.#onSelectEnd("main_toolbar"); this.highlightSelection("main_toolbar");
} }
} }
} }
@ -1384,6 +1333,7 @@ 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,
@ -1394,6 +1344,7 @@ class AnnotationEditorUIManager {
}); });
} else { } else {
this.#removeFocusManager(); this.#removeFocusManager();
this.#removeKeyboardManager();
this.#removeCopyPasteListeners(); this.#removeCopyPasteListeners();
this.#dispatchUpdateStates({ this.#dispatchUpdateStates({
isEditing: false, isEditing: false,

View File

@ -424,22 +424,21 @@ class PDFNodeStreamFsFullReader extends BaseFullReader {
path = path.replace(/^\//, ""); path = path.replace(/^\//, "");
} }
fs.promises.lstat(path).then( fs.lstat(path, (error, stat) => {
stat => { if (error) {
// 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();
});
} }
} }

View File

@ -71,7 +71,15 @@ if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("SKIP_BABEL")) {
} }
const fetchData = function (url) { const fetchData = function (url) {
return fs.promises.readFile(url).then(data => new Uint8Array(data)); return new Promise((resolve, reject) => {
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 {}

View File

@ -13,63 +13,33 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { info, objectFromMap, unreachable, warn } from "../shared/util.js";
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(renderingIntent, { name, intent, usage }) { constructor(name, intent) {
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() {
if (this.#userSet) {
return this.#visible; 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, userSet = false) { _setVisible(internal, visible) {
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;
} }
} }
@ -83,9 +53,7 @@ class OptionalContentConfig {
#order = null; #order = null;
constructor(data, renderingIntent = RenderingIntentFlag.DISPLAY) { constructor(data) {
this.renderingIntent = renderingIntent;
this.name = null; this.name = null;
this.creator = null; this.creator = null;
@ -98,7 +66,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(renderingIntent, group) new OptionalContentGroup(group.name, group.intent)
); );
} }
@ -230,44 +198,11 @@ class OptionalContentConfig {
} }
setVisibility(id, visible = true) { setVisibility(id, visible = true) {
const group = this.#groups.get(id); if (!this.#groups.has(id)) {
if (!group) {
warn(`Optional content group not found: ${id}`); warn(`Optional content group not found: ${id}`);
return; return;
} }
group._setVisible(INTERNAL, !!visible, /* userSet = */ true); this.#groups.get(id)._setVisible(INTERNAL, !!visible);
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;
} }

View File

@ -684,9 +684,7 @@ 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),

View File

@ -35,7 +35,6 @@ 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",
], ],
}); });

View File

@ -46,11 +46,8 @@ const getXY = (page, selector) =>
return `${bbox.x}::${bbox.y}`; return `${bbox.x}::${bbox.y}`;
}, selector); }, selector);
const getSpanRectFromText = async (page, pageNumber, text) => { const getSpanRectFromText = (page, pageNumber, text) =>
await page.waitForSelector( page.evaluate(
`.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`
@ -65,7 +62,6 @@ const getSpanRectFromText = async (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", () => {
@ -1514,46 +1510,4 @@ 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");
})
);
});
});
}); });

View File

@ -1,58 +0,0 @@
/* 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");
})
);
});
});
});

View File

@ -1 +0,0 @@
https://bugzilla.mozilla.org/attachment.cgi?id=9327375

View File

@ -4016,23 +4016,6 @@
"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",

View File

@ -1,41 +0,0 @@
/* 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);
});
});

View File

@ -7,7 +7,6 @@
"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",

View File

@ -50,7 +50,6 @@ 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",

View File

@ -48,19 +48,6 @@
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);
@ -195,12 +182,10 @@
} }
.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);
@ -286,25 +271,6 @@
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);

View File

@ -424,9 +424,6 @@ 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"),
@ -1799,6 +1796,7 @@ 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();
@ -2024,9 +2022,8 @@ const PDFViewerApplication = {
}); });
const scroll = (_boundEvents.mainContainerScroll = () => { const scroll = (_boundEvents.mainContainerScroll = () => {
if ( if (
this._isCtrlKeyDown || this._lastScrollTop === mainContainer.scrollTop &&
(this._lastScrollTop === mainContainer.scrollTop && this._lastScrollLeft === mainContainer.scrollLeft
this._lastScrollLeft === mainContainer.scrollLeft)
) { ) {
return; return;
} }

View File

@ -143,14 +143,6 @@ 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,
@ -417,64 +409,57 @@ 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 getCompat(name) {
return compatibilityParams[name] ?? undefined;
}
static get(name) { static get(name) {
return ( const userOption = userOptions[name];
userOptions[name] ?? if (userOption !== undefined) {
compatibilityParams[name] ?? return userOption;
defaultOptions[name]?.value ?? }
undefined const defaultOption = defaultOptions[name];
); if (defaultOption !== undefined) {
return compatibilityParams[name] ?? defaultOption.value;
}
return undefined;
} }
static getAll(kind = null, defaultOnly = false) { static getAll(kind = null) {
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 && !(kind & defaultOption.kind)) { if (!(kind & defaultOption.kind)) {
continue; continue;
} }
options[name] = defaultOnly if (
? defaultOption.value (typeof PDFJSDev === "undefined" || PDFJSDev.test("LIB")) &&
: userOptions[name] ?? compatibilityParams[name] ?? defaultOption.value; kind === OptionKind.PREFERENCE
) {
if (defaultOption.kind & OptionKind.BROWSER) {
throw new Error(`Invalid kind for preference: ${name}`);
}
const value = defaultOption.value,
valueType = typeof value;
if (
valueType === "boolean" ||
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] =
userOption !== undefined
? userOption
: compatibilityParams[name] ?? defaultOption.value;
} }
return options; return options;
} }
@ -508,4 +493,4 @@ class AppOptions {
} }
} }
export { AppOptions, OptionKind }; export { AppOptions, compatibilityParams, OptionKind };

View File

@ -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 = pdfDocument.getOptionalContentConfig({ this._optionalContentConfigPromise =
intent: "print", optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
});
this._printAnnotationStoragePromise = this._printAnnotationStoragePromise =
printAnnotationStoragePromise || Promise.resolve(); printAnnotationStoragePromise || Promise.resolve();
} }

View File

@ -258,8 +258,14 @@ 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 = hasUnchangedAnnotations && !hasWillPrint; result =
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.");
} }

View File

@ -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({ intent: "display" })); pdfDocument.getOptionalContentConfig());
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.

View File

@ -517,7 +517,31 @@ 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.
} }
optionalContentConfig.setOCGState(action); let operator;
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

View File

@ -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 { AppOptions } from "./app_options.js"; import { compatibilityParams } 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,6 +83,8 @@ 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
@ -150,9 +152,7 @@ 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 = this.maxCanvasPixels = options.maxCanvasPixels ?? MAX_CANVAS_PIXELS;
options.maxCanvasPixels ??
(AppOptions.getCompat("maxCanvasPixels") || 16777216);
this.pageColors = options.pageColors || null; this.pageColors = options.pageColors || null;
this.eventBus = options.eventBus; this.eventBus = options.eventBus;

View File

@ -13,12 +13,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { AnnotationMode, PixelsPerInch, shadow } from "pdfjs-lib";
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;
@ -63,14 +58,7 @@ function renderPage(
optionalContentConfigPromise, optionalContentConfigPromise,
printAnnotationStorage, printAnnotationStorage,
}; };
const renderTask = pdfPage.render(renderContext); return pdfPage.render(renderContext).promise;
return renderTask.promise.catch(reason => {
if (!(reason instanceof RenderingCancelledException)) {
console.error(reason);
}
throw reason;
});
}); });
} }
@ -80,15 +68,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 = pdfDocument.getOptionalContentConfig({ this._optionalContentConfigPromise =
intent: "print", optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
});
this._printAnnotationStoragePromise = this._printAnnotationStoragePromise =
printAnnotationStoragePromise || Promise.resolve(); printAnnotationStoragePromise || Promise.resolve();
this.currentPage = -1; this.currentPage = -1;

View File

@ -189,9 +189,7 @@ 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 => {

View File

@ -214,8 +214,6 @@ class PDFViewer {
#copyCallbackBound = null; #copyCallbackBound = null;
#enableHighlightFloatingButton = false;
#enablePermissions = false; #enablePermissions = false;
#mlManager = null; #mlManager = null;
@ -284,8 +282,6 @@ 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")) {
@ -785,9 +781,7 @@ 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();
@ -867,7 +861,6 @@ 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", {
@ -1829,7 +1822,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({ intent: "display" }); return this.pdfDocument.getOptionalContentConfig();
} }
return this._optionalContentConfigPromise; return this._optionalContentConfigPromise;
} }

View File

@ -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, /* defaultOnly = */ true) ? AppOptions.getAll(OptionKind.PREFERENCE)
: 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, /* defaultOnly = */ true) ? AppOptions.getAll(OptionKind.BROWSER)
: PDFJSDev.eval("BROWSER_PREFERENCES"); : PDFJSDev.eval("BROWSER_PREFERENCES");
const options = Object.create(null); const options = Object.create(null);