[api-minor] Combine the textContent
/textContentStream
parameters
Rather than handling these parameters separately, which is a left-over from back when streaming of textContent was originally added, we can simply pass either data directly to the `TextLayer` and let it handle things accordingly. Also, improves a few JSDoc comments and `typedef`-imports.
This commit is contained in:
parent
67e1c37e0f
commit
fe8fded23b
@ -13,25 +13,28 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/** @typedef {import("./display_utils").PageViewport} PageViewport */
|
||||||
|
/** @typedef {import("./api").TextContent} TextContent */
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AbortException,
|
AbortException,
|
||||||
createPromiseCapability,
|
createPromiseCapability,
|
||||||
FeatureTest,
|
FeatureTest,
|
||||||
Util,
|
Util,
|
||||||
} from "../shared/util.js";
|
} from "../shared/util.js";
|
||||||
|
import { deprecated } from "./display_utils.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text layer render parameters.
|
* Text layer render parameters.
|
||||||
*
|
*
|
||||||
* @typedef {Object} TextLayerRenderParameters
|
* @typedef {Object} TextLayerRenderParameters
|
||||||
* @property {import("./api").TextContent} [textContent] - Text content to
|
* @property {ReadableStream | TextContent} textContentSource - Text content to
|
||||||
* render (the object is returned by the page's `getTextContent` method).
|
* render, i.e. the value returned by the page's `streamTextContent` or
|
||||||
* @property {ReadableStream} [textContentStream] - Text content stream to
|
* `getTextContent` method.
|
||||||
* render (the stream is returned by the page's `streamTextContent` method).
|
|
||||||
* @property {HTMLElement} container - The DOM node that will contain the text
|
* @property {HTMLElement} container - The DOM node that will contain the text
|
||||||
* runs.
|
* runs.
|
||||||
* @property {import("./display_utils").PageViewport} viewport - The target
|
* @property {PageViewport} viewport - The target viewport to properly layout
|
||||||
* viewport to properly layout the text runs.
|
* the text runs.
|
||||||
* @property {Array<HTMLElement>} [textDivs] - HTML elements that correspond to
|
* @property {Array<HTMLElement>} [textDivs] - HTML elements that correspond to
|
||||||
* the text items of the textContent input.
|
* the text items of the textContent input.
|
||||||
* This is output and shall initially be set to an empty array.
|
* This is output and shall initially be set to an empty array.
|
||||||
@ -49,9 +52,9 @@ import {
|
|||||||
*
|
*
|
||||||
* @typedef {Object} TextLayerUpdateParameters
|
* @typedef {Object} TextLayerUpdateParameters
|
||||||
* @property {HTMLElement} container - The DOM node that will contain the text
|
* @property {HTMLElement} container - The DOM node that will contain the text
|
||||||
* runs.
|
* runs.
|
||||||
* @property {import("./display_utils").PageViewport} viewport - The target
|
* @property {PageViewport} viewport - The target viewport to properly layout
|
||||||
* viewport to properly layout the text runs.
|
* the text runs.
|
||||||
* @property {Array<HTMLElement>} [textDivs] - HTML elements that correspond to
|
* @property {Array<HTMLElement>} [textDivs] - HTML elements that correspond to
|
||||||
* the text items of the textContent input.
|
* the text items of the textContent input.
|
||||||
* This is output and shall initially be set to an empty array.
|
* This is output and shall initially be set to an empty array.
|
||||||
@ -61,7 +64,7 @@ import {
|
|||||||
* OffscreenCanvas to measure string widths.
|
* OffscreenCanvas to measure string widths.
|
||||||
* @property {boolean} [mustRotate] true if the text layer must be rotated.
|
* @property {boolean} [mustRotate] true if the text layer must be rotated.
|
||||||
* @property {boolean} [mustRescale] true if the text layer contents must be
|
* @property {boolean} [mustRescale] true if the text layer contents must be
|
||||||
* rescaled.
|
* rescaled.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const MAX_TEXT_DIVS_TO_RENDER = 100000;
|
const MAX_TEXT_DIVS_TO_RENDER = 100000;
|
||||||
@ -236,7 +239,7 @@ function appendText(task, geom, styles) {
|
|||||||
textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width;
|
textDivProperties.canvasWidth = style.vertical ? geom.height : geom.width;
|
||||||
}
|
}
|
||||||
task._textDivProperties.set(textDiv, textDivProperties);
|
task._textDivProperties.set(textDiv, textDivProperties);
|
||||||
if (task._textContentStream) {
|
if (task._isReadableStream) {
|
||||||
task._layoutText(textDiv);
|
task._layoutText(textDiv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,7 +289,7 @@ function render(task) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!task._textContentStream) {
|
if (!task._isReadableStream) {
|
||||||
for (const textDiv of textDivs) {
|
for (const textDiv of textDivs) {
|
||||||
task._layoutText(textDiv);
|
task._layoutText(textDiv);
|
||||||
}
|
}
|
||||||
@ -298,8 +301,7 @@ function render(task) {
|
|||||||
|
|
||||||
class TextLayerRenderTask {
|
class TextLayerRenderTask {
|
||||||
constructor({
|
constructor({
|
||||||
textContent,
|
textContentSource,
|
||||||
textContentStream,
|
|
||||||
container,
|
container,
|
||||||
viewport,
|
viewport,
|
||||||
textDivs,
|
textDivs,
|
||||||
@ -307,8 +309,8 @@ class TextLayerRenderTask {
|
|||||||
textContentItemsStr,
|
textContentItemsStr,
|
||||||
isOffscreenCanvasSupported,
|
isOffscreenCanvasSupported,
|
||||||
}) {
|
}) {
|
||||||
this._textContent = textContent;
|
this._textContentSource = textContentSource;
|
||||||
this._textContentStream = textContentStream;
|
this._isReadableStream = textContentSource instanceof ReadableStream;
|
||||||
this._container = this._rootContainer = container;
|
this._container = this._rootContainer = container;
|
||||||
this._textDivs = textDivs || [];
|
this._textDivs = textDivs || [];
|
||||||
this._textContentItemsStr = textContentItemsStr || [];
|
this._textContentItemsStr = textContentItemsStr || [];
|
||||||
@ -421,14 +423,7 @@ class TextLayerRenderTask {
|
|||||||
const capability = createPromiseCapability();
|
const capability = createPromiseCapability();
|
||||||
let styleCache = Object.create(null);
|
let styleCache = Object.create(null);
|
||||||
|
|
||||||
// The temporary canvas is used to measure text length in the DOM.
|
if (this._isReadableStream) {
|
||||||
|
|
||||||
if (this._textContent) {
|
|
||||||
const textItems = this._textContent.items;
|
|
||||||
const textStyles = this._textContent.styles;
|
|
||||||
this._processItems(textItems, textStyles);
|
|
||||||
capability.resolve();
|
|
||||||
} else if (this._textContentStream) {
|
|
||||||
const pump = () => {
|
const pump = () => {
|
||||||
this._reader.read().then(({ value, done }) => {
|
this._reader.read().then(({ value, done }) => {
|
||||||
if (done) {
|
if (done) {
|
||||||
@ -442,12 +437,14 @@ class TextLayerRenderTask {
|
|||||||
}, capability.reject);
|
}, capability.reject);
|
||||||
};
|
};
|
||||||
|
|
||||||
this._reader = this._textContentStream.getReader();
|
this._reader = this._textContentSource.getReader();
|
||||||
pump();
|
pump();
|
||||||
|
} else if (this._textContentSource) {
|
||||||
|
const { items, styles } = this._textContentSource;
|
||||||
|
this._processItems(items, styles);
|
||||||
|
capability.resolve();
|
||||||
} else {
|
} else {
|
||||||
throw new Error(
|
throw new Error('No "textContentSource" parameter specified.');
|
||||||
'Neither "textContent" nor "textContentStream" parameters specified.'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
capability.promise.then(() => {
|
capability.promise.then(() => {
|
||||||
@ -458,27 +455,29 @@ class TextLayerRenderTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {TextLayerRenderParameters} renderParameters
|
* @param {TextLayerRenderParameters} params
|
||||||
* @returns {TextLayerRenderTask}
|
* @returns {TextLayerRenderTask}
|
||||||
*/
|
*/
|
||||||
function renderTextLayer(renderParameters) {
|
function renderTextLayer(params) {
|
||||||
const task = new TextLayerRenderTask({
|
if (
|
||||||
textContent: renderParameters.textContent,
|
(typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) &&
|
||||||
textContentStream: renderParameters.textContentStream,
|
!params.textContentSource &&
|
||||||
container: renderParameters.container,
|
(params.textContent || params.textContentStream)
|
||||||
viewport: renderParameters.viewport,
|
) {
|
||||||
textDivs: renderParameters.textDivs,
|
deprecated(
|
||||||
textContentItemsStr: renderParameters.textContentItemsStr,
|
"The TextLayerRender `textContent`/`textContentStream` parameters " +
|
||||||
textDivProperties: renderParameters.textDivProperties,
|
"will be removed in the future, please use `textContentSource` instead."
|
||||||
isOffscreenCanvasSupported: renderParameters.isOffscreenCanvasSupported,
|
);
|
||||||
});
|
params.textContentSource = params.textContent || params.textContentStream;
|
||||||
|
}
|
||||||
|
const task = new TextLayerRenderTask(params);
|
||||||
task._render();
|
task._render();
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {TextLayerUpdateParameters} renderParameters
|
* @param {TextLayerUpdateParameters} params
|
||||||
* @returns {TextLayerRenderTask}
|
* @returns {undefined}
|
||||||
*/
|
*/
|
||||||
function updateTextLayer({
|
function updateTextLayer({
|
||||||
container,
|
container,
|
||||||
|
@ -263,7 +263,7 @@ class Rasterize {
|
|||||||
|
|
||||||
// Rendering text layer as HTML.
|
// Rendering text layer as HTML.
|
||||||
const task = renderTextLayer({
|
const task = renderTextLayer({
|
||||||
textContent,
|
textContentSource: textContent,
|
||||||
container: div,
|
container: div,
|
||||||
viewport,
|
viewport,
|
||||||
});
|
});
|
||||||
|
@ -33,7 +33,7 @@ describe("textLayer", function () {
|
|||||||
const textContentItemsStr = [];
|
const textContentItemsStr = [];
|
||||||
|
|
||||||
const textLayerRenderTask = renderTextLayer({
|
const textLayerRenderTask = renderTextLayer({
|
||||||
textContentStream: page.streamTextContent(),
|
textContentSource: page.streamTextContent(),
|
||||||
container: document.createElement("div"),
|
container: document.createElement("div"),
|
||||||
viewport: page.getViewport(),
|
viewport: page.getViewport(),
|
||||||
textContentItemsStr,
|
textContentItemsStr,
|
||||||
|
@ -301,7 +301,7 @@ class PDFPageView {
|
|||||||
const readableStream = pdfPage.streamTextContent({
|
const readableStream = pdfPage.streamTextContent({
|
||||||
includeMarkedContent: true,
|
includeMarkedContent: true,
|
||||||
});
|
});
|
||||||
textLayer.setTextContentStream(readableStream);
|
textLayer.setTextContentSource(readableStream);
|
||||||
}
|
}
|
||||||
await textLayer.render(viewport);
|
await textLayer.render(viewport);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
|
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
/** @typedef {import("../src/display/display_utils").PageViewport} PageViewport */
|
/** @typedef {import("../src/display/display_utils").PageViewport} PageViewport */
|
||||||
|
/** @typedef {import("../src/display/api").TextContent} TextContent */
|
||||||
/** @typedef {import("./text_highlighter").TextHighlighter} TextHighlighter */
|
/** @typedef {import("./text_highlighter").TextHighlighter} TextHighlighter */
|
||||||
// eslint-disable-next-line max-len
|
// eslint-disable-next-line max-len
|
||||||
/** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
|
/** @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.
|
* contain text that matches the PDF text they are overlaying.
|
||||||
*/
|
*/
|
||||||
class TextLayerBuilder {
|
class TextLayerBuilder {
|
||||||
|
#rotation = 0;
|
||||||
|
|
||||||
#scale = 0;
|
#scale = 0;
|
||||||
|
|
||||||
#rotation = 0;
|
#textContentSource = null;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
highlighter = null,
|
highlighter = null,
|
||||||
accessibilityManager = null,
|
accessibilityManager = null,
|
||||||
isOffscreenCanvasSupported = true,
|
isOffscreenCanvasSupported = true,
|
||||||
}) {
|
}) {
|
||||||
this.textContent = null;
|
|
||||||
this.textContentItemsStr = [];
|
this.textContentItemsStr = [];
|
||||||
this.textContentStream = null;
|
|
||||||
this.renderingDone = false;
|
this.renderingDone = false;
|
||||||
this.textDivs = [];
|
this.textDivs = [];
|
||||||
this.textDivProperties = new WeakMap();
|
this.textDivProperties = new WeakMap();
|
||||||
@ -76,12 +77,11 @@ class TextLayerBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the text layer.
|
* Renders the text layer.
|
||||||
|
* @param {PageViewport} viewport
|
||||||
*/
|
*/
|
||||||
async render(viewport) {
|
async render(viewport) {
|
||||||
if (!(this.textContent || this.textContentStream)) {
|
if (!this.#textContentSource) {
|
||||||
throw new Error(
|
throw new Error('No "textContentSource" parameter specified.');
|
||||||
`Neither "textContent" nor "textContentStream" specified.`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const scale = viewport.scale * (globalThis.devicePixelRatio || 1);
|
const scale = viewport.scale * (globalThis.devicePixelRatio || 1);
|
||||||
@ -112,8 +112,7 @@ class TextLayerBuilder {
|
|||||||
this.accessibilityManager?.setTextMapping(this.textDivs);
|
this.accessibilityManager?.setTextMapping(this.textDivs);
|
||||||
|
|
||||||
this.textLayerRenderTask = renderTextLayer({
|
this.textLayerRenderTask = renderTextLayer({
|
||||||
textContent: this.textContent,
|
textContentSource: this.#textContentSource,
|
||||||
textContentStream: this.textContentStream,
|
|
||||||
container: this.div,
|
container: this.div,
|
||||||
viewport,
|
viewport,
|
||||||
textDivs: this.textDivs,
|
textDivs: this.textDivs,
|
||||||
@ -156,14 +155,12 @@ class TextLayerBuilder {
|
|||||||
this.textDivProperties = new WeakMap();
|
this.textDivProperties = new WeakMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
setTextContentStream(readableStream) {
|
/**
|
||||||
|
* @param {ReadableStream | TextContent} source
|
||||||
|
*/
|
||||||
|
setTextContentSource(source) {
|
||||||
this.cancel();
|
this.cancel();
|
||||||
this.textContentStream = readableStream;
|
this.#textContentSource = source;
|
||||||
}
|
|
||||||
|
|
||||||
setTextContent(textContent) {
|
|
||||||
this.cancel();
|
|
||||||
this.textContent = textContent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user