diff --git a/src/display/text_layer.js b/src/display/text_layer.js index ec1ef6e65..d9fbefdd6 100644 --- a/src/display/text_layer.js +++ b/src/display/text_layer.js @@ -13,25 +13,28 @@ * limitations under the License. */ +/** @typedef {import("./display_utils").PageViewport} PageViewport */ +/** @typedef {import("./api").TextContent} TextContent */ + import { AbortException, createPromiseCapability, FeatureTest, Util, } from "../shared/util.js"; +import { deprecated } from "./display_utils.js"; /** * Text layer render parameters. * * @typedef {Object} TextLayerRenderParameters - * @property {import("./api").TextContent} [textContent] - Text content to - * render (the object is returned by the page's `getTextContent` method). - * @property {ReadableStream} [textContentStream] - Text content stream to - * render (the stream is returned by the page's `streamTextContent` method). + * @property {ReadableStream | TextContent} textContentSource - Text content to + * render, i.e. the value returned by the page's `streamTextContent` or + * `getTextContent` method. * @property {HTMLElement} container - The DOM node that will contain the text - * runs. - * @property {import("./display_utils").PageViewport} viewport - The target - * viewport to properly layout the text runs. + * runs. + * @property {PageViewport} viewport - The target viewport to properly layout + * the text runs. * @property {Array} [textDivs] - HTML elements that correspond to * the text items of the textContent input. * This is output and shall initially be set to an empty array. @@ -49,9 +52,9 @@ import { * * @typedef {Object} TextLayerUpdateParameters * @property {HTMLElement} container - The DOM node that will contain the text - * runs. - * @property {import("./display_utils").PageViewport} viewport - The target - * viewport to properly layout the text runs. + * runs. + * @property {PageViewport} viewport - The target viewport to properly layout + * the text runs. * @property {Array} [textDivs] - HTML elements that correspond to * the text items of the textContent input. * This is output and shall initially be set to an empty array. @@ -61,7 +64,7 @@ import { * OffscreenCanvas to measure string widths. * @property {boolean} [mustRotate] true if the text layer must be rotated. * @property {boolean} [mustRescale] true if the text layer contents must be - * rescaled. + * rescaled. */ const MAX_TEXT_DIVS_TO_RENDER = 100000; @@ -236,7 +239,7 @@ function appendText(task, geom, styles) { textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width; } task._textDivProperties.set(textDiv, textDivProperties); - if (task._textContentStream) { + if (task._isReadableStream) { task._layoutText(textDiv); } } @@ -286,7 +289,7 @@ function render(task) { return; } - if (!task._textContentStream) { + if (!task._isReadableStream) { for (const textDiv of textDivs) { task._layoutText(textDiv); } @@ -298,8 +301,7 @@ function render(task) { class TextLayerRenderTask { constructor({ - textContent, - textContentStream, + textContentSource, container, viewport, textDivs, @@ -307,8 +309,8 @@ class TextLayerRenderTask { textContentItemsStr, isOffscreenCanvasSupported, }) { - this._textContent = textContent; - this._textContentStream = textContentStream; + this._textContentSource = textContentSource; + this._isReadableStream = textContentSource instanceof ReadableStream; this._container = this._rootContainer = container; this._textDivs = textDivs || []; this._textContentItemsStr = textContentItemsStr || []; @@ -421,14 +423,7 @@ class TextLayerRenderTask { const capability = createPromiseCapability(); let styleCache = Object.create(null); - // The temporary canvas is used to measure text length in the DOM. - - if (this._textContent) { - const textItems = this._textContent.items; - const textStyles = this._textContent.styles; - this._processItems(textItems, textStyles); - capability.resolve(); - } else if (this._textContentStream) { + if (this._isReadableStream) { const pump = () => { this._reader.read().then(({ value, done }) => { if (done) { @@ -442,12 +437,14 @@ class TextLayerRenderTask { }, capability.reject); }; - this._reader = this._textContentStream.getReader(); + this._reader = this._textContentSource.getReader(); pump(); + } else if (this._textContentSource) { + const { items, styles } = this._textContentSource; + this._processItems(items, styles); + capability.resolve(); } else { - throw new Error( - 'Neither "textContent" nor "textContentStream" parameters specified.' - ); + throw new Error('No "textContentSource" parameter specified.'); } capability.promise.then(() => { @@ -458,27 +455,29 @@ class TextLayerRenderTask { } /** - * @param {TextLayerRenderParameters} renderParameters + * @param {TextLayerRenderParameters} params * @returns {TextLayerRenderTask} */ -function renderTextLayer(renderParameters) { - const task = new TextLayerRenderTask({ - textContent: renderParameters.textContent, - textContentStream: renderParameters.textContentStream, - container: renderParameters.container, - viewport: renderParameters.viewport, - textDivs: renderParameters.textDivs, - textContentItemsStr: renderParameters.textContentItemsStr, - textDivProperties: renderParameters.textDivProperties, - isOffscreenCanvasSupported: renderParameters.isOffscreenCanvasSupported, - }); +function renderTextLayer(params) { + if ( + (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) && + !params.textContentSource && + (params.textContent || params.textContentStream) + ) { + deprecated( + "The TextLayerRender `textContent`/`textContentStream` parameters " + + "will be removed in the future, please use `textContentSource` instead." + ); + params.textContentSource = params.textContent || params.textContentStream; + } + const task = new TextLayerRenderTask(params); task._render(); return task; } /** - * @param {TextLayerUpdateParameters} renderParameters - * @returns {TextLayerRenderTask} + * @param {TextLayerUpdateParameters} params + * @returns {undefined} */ function updateTextLayer({ container, diff --git a/test/driver.js b/test/driver.js index 53b9b0eb6..fd11d3281 100644 --- a/test/driver.js +++ b/test/driver.js @@ -263,7 +263,7 @@ class Rasterize { // Rendering text layer as HTML. const task = renderTextLayer({ - textContent, + textContentSource: textContent, container: div, viewport, }); diff --git a/test/unit/text_layer_spec.js b/test/unit/text_layer_spec.js index 37c9b8722..0600dfe22 100644 --- a/test/unit/text_layer_spec.js +++ b/test/unit/text_layer_spec.js @@ -33,7 +33,7 @@ describe("textLayer", function () { const textContentItemsStr = []; const textLayerRenderTask = renderTextLayer({ - textContentStream: page.streamTextContent(), + textContentSource: page.streamTextContent(), container: document.createElement("div"), viewport: page.getViewport(), textContentItemsStr, diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index 3f98b2ea0..22b5f6e62 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -301,7 +301,7 @@ class PDFPageView { const readableStream = pdfPage.streamTextContent({ includeMarkedContent: true, }); - textLayer.setTextContentStream(readableStream); + textLayer.setTextContentSource(readableStream); } await textLayer.render(viewport); } catch (ex) { diff --git a/web/text_layer_builder.js b/web/text_layer_builder.js index 73a8cbde2..0f088ed62 100644 --- a/web/text_layer_builder.js +++ b/web/text_layer_builder.js @@ -15,6 +15,7 @@ // eslint-disable-next-line max-len /** @typedef {import("../src/display/display_utils").PageViewport} PageViewport */ +/** @typedef {import("../src/display/api").TextContent} TextContent */ /** @typedef {import("./text_highlighter").TextHighlighter} TextHighlighter */ // eslint-disable-next-line max-len /** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */ @@ -36,18 +37,18 @@ import { renderTextLayer, updateTextLayer } from "pdfjs-lib"; * contain text that matches the PDF text they are overlaying. */ class TextLayerBuilder { + #rotation = 0; + #scale = 0; - #rotation = 0; + #textContentSource = null; constructor({ highlighter = null, accessibilityManager = null, isOffscreenCanvasSupported = true, }) { - this.textContent = null; this.textContentItemsStr = []; - this.textContentStream = null; this.renderingDone = false; this.textDivs = []; this.textDivProperties = new WeakMap(); @@ -76,12 +77,11 @@ class TextLayerBuilder { /** * Renders the text layer. + * @param {PageViewport} viewport */ async render(viewport) { - if (!(this.textContent || this.textContentStream)) { - throw new Error( - `Neither "textContent" nor "textContentStream" specified.` - ); + if (!this.#textContentSource) { + throw new Error('No "textContentSource" parameter specified.'); } const scale = viewport.scale * (globalThis.devicePixelRatio || 1); @@ -112,8 +112,7 @@ class TextLayerBuilder { this.accessibilityManager?.setTextMapping(this.textDivs); this.textLayerRenderTask = renderTextLayer({ - textContent: this.textContent, - textContentStream: this.textContentStream, + textContentSource: this.#textContentSource, container: this.div, viewport, textDivs: this.textDivs, @@ -156,14 +155,12 @@ class TextLayerBuilder { this.textDivProperties = new WeakMap(); } - setTextContentStream(readableStream) { + /** + * @param {ReadableStream | TextContent} source + */ + setTextContentSource(source) { this.cancel(); - this.textContentStream = readableStream; - } - - setTextContent(textContent) { - this.cancel(); - this.textContent = textContent; + this.#textContentSource = source; } /**