Compare commits
	
		
			No commits in common. "2a68724b5342f8ac8e038f6ed2e1a39b2132d7d8" and "b14f6960710127adccba3a5fca5906c333259da5" have entirely different histories.
		
	
	
		
			2a68724b53
			...
			b14f696071
		
	
		
@ -72,10 +72,6 @@
 | 
			
		||||
      "type": "boolean",
 | 
			
		||||
      "default": false
 | 
			
		||||
    },
 | 
			
		||||
    "enableHighlightFloatingButton": {
 | 
			
		||||
      "type": "boolean",
 | 
			
		||||
      "default": false
 | 
			
		||||
    },
 | 
			
		||||
    "highlightEditorColors": {
 | 
			
		||||
      "type": "string",
 | 
			
		||||
      "default": "yellow=#FFFF98,green=#53FFBC,blue=#80EBFF,pink=#FFCBE6,red=#FF4F5F"
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										28
									
								
								gulpfile.mjs
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								gulpfile.mjs
									
									
									
									
									
								
							@ -863,17 +863,11 @@ async function parseDefaultPreferences(dir) {
 | 
			
		||||
    "./" + DEFAULT_PREFERENCES_DIR + dir + "app_options.mjs"
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const browserPrefs = AppOptions.getAll(
 | 
			
		||||
    OptionKind.BROWSER,
 | 
			
		||||
    /* defaultOnly = */ true
 | 
			
		||||
  );
 | 
			
		||||
  const browserPrefs = AppOptions.getAll(OptionKind.BROWSER);
 | 
			
		||||
  if (Object.keys(browserPrefs).length === 0) {
 | 
			
		||||
    throw new Error("No browser preferences found.");
 | 
			
		||||
  }
 | 
			
		||||
  const prefs = AppOptions.getAll(
 | 
			
		||||
    OptionKind.PREFERENCE,
 | 
			
		||||
    /* defaultOnly = */ true
 | 
			
		||||
  );
 | 
			
		||||
  const prefs = AppOptions.getAll(OptionKind.PREFERENCE);
 | 
			
		||||
  if (Object.keys(prefs).length === 0) {
 | 
			
		||||
    throw new Error("No default preferences found.");
 | 
			
		||||
  }
 | 
			
		||||
@ -2228,15 +2222,25 @@ gulp.task(
 | 
			
		||||
          ])
 | 
			
		||||
          .pipe(gulp.dest(DIST_DIR + "legacy/build/")),
 | 
			
		||||
        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/")),
 | 
			
		||||
        gulp
 | 
			
		||||
          .src(MINIFIED_DIR + "image_decoders/pdf.image_decoders.min.mjs")
 | 
			
		||||
          .pipe(gulp.dest(DIST_DIR + "image_decoders/")),
 | 
			
		||||
        gulp
 | 
			
		||||
          .src(
 | 
			
		||||
            MINIFIED_LEGACY_DIR + "build/{pdf,pdf.worker,pdf.sandbox}.min.mjs"
 | 
			
		||||
          )
 | 
			
		||||
          .src(MINIFIED_LEGACY_DIR + "build/pdf.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/")),
 | 
			
		||||
        gulp
 | 
			
		||||
          .src(
 | 
			
		||||
 | 
			
		||||
@ -318,8 +318,6 @@ pdfjs-editor-stamp-button-label = Add or edit images
 | 
			
		||||
pdfjs-editor-highlight-button =
 | 
			
		||||
    .title = Highlight
 | 
			
		||||
pdfjs-editor-highlight-button-label = Highlight
 | 
			
		||||
pdfjs-highlight-floating-button =
 | 
			
		||||
    .title = Highlight
 | 
			
		||||
 | 
			
		||||
## Remove button for the various kind of editor.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -445,10 +445,20 @@ class Catalog {
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
        groupRefs.put(groupRef);
 | 
			
		||||
 | 
			
		||||
        groups.push(this.#readOptionalContentGroup(groupRef));
 | 
			
		||||
        const group = this.xref.fetch(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;
 | 
			
		||||
    } catch (ex) {
 | 
			
		||||
      if (ex instanceof MissingDataException) {
 | 
			
		||||
@ -459,65 +469,7 @@ class Catalog {
 | 
			
		||||
    return shadow(this, "optionalContentConfig", config);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #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) {
 | 
			
		||||
  _readOptionalContentConfig(config, contentGroupRefs) {
 | 
			
		||||
    function parseOnOff(refs) {
 | 
			
		||||
      const onParsed = [];
 | 
			
		||||
      if (Array.isArray(refs)) {
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
   *   an {@link OptionalContentConfig} that contains all the optional content
 | 
			
		||||
   *   groups (assuming that the document has any).
 | 
			
		||||
   */
 | 
			
		||||
  getOptionalContentConfig({ intent = "display" } = {}) {
 | 
			
		||||
    const { renderingIntent } = this._transport.getRenderingIntent(intent);
 | 
			
		||||
 | 
			
		||||
    return this._transport.getOptionalContentConfig(renderingIntent);
 | 
			
		||||
  getOptionalContentConfig() {
 | 
			
		||||
    return this._transport.getOptionalContentConfig();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
@ -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
 | 
			
		||||
   *   {Array} of the annotation objects.
 | 
			
		||||
   */
 | 
			
		||||
  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,
 | 
			
		||||
      printAnnotationStorage
 | 
			
		||||
    );
 | 
			
		||||
    const { renderingIntent, cacheKey } = intentArgs;
 | 
			
		||||
    // If there was a pending destroy, cancel it so no cleanup happens during
 | 
			
		||||
    // this call to render...
 | 
			
		||||
    this.#pendingCleanup = false;
 | 
			
		||||
    // ... and ensure that a delayed cleanup is always aborted.
 | 
			
		||||
    this.#abortDelayedCleanup();
 | 
			
		||||
 | 
			
		||||
    optionalContentConfigPromise ||=
 | 
			
		||||
      this._transport.getOptionalContentConfig(renderingIntent);
 | 
			
		||||
    if (!optionalContentConfigPromise) {
 | 
			
		||||
      optionalContentConfigPromise = this._transport.getOptionalContentConfig();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let intentState = this._intentStates.get(cacheKey);
 | 
			
		||||
    let intentState = this._intentStates.get(intentArgs.cacheKey);
 | 
			
		||||
    if (!intentState) {
 | 
			
		||||
      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.
 | 
			
		||||
@ -1444,7 +1433,9 @@ class PDFPageProxy {
 | 
			
		||||
      intentState.streamReaderCancelTimeout = null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const intentPrint = !!(renderingIntent & RenderingIntentFlag.PRINT);
 | 
			
		||||
    const intentPrint = !!(
 | 
			
		||||
      intentArgs.renderingIntent & RenderingIntentFlag.PRINT
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // If there's no displayReadyCapability yet, then the operatorList
 | 
			
		||||
    // was never requested before. Make the request and create the promise.
 | 
			
		||||
@ -1521,12 +1512,6 @@ class PDFPageProxy {
 | 
			
		||||
        }
 | 
			
		||||
        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({
 | 
			
		||||
          transparency,
 | 
			
		||||
          optionalContentConfig,
 | 
			
		||||
@ -3009,10 +2994,10 @@ class WorkerTransport {
 | 
			
		||||
    return this.messageHandler.sendWithPromise("GetOutline", null);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getOptionalContentConfig(renderingIntent) {
 | 
			
		||||
    return this.#cacheSimpleMethod("GetOptionalContentConfig").then(
 | 
			
		||||
      data => new OptionalContentConfig(data, renderingIntent)
 | 
			
		||||
    );
 | 
			
		||||
  getOptionalContentConfig() {
 | 
			
		||||
    return this.messageHandler
 | 
			
		||||
      .sendWithPromise("GetOptionalContentConfig", null)
 | 
			
		||||
      .then(results => new OptionalContentConfig(results));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  getPermissions() {
 | 
			
		||||
 | 
			
		||||
@ -66,7 +66,6 @@ class DrawLayer {
 | 
			
		||||
  #createSVG(box) {
 | 
			
		||||
    const svg = DrawLayer._svgFactory.create(1, 1, /* skipDimensions = */ true);
 | 
			
		||||
    this.#parent.append(svg);
 | 
			
		||||
    svg.setAttribute("aria-hidden", true);
 | 
			
		||||
    DrawLayer.#setBox(svg, box);
 | 
			
		||||
 | 
			
		||||
    return svg;
 | 
			
		||||
 | 
			
		||||
@ -93,7 +93,6 @@ class ColorPicker {
 | 
			
		||||
    button.addEventListener("keydown", this.#boundKeyDown);
 | 
			
		||||
    const swatch = (this.#buttonSwatch = document.createElement("span"));
 | 
			
		||||
    swatch.className = "swatch";
 | 
			
		||||
    swatch.setAttribute("aria-hidden", true);
 | 
			
		||||
    swatch.style.backgroundColor = this.#defaultColor;
 | 
			
		||||
    button.append(swatch);
 | 
			
		||||
    return button;
 | 
			
		||||
 | 
			
		||||
@ -61,8 +61,6 @@ class HighlightEditor extends AnnotationEditor {
 | 
			
		||||
 | 
			
		||||
  #outlineId = null;
 | 
			
		||||
 | 
			
		||||
  #text = "";
 | 
			
		||||
 | 
			
		||||
  #thickness;
 | 
			
		||||
 | 
			
		||||
  #methodOfCreation = "";
 | 
			
		||||
@ -106,7 +104,6 @@ class HighlightEditor extends AnnotationEditor {
 | 
			
		||||
    this.#opacity = params.opacity || HighlightEditor._defaultOpacity;
 | 
			
		||||
    this.#boxes = params.boxes || null;
 | 
			
		||||
    this.#methodOfCreation = params.methodOfCreation || "";
 | 
			
		||||
    this.#text = params.text || "";
 | 
			
		||||
    this._isDraggable = false;
 | 
			
		||||
 | 
			
		||||
    if (params.highlightId > -1) {
 | 
			
		||||
@ -561,13 +558,6 @@ class HighlightEditor extends AnnotationEditor {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
      div.classList.add("free");
 | 
			
		||||
    } else {
 | 
			
		||||
@ -575,7 +565,6 @@ class HighlightEditor extends AnnotationEditor {
 | 
			
		||||
    }
 | 
			
		||||
    const highlightDiv = (this.#highlightDiv = document.createElement("div"));
 | 
			
		||||
    div.append(highlightDiv);
 | 
			
		||||
    highlightDiv.setAttribute("aria-hidden", "true");
 | 
			
		||||
    highlightDiv.className = "internal";
 | 
			
		||||
    highlightDiv.style.clipPath = this.#clipPathId;
 | 
			
		||||
    const [parentWidth, parentHeight] = this.parentDimensions;
 | 
			
		||||
 | 
			
		||||
@ -31,7 +31,6 @@ class EditorToolbar {
 | 
			
		||||
  render() {
 | 
			
		||||
    const editToolbar = (this.#toolbar = document.createElement("div"));
 | 
			
		||||
    editToolbar.className = "editToolbar";
 | 
			
		||||
    editToolbar.setAttribute("role", "toolbar");
 | 
			
		||||
    editToolbar.addEventListener("contextmenu", noContextMenu);
 | 
			
		||||
    editToolbar.addEventListener("pointerdown", EditorToolbar.#pointerDown);
 | 
			
		||||
 | 
			
		||||
@ -135,80 +134,4 @@ class 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 };
 | 
			
		||||
export { EditorToolbar };
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,6 @@ import {
 | 
			
		||||
  getRGB,
 | 
			
		||||
  PixelsPerInch,
 | 
			
		||||
} from "../display_utils.js";
 | 
			
		||||
import { HighlightToolbar } from "./toolbar.js";
 | 
			
		||||
 | 
			
		||||
function bindEvents(obj, element, names) {
 | 
			
		||||
  for (const name of names) {
 | 
			
		||||
@ -556,8 +555,6 @@ class AnnotationEditorUIManager {
 | 
			
		||||
 | 
			
		||||
  #editorsToRescale = new Set();
 | 
			
		||||
 | 
			
		||||
  #enableHighlightFloatingButton = false;
 | 
			
		||||
 | 
			
		||||
  #filterFactory = null;
 | 
			
		||||
 | 
			
		||||
  #focusMainContainerTimeoutId = null;
 | 
			
		||||
@ -566,8 +563,6 @@ class AnnotationEditorUIManager {
 | 
			
		||||
 | 
			
		||||
  #highlightWhenShiftUp = false;
 | 
			
		||||
 | 
			
		||||
  #highlightToolbar = null;
 | 
			
		||||
 | 
			
		||||
  #idManager = new IdManager();
 | 
			
		||||
 | 
			
		||||
  #isEnabled = false;
 | 
			
		||||
@ -776,7 +771,6 @@ class AnnotationEditorUIManager {
 | 
			
		||||
    pdfDocument,
 | 
			
		||||
    pageColors,
 | 
			
		||||
    highlightColors,
 | 
			
		||||
    enableHighlightFloatingButton,
 | 
			
		||||
    mlManager
 | 
			
		||||
  ) {
 | 
			
		||||
    this.#container = container;
 | 
			
		||||
@ -788,12 +782,10 @@ class AnnotationEditorUIManager {
 | 
			
		||||
    this._eventBus._on("scalechanging", this.#boundOnScaleChanging);
 | 
			
		||||
    this._eventBus._on("rotationchanging", this.#boundOnRotationChanging);
 | 
			
		||||
    this.#addSelectionListener();
 | 
			
		||||
    this.#addKeyboardManager();
 | 
			
		||||
    this.#annotationStorage = pdfDocument.annotationStorage;
 | 
			
		||||
    this.#filterFactory = pdfDocument.filterFactory;
 | 
			
		||||
    this.#pageColors = pageColors;
 | 
			
		||||
    this.#highlightColors = highlightColors || null;
 | 
			
		||||
    this.#enableHighlightFloatingButton = enableHighlightFloatingButton;
 | 
			
		||||
    this.#mlManager = mlManager || null;
 | 
			
		||||
    this.viewParameters = {
 | 
			
		||||
      realScale: PixelsPerInch.PDF_TO_CSS_UNITS,
 | 
			
		||||
@ -829,8 +821,6 @@ class AnnotationEditorUIManager {
 | 
			
		||||
    this.#selectedEditors.clear();
 | 
			
		||||
    this.#commandManager.destroy();
 | 
			
		||||
    this.#altTextManager?.destroy();
 | 
			
		||||
    this.#highlightToolbar?.hide();
 | 
			
		||||
    this.#highlightToolbar = null;
 | 
			
		||||
    if (this.#focusMainContainerTimeoutId) {
 | 
			
		||||
      clearTimeout(this.#focusMainContainerTimeoutId);
 | 
			
		||||
      this.#focusMainContainerTimeoutId = null;
 | 
			
		||||
@ -956,32 +946,24 @@ class AnnotationEditorUIManager {
 | 
			
		||||
    this.viewParameters.rotation = pagesRotation;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #getAnchorElementForSelection({ anchorNode }) {
 | 
			
		||||
    return anchorNode.nodeType === Node.TEXT_NODE
 | 
			
		||||
      ? anchorNode.parentElement
 | 
			
		||||
      : anchorNode;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  highlightSelection(methodOfCreation = "") {
 | 
			
		||||
    const selection = document.getSelection();
 | 
			
		||||
    if (!selection || selection.isCollapsed) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const { anchorNode, anchorOffset, focusNode, focusOffset } = selection;
 | 
			
		||||
    const text = selection.toString();
 | 
			
		||||
    const anchorElement = this.#getAnchorElementForSelection(selection);
 | 
			
		||||
    const anchorElement =
 | 
			
		||||
      anchorNode.nodeType === Node.TEXT_NODE
 | 
			
		||||
        ? anchorNode.parentElement
 | 
			
		||||
        : anchorNode;
 | 
			
		||||
    const textLayer = anchorElement.closest(".textLayer");
 | 
			
		||||
    const boxes = this.getSelectionBoxes(textLayer);
 | 
			
		||||
    if (!boxes) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    selection.empty();
 | 
			
		||||
    if (this.#mode === AnnotationEditorType.NONE) {
 | 
			
		||||
      this._eventBus.dispatch("showannotationeditorui", {
 | 
			
		||||
        source: this,
 | 
			
		||||
        mode: AnnotationEditorType.HIGHLIGHT,
 | 
			
		||||
      });
 | 
			
		||||
      this.showAllEditors("highlight", true, /* updateButton = */ true);
 | 
			
		||||
    }
 | 
			
		||||
    for (const layer of this.#allLayers.values()) {
 | 
			
		||||
      if (layer.hasTextLayer(textLayer)) {
 | 
			
		||||
@ -992,28 +974,12 @@ class AnnotationEditorUIManager {
 | 
			
		||||
          anchorOffset,
 | 
			
		||||
          focusNode,
 | 
			
		||||
          focusOffset,
 | 
			
		||||
          text,
 | 
			
		||||
        });
 | 
			
		||||
        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.
 | 
			
		||||
   * @param {AnnotationEditor} editor
 | 
			
		||||
@ -1032,7 +998,6 @@ class AnnotationEditorUIManager {
 | 
			
		||||
    const selection = document.getSelection();
 | 
			
		||||
    if (!selection || selection.isCollapsed) {
 | 
			
		||||
      if (this.#selectedTextNode) {
 | 
			
		||||
        this.#highlightToolbar?.hide();
 | 
			
		||||
        this.#selectedTextNode = null;
 | 
			
		||||
        this.#dispatchUpdateStates({
 | 
			
		||||
          hasSelectedText: false,
 | 
			
		||||
@ -1045,11 +1010,12 @@ class AnnotationEditorUIManager {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const anchorElement = this.#getAnchorElementForSelection(selection);
 | 
			
		||||
    const textLayer = anchorElement.closest(".textLayer");
 | 
			
		||||
    if (!textLayer) {
 | 
			
		||||
    const anchorElement =
 | 
			
		||||
      anchorNode.nodeType === Node.TEXT_NODE
 | 
			
		||||
        ? anchorNode.parentElement
 | 
			
		||||
        : anchorNode;
 | 
			
		||||
    if (!anchorElement.closest(".textLayer")) {
 | 
			
		||||
      if (this.#selectedTextNode) {
 | 
			
		||||
        this.#highlightToolbar?.hide();
 | 
			
		||||
        this.#selectedTextNode = null;
 | 
			
		||||
        this.#dispatchUpdateStates({
 | 
			
		||||
          hasSelectedText: false,
 | 
			
		||||
@ -1057,22 +1023,16 @@ class AnnotationEditorUIManager {
 | 
			
		||||
      }
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this.#highlightToolbar?.hide();
 | 
			
		||||
    this.#selectedTextNode = anchorNode;
 | 
			
		||||
    this.#dispatchUpdateStates({
 | 
			
		||||
      hasSelectedText: true,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
      this.#mode !== AnnotationEditorType.HIGHLIGHT &&
 | 
			
		||||
      this.#mode !== AnnotationEditorType.NONE
 | 
			
		||||
    ) {
 | 
			
		||||
    if (this.#mode !== AnnotationEditorType.HIGHLIGHT) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (this.#mode === AnnotationEditorType.HIGHLIGHT) {
 | 
			
		||||
      this.showAllEditors("highlight", true, /* updateButton = */ true);
 | 
			
		||||
    }
 | 
			
		||||
    this.showAllEditors("highlight", true, /* updateButton = */ true);
 | 
			
		||||
 | 
			
		||||
    this.#highlightWhenShiftUp = this.isShiftKeyDown;
 | 
			
		||||
    if (!this.isShiftKeyDown) {
 | 
			
		||||
@ -1084,7 +1044,7 @@ class AnnotationEditorUIManager {
 | 
			
		||||
        window.removeEventListener("pointerup", pointerup);
 | 
			
		||||
        window.removeEventListener("blur", pointerup);
 | 
			
		||||
        if (e.type === "pointerup") {
 | 
			
		||||
          this.#onSelectEnd("main_toolbar");
 | 
			
		||||
          this.highlightSelection("main_toolbar");
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
      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() {
 | 
			
		||||
    document.addEventListener("selectionchange", this.#boundSelectionChange);
 | 
			
		||||
  }
 | 
			
		||||
@ -1122,7 +1074,7 @@ class AnnotationEditorUIManager {
 | 
			
		||||
    this.isShiftKeyDown = false;
 | 
			
		||||
    if (this.#highlightWhenShiftUp) {
 | 
			
		||||
      this.#highlightWhenShiftUp = false;
 | 
			
		||||
      this.#onSelectEnd("main_toolbar");
 | 
			
		||||
      this.highlightSelection("main_toolbar");
 | 
			
		||||
    }
 | 
			
		||||
    if (!this.hasSelection) {
 | 
			
		||||
      return;
 | 
			
		||||
@ -1298,10 +1250,7 @@ class AnnotationEditorUIManager {
 | 
			
		||||
    if (!this.isShiftKeyDown && event.key === "Shift") {
 | 
			
		||||
      this.isShiftKeyDown = true;
 | 
			
		||||
    }
 | 
			
		||||
    if (
 | 
			
		||||
      this.#mode !== AnnotationEditorType.NONE &&
 | 
			
		||||
      !this.isEditorHandlingKeyboard
 | 
			
		||||
    ) {
 | 
			
		||||
    if (!this.isEditorHandlingKeyboard) {
 | 
			
		||||
      AnnotationEditorUIManager._keyboardManager.exec(this, event);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -1315,7 +1264,7 @@ class AnnotationEditorUIManager {
 | 
			
		||||
      this.isShiftKeyDown = false;
 | 
			
		||||
      if (this.#highlightWhenShiftUp) {
 | 
			
		||||
        this.#highlightWhenShiftUp = false;
 | 
			
		||||
        this.#onSelectEnd("main_toolbar");
 | 
			
		||||
        this.highlightSelection("main_toolbar");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -1384,6 +1333,7 @@ class AnnotationEditorUIManager {
 | 
			
		||||
  setEditingState(isEditing) {
 | 
			
		||||
    if (isEditing) {
 | 
			
		||||
      this.#addFocusManager();
 | 
			
		||||
      this.#addKeyboardManager();
 | 
			
		||||
      this.#addCopyPasteListeners();
 | 
			
		||||
      this.#dispatchUpdateStates({
 | 
			
		||||
        isEditing: this.#mode !== AnnotationEditorType.NONE,
 | 
			
		||||
@ -1394,6 +1344,7 @@ class AnnotationEditorUIManager {
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
      this.#removeFocusManager();
 | 
			
		||||
      this.#removeKeyboardManager();
 | 
			
		||||
      this.#removeCopyPasteListeners();
 | 
			
		||||
      this.#dispatchUpdateStates({
 | 
			
		||||
        isEditing: false,
 | 
			
		||||
 | 
			
		||||
@ -424,22 +424,21 @@ class PDFNodeStreamFsFullReader extends BaseFullReader {
 | 
			
		||||
      path = path.replace(/^\//, "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fs.promises.lstat(path).then(
 | 
			
		||||
      stat => {
 | 
			
		||||
        // Setting right content length.
 | 
			
		||||
        this._contentLength = stat.size;
 | 
			
		||||
 | 
			
		||||
        this._setReadableStream(fs.createReadStream(path));
 | 
			
		||||
        this._headersCapability.resolve();
 | 
			
		||||
      },
 | 
			
		||||
      error => {
 | 
			
		||||
    fs.lstat(path, (error, stat) => {
 | 
			
		||||
      if (error) {
 | 
			
		||||
        if (error.code === "ENOENT") {
 | 
			
		||||
          error = new MissingPDFException(`Missing PDF "${path}".`);
 | 
			
		||||
        }
 | 
			
		||||
        this._storedError = error;
 | 
			
		||||
        this._headersCapability.reject(error);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    );
 | 
			
		||||
      // Setting right content length.
 | 
			
		||||
      this._contentLength = stat.size;
 | 
			
		||||
 | 
			
		||||
      this._setReadableStream(fs.createReadStream(path));
 | 
			
		||||
      this._headersCapability.resolve();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -71,7 +71,15 @@ if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("SKIP_BABEL")) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {}
 | 
			
		||||
 | 
			
		||||
@ -13,63 +13,33 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  info,
 | 
			
		||||
  objectFromMap,
 | 
			
		||||
  RenderingIntentFlag,
 | 
			
		||||
  unreachable,
 | 
			
		||||
  warn,
 | 
			
		||||
} from "../shared/util.js";
 | 
			
		||||
import { info, objectFromMap, unreachable, warn } from "../shared/util.js";
 | 
			
		||||
import { MurmurHash3_64 } from "../shared/murmurhash3.js";
 | 
			
		||||
 | 
			
		||||
const INTERNAL = Symbol("INTERNAL");
 | 
			
		||||
 | 
			
		||||
class OptionalContentGroup {
 | 
			
		||||
  #isDisplay = false;
 | 
			
		||||
 | 
			
		||||
  #isPrint = false;
 | 
			
		||||
 | 
			
		||||
  #userSet = false;
 | 
			
		||||
 | 
			
		||||
  #visible = true;
 | 
			
		||||
 | 
			
		||||
  constructor(renderingIntent, { name, intent, usage }) {
 | 
			
		||||
    this.#isDisplay = !!(renderingIntent & RenderingIntentFlag.DISPLAY);
 | 
			
		||||
    this.#isPrint = !!(renderingIntent & RenderingIntentFlag.PRINT);
 | 
			
		||||
 | 
			
		||||
  constructor(name, intent) {
 | 
			
		||||
    this.name = name;
 | 
			
		||||
    this.intent = intent;
 | 
			
		||||
    this.usage = usage;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @type {boolean}
 | 
			
		||||
   */
 | 
			
		||||
  get 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;
 | 
			
		||||
    return this.#visible;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @ignore
 | 
			
		||||
   */
 | 
			
		||||
  _setVisible(internal, visible, userSet = false) {
 | 
			
		||||
  _setVisible(internal, visible) {
 | 
			
		||||
    if (internal !== INTERNAL) {
 | 
			
		||||
      unreachable("Internal method `_setVisible` called.");
 | 
			
		||||
    }
 | 
			
		||||
    this.#userSet = userSet;
 | 
			
		||||
    this.#visible = visible;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -83,9 +53,7 @@ class OptionalContentConfig {
 | 
			
		||||
 | 
			
		||||
  #order = null;
 | 
			
		||||
 | 
			
		||||
  constructor(data, renderingIntent = RenderingIntentFlag.DISPLAY) {
 | 
			
		||||
    this.renderingIntent = renderingIntent;
 | 
			
		||||
 | 
			
		||||
  constructor(data) {
 | 
			
		||||
    this.name = null;
 | 
			
		||||
    this.creator = null;
 | 
			
		||||
 | 
			
		||||
@ -98,7 +66,7 @@ class OptionalContentConfig {
 | 
			
		||||
    for (const group of data.groups) {
 | 
			
		||||
      this.#groups.set(
 | 
			
		||||
        group.id,
 | 
			
		||||
        new OptionalContentGroup(renderingIntent, group)
 | 
			
		||||
        new OptionalContentGroup(group.name, group.intent)
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -230,44 +198,11 @@ class OptionalContentConfig {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setVisibility(id, visible = true) {
 | 
			
		||||
    const group = this.#groups.get(id);
 | 
			
		||||
    if (!group) {
 | 
			
		||||
    if (!this.#groups.has(id)) {
 | 
			
		||||
      warn(`Optional content group not found: ${id}`);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    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.#groups.get(id)._setVisible(INTERNAL, !!visible);
 | 
			
		||||
 | 
			
		||||
    this.#cachedGetHash = null;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -684,9 +684,7 @@ class Driver {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            task.pdfDoc = doc;
 | 
			
		||||
            task.optionalContentConfigPromise = doc.getOptionalContentConfig({
 | 
			
		||||
              intent: task.print ? "print" : "display",
 | 
			
		||||
            });
 | 
			
		||||
            task.optionalContentConfigPromise = doc.getOptionalContentConfig();
 | 
			
		||||
 | 
			
		||||
            if (task.optionalContent) {
 | 
			
		||||
              const entries = Object.entries(task.optionalContent),
 | 
			
		||||
 | 
			
		||||
@ -35,7 +35,6 @@ async function runTests(results) {
 | 
			
		||||
      "scripting_spec.mjs",
 | 
			
		||||
      "stamp_editor_spec.mjs",
 | 
			
		||||
      "text_field_spec.mjs",
 | 
			
		||||
      "viewer_spec.mjs",
 | 
			
		||||
    ],
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -46,11 +46,8 @@ const getXY = (page, selector) =>
 | 
			
		||||
    return `${bbox.x}::${bbox.y}`;
 | 
			
		||||
  }, selector);
 | 
			
		||||
 | 
			
		||||
const getSpanRectFromText = async (page, pageNumber, text) => {
 | 
			
		||||
  await page.waitForSelector(
 | 
			
		||||
    `.page[data-page-number="${pageNumber}"] > .textLayer .endOfContent`
 | 
			
		||||
  );
 | 
			
		||||
  return page.evaluate(
 | 
			
		||||
const getSpanRectFromText = (page, pageNumber, text) =>
 | 
			
		||||
  page.evaluate(
 | 
			
		||||
    (number, content) => {
 | 
			
		||||
      for (const el of document.querySelectorAll(
 | 
			
		||||
        `.page[data-page-number="${number}"] > .textLayer > span`
 | 
			
		||||
@ -65,7 +62,6 @@ const getSpanRectFromText = async (page, pageNumber, text) => {
 | 
			
		||||
    pageNumber,
 | 
			
		||||
    text
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe("Highlight Editor", () => {
 | 
			
		||||
  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");
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -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");
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -1 +0,0 @@
 | 
			
		||||
https://bugzilla.mozilla.org/attachment.cgi?id=9327375
 | 
			
		||||
@ -4016,23 +4016,6 @@
 | 
			
		||||
    "lastPage": 5,
 | 
			
		||||
    "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",
 | 
			
		||||
    "file": "pdfs/issue8586.pdf",
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -7,7 +7,6 @@
 | 
			
		||||
    "annotation_spec.js",
 | 
			
		||||
    "annotation_storage_spec.js",
 | 
			
		||||
    "api_spec.js",
 | 
			
		||||
    "app_options_spec.js",
 | 
			
		||||
    "bidi_spec.js",
 | 
			
		||||
    "cff_parser_spec.js",
 | 
			
		||||
    "cmap_spec.js",
 | 
			
		||||
 | 
			
		||||
@ -50,7 +50,6 @@ async function initializePDFJS(callback) {
 | 
			
		||||
      "pdfjs-test/unit/annotation_spec.js",
 | 
			
		||||
      "pdfjs-test/unit/annotation_storage_spec.js",
 | 
			
		||||
      "pdfjs-test/unit/api_spec.js",
 | 
			
		||||
      "pdfjs-test/unit/app_options_spec.js",
 | 
			
		||||
      "pdfjs-test/unit/bidi_spec.js",
 | 
			
		||||
      "pdfjs-test/unit/cff_parser_spec.js",
 | 
			
		||||
      "pdfjs-test/unit/cmap_spec.js",
 | 
			
		||||
 | 
			
		||||
@ -48,19 +48,6 @@
 | 
			
		||||
    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 {
 | 
			
		||||
  cursor: var(--editorFreeHighlight-editing-cursor);
 | 
			
		||||
 | 
			
		||||
@ -195,12 +182,10 @@
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.annotationEditorLayer
 | 
			
		||||
  :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor),
 | 
			
		||||
.textLayer {
 | 
			
		||||
  :is(.freeTextEditor, .inkEditor, .stampEditor, .highlightEditor) {
 | 
			
		||||
  .editToolbar {
 | 
			
		||||
    --editor-toolbar-delete-image: url(images/editor-toolbar-delete.svg);
 | 
			
		||||
    --editor-toolbar-bg-color: #f0f0f4;
 | 
			
		||||
    --editor-toolbar-highlight-image: url(images/toolbarButton-editorHighlight.svg);
 | 
			
		||||
    --editor-toolbar-fg-color: #2e2e56;
 | 
			
		||||
    --editor-toolbar-border-color: #8f8f9d;
 | 
			
		||||
    --editor-toolbar-hover-border-color: var(--editor-toolbar-border-color);
 | 
			
		||||
@ -286,25 +271,6 @@
 | 
			
		||||
        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 {
 | 
			
		||||
        width: var(--editor-toolbar-height);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -424,9 +424,6 @@ const PDFViewerApplication = {
 | 
			
		||||
      annotationMode: AppOptions.get("annotationMode"),
 | 
			
		||||
      annotationEditorMode,
 | 
			
		||||
      annotationEditorHighlightColors: AppOptions.get("highlightEditorColors"),
 | 
			
		||||
      enableHighlightFloatingButton: AppOptions.get(
 | 
			
		||||
        "enableHighlightFloatingButton"
 | 
			
		||||
      ),
 | 
			
		||||
      imageResourcesPath: AppOptions.get("imageResourcesPath"),
 | 
			
		||||
      enablePrintAutoRotate: AppOptions.get("enablePrintAutoRotate"),
 | 
			
		||||
      maxCanvasPixels: AppOptions.get("maxCanvasPixels"),
 | 
			
		||||
@ -1799,6 +1796,7 @@ const PDFViewerApplication = {
 | 
			
		||||
      pagesOverview: this.pdfViewer.getPagesOverview(),
 | 
			
		||||
      printContainer: this.appConfig.printContainer,
 | 
			
		||||
      printResolution: AppOptions.get("printResolution"),
 | 
			
		||||
      optionalContentConfigPromise: this.pdfViewer.optionalContentConfigPromise,
 | 
			
		||||
      printAnnotationStoragePromise: this._printAnnotationStoragePromise,
 | 
			
		||||
    });
 | 
			
		||||
    this.forceRendering();
 | 
			
		||||
@ -2024,9 +2022,8 @@ const PDFViewerApplication = {
 | 
			
		||||
    });
 | 
			
		||||
    const scroll = (_boundEvents.mainContainerScroll = () => {
 | 
			
		||||
      if (
 | 
			
		||||
        this._isCtrlKeyDown ||
 | 
			
		||||
        (this._lastScrollTop === mainContainer.scrollTop &&
 | 
			
		||||
          this._lastScrollLeft === mainContainer.scrollLeft)
 | 
			
		||||
        this._lastScrollTop === mainContainer.scrollTop &&
 | 
			
		||||
        this._lastScrollLeft === mainContainer.scrollLeft
 | 
			
		||||
      ) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
@ -143,14 +143,6 @@ const defaultOptions = {
 | 
			
		||||
    value: typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING"),
 | 
			
		||||
    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: {
 | 
			
		||||
    /** @type {boolean} */
 | 
			
		||||
    value: false,
 | 
			
		||||
@ -417,64 +409,57 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
  constructor() {
 | 
			
		||||
    throw new Error("Cannot initialize AppOptions.");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getCompat(name) {
 | 
			
		||||
    return compatibilityParams[name] ?? undefined;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get(name) {
 | 
			
		||||
    return (
 | 
			
		||||
      userOptions[name] ??
 | 
			
		||||
      compatibilityParams[name] ??
 | 
			
		||||
      defaultOptions[name]?.value ??
 | 
			
		||||
      undefined
 | 
			
		||||
    );
 | 
			
		||||
    const userOption = userOptions[name];
 | 
			
		||||
    if (userOption !== undefined) {
 | 
			
		||||
      return userOption;
 | 
			
		||||
    }
 | 
			
		||||
    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);
 | 
			
		||||
    for (const name in defaultOptions) {
 | 
			
		||||
      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 (kind && !(kind & defaultOption.kind)) {
 | 
			
		||||
        continue;
 | 
			
		||||
          if (
 | 
			
		||||
            valueType === "boolean" ||
 | 
			
		||||
            valueType === "string" ||
 | 
			
		||||
            (valueType === "number" && Number.isInteger(value))
 | 
			
		||||
          ) {
 | 
			
		||||
            options[name] = value;
 | 
			
		||||
            continue;
 | 
			
		||||
          }
 | 
			
		||||
          throw new Error(`Invalid type for preference: ${name}`);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      options[name] = defaultOnly
 | 
			
		||||
        ? defaultOption.value
 | 
			
		||||
        : userOptions[name] ?? compatibilityParams[name] ?? defaultOption.value;
 | 
			
		||||
      const userOption = userOptions[name];
 | 
			
		||||
      options[name] =
 | 
			
		||||
        userOption !== undefined
 | 
			
		||||
          ? userOption
 | 
			
		||||
          : compatibilityParams[name] ?? defaultOption.value;
 | 
			
		||||
    }
 | 
			
		||||
    return options;
 | 
			
		||||
  }
 | 
			
		||||
@ -508,4 +493,4 @@ class AppOptions {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export { AppOptions, OptionKind };
 | 
			
		||||
export { AppOptions, compatibilityParams, OptionKind };
 | 
			
		||||
 | 
			
		||||
@ -119,15 +119,15 @@ class FirefoxPrintService {
 | 
			
		||||
    pagesOverview,
 | 
			
		||||
    printContainer,
 | 
			
		||||
    printResolution,
 | 
			
		||||
    optionalContentConfigPromise = null,
 | 
			
		||||
    printAnnotationStoragePromise = null,
 | 
			
		||||
  }) {
 | 
			
		||||
    this.pdfDocument = pdfDocument;
 | 
			
		||||
    this.pagesOverview = pagesOverview;
 | 
			
		||||
    this.printContainer = printContainer;
 | 
			
		||||
    this._printResolution = printResolution || 150;
 | 
			
		||||
    this._optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
 | 
			
		||||
      intent: "print",
 | 
			
		||||
    });
 | 
			
		||||
    this._optionalContentConfigPromise =
 | 
			
		||||
      optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
 | 
			
		||||
    this._printAnnotationStoragePromise =
 | 
			
		||||
      printAnnotationStoragePromise || Promise.resolve();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -258,8 +258,14 @@ if (PDFJSDev.test("GECKOVIEW")) {
 | 
			
		||||
            const hasWillPrint =
 | 
			
		||||
              pdfViewer.enableScripting &&
 | 
			
		||||
              !!(await pdfDocument.getJSActions())?.WillPrint;
 | 
			
		||||
            const hasUnchangedOptionalContent = (
 | 
			
		||||
              await pdfViewer.optionalContentConfigPromise
 | 
			
		||||
            ).hasInitialVisibility;
 | 
			
		||||
 | 
			
		||||
            result = hasUnchangedAnnotations && !hasWillPrint;
 | 
			
		||||
            result =
 | 
			
		||||
              hasUnchangedAnnotations &&
 | 
			
		||||
              !hasWillPrint &&
 | 
			
		||||
              hasUnchangedOptionalContent;
 | 
			
		||||
          } catch {
 | 
			
		||||
            console.warn("Unable to check if the document can be downloaded.");
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
@ -182,7 +182,7 @@ class PDFLayerViewer extends BaseTreeViewer {
 | 
			
		||||
    }
 | 
			
		||||
    const pdfDocument = this._pdfDocument;
 | 
			
		||||
    const optionalContentConfig = await (promise ||
 | 
			
		||||
      pdfDocument.getOptionalContentConfig({ intent: "display" }));
 | 
			
		||||
      pdfDocument.getOptionalContentConfig());
 | 
			
		||||
 | 
			
		||||
    if (pdfDocument !== this._pdfDocument) {
 | 
			
		||||
      return; // The document was closed while the optional content resolved.
 | 
			
		||||
 | 
			
		||||
@ -517,7 +517,31 @@ class PDFLinkService {
 | 
			
		||||
    if (pdfDocument !== this.pdfDocument) {
 | 
			
		||||
      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(
 | 
			
		||||
      optionalContentConfig
 | 
			
		||||
 | 
			
		||||
@ -41,7 +41,7 @@ import {
 | 
			
		||||
} from "./ui_utils.js";
 | 
			
		||||
import { AnnotationEditorLayerBuilder } from "./annotation_editor_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 { GenericL10n } from "web-null_l10n";
 | 
			
		||||
import { SimpleLinkService } from "./pdf_link_service.js";
 | 
			
		||||
@ -83,6 +83,8 @@ import { XfaLayerBuilder } from "./xfa_layer_builder.js";
 | 
			
		||||
 *   the necessary layer-properties.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
const MAX_CANVAS_PIXELS = compatibilityParams.maxCanvasPixels || 16777216;
 | 
			
		||||
 | 
			
		||||
const DEFAULT_LAYER_PROPERTIES =
 | 
			
		||||
  typeof PDFJSDev === "undefined" || !PDFJSDev.test("COMPONENTS")
 | 
			
		||||
    ? null
 | 
			
		||||
@ -150,9 +152,7 @@ class PDFPageView {
 | 
			
		||||
    this.#annotationMode =
 | 
			
		||||
      options.annotationMode ?? AnnotationMode.ENABLE_FORMS;
 | 
			
		||||
    this.imageResourcesPath = options.imageResourcesPath || "";
 | 
			
		||||
    this.maxCanvasPixels =
 | 
			
		||||
      options.maxCanvasPixels ??
 | 
			
		||||
      (AppOptions.getCompat("maxCanvasPixels") || 16777216);
 | 
			
		||||
    this.maxCanvasPixels = options.maxCanvasPixels ?? MAX_CANVAS_PIXELS;
 | 
			
		||||
    this.pageColors = options.pageColors || null;
 | 
			
		||||
 | 
			
		||||
    this.eventBus = options.eventBus;
 | 
			
		||||
 | 
			
		||||
@ -13,12 +13,7 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import {
 | 
			
		||||
  AnnotationMode,
 | 
			
		||||
  PixelsPerInch,
 | 
			
		||||
  RenderingCancelledException,
 | 
			
		||||
  shadow,
 | 
			
		||||
} from "pdfjs-lib";
 | 
			
		||||
import { AnnotationMode, PixelsPerInch, shadow } from "pdfjs-lib";
 | 
			
		||||
import { getXfaHtmlForPrinting } from "./print_utils.js";
 | 
			
		||||
 | 
			
		||||
let activeService = null;
 | 
			
		||||
@ -63,14 +58,7 @@ function renderPage(
 | 
			
		||||
      optionalContentConfigPromise,
 | 
			
		||||
      printAnnotationStorage,
 | 
			
		||||
    };
 | 
			
		||||
    const renderTask = pdfPage.render(renderContext);
 | 
			
		||||
 | 
			
		||||
    return renderTask.promise.catch(reason => {
 | 
			
		||||
      if (!(reason instanceof RenderingCancelledException)) {
 | 
			
		||||
        console.error(reason);
 | 
			
		||||
      }
 | 
			
		||||
      throw reason;
 | 
			
		||||
    });
 | 
			
		||||
    return pdfPage.render(renderContext).promise;
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -80,15 +68,15 @@ class PDFPrintService {
 | 
			
		||||
    pagesOverview,
 | 
			
		||||
    printContainer,
 | 
			
		||||
    printResolution,
 | 
			
		||||
    optionalContentConfigPromise = null,
 | 
			
		||||
    printAnnotationStoragePromise = null,
 | 
			
		||||
  }) {
 | 
			
		||||
    this.pdfDocument = pdfDocument;
 | 
			
		||||
    this.pagesOverview = pagesOverview;
 | 
			
		||||
    this.printContainer = printContainer;
 | 
			
		||||
    this._printResolution = printResolution || 150;
 | 
			
		||||
    this._optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
 | 
			
		||||
      intent: "print",
 | 
			
		||||
    });
 | 
			
		||||
    this._optionalContentConfigPromise =
 | 
			
		||||
      optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
 | 
			
		||||
    this._printAnnotationStoragePromise =
 | 
			
		||||
      printAnnotationStoragePromise || Promise.resolve();
 | 
			
		||||
    this.currentPage = -1;
 | 
			
		||||
 | 
			
		||||
@ -189,9 +189,7 @@ class PDFThumbnailViewer {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    const firstPagePromise = pdfDocument.getPage(1);
 | 
			
		||||
    const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
 | 
			
		||||
      intent: "display",
 | 
			
		||||
    });
 | 
			
		||||
    const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig();
 | 
			
		||||
 | 
			
		||||
    firstPagePromise
 | 
			
		||||
      .then(firstPdfPage => {
 | 
			
		||||
 | 
			
		||||
@ -214,8 +214,6 @@ class PDFViewer {
 | 
			
		||||
 | 
			
		||||
  #copyCallbackBound = null;
 | 
			
		||||
 | 
			
		||||
  #enableHighlightFloatingButton = false;
 | 
			
		||||
 | 
			
		||||
  #enablePermissions = false;
 | 
			
		||||
 | 
			
		||||
  #mlManager = null;
 | 
			
		||||
@ -284,8 +282,6 @@ class PDFViewer {
 | 
			
		||||
      options.annotationEditorMode ?? AnnotationEditorType.NONE;
 | 
			
		||||
    this.#annotationEditorHighlightColors =
 | 
			
		||||
      options.annotationEditorHighlightColors || null;
 | 
			
		||||
    this.#enableHighlightFloatingButton =
 | 
			
		||||
      options.enableHighlightFloatingButton === true;
 | 
			
		||||
    this.imageResourcesPath = options.imageResourcesPath || "";
 | 
			
		||||
    this.enablePrintAutoRotate = options.enablePrintAutoRotate || false;
 | 
			
		||||
    if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) {
 | 
			
		||||
@ -785,9 +781,7 @@ class PDFViewer {
 | 
			
		||||
    const pagesCount = pdfDocument.numPages;
 | 
			
		||||
    const firstPagePromise = pdfDocument.getPage(1);
 | 
			
		||||
    // Rendering (potentially) depends on this, hence fetching it immediately.
 | 
			
		||||
    const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig({
 | 
			
		||||
      intent: "display",
 | 
			
		||||
    });
 | 
			
		||||
    const optionalContentConfigPromise = pdfDocument.getOptionalContentConfig();
 | 
			
		||||
    const permissionsPromise = this.#enablePermissions
 | 
			
		||||
      ? pdfDocument.getPermissions()
 | 
			
		||||
      : Promise.resolve();
 | 
			
		||||
@ -867,7 +861,6 @@ class PDFViewer {
 | 
			
		||||
              pdfDocument,
 | 
			
		||||
              this.pageColors,
 | 
			
		||||
              this.#annotationEditorHighlightColors,
 | 
			
		||||
              this.#enableHighlightFloatingButton,
 | 
			
		||||
              this.#mlManager
 | 
			
		||||
            );
 | 
			
		||||
            this.eventBus.dispatch("annotationeditoruimanager", {
 | 
			
		||||
@ -1829,7 +1822,7 @@ class PDFViewer {
 | 
			
		||||
      console.error("optionalContentConfigPromise: Not initialized yet.");
 | 
			
		||||
      // Prevent issues if the getter is accessed *before* the `onePageRendered`
 | 
			
		||||
      // promise has resolved; won't (normally) happen in the default viewer.
 | 
			
		||||
      return this.pdfDocument.getOptionalContentConfig({ intent: "display" });
 | 
			
		||||
      return this.pdfDocument.getOptionalContentConfig();
 | 
			
		||||
    }
 | 
			
		||||
    return this._optionalContentConfigPromise;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -23,7 +23,7 @@ import { AppOptions, OptionKind } from "./app_options.js";
 | 
			
		||||
class BasePreferences {
 | 
			
		||||
  #defaults = Object.freeze(
 | 
			
		||||
    typeof PDFJSDev === "undefined"
 | 
			
		||||
      ? AppOptions.getAll(OptionKind.PREFERENCE, /* defaultOnly = */ true)
 | 
			
		||||
      ? AppOptions.getAll(OptionKind.PREFERENCE)
 | 
			
		||||
      : PDFJSDev.eval("DEFAULT_PREFERENCES")
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,7 @@ class BasePreferences {
 | 
			
		||||
      ({ browserPrefs, prefs }) => {
 | 
			
		||||
        const BROWSER_PREFS =
 | 
			
		||||
          typeof PDFJSDev === "undefined"
 | 
			
		||||
            ? AppOptions.getAll(OptionKind.BROWSER, /* defaultOnly = */ true)
 | 
			
		||||
            ? AppOptions.getAll(OptionKind.BROWSER)
 | 
			
		||||
            : PDFJSDev.eval("BROWSER_PREFERENCES");
 | 
			
		||||
        const options = Object.create(null);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user