1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-22 16:18:08 +02:00

[api-minor] Add basic support for the SetOCGState action (issue 15372)

Note that this patch implements the `SetOCGState`-handling in `PDFLinkService`, rather than as a new method in `OptionalContentConfig`[1], since this action is nothing but a series of `setVisibility`-calls and that it seems quite uncommon in real-world PDF documents.

The new functionality also required some tweaks in the `PDFLayerViewer`, to ensure that the `layersView` in the sidebar is updated correctly when the optional-content visibility changes from "outside" of `PDFLayerViewer`.

---
[1] We can obviously move this code into `OptionalContentConfig` instead, if deemed necessary, but for an initial implementation I figured that doing it this way might be acceptable.
This commit is contained in:
Jonas Jenwald 2022-08-31 17:50:28 +02:00
parent e9bdbe4574
commit cc4baa2fe9
10 changed files with 185 additions and 31 deletions

View file

@ -110,6 +110,11 @@ class IPDFLinkService {
*/
executeNamedAction(action) {}
/**
* @param {Object} action
*/
executeSetOCGState(action) {}
/**
* @param {number} pageNum - page number.
* @param {Object} pageRef - reference to the page.

View file

@ -34,13 +34,19 @@ class PDFLayerViewer extends BaseTreeViewer {
super(options);
this.l10n = options.l10n;
this.eventBus._on("resetlayers", this._resetLayers.bind(this));
this.eventBus._on("optionalcontentconfigchanged", evt => {
this.#updateLayers(evt.promise);
});
this.eventBus._on("resetlayers", () => {
this.#updateLayers();
});
this.eventBus._on("togglelayerstree", this._toggleAllTreeItems.bind(this));
}
reset() {
super.reset();
this._optionalContentConfig = null;
this._optionalContentHash = null;
}
/**
@ -59,6 +65,7 @@ class PDFLayerViewer extends BaseTreeViewer {
_bindLink(element, { groupId, input }) {
const setVisibility = () => {
this._optionalContentConfig.setVisibility(groupId, input.checked);
this._optionalContentHash = this._optionalContentConfig.getHash();
this.eventBus.dispatch("optionalcontentconfig", {
source: this,
@ -123,6 +130,7 @@ class PDFLayerViewer extends BaseTreeViewer {
this._dispatchEvent(/* layersCount = */ 0);
return;
}
this._optionalContentHash = optionalContentConfig.getHash();
const fragment = document.createDocumentFragment(),
queue = [{ parent: fragment, groups }];
@ -170,23 +178,29 @@ class PDFLayerViewer extends BaseTreeViewer {
this._finishRendering(fragment, layersCount, hasAnyNesting);
}
/**
* @private
*/
async _resetLayers() {
async #updateLayers(promise = null) {
if (!this._optionalContentConfig) {
return;
}
// Fetch the default optional content configuration...
const optionalContentConfig =
await this._pdfDocument.getOptionalContentConfig();
const pdfDocument = this._pdfDocument;
const optionalContentConfig = await (promise ||
pdfDocument.getOptionalContentConfig());
this.eventBus.dispatch("optionalcontentconfig", {
source: this,
promise: Promise.resolve(optionalContentConfig),
});
if (pdfDocument !== this._pdfDocument) {
return; // The document was closed while the optional content resolved.
}
if (promise) {
if (optionalContentConfig.getHash() === this._optionalContentHash) {
return; // The optional content didn't change, hence no need to reset the UI.
}
} else {
this.eventBus.dispatch("optionalcontentconfig", {
source: this,
promise: Promise.resolve(optionalContentConfig),
});
}
// ... and reset the sidebarView to the default state.
// Reset the sidebarView to the new state.
this.render({
optionalContentConfig,
pdfDocument: this._pdfDocument,

View file

@ -493,6 +493,48 @@ class PDFLinkService {
});
}
/**
* @param {Object} action
*/
async executeSetOCGState(action) {
const pdfDocument = this.pdfDocument;
const optionalContentConfig = await this.pdfViewer
.optionalContentConfigPromise;
if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the optional content resolved.
}
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
);
}
/**
* @param {number} pageNum - page number.
* @param {Object} pageRef - reference to the page.
@ -676,6 +718,11 @@ class SimpleLinkService {
*/
executeNamedAction(action) {}
/**
* @param {Object} action
*/
executeSetOCGState(action) {}
/**
* @param {number} pageNum - page number.
* @param {Object} pageRef - reference to the page.

View file

@ -109,7 +109,7 @@ class PDFOutlineViewer extends BaseTreeViewer {
/**
* @private
*/
_bindLink(element, { url, newWindow, action, dest }) {
_bindLink(element, { url, newWindow, action, dest, setOCGState }) {
const { linkService } = this;
if (url) {
@ -124,6 +124,14 @@ class PDFOutlineViewer extends BaseTreeViewer {
};
return;
}
if (setOCGState) {
element.href = linkService.getAnchorUrl("");
element.onclick = () => {
linkService.executeSetOCGState(setOCGState);
return false;
};
return;
}
element.href = linkService.getDestinationHash(dest);
element.onclick = evt => {