pdf.js/web/annotation_layer_builder.js
Jonas Jenwald ee5862bd81 Prevent the annotationLayer from, in some cases, becoming duplicated on the first page when the document loads
I don't know if this is a regression, but I noticed earlier today that depending on the initial scale *and* sidebar state, the `annotationLayer` of the first rendered page may end up duplicated; please see screen-shot below.

[screen-shot]

I can reproduce this reliable with e.g. https://arxiv.org/pdf/1112.0542v1.pdf#zoom=page-width&pagemode=bookmarks.
When the document loads, rendering of the first page begins immediately. When the sidebar is then opened, that forces re-rendering which thus aborts rendering of the first page.
Note that calling `PDFPageView.draw()` will always, provided an `AnnotationLayerFactory` instance exists, call `AnnotationLayerBuilder.render()`. Hence the events described above will result in *two* such calls, where the actual annotation rendering/updating happens asynchronously.

For reasons that I don't (at all) understand, when multiple `pdfPage.getAnnotations()` promises are handled back-to-back (in `AnnotationLayerBuilder.render()`), the `this.div` property seems to not update in time for the subsequent calls.
This thus, at least in Firefox, result in double rendering of all annotations on the first page.

Obviously it'd be good to find out why it breaks, since it *really* shouldn't, but this patch at least provides a (hopefully) acceptable work-around by ignoring `getAnnotations()` calls for `AnnotationLayerBuilder` instances that we're destroying (in `PDFPageView.reset()`).
2017-10-07 11:34:53 +02:00

127 lines
3.5 KiB
JavaScript

/* Copyright 2014 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 { AnnotationLayer } from 'pdfjs-lib';
import { NullL10n } from './ui_utils';
import { SimpleLinkService } from './pdf_link_service';
/**
* @typedef {Object} AnnotationLayerBuilderOptions
* @property {HTMLDivElement} pageDiv
* @property {PDFPage} pdfPage
* @property {boolean} renderInteractiveForms
* @property {IPDFLinkService} linkService
* @property {DownloadManager} downloadManager
* @property {IL10n} l10n - Localization service.
*/
class AnnotationLayerBuilder {
/**
* @param {AnnotationLayerBuilderOptions} options
*/
constructor({ pageDiv, pdfPage, linkService, downloadManager,
renderInteractiveForms = false, l10n = NullL10n, }) {
this.pageDiv = pageDiv;
this.pdfPage = pdfPage;
this.linkService = linkService;
this.downloadManager = downloadManager;
this.renderInteractiveForms = renderInteractiveForms;
this.l10n = l10n;
this.div = null;
this._cancelled = false;
}
/**
* @param {PageViewport} viewport
* @param {string} intent (default value is 'display')
*/
render(viewport, intent = 'display') {
this.pdfPage.getAnnotations({ intent, }).then((annotations) => {
if (this._cancelled) {
return;
}
let parameters = {
viewport: viewport.clone({ dontFlip: true, }),
div: this.div,
annotations,
page: this.pdfPage,
renderInteractiveForms: this.renderInteractiveForms,
linkService: this.linkService,
downloadManager: this.downloadManager,
};
if (this.div) {
// If an annotationLayer already exists, refresh its children's
// transformation matrices.
AnnotationLayer.update(parameters);
} else {
// Create an annotation layer div and render the annotations
// if there is at least one annotation.
if (annotations.length === 0) {
return;
}
this.div = document.createElement('div');
this.div.className = 'annotationLayer';
this.pageDiv.appendChild(this.div);
parameters.div = this.div;
AnnotationLayer.render(parameters);
this.l10n.translate(this.div);
}
});
}
cancel() {
this._cancelled = true;
}
hide() {
if (!this.div) {
return;
}
this.div.setAttribute('hidden', 'true');
}
}
/**
* @implements IPDFAnnotationLayerFactory
*/
class DefaultAnnotationLayerFactory {
/**
* @param {HTMLDivElement} pageDiv
* @param {PDFPage} pdfPage
* @param {boolean} renderInteractiveForms
* @param {IL10n} l10n
* @returns {AnnotationLayerBuilder}
*/
createAnnotationLayerBuilder(pageDiv, pdfPage, renderInteractiveForms = false,
l10n = NullL10n) {
return new AnnotationLayerBuilder({
pageDiv,
pdfPage,
renderInteractiveForms,
linkService: new SimpleLinkService(),
l10n,
});
}
}
export {
AnnotationLayerBuilder,
DefaultAnnotationLayerFactory,
};