@laminapdf/viewer 0.2.0-alpha.3 → 0.2.0-alpha.5

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.
Files changed (149) hide show
  1. package/README.md +102 -8
  2. package/dist/client.d.ts +75 -2
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +192 -32
  5. package/dist/client.js.map +1 -1
  6. package/dist/find/controller.d.ts +73 -0
  7. package/dist/find/controller.d.ts.map +1 -0
  8. package/dist/find/controller.js +227 -0
  9. package/dist/find/controller.js.map +1 -0
  10. package/dist/find/fold.d.ts +23 -0
  11. package/dist/find/fold.d.ts.map +1 -0
  12. package/dist/find/fold.js +99 -0
  13. package/dist/find/fold.js.map +1 -0
  14. package/dist/find/page.d.ts +44 -0
  15. package/dist/find/page.d.ts.map +1 -0
  16. package/dist/find/page.js +173 -0
  17. package/dist/find/page.js.map +1 -0
  18. package/dist/first-draw.d.ts +10 -0
  19. package/dist/first-draw.d.ts.map +1 -0
  20. package/dist/first-draw.js +33 -0
  21. package/dist/first-draw.js.map +1 -0
  22. package/dist/history/history.d.ts +65 -0
  23. package/dist/history/history.d.ts.map +1 -0
  24. package/dist/history/history.js +135 -0
  25. package/dist/history/history.js.map +1 -0
  26. package/dist/index.d.ts +13 -2
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +4 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/layers/box.d.ts +10 -0
  31. package/dist/layers/box.d.ts.map +1 -0
  32. package/dist/layers/box.js +19 -0
  33. package/dist/layers/box.js.map +1 -0
  34. package/dist/layers/copy.d.ts +26 -0
  35. package/dist/layers/copy.d.ts.map +1 -0
  36. package/dist/layers/copy.js +150 -0
  37. package/dist/layers/copy.js.map +1 -0
  38. package/dist/layers/highlight-layer.d.ts +31 -0
  39. package/dist/layers/highlight-layer.d.ts.map +1 -0
  40. package/dist/layers/highlight-layer.js +95 -0
  41. package/dist/layers/highlight-layer.js.map +1 -0
  42. package/dist/layers/link-layer.d.ts +68 -0
  43. package/dist/layers/link-layer.d.ts.map +1 -0
  44. package/dist/layers/link-layer.js +279 -0
  45. package/dist/layers/link-layer.js.map +1 -0
  46. package/dist/layers/link-url.d.ts +19 -0
  47. package/dist/layers/link-url.d.ts.map +1 -0
  48. package/dist/layers/link-url.js +47 -0
  49. package/dist/layers/link-url.js.map +1 -0
  50. package/dist/layers/spans.d.ts +36 -0
  51. package/dist/layers/spans.d.ts.map +1 -0
  52. package/dist/layers/spans.js +103 -0
  53. package/dist/layers/spans.js.map +1 -0
  54. package/dist/layers/structure-layer.d.ts +15 -0
  55. package/dist/layers/structure-layer.d.ts.map +1 -0
  56. package/dist/layers/structure-layer.js +155 -0
  57. package/dist/layers/structure-layer.js.map +1 -0
  58. package/dist/layers/text-layer.d.ts +70 -0
  59. package/dist/layers/text-layer.d.ts.map +1 -0
  60. package/dist/layers/text-layer.js +576 -0
  61. package/dist/layers/text-layer.js.map +1 -0
  62. package/dist/layout/range.d.ts +3 -0
  63. package/dist/layout/range.d.ts.map +1 -0
  64. package/dist/layout/range.js +5 -0
  65. package/dist/layout/range.js.map +1 -0
  66. package/dist/network/watch.d.ts +30 -0
  67. package/dist/network/watch.d.ts.map +1 -0
  68. package/dist/network/watch.js +181 -0
  69. package/dist/network/watch.js.map +1 -0
  70. package/dist/protocol.d.ts +193 -6
  71. package/dist/protocol.d.ts.map +1 -1
  72. package/dist/protocol.js +16 -4
  73. package/dist/protocol.js.map +1 -1
  74. package/dist/render/tiles.d.ts +43 -3
  75. package/dist/render/tiles.d.ts.map +1 -1
  76. package/dist/render/tiles.js +62 -10
  77. package/dist/render/tiles.js.map +1 -1
  78. package/dist/source/http-range.d.ts +59 -0
  79. package/dist/source/http-range.d.ts.map +1 -0
  80. package/dist/source/http-range.js +284 -0
  81. package/dist/source/http-range.js.map +1 -0
  82. package/dist/source/opfs.d.ts +35 -0
  83. package/dist/source/opfs.d.ts.map +1 -0
  84. package/dist/source/opfs.js +178 -0
  85. package/dist/source/opfs.js.map +1 -0
  86. package/dist/thumbnails/strip.d.ts +37 -0
  87. package/dist/thumbnails/strip.d.ts.map +1 -0
  88. package/dist/thumbnails/strip.js +324 -0
  89. package/dist/thumbnails/strip.js.map +1 -0
  90. package/dist/view/budgets.d.ts +29 -0
  91. package/dist/view/budgets.d.ts.map +1 -0
  92. package/dist/view/budgets.js +63 -0
  93. package/dist/view/budgets.js.map +1 -0
  94. package/dist/view/touch.d.ts +71 -0
  95. package/dist/view/touch.d.ts.map +1 -0
  96. package/dist/view/touch.js +220 -0
  97. package/dist/view/touch.js.map +1 -0
  98. package/dist/view/user-select.d.ts +3 -0
  99. package/dist/view/user-select.d.ts.map +1 -0
  100. package/dist/view/user-select.js +10 -0
  101. package/dist/view/user-select.js.map +1 -0
  102. package/dist/view/viewer.d.ts +102 -1
  103. package/dist/view/viewer.d.ts.map +1 -1
  104. package/dist/view/viewer.js +660 -95
  105. package/dist/view/viewer.js.map +1 -1
  106. package/dist/worker/document.d.ts +53 -1
  107. package/dist/worker/document.d.ts.map +1 -1
  108. package/dist/worker/document.js +222 -24
  109. package/dist/worker/document.js.map +1 -1
  110. package/dist/worker/entry.d.ts +1 -1
  111. package/dist/worker/entry.d.ts.map +1 -1
  112. package/dist/worker/entry.js +2 -0
  113. package/dist/worker/entry.js.map +1 -1
  114. package/dist/worker/find.d.ts +7 -0
  115. package/dist/worker/find.d.ts.map +1 -0
  116. package/dist/worker/find.js +46 -0
  117. package/dist/worker/find.js.map +1 -0
  118. package/dist/worker/methods.d.ts.map +1 -1
  119. package/dist/worker/methods.js +49 -4
  120. package/dist/worker/methods.js.map +1 -1
  121. package/dist/worker/navigation.d.ts +10 -0
  122. package/dist/worker/navigation.d.ts.map +1 -0
  123. package/dist/worker/navigation.js +34 -0
  124. package/dist/worker/navigation.js.map +1 -0
  125. package/dist/worker/network.d.ts +4 -0
  126. package/dist/worker/network.d.ts.map +1 -0
  127. package/dist/worker/network.js +24 -0
  128. package/dist/worker/network.js.map +1 -0
  129. package/dist/worker/print.d.ts +14 -0
  130. package/dist/worker/print.d.ts.map +1 -0
  131. package/dist/worker/print.js +49 -0
  132. package/dist/worker/print.js.map +1 -0
  133. package/dist/worker/recent-pages.d.ts +14 -0
  134. package/dist/worker/recent-pages.d.ts.map +1 -0
  135. package/dist/worker/recent-pages.js +53 -0
  136. package/dist/worker/recent-pages.js.map +1 -0
  137. package/dist/worker/render.d.ts +5 -2
  138. package/dist/worker/render.d.ts.map +1 -1
  139. package/dist/worker/render.js +16 -6
  140. package/dist/worker/render.js.map +1 -1
  141. package/dist/worker/text.d.ts +25 -0
  142. package/dist/worker/text.d.ts.map +1 -0
  143. package/dist/worker/text.js +82 -0
  144. package/dist/worker/text.js.map +1 -0
  145. package/dist/zoom/destination.d.ts +18 -0
  146. package/dist/zoom/destination.d.ts.map +1 -0
  147. package/dist/zoom/destination.js +56 -0
  148. package/dist/zoom/destination.js.map +1 -0
  149. package/package.json +6 -5
@@ -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,68 @@
1
+ import type { PageLink, Target } from "@laminapdf/sdk";
2
+ import type { ViewerClient } from "../client.js";
3
+ import type { PageSize, Rotation } from "../layout/page-layout.js";
4
+ import type { PageLayer } from "../view/viewer.js";
5
+ import { type OpenableLink } from "./link-url.js";
6
+ /** The words the layer gives links without a description of their own, and its hover notes. */
7
+ export interface LinkWords {
8
+ /** A link to page `page` (0-based). */
9
+ page(page: number): string;
10
+ readonly nextPage: string;
11
+ readonly previousPage: string;
12
+ readonly firstPage: string;
13
+ readonly lastPage: string;
14
+ /** A link to another file, named when the file says which. */
15
+ otherFile(file: string | undefined): string;
16
+ /** A link whose address can't be opened (a `javascript:`, `file:` or `data:` one). */
17
+ refused(uri: string): string;
18
+ /** A link that asks to run an action, which the reader never does (`Launch`, `JavaScript`…). */
19
+ readonly unsupported: string;
20
+ /** A link whose destination isn't in the document. */
21
+ readonly broken: string;
22
+ }
23
+ /** The link layer's options (`createViewer`'s `links`). */
24
+ export interface LinkLayerOptions {
25
+ /**
26
+ * The address of page `page` (0-based) of this document, for an internal link's `href`, so a
27
+ * new tab opens the same document there; `undefined` leaves the link without one.
28
+ */
29
+ readonly href?: (page: number) => string | undefined;
30
+ /** A web or email link was followed: ask the reader before opening it (normalised, punycode host). */
31
+ readonly onExternal?: (link: OpenableLink) => void;
32
+ /** The words for links without a description, and the hover notes (default English). */
33
+ readonly words?: LinkWords;
34
+ /**
35
+ * A link that does nothing was followed (another file, an action never run, a refused
36
+ * address, a broken destination), with the note it shows: for the app's debug log.
37
+ */
38
+ readonly onInert?: (link: PageLink, note: string) => void;
39
+ }
40
+ /** What the link layer needs of its viewer. */
41
+ export interface LinkHost {
42
+ /** The viewer's root, where a selection drag starts. */
43
+ readonly root: HTMLElement;
44
+ readonly client: Pick<ViewerClient, "pageLinks">;
45
+ /** Page `page`'s displayed size in points. */
46
+ points(page: number): PageSize | undefined;
47
+ rotation(): Rotation;
48
+ /** The number of pages. */
49
+ pageCount(): number;
50
+ /** Goes to `target` as a history jump. */
51
+ goTo(target: Target): void;
52
+ /** Goes to page `page` (0-based) as a history jump. */
53
+ goToPage(page: number): void;
54
+ }
55
+ /** The link layer of one viewer. */
56
+ export declare class LinkLayer implements PageLayer {
57
+ #private;
58
+ readonly name = "annot";
59
+ constructor(host: LinkHost, options?: LinkLayerOptions);
60
+ /** Stops listening: the viewer is going. */
61
+ destroy(): void;
62
+ onMount(page: number, slot: HTMLElement): void;
63
+ onUnmount(page: number): void;
64
+ onZoom(page: number, _slot: HTMLElement, size: PageSize): void;
65
+ /** Places every page again: the view turned. */
66
+ refresh(): void;
67
+ }
68
+ //# sourceMappingURL=link-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-layer.d.ts","sourceRoot":"","sources":["../../src/layers/link-layer.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAEhE,+FAA+F;AAC/F,MAAM,WAAW,SAAS;IACxB,uCAAuC;IACvC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8DAA8D;IAC9D,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAC5C,sFAAsF;IACtF,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,gGAAgG;IAChG,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,sDAAsD;IACtD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAkBD,2DAA2D;AAC3D,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACrD,sGAAsG;IACtG,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD,wFAAwF;IACxF,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3D;AAED,+CAA+C;AAC/C,MAAM,WAAW,QAAQ;IACvB,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC;IACjD,8CAA8C;IAC9C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC3C,QAAQ,IAAI,QAAQ,CAAC;IACrB,2BAA2B;IAC3B,SAAS,IAAI,MAAM,CAAC;IACpB,0CAA0C;IAC1C,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AA0DD,oCAAoC;AACpC,qBAAa,SAAU,YAAW,SAAS;;IACzC,QAAQ,CAAC,IAAI,WAAW;IAQxB,YAAY,IAAI,EAAE,QAAQ,EAAE,OAAO,GAAE,gBAAqB,EAiBzD;IAED,4CAA4C;IAC5C,OAAO,IAAI,IAAI,CAEd;IASD,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI,CAgC7C;IAED,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAK5B;IAED,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,GAAG,IAAI,CAK7D;IAED,gDAAgD;IAChD,OAAO,IAAI,IAAI,CAEd;CAyHF"}
@@ -0,0 +1,279 @@
1
+ // The link layer (web-app §6 C6, R07): on every mounted page, an anchor over each of Lamina PDF's
2
+ // links, in the page's `lamina-layer-annot` slot, above the text and below the find highlights.
3
+ // Like the other layers, the anchors sit in page points inside one box that a CSS transform scales
4
+ // and turns to the page. Only the anchors take pointer events, so text around a link still selects.
5
+ // Anchors follow page order, so Tab moves through a page's links in order, then the next page's.
6
+ // While a drag that started outside every link is under way, no anchor takes pointer events, so a
7
+ // selection begun in the text runs on over the text under a link.
8
+ //
9
+ // What a link does is decided here, never by the browser: a primary click or Enter is always
10
+ // handled (`preventDefault`). A link inside the document goes there through the viewer (a history
11
+ // jump); its `href` is the app's address for that page, so a middle click or "Open in new tab"
12
+ // opens the same document there. A web or email link asks the app to confirm first, on a click,
13
+ // a middle click or Enter. It has no `href`, so no browser menu ("Open in new tab") can open it
14
+ // without asking; its title shows the address. Anything else (another file, an action a reader
15
+ // never runs, a refused scheme, a broken destination) has no `href` either, does nothing, and
16
+ // says why on hover.
17
+ import { placeBox } from "./box.js";
18
+ import { externalLink } from "./link-url.js";
19
+ /** English, for a viewer whose app gives none. */
20
+ const ENGLISH = {
21
+ page: (page) => `Link to page ${page + 1}`,
22
+ nextPage: "Goes to the next page",
23
+ previousPage: "Goes to the previous page",
24
+ firstPage: "Goes to the first page",
25
+ lastPage: "Goes to the last page",
26
+ otherFile: (file) => file === undefined
27
+ ? "Links to another file, which can't be opened from here"
28
+ : `Links to another file, ${file}, which can't be opened from here`,
29
+ refused: (uri) => `This link's address can't be opened: ${uri}`,
30
+ unsupported: "This link asks to run an action. The reader never runs these.",
31
+ broken: "This link's destination isn't in the document",
32
+ };
33
+ /** The page a standard named action goes to from page `page` of `count`, and its words. */
34
+ function namedAction(name, page, count, words) {
35
+ switch (name) {
36
+ case "NextPage":
37
+ return { page: Math.min(count - 1, page + 1), label: words.nextPage };
38
+ case "PrevPage":
39
+ return { page: Math.max(0, page - 1), label: words.previousPage };
40
+ case "FirstPage":
41
+ return { page: 0, label: words.firstPage };
42
+ case "LastPage":
43
+ return { page: count - 1, label: words.lastPage };
44
+ default:
45
+ return undefined;
46
+ }
47
+ }
48
+ /** What a link that does nothing means: named and noted by `note`. */
49
+ function inert(link, note) {
50
+ return { follow: { kind: "none", link, note }, label: note, note };
51
+ }
52
+ /** The box around eight-number quads (or the rectangle, without any), in page points. */
53
+ function areas(link) {
54
+ const out = [];
55
+ const q = link.quads ?? [];
56
+ for (let at = 0; at + 8 <= q.length; at += 8) {
57
+ const xs = [q[at], q[at + 2], q[at + 4], q[at + 6]].map((v) => v ?? 0);
58
+ const ys = [q[at + 1], q[at + 3], q[at + 5], q[at + 7]].map((v) => v ?? 0);
59
+ const x = Math.min(...xs);
60
+ const y = Math.min(...ys);
61
+ out.push({ x, y, width: Math.max(...xs) - x, height: Math.max(...ys) - y });
62
+ }
63
+ if (out.length > 0)
64
+ return out;
65
+ const [x0, y0, x1, y1] = link.rect;
66
+ return [{ x: Math.min(x0, x1), y: Math.min(y0, y1), width: Math.abs(x1 - x0), height: Math.abs(y1 - y0) }];
67
+ }
68
+ /** The link layer of one viewer. */
69
+ export class LinkLayer {
70
+ name = "annot";
71
+ #host;
72
+ #options;
73
+ #words;
74
+ #pages = new Map();
75
+ #follows = new WeakMap();
76
+ #listening = new AbortController();
77
+ constructor(host, options = {}) {
78
+ this.#host = host;
79
+ this.#options = options;
80
+ this.#words = options.words ?? ENGLISH;
81
+ const { signal } = this.#listening;
82
+ host.root.addEventListener("pointerdown", (e) => {
83
+ if (e.button === 0 && !this.#followOf(e))
84
+ this.#passThrough(true);
85
+ }, { signal });
86
+ for (const end of ["pointerup", "pointercancel"]) {
87
+ document.addEventListener(end, () => this.#passThrough(false), { signal });
88
+ }
89
+ // A button released outside the window may send no pointerup: leaving the window ends the drag too.
90
+ window.addEventListener("blur", () => this.#passThrough(false), { signal });
91
+ }
92
+ /** Stops listening: the viewer is going. */
93
+ destroy() {
94
+ this.#listening.abort();
95
+ }
96
+ /** Lets the pointer through every anchor (a selection drag is under way), or stops letting it. */
97
+ #passThrough(through) {
98
+ for (const { box } of this.#pages.values()) {
99
+ for (const a of box.children)
100
+ a.style.pointerEvents = through ? "none" : "auto";
101
+ }
102
+ }
103
+ onMount(page, slot) {
104
+ const box = document.createElement("div");
105
+ box.className = "lamina-link-layer";
106
+ Object.assign(box.style, {
107
+ position: "absolute",
108
+ left: "0",
109
+ top: "0",
110
+ transformOrigin: "0 0",
111
+ pointerEvents: "none",
112
+ });
113
+ slot.append(box);
114
+ const view = { box, abort: new AbortController(), size: undefined };
115
+ this.#pages.set(page, view);
116
+ box.addEventListener("click", (e) => this.#click(e));
117
+ // A link without an `href` (a web link) gets no click from Enter: it follows it here.
118
+ box.addEventListener("keydown", (e) => {
119
+ const anchor = e.target instanceof HTMLElement ? e.target : undefined;
120
+ // One follow per press, as a native link's click: a held key's repeats ask nothing more.
121
+ if (e.key === "Enter" && !e.repeat && anchor !== undefined && !anchor.hasAttribute("href"))
122
+ this.#click(e);
123
+ });
124
+ // A middle click on a web link would open it without asking: it asks too.
125
+ box.addEventListener("auxclick", (e) => {
126
+ if (e.button === 1 && this.#followOf(e)?.kind === "external")
127
+ this.#click(e);
128
+ });
129
+ this.#host.client
130
+ .pageLinks(page, { signal: view.abort.signal })
131
+ .then((links) => {
132
+ if (this.#pages.get(page) === view)
133
+ this.#draw(page, view, links);
134
+ })
135
+ .catch(() => {
136
+ // Aborted (the page left), or links that can't be read: the page stays without them.
137
+ });
138
+ }
139
+ onUnmount(page) {
140
+ const view = this.#pages.get(page);
141
+ view?.abort.abort();
142
+ view?.box.remove();
143
+ this.#pages.delete(page);
144
+ }
145
+ onZoom(page, _slot, size) {
146
+ const view = this.#pages.get(page);
147
+ if (view === undefined)
148
+ return;
149
+ view.size = size;
150
+ this.#place(page, view);
151
+ }
152
+ /** Places every page again: the view turned. */
153
+ refresh() {
154
+ for (const [page, view] of this.#pages)
155
+ this.#place(page, view);
156
+ }
157
+ #place(page, view) {
158
+ const points = this.#host.points(page);
159
+ if (points !== undefined && view.size !== undefined)
160
+ placeBox(view.box, points, view.size, this.#host.rotation());
161
+ }
162
+ #draw(page, view, links) {
163
+ view.box.replaceChildren(...links.map((link) => this.#anchor(page, link)));
164
+ }
165
+ /** The anchor for `link` on page `page`: placed, named, and with what following it does. */
166
+ #anchor(page, link) {
167
+ const { follow, label, note } = this.#meaning(page, link);
168
+ const a = document.createElement("a");
169
+ a.className = "lamina-link";
170
+ a.draggable = false;
171
+ const href = this.#internalHref(follow);
172
+ if (href === undefined) {
173
+ // No address for the browser to follow: still a link in the tab order, and named.
174
+ a.setAttribute("role", "link");
175
+ a.tabIndex = 0;
176
+ if (follow.kind === "none")
177
+ a.setAttribute("aria-disabled", "true");
178
+ }
179
+ else {
180
+ a.href = href;
181
+ a.referrerPolicy = "no-referrer";
182
+ }
183
+ a.setAttribute("aria-label", link.contents ?? label);
184
+ if (note !== undefined)
185
+ a.title = note;
186
+ const [first, ...rest] = areas(link);
187
+ const bounds = first ?? { x: 0, y: 0, width: 0, height: 0 };
188
+ Object.assign(a.style, {
189
+ position: "absolute",
190
+ left: `${bounds.x}px`,
191
+ top: `${bounds.y}px`,
192
+ width: `${bounds.width}px`,
193
+ height: `${bounds.height}px`,
194
+ pointerEvents: "auto",
195
+ cursor: follow.kind === "none" ? "default" : "pointer",
196
+ });
197
+ // A link over several areas (its quads): the first is the anchor, the others its children,
198
+ // placed from it, so only the areas follow the link and the focus ring is on the first.
199
+ for (const area of rest) {
200
+ const part = document.createElement("span");
201
+ Object.assign(part.style, {
202
+ position: "absolute",
203
+ left: `${area.x - bounds.x}px`,
204
+ top: `${area.y - bounds.y}px`,
205
+ width: `${area.width}px`,
206
+ height: `${area.height}px`,
207
+ });
208
+ a.append(part);
209
+ }
210
+ this.#follows.set(a, follow);
211
+ return a;
212
+ }
213
+ /** What `link` on page `page` does, its name, and the note it shows on hover. */
214
+ #meaning(page, link) {
215
+ const words = this.#words;
216
+ switch (link.kind) {
217
+ case "dest":
218
+ case "named":
219
+ return link.target === undefined
220
+ ? inert(link, words.broken)
221
+ : { follow: { kind: "target", target: link.target }, label: words.page(link.target.page) };
222
+ case "uri": {
223
+ const external = externalLink(link.uri);
224
+ return external.kind === "open"
225
+ ? { follow: { kind: "external", link: external }, label: external.href, note: external.href }
226
+ : inert(link, words.refused(link.uri));
227
+ }
228
+ case "remote": {
229
+ return inert(link, words.otherFile(link.file));
230
+ }
231
+ case "named-action": {
232
+ const to = namedAction(link.name, page, this.#host.pageCount(), words);
233
+ return to === undefined
234
+ ? inert(link, words.unsupported)
235
+ : { follow: { kind: "page", page: to.page }, label: to.label, note: to.label };
236
+ }
237
+ default:
238
+ return inert(link, words.unsupported);
239
+ }
240
+ }
241
+ #internalHref(follow) {
242
+ if (follow.kind === "target")
243
+ return this.#options.href?.(follow.target.page);
244
+ if (follow.kind === "page")
245
+ return this.#options.href?.(follow.page);
246
+ return undefined;
247
+ }
248
+ /** What following the anchor `e` happened on does. */
249
+ #followOf(e) {
250
+ const anchor = e.target instanceof Element ? e.target.closest("a.lamina-link") : null;
251
+ return anchor instanceof HTMLElement ? this.#follows.get(anchor) : undefined;
252
+ }
253
+ #click(e) {
254
+ const follow = this.#followOf(e);
255
+ if (follow === undefined)
256
+ return;
257
+ const external = follow.kind === "external";
258
+ // A modified click on a link inside the document opens its `href` where the browser puts it.
259
+ const modified = e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || (e instanceof MouseEvent && e.button !== 0);
260
+ if (!external && modified)
261
+ return;
262
+ e.preventDefault();
263
+ switch (follow.kind) {
264
+ case "target":
265
+ this.#host.goTo(follow.target);
266
+ return;
267
+ case "page":
268
+ this.#host.goToPage(follow.page);
269
+ return;
270
+ case "external":
271
+ this.#options.onExternal?.(follow.link);
272
+ return;
273
+ case "none":
274
+ this.#options.onInert?.(follow.link, follow.note);
275
+ return;
276
+ }
277
+ }
278
+ }
279
+ //# sourceMappingURL=link-layer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-layer.js","sourceRoot":"","sources":["../../src/layers/link-layer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /** A link address as a reader treats it. */
2
+ export type ExternalLink = OpenableLink | {
3
+ readonly kind: "refused";
4
+ readonly uri: string;
5
+ };
6
+ /** A link address that may open after the reader confirms. */
7
+ export type OpenableLink = {
8
+ readonly kind: "open";
9
+ readonly href: string;
10
+ /** The address in three parts, the host (or a mailto address's domain) emphasised. */
11
+ readonly parts: {
12
+ readonly before: string;
13
+ readonly host: string;
14
+ readonly after: string;
15
+ };
16
+ };
17
+ /** What a reader does with the link address `uri`. */
18
+ export declare function externalLink(uri: string): ExternalLink;
19
+ //# sourceMappingURL=link-url.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-url.d.ts","sourceRoot":"","sources":["../../src/layers/link-url.ts"],"names":[],"mappings":"AASA,4CAA4C;AAC5C,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F,8DAA8D;AAC9D,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAC5F,CAAC;AAEF,sDAAsD;AACtD,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,CAkCtD"}
@@ -0,0 +1,47 @@
1
+ // Which link addresses a reader may open (web-app §2: external links confirm before leaving; R07:
2
+ // only `http(s):` and `mailto:` open), and how one is shown so it can't pass for another. The
3
+ // address comes from the file and is never trusted: `javascript:`, `file:`, `data:` and every other
4
+ // scheme are refused, as is anything `URL` can't parse. A host is shown as `URL` gives it: an
5
+ // international name in punycode (`xn--…`), so a look-alike host reads as what it is.
6
+ /** The schemes a link may open. */
7
+ const OPENABLE = new Set(["http:", "https:", "mailto:"]);
8
+ /** What a reader does with the link address `uri`. */
9
+ export function externalLink(uri) {
10
+ let url;
11
+ try {
12
+ url = new URL(uri.trim());
13
+ }
14
+ catch {
15
+ return { kind: "refused", uri };
16
+ }
17
+ if (!OPENABLE.has(url.protocol))
18
+ return { kind: "refused", uri };
19
+ const { href } = url;
20
+ if (url.protocol === "mailto:") {
21
+ // `mailto:` has no host: the domain after the last `@` is what a reader checks.
22
+ const at = href.lastIndexOf("@");
23
+ const end = href.search(/[?#]/);
24
+ const stop = end < 0 ? href.length : end;
25
+ if (at < 0 || at > stop)
26
+ return { kind: "open", href, parts: { before: href, host: "", after: "" } };
27
+ return {
28
+ kind: "open",
29
+ href,
30
+ parts: { before: href.slice(0, at + 1), host: href.slice(at + 1, stop), after: href.slice(stop) },
31
+ };
32
+ }
33
+ // The host follows `//` and any credentials (`user:password@`), which may hold the host's own
34
+ // text to pass for it: count past them rather than search.
35
+ const userinfo = url.username === "" && url.password === "" ? 0 : href.indexOf("@", url.protocol.length) + 1;
36
+ const start = userinfo > 0 ? userinfo : url.protocol.length + 2;
37
+ return {
38
+ kind: "open",
39
+ href,
40
+ parts: {
41
+ before: href.slice(0, start),
42
+ host: url.host,
43
+ after: href.slice(start + url.host.length),
44
+ },
45
+ };
46
+ }
47
+ //# sourceMappingURL=link-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"link-url.js","sourceRoot":"","sources":["../../src/layers/link-url.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,8FAA8F;AAC9F,oGAAoG;AACpG,8FAA8F;AAC9F,sFAAsF;AAEtF,mCAAmC;AACnC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;AAazD,sDAAsD;AACtD,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;IAClC,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC;IACjE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC;IACrB,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/B,gFAAgF;QAChF,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;QACzC,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,IAAI;YAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC;QACrG,OAAO;YACL,IAAI,EAAE,MAAM;YACZ,IAAI;YACJ,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;SAClG,CAAC;IACJ,CAAC;IACD,8FAA8F;IAC9F,2DAA2D;IAC3D,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,KAAK,EAAE,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7G,MAAM,KAAK,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAChE,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,IAAI;QACJ,KAAK,EAAE;YACL,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC;YAC5B,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;SAC3C;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,36 @@
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
+ /** What goes between two pieces as copy writes them: a new line, a space or nothing. */
35
+ export declare function separator(a: Piece, b: Piece): string;
36
+ //# 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;AA2CD,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,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,GAAG,MAAM,CAgBpD"}