Merge pull request #15778 from Snuffleupagus/keep-structTree

Don't re-create the `structTreeLayer` on zooming and rotation
This commit is contained in:
Jonas Jenwald 2022-12-06 10:02:20 +01:00 committed by GitHub
commit cdd39ec69e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 51 additions and 86 deletions

View File

@ -1623,9 +1623,7 @@ class PDFPageProxy {
* or `null` when no structure tree is present for the current page. * or `null` when no structure tree is present for the current page.
*/ */
getStructTree() { getStructTree() {
return (this._structTreePromise ||= this._transport.getStructTree( return this._transport.getStructTree(this._pageIndex);
this._pageIndex
));
} }
/** /**
@ -1659,7 +1657,6 @@ class PDFPageProxy {
this._bitmaps.clear(); this._bitmaps.clear();
this._annotationPromises.clear(); this._annotationPromises.clear();
this._jsActionsPromise = null; this._jsActionsPromise = null;
this._structTreePromise = null;
this.pendingCleanup = false; this.pendingCleanup = false;
return Promise.all(waitOn); return Promise.all(waitOn);
} }
@ -1694,7 +1691,6 @@ class PDFPageProxy {
this.objs.clear(); this.objs.clear();
this._annotationPromises.clear(); this._annotationPromises.clear();
this._jsActionsPromise = null; this._jsActionsPromise = null;
this._structTreePromise = null;
if (resetStats && this._stats) { if (resetStats && this._stats) {
this._stats = new StatTimer(); this._stats = new StatTimer();
} }

View File

@ -144,18 +144,10 @@ class DefaultAnnotationEditorLayerFactory {
*/ */
class DefaultStructTreeLayerFactory { class DefaultStructTreeLayerFactory {
/** /**
* @typedef {Object} CreateStructTreeLayerBuilderParameters
* @property {PDFPageProxy} pdfPage
*/
/**
* @param {CreateStructTreeLayerBuilderParameters}
* @returns {StructTreeLayerBuilder} * @returns {StructTreeLayerBuilder}
*/ */
createStructTreeLayerBuilder({ pdfPage }) { createStructTreeLayerBuilder() {
return new StructTreeLayerBuilder({ return new StructTreeLayerBuilder();
pdfPage,
});
} }
} }

View File

@ -280,15 +280,9 @@ class IPDFXfaLayerFactory {
*/ */
class IPDFStructTreeLayerFactory { class IPDFStructTreeLayerFactory {
/** /**
* @typedef {Object} CreateStructTreeLayerBuilderParameters
* @property {PDFPageProxy} pdfPage
*/
/**
* @param {CreateStructTreeLayerBuilderParameters}
* @returns {StructTreeLayerBuilder} * @returns {StructTreeLayerBuilder}
*/ */
createStructTreeLayerBuilder({ pdfPage }) {} createStructTreeLayerBuilder() {}
} }
/** /**

View File

@ -318,6 +318,33 @@ class PDFPageView {
numTextDivs: textLayer.numTextDivs, numTextDivs: textLayer.numTextDivs,
error, error,
}); });
if (this.structTreeLayerFactory) {
this.#renderStructTreeLayer();
}
}
/**
* The structure tree is currently only supported when the text layer is
* enabled and a canvas is used for rendering.
*
* The structure tree must be generated after the text layer for the
* aria-owns to work.
*/
async #renderStructTreeLayer() {
if (!this.textLayer) {
return;
}
this.structTreeLayer ||=
this.structTreeLayerFactory.createStructTreeLayerBuilder();
const tree = await (!this.structTreeLayer.renderingDone
? this.pdfPage.getStructTree()
: null);
const treeDom = this.structTreeLayer?.render(tree);
if (treeDom) {
this.canvas?.append(treeDom);
}
} }
async _buildXfaTextContentItems(textDivs) { async _buildXfaTextContentItems(textDivs) {
@ -578,6 +605,9 @@ class PDFPageView {
this.textLayer.cancel(); this.textLayer.cancel();
this.textLayer = null; this.textLayer = null;
} }
if (this.structTreeLayer && !this.textLayer) {
this.structTreeLayer = null;
}
if ( if (
this.annotationLayer && this.annotationLayer &&
(!keepAnnotationLayer || !this.annotationLayer.div) (!keepAnnotationLayer || !this.annotationLayer.div)
@ -598,10 +628,6 @@ class PDFPageView {
this.xfaLayer = null; this.xfaLayer = null;
this.textHighlighter?.disable(); this.textHighlighter?.disable();
} }
if (this._onTextLayerRendered) {
this.eventBus._off("textlayerrendered", this._onTextLayerRendered);
this._onTextLayerRendered = null;
}
} }
cssTransform({ cssTransform({
@ -844,38 +870,6 @@ class PDFPageView {
this._renderXfaLayer(); this._renderXfaLayer();
} }
// The structure tree is currently only supported when the text layer is
// enabled and a canvas is used for rendering.
if (this.structTreeLayerFactory && this.textLayer && this.canvas) {
// The structure tree must be generated after the text layer for the
// aria-owns to work.
this._onTextLayerRendered = event => {
if (event.pageNumber !== this.id) {
return;
}
this.eventBus._off("textlayerrendered", this._onTextLayerRendered);
this._onTextLayerRendered = null;
if (!this.canvas) {
return; // The canvas was removed, prevent errors below.
}
this.pdfPage.getStructTree().then(tree => {
if (!tree) {
return;
}
if (!this.canvas) {
return; // The canvas was removed, prevent errors below.
}
const treeDom = this.structTreeLayer.render(tree);
treeDom.classList.add("structTree");
this.canvas.append(treeDom);
});
};
this.eventBus._on("textlayerrendered", this._onTextLayerRendered);
this.structTreeLayer =
this.structTreeLayerFactory.createStructTreeLayerBuilder({ pdfPage });
}
div.setAttribute("data-loaded", true); div.setAttribute("data-loaded", true);
this.eventBus.dispatch("pagerender", { this.eventBus.dispatch("pagerender", {

View File

@ -1794,18 +1794,10 @@ class PDFViewer {
} }
/** /**
* @typedef {Object} CreateStructTreeLayerBuilderParameters
* @property {PDFPageProxy} pdfPage
*/
/**
* @param {CreateStructTreeLayerBuilderParameters}
* @returns {StructTreeLayerBuilder} * @returns {StructTreeLayerBuilder}
*/ */
createStructTreeLayerBuilder({ pdfPage }) { createStructTreeLayerBuilder() {
return new StructTreeLayerBuilder({ return new StructTreeLayerBuilder();
pdfPage,
});
} }
/** /**

View File

@ -13,8 +13,6 @@
* limitations under the License. * limitations under the License.
*/ */
/** @typedef {import("../src/display/api").PDFPageProxy} PDFPageProxy */
const PDF_ROLE_TO_HTML_ROLE = { const PDF_ROLE_TO_HTML_ROLE = {
// Document level structure types // Document level structure types
Document: null, // There's a "document" role, but it doesn't make sense here. Document: null, // There's a "document" role, but it doesn't make sense here.
@ -73,24 +71,23 @@ const PDF_ROLE_TO_HTML_ROLE = {
const HEADING_PATTERN = /^H(\d+)$/; const HEADING_PATTERN = /^H(\d+)$/;
/**
* @typedef {Object} StructTreeLayerBuilderOptions
* @property {PDFPageProxy} pdfPage
*/
class StructTreeLayerBuilder { class StructTreeLayerBuilder {
/** #treeDom = undefined;
* @param {StructTreeLayerBuilderOptions} options
*/ get renderingDone() {
constructor({ pdfPage }) { return this.#treeDom !== undefined;
this.pdfPage = pdfPage;
} }
render(structTree) { render(structTree) {
return this._walk(structTree); if (this.#treeDom !== undefined) {
return this.#treeDom;
}
const treeDom = this.#walk(structTree);
treeDom?.classList.add("structTree");
return (this.#treeDom = treeDom);
} }
_setAttributes(structElement, htmlElement) { #setAttributes(structElement, htmlElement) {
if (structElement.alt !== undefined) { if (structElement.alt !== undefined) {
htmlElement.setAttribute("aria-label", structElement.alt); htmlElement.setAttribute("aria-label", structElement.alt);
} }
@ -102,7 +99,7 @@ class StructTreeLayerBuilder {
} }
} }
_walk(node) { #walk(node) {
if (!node) { if (!node) {
return null; return null;
} }
@ -119,16 +116,16 @@ class StructTreeLayerBuilder {
} }
} }
this._setAttributes(node, element); this.#setAttributes(node, element);
if (node.children) { if (node.children) {
if (node.children.length === 1 && "id" in node.children[0]) { if (node.children.length === 1 && "id" in node.children[0]) {
// Often there is only one content node so just set the values on the // Often there is only one content node so just set the values on the
// parent node to avoid creating an extra span. // parent node to avoid creating an extra span.
this._setAttributes(node.children[0], element); this.#setAttributes(node.children[0], element);
} else { } else {
for (const kid of node.children) { for (const kid of node.children) {
element.append(this._walk(kid)); element.append(this.#walk(kid));
} }
} }
} }