@laminapdf/viewer 0.2.0-alpha.2 → 0.2.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -7
- package/dist/client.d.ts +51 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +94 -30
- package/dist/client.js.map +1 -1
- package/dist/find/controller.d.ts +73 -0
- package/dist/find/controller.d.ts.map +1 -0
- package/dist/find/controller.js +227 -0
- package/dist/find/controller.js.map +1 -0
- package/dist/find/fold.d.ts +23 -0
- package/dist/find/fold.d.ts.map +1 -0
- package/dist/find/fold.js +99 -0
- package/dist/find/fold.js.map +1 -0
- package/dist/find/page.d.ts +44 -0
- package/dist/find/page.d.ts.map +1 -0
- package/dist/find/page.js +173 -0
- package/dist/find/page.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/layers/box.d.ts +10 -0
- package/dist/layers/box.d.ts.map +1 -0
- package/dist/layers/box.js +19 -0
- package/dist/layers/box.js.map +1 -0
- package/dist/layers/highlight-layer.d.ts +31 -0
- package/dist/layers/highlight-layer.d.ts.map +1 -0
- package/dist/layers/highlight-layer.js +95 -0
- package/dist/layers/highlight-layer.js.map +1 -0
- package/dist/layers/spans.d.ts +42 -0
- package/dist/layers/spans.d.ts.map +1 -0
- package/dist/layers/spans.js +121 -0
- package/dist/layers/spans.js.map +1 -0
- package/dist/layers/structure-layer.d.ts +15 -0
- package/dist/layers/structure-layer.d.ts.map +1 -0
- package/dist/layers/structure-layer.js +152 -0
- package/dist/layers/structure-layer.js.map +1 -0
- package/dist/layers/text-layer.d.ts +53 -0
- package/dist/layers/text-layer.d.ts.map +1 -0
- package/dist/layers/text-layer.js +292 -0
- package/dist/layers/text-layer.js.map +1 -0
- package/dist/layout/range.d.ts +3 -0
- package/dist/layout/range.d.ts.map +1 -0
- package/dist/layout/range.js +5 -0
- package/dist/layout/range.js.map +1 -0
- package/dist/network/watch.d.ts +30 -0
- package/dist/network/watch.d.ts.map +1 -0
- package/dist/network/watch.js +181 -0
- package/dist/network/watch.js.map +1 -0
- package/dist/protocol.d.ts +127 -2
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +2 -2
- package/dist/protocol.js.map +1 -1
- package/dist/render/tiles.d.ts +43 -3
- package/dist/render/tiles.d.ts.map +1 -1
- package/dist/render/tiles.js +62 -10
- package/dist/render/tiles.js.map +1 -1
- package/dist/source/http-range.d.ts +59 -0
- package/dist/source/http-range.d.ts.map +1 -0
- package/dist/source/http-range.js +284 -0
- package/dist/source/http-range.js.map +1 -0
- package/dist/thumbnails/strip.d.ts +37 -0
- package/dist/thumbnails/strip.d.ts.map +1 -0
- package/dist/thumbnails/strip.js +313 -0
- package/dist/thumbnails/strip.js.map +1 -0
- package/dist/view/budgets.d.ts +29 -0
- package/dist/view/budgets.d.ts.map +1 -0
- package/dist/view/budgets.js +63 -0
- package/dist/view/budgets.js.map +1 -0
- package/dist/view/touch.d.ts +71 -0
- package/dist/view/touch.d.ts.map +1 -0
- package/dist/view/touch.js +220 -0
- package/dist/view/touch.js.map +1 -0
- package/dist/view/viewer.d.ts +42 -1
- package/dist/view/viewer.d.ts.map +1 -1
- package/dist/view/viewer.js +433 -69
- package/dist/view/viewer.js.map +1 -1
- package/dist/worker/document.d.ts +33 -1
- package/dist/worker/document.d.ts.map +1 -1
- package/dist/worker/document.js +181 -24
- package/dist/worker/document.js.map +1 -1
- package/dist/worker/entry.d.ts +1 -1
- package/dist/worker/entry.d.ts.map +1 -1
- package/dist/worker/entry.js +3 -1
- package/dist/worker/entry.js.map +1 -1
- package/dist/worker/find.d.ts +7 -0
- package/dist/worker/find.d.ts.map +1 -0
- package/dist/worker/find.js +59 -0
- package/dist/worker/find.js.map +1 -0
- package/dist/worker/methods.d.ts.map +1 -1
- package/dist/worker/methods.js +31 -4
- package/dist/worker/methods.js.map +1 -1
- package/dist/worker/navigation.d.ts +6 -0
- package/dist/worker/navigation.d.ts.map +1 -0
- package/dist/worker/navigation.js +16 -0
- package/dist/worker/navigation.js.map +1 -0
- package/dist/worker/network.d.ts +4 -0
- package/dist/worker/network.d.ts.map +1 -0
- package/dist/worker/network.js +24 -0
- package/dist/worker/network.js.map +1 -0
- package/dist/worker/render.d.ts +5 -2
- package/dist/worker/render.d.ts.map +1 -1
- package/dist/worker/render.js +14 -7
- package/dist/worker/render.js.map +1 -1
- package/dist/worker/text.d.ts +17 -0
- package/dist/worker/text.d.ts.map +1 -0
- package/dist/worker/text.js +75 -0
- package/dist/worker/text.js.map +1 -0
- package/dist/zoom/destination.d.ts +18 -0
- package/dist/zoom/destination.d.ts.map +1 -0
- package/dist/zoom/destination.js +56 -0
- package/dist/zoom/destination.js.map +1 -0
- package/package.json +6 -5
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// canvas pool, the bitmap cache, the tile and zoom maths) are not exported: the README describes
|
|
4
4
|
// them, and their unit tests import them by path within the package.
|
|
5
5
|
export { ViewerClient } from "./client.js";
|
|
6
|
+
export { watchNetwork } from "./network/watch.js";
|
|
7
|
+
export { ThumbnailStrip } from "./thumbnails/strip.js";
|
|
6
8
|
export { createViewer, Viewer } from "./view/viewer.js";
|
|
7
9
|
export { MAX_ZOOM, MIN_ZOOM } from "./zoom/zoom.js";
|
|
8
10
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,iGAAiG;AACjG,iGAAiG;AACjG,qEAAqE;AAIrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,iGAAiG;AACjG,iGAAiG;AACjG,qEAAqE;AAIrE,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAM3C,OAAO,EAAoB,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAyBpE,OAAO,EAAE,cAAc,EAA8B,MAAM,uBAAuB,CAAC;AASnF,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Rotation } from "../layout/page-layout.js";
|
|
2
|
+
/** Sizes `box` in points and places it over a page `size` CSS px at `rotation`. */
|
|
3
|
+
export declare function placeBox(box: HTMLElement, points: {
|
|
4
|
+
readonly width: number;
|
|
5
|
+
readonly height: number;
|
|
6
|
+
}, size: {
|
|
7
|
+
readonly width: number;
|
|
8
|
+
readonly height: number;
|
|
9
|
+
}, rotation: Rotation): void;
|
|
10
|
+
//# sourceMappingURL=box.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"box.d.ts","sourceRoot":"","sources":["../../src/layers/box.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAczD,mFAAmF;AACnF,wBAAgB,QAAQ,CACtB,GAAG,EAAE,WAAW,EAChB,MAAM,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAC3D,IAAI,EAAE;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACzD,QAAQ,EAAE,QAAQ,GACjB,IAAI,CAMN"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// A page layer's box: a div sized in page points, scaled and turned by one CSS transform to the
|
|
2
|
+
// page's current size, so what a layer places in points stays over the rendered page at every
|
|
3
|
+
// zoom and rotation. The text and highlight layers share it.
|
|
4
|
+
/** The page's box: its size in points, scaled and turned to `size` (CSS px) at `rotation`. */
|
|
5
|
+
function boxTransform(points, size, rotation) {
|
|
6
|
+
const turned = rotation === 90 || rotation === 270;
|
|
7
|
+
const scale = size.width / (turned ? points.height : points.width);
|
|
8
|
+
const [x, y] = { 0: [0, 0], 90: [size.width, 0], 180: [size.width, size.height], 270: [0, size.height] }[rotation];
|
|
9
|
+
return `translate(${x}px, ${y}px) rotate(${rotation}deg) scale(${scale})`;
|
|
10
|
+
}
|
|
11
|
+
/** Sizes `box` in points and places it over a page `size` CSS px at `rotation`. */
|
|
12
|
+
export function placeBox(box, points, size, rotation) {
|
|
13
|
+
Object.assign(box.style, {
|
|
14
|
+
width: `${points.width}px`,
|
|
15
|
+
height: `${points.height}px`,
|
|
16
|
+
transform: boxTransform(points, size, rotation),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=box.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"box.js","sourceRoot":"","sources":["../../src/layers/box.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,8FAA8F;AAC9F,6DAA6D;AAI7D,8FAA8F;AAC9F,SAAS,YAAY,CACnB,MAA2D,EAC3D,IAAyD,EACzD,QAAkB;IAElB,MAAM,MAAM,GAAG,QAAQ,KAAK,EAAE,IAAI,QAAQ,KAAK,GAAG,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACnE,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC;IACnH,OAAO,aAAa,CAAC,OAAO,CAAC,cAAc,QAAQ,cAAc,KAAK,GAAG,CAAC;AAC5E,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,QAAQ,CACtB,GAAgB,EAChB,MAA2D,EAC3D,IAAyD,EACzD,QAAkB;IAElB,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE;QACvB,KAAK,EAAE,GAAG,MAAM,CAAC,KAAK,IAAI;QAC1B,MAAM,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI;QAC5B,SAAS,EAAE,YAAY,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,CAAC;KAChD,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Rotation } from "../layout/page-layout.js";
|
|
2
|
+
import type { PageLayer } from "../view/viewer.js";
|
|
3
|
+
/** What the highlight layer reads of the find controller and the viewer. */
|
|
4
|
+
export interface HighlightHost {
|
|
5
|
+
/** Page `page`'s matches (quads, 8 numbers per run) and which one is current, if any. */
|
|
6
|
+
matchesOn(page: number): {
|
|
7
|
+
readonly matches: readonly (readonly number[])[];
|
|
8
|
+
readonly current: number | undefined;
|
|
9
|
+
};
|
|
10
|
+
/** Page `page`'s displayed size in points. */
|
|
11
|
+
points(page: number): {
|
|
12
|
+
readonly width: number;
|
|
13
|
+
readonly height: number;
|
|
14
|
+
} | undefined;
|
|
15
|
+
rotation(): Rotation;
|
|
16
|
+
}
|
|
17
|
+
/** The find highlights of one viewer. */
|
|
18
|
+
export declare class HighlightLayer implements PageLayer {
|
|
19
|
+
#private;
|
|
20
|
+
readonly name = "highlight";
|
|
21
|
+
constructor(host: HighlightHost);
|
|
22
|
+
onMount(page: number, slot: HTMLElement): void;
|
|
23
|
+
onUnmount(page: number): void;
|
|
24
|
+
onZoom(page: number, _slot: HTMLElement, size: {
|
|
25
|
+
readonly width: number;
|
|
26
|
+
readonly height: number;
|
|
27
|
+
}): void;
|
|
28
|
+
/** Draws the pages again: matches arrived or went, the current one moved, or the view turned. */
|
|
29
|
+
refresh(pages?: Iterable<number>): void;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=highlight-layer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"highlight-layer.d.ts","sourceRoot":"","sources":["../../src/layers/highlight-layer.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAGnD,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B,yFAAyF;IACzF,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC;IACpH,8CAA8C;IAC9C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IACtF,QAAQ,IAAI,QAAQ,CAAC;CACtB;AAiBD,yCAAyC;AACzC,qBAAa,cAAe,YAAW,SAAS;;IAC9C,QAAQ,CAAC,IAAI,eAAe;IAI5B,YAAY,IAAI,EAAE,aAAa,EAE9B;IAED,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAa7C;IAED,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAG5B;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAKxG;IAED,iGAAiG;IACjG,OAAO,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAOtC;CAmCF"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// The find highlights: on every mounted page, a box over each match, in the page's
|
|
2
|
+
// `lamina-layer-highlight` slot. Like the text layer, the boxes sit in page points inside one box
|
|
3
|
+
// that a CSS transform scales and turns to the page, so a zoom or a rotation moves none of them.
|
|
4
|
+
// Each match quad is drawn as its bounding box. The current match gets its own class. The viewer
|
|
5
|
+
// adds no style sheet: colours come from `--lamina-find-match` and `--lamina-find-current`, and
|
|
6
|
+
// the current match also gets an outline (`--lamina-find-current-outline`), so it never differs
|
|
7
|
+
// by colour alone. The boxes take no pointer events, so they never stand in a selection's way.
|
|
8
|
+
import { placeBox } from "./box.js";
|
|
9
|
+
/** The axis-aligned box around a quad's four corners. */
|
|
10
|
+
function bounds(q, at) {
|
|
11
|
+
const xs = [q[at], q[at + 2], q[at + 4], q[at + 6]].map((v) => v ?? 0);
|
|
12
|
+
const ys = [q[at + 1], q[at + 3], q[at + 5], q[at + 7]].map((v) => v ?? 0);
|
|
13
|
+
const x = Math.min(...xs);
|
|
14
|
+
const y = Math.min(...ys);
|
|
15
|
+
return { x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y };
|
|
16
|
+
}
|
|
17
|
+
/** The find highlights of one viewer. */
|
|
18
|
+
export class HighlightLayer {
|
|
19
|
+
name = "highlight";
|
|
20
|
+
#host;
|
|
21
|
+
#pages = new Map();
|
|
22
|
+
constructor(host) {
|
|
23
|
+
this.#host = host;
|
|
24
|
+
}
|
|
25
|
+
onMount(page, slot) {
|
|
26
|
+
const box = document.createElement("div");
|
|
27
|
+
box.className = "lamina-highlight-layer";
|
|
28
|
+
Object.assign(box.style, {
|
|
29
|
+
position: "absolute",
|
|
30
|
+
left: "0",
|
|
31
|
+
top: "0",
|
|
32
|
+
transformOrigin: "0 0",
|
|
33
|
+
pointerEvents: "none",
|
|
34
|
+
});
|
|
35
|
+
slot.append(box);
|
|
36
|
+
this.#pages.set(page, { box, size: undefined });
|
|
37
|
+
this.#draw(page);
|
|
38
|
+
}
|
|
39
|
+
onUnmount(page) {
|
|
40
|
+
this.#pages.get(page)?.box.remove();
|
|
41
|
+
this.#pages.delete(page);
|
|
42
|
+
}
|
|
43
|
+
onZoom(page, _slot, size) {
|
|
44
|
+
const view = this.#pages.get(page);
|
|
45
|
+
if (view === undefined)
|
|
46
|
+
return;
|
|
47
|
+
view.size = size;
|
|
48
|
+
this.#place(page, view);
|
|
49
|
+
}
|
|
50
|
+
/** Draws the pages again: matches arrived or went, the current one moved, or the view turned. */
|
|
51
|
+
refresh(pages) {
|
|
52
|
+
for (const page of pages ?? this.#pages.keys()) {
|
|
53
|
+
const view = this.#pages.get(page);
|
|
54
|
+
if (view === undefined)
|
|
55
|
+
continue;
|
|
56
|
+
this.#place(page, view);
|
|
57
|
+
this.#draw(page);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
#place(page, view) {
|
|
61
|
+
const points = this.#host.points(page);
|
|
62
|
+
if (points !== undefined && view.size !== undefined)
|
|
63
|
+
placeBox(view.box, points, view.size, this.#host.rotation());
|
|
64
|
+
}
|
|
65
|
+
#draw(page) {
|
|
66
|
+
const view = this.#pages.get(page);
|
|
67
|
+
if (view === undefined)
|
|
68
|
+
return;
|
|
69
|
+
const { matches, current } = this.#host.matchesOn(page);
|
|
70
|
+
const marks = [];
|
|
71
|
+
matches.forEach((quads, index) => {
|
|
72
|
+
for (let at = 0; at + 8 <= quads.length; at += 8) {
|
|
73
|
+
const r = bounds(quads, at);
|
|
74
|
+
const mark = document.createElement("div");
|
|
75
|
+
const isCurrent = index === current;
|
|
76
|
+
mark.className = isCurrent ? "lamina-find-match lamina-find-current" : "lamina-find-match";
|
|
77
|
+
Object.assign(mark.style, {
|
|
78
|
+
position: "absolute",
|
|
79
|
+
left: `${r.x}px`,
|
|
80
|
+
top: `${r.y}px`,
|
|
81
|
+
width: `${r.width}px`,
|
|
82
|
+
height: `${r.height}px`,
|
|
83
|
+
background: isCurrent
|
|
84
|
+
? "var(--lamina-find-current, rgba(237, 191, 106, 0.6))"
|
|
85
|
+
: "var(--lamina-find-match, rgba(246, 227, 188, 0.6))",
|
|
86
|
+
mixBlendMode: "multiply",
|
|
87
|
+
outline: isCurrent ? "2px solid var(--lamina-find-current-outline, #8a5300)" : "",
|
|
88
|
+
});
|
|
89
|
+
marks.push(mark);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
view.box.replaceChildren(...marks);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=highlight-layer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"highlight-layer.js","sourceRoot":"","sources":["../../src/layers/highlight-layer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { TextRunData } from "../protocol.js";
|
|
2
|
+
/** Where one run's span goes, in page space (points). */
|
|
3
|
+
export interface SpanSpec {
|
|
4
|
+
/** The run's index in the page's runs. */
|
|
5
|
+
readonly run: number;
|
|
6
|
+
/** Its text (document text). */
|
|
7
|
+
readonly text: string;
|
|
8
|
+
/** The span's top-left corner, before its rotation. */
|
|
9
|
+
readonly left: number;
|
|
10
|
+
readonly top: number;
|
|
11
|
+
/** Its extent along the baseline and across it. */
|
|
12
|
+
readonly width: number;
|
|
13
|
+
readonly height: number;
|
|
14
|
+
/** The baseline's angle, clockwise from the x axis, in radians. */
|
|
15
|
+
readonly angle: number;
|
|
16
|
+
/** The run reads right to left. */
|
|
17
|
+
readonly rtl: boolean;
|
|
18
|
+
/** The marked-content ID, for the structure tree. */
|
|
19
|
+
readonly mcid?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Each run's span, in run order (the page's reading order). Runs without glyphs or text have none.
|
|
23
|
+
* The baseline's direction comes from the first glyph's quad (its bottom edge), or for vertical
|
|
24
|
+
* writing from the first glyph's centre to the last's; the span's box covers every glyph's quad
|
|
25
|
+
* in that frame.
|
|
26
|
+
*/
|
|
27
|
+
export declare function layoutSpans(runs: readonly TextRunData[], quads: Float32Array): SpanSpec[];
|
|
28
|
+
/** A selected piece of a span's text, with where its span is, for joining. */
|
|
29
|
+
export interface Piece {
|
|
30
|
+
readonly page: number;
|
|
31
|
+
readonly text: string;
|
|
32
|
+
readonly span: SpanSpec;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The copied text of `pieces`, in page and reading order: a new line between pages and where the
|
|
36
|
+
* next span starts a new line (its baseline moved by more than half a line), a space where it
|
|
37
|
+
* starts past a gap on the same line. Soft hyphens, which mark where a line may break, go.
|
|
38
|
+
*/
|
|
39
|
+
export declare function joinPieces(pieces: readonly Piece[]): string;
|
|
40
|
+
/** What goes between two pieces as copy writes them: a new line, a space or nothing. */
|
|
41
|
+
export declare function separator(a: Piece, b: Piece): string;
|
|
42
|
+
//# sourceMappingURL=spans.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spans.d.ts","sourceRoot":"","sources":["../../src/layers/spans.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,yDAAyD;AACzD,MAAM,WAAW,QAAQ;IACvB,0CAA0C;IAC1C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,mCAAmC;IACnC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,qDAAqD;IACrD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAsCD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,EAAE,KAAK,EAAE,YAAY,GAAG,QAAQ,EAAE,CAKzF;AA8CD,8EAA8E;AAC9E,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;CACzB;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,CAS3D;AAED,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,GAAG,MAAM,CAgBpD"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// The text layer's geometry, pure: one span per text run, placed from its glyphs' quads (Lamina
|
|
2
|
+
// PDF's, never the renderer's: ADR 0004's two-parsers rule) in page space, points from the
|
|
3
|
+
// displayed page's top-left, y down. The span's own frame follows the run's baseline, so a rotated
|
|
4
|
+
// or vertical run gets a rotated span, and a right-to-left run reads right to left. Also how the
|
|
5
|
+
// pieces of a selection join into the copied text.
|
|
6
|
+
const dot = (a, b) => a.x * b.x + a.y * b.y;
|
|
7
|
+
/** A unit vector along `v`, or `fallback` for a zero vector. */
|
|
8
|
+
function unit(v, fallback) {
|
|
9
|
+
const length = Math.hypot(v.x, v.y);
|
|
10
|
+
return length < 1e-6 ? fallback : { x: v.x / length, y: v.y / length };
|
|
11
|
+
}
|
|
12
|
+
/** Glyph `g`'s corners: bottom-left, bottom-right, top-right, top-left. */
|
|
13
|
+
function corners(quads, g) {
|
|
14
|
+
const at = (k) => ({ x: quads[g * 8 + k * 2] ?? 0, y: quads[g * 8 + k * 2 + 1] ?? 0 });
|
|
15
|
+
return [at(0), at(1), at(2), at(3)];
|
|
16
|
+
}
|
|
17
|
+
function centre(c) {
|
|
18
|
+
return { x: c.reduce((s, p) => s + p.x, 0) / c.length, y: c.reduce((s, p) => s + p.y, 0) / c.length };
|
|
19
|
+
}
|
|
20
|
+
/** Letters of right-to-left scripts (Hebrew, Arabic, Syriac, Thaana, N'Ko and their presentation forms). */
|
|
21
|
+
const RTL = /[\u0590-\u08ff\ufb1d-\ufdff\ufe70-\ufefc]/;
|
|
22
|
+
/**
|
|
23
|
+
* Whether `text` reads right to left: its first letter is of a right-to-left script (UAX #9's
|
|
24
|
+
* rule P2 for a paragraph's direction). A run's glyphs come in drawing order and its text in
|
|
25
|
+
* logical order, so only the text can say.
|
|
26
|
+
*/
|
|
27
|
+
function readsRightToLeft(text) {
|
|
28
|
+
const first = /\p{L}/u.exec(text)?.[0];
|
|
29
|
+
return first !== undefined && RTL.test(first);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Each run's span, in run order (the page's reading order). Runs without glyphs or text have none.
|
|
33
|
+
* The baseline's direction comes from the first glyph's quad (its bottom edge), or for vertical
|
|
34
|
+
* writing from the first glyph's centre to the last's; the span's box covers every glyph's quad
|
|
35
|
+
* in that frame.
|
|
36
|
+
*/
|
|
37
|
+
export function layoutSpans(runs, quads) {
|
|
38
|
+
return runs.flatMap((run, index) => {
|
|
39
|
+
const span = spanOf(run, index, quads);
|
|
40
|
+
return span === undefined ? [] : [span];
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** Run `run`'s span, or undefined for a run without glyphs or text. */
|
|
44
|
+
function spanOf(run, index, quads) {
|
|
45
|
+
const [start, end] = run.glyphRange;
|
|
46
|
+
if (end <= start || run.text === "")
|
|
47
|
+
return undefined;
|
|
48
|
+
const first = corners(quads, start);
|
|
49
|
+
const last = corners(quads, end - 1);
|
|
50
|
+
const along = run.vertical
|
|
51
|
+
? unit({ x: centre(last).x - centre(first).x, y: centre(last).y - centre(first).y }, { x: 0, y: 1 })
|
|
52
|
+
: unit({ x: first[1].x - first[0].x, y: first[1].y - first[0].y }, { x: 1, y: 0 });
|
|
53
|
+
// Across the baseline, upwards: the baseline turned a quarter anticlockwise (y points down).
|
|
54
|
+
const up = { x: along.y, y: -along.x };
|
|
55
|
+
const { minA, maxA, minB, maxB } = extent(quads, start, end, along, up);
|
|
56
|
+
return {
|
|
57
|
+
run: index,
|
|
58
|
+
text: run.text,
|
|
59
|
+
left: along.x * minA + up.x * maxB,
|
|
60
|
+
top: along.y * minA + up.y * maxB,
|
|
61
|
+
width: maxA - minA,
|
|
62
|
+
height: maxB - minB,
|
|
63
|
+
angle: Math.atan2(along.y, along.x),
|
|
64
|
+
rtl: !run.vertical && readsRightToLeft(run.text),
|
|
65
|
+
...(run.mcid === undefined ? {} : { mcid: run.mcid }),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** How far glyphs `start..end` reach along the baseline (`A`) and across it (`B`). */
|
|
69
|
+
function extent(quads, start, end, along, up) {
|
|
70
|
+
let [minA, maxA, minB, maxB] = [Infinity, -Infinity, Infinity, -Infinity];
|
|
71
|
+
for (let g = start; g < end; g++) {
|
|
72
|
+
for (const p of corners(quads, g)) {
|
|
73
|
+
const a = dot(p, along);
|
|
74
|
+
const b = dot(p, up);
|
|
75
|
+
minA = Math.min(minA, a);
|
|
76
|
+
maxA = Math.max(maxA, a);
|
|
77
|
+
minB = Math.min(minB, b);
|
|
78
|
+
maxB = Math.max(maxB, b);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return { minA, maxA, minB, maxB };
|
|
82
|
+
}
|
|
83
|
+
/** U+00AD, which marks where a word may break: written as a code, since it is invisible. */
|
|
84
|
+
const SOFT_HYPHEN = String.fromCharCode(0xad);
|
|
85
|
+
/**
|
|
86
|
+
* The copied text of `pieces`, in page and reading order: a new line between pages and where the
|
|
87
|
+
* next span starts a new line (its baseline moved by more than half a line), a space where it
|
|
88
|
+
* starts past a gap on the same line. Soft hyphens, which mark where a line may break, go.
|
|
89
|
+
*/
|
|
90
|
+
export function joinPieces(pieces) {
|
|
91
|
+
let out = "";
|
|
92
|
+
let before;
|
|
93
|
+
for (const piece of pieces) {
|
|
94
|
+
if (before !== undefined)
|
|
95
|
+
out += separator(before, piece);
|
|
96
|
+
out += piece.text;
|
|
97
|
+
before = piece;
|
|
98
|
+
}
|
|
99
|
+
return out.replaceAll(SOFT_HYPHEN, "");
|
|
100
|
+
}
|
|
101
|
+
/** What goes between two pieces as copy writes them: a new line, a space or nothing. */
|
|
102
|
+
export function separator(a, b) {
|
|
103
|
+
if (a.page !== b.page)
|
|
104
|
+
return "\n";
|
|
105
|
+
const along = { x: Math.cos(a.span.angle), y: Math.sin(a.span.angle) };
|
|
106
|
+
const up = { x: along.y, y: -along.x };
|
|
107
|
+
// Bottoms, not tops: a drop cap or a superscript on the same line has another top, while its
|
|
108
|
+
// bottom stays within half a line of the line's.
|
|
109
|
+
const lineA = dot({ x: a.span.left, y: a.span.top }, up) - a.span.height;
|
|
110
|
+
const lineB = dot({ x: b.span.left, y: b.span.top }, up) - b.span.height;
|
|
111
|
+
if (Math.abs(lineA - lineB) > Math.max(a.span.height, b.span.height) / 2)
|
|
112
|
+
return "\n";
|
|
113
|
+
const startA = dot({ x: a.span.left, y: a.span.top }, along);
|
|
114
|
+
const startB = dot({ x: b.span.left, y: b.span.top }, along);
|
|
115
|
+
// The space between the two boxes along the baseline, whichever comes first: right to left, or a
|
|
116
|
+
// comma between two words of another direction, the next run sits before the last.
|
|
117
|
+
const gap = Math.max(startB - (startA + a.span.width), startA - (startB + b.span.width));
|
|
118
|
+
const joined = /\s$/.test(a.text) || /^\s/.test(b.text);
|
|
119
|
+
return !joined && gap > a.span.height * 0.15 ? " " : "";
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=spans.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spans.js","sourceRoot":"","sources":["../../src/layers/spans.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { StructureData } from "../protocol.js";
|
|
2
|
+
/** One viewer's structure tree. */
|
|
3
|
+
export declare class StructureLayer {
|
|
4
|
+
#private;
|
|
5
|
+
constructor(root: HTMLElement, untaggedLabel: string, idPrefix: string);
|
|
6
|
+
/** Builds the tree from the worker's answer, or the one-time note for an untagged document. */
|
|
7
|
+
show(structure: StructureData): void;
|
|
8
|
+
/** Span `id` drew marked content `mcid` of `page`: its element owns it. */
|
|
9
|
+
link(page: number, mcid: number, id: string): void;
|
|
10
|
+
/** Page `page` unmounted: its spans leave the elements that owned them. */
|
|
11
|
+
unlink(page: number): void;
|
|
12
|
+
/** Forgets the tree (a new document). */
|
|
13
|
+
reset(): void;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=structure-layer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure-layer.d.ts","sourceRoot":"","sources":["../../src/layers/structure-layer.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAiCpD,mCAAmC;AACnC,qBAAa,cAAc;;IASzB,YAAY,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAIrE;IAED,+FAA+F;IAC/F,IAAI,CAAC,SAAS,EAAE,aAAa,GAAG,IAAI,CAgBnC;IAED,2EAA2E;IAC3E,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAMjD;IAUD,2EAA2E;IAC3E,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAYzB;IAED,yCAAyC;IACzC,KAAK,IAAI,IAAI,CAIZ;CA2BF"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// The structure layer (web-app §6 C5): a tagged document's structure tree as a hidden but
|
|
2
|
+
// accessible tree of elements with ARIA roles (headings, paragraphs, lists, tables, figures with
|
|
3
|
+
// their alternate text) whose leaves own the text layer's spans through `aria-owns`, matched by
|
|
4
|
+
// page and MCID. Assistive technology then reads the document in structure order with its
|
|
5
|
+
// semantics, whichever backend renders it (the pdf.js pattern). The spans join their elements as
|
|
6
|
+
// their pages mount and leave as they unmount. A document without structure says so once.
|
|
7
|
+
/** An ARIA role (and level) per standard structure type; types not here are plain groups. */
|
|
8
|
+
const ROLES = {
|
|
9
|
+
P: "paragraph",
|
|
10
|
+
H: "heading",
|
|
11
|
+
Title: "heading",
|
|
12
|
+
L: "list",
|
|
13
|
+
LI: "listitem",
|
|
14
|
+
Table: "table",
|
|
15
|
+
THead: "rowgroup",
|
|
16
|
+
TBody: "rowgroup",
|
|
17
|
+
TFoot: "rowgroup",
|
|
18
|
+
TR: "row",
|
|
19
|
+
TH: "columnheader",
|
|
20
|
+
TD: "cell",
|
|
21
|
+
Formula: "math",
|
|
22
|
+
BlockQuote: "blockquote",
|
|
23
|
+
TOC: "list",
|
|
24
|
+
TOCI: "listitem",
|
|
25
|
+
Note: "note",
|
|
26
|
+
};
|
|
27
|
+
/** Hidden from sight, present for assistive technology (as sr-only CSS does). */
|
|
28
|
+
const HIDDEN = {
|
|
29
|
+
position: "absolute",
|
|
30
|
+
width: "1px",
|
|
31
|
+
height: "1px",
|
|
32
|
+
overflow: "hidden",
|
|
33
|
+
clipPath: "inset(50%)",
|
|
34
|
+
whiteSpace: "nowrap",
|
|
35
|
+
};
|
|
36
|
+
/** One viewer's structure tree. */
|
|
37
|
+
export class StructureLayer {
|
|
38
|
+
#root;
|
|
39
|
+
#untagged;
|
|
40
|
+
#tree;
|
|
41
|
+
/** The elements that own each page's marked content, by `page:mcid`. */
|
|
42
|
+
#owners = new Map();
|
|
43
|
+
#idPrefix;
|
|
44
|
+
constructor(root, untaggedLabel, idPrefix) {
|
|
45
|
+
this.#root = root;
|
|
46
|
+
this.#untagged = untaggedLabel;
|
|
47
|
+
this.#idPrefix = idPrefix;
|
|
48
|
+
}
|
|
49
|
+
/** Builds the tree from the worker's answer, or the one-time note for an untagged document. */
|
|
50
|
+
show(structure) {
|
|
51
|
+
this.reset();
|
|
52
|
+
const tree = document.createElement("div");
|
|
53
|
+
tree.className = "lamina-structure";
|
|
54
|
+
Object.assign(tree.style, HIDDEN);
|
|
55
|
+
const untagged = structure.tree === undefined || structure.tree.length === 0;
|
|
56
|
+
if (untagged)
|
|
57
|
+
tree.setAttribute("role", "status");
|
|
58
|
+
else
|
|
59
|
+
for (const node of structure.tree)
|
|
60
|
+
tree.append(this.#element(node, undefined));
|
|
61
|
+
this.#root.prepend(tree);
|
|
62
|
+
this.#tree = tree;
|
|
63
|
+
// A live region speaks what changes in it after it is in the page, not what it was made with.
|
|
64
|
+
if (untagged) {
|
|
65
|
+
requestAnimationFrame(() => {
|
|
66
|
+
if (this.#tree === tree)
|
|
67
|
+
tree.textContent = this.#untagged;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/** Span `id` drew marked content `mcid` of `page`: its element owns it. */
|
|
72
|
+
link(page, mcid, id) {
|
|
73
|
+
for (const owner of this.#owners.get(`${page}:${mcid}`) ?? []) {
|
|
74
|
+
const owned = (owner.getAttribute("aria-owns") ?? "").split(" ").filter(Boolean);
|
|
75
|
+
// In page and reading order, whatever order the pages mounted in.
|
|
76
|
+
if (!owned.includes(id))
|
|
77
|
+
owner.setAttribute("aria-owns", [...owned, id].sort(this.#byPlace).join(" "));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Orders span ids (`<prefix><page>-<index>`) by page, then by index. */
|
|
81
|
+
#byPlace = (a, b) => {
|
|
82
|
+
const place = (id) => id.slice(this.#idPrefix.length).split("-").map(Number);
|
|
83
|
+
const [pa = 0, ia = 0] = place(a);
|
|
84
|
+
const [pb = 0, ib = 0] = place(b);
|
|
85
|
+
return pa - pb || ia - ib;
|
|
86
|
+
};
|
|
87
|
+
/** Page `page` unmounted: its spans leave the elements that owned them. */
|
|
88
|
+
unlink(page) {
|
|
89
|
+
const prefix = `${this.#idPrefix}${page}-`;
|
|
90
|
+
for (const [key, owners] of this.#owners) {
|
|
91
|
+
if (!key.startsWith(`${page}:`))
|
|
92
|
+
continue;
|
|
93
|
+
for (const owner of owners) {
|
|
94
|
+
const kept = (owner.getAttribute("aria-owns") ?? "")
|
|
95
|
+
.split(" ")
|
|
96
|
+
.filter((id) => id !== "" && !id.startsWith(prefix));
|
|
97
|
+
if (kept.length === 0)
|
|
98
|
+
owner.removeAttribute("aria-owns");
|
|
99
|
+
else
|
|
100
|
+
owner.setAttribute("aria-owns", kept.join(" "));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/** Forgets the tree (a new document). */
|
|
105
|
+
reset() {
|
|
106
|
+
this.#tree?.remove();
|
|
107
|
+
this.#tree = undefined;
|
|
108
|
+
this.#owners = new Map();
|
|
109
|
+
}
|
|
110
|
+
/** `node` as an element; `parent` is its parent's structure type (a caption needs a table or figure). */
|
|
111
|
+
#element(node, parent) {
|
|
112
|
+
const el = document.createElement("div");
|
|
113
|
+
const heading = /^H([1-6])$/.exec(node.kind);
|
|
114
|
+
const alt = node.alt !== undefined && node.alt !== "" ? node.alt : undefined;
|
|
115
|
+
const role = heading !== null ? "heading" : roleOf(node.kind, parent, alt);
|
|
116
|
+
if (role !== undefined)
|
|
117
|
+
el.setAttribute("role", role);
|
|
118
|
+
if (heading !== null)
|
|
119
|
+
el.setAttribute("aria-level", heading[1]);
|
|
120
|
+
if (node.kind === "Title")
|
|
121
|
+
el.setAttribute("aria-level", "1");
|
|
122
|
+
if (alt !== undefined)
|
|
123
|
+
el.setAttribute("aria-label", alt);
|
|
124
|
+
if (node.lang !== undefined && node.lang !== "")
|
|
125
|
+
el.lang = node.lang;
|
|
126
|
+
for (const kid of node.kids)
|
|
127
|
+
this.#kid(el, kid, node.kind);
|
|
128
|
+
return el;
|
|
129
|
+
}
|
|
130
|
+
#kid(parent, kid, kind) {
|
|
131
|
+
if (kid.type === "node") {
|
|
132
|
+
parent.append(this.#element(kid, kind));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const key = `${kid.page}:${kid.mcid}`;
|
|
136
|
+
const owners = this.#owners.get(key) ?? [];
|
|
137
|
+
owners.push(parent);
|
|
138
|
+
this.#owners.set(key, owners);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The role of a structure type that ROLES doesn't settle alone: a figure is an image only with
|
|
143
|
+
* alternate text to name it (else a group), and a caption only inside a table or a figure.
|
|
144
|
+
*/
|
|
145
|
+
function roleOf(kind, parent, alt) {
|
|
146
|
+
if (kind === "Figure")
|
|
147
|
+
return alt === undefined ? "group" : "img";
|
|
148
|
+
if (kind === "Caption")
|
|
149
|
+
return parent === "Table" || parent === "Figure" ? "caption" : undefined;
|
|
150
|
+
return ROLES[kind];
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=structure-layer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure-layer.js","sourceRoot":"","sources":["../../src/layers/structure-layer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ViewerClient } from "../client.js";
|
|
2
|
+
import type { Rotation } from "../layout/page-layout.js";
|
|
3
|
+
import type { OpenedDocument } from "../protocol.js";
|
|
4
|
+
import type { PageLayer } from "../view/viewer.js";
|
|
5
|
+
/** What the text layer needs of its viewer. */
|
|
6
|
+
export interface TextLayerHost {
|
|
7
|
+
/** The viewer's root: the selection root, and where the structure tree lives. */
|
|
8
|
+
readonly root: HTMLElement;
|
|
9
|
+
readonly client: Pick<ViewerClient, "pageText" | "structure">;
|
|
10
|
+
/** The view's rotation now. */
|
|
11
|
+
rotation(): Rotation;
|
|
12
|
+
/** Keeps these pages mounted (the ones the selection touches). */
|
|
13
|
+
keepMounted(pages: Iterable<number>): void;
|
|
14
|
+
}
|
|
15
|
+
/** A selection in the text layer, for the app's selection surfaces (UI/UX plan §5.3). */
|
|
16
|
+
export interface TextSelection {
|
|
17
|
+
/** The selected text, as copy writes it. */
|
|
18
|
+
readonly text: string;
|
|
19
|
+
/** Its client rects, to place a toolbar or a menu near. */
|
|
20
|
+
readonly rects: readonly DOMRect[];
|
|
21
|
+
/** The pages it touches, in order. */
|
|
22
|
+
readonly pages: readonly number[];
|
|
23
|
+
/** The pointer that made it: `mouse`, `pen`, `touch`, or `""` for the keyboard. */
|
|
24
|
+
readonly pointerType: string;
|
|
25
|
+
}
|
|
26
|
+
/** The text layer's options (`createViewer`'s `text`). */
|
|
27
|
+
export interface TextLayerOptions {
|
|
28
|
+
/** Outlines each span, to check its fit over the rendered glyphs (a debug view). */
|
|
29
|
+
readonly outline?: boolean;
|
|
30
|
+
/** What a screen reader hears once for a document without structure (default "Untagged document"). */
|
|
31
|
+
readonly untaggedLabel?: string;
|
|
32
|
+
/** Called as the selection changes; `undefined` when it leaves the text layer. */
|
|
33
|
+
readonly onSelection?: (selection: TextSelection | undefined) => void;
|
|
34
|
+
}
|
|
35
|
+
/** The text layer of one viewer. */
|
|
36
|
+
export declare class TextLayer implements PageLayer {
|
|
37
|
+
#private;
|
|
38
|
+
readonly name = "text";
|
|
39
|
+
constructor(host: TextLayerHost, options?: TextLayerOptions);
|
|
40
|
+
/** A new document: its page sizes, and its structure tree, read once. */
|
|
41
|
+
open(document: OpenedDocument): void;
|
|
42
|
+
onMount(page: number, slot: HTMLElement): void;
|
|
43
|
+
onUnmount(page: number): void;
|
|
44
|
+
onZoom(page: number, _slot: HTMLElement, size: {
|
|
45
|
+
readonly width: number;
|
|
46
|
+
readonly height: number;
|
|
47
|
+
}): void;
|
|
48
|
+
/** Places every page's box again: the view turned. */
|
|
49
|
+
refresh(): void;
|
|
50
|
+
/** Stops listening; the viewer calls it on destroy. */
|
|
51
|
+
destroy(): void;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=text-layer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-layer.d.ts","sourceRoot":"","sources":["../../src/layers/text-layer.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAKnD,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,UAAU,GAAG,WAAW,CAAC,CAAC;IAC9D,+BAA+B;IAC/B,QAAQ,IAAI,QAAQ,CAAC;IACrB,kEAAkE;IAClE,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;CAC5C;AAED,yFAAyF;AACzF,MAAM,WAAW,aAAa;IAC5B,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,SAAS,OAAO,EAAE,CAAC;IACnC,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,mFAAmF;IACnF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,0DAA0D;AAC1D,MAAM,WAAW,gBAAgB;IAC/B,oFAAoF;IACpF,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,sGAAsG;IACtG,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,kFAAkF;IAClF,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,aAAa,GAAG,SAAS,KAAK,IAAI,CAAC;CACvE;AAYD,oCAAoC;AACpC,qBAAa,SAAU,YAAW,SAAS;;IACzC,QAAQ,CAAC,IAAI,UAAU;IAkBvB,YAAY,IAAI,EAAE,aAAa,EAAE,OAAO,GAAE,gBAAqB,EAa9D;IAED,yEAAyE;IACzE,IAAI,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAgBnC;IAED,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAyB7C;IAED,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAQ5B;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAGxG;IAED,sDAAsD;IACtD,OAAO,IAAI,IAAI,CAEd;IAUD,uDAAuD;IACvD,OAAO,IAAI,IAAI,CAId;CA+IF"}
|