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.
*/
getStructTree() {
return (this._structTreePromise ||= this._transport.getStructTree(
this._pageIndex
));
return this._transport.getStructTree(this._pageIndex);
}
/**
@ -1659,7 +1657,6 @@ class PDFPageProxy {
this._bitmaps.clear();
this._annotationPromises.clear();
this._jsActionsPromise = null;
this._structTreePromise = null;
this.pendingCleanup = false;
return Promise.all(waitOn);
}
@ -1694,7 +1691,6 @@ class PDFPageProxy {
this.objs.clear();
this._annotationPromises.clear();
this._jsActionsPromise = null;
this._structTreePromise = null;
if (resetStats && this._stats) {
this._stats = new StatTimer();
}

View File

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

View File

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

View File

@ -318,6 +318,33 @@ class PDFPageView {
numTextDivs: textLayer.numTextDivs,
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) {
@ -578,6 +605,9 @@ class PDFPageView {
this.textLayer.cancel();
this.textLayer = null;
}
if (this.structTreeLayer && !this.textLayer) {
this.structTreeLayer = null;
}
if (
this.annotationLayer &&
(!keepAnnotationLayer || !this.annotationLayer.div)
@ -598,10 +628,6 @@ class PDFPageView {
this.xfaLayer = null;
this.textHighlighter?.disable();
}
if (this._onTextLayerRendered) {
this.eventBus._off("textlayerrendered", this._onTextLayerRendered);
this._onTextLayerRendered = null;
}
}
cssTransform({
@ -844,38 +870,6 @@ class PDFPageView {
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);
this.eventBus.dispatch("pagerender", {

View File

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

View File

@ -13,8 +13,6 @@
* limitations under the License.
*/
/** @typedef {import("../src/display/api").PDFPageProxy} PDFPageProxy */
const PDF_ROLE_TO_HTML_ROLE = {
// Document level structure types
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+)$/;
/**
* @typedef {Object} StructTreeLayerBuilderOptions
* @property {PDFPageProxy} pdfPage
*/
class StructTreeLayerBuilder {
/**
* @param {StructTreeLayerBuilderOptions} options
*/
constructor({ pdfPage }) {
this.pdfPage = pdfPage;
#treeDom = undefined;
get renderingDone() {
return this.#treeDom !== undefined;
}
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) {
htmlElement.setAttribute("aria-label", structElement.alt);
}
@ -102,7 +99,7 @@ class StructTreeLayerBuilder {
}
}
_walk(node) {
#walk(node) {
if (!node) {
return null;
}
@ -119,16 +116,16 @@ class StructTreeLayerBuilder {
}
}
this._setAttributes(node, element);
this.#setAttributes(node, element);
if (node.children) {
if (node.children.length === 1 && "id" in node.children[0]) {
// Often there is only one content node so just set the values on the
// parent node to avoid creating an extra span.
this._setAttributes(node.children[0], element);
this.#setAttributes(node.children[0], element);
} else {
for (const kid of node.children) {
element.append(this._walk(kid));
element.append(this.#walk(kid));
}
}
}