@embedpdf/angular 3.0.0-next.14 → 3.0.0-next.16
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 +309 -0
- package/dist/README.md +309 -0
- package/dist/actions/index.d.ts +34 -16
- package/dist/anchored/index.d.ts +67 -0
- package/dist/annotation/index.d.ts +602 -0
- package/dist/attachment/index.d.ts +29 -0
- package/dist/commands/index.d.ts +109 -0
- package/dist/fesm2022/embedpdf-angular-actions.mjs +117 -36
- package/dist/fesm2022/embedpdf-angular-actions.mjs.map +1 -1
- package/dist/fesm2022/embedpdf-angular-anchored.mjs +148 -0
- package/dist/fesm2022/embedpdf-angular-anchored.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-annotation.mjs +1933 -0
- package/dist/fesm2022/embedpdf-angular-annotation.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-attachment.mjs +67 -0
- package/dist/fesm2022/embedpdf-angular-attachment.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-commands.mjs +235 -0
- package/dist/fesm2022/embedpdf-angular-commands.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-form.mjs +1074 -0
- package/dist/fesm2022/embedpdf-angular-form.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-history.mjs +54 -0
- package/dist/fesm2022/embedpdf-angular-history.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-i18n.mjs +100 -0
- package/dist/fesm2022/embedpdf-angular-i18n.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-interaction.mjs +157 -0
- package/dist/fesm2022/embedpdf-angular-interaction.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-link.mjs +306 -0
- package/dist/fesm2022/embedpdf-angular-link.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-measurement.mjs +106 -0
- package/dist/fesm2022/embedpdf-angular-measurement.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-metadata.mjs +77 -0
- package/dist/fesm2022/embedpdf-angular-metadata.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-outline.mjs +76 -0
- package/dist/fesm2022/embedpdf-angular-outline.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-page-edit.mjs +68 -0
- package/dist/fesm2022/embedpdf-angular-page-edit.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-page-view.mjs +297 -0
- package/dist/fesm2022/embedpdf-angular-page-view.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-redaction.mjs +93 -0
- package/dist/fesm2022/embedpdf-angular-redaction.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-render.mjs +331 -69
- package/dist/fesm2022/embedpdf-angular-render.mjs.map +1 -1
- package/dist/fesm2022/embedpdf-angular-runtime.mjs +1151 -390
- package/dist/fesm2022/embedpdf-angular-runtime.mjs.map +1 -1
- package/dist/fesm2022/embedpdf-angular-search.mjs +251 -0
- package/dist/fesm2022/embedpdf-angular-search.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-selection.mjs +477 -0
- package/dist/fesm2022/embedpdf-angular-selection.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-shell.mjs +177 -0
- package/dist/fesm2022/embedpdf-angular-shell.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-signature.mjs +120 -0
- package/dist/fesm2022/embedpdf-angular-signature.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-stage.mjs +688 -288
- package/dist/fesm2022/embedpdf-angular-stage.mjs.map +1 -1
- package/dist/fesm2022/embedpdf-angular-stamp.mjs +169 -0
- package/dist/fesm2022/embedpdf-angular-stamp.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-testing.mjs +89 -0
- package/dist/fesm2022/embedpdf-angular-testing.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-toolbar.mjs +1123 -0
- package/dist/fesm2022/embedpdf-angular-toolbar.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular-view-manager.mjs +69 -0
- package/dist/fesm2022/embedpdf-angular-view-manager.mjs.map +1 -0
- package/dist/fesm2022/embedpdf-angular.mjs +25 -3
- package/dist/fesm2022/embedpdf-angular.mjs.map +1 -1
- package/dist/form/index.d.ts +79 -0
- package/dist/history/index.d.ts +28 -0
- package/dist/i18n/index.d.ts +52 -0
- package/dist/index.d.ts +22 -0
- package/dist/interaction/index.d.ts +58 -0
- package/dist/link/index.d.ts +77 -0
- package/dist/measurement/index.d.ts +46 -0
- package/dist/metadata/index.d.ts +51 -0
- package/dist/outline/index.d.ts +32 -0
- package/dist/page-edit/index.d.ts +27 -0
- package/dist/page-view/index.d.ts +94 -0
- package/dist/redaction/index.d.ts +38 -0
- package/dist/render/index.d.ts +45 -6
- package/dist/runtime/index.d.ts +579 -199
- package/dist/search/index.d.ts +71 -0
- package/dist/selection/index.d.ts +141 -0
- package/dist/shell/index.d.ts +88 -0
- package/dist/signature/index.d.ts +45 -0
- package/dist/stage/index.d.ts +242 -141
- package/dist/stamp/index.d.ts +58 -0
- package/dist/testing/index.d.ts +53 -0
- package/dist/toolbar/index.d.ts +257 -0
- package/dist/view-manager/index.d.ts +23 -0
- package/package.json +145 -14
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import { LinkToken, linkPlugin } from '@embedpdf/plugin-link';
|
|
2
|
+
export * from '@embedpdf/plugin-link';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { effect, untracked, Injectable, inject, TemplateRef, Directive, output, ElementRef, DestroyRef, contentChild, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
5
|
+
import { pluginService, CapabilityBinding, injectDocumentScope, injectPage, injectKernelHost } from '@embedpdf/angular/runtime';
|
|
6
|
+
import { LinkToken as LinkToken$1 } from '@embedpdf/plugin-link/contract/host';
|
|
7
|
+
import { openExternalUri, isolatePointerDown, navigableLinksOf, linkAnchorOf, isModifiedClick, hoverLink, sendLinkEvent, linkActivateContextOf } from '@embedpdf/web';
|
|
8
|
+
import { NgTemplateOutlet } from '@angular/common';
|
|
9
|
+
import { StageToken } from '@embedpdf/plugin-stage/contract';
|
|
10
|
+
import { ActionsToken, createHoverPump } from '@embedpdf/plugin-actions/contract';
|
|
11
|
+
import { InteractionToken } from '@embedpdf/plugin-interaction/contract/host';
|
|
12
|
+
import { EpdfStage } from '@embedpdf/angular/stage';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The link plugin's service and feature:
|
|
16
|
+
*
|
|
17
|
+
* withLink() the plugin, for provideEmbedPdf()
|
|
18
|
+
* inject(EpdfLink) read a page's links, follow one from code (`activate()`), and the
|
|
19
|
+
* events as streams (`activated$`, `loaded$`)
|
|
20
|
+
*
|
|
21
|
+
* The plugin never touches the browser, so the website opener is this package's: while the
|
|
22
|
+
* service or an `<epdf-link-layer>` is there, following a website link opens it in a new tab
|
|
23
|
+
* (only `http`, `https`, `mailto` and `tel`; anything else is refused and reported).
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Hand the link plugin this package's website opener while `link` resolves, and take it back
|
|
27
|
+
* when the caller goes. Call it in an injection context: the effect lives as long as it.
|
|
28
|
+
*/
|
|
29
|
+
function registerWebsiteOpener(link) {
|
|
30
|
+
effect((onCleanup) => {
|
|
31
|
+
const host = link();
|
|
32
|
+
if (host)
|
|
33
|
+
onCleanup(untracked(() => host.registerUriOpener(openExternalUri)));
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Links: `listLinks(page)`, `getLinkAt(page, point)`, `activate(link)` and the rest of the Links
|
|
38
|
+
* page's methods, and its events as streams (`link.activated$`). While the service is there,
|
|
39
|
+
* `activate()` opens a website in a new tab.
|
|
40
|
+
*/
|
|
41
|
+
class EpdfLink extends pluginService({
|
|
42
|
+
name: 'EpdfLink',
|
|
43
|
+
feature: 'withLink()',
|
|
44
|
+
token: LinkToken,
|
|
45
|
+
methods: [
|
|
46
|
+
'listLinks',
|
|
47
|
+
'getLink',
|
|
48
|
+
'getLinkAt',
|
|
49
|
+
'listAllLinks',
|
|
50
|
+
'ensureLoaded',
|
|
51
|
+
'isLoaded',
|
|
52
|
+
'getStatus',
|
|
53
|
+
'activate',
|
|
54
|
+
'activateAt',
|
|
55
|
+
'resolve',
|
|
56
|
+
'getLabel',
|
|
57
|
+
],
|
|
58
|
+
events: ['onActivated', 'onLoaded'],
|
|
59
|
+
}) {
|
|
60
|
+
constructor() {
|
|
61
|
+
super();
|
|
62
|
+
// The opener is registered through the plugin's host side, for this service's document.
|
|
63
|
+
registerWebsiteOpener(new CapabilityBinding(this.binding.host, () => LinkToken$1, injectDocumentScope()).capability);
|
|
64
|
+
}
|
|
65
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfLink, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
66
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfLink, providedIn: 'root' });
|
|
67
|
+
}
|
|
68
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfLink, decorators: [{
|
|
69
|
+
type: Injectable,
|
|
70
|
+
args: [{ providedIn: 'root' }]
|
|
71
|
+
}], ctorParameters: () => [] });
|
|
72
|
+
/** The link plugin: links that work, in every page with an `<epdf-link-layer>`. */
|
|
73
|
+
function withLink() {
|
|
74
|
+
return { plugins: [linkPlugin()], services: [EpdfLink] };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* `<epdf-link-layer>`: the links on one page, as real anchors. A website link gets an `href`
|
|
79
|
+
* (so middle-click, "copy link" and the status bar work), every link can be reached with Tab
|
|
80
|
+
* and followed with Enter, and a click or a key follows it through the plugin's `activate()`,
|
|
81
|
+
* so every way of following a link fires `activated$`.
|
|
82
|
+
*
|
|
83
|
+
* The layer steps aside while the active tool doesn't navigate links (an annotation tool owns
|
|
84
|
+
* them then); while a tool can edit annotations, a link that belongs to another annotation
|
|
85
|
+
* steps aside too, so its parent can be selected and moved.
|
|
86
|
+
*
|
|
87
|
+
* Draw a link yourself with a template; `native` is the layer's own anchor, to wrap so a click
|
|
88
|
+
* keeps doing what it does:
|
|
89
|
+
*
|
|
90
|
+
* <epdf-link-layer>
|
|
91
|
+
* <ng-template epdfLink let-link let-native="native">
|
|
92
|
+
* <span class="pdf-link"><ng-container [ngTemplateOutlet]="native" /></span>
|
|
93
|
+
* </ng-template>
|
|
94
|
+
* </epdf-link-layer>
|
|
95
|
+
*
|
|
96
|
+
* With the actions plugin, the link's own actions run on enter, exit, press, release, focus
|
|
97
|
+
* and blur. Which links are shown, each anchor's box, `href` and label, how a click is told
|
|
98
|
+
* apart, and what a link sends the plugins are `@embedpdf/web`'s, shared by every framework.
|
|
99
|
+
*/
|
|
100
|
+
/**
|
|
101
|
+
* How each link is drawn instead of the layer's bare anchor:
|
|
102
|
+
* `<ng-template epdfLink let-link let-native="native">`. For a link you don't want to change,
|
|
103
|
+
* show `native` alone.
|
|
104
|
+
*/
|
|
105
|
+
class EpdfLinkTemplate {
|
|
106
|
+
template = inject(TemplateRef);
|
|
107
|
+
static ngTemplateContextGuard(_directive, context) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfLinkTemplate, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
111
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.4", type: EpdfLinkTemplate, isStandalone: true, selector: "ng-template[epdfLink]", ngImport: i0 });
|
|
112
|
+
}
|
|
113
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfLinkTemplate, decorators: [{
|
|
114
|
+
type: Directive,
|
|
115
|
+
args: [{ selector: 'ng-template[epdfLink]' }]
|
|
116
|
+
}] });
|
|
117
|
+
/**
|
|
118
|
+
* A press on a link: kept from the Stage under it (which would start the active tool's gesture,
|
|
119
|
+
* or end an edit as a click outside), and reported as `(epdfLinkPress)` for the link's own
|
|
120
|
+
* "mouse down" action. One native listener does both.
|
|
121
|
+
*/
|
|
122
|
+
class EpdfLinkPress {
|
|
123
|
+
pressed = output({ alias: 'epdfLinkPress' });
|
|
124
|
+
constructor() {
|
|
125
|
+
const element = inject(ElementRef).nativeElement;
|
|
126
|
+
inject(DestroyRef).onDestroy(isolatePointerDown(element, (event) => this.pressed.emit(event)));
|
|
127
|
+
}
|
|
128
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfLinkPress, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
129
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.2.4", type: EpdfLinkPress, isStandalone: true, selector: "[epdfLinkPress]", outputs: { pressed: "epdfLinkPress" }, ngImport: i0 });
|
|
130
|
+
}
|
|
131
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfLinkPress, decorators: [{
|
|
132
|
+
type: Directive,
|
|
133
|
+
args: [{ selector: '[epdfLinkPress]' }]
|
|
134
|
+
}], ctorParameters: () => [] });
|
|
135
|
+
const NO_LINKS = Object.freeze([]);
|
|
136
|
+
class EpdfLinkLayer {
|
|
137
|
+
page = injectPage('<epdf-link-layer>');
|
|
138
|
+
host = injectKernelHost('<epdf-link-layer>');
|
|
139
|
+
enclosingStage = inject(EpdfStage, { optional: true });
|
|
140
|
+
/** The app's own drawing of a link, when it gives one. */
|
|
141
|
+
custom = contentChild(EpdfLinkTemplate, ...(ngDevMode ? [{ debugName: "custom" }] : []));
|
|
142
|
+
/** A plugin of the page's own document: every plugin below is read for it. */
|
|
143
|
+
bindTo(token) {
|
|
144
|
+
return new CapabilityBinding(this.host, () => token, () => this.page.documentId);
|
|
145
|
+
}
|
|
146
|
+
link = this.bindTo(LinkToken$1);
|
|
147
|
+
/** Optional: with the actions plugin, a link's own actions run. */
|
|
148
|
+
actions = this.bindTo(ActionsToken).capability;
|
|
149
|
+
/** Optional: a tool that can edit annotations owns the links that belong to one. */
|
|
150
|
+
editing = this.bindTo(InteractionToken).select((interaction) => interaction.activeToolEnables('annotation-edit'), false);
|
|
151
|
+
/** A link to a page moves the view it was followed in: this Stage, or the main one. */
|
|
152
|
+
stage = new CapabilityBinding(this.host, () => this.enclosingStage?.token() ?? StageToken, () => this.page.documentId).capability;
|
|
153
|
+
links = this.link.select((link) => link.listLinks(this.page.ref), NO_LINKS);
|
|
154
|
+
/** Whether the active tool follows links: the layer draws nothing otherwise. */
|
|
155
|
+
engaged = this.link.select((link) => link.isNavigationEngaged(), false);
|
|
156
|
+
/** Each link's anchor: its box, its label, and a native `href` only for a website with nothing chained after it. */
|
|
157
|
+
items = computed(() => {
|
|
158
|
+
const link = this.link.capability();
|
|
159
|
+
if (!link || !this.engaged())
|
|
160
|
+
return [];
|
|
161
|
+
const transform = this.page.transform();
|
|
162
|
+
return navigableLinksOf(this.links(), this.editing()).map((item) => linkAnchorOf(item, transform, (each) => link.getLabel(each)));
|
|
163
|
+
}, ...(ngDevMode ? [{ debugName: "items" }] : []));
|
|
164
|
+
/** One hover pump per layer: the enter and exit actions of the link under the pointer. */
|
|
165
|
+
pump = computed(() => {
|
|
166
|
+
const actions = this.actions();
|
|
167
|
+
return actions ? createHoverPump(actions.dispatch) : null;
|
|
168
|
+
}, ...(ngDevMode ? [{ debugName: "pump" }] : []));
|
|
169
|
+
constructor() {
|
|
170
|
+
registerWebsiteOpener(this.link.capability);
|
|
171
|
+
// Read the page's links as soon as the page is shown.
|
|
172
|
+
effect(() => {
|
|
173
|
+
const link = this.link.capability();
|
|
174
|
+
const page = this.page.ref;
|
|
175
|
+
if (link)
|
|
176
|
+
untracked(() => void link.ensureLoaded(page));
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
follow(event, item) {
|
|
180
|
+
// A click with a modifier on a real href stays the browser's: a background tab, a window.
|
|
181
|
+
if (item.href && isModifiedClick(event))
|
|
182
|
+
return;
|
|
183
|
+
// Every other click follows the link through the plugin, which opens a website through
|
|
184
|
+
// this package's opener.
|
|
185
|
+
event.preventDefault();
|
|
186
|
+
this.activate(item.link);
|
|
187
|
+
}
|
|
188
|
+
followByKey(event, link) {
|
|
189
|
+
if (event.key !== 'Enter' && event.key !== ' ')
|
|
190
|
+
return;
|
|
191
|
+
event.preventDefault();
|
|
192
|
+
this.activate(link);
|
|
193
|
+
}
|
|
194
|
+
hover(link) {
|
|
195
|
+
const pump = this.pump();
|
|
196
|
+
if (!link)
|
|
197
|
+
return pump?.hover(null);
|
|
198
|
+
hoverLink(pump, link, this.page.ref);
|
|
199
|
+
}
|
|
200
|
+
/** Send one of the link's own events to the actions plugin, when the link has actions. */
|
|
201
|
+
notify(link, event) {
|
|
202
|
+
sendLinkEvent(this.actions(), link, this.page.ref, event);
|
|
203
|
+
}
|
|
204
|
+
activate(link) {
|
|
205
|
+
const context = linkActivateContextOf(link, this.page.ref, this.stage());
|
|
206
|
+
this.link.capability()?.activate(link, context);
|
|
207
|
+
}
|
|
208
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfLinkLayer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
209
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.2.4", type: EpdfLinkLayer, isStandalone: true, selector: "epdf-link-layer", queries: [{ propertyName: "custom", first: true, predicate: EpdfLinkTemplate, descendants: true, isSignal: true }], ngImport: i0, template: `
|
|
210
|
+
@if (items().length > 0) {
|
|
211
|
+
<div style="position: absolute; inset: 0; pointer-events: none">
|
|
212
|
+
@for (item of items(); track item.link.id) {
|
|
213
|
+
<ng-template #native>
|
|
214
|
+
<a
|
|
215
|
+
target="_blank"
|
|
216
|
+
rel="noopener noreferrer"
|
|
217
|
+
role="link"
|
|
218
|
+
tabindex="0"
|
|
219
|
+
style="position: absolute; cursor: pointer; pointer-events: auto"
|
|
220
|
+
[attr.href]="item.href"
|
|
221
|
+
[attr.title]="item.label"
|
|
222
|
+
[attr.aria-label]="item.label"
|
|
223
|
+
[style.left.px]="item.box.left"
|
|
224
|
+
[style.top.px]="item.box.top"
|
|
225
|
+
[style.width.px]="item.box.width"
|
|
226
|
+
[style.height.px]="item.box.height"
|
|
227
|
+
(epdfLinkPress)="notify(item.link, 'mouseDown')"
|
|
228
|
+
(click)="follow($event, item)"
|
|
229
|
+
(keydown)="followByKey($event, item.link)"
|
|
230
|
+
(pointerenter)="hover(item.link)"
|
|
231
|
+
(pointerleave)="hover(null)"
|
|
232
|
+
(pointerup)="notify(item.link, 'mouseUp')"
|
|
233
|
+
(focus)="notify(item.link, 'focus')"
|
|
234
|
+
(blur)="notify(item.link, 'blur')"
|
|
235
|
+
></a>
|
|
236
|
+
</ng-template>
|
|
237
|
+
<ng-container
|
|
238
|
+
[ngTemplateOutlet]="custom()?.template ?? native"
|
|
239
|
+
[ngTemplateOutletContext]="{ $implicit: item.link, native }"
|
|
240
|
+
/>
|
|
241
|
+
}
|
|
242
|
+
</div>
|
|
243
|
+
}
|
|
244
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: EpdfLinkPress, selector: "[epdfLinkPress]", outputs: ["epdfLinkPress"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
245
|
+
}
|
|
246
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfLinkLayer, decorators: [{
|
|
247
|
+
type: Component,
|
|
248
|
+
args: [{
|
|
249
|
+
selector: 'epdf-link-layer',
|
|
250
|
+
imports: [NgTemplateOutlet, EpdfLinkPress],
|
|
251
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
252
|
+
template: `
|
|
253
|
+
@if (items().length > 0) {
|
|
254
|
+
<div style="position: absolute; inset: 0; pointer-events: none">
|
|
255
|
+
@for (item of items(); track item.link.id) {
|
|
256
|
+
<ng-template #native>
|
|
257
|
+
<a
|
|
258
|
+
target="_blank"
|
|
259
|
+
rel="noopener noreferrer"
|
|
260
|
+
role="link"
|
|
261
|
+
tabindex="0"
|
|
262
|
+
style="position: absolute; cursor: pointer; pointer-events: auto"
|
|
263
|
+
[attr.href]="item.href"
|
|
264
|
+
[attr.title]="item.label"
|
|
265
|
+
[attr.aria-label]="item.label"
|
|
266
|
+
[style.left.px]="item.box.left"
|
|
267
|
+
[style.top.px]="item.box.top"
|
|
268
|
+
[style.width.px]="item.box.width"
|
|
269
|
+
[style.height.px]="item.box.height"
|
|
270
|
+
(epdfLinkPress)="notify(item.link, 'mouseDown')"
|
|
271
|
+
(click)="follow($event, item)"
|
|
272
|
+
(keydown)="followByKey($event, item.link)"
|
|
273
|
+
(pointerenter)="hover(item.link)"
|
|
274
|
+
(pointerleave)="hover(null)"
|
|
275
|
+
(pointerup)="notify(item.link, 'mouseUp')"
|
|
276
|
+
(focus)="notify(item.link, 'focus')"
|
|
277
|
+
(blur)="notify(item.link, 'blur')"
|
|
278
|
+
></a>
|
|
279
|
+
</ng-template>
|
|
280
|
+
<ng-container
|
|
281
|
+
[ngTemplateOutlet]="custom()?.template ?? native"
|
|
282
|
+
[ngTemplateOutletContext]="{ $implicit: item.link, native }"
|
|
283
|
+
/>
|
|
284
|
+
}
|
|
285
|
+
</div>
|
|
286
|
+
}
|
|
287
|
+
`,
|
|
288
|
+
}]
|
|
289
|
+
}], ctorParameters: () => [] });
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* @embedpdf/angular/link: links that work.
|
|
293
|
+
*
|
|
294
|
+
* withLink() the plugin, for provideEmbedPdf()
|
|
295
|
+
* inject(EpdfLink) a page's links, follow one from code, the events
|
|
296
|
+
* <epdf-link-layer> the links on each page, as real anchors
|
|
297
|
+
* <ng-template epdfLink let-link let-native="native"> draw a link yourself
|
|
298
|
+
*/
|
|
299
|
+
// The plugin's types and helpers, so app code has one import for the feature.
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Generated bundle index. Do not edit.
|
|
303
|
+
*/
|
|
304
|
+
|
|
305
|
+
export { EpdfLink, EpdfLinkLayer, EpdfLinkTemplate, withLink };
|
|
306
|
+
//# sourceMappingURL=embedpdf-angular-link.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedpdf-angular-link.mjs","sources":["../../link/src/link.ts","../../link/src/link-layer.ts","../../link/src/public_api.ts","../../link/src/embedpdf-angular-link.ts"],"sourcesContent":["/**\n * The link plugin's service and feature:\n *\n * withLink() the plugin, for provideEmbedPdf()\n * inject(EpdfLink) read a page's links, follow one from code (`activate()`), and the\n * events as streams (`activated$`, `loaded$`)\n *\n * The plugin never touches the browser, so the website opener is this package's: while the\n * service or an `<epdf-link-layer>` is there, following a website link opens it in a new tab\n * (only `http`, `https`, `mailto` and `tel`; anything else is refused and reported).\n */\nimport { effect, Injectable, untracked, type Signal } from '@angular/core';\nimport {\n CapabilityBinding,\n injectDocumentScope,\n pluginService,\n type EmbedPdfFeature,\n} from '@embedpdf/angular/runtime';\nimport { linkPlugin, LinkToken } from '@embedpdf/plugin-link';\nimport {\n LinkToken as LinkHostToken,\n type LinkHostCapability,\n} from '@embedpdf/plugin-link/contract/host';\nimport { openExternalUri } from '@embedpdf/web';\n\n/**\n * Hand the link plugin this package's website opener while `link` resolves, and take it back\n * when the caller goes. Call it in an injection context: the effect lives as long as it.\n */\nexport function registerWebsiteOpener(link: Signal<LinkHostCapability | null>): void {\n effect((onCleanup) => {\n const host = link();\n if (host) onCleanup(untracked(() => host.registerUriOpener(openExternalUri)));\n });\n}\n\n/**\n * Links: `listLinks(page)`, `getLinkAt(page, point)`, `activate(link)` and the rest of the Links\n * page's methods, and its events as streams (`link.activated$`). While the service is there,\n * `activate()` opens a website in a new tab.\n */\n@Injectable({ providedIn: 'root' })\nexport class EpdfLink extends pluginService({\n name: 'EpdfLink',\n feature: 'withLink()',\n token: LinkToken,\n methods: [\n 'listLinks',\n 'getLink',\n 'getLinkAt',\n 'listAllLinks',\n 'ensureLoaded',\n 'isLoaded',\n 'getStatus',\n 'activate',\n 'activateAt',\n 'resolve',\n 'getLabel',\n ],\n events: ['onActivated', 'onLoaded'],\n}) {\n constructor() {\n super();\n // The opener is registered through the plugin's host side, for this service's document.\n registerWebsiteOpener(\n new CapabilityBinding<LinkHostCapability>(\n this.binding.host,\n () => LinkHostToken,\n injectDocumentScope(),\n ).capability,\n );\n }\n}\n\n/** The link plugin: links that work, in every page with an `<epdf-link-layer>`. */\nexport function withLink(): EmbedPdfFeature {\n return { plugins: [linkPlugin()], services: [EpdfLink] };\n}\n","/**\n * `<epdf-link-layer>`: the links on one page, as real anchors. A website link gets an `href`\n * (so middle-click, \"copy link\" and the status bar work), every link can be reached with Tab\n * and followed with Enter, and a click or a key follows it through the plugin's `activate()`,\n * so every way of following a link fires `activated$`.\n *\n * The layer steps aside while the active tool doesn't navigate links (an annotation tool owns\n * them then); while a tool can edit annotations, a link that belongs to another annotation\n * steps aside too, so its parent can be selected and moved.\n *\n * Draw a link yourself with a template; `native` is the layer's own anchor, to wrap so a click\n * keeps doing what it does:\n *\n * <epdf-link-layer>\n * <ng-template epdfLink let-link let-native=\"native\">\n * <span class=\"pdf-link\"><ng-container [ngTemplateOutlet]=\"native\" /></span>\n * </ng-template>\n * </epdf-link-layer>\n *\n * With the actions plugin, the link's own actions run on enter, exit, press, release, focus\n * and blur. Which links are shown, each anchor's box, `href` and label, how a click is told\n * apart, and what a link sends the plugins are `@embedpdf/web`'s, shared by every framework.\n */\nimport { NgTemplateOutlet } from '@angular/common';\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n contentChild,\n DestroyRef,\n Directive,\n effect,\n ElementRef,\n inject,\n output,\n TemplateRef,\n untracked,\n} from '@angular/core';\nimport type { CapabilityToken } from '@embedpdf/core';\nimport { StageToken, type StageCapability } from '@embedpdf/plugin-stage/contract';\nimport { ActionsToken, createHoverPump } from '@embedpdf/plugin-actions/contract';\nimport { InteractionToken as InteractionHostToken } from '@embedpdf/plugin-interaction/contract/host';\nimport type { Link } from '@embedpdf/plugin-link';\nimport { LinkToken as LinkHostToken } from '@embedpdf/plugin-link/contract/host';\nimport {\n hoverLink,\n isModifiedClick,\n isolatePointerDown,\n linkActivateContextOf,\n linkAnchorOf,\n navigableLinksOf,\n sendLinkEvent,\n type LinkAnchor,\n type LinkAnchorEvent,\n} from '@embedpdf/web';\nimport { CapabilityBinding, injectKernelHost, injectPage } from '@embedpdf/angular/runtime';\nimport { EpdfStage } from '@embedpdf/angular/stage';\nimport { registerWebsiteOpener } from './link';\n\n/** What a link template gets: `let-link` is the link, `let-native=\"native\"` the layer's anchor. */\nexport interface EpdfLinkTemplateContext {\n /** The link: its `id`, its `bounds` on the page and its `target`. */\n $implicit: Link;\n /** The layer's own anchor: `<ng-container [ngTemplateOutlet]=\"native\" />`. */\n native: TemplateRef<unknown>;\n}\n\n/**\n * How each link is drawn instead of the layer's bare anchor:\n * `<ng-template epdfLink let-link let-native=\"native\">`. For a link you don't want to change,\n * show `native` alone.\n */\n@Directive({ selector: 'ng-template[epdfLink]' })\nexport class EpdfLinkTemplate {\n readonly template = inject<TemplateRef<EpdfLinkTemplateContext>>(TemplateRef);\n\n static ngTemplateContextGuard(\n _directive: EpdfLinkTemplate,\n context: unknown,\n ): context is EpdfLinkTemplateContext {\n return true;\n }\n}\n\n/**\n * A press on a link: kept from the Stage under it (which would start the active tool's gesture,\n * or end an edit as a click outside), and reported as `(epdfLinkPress)` for the link's own\n * \"mouse down\" action. One native listener does both.\n */\n@Directive({ selector: '[epdfLinkPress]' })\nexport class EpdfLinkPress {\n readonly pressed = output<PointerEvent>({ alias: 'epdfLinkPress' });\n\n constructor() {\n const element = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n inject(DestroyRef).onDestroy(isolatePointerDown(element, (event) => this.pressed.emit(event)));\n }\n}\n\nconst NO_LINKS: readonly Link[] = Object.freeze([]);\n\n@Component({\n selector: 'epdf-link-layer',\n imports: [NgTemplateOutlet, EpdfLinkPress],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (items().length > 0) {\n <div style=\"position: absolute; inset: 0; pointer-events: none\">\n @for (item of items(); track item.link.id) {\n <ng-template #native>\n <a\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n role=\"link\"\n tabindex=\"0\"\n style=\"position: absolute; cursor: pointer; pointer-events: auto\"\n [attr.href]=\"item.href\"\n [attr.title]=\"item.label\"\n [attr.aria-label]=\"item.label\"\n [style.left.px]=\"item.box.left\"\n [style.top.px]=\"item.box.top\"\n [style.width.px]=\"item.box.width\"\n [style.height.px]=\"item.box.height\"\n (epdfLinkPress)=\"notify(item.link, 'mouseDown')\"\n (click)=\"follow($event, item)\"\n (keydown)=\"followByKey($event, item.link)\"\n (pointerenter)=\"hover(item.link)\"\n (pointerleave)=\"hover(null)\"\n (pointerup)=\"notify(item.link, 'mouseUp')\"\n (focus)=\"notify(item.link, 'focus')\"\n (blur)=\"notify(item.link, 'blur')\"\n ></a>\n </ng-template>\n <ng-container\n [ngTemplateOutlet]=\"custom()?.template ?? native\"\n [ngTemplateOutletContext]=\"{ $implicit: item.link, native }\"\n />\n }\n </div>\n }\n `,\n})\nexport class EpdfLinkLayer {\n private readonly page = injectPage('<epdf-link-layer>');\n private readonly host = injectKernelHost('<epdf-link-layer>');\n private readonly enclosingStage = inject(EpdfStage, { optional: true });\n /** The app's own drawing of a link, when it gives one. */\n protected readonly custom = contentChild(EpdfLinkTemplate);\n\n /** A plugin of the page's own document: every plugin below is read for it. */\n private bindTo<Capability>(token: CapabilityToken<Capability>): CapabilityBinding<Capability> {\n return new CapabilityBinding(\n this.host,\n () => token,\n () => this.page.documentId,\n );\n }\n\n private readonly link = this.bindTo(LinkHostToken);\n /** Optional: with the actions plugin, a link's own actions run. */\n private readonly actions = this.bindTo(ActionsToken).capability;\n /** Optional: a tool that can edit annotations owns the links that belong to one. */\n private readonly editing = this.bindTo(InteractionHostToken).select(\n (interaction) => interaction.activeToolEnables('annotation-edit'),\n false,\n );\n /** A link to a page moves the view it was followed in: this Stage, or the main one. */\n private readonly stage = new CapabilityBinding<StageCapability>(\n this.host,\n () => this.enclosingStage?.token() ?? StageToken,\n () => this.page.documentId,\n ).capability;\n\n private readonly links = this.link.select((link) => link.listLinks(this.page.ref), NO_LINKS);\n /** Whether the active tool follows links: the layer draws nothing otherwise. */\n private readonly engaged = this.link.select((link) => link.isNavigationEngaged(), false);\n\n /** Each link's anchor: its box, its label, and a native `href` only for a website with nothing chained after it. */\n protected readonly items = computed((): readonly LinkAnchor<Link>[] => {\n const link = this.link.capability();\n if (!link || !this.engaged()) return [];\n const transform = this.page.transform();\n return navigableLinksOf(this.links(), this.editing()).map((item) =>\n linkAnchorOf(item, transform, (each) => link.getLabel(each)),\n );\n });\n\n /** One hover pump per layer: the enter and exit actions of the link under the pointer. */\n private readonly pump = computed(() => {\n const actions = this.actions();\n return actions ? createHoverPump(actions.dispatch) : null;\n });\n\n constructor() {\n registerWebsiteOpener(this.link.capability);\n // Read the page's links as soon as the page is shown.\n effect(() => {\n const link = this.link.capability();\n const page = this.page.ref;\n if (link) untracked(() => void link.ensureLoaded(page));\n });\n }\n\n protected follow(event: MouseEvent, item: LinkAnchor<Link>): void {\n // A click with a modifier on a real href stays the browser's: a background tab, a window.\n if (item.href && isModifiedClick(event)) return;\n // Every other click follows the link through the plugin, which opens a website through\n // this package's opener.\n event.preventDefault();\n this.activate(item.link);\n }\n\n protected followByKey(event: KeyboardEvent, link: Link): void {\n if (event.key !== 'Enter' && event.key !== ' ') return;\n event.preventDefault();\n this.activate(link);\n }\n\n protected hover(link: Link | null): void {\n const pump = this.pump();\n if (!link) return pump?.hover(null);\n hoverLink(pump, link, this.page.ref);\n }\n\n /** Send one of the link's own events to the actions plugin, when the link has actions. */\n protected notify(link: Link, event: LinkAnchorEvent): void {\n sendLinkEvent(this.actions(), link, this.page.ref, event);\n }\n\n private activate(link: Link): void {\n const context = linkActivateContextOf(link, this.page.ref, this.stage());\n this.link.capability()?.activate(link, context);\n }\n}\n","/**\n * @embedpdf/angular/link: links that work.\n *\n * withLink() the plugin, for provideEmbedPdf()\n * inject(EpdfLink) a page's links, follow one from code, the events\n * <epdf-link-layer> the links on each page, as real anchors\n * <ng-template epdfLink let-link let-native=\"native\"> draw a link yourself\n */\n\n// The plugin's types and helpers, so app code has one import for the feature.\nexport * from '@embedpdf/plugin-link';\nexport { EpdfLink, withLink } from './link';\nexport { EpdfLinkLayer, EpdfLinkTemplate } from './link-layer';\nexport type { EpdfLinkTemplateContext } from './link-layer';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":["LinkHostToken","InteractionHostToken"],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { measurementState, MeasurementToken, NO_PAGE_SCALE, NO_READOUT, measurementPlugin } from '@embedpdf/plugin-measurement';
|
|
2
|
+
export * from '@embedpdf/plugin-measurement';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { computed, Injectable } from '@angular/core';
|
|
5
|
+
import { pluginService } from '@embedpdf/angular/runtime';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The measurement plugin's service and feature:
|
|
9
|
+
*
|
|
10
|
+
* withMeasurement(options) the plugin, for provideEmbedPdf()
|
|
11
|
+
* inject(EpdfMeasurement) the scales, calibration and measuring, the State table as
|
|
12
|
+
* signals (`busy()`, `calibrationRequest()`, `lastReports()`), a
|
|
13
|
+
* page's scale (`scaleOf(page)`), a measurement's readout
|
|
14
|
+
* (`readoutOf(ref)`), the events as streams, and the settings
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Measurements of the document in scope (`[epdfDocumentScope]`), else the active one: the
|
|
18
|
+
* Measurements page's methods, its State table as signals, a page's scale and a measurement's
|
|
19
|
+
* readout as signals, the events as streams (`scaleChanged$`, `calibrationRequested$`, …), and
|
|
20
|
+
* the settings. Without a document the signals read empty and the methods refuse with
|
|
21
|
+
* `not-ready`.
|
|
22
|
+
*/
|
|
23
|
+
class EpdfMeasurement extends pluginService({
|
|
24
|
+
name: 'EpdfMeasurement',
|
|
25
|
+
feature: 'withMeasurement()',
|
|
26
|
+
token: MeasurementToken,
|
|
27
|
+
state: measurementState,
|
|
28
|
+
methods: [
|
|
29
|
+
'listPresets',
|
|
30
|
+
'setPreset',
|
|
31
|
+
'setScale',
|
|
32
|
+
'clearScale',
|
|
33
|
+
'startCalibration',
|
|
34
|
+
'calibrate',
|
|
35
|
+
'dismissCalibration',
|
|
36
|
+
'setUnit',
|
|
37
|
+
'setAreaUnit',
|
|
38
|
+
'setPrecision',
|
|
39
|
+
'listUnits',
|
|
40
|
+
'listAreaUnits',
|
|
41
|
+
'getPageScale',
|
|
42
|
+
'ensureLoaded',
|
|
43
|
+
'getReadout',
|
|
44
|
+
'measureDistance',
|
|
45
|
+
'measureArea',
|
|
46
|
+
'createMeasurement',
|
|
47
|
+
'canCalibrate',
|
|
48
|
+
'canMeasure',
|
|
49
|
+
'isBusy',
|
|
50
|
+
'getCalibrationRequest',
|
|
51
|
+
'listLastReports',
|
|
52
|
+
],
|
|
53
|
+
events: [
|
|
54
|
+
'onScaleChanged',
|
|
55
|
+
'onCalibrationRequested',
|
|
56
|
+
'onCalibrationCompleted',
|
|
57
|
+
'onCalibrationDismissed',
|
|
58
|
+
],
|
|
59
|
+
}) {
|
|
60
|
+
scaleOf(page) {
|
|
61
|
+
const pageOf = typeof page === 'function' ? page : () => page;
|
|
62
|
+
// The plugin hands out the same scale until it changes. With no document there is none to
|
|
63
|
+
// read, but a page asked for still gets an answer: a scale that hasn't loaded.
|
|
64
|
+
const scale = this.binding.select((measurement) => {
|
|
65
|
+
const wanted = pageOf();
|
|
66
|
+
return wanted === null ? NO_PAGE_SCALE : measurement.getPageScale(wanted);
|
|
67
|
+
}, NO_PAGE_SCALE, Object.is);
|
|
68
|
+
return computed(() => (pageOf() === null ? null : scale()));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A measurement's `{ kind, value, label }`, or `{ unavailable }` with why there's none (no
|
|
72
|
+
* scale, a scale from map software). Pass a function (`this.annotation`, an input) and it
|
|
73
|
+
* follows the measurement you show. The plugin hands out the same readout until the
|
|
74
|
+
* measurement changes, so it changes only then.
|
|
75
|
+
*/
|
|
76
|
+
readoutOf(ref) {
|
|
77
|
+
const refOf = typeof ref === 'function' ? ref : () => ref;
|
|
78
|
+
return this.binding.select((measurement) => measurement.getReadout(refOf()), NO_READOUT);
|
|
79
|
+
}
|
|
80
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfMeasurement, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
81
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfMeasurement, providedIn: 'root' });
|
|
82
|
+
}
|
|
83
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfMeasurement, decorators: [{
|
|
84
|
+
type: Injectable,
|
|
85
|
+
args: [{ providedIn: 'root' }]
|
|
86
|
+
}] });
|
|
87
|
+
/** Measurements, with their settings: `withMeasurement({ defaultScale: 'imperial' })`. */
|
|
88
|
+
function withMeasurement(options) {
|
|
89
|
+
return { plugins: [measurementPlugin(options)], services: [EpdfMeasurement] };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @embedpdf/angular/measurement: distances, perimeters and areas at a real-world scale.
|
|
94
|
+
*
|
|
95
|
+
* withMeasurement(options) the plugin, for provideEmbedPdf()
|
|
96
|
+
* inject(EpdfMeasurement) setPreset(), calibrate(), createMeasurement(), busy(),
|
|
97
|
+
* calibrationRequest(), scaleOf(page), readoutOf(ref), the events
|
|
98
|
+
*/
|
|
99
|
+
// The plugin's types and helpers, so app code has one import for the feature.
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Generated bundle index. Do not edit.
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
export { EpdfMeasurement, withMeasurement };
|
|
106
|
+
//# sourceMappingURL=embedpdf-angular-measurement.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedpdf-angular-measurement.mjs","sources":["../../measurement/src/measurement.ts","../../measurement/src/public_api.ts","../../measurement/src/embedpdf-angular-measurement.ts"],"sourcesContent":["/**\n * The measurement plugin's service and feature:\n *\n * withMeasurement(options) the plugin, for provideEmbedPdf()\n * inject(EpdfMeasurement) the scales, calibration and measuring, the State table as\n * signals (`busy()`, `calibrationRequest()`, `lastReports()`), a\n * page's scale (`scaleOf(page)`), a measurement's readout\n * (`readoutOf(ref)`), the events as streams, and the settings\n */\nimport { computed, Injectable, type Signal } from '@angular/core';\nimport type { PageRef } from '@embedpdf/core';\nimport { pluginService, type EmbedPdfFeature } from '@embedpdf/angular/runtime';\nimport {\n measurementPlugin,\n measurementState,\n MeasurementToken,\n NO_PAGE_SCALE,\n NO_READOUT,\n type AnnotationRef,\n type MeasurementConfig,\n type MeasurementReadout,\n type MeasurementUnavailable,\n type PageScale,\n} from '@embedpdf/plugin-measurement';\n\n/** A page, or a function that gives the page to follow. */\ntype PageInput<Page> = Page | (() => Page);\n\n/**\n * Measurements of the document in scope (`[epdfDocumentScope]`), else the active one: the\n * Measurements page's methods, its State table as signals, a page's scale and a measurement's\n * readout as signals, the events as streams (`scaleChanged$`, `calibrationRequested$`, …), and\n * the settings. Without a document the signals read empty and the methods refuse with\n * `not-ready`.\n */\n@Injectable({ providedIn: 'root' })\nexport class EpdfMeasurement extends pluginService({\n name: 'EpdfMeasurement',\n feature: 'withMeasurement()',\n token: MeasurementToken,\n state: measurementState,\n methods: [\n 'listPresets',\n 'setPreset',\n 'setScale',\n 'clearScale',\n 'startCalibration',\n 'calibrate',\n 'dismissCalibration',\n 'setUnit',\n 'setAreaUnit',\n 'setPrecision',\n 'listUnits',\n 'listAreaUnits',\n 'getPageScale',\n 'ensureLoaded',\n 'getReadout',\n 'measureDistance',\n 'measureArea',\n 'createMeasurement',\n 'canCalibrate',\n 'canMeasure',\n 'isBusy',\n 'getCalibrationRequest',\n 'listLastReports',\n ],\n events: [\n 'onScaleChanged',\n 'onCalibrationRequested',\n 'onCalibrationCompleted',\n 'onCalibrationDismissed',\n ],\n}) {\n /**\n * A page's scale (its ref or its index), for a scale bar or a \"1:100\" badge: where it came\n * from, and whether it has loaded. Pass a function (`() => this.page().ref`) and it follows\n * the page you show; `null` (no page) answers `null`, so a badge can stay without inventing a\n * page. It changes only when that page's scale does.\n */\n scaleOf(page: PageInput<PageRef | number>): Signal<PageScale>;\n scaleOf(page: PageInput<PageRef | number | null>): Signal<PageScale | null>;\n scaleOf(page: PageInput<PageRef | number | null>): Signal<PageScale | null> {\n const pageOf = typeof page === 'function' ? page : () => page;\n // The plugin hands out the same scale until it changes. With no document there is none to\n // read, but a page asked for still gets an answer: a scale that hasn't loaded.\n const scale = this.binding.select(\n (measurement) => {\n const wanted = pageOf();\n return wanted === null ? NO_PAGE_SCALE : measurement.getPageScale(wanted);\n },\n NO_PAGE_SCALE,\n Object.is,\n );\n return computed(() => (pageOf() === null ? null : scale()));\n }\n\n /**\n * A measurement's `{ kind, value, label }`, or `{ unavailable }` with why there's none (no\n * scale, a scale from map software). Pass a function (`this.annotation`, an input) and it\n * follows the measurement you show. The plugin hands out the same readout until the\n * measurement changes, so it changes only then.\n */\n readoutOf(\n ref: AnnotationRef | (() => AnnotationRef),\n ): Signal<MeasurementReadout | MeasurementUnavailable> {\n const refOf = typeof ref === 'function' ? ref : () => ref;\n return this.binding.select<MeasurementReadout | MeasurementUnavailable>(\n (measurement) => measurement.getReadout(refOf()),\n NO_READOUT,\n );\n }\n}\n\n/** Measurements, with their settings: `withMeasurement({ defaultScale: 'imperial' })`. */\nexport function withMeasurement(options?: MeasurementConfig): EmbedPdfFeature {\n return { plugins: [measurementPlugin(options)], services: [EpdfMeasurement] };\n}\n","/**\n * @embedpdf/angular/measurement: distances, perimeters and areas at a real-world scale.\n *\n * withMeasurement(options) the plugin, for provideEmbedPdf()\n * inject(EpdfMeasurement) setPreset(), calibrate(), createMeasurement(), busy(),\n * calibrationRequest(), scaleOf(page), readoutOf(ref), the events\n */\n\n// The plugin's types and helpers, so app code has one import for the feature.\nexport * from '@embedpdf/plugin-measurement';\nexport * from './measurement';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":";;;;;;AAAA;;;;;;;;AAQG;AAoBH;;;;;;AAMG;AAEG,MAAO,eAAgB,SAAQ,aAAa,CAAC;AACjD,IAAA,IAAI,EAAE,iBAAiB;AACvB,IAAA,OAAO,EAAE,mBAAmB;AAC5B,IAAA,KAAK,EAAE,gBAAgB;AACvB,IAAA,KAAK,EAAE,gBAAgB;AACvB,IAAA,OAAO,EAAE;QACP,aAAa;QACb,WAAW;QACX,UAAU;QACV,YAAY;QACZ,kBAAkB;QAClB,WAAW;QACX,oBAAoB;QACpB,SAAS;QACT,aAAa;QACb,cAAc;QACd,WAAW;QACX,eAAe;QACf,cAAc;QACd,cAAc;QACd,YAAY;QACZ,iBAAiB;QACjB,aAAa;QACb,mBAAmB;QACnB,cAAc;QACd,YAAY;QACZ,QAAQ;QACR,uBAAuB;QACvB,iBAAiB;AAClB,KAAA;AACD,IAAA,MAAM,EAAE;QACN,gBAAgB;QAChB,wBAAwB;QACxB,wBAAwB;QACxB,wBAAwB;AACzB,KAAA;CACF,CAAC,CAAA;AASA,IAAA,OAAO,CAAC,IAAwC,EAAA;AAC9C,QAAA,MAAM,MAAM,GAAG,OAAO,IAAI,KAAK,UAAU,GAAG,IAAI,GAAG,MAAM,IAAI;;;QAG7D,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAC/B,CAAC,WAAW,KAAI;AACd,YAAA,MAAM,MAAM,GAAG,MAAM,EAAE;AACvB,YAAA,OAAO,MAAM,KAAK,IAAI,GAAG,aAAa,GAAG,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC;AAC3E,QAAA,CAAC,EACD,aAAa,EACb,MAAM,CAAC,EAAE,CACV;QACD,OAAO,QAAQ,CAAC,OAAO,MAAM,EAAE,KAAK,IAAI,GAAG,IAAI,GAAG,KAAK,EAAE,CAAC,CAAC;IAC7D;AAEA;;;;;AAKG;AACH,IAAA,SAAS,CACP,GAA0C,EAAA;AAE1C,QAAA,MAAM,KAAK,GAAG,OAAO,GAAG,KAAK,UAAU,GAAG,GAAG,GAAG,MAAM,GAAG;QACzD,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CACxB,CAAC,WAAW,KAAK,WAAW,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,EAChD,UAAU,CACX;IACH;uGA1EW,eAAe,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAf,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,eAAe,cADF,MAAM,EAAA,CAAA;;2FACnB,eAAe,EAAA,UAAA,EAAA,CAAA;kBAD3B,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;AA8ElC;AACM,SAAU,eAAe,CAAC,OAA2B,EAAA;AACzD,IAAA,OAAO,EAAE,OAAO,EAAE,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,eAAe,CAAC,EAAE;AAC/E;;ACpHA;;;;;;AAMG;AAEH;;ACRA;;AAEG;;;;"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { metadataState, MetadataToken, metadataPlugin } from '@embedpdf/plugin-metadata';
|
|
2
|
+
export * from '@embedpdf/plugin-metadata';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { Injectable } from '@angular/core';
|
|
5
|
+
import { pluginService } from '@embedpdf/angular/runtime';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The metadata plugin's service and feature. The plugin keeps the metadata live off the
|
|
9
|
+
* document's event stream, so the signals follow your own changes and other sessions' alike:
|
|
10
|
+
*
|
|
11
|
+
* protected readonly metadata = inject(EpdfMetadata);
|
|
12
|
+
* title = computed(() => this.metadata.fields()?.title ?? '');
|
|
13
|
+
* await this.metadata.update({ title: 'New title' }); // resolves once fields() shows it
|
|
14
|
+
*
|
|
15
|
+
* The service is both the plugin's API and its state, so the plugin's State table reads a little
|
|
16
|
+
* differently here: the standard fields are `fields()` (React's `metadata`), and your own fields
|
|
17
|
+
* are the `custom` namespace with its own `fields()` and `status()`. A namespace is never itself
|
|
18
|
+
* a signal.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* The metadata of the document in scope (`[epdfDocumentScope]`), else the active one: the
|
|
22
|
+
* standard fields as `fields()`, their load state as `status()`, `update()`, `refresh()`,
|
|
23
|
+
* `ensureLoaded()` (the service calls it for the document it reads), the
|
|
24
|
+
* check `canUpdate()`, the streams `updated$` and `resynced$`, and your own fields as `custom`.
|
|
25
|
+
* With no document the signals read null and `'idle'`, and every call refuses with `not-ready`.
|
|
26
|
+
*/
|
|
27
|
+
class EpdfMetadata extends pluginService({
|
|
28
|
+
name: 'EpdfMetadata',
|
|
29
|
+
feature: 'withMetadata()',
|
|
30
|
+
token: MetadataToken,
|
|
31
|
+
// Reading the fields loads them: nothing reads them as a document opens.
|
|
32
|
+
ensure: metadataState.ensure,
|
|
33
|
+
methods: ['update', 'refresh', 'ensureLoaded', 'canUpdate', 'getSnapshot', 'getStatus'],
|
|
34
|
+
events: ['onUpdated', 'onResynced'],
|
|
35
|
+
}) {
|
|
36
|
+
/** Every standard field (`title`, `author`, …), or null until it has loaded. */
|
|
37
|
+
fields = this.binding.select((metadata) => metadata.getSnapshot(), null, Object.is);
|
|
38
|
+
/** Whether the standard fields are still loading, there, or not allowed. */
|
|
39
|
+
status = this.binding.select((metadata) => metadata.getStatus(), 'idle', Object.is);
|
|
40
|
+
/** Your own fields, kept in the document: a contract number, who reviewed it. */
|
|
41
|
+
custom = {
|
|
42
|
+
fields: this.binding.select((metadata) => metadata.custom.getSnapshot(), null, Object.is),
|
|
43
|
+
status: this.binding.select((metadata) => metadata.custom.getStatus(), 'idle', Object.is),
|
|
44
|
+
update: this.binding.namespaceMethod('custom', 'update'),
|
|
45
|
+
refresh: this.binding.namespaceMethod('custom', 'refresh'),
|
|
46
|
+
getSnapshot: this.binding.namespaceMethod('custom', 'getSnapshot'),
|
|
47
|
+
getStatus: this.binding.namespaceMethod('custom', 'getStatus'),
|
|
48
|
+
updated$: this.binding.stream((metadata) => metadata.custom.onUpdated),
|
|
49
|
+
resynced$: this.binding.stream((metadata) => metadata.custom.onResynced),
|
|
50
|
+
};
|
|
51
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfMetadata, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
52
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfMetadata, providedIn: 'root' });
|
|
53
|
+
}
|
|
54
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfMetadata, decorators: [{
|
|
55
|
+
type: Injectable,
|
|
56
|
+
args: [{ providedIn: 'root' }]
|
|
57
|
+
}] });
|
|
58
|
+
/** Metadata, for `provideEmbedPdf()`. */
|
|
59
|
+
function withMetadata() {
|
|
60
|
+
return { plugins: [metadataPlugin()], services: [EpdfMetadata] };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @embedpdf/angular/metadata: a document's properties (title, author, dates) and your own fields.
|
|
65
|
+
*
|
|
66
|
+
* withMetadata() the plugin, for provideEmbedPdf()
|
|
67
|
+
* inject(EpdfMetadata) fields(), status(), update(), canUpdate(), updated$, …
|
|
68
|
+
* metadata.custom your own fields: custom.fields(), custom.update(), custom.updated$, …
|
|
69
|
+
*/
|
|
70
|
+
// The plugin's types and helpers, so app code has one import for the feature.
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generated bundle index. Do not edit.
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
export { EpdfMetadata, withMetadata };
|
|
77
|
+
//# sourceMappingURL=embedpdf-angular-metadata.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"embedpdf-angular-metadata.mjs","sources":["../../metadata/src/metadata.ts","../../metadata/src/public_api.ts","../../metadata/src/embedpdf-angular-metadata.ts"],"sourcesContent":["/**\n * The metadata plugin's service and feature. The plugin keeps the metadata live off the\n * document's event stream, so the signals follow your own changes and other sessions' alike:\n *\n * protected readonly metadata = inject(EpdfMetadata);\n * title = computed(() => this.metadata.fields()?.title ?? '');\n * await this.metadata.update({ title: 'New title' }); // resolves once fields() shows it\n *\n * The service is both the plugin's API and its state, so the plugin's State table reads a little\n * differently here: the standard fields are `fields()` (React's `metadata`), and your own fields\n * are the `custom` namespace with its own `fields()` and `status()`. A namespace is never itself\n * a signal.\n */\nimport { Injectable, type Signal } from '@angular/core';\nimport { pluginService, type EmbedPdfFeature } from '@embedpdf/angular/runtime';\nimport type { CustomMetadata, DocumentMetadata, ResourceStatus } from '@embedpdf/angular/runtime';\nimport { metadataPlugin, metadataState, MetadataToken } from '@embedpdf/plugin-metadata';\nimport type {\n CustomMetadataCapability,\n CustomMetadataResyncedEvent,\n CustomMetadataUpdatedEvent,\n} from '@embedpdf/plugin-metadata';\nimport type { Observable } from 'rxjs';\n\n/** Your own fields of a document, as `metadata.custom`: the same reads and calls as the standard ones. */\nexport interface EpdfCustomMetadata {\n /** Your own fields, as `{ name: value }`, or null until they have loaded. */\n readonly fields: Signal<CustomMetadata | null>;\n /** `'idle'`, `'loading'`, `'ready'`, `'forbidden'` or `'error'`. */\n readonly status: Signal<ResourceStatus>;\n /** A string sets a field, `null` removes it, and the fields left out stay. Resolves `{ custom }`. */\n readonly update: CustomMetadataCapability['update'];\n /** Read your own fields from the file again. Rarely needed: they stay up to date by themselves. */\n readonly refresh: CustomMetadataCapability['refresh'];\n /** Your own fields, read once. */\n readonly getSnapshot: CustomMetadataCapability['getSnapshot'];\n /** Their load state, read once. */\n readonly getStatus: CustomMetadataCapability['getStatus'];\n /** Your own fields changed, by you or by someone else. */\n readonly updated$: Observable<CustomMetadataUpdatedEvent>;\n /** Your own fields were loaded from the file. */\n readonly resynced$: Observable<CustomMetadataResyncedEvent>;\n}\n\n/**\n * The metadata of the document in scope (`[epdfDocumentScope]`), else the active one: the\n * standard fields as `fields()`, their load state as `status()`, `update()`, `refresh()`,\n * `ensureLoaded()` (the service calls it for the document it reads), the\n * check `canUpdate()`, the streams `updated$` and `resynced$`, and your own fields as `custom`.\n * With no document the signals read null and `'idle'`, and every call refuses with `not-ready`.\n */\n@Injectable({ providedIn: 'root' })\nexport class EpdfMetadata extends pluginService({\n name: 'EpdfMetadata',\n feature: 'withMetadata()',\n token: MetadataToken,\n // Reading the fields loads them: nothing reads them as a document opens.\n ensure: metadataState.ensure,\n methods: ['update', 'refresh', 'ensureLoaded', 'canUpdate', 'getSnapshot', 'getStatus'],\n events: ['onUpdated', 'onResynced'],\n}) {\n /** Every standard field (`title`, `author`, …), or null until it has loaded. */\n readonly fields: Signal<DocumentMetadata | null> = this.binding.select(\n (metadata) => metadata.getSnapshot(),\n null,\n Object.is,\n );\n\n /** Whether the standard fields are still loading, there, or not allowed. */\n readonly status: Signal<ResourceStatus> = this.binding.select(\n (metadata) => metadata.getStatus(),\n 'idle',\n Object.is,\n );\n\n /** Your own fields, kept in the document: a contract number, who reviewed it. */\n readonly custom: EpdfCustomMetadata = {\n fields: this.binding.select((metadata) => metadata.custom.getSnapshot(), null, Object.is),\n status: this.binding.select((metadata) => metadata.custom.getStatus(), 'idle', Object.is),\n update: this.binding.namespaceMethod('custom', 'update'),\n refresh: this.binding.namespaceMethod('custom', 'refresh'),\n getSnapshot: this.binding.namespaceMethod('custom', 'getSnapshot'),\n getStatus: this.binding.namespaceMethod('custom', 'getStatus'),\n updated$: this.binding.stream((metadata) => metadata.custom.onUpdated),\n resynced$: this.binding.stream((metadata) => metadata.custom.onResynced),\n };\n}\n\n/** Metadata, for `provideEmbedPdf()`. */\nexport function withMetadata(): EmbedPdfFeature {\n return { plugins: [metadataPlugin()], services: [EpdfMetadata] };\n}\n","/**\n * @embedpdf/angular/metadata: a document's properties (title, author, dates) and your own fields.\n *\n * withMetadata() the plugin, for provideEmbedPdf()\n * inject(EpdfMetadata) fields(), status(), update(), canUpdate(), updated$, …\n * metadata.custom your own fields: custom.fields(), custom.update(), custom.updated$, …\n */\n\n// The plugin's types and helpers, so app code has one import for the feature.\nexport * from '@embedpdf/plugin-metadata';\nexport * from './metadata';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public_api';\n"],"names":[],"mappings":";;;;;;AAAA;;;;;;;;;;;;AAYG;AAgCH;;;;;;AAMG;AAEG,MAAO,YAAa,SAAQ,aAAa,CAAC;AAC9C,IAAA,IAAI,EAAE,cAAc;AACpB,IAAA,OAAO,EAAE,gBAAgB;AACzB,IAAA,KAAK,EAAE,aAAa;;IAEpB,MAAM,EAAE,aAAa,CAAC,MAAM;AAC5B,IAAA,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,CAAC;AACvF,IAAA,MAAM,EAAE,CAAC,WAAW,EAAE,YAAY,CAAC;CACpC,CAAC,CAAA;;IAES,MAAM,GAAoC,IAAI,CAAC,OAAO,CAAC,MAAM,CACpE,CAAC,QAAQ,KAAK,QAAQ,CAAC,WAAW,EAAE,EACpC,IAAI,EACJ,MAAM,CAAC,EAAE,CACV;;IAGQ,MAAM,GAA2B,IAAI,CAAC,OAAO,CAAC,MAAM,CAC3D,CAAC,QAAQ,KAAK,QAAQ,CAAC,SAAS,EAAE,EAClC,MAAM,EACN,MAAM,CAAC,EAAE,CACV;;AAGQ,IAAA,MAAM,GAAuB;QACpC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;QACzF,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;QACzF,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,QAAQ,EAAE,QAAQ,CAAC;QACxD,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,QAAQ,EAAE,SAAS,CAAC;QAC1D,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,QAAQ,EAAE,aAAa,CAAC;QAClE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,QAAQ,EAAE,WAAW,CAAC;AAC9D,QAAA,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC;AACtE,QAAA,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,MAAM,CAAC,UAAU,CAAC;KACzE;uGAjCU,YAAY,EAAA,IAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,YAAY,cADC,MAAM,EAAA,CAAA;;2FACnB,YAAY,EAAA,UAAA,EAAA,CAAA;kBADxB,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;AAqClC;SACgB,YAAY,GAAA;AAC1B,IAAA,OAAO,EAAE,OAAO,EAAE,CAAC,cAAc,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,YAAY,CAAC,EAAE;AAClE;;AC3FA;;;;;;AAMG;AAEH;;ACRA;;AAEG;;;;"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { outlineState, OutlineToken, outlinePlugin } from '@embedpdf/plugin-outline';
|
|
2
|
+
export * from '@embedpdf/plugin-outline';
|
|
3
|
+
import * as i0 from '@angular/core';
|
|
4
|
+
import { Injectable } from '@angular/core';
|
|
5
|
+
import { pluginService } from '@embedpdf/angular/runtime';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The outline plugin's service and feature: the document's outline (its bookmarks). Reading it
|
|
9
|
+
* reads the outline; the plugin keeps it live off the document's event stream, and shows your
|
|
10
|
+
* own edits at once:
|
|
11
|
+
*
|
|
12
|
+
* protected readonly outline = inject(EpdfOutline);
|
|
13
|
+
* @for (row of outline.rows(); track row.item.ref.objectNumber) { … }
|
|
14
|
+
* (click)="outline.activate(row.item.ref)" goes where the item goes, as a click does
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The outline of the document in scope (`[epdfDocumentScope]`), else the active one: the State
|
|
18
|
+
* table as signals (`items()`, `rows()`, `current()`, `selected()`, `status()`, `incomplete()`,
|
|
19
|
+
* `canEdit()`), the reads that take an item (`get()`, `getChildren()`, `isExpanded()`), the
|
|
20
|
+
* calls that expand, select, follow and edit items, and the streams `changed$` and
|
|
21
|
+
* `resynced$`. Reading it reads the outline. With no document the signals read empty, and every
|
|
22
|
+
* call refuses with `not-ready`.
|
|
23
|
+
*/
|
|
24
|
+
class EpdfOutline extends pluginService({
|
|
25
|
+
name: 'EpdfOutline',
|
|
26
|
+
feature: 'withOutline()',
|
|
27
|
+
token: OutlineToken,
|
|
28
|
+
state: outlineState,
|
|
29
|
+
methods: [
|
|
30
|
+
'get',
|
|
31
|
+
'getChildren',
|
|
32
|
+
'isExpanded',
|
|
33
|
+
'getCurrentViewTarget',
|
|
34
|
+
'ensureLoaded',
|
|
35
|
+
'refresh',
|
|
36
|
+
'expand',
|
|
37
|
+
'collapse',
|
|
38
|
+
'toggle',
|
|
39
|
+
'expandAll',
|
|
40
|
+
'collapseAll',
|
|
41
|
+
'reveal',
|
|
42
|
+
'select',
|
|
43
|
+
'activate',
|
|
44
|
+
'create',
|
|
45
|
+
'update',
|
|
46
|
+
'move',
|
|
47
|
+
'delete',
|
|
48
|
+
],
|
|
49
|
+
events: ['onChanged', 'onResynced'],
|
|
50
|
+
}) {
|
|
51
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfOutline, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
52
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfOutline, providedIn: 'root' });
|
|
53
|
+
}
|
|
54
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.4", ngImport: i0, type: EpdfOutline, decorators: [{
|
|
55
|
+
type: Injectable,
|
|
56
|
+
args: [{ providedIn: 'root' }]
|
|
57
|
+
}] });
|
|
58
|
+
/** The outline, for `provideEmbedPdf()`. */
|
|
59
|
+
function withOutline() {
|
|
60
|
+
return { plugins: [outlinePlugin()], services: [EpdfOutline] };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @embedpdf/angular/outline: the document's outline (its bookmarks).
|
|
65
|
+
*
|
|
66
|
+
* withOutline() the plugin, for provideEmbedPdf()
|
|
67
|
+
* inject(EpdfOutline) rows(), current(), canEdit(), activate(), create(), changed$, …
|
|
68
|
+
*/
|
|
69
|
+
// The plugin's types and helpers, so app code has one import for the feature.
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Generated bundle index. Do not edit.
|
|
73
|
+
*/
|
|
74
|
+
|
|
75
|
+
export { EpdfOutline, withOutline };
|
|
76
|
+
//# sourceMappingURL=embedpdf-angular-outline.mjs.map
|