mirror of
https://github.com/siyuan-note/siyuan.git
synced 2026-03-14 00:16:13 +01:00
This commit is contained in:
parent
7ad5cdd067
commit
23f2af3501
57 changed files with 83865 additions and 2979 deletions
|
|
@ -15,23 +15,21 @@
|
|||
|
||||
// eslint-disable-next-line max-len
|
||||
/** @typedef {import("../src/display/display_utils").PageViewport} PageViewport */
|
||||
/** @typedef {import("./event_utils").EventBus} EventBus */
|
||||
/** @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 */
|
||||
|
||||
import { renderTextLayer } from "./pdfjs";
|
||||
import { getHighlight } from '../anno'
|
||||
import { renderTextLayer, updateTextLayer } from "./pdfjs";
|
||||
import {getHighlight} from "../anno";
|
||||
|
||||
/**
|
||||
* @typedef {Object} TextLayerBuilderOptions
|
||||
* @property {HTMLDivElement} textLayerDiv - The text layer container.
|
||||
* @property {EventBus} eventBus - The application event bus.
|
||||
* @property {number} pageIndex - The page index.
|
||||
* @property {PageViewport} viewport - The viewport of the text layer.
|
||||
* @property {TextHighlighter} highlighter - Optional object that will handle
|
||||
* highlighting text from the find controller.
|
||||
* @property {TextAccessibilityManager} [accessibilityManager]
|
||||
* @property {boolean} [isOffscreenCanvasSupported] - Allows to use an
|
||||
* OffscreenCanvas if needed.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -40,28 +38,29 @@ import { getHighlight } from '../anno'
|
|||
* contain text that matches the PDF text they are overlaying.
|
||||
*/
|
||||
class TextLayerBuilder {
|
||||
#rotation = 0;
|
||||
|
||||
#scale = 0;
|
||||
|
||||
#textContentSource = null;
|
||||
|
||||
constructor({
|
||||
textLayerDiv,
|
||||
eventBus,
|
||||
pageIndex,
|
||||
viewport,
|
||||
highlighter = null,
|
||||
accessibilityManager = null,
|
||||
isOffscreenCanvasSupported = true,
|
||||
}) {
|
||||
this.textLayerDiv = textLayerDiv;
|
||||
this.eventBus = eventBus;
|
||||
this.textContent = null;
|
||||
this.textContentItemsStr = [];
|
||||
this.textContentStream = null;
|
||||
this.renderingDone = false;
|
||||
this.pageNumber = pageIndex + 1;
|
||||
this.viewport = viewport;
|
||||
this.textDivs = [];
|
||||
this.textDivProperties = new WeakMap();
|
||||
this.textLayerRenderTask = null;
|
||||
this.highlighter = highlighter;
|
||||
this.accessibilityManager = accessibilityManager;
|
||||
this.isOffscreenCanvasSupported = isOffscreenCanvasSupported;
|
||||
|
||||
this.#bindMouse();
|
||||
this.div = document.createElement("div");
|
||||
this.div.className = "textLayer";
|
||||
this.hide();
|
||||
}
|
||||
|
||||
#finishRendering() {
|
||||
|
|
@ -69,54 +68,86 @@ class TextLayerBuilder {
|
|||
|
||||
const endOfContent = document.createElement("div");
|
||||
endOfContent.className = "endOfContent";
|
||||
this.textLayerDiv.append(endOfContent);
|
||||
this.div.append(endOfContent);
|
||||
|
||||
this.#bindMouse();
|
||||
|
||||
this.eventBus.dispatch("textlayerrendered", {
|
||||
source: this,
|
||||
pageNumber: this.pageNumber,
|
||||
numTextDivs: this.textDivs.length,
|
||||
});
|
||||
// NOTE
|
||||
getHighlight(this.textLayerDiv)
|
||||
getHighlight(this.div)
|
||||
}
|
||||
|
||||
get numTextDivs() {
|
||||
return this.textDivs.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the text layer.
|
||||
*
|
||||
* @param {number} [timeout] - Wait for a specified amount of milliseconds
|
||||
* before rendering.
|
||||
* @param {PageViewport} viewport
|
||||
*/
|
||||
render(timeout = 0) {
|
||||
if (!(this.textContent || this.textContentStream) || this.renderingDone) {
|
||||
async render(viewport) {
|
||||
if (!this.#textContentSource) {
|
||||
throw new Error('No "textContentSource" parameter specified.');
|
||||
}
|
||||
|
||||
const scale = viewport.scale * (globalThis.devicePixelRatio || 1);
|
||||
const { rotation } = viewport;
|
||||
if (this.renderingDone) {
|
||||
const mustRotate = rotation !== this.#rotation;
|
||||
const mustRescale = scale !== this.#scale;
|
||||
if (mustRotate || mustRescale) {
|
||||
this.hide();
|
||||
updateTextLayer({
|
||||
container: this.div,
|
||||
viewport,
|
||||
textDivs: this.textDivs,
|
||||
textDivProperties: this.textDivProperties,
|
||||
isOffscreenCanvasSupported: this.isOffscreenCanvasSupported,
|
||||
mustRescale,
|
||||
mustRotate,
|
||||
});
|
||||
this.#scale = scale;
|
||||
this.#rotation = rotation;
|
||||
}
|
||||
this.show();
|
||||
return;
|
||||
}
|
||||
this.cancel();
|
||||
|
||||
this.textDivs.length = 0;
|
||||
this.cancel();
|
||||
this.highlighter?.setTextMapping(this.textDivs, this.textContentItemsStr);
|
||||
this.accessibilityManager?.setTextMapping(this.textDivs);
|
||||
|
||||
const textLayerFrag = document.createDocumentFragment();
|
||||
this.textLayerRenderTask = renderTextLayer({
|
||||
textContent: this.textContent,
|
||||
textContentStream: this.textContentStream,
|
||||
container: textLayerFrag,
|
||||
viewport: this.viewport,
|
||||
textContentSource: this.#textContentSource,
|
||||
container: this.div,
|
||||
viewport,
|
||||
textDivs: this.textDivs,
|
||||
textDivProperties: this.textDivProperties,
|
||||
textContentItemsStr: this.textContentItemsStr,
|
||||
timeout,
|
||||
isOffscreenCanvasSupported: this.isOffscreenCanvasSupported,
|
||||
});
|
||||
this.textLayerRenderTask.promise.then(
|
||||
() => {
|
||||
this.textLayerDiv.append(textLayerFrag);
|
||||
this.#finishRendering();
|
||||
this.highlighter?.enable();
|
||||
this.accessibilityManager?.enable();
|
||||
},
|
||||
function (reason) {
|
||||
// Cancelled or failed to render text layer; skipping errors.
|
||||
}
|
||||
);
|
||||
|
||||
await this.textLayerRenderTask.promise;
|
||||
this.#finishRendering();
|
||||
this.#scale = scale;
|
||||
this.#rotation = rotation;
|
||||
this.show();
|
||||
this.accessibilityManager?.enable();
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (!this.div.hidden) {
|
||||
// We turn off the highlighter in order to avoid to scroll into view an
|
||||
// element of the text layer which could be hidden.
|
||||
this.highlighter?.disable();
|
||||
this.div.hidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
show() {
|
||||
if (this.div.hidden && this.renderingDone) {
|
||||
this.div.hidden = false;
|
||||
this.highlighter?.enable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -129,16 +160,17 @@ class TextLayerBuilder {
|
|||
}
|
||||
this.highlighter?.disable();
|
||||
this.accessibilityManager?.disable();
|
||||
this.textContentItemsStr.length = 0;
|
||||
this.textDivs.length = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -147,7 +179,7 @@ class TextLayerBuilder {
|
|||
* dragged up or down.
|
||||
*/
|
||||
#bindMouse() {
|
||||
const div = this.textLayerDiv;
|
||||
const { div } = this;
|
||||
|
||||
div.addEventListener("mousedown", evt => {
|
||||
const end = div.querySelector(".endOfContent");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue