vue-pdfjs-headless 4.4.0 → 4.6.0
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 +19 -15
- package/dist/index.d.ts +211 -0
- package/dist/index.js +898 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[![NPM downloads][badge-npm-downloads]][url-npm]
|
|
6
6
|
[![NPM Unpacked Size (with version)][badge-npm-unpacked-size]][url-npm]
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
This package provides headless Vue composables over pdfjs-dist. The composables load a document, draw its pages and track the viewer's state, with no styling and no renderer of their own.
|
|
9
9
|
|
|
10
10
|
Design notes live in [the specs](https://github.com/Esposter/Esposter/tree/main/openspec/specs/vue-pdfjs-headless).
|
|
11
11
|
|
|
@@ -19,10 +19,14 @@ Design notes live in [the specs](https://github.com/Esposter/Esposter/tree/main/
|
|
|
19
19
|
|
|
20
20
|
## <a name="getting-started">🚀 Getting Started</a>
|
|
21
21
|
|
|
22
|
+
1. Install the package.
|
|
23
|
+
|
|
22
24
|
```bash
|
|
23
25
|
pnpm i vue-pdfjs-headless
|
|
24
26
|
```
|
|
25
27
|
|
|
28
|
+
2. Add this code to the `<script setup>` of a component.
|
|
29
|
+
|
|
26
30
|
```ts
|
|
27
31
|
import * as pdfjs from "pdfjs-dist";
|
|
28
32
|
import { shallowRef } from "vue";
|
|
@@ -42,23 +46,23 @@ const { pdfDocument } = usePdfDocument({
|
|
|
42
46
|
usePdfPage({ canvas, pageNumber: 1, pdfDocument });
|
|
43
47
|
```
|
|
44
48
|
|
|
45
|
-
Bind `canvas` to a `<canvas ref="canvas" />` in the
|
|
49
|
+
3. Bind `canvas` to a `<canvas ref="canvas" />` in the template. The first page appears in the canvas.
|
|
46
50
|
|
|
47
51
|
## <a name="key-exports">🧩 Key Exports</a>
|
|
48
52
|
|
|
49
|
-
| Export | Kind | Purpose
|
|
50
|
-
| ------------------- | ---------- |
|
|
51
|
-
| `PdfRenderer` | interface | The renderer
|
|
52
|
-
| `PdfSource` | type | A document by its URL, its bytes
|
|
53
|
-
| `PdfViewerState` | interface | The viewer's pages, zoom, rotation, search, download and print, as state and actions
|
|
54
|
-
| `readXfdf` | function | Reads the free-text annotations of an XFDF file, or an error
|
|
55
|
-
| `usePdfAnnotations` | composable |
|
|
56
|
-
| `usePdfDocument` | composable | Loads a document from a source through the consumer's renderer
|
|
57
|
-
| `usePdfPage` | composable | Draws one page of a loaded document into a canvas, at the canvas's width and the device's pixel ratio
|
|
58
|
-
| `usePdfThumbnails` | composable | One thumbnail per page
|
|
59
|
-
| `usePdfViewer` | composable | Builds the renderer's viewer for a loaded document
|
|
60
|
-
| `usePdfZoomGesture` | composable |
|
|
61
|
-
| `writeXfdf` | function | Writes free-text annotations as XFDF
|
|
53
|
+
| Export | Kind | Purpose |
|
|
54
|
+
| ------------------- | ---------- | -------------------------------------------------------------------------------------------------------------- |
|
|
55
|
+
| `PdfRenderer` | interface | The renderer: its API module, its data folder URLs and its worker. The consumer supplies it. |
|
|
56
|
+
| `PdfSource` | type | A document, given by its URL, its bytes or a file that the reader picked |
|
|
57
|
+
| `PdfViewerState` | interface | The viewer's pages, zoom, rotation, search, download and print, as state and actions |
|
|
58
|
+
| `readXfdf` | function | Reads the free-text annotations of an XFDF file, or returns an error that names what is wrong with it |
|
|
59
|
+
| `usePdfAnnotations` | composable | Lists the reader's comments once they are wanted, and imports and exports free texts as XFDF |
|
|
60
|
+
| `usePdfDocument` | composable | Loads a document from a source through the consumer's renderer. Its password prompt and its failure are state. |
|
|
61
|
+
| `usePdfPage` | composable | Draws one page of a loaded document into a canvas, at the canvas's width and the device's pixel ratio |
|
|
62
|
+
| `usePdfThumbnails` | composable | One thumbnail per page. A thumbnail's page is loaded and drawn only while its row is in view of the sidebar. |
|
|
63
|
+
| `usePdfViewer` | composable | Builds the renderer's viewer for a loaded document. Its pages, zoom and search are state. |
|
|
64
|
+
| `usePdfZoomGesture` | composable | Zooms the viewer about the pointer on a pinch or a Ctrl-wheel. The content under the pointer stays in place. |
|
|
65
|
+
| `writeXfdf` | function | Writes free-text annotations as XFDF |
|
|
62
66
|
|
|
63
67
|
## <a name="license">⚖️ License</a>
|
|
64
68
|
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { Result } from "neverthrow";
|
|
2
|
+
import { ComputedRef, MaybeRefOrGetter, Ref, ShallowRef } from "vue";
|
|
3
|
+
import * as PdfJs from "pdfjs-dist";
|
|
4
|
+
import { PDFDocumentLoadingTask, PDFDocumentProxy, PDFPageProxy, PageViewport } from "pdfjs-dist";
|
|
5
|
+
import * as PdfJsViewer from "pdfjs-dist/web/pdf_viewer.mjs";
|
|
6
|
+
import { PDFViewer } from "pdfjs-dist/web/pdf_viewer.mjs";
|
|
7
|
+
export declare enum PdfEditorMode {
|
|
8
|
+
None = 0,
|
|
9
|
+
FreeText = 3,
|
|
10
|
+
Highlight = 9,
|
|
11
|
+
Stamp = 13,
|
|
12
|
+
Ink = 15
|
|
13
|
+
}
|
|
14
|
+
export interface PdfAnnotationChange {
|
|
15
|
+
kind: PdfEditorMode;
|
|
16
|
+
pageNumber: number;
|
|
17
|
+
}
|
|
18
|
+
export interface PdfComment {
|
|
19
|
+
contents: string;
|
|
20
|
+
pageNumber: number;
|
|
21
|
+
}
|
|
22
|
+
export interface PdfAnnotationsState {
|
|
23
|
+
comments: ShallowRef<PdfComment[] | undefined>;
|
|
24
|
+
exportXfdf: () => string;
|
|
25
|
+
importXfdf: (xfdf: string) => Result<void, string>;
|
|
26
|
+
setEditorMode: (mode: PdfEditorMode) => void;
|
|
27
|
+
}
|
|
28
|
+
export interface PdfMatchesCount {
|
|
29
|
+
current: number;
|
|
30
|
+
total: number;
|
|
31
|
+
}
|
|
32
|
+
export type PdfOutline = NonNullable<Awaited<ReturnType<PDFDocumentProxy["getOutline"]>>>;
|
|
33
|
+
export interface PdfPageOverlay {
|
|
34
|
+
element: HTMLElement;
|
|
35
|
+
pageNumber: number;
|
|
36
|
+
viewport: PageViewport;
|
|
37
|
+
}
|
|
38
|
+
export interface PdfViewerState {
|
|
39
|
+
actualSize: () => void;
|
|
40
|
+
currentPageNumber: Ref<number>;
|
|
41
|
+
download: (filename: string, watermark?: string) => void;
|
|
42
|
+
drawStoredEditors: () => void;
|
|
43
|
+
editorMode: Ref<PdfEditorMode>;
|
|
44
|
+
findNext: () => void;
|
|
45
|
+
findPrevious: () => void;
|
|
46
|
+
fitPage: () => void;
|
|
47
|
+
fitWidth: () => void;
|
|
48
|
+
followBookmark: (item: PdfOutline[number]) => void;
|
|
49
|
+
goToPage: (pageNumber: number) => void;
|
|
50
|
+
isMatchCase: Ref<boolean>;
|
|
51
|
+
isNotFound: Ref<boolean>;
|
|
52
|
+
isWholeWord: Ref<boolean>;
|
|
53
|
+
isZoomInDisabled: ComputedRef<boolean>;
|
|
54
|
+
isZoomOutDisabled: ComputedRef<boolean>;
|
|
55
|
+
matchesCount: Ref<PdfMatchesCount>;
|
|
56
|
+
nextPage: () => void;
|
|
57
|
+
outline: ShallowRef<PdfOutline | undefined>;
|
|
58
|
+
overlays: ShallowRef<PdfPageOverlay[]>;
|
|
59
|
+
pagesCount: Ref<number>;
|
|
60
|
+
pagesRotation: Ref<number>;
|
|
61
|
+
previousPage: () => void;
|
|
62
|
+
print: () => void;
|
|
63
|
+
resetZoom: () => void;
|
|
64
|
+
rotate: (degrees: number) => void;
|
|
65
|
+
scale: Ref<number>;
|
|
66
|
+
searchQuery: Ref<string>;
|
|
67
|
+
zoomIn: () => void;
|
|
68
|
+
zoomOut: () => void;
|
|
69
|
+
}
|
|
70
|
+
interface UsePdfAnnotationsOptions {
|
|
71
|
+
isCommentsWanted?: MaybeRefOrGetter<boolean>;
|
|
72
|
+
onChange?: (change: PdfAnnotationChange) => void;
|
|
73
|
+
pdfDocument: MaybeRefOrGetter<PDFDocumentProxy | undefined>;
|
|
74
|
+
viewer: PdfViewerState;
|
|
75
|
+
}
|
|
76
|
+
export declare const usePdfAnnotations: ({ isCommentsWanted, onChange, pdfDocument, viewer }: UsePdfAnnotationsOptions) => PdfAnnotationsState;
|
|
77
|
+
export type PdfApi = Pick<typeof PdfJs, "getDocument" | "GlobalWorkerOptions" | "PasswordResponses">;
|
|
78
|
+
export type PdfDocumentParameters = Partial<Parameters<PdfApi["getDocument"]>[0]>;
|
|
79
|
+
export interface PdfLoadingProgress {
|
|
80
|
+
loaded: number;
|
|
81
|
+
total: number;
|
|
82
|
+
}
|
|
83
|
+
export interface PdfDocumentState {
|
|
84
|
+
isFailed: Ref<boolean>;
|
|
85
|
+
isPasswordRequested: Ref<boolean>;
|
|
86
|
+
isPasswordWrong: Ref<boolean>;
|
|
87
|
+
pdfDocument: ShallowRef<PDFDocumentProxy | undefined>;
|
|
88
|
+
progress: Ref<PdfLoadingProgress>;
|
|
89
|
+
submitPassword: (password: string) => void;
|
|
90
|
+
}
|
|
91
|
+
export interface PdfRenderer {
|
|
92
|
+
api: PdfApi;
|
|
93
|
+
cMapUrl: string;
|
|
94
|
+
standardFontDataUrl: string;
|
|
95
|
+
wasmUrl: string;
|
|
96
|
+
workerSrc: string;
|
|
97
|
+
}
|
|
98
|
+
export type PdfSource = File | string | Uint8Array;
|
|
99
|
+
interface UsePdfDocumentOptions {
|
|
100
|
+
parameters?: PdfDocumentParameters;
|
|
101
|
+
renderer: MaybeRefOrGetter<PdfRenderer | undefined>;
|
|
102
|
+
source: MaybeRefOrGetter<PdfSource | undefined>;
|
|
103
|
+
}
|
|
104
|
+
export declare const usePdfDocument: ({ parameters, renderer, source }: UsePdfDocumentOptions) => PdfDocumentState;
|
|
105
|
+
interface UsePdfPageOptions {
|
|
106
|
+
canvas: MaybeRefOrGetter<HTMLCanvasElement | undefined>;
|
|
107
|
+
isActive?: MaybeRefOrGetter<boolean>;
|
|
108
|
+
isVisible?: MaybeRefOrGetter<boolean>;
|
|
109
|
+
pageNumber: MaybeRefOrGetter<number>;
|
|
110
|
+
pagesRotation?: MaybeRefOrGetter<number>;
|
|
111
|
+
pdfDocument: MaybeRefOrGetter<PDFDocumentProxy | undefined>;
|
|
112
|
+
}
|
|
113
|
+
export declare const usePdfPage: ({ canvas, isActive, isVisible, pageNumber, pagesRotation, pdfDocument }: UsePdfPageOptions) => void;
|
|
114
|
+
export interface PdfThumbnail {
|
|
115
|
+
canvas: ShallowRef<HTMLCanvasElement | undefined>;
|
|
116
|
+
isCurrent: ComputedRef<boolean>;
|
|
117
|
+
isVisible: Ref<boolean>;
|
|
118
|
+
pageNumber: number;
|
|
119
|
+
}
|
|
120
|
+
interface UsePdfThumbnailsOptions {
|
|
121
|
+
currentPageNumber: MaybeRefOrGetter<number>;
|
|
122
|
+
pagesRotation: MaybeRefOrGetter<number>;
|
|
123
|
+
pdfDocument: MaybeRefOrGetter<PDFDocumentProxy | undefined>;
|
|
124
|
+
root: MaybeRefOrGetter<HTMLElement | undefined>;
|
|
125
|
+
}
|
|
126
|
+
export declare const usePdfThumbnails: ({ currentPageNumber, pagesRotation, pdfDocument, root }: UsePdfThumbnailsOptions) => ShallowRef<PdfThumbnail[]>;
|
|
127
|
+
export type PdfViewerApi = Pick<typeof PdfJsViewer, "EventBus" | "FindState" | "LinkTarget" | "PDFFindController" | "PDFLinkService" | "PDFViewer">;
|
|
128
|
+
interface UsePdfViewerOptions {
|
|
129
|
+
container: MaybeRefOrGetter<HTMLDivElement | undefined>;
|
|
130
|
+
highlightTerms?: MaybeRefOrGetter<string[]>;
|
|
131
|
+
imageResourcesPath: string;
|
|
132
|
+
pdfDocument: MaybeRefOrGetter<PDFDocumentProxy | undefined>;
|
|
133
|
+
viewer: MaybeRefOrGetter<HTMLDivElement | undefined>;
|
|
134
|
+
viewerApi: MaybeRefOrGetter<PdfViewerApi | undefined>;
|
|
135
|
+
}
|
|
136
|
+
export declare const usePdfViewer: ({ container, highlightTerms, imageResourcesPath, pdfDocument, viewer, viewerApi }: UsePdfViewerOptions) => PdfViewerState;
|
|
137
|
+
interface UsePdfZoomGestureOptions {
|
|
138
|
+
container: MaybeRefOrGetter<HTMLDivElement | undefined>;
|
|
139
|
+
getViewer: () => PDFViewer | undefined;
|
|
140
|
+
}
|
|
141
|
+
export declare const usePdfZoomGesture: ({ container, getViewer }: UsePdfZoomGestureOptions) => void;
|
|
142
|
+
export interface PdfFreeText {
|
|
143
|
+
contents: string;
|
|
144
|
+
id: string;
|
|
145
|
+
pageNumber: number;
|
|
146
|
+
rect: [number, number, number, number];
|
|
147
|
+
}
|
|
148
|
+
export interface PdfFreeTextEntry {
|
|
149
|
+
annotationType: PdfEditorMode.FreeText;
|
|
150
|
+
color: number[];
|
|
151
|
+
deleted: false;
|
|
152
|
+
fontSize: number;
|
|
153
|
+
id: string;
|
|
154
|
+
pageIndex: number;
|
|
155
|
+
rect: [number, number, number, number];
|
|
156
|
+
rotation: 0;
|
|
157
|
+
value: string;
|
|
158
|
+
}
|
|
159
|
+
export declare const PDF_MAX_CANVAS_AREA = 16777216;
|
|
160
|
+
export declare const PDF_MIN_SCALE = .1;
|
|
161
|
+
export declare const PDF_MAX_SCALE = 10;
|
|
162
|
+
export declare const PDF_ZOOM_STEP = 1.1;
|
|
163
|
+
export declare const PDF_OPENING_SCALE = "auto";
|
|
164
|
+
export declare const PDF_ROTATION_STEP = 90;
|
|
165
|
+
export declare const PDF_FULL_TURN = 360;
|
|
166
|
+
export declare const PDF_PRINT_SCALE = 2;
|
|
167
|
+
export declare const PDF_PRINT_BATCH_SIZE = 10;
|
|
168
|
+
export declare const PDF_PRINT_CLASS = "pdf-print";
|
|
169
|
+
export declare const PDF_MIME_TYPE = "application/pdf";
|
|
170
|
+
export declare const PDF_EDITOR_KEY_PREFIX = "pdfjs_internal_editor_";
|
|
171
|
+
export declare const PDF_FREE_TEXT_FONT_SIZE = 12;
|
|
172
|
+
export declare const PDF_FREE_TEXT_COLOR: number[];
|
|
173
|
+
export declare const PDF_WATERMARK_FONT_SIZE = 48;
|
|
174
|
+
export declare const PDF_WATERMARK_COLOR: number[];
|
|
175
|
+
export declare const PDF_PRINT_STYLE: string;
|
|
176
|
+
export declare const PDF_WHEEL_ZOOM_DIVISOR = 500;
|
|
177
|
+
interface CreateFreeTextEntryParameters {
|
|
178
|
+
color: number[];
|
|
179
|
+
fontSize: number;
|
|
180
|
+
id: string;
|
|
181
|
+
pageIndex: number;
|
|
182
|
+
rect: [number, number, number, number];
|
|
183
|
+
value: string;
|
|
184
|
+
}
|
|
185
|
+
export declare const createFreeTextEntry: ({ color, fontSize, id, pageIndex, rect, value }: CreateFreeTextEntryParameters) => PdfFreeTextEntry;
|
|
186
|
+
export declare const destroyLoadingTask: (task: PDFDocumentLoadingTask) => void;
|
|
187
|
+
interface Props {
|
|
188
|
+
cssWidth: number;
|
|
189
|
+
pageHeight: number;
|
|
190
|
+
pageWidth: number;
|
|
191
|
+
pixelRatio: number;
|
|
192
|
+
}
|
|
193
|
+
export declare const getCanvasScale: ({ cssWidth, pageHeight, pageWidth, pixelRatio }: Props) => number;
|
|
194
|
+
export declare const getDownloadBytes: (pdfDocument: PDFDocumentProxy, watermark: string | undefined) => Promise<Uint8Array<ArrayBuffer>>;
|
|
195
|
+
export declare const getPdfPages: (pdfDocument: PDFDocumentProxy) => Promise<PDFPageProxy[]>;
|
|
196
|
+
export declare const getSourceParameters: (source: PdfSource) => Promise<{
|
|
197
|
+
data: Uint8Array;
|
|
198
|
+
} | {
|
|
199
|
+
url: string;
|
|
200
|
+
}>;
|
|
201
|
+
export declare const getZoomedScroll: ({ point, ratio, scroll }: {
|
|
202
|
+
point: number;
|
|
203
|
+
ratio: number;
|
|
204
|
+
scroll: number;
|
|
205
|
+
}) => number;
|
|
206
|
+
export declare const loadPdfDocument: (renderer: PdfRenderer, source: PdfSource, parameters: PdfDocumentParameters) => Promise<PDFDocumentLoadingTask>;
|
|
207
|
+
export declare const renderPrintImage: (pdfDocument: PDFDocumentProxy, pageNumber: number) => Promise<HTMLImageElement>;
|
|
208
|
+
export declare const renderPrintSheets: (pdfDocument: PDFDocumentProxy, checkIsCancelled: () => boolean) => Promise<HTMLDivElement | undefined>;
|
|
209
|
+
export declare const readXfdf: (xfdf: string, takenIds: ReadonlySet<string>) => Result<PdfFreeText[], string>;
|
|
210
|
+
export declare const writeXfdf: (freeTexts: PdfFreeText[]) => string;
|
|
211
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
import { Result, err, ok } from "neverthrow";
|
|
2
|
+
import { downloadFile, getResultAsync, getSynchronizedFunction, noop, withFinalizerAsync } from "@esposter/shared";
|
|
3
|
+
import { computed, effectScope, onScopeDispose, ref, shallowRef, toValue, watch } from "vue";
|
|
4
|
+
// src/models/PdfEditorMode.ts
|
|
5
|
+
let PdfEditorMode = /* @__PURE__ */ function(PdfEditorMode) {
|
|
6
|
+
PdfEditorMode[PdfEditorMode["None"] = 0] = "None";
|
|
7
|
+
PdfEditorMode[PdfEditorMode["FreeText"] = 3] = "FreeText";
|
|
8
|
+
PdfEditorMode[PdfEditorMode["Highlight"] = 9] = "Highlight";
|
|
9
|
+
PdfEditorMode[PdfEditorMode["Stamp"] = 13] = "Stamp";
|
|
10
|
+
PdfEditorMode[PdfEditorMode["Ink"] = 15] = "Ink";
|
|
11
|
+
return PdfEditorMode;
|
|
12
|
+
}({});
|
|
13
|
+
// src/services/constants.ts
|
|
14
|
+
const PDF_MAX_CANVAS_AREA = 16777216;
|
|
15
|
+
const PDF_MIN_SCALE = .1;
|
|
16
|
+
const PDF_MAX_SCALE = 10;
|
|
17
|
+
const PDF_ZOOM_STEP = 1.1;
|
|
18
|
+
const PDF_OPENING_SCALE = "auto";
|
|
19
|
+
const PDF_ROTATION_STEP = 90;
|
|
20
|
+
const PDF_FULL_TURN = 360;
|
|
21
|
+
const PDF_PRINT_SCALE = 2;
|
|
22
|
+
const PDF_PRINT_BATCH_SIZE = 10;
|
|
23
|
+
const PDF_PRINT_CLASS = "pdf-print";
|
|
24
|
+
const PDF_MIME_TYPE = "application/pdf";
|
|
25
|
+
const PDF_EDITOR_KEY_PREFIX = "pdfjs_internal_editor_";
|
|
26
|
+
const PDF_FREE_TEXT_FONT_SIZE = 12;
|
|
27
|
+
const PDF_FREE_TEXT_COLOR = [
|
|
28
|
+
0,
|
|
29
|
+
0,
|
|
30
|
+
0
|
|
31
|
+
];
|
|
32
|
+
const PDF_WATERMARK_FONT_SIZE = 48;
|
|
33
|
+
const PDF_WATERMARK_COLOR = [
|
|
34
|
+
128,
|
|
35
|
+
128,
|
|
36
|
+
128
|
|
37
|
+
];
|
|
38
|
+
const PDF_PRINT_STYLE = `@page { margin: 3mm } .${PDF_PRINT_CLASS} { display: none } .${PDF_PRINT_CLASS} img { display: block; page-break-after: always; width: 100% } @media print { .${PDF_PRINT_CLASS} { display: block } body > :not(.${PDF_PRINT_CLASS}) { display: none !important } }`;
|
|
39
|
+
const PDF_WHEEL_ZOOM_DIVISOR = 500;
|
|
40
|
+
// src/services/createFreeTextEntry.ts
|
|
41
|
+
const createFreeTextEntry = ({ color, fontSize, id, pageIndex, rect, value }) => ({
|
|
42
|
+
annotationType: 3,
|
|
43
|
+
color,
|
|
44
|
+
deleted: false,
|
|
45
|
+
fontSize,
|
|
46
|
+
id,
|
|
47
|
+
pageIndex,
|
|
48
|
+
rect,
|
|
49
|
+
rotation: 0,
|
|
50
|
+
value
|
|
51
|
+
});
|
|
52
|
+
// src/services/getPdfPages.ts
|
|
53
|
+
const getPdfPages = (pdfDocument) => Promise.all(Array.from({ length: pdfDocument.numPages }, (_value, pageIndex) => pdfDocument.getPage(pageIndex + 1)));
|
|
54
|
+
// src/xfdf/readXfdf.ts
|
|
55
|
+
const parseCoordinate = (text) => text.trim() === "" ? NaN : Number(text);
|
|
56
|
+
const checkRectangle = (coordinates) => coordinates.length === 4 && coordinates.every((coordinate) => Number.isFinite(coordinate));
|
|
57
|
+
const readFreeText = (element, index, usedIds) => {
|
|
58
|
+
const page = element.getAttribute("page") ?? "";
|
|
59
|
+
const coordinates = (element.getAttribute("rect") ?? "").split(",").map((text) => parseCoordinate(text));
|
|
60
|
+
if (!/^\d+$/u.test(page)) return err(`Free text ${index + 1} has no valid page`);
|
|
61
|
+
if (!checkRectangle(coordinates)) return err(`Free text ${index + 1} has no valid rect`);
|
|
62
|
+
const contents = element.querySelector("contents")?.textContent ?? "";
|
|
63
|
+
if (contents.trim() === "") return err(`Free text ${index + 1} has no contents`);
|
|
64
|
+
const name = element.getAttribute("name") ?? "";
|
|
65
|
+
const id = name === "" || usedIds.has(name) ? crypto.randomUUID() : name;
|
|
66
|
+
usedIds.add(id);
|
|
67
|
+
return ok({
|
|
68
|
+
contents,
|
|
69
|
+
id,
|
|
70
|
+
pageNumber: Number(page) + 1,
|
|
71
|
+
rect: coordinates
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const readXfdf = (xfdf, takenIds) => {
|
|
75
|
+
const xfdfDocument = new DOMParser().parseFromString(xfdf, "application/xml");
|
|
76
|
+
if (xfdfDocument.querySelector("parsererror")) return err("The XFDF is not well-formed XML");
|
|
77
|
+
const usedIds = new Set(takenIds);
|
|
78
|
+
return Result.combine(Array.from(xfdfDocument.querySelectorAll("freetext"), (element, index) => readFreeText(element, index, usedIds)));
|
|
79
|
+
};
|
|
80
|
+
// src/xfdf/writeXfdf.ts
|
|
81
|
+
const escapeXml = (text) => text.replaceAll(/[\u0000-\u0008\u000B\u000C\u000E-\u001F]/gu, "").replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
82
|
+
const writeXfdf = (freeTexts) => {
|
|
83
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve"><annots>${freeTexts.map(({ contents, id, pageNumber, rect }) => `<freetext name="${escapeXml(id)}" page="${pageNumber - 1}" rect="${rect.join(",")}"><contents>${escapeXml(contents)}</contents></freetext>`).join("")}</annots></xfdf>`;
|
|
84
|
+
};
|
|
85
|
+
// src/composables/usePdfAnnotations.ts
|
|
86
|
+
const readComments = async (loaded) => {
|
|
87
|
+
const pages = await getPdfPages(loaded);
|
|
88
|
+
return (await Promise.all(pages.map(async (page, pageIndex) => {
|
|
89
|
+
const annotations = await page.getAnnotations();
|
|
90
|
+
page.cleanup();
|
|
91
|
+
return annotations.filter(({ subtype }) => subtype === "Text").map(({ contentsObj }) => ({
|
|
92
|
+
contents: contentsObj?.str ?? "",
|
|
93
|
+
pageNumber: pageIndex + 1
|
|
94
|
+
}));
|
|
95
|
+
}))).flat();
|
|
96
|
+
};
|
|
97
|
+
const setEditorHook = (loaded, onEditor) => {
|
|
98
|
+
Object.assign(loaded.annotationStorage, { onAnnotationEditor: onEditor });
|
|
99
|
+
};
|
|
100
|
+
const readFreeTexts = (loaded) => Array.from(loaded.annotationStorage, ([key, value]) => {
|
|
101
|
+
const data = typeof value.serialize === "function" ? value.serialize() : value;
|
|
102
|
+
if (data?.annotationType !== 3 || !data.rect) return [];
|
|
103
|
+
const id = data.id ?? key.slice(22);
|
|
104
|
+
return [{
|
|
105
|
+
contents: data.value ?? "",
|
|
106
|
+
id,
|
|
107
|
+
pageNumber: (data.pageIndex ?? 0) + 1,
|
|
108
|
+
rect: data.rect
|
|
109
|
+
}];
|
|
110
|
+
}).flat();
|
|
111
|
+
const storeFreeTexts = (loaded, freeTexts) => {
|
|
112
|
+
for (const { contents, id, pageNumber, rect } of freeTexts) loaded.annotationStorage.setValue(`${PDF_EDITOR_KEY_PREFIX}xfdf${id}`, {
|
|
113
|
+
...createFreeTextEntry({
|
|
114
|
+
color: PDF_FREE_TEXT_COLOR,
|
|
115
|
+
fontSize: 12,
|
|
116
|
+
id,
|
|
117
|
+
pageIndex: pageNumber - 1,
|
|
118
|
+
rect,
|
|
119
|
+
value: contents
|
|
120
|
+
}),
|
|
121
|
+
isClone: true,
|
|
122
|
+
isCopy: true
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
const usePdfAnnotations = ({ isCommentsWanted = false, onChange, pdfDocument, viewer }) => {
|
|
126
|
+
const comments = shallowRef();
|
|
127
|
+
const loadComments = getSynchronizedFunction(async (loaded) => {
|
|
128
|
+
await getResultAsync(async () => {
|
|
129
|
+
const loadedComments = await readComments(loaded);
|
|
130
|
+
if (loaded === toValue(pdfDocument)) comments.value = loadedComments;
|
|
131
|
+
}).match(noop, (error) => {
|
|
132
|
+
console.error(error);
|
|
133
|
+
if (loaded === toValue(pdfDocument)) comments.value = [];
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
const setEditorMode = (mode) => {
|
|
137
|
+
viewer.editorMode.value = mode;
|
|
138
|
+
};
|
|
139
|
+
const exportXfdf = () => {
|
|
140
|
+
const loaded = toValue(pdfDocument);
|
|
141
|
+
return writeXfdf(loaded ? readFreeTexts(loaded) : []);
|
|
142
|
+
};
|
|
143
|
+
const importXfdf = (xfdf) => {
|
|
144
|
+
const loaded = toValue(pdfDocument);
|
|
145
|
+
if (!loaded) return err("No document is open to import into");
|
|
146
|
+
return readXfdf(xfdf, new Set(readFreeTexts(loaded).map(({ id }) => id))).andThen((freeTexts) => {
|
|
147
|
+
const stray = freeTexts.find(({ pageNumber }) => pageNumber > loaded.numPages);
|
|
148
|
+
if (stray) return err(`The XFDF has a free text on page ${stray.pageNumber}, and the document ends at page ${loaded.numPages}`);
|
|
149
|
+
storeFreeTexts(loaded, freeTexts);
|
|
150
|
+
viewer.drawStoredEditors();
|
|
151
|
+
return ok(void 0);
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
watch(() => toValue(pdfDocument), (loaded) => {
|
|
155
|
+
comments.value = void 0;
|
|
156
|
+
if (!loaded) return;
|
|
157
|
+
setEditorHook(loaded, (kind) => {
|
|
158
|
+
if (kind === null) return;
|
|
159
|
+
const drawn = [...loaded.annotationStorage].at(-1)?.[1];
|
|
160
|
+
if (drawn) onChange?.({
|
|
161
|
+
kind,
|
|
162
|
+
pageNumber: drawn.pageIndex + 1
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
}, { immediate: true });
|
|
166
|
+
let commentsDocument;
|
|
167
|
+
watch([() => toValue(pdfDocument), () => toValue(isCommentsWanted)], ([loaded, isWanted]) => {
|
|
168
|
+
if (!loaded || !isWanted || loaded === commentsDocument) return;
|
|
169
|
+
commentsDocument = loaded;
|
|
170
|
+
loadComments(loaded);
|
|
171
|
+
}, { immediate: true });
|
|
172
|
+
return {
|
|
173
|
+
comments,
|
|
174
|
+
exportXfdf,
|
|
175
|
+
importXfdf,
|
|
176
|
+
setEditorMode
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
// src/services/destroyLoadingTask.ts
|
|
180
|
+
const destroyLoadingTask = getSynchronizedFunction(async (task) => {
|
|
181
|
+
await task.destroy();
|
|
182
|
+
});
|
|
183
|
+
// src/services/getSourceParameters.ts
|
|
184
|
+
const getSourceParameters = async (source) => {
|
|
185
|
+
if (typeof source === "string") return { url: source };
|
|
186
|
+
if (source instanceof File) return { data: new Uint8Array(await source.arrayBuffer()) };
|
|
187
|
+
return { data: new Uint8Array(source) };
|
|
188
|
+
};
|
|
189
|
+
// src/services/loadPdfDocument.ts
|
|
190
|
+
const loadPdfDocument = async (renderer, source, parameters) => {
|
|
191
|
+
renderer.api.GlobalWorkerOptions.workerSrc = renderer.workerSrc;
|
|
192
|
+
const sourceParameters = await getSourceParameters(source);
|
|
193
|
+
return renderer.api.getDocument({
|
|
194
|
+
cMapPacked: true,
|
|
195
|
+
cMapUrl: renderer.cMapUrl,
|
|
196
|
+
standardFontDataUrl: renderer.standardFontDataUrl,
|
|
197
|
+
wasmUrl: renderer.wasmUrl,
|
|
198
|
+
...parameters,
|
|
199
|
+
...sourceParameters
|
|
200
|
+
});
|
|
201
|
+
};
|
|
202
|
+
// src/composables/usePdfDocument.ts
|
|
203
|
+
const EMPTY_PROGRESS = {
|
|
204
|
+
loaded: 0,
|
|
205
|
+
total: 0
|
|
206
|
+
};
|
|
207
|
+
const usePdfDocument = ({ parameters = {}, renderer, source }) => {
|
|
208
|
+
const pdfDocument = shallowRef();
|
|
209
|
+
const progress = ref(EMPTY_PROGRESS);
|
|
210
|
+
const isFailed = ref(false);
|
|
211
|
+
const isPasswordRequested = ref(false);
|
|
212
|
+
const isPasswordWrong = ref(false);
|
|
213
|
+
let loadingTask;
|
|
214
|
+
let updatePassword;
|
|
215
|
+
let loadGeneration = 0;
|
|
216
|
+
const destroyCurrentTask = () => {
|
|
217
|
+
if (loadingTask) destroyLoadingTask(loadingTask);
|
|
218
|
+
loadingTask = void 0;
|
|
219
|
+
};
|
|
220
|
+
const release = () => {
|
|
221
|
+
loadGeneration++;
|
|
222
|
+
destroyCurrentTask();
|
|
223
|
+
pdfDocument.value = void 0;
|
|
224
|
+
progress.value = EMPTY_PROGRESS;
|
|
225
|
+
isFailed.value = false;
|
|
226
|
+
isPasswordRequested.value = false;
|
|
227
|
+
isPasswordWrong.value = false;
|
|
228
|
+
};
|
|
229
|
+
const load = async (currentRenderer, currentSource) => {
|
|
230
|
+
const generation = loadGeneration;
|
|
231
|
+
await getResultAsync(async () => {
|
|
232
|
+
const task = await loadPdfDocument(currentRenderer, currentSource, parameters);
|
|
233
|
+
if (generation !== loadGeneration) {
|
|
234
|
+
destroyLoadingTask(task);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
loadingTask = task;
|
|
238
|
+
task.onProgress = ({ loaded, total }) => {
|
|
239
|
+
if (generation === loadGeneration) progress.value = {
|
|
240
|
+
loaded,
|
|
241
|
+
total
|
|
242
|
+
};
|
|
243
|
+
};
|
|
244
|
+
task.onPassword = (passwordCallback, reason) => {
|
|
245
|
+
if (generation !== loadGeneration) return;
|
|
246
|
+
updatePassword = passwordCallback;
|
|
247
|
+
isPasswordWrong.value = reason === currentRenderer.api.PasswordResponses.INCORRECT_PASSWORD;
|
|
248
|
+
isPasswordRequested.value = true;
|
|
249
|
+
};
|
|
250
|
+
const loadedDocument = await task.promise;
|
|
251
|
+
if (generation === loadGeneration) pdfDocument.value = loadedDocument;
|
|
252
|
+
}).match(noop, (error) => {
|
|
253
|
+
if (generation !== loadGeneration) return;
|
|
254
|
+
isFailed.value = true;
|
|
255
|
+
destroyCurrentTask();
|
|
256
|
+
console.error(error);
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
const submitPassword = (password) => {
|
|
260
|
+
isPasswordRequested.value = false;
|
|
261
|
+
updatePassword?.(password);
|
|
262
|
+
};
|
|
263
|
+
watch([() => toValue(renderer), () => toValue(source)], getSynchronizedFunction(async ([currentRenderer, currentSource]) => {
|
|
264
|
+
release();
|
|
265
|
+
if (currentRenderer && currentSource) await load(currentRenderer, currentSource);
|
|
266
|
+
}), { immediate: true });
|
|
267
|
+
onScopeDispose(release);
|
|
268
|
+
return {
|
|
269
|
+
isFailed,
|
|
270
|
+
isPasswordRequested,
|
|
271
|
+
isPasswordWrong,
|
|
272
|
+
pdfDocument,
|
|
273
|
+
progress,
|
|
274
|
+
submitPassword
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
// src/services/getCanvasScale.ts
|
|
278
|
+
const getCanvasScale = ({ cssWidth, pageHeight, pageWidth, pixelRatio }) => {
|
|
279
|
+
const scale = cssWidth / pageWidth * pixelRatio;
|
|
280
|
+
if (pageWidth * pageHeight * scale ** 2 <= 16777216) return scale;
|
|
281
|
+
return Math.sqrt(PDF_MAX_CANVAS_AREA / (pageWidth * pageHeight));
|
|
282
|
+
};
|
|
283
|
+
// src/composables/usePdfPage.ts
|
|
284
|
+
const usePdfPage = ({ canvas, isActive = true, isVisible = true, pageNumber, pagesRotation = 0, pdfDocument }) => {
|
|
285
|
+
let cssWidth = 0;
|
|
286
|
+
let loadGeneration = 0;
|
|
287
|
+
let page;
|
|
288
|
+
let renderTask;
|
|
289
|
+
const awaitRender = getSynchronizedFunction(async (task) => {
|
|
290
|
+
await getResultAsync(() => task.promise).match(noop, (error) => {
|
|
291
|
+
if (task === renderTask) console.error(error);
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
const cancelRender = () => {
|
|
295
|
+
renderTask?.cancel();
|
|
296
|
+
renderTask = void 0;
|
|
297
|
+
};
|
|
298
|
+
const drawPage = () => {
|
|
299
|
+
const currentCanvas = toValue(canvas);
|
|
300
|
+
if (!page || !currentCanvas || cssWidth === 0 || !toValue(isVisible)) return;
|
|
301
|
+
cancelRender();
|
|
302
|
+
const rotation = (page.rotate + toValue(pagesRotation)) % 360;
|
|
303
|
+
const { height, width } = page.getViewport({
|
|
304
|
+
rotation,
|
|
305
|
+
scale: 1
|
|
306
|
+
});
|
|
307
|
+
const viewport = page.getViewport({
|
|
308
|
+
rotation,
|
|
309
|
+
scale: getCanvasScale({
|
|
310
|
+
cssWidth,
|
|
311
|
+
pageHeight: height,
|
|
312
|
+
pageWidth: width,
|
|
313
|
+
pixelRatio: window.devicePixelRatio
|
|
314
|
+
})
|
|
315
|
+
});
|
|
316
|
+
currentCanvas.width = viewport.width;
|
|
317
|
+
currentCanvas.height = viewport.height;
|
|
318
|
+
const task = page.render({
|
|
319
|
+
canvas: currentCanvas,
|
|
320
|
+
viewport
|
|
321
|
+
});
|
|
322
|
+
renderTask = task;
|
|
323
|
+
awaitRender(task);
|
|
324
|
+
};
|
|
325
|
+
const isWanted = ref(false);
|
|
326
|
+
watch(() => toValue(isActive), (active) => {
|
|
327
|
+
if (active) isWanted.value = true;
|
|
328
|
+
}, { immediate: true });
|
|
329
|
+
watch([
|
|
330
|
+
() => toValue(pdfDocument),
|
|
331
|
+
() => toValue(pageNumber),
|
|
332
|
+
isWanted
|
|
333
|
+
], getSynchronizedFunction(async ([currentDocument, currentPageNumber, currentIsWanted]) => {
|
|
334
|
+
const generation = ++loadGeneration;
|
|
335
|
+
cancelRender();
|
|
336
|
+
page = void 0;
|
|
337
|
+
if (!currentDocument || !currentIsWanted) return;
|
|
338
|
+
await getResultAsync(async () => {
|
|
339
|
+
const loadedPage = await currentDocument.getPage(currentPageNumber);
|
|
340
|
+
if (generation !== loadGeneration) return;
|
|
341
|
+
page = loadedPage;
|
|
342
|
+
drawPage();
|
|
343
|
+
}).match(noop, (error) => {
|
|
344
|
+
if (generation === loadGeneration) console.error(error);
|
|
345
|
+
});
|
|
346
|
+
}), { immediate: true });
|
|
347
|
+
watch(() => toValue(canvas), (currentCanvas, _previousCanvas, onCleanup) => {
|
|
348
|
+
if (!currentCanvas) {
|
|
349
|
+
cancelRender();
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
cssWidth = 0;
|
|
353
|
+
const observer = new ResizeObserver((entries) => {
|
|
354
|
+
const width = entries[0]?.contentRect.width ?? 0;
|
|
355
|
+
if (width === cssWidth) return;
|
|
356
|
+
cssWidth = width;
|
|
357
|
+
drawPage();
|
|
358
|
+
});
|
|
359
|
+
observer.observe(currentCanvas);
|
|
360
|
+
onCleanup(() => {
|
|
361
|
+
observer.disconnect();
|
|
362
|
+
});
|
|
363
|
+
}, { immediate: true });
|
|
364
|
+
watch(() => toValue(isVisible), (visible) => {
|
|
365
|
+
if (visible) drawPage();
|
|
366
|
+
});
|
|
367
|
+
watch(() => toValue(pagesRotation), () => {
|
|
368
|
+
drawPage();
|
|
369
|
+
});
|
|
370
|
+
onScopeDispose(() => {
|
|
371
|
+
loadGeneration++;
|
|
372
|
+
cancelRender();
|
|
373
|
+
page?.cleanup();
|
|
374
|
+
});
|
|
375
|
+
};
|
|
376
|
+
// src/composables/usePdfThumbnails.ts
|
|
377
|
+
const usePdfThumbnails = ({ currentPageNumber, pagesRotation, pdfDocument, root }) => {
|
|
378
|
+
const thumbnails = shallowRef([]);
|
|
379
|
+
const rows = /* @__PURE__ */ new Map();
|
|
380
|
+
let observer;
|
|
381
|
+
let scope;
|
|
382
|
+
const createThumbnail = (pageNumber, currentDocument) => {
|
|
383
|
+
const canvas = shallowRef();
|
|
384
|
+
const isVisible = ref(false);
|
|
385
|
+
const thumbnail = {
|
|
386
|
+
canvas,
|
|
387
|
+
isCurrent: computed(() => toValue(currentPageNumber) === pageNumber),
|
|
388
|
+
isVisible,
|
|
389
|
+
pageNumber
|
|
390
|
+
};
|
|
391
|
+
usePdfPage({
|
|
392
|
+
canvas: () => canvas.value,
|
|
393
|
+
isActive: () => isVisible.value,
|
|
394
|
+
isVisible: () => isVisible.value,
|
|
395
|
+
pageNumber,
|
|
396
|
+
pagesRotation: () => toValue(pagesRotation),
|
|
397
|
+
pdfDocument: currentDocument
|
|
398
|
+
});
|
|
399
|
+
watch(canvas, (element, _previousElement, onCleanup) => {
|
|
400
|
+
if (!element) return;
|
|
401
|
+
rows.set(element, thumbnail);
|
|
402
|
+
observer?.observe(element);
|
|
403
|
+
onCleanup(() => {
|
|
404
|
+
observer?.unobserve(element);
|
|
405
|
+
rows.delete(element);
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
return thumbnail;
|
|
409
|
+
};
|
|
410
|
+
watch(() => toValue(root), (rootElement) => {
|
|
411
|
+
observer?.disconnect();
|
|
412
|
+
observer = new IntersectionObserver((entries) => {
|
|
413
|
+
for (const entry of entries) {
|
|
414
|
+
const row = rows.get(entry.target);
|
|
415
|
+
if (row) row.isVisible.value = entry.isIntersecting;
|
|
416
|
+
}
|
|
417
|
+
}, { root: rootElement ?? null });
|
|
418
|
+
for (const element of rows.keys()) observer.observe(element);
|
|
419
|
+
}, { immediate: true });
|
|
420
|
+
watch(() => toValue(pdfDocument), (loaded) => {
|
|
421
|
+
scope?.stop();
|
|
422
|
+
rows.clear();
|
|
423
|
+
thumbnails.value = [];
|
|
424
|
+
if (!loaded) return;
|
|
425
|
+
scope = effectScope();
|
|
426
|
+
thumbnails.value = scope.run(() => Array.from({ length: loaded.numPages }, (_value, index) => createThumbnail(index + 1, loaded))) ?? [];
|
|
427
|
+
}, { immediate: true });
|
|
428
|
+
onScopeDispose(() => {
|
|
429
|
+
scope?.stop();
|
|
430
|
+
observer?.disconnect();
|
|
431
|
+
});
|
|
432
|
+
return thumbnails;
|
|
433
|
+
};
|
|
434
|
+
// src/services/getZoomedScroll.ts
|
|
435
|
+
const getZoomedScroll = ({ point, ratio, scroll }) => (scroll + point) * ratio - point;
|
|
436
|
+
// src/composables/usePdfZoomGesture.ts
|
|
437
|
+
const usePdfZoomGesture = ({ container, getViewer }) => {
|
|
438
|
+
const abortController = new AbortController();
|
|
439
|
+
const pointers = /* @__PURE__ */ new Map();
|
|
440
|
+
let pinchDistance = 0;
|
|
441
|
+
const measurePinch = () => {
|
|
442
|
+
const [first, second] = [...pointers.values()];
|
|
443
|
+
if (pointers.size !== 2 || !first || !second) return void 0;
|
|
444
|
+
return {
|
|
445
|
+
distance: Math.hypot(second.x - first.x, second.y - first.y),
|
|
446
|
+
point: {
|
|
447
|
+
x: (first.x + second.x) / 2,
|
|
448
|
+
y: (first.y + second.y) / 2
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
};
|
|
452
|
+
const measurePinchDistance = () => measurePinch()?.distance ?? 0;
|
|
453
|
+
const zoomAbout = (ratio, point) => {
|
|
454
|
+
const viewer = getViewer();
|
|
455
|
+
const element = toValue(container);
|
|
456
|
+
if (!viewer || !element) return;
|
|
457
|
+
const { currentScale } = viewer;
|
|
458
|
+
const nextScale = Math.min(Math.max(currentScale * ratio, PDF_MIN_SCALE), 10);
|
|
459
|
+
const scaleRatio = nextScale / currentScale;
|
|
460
|
+
const { left, top } = element.getBoundingClientRect();
|
|
461
|
+
const { scrollLeft, scrollTop } = element;
|
|
462
|
+
viewer.currentScale = nextScale;
|
|
463
|
+
element.scrollLeft = getZoomedScroll({
|
|
464
|
+
point: point.x - left,
|
|
465
|
+
ratio: scaleRatio,
|
|
466
|
+
scroll: scrollLeft
|
|
467
|
+
});
|
|
468
|
+
element.scrollTop = getZoomedScroll({
|
|
469
|
+
point: point.y - top,
|
|
470
|
+
ratio: scaleRatio,
|
|
471
|
+
scroll: scrollTop
|
|
472
|
+
});
|
|
473
|
+
};
|
|
474
|
+
const onPointerDown = (event) => {
|
|
475
|
+
pointers.set(event.pointerId, {
|
|
476
|
+
x: event.clientX,
|
|
477
|
+
y: event.clientY
|
|
478
|
+
});
|
|
479
|
+
pinchDistance = measurePinchDistance();
|
|
480
|
+
};
|
|
481
|
+
const onPointerMove = (event) => {
|
|
482
|
+
if (!pointers.has(event.pointerId)) return;
|
|
483
|
+
pointers.set(event.pointerId, {
|
|
484
|
+
x: event.clientX,
|
|
485
|
+
y: event.clientY
|
|
486
|
+
});
|
|
487
|
+
const pinch = measurePinch();
|
|
488
|
+
if (pinch && pinchDistance > 0) zoomAbout(pinch.distance / pinchDistance, pinch.point);
|
|
489
|
+
pinchDistance = pinch?.distance ?? 0;
|
|
490
|
+
};
|
|
491
|
+
const onPointerEnd = (event) => {
|
|
492
|
+
pointers.delete(event.pointerId);
|
|
493
|
+
pinchDistance = measurePinchDistance();
|
|
494
|
+
};
|
|
495
|
+
const onWheel = (event) => {
|
|
496
|
+
if (!event.ctrlKey) return;
|
|
497
|
+
event.preventDefault();
|
|
498
|
+
zoomAbout(Math.exp(-event.deltaY / 500), {
|
|
499
|
+
x: event.clientX,
|
|
500
|
+
y: event.clientY
|
|
501
|
+
});
|
|
502
|
+
};
|
|
503
|
+
watch(() => toValue(container), (element) => {
|
|
504
|
+
if (!element) return;
|
|
505
|
+
const { signal } = abortController;
|
|
506
|
+
element.addEventListener("pointerdown", (event) => {
|
|
507
|
+
onPointerDown(event);
|
|
508
|
+
}, { signal });
|
|
509
|
+
element.addEventListener("pointermove", (event) => {
|
|
510
|
+
onPointerMove(event);
|
|
511
|
+
}, { signal });
|
|
512
|
+
element.addEventListener("pointerup", (event) => {
|
|
513
|
+
onPointerEnd(event);
|
|
514
|
+
}, { signal });
|
|
515
|
+
element.addEventListener("pointercancel", (event) => {
|
|
516
|
+
onPointerEnd(event);
|
|
517
|
+
}, { signal });
|
|
518
|
+
element.addEventListener("pointerleave", (event) => {
|
|
519
|
+
onPointerEnd(event);
|
|
520
|
+
}, { signal });
|
|
521
|
+
element.addEventListener("wheel", (event) => {
|
|
522
|
+
onWheel(event);
|
|
523
|
+
}, {
|
|
524
|
+
passive: false,
|
|
525
|
+
signal
|
|
526
|
+
});
|
|
527
|
+
}, { immediate: true });
|
|
528
|
+
onScopeDispose(() => {
|
|
529
|
+
abortController.abort();
|
|
530
|
+
});
|
|
531
|
+
};
|
|
532
|
+
// src/services/getDownloadBytes.ts
|
|
533
|
+
const addWatermark = async (pdfDocument, watermark) => {
|
|
534
|
+
const { annotationStorage } = pdfDocument;
|
|
535
|
+
return (await getPdfPages(pdfDocument)).map((page, pageIndex) => {
|
|
536
|
+
const [left = 0, bottom = 0, right = 0, top = 0] = page.view;
|
|
537
|
+
page.cleanup();
|
|
538
|
+
const middle = (bottom + top) / 2;
|
|
539
|
+
const id = `watermark${pageIndex}`;
|
|
540
|
+
const key = `${PDF_EDITOR_KEY_PREFIX}${id}`;
|
|
541
|
+
annotationStorage.setValue(key, createFreeTextEntry({
|
|
542
|
+
color: PDF_WATERMARK_COLOR,
|
|
543
|
+
fontSize: 48,
|
|
544
|
+
id,
|
|
545
|
+
pageIndex,
|
|
546
|
+
rect: [
|
|
547
|
+
left,
|
|
548
|
+
middle - 24,
|
|
549
|
+
right,
|
|
550
|
+
middle + 24
|
|
551
|
+
],
|
|
552
|
+
value: watermark
|
|
553
|
+
}));
|
|
554
|
+
return key;
|
|
555
|
+
});
|
|
556
|
+
};
|
|
557
|
+
const getDownloadBytes = async (pdfDocument, watermark) => {
|
|
558
|
+
const { annotationStorage } = pdfDocument;
|
|
559
|
+
if (annotationStorage.size === 0 && !watermark) return new Uint8Array(await pdfDocument.getData());
|
|
560
|
+
const watermarkKeys = watermark ? await addWatermark(pdfDocument, watermark) : [];
|
|
561
|
+
return withFinalizerAsync(() => pdfDocument.saveDocument(), () => {
|
|
562
|
+
for (const key of watermarkKeys) annotationStorage.remove(key);
|
|
563
|
+
});
|
|
564
|
+
};
|
|
565
|
+
// src/services/renderPrintImage.ts
|
|
566
|
+
const renderPrintImage = async (pdfDocument, pageNumber) => {
|
|
567
|
+
const page = await pdfDocument.getPage(pageNumber);
|
|
568
|
+
const { height, width } = page.getViewport({ scale: 1 });
|
|
569
|
+
const printScale = getCanvasScale({
|
|
570
|
+
cssWidth: width,
|
|
571
|
+
pageHeight: height,
|
|
572
|
+
pageWidth: width,
|
|
573
|
+
pixelRatio: 2
|
|
574
|
+
});
|
|
575
|
+
const viewport = page.getViewport({ scale: printScale });
|
|
576
|
+
const canvas = window.document.createElement("canvas");
|
|
577
|
+
canvas.width = viewport.width;
|
|
578
|
+
canvas.height = viewport.height;
|
|
579
|
+
await page.render({
|
|
580
|
+
canvas,
|
|
581
|
+
viewport
|
|
582
|
+
}).promise;
|
|
583
|
+
const image = window.document.createElement("img");
|
|
584
|
+
image.src = canvas.toDataURL("image/png");
|
|
585
|
+
return image;
|
|
586
|
+
};
|
|
587
|
+
// src/services/renderPrintSheets.ts
|
|
588
|
+
const renderPrintSheets = async (pdfDocument, checkIsCancelled) => {
|
|
589
|
+
const style = window.document.createElement("style");
|
|
590
|
+
style.textContent = PDF_PRINT_STYLE;
|
|
591
|
+
const printRoot = window.document.createElement("div");
|
|
592
|
+
printRoot.className = PDF_PRINT_CLASS;
|
|
593
|
+
printRoot.append(style);
|
|
594
|
+
const renderBatch = async (firstPageNumber) => {
|
|
595
|
+
if (checkIsCancelled() || firstPageNumber > pdfDocument.numPages) return;
|
|
596
|
+
const lastPageNumber = Math.min(firstPageNumber + 10 - 1, pdfDocument.numPages);
|
|
597
|
+
const pageNumbers = Array.from({ length: lastPageNumber - firstPageNumber + 1 }, (_value, index) => firstPageNumber + index);
|
|
598
|
+
printRoot.append(...await Promise.all(pageNumbers.map((pageNumber) => renderPrintImage(pdfDocument, pageNumber))));
|
|
599
|
+
await renderBatch(lastPageNumber + 1);
|
|
600
|
+
};
|
|
601
|
+
await renderBatch(1);
|
|
602
|
+
return checkIsCancelled() ? void 0 : printRoot;
|
|
603
|
+
};
|
|
604
|
+
// src/composables/usePdfViewer.ts
|
|
605
|
+
const usePdfViewer = ({ container, highlightTerms = [], imageResourcesPath, pdfDocument, viewer, viewerApi }) => {
|
|
606
|
+
const currentPageNumber = ref(0);
|
|
607
|
+
const editorMode = ref(0);
|
|
608
|
+
const isMatchCase = ref(false);
|
|
609
|
+
const isNotFound = ref(false);
|
|
610
|
+
const isWholeWord = ref(false);
|
|
611
|
+
const matchesCount = ref({
|
|
612
|
+
current: 0,
|
|
613
|
+
total: 0
|
|
614
|
+
});
|
|
615
|
+
const outline = shallowRef();
|
|
616
|
+
const overlays = shallowRef([]);
|
|
617
|
+
const pagesCount = ref(0);
|
|
618
|
+
const pagesRotation = ref(0);
|
|
619
|
+
const scale = ref(0);
|
|
620
|
+
const searchQuery = ref("");
|
|
621
|
+
const documentGeneration = ref(0);
|
|
622
|
+
const isZoomInDisabled = computed(() => scale.value >= 10);
|
|
623
|
+
const isZoomOutDisabled = computed(() => scale.value <= PDF_MIN_SCALE);
|
|
624
|
+
let abortController;
|
|
625
|
+
let eventBus;
|
|
626
|
+
let linkService;
|
|
627
|
+
let pdfViewer;
|
|
628
|
+
let resizeObserver;
|
|
629
|
+
let printRoot;
|
|
630
|
+
let printGeneration = 0;
|
|
631
|
+
let editorManager;
|
|
632
|
+
const applyEditorMode = () => {
|
|
633
|
+
if (pdfViewer && editorManager) pdfViewer.annotationEditorMode = { mode: editorMode.value };
|
|
634
|
+
};
|
|
635
|
+
const createViewer = (currentViewerApi, containerValue, viewerElement) => {
|
|
636
|
+
const { EventBus, FindState, LinkTarget, PDFFindController, PDFLinkService, PDFViewer } = currentViewerApi;
|
|
637
|
+
const bus = new EventBus();
|
|
638
|
+
const links = new PDFLinkService({
|
|
639
|
+
eventBus: bus,
|
|
640
|
+
externalLinkTarget: LinkTarget.BLANK
|
|
641
|
+
});
|
|
642
|
+
const controller = new PDFFindController({
|
|
643
|
+
eventBus: bus,
|
|
644
|
+
linkService: links
|
|
645
|
+
});
|
|
646
|
+
const viewerAbortController = new AbortController();
|
|
647
|
+
const { signal } = viewerAbortController;
|
|
648
|
+
const instance = new PDFViewer({
|
|
649
|
+
abortSignal: signal,
|
|
650
|
+
container: containerValue,
|
|
651
|
+
eventBus: bus,
|
|
652
|
+
findController: controller,
|
|
653
|
+
imageResourcesPath,
|
|
654
|
+
linkService: links,
|
|
655
|
+
maxCanvasPixels: PDF_MAX_CANVAS_AREA,
|
|
656
|
+
viewer: viewerElement
|
|
657
|
+
});
|
|
658
|
+
links.setViewer(instance);
|
|
659
|
+
abortController = viewerAbortController;
|
|
660
|
+
eventBus = bus;
|
|
661
|
+
linkService = links;
|
|
662
|
+
pdfViewer = instance;
|
|
663
|
+
bus.on("pagesinit", () => {
|
|
664
|
+
instance.currentScaleValue = PDF_OPENING_SCALE;
|
|
665
|
+
});
|
|
666
|
+
bus.on("annotationeditoruimanager", (event) => {
|
|
667
|
+
editorManager = event.uiManager;
|
|
668
|
+
applyEditorMode();
|
|
669
|
+
});
|
|
670
|
+
bus.on("pagechanging", (event) => {
|
|
671
|
+
currentPageNumber.value = event.pageNumber;
|
|
672
|
+
});
|
|
673
|
+
bus.on("pagerendered", (event) => {
|
|
674
|
+
const overlay = {
|
|
675
|
+
element: event.source.div,
|
|
676
|
+
pageNumber: event.pageNumber,
|
|
677
|
+
viewport: event.source.viewport
|
|
678
|
+
};
|
|
679
|
+
overlays.value = [...overlays.value.filter(({ pageNumber }) => pageNumber !== event.pageNumber), overlay];
|
|
680
|
+
});
|
|
681
|
+
bus.on("scalechanging", (event) => {
|
|
682
|
+
scale.value = event.scale;
|
|
683
|
+
});
|
|
684
|
+
bus.on("rotationchanging", (event) => {
|
|
685
|
+
pagesRotation.value = event.pagesRotation;
|
|
686
|
+
});
|
|
687
|
+
bus.on("updatefindcontrolstate", (event) => {
|
|
688
|
+
isNotFound.value = event.state === FindState.NOT_FOUND;
|
|
689
|
+
matchesCount.value = event.matchesCount;
|
|
690
|
+
});
|
|
691
|
+
bus.on("updatefindmatchescount", (event) => {
|
|
692
|
+
matchesCount.value = event.matchesCount;
|
|
693
|
+
});
|
|
694
|
+
resizeObserver = new ResizeObserver(() => {
|
|
695
|
+
if (!pdfViewer) return;
|
|
696
|
+
const { currentScaleValue } = pdfViewer;
|
|
697
|
+
if (currentScaleValue) pdfViewer.currentScaleValue = currentScaleValue;
|
|
698
|
+
});
|
|
699
|
+
resizeObserver.observe(containerValue);
|
|
700
|
+
window.addEventListener("afterprint", () => {
|
|
701
|
+
removePrint();
|
|
702
|
+
}, { signal });
|
|
703
|
+
applyDocument(toValue(pdfDocument));
|
|
704
|
+
};
|
|
705
|
+
const loadOutline = getSynchronizedFunction(async (loaded) => {
|
|
706
|
+
await getResultAsync(async () => {
|
|
707
|
+
const items = await loaded.getOutline();
|
|
708
|
+
if (loaded === toValue(pdfDocument)) outline.value = items ?? [];
|
|
709
|
+
}).match(noop, (error) => {
|
|
710
|
+
console.error(error);
|
|
711
|
+
if (loaded === toValue(pdfDocument)) outline.value = [];
|
|
712
|
+
});
|
|
713
|
+
});
|
|
714
|
+
const applyDocument = (loaded) => {
|
|
715
|
+
if (!pdfViewer || !linkService) return;
|
|
716
|
+
pagesCount.value = loaded?.numPages ?? 0;
|
|
717
|
+
overlays.value = [];
|
|
718
|
+
outline.value = void 0;
|
|
719
|
+
editorManager = void 0;
|
|
720
|
+
pdfViewer.setDocument(loaded ?? null);
|
|
721
|
+
linkService.setDocument(loaded ?? null);
|
|
722
|
+
currentPageNumber.value = 1;
|
|
723
|
+
pagesRotation.value = 0;
|
|
724
|
+
searchQuery.value = "";
|
|
725
|
+
matchesCount.value = {
|
|
726
|
+
current: 0,
|
|
727
|
+
total: 0
|
|
728
|
+
};
|
|
729
|
+
isNotFound.value = false;
|
|
730
|
+
if (!loaded) return;
|
|
731
|
+
loadOutline(loaded);
|
|
732
|
+
searchQuery.value = toValue(highlightTerms).join(" ");
|
|
733
|
+
documentGeneration.value++;
|
|
734
|
+
};
|
|
735
|
+
const findText = ({ isPrevious = false, query, type = "" }) => {
|
|
736
|
+
eventBus?.dispatch("find", {
|
|
737
|
+
caseSensitive: isMatchCase.value,
|
|
738
|
+
entireWord: isWholeWord.value,
|
|
739
|
+
findPrevious: isPrevious,
|
|
740
|
+
highlightAll: true,
|
|
741
|
+
matchDiacritics: false,
|
|
742
|
+
query,
|
|
743
|
+
type
|
|
744
|
+
});
|
|
745
|
+
};
|
|
746
|
+
watch([
|
|
747
|
+
searchQuery,
|
|
748
|
+
isMatchCase,
|
|
749
|
+
isWholeWord,
|
|
750
|
+
documentGeneration
|
|
751
|
+
], () => {
|
|
752
|
+
if (toValue(pdfDocument)) findText({ query: searchQuery.value });
|
|
753
|
+
});
|
|
754
|
+
const removePrint = () => {
|
|
755
|
+
printGeneration++;
|
|
756
|
+
printRoot?.remove();
|
|
757
|
+
printRoot = void 0;
|
|
758
|
+
};
|
|
759
|
+
const followBookmark = getSynchronizedFunction(async (item) => {
|
|
760
|
+
await getResultAsync(async () => {
|
|
761
|
+
if (item.dest) await linkService?.goToDestination(item.dest);
|
|
762
|
+
}).match(noop, console.error);
|
|
763
|
+
});
|
|
764
|
+
const goToPage = (pageNumber) => {
|
|
765
|
+
if (pdfViewer && Number.isInteger(pageNumber) && pageNumber >= 1 && pageNumber <= pagesCount.value) pdfViewer.currentPageNumber = pageNumber;
|
|
766
|
+
};
|
|
767
|
+
const previousPage = () => {
|
|
768
|
+
pdfViewer?.previousPage();
|
|
769
|
+
};
|
|
770
|
+
const nextPage = () => {
|
|
771
|
+
pdfViewer?.nextPage();
|
|
772
|
+
};
|
|
773
|
+
const zoomTo = (nextScale) => {
|
|
774
|
+
if (pdfViewer) pdfViewer.currentScale = Math.min(Math.max(nextScale, PDF_MIN_SCALE), 10);
|
|
775
|
+
};
|
|
776
|
+
const zoomIn = () => {
|
|
777
|
+
if (pdfViewer && pdfViewer.currentScale > 0) zoomTo(pdfViewer.currentScale * PDF_ZOOM_STEP);
|
|
778
|
+
};
|
|
779
|
+
const zoomOut = () => {
|
|
780
|
+
if (pdfViewer && pdfViewer.currentScale > 0) zoomTo(pdfViewer.currentScale / PDF_ZOOM_STEP);
|
|
781
|
+
};
|
|
782
|
+
const resetZoom = () => {
|
|
783
|
+
if (pdfViewer) pdfViewer.currentScaleValue = PDF_OPENING_SCALE;
|
|
784
|
+
};
|
|
785
|
+
const fitWidth = () => {
|
|
786
|
+
if (pdfViewer) pdfViewer.currentScaleValue = "page-width";
|
|
787
|
+
};
|
|
788
|
+
const fitPage = () => {
|
|
789
|
+
if (pdfViewer) pdfViewer.currentScaleValue = "page-fit";
|
|
790
|
+
};
|
|
791
|
+
const actualSize = () => {
|
|
792
|
+
if (pdfViewer) pdfViewer.currentScaleValue = "page-actual";
|
|
793
|
+
};
|
|
794
|
+
const rotate = (degrees) => {
|
|
795
|
+
if (pdfViewer) pdfViewer.pagesRotation = ((pdfViewer.pagesRotation + degrees) % 360 + 360) % 360;
|
|
796
|
+
};
|
|
797
|
+
const findNext = () => {
|
|
798
|
+
findText({
|
|
799
|
+
query: searchQuery.value,
|
|
800
|
+
type: "again"
|
|
801
|
+
});
|
|
802
|
+
};
|
|
803
|
+
const findPrevious = () => {
|
|
804
|
+
findText({
|
|
805
|
+
isPrevious: true,
|
|
806
|
+
query: searchQuery.value,
|
|
807
|
+
type: "again"
|
|
808
|
+
});
|
|
809
|
+
};
|
|
810
|
+
const download = getSynchronizedFunction(async (filename, watermark) => {
|
|
811
|
+
const downloaded = toValue(pdfDocument);
|
|
812
|
+
if (!downloaded) return;
|
|
813
|
+
await getResultAsync(async () => {
|
|
814
|
+
downloadFile(filename, await getDownloadBytes(downloaded, watermark), PDF_MIME_TYPE);
|
|
815
|
+
}).match(noop, console.error);
|
|
816
|
+
});
|
|
817
|
+
const print = getSynchronizedFunction(async () => {
|
|
818
|
+
const printed = toValue(pdfDocument);
|
|
819
|
+
if (!printed) return;
|
|
820
|
+
removePrint();
|
|
821
|
+
const generation = printGeneration;
|
|
822
|
+
await getResultAsync(async () => {
|
|
823
|
+
const sheets = await renderPrintSheets(printed, () => generation !== printGeneration);
|
|
824
|
+
if (!sheets) return;
|
|
825
|
+
window.document.body.append(sheets);
|
|
826
|
+
printRoot = sheets;
|
|
827
|
+
window.print();
|
|
828
|
+
}).match(noop, console.error);
|
|
829
|
+
});
|
|
830
|
+
const drawStoredEditors = getSynchronizedFunction(async () => {
|
|
831
|
+
const manager = editorManager;
|
|
832
|
+
if (!manager) return;
|
|
833
|
+
await getResultAsync(async () => {
|
|
834
|
+
await Promise.all(Array.from({ length: pagesCount.value }, async (_value, pageIndex) => {
|
|
835
|
+
const layer = manager.getLayer(pageIndex);
|
|
836
|
+
if (!layer) return;
|
|
837
|
+
await manager.findClonesForPage(layer);
|
|
838
|
+
layer.div.hidden = layer.isEmpty;
|
|
839
|
+
}));
|
|
840
|
+
}).match(noop, console.error);
|
|
841
|
+
});
|
|
842
|
+
usePdfZoomGesture({
|
|
843
|
+
container,
|
|
844
|
+
getViewer: () => pdfViewer
|
|
845
|
+
});
|
|
846
|
+
watch([
|
|
847
|
+
() => toValue(viewerApi),
|
|
848
|
+
() => toValue(container),
|
|
849
|
+
() => toValue(viewer)
|
|
850
|
+
], ([currentViewerApi, containerElementValue, viewerElement]) => {
|
|
851
|
+
if (pdfViewer || !currentViewerApi || !containerElementValue || !viewerElement) return;
|
|
852
|
+
createViewer(currentViewerApi, containerElementValue, viewerElement);
|
|
853
|
+
}, { immediate: true });
|
|
854
|
+
watch(() => toValue(pdfDocument), (loaded) => {
|
|
855
|
+
applyDocument(loaded);
|
|
856
|
+
});
|
|
857
|
+
watch(editorMode, applyEditorMode);
|
|
858
|
+
onScopeDispose(() => {
|
|
859
|
+
removePrint();
|
|
860
|
+
if (!pdfViewer) return;
|
|
861
|
+
pdfViewer.setDocument(null);
|
|
862
|
+
abortController?.abort();
|
|
863
|
+
resizeObserver?.disconnect();
|
|
864
|
+
});
|
|
865
|
+
return {
|
|
866
|
+
actualSize,
|
|
867
|
+
currentPageNumber,
|
|
868
|
+
download,
|
|
869
|
+
drawStoredEditors,
|
|
870
|
+
editorMode,
|
|
871
|
+
findNext,
|
|
872
|
+
findPrevious,
|
|
873
|
+
fitPage,
|
|
874
|
+
fitWidth,
|
|
875
|
+
followBookmark,
|
|
876
|
+
goToPage,
|
|
877
|
+
isMatchCase,
|
|
878
|
+
isNotFound,
|
|
879
|
+
isWholeWord,
|
|
880
|
+
isZoomInDisabled,
|
|
881
|
+
isZoomOutDisabled,
|
|
882
|
+
matchesCount,
|
|
883
|
+
nextPage,
|
|
884
|
+
outline,
|
|
885
|
+
overlays,
|
|
886
|
+
pagesCount,
|
|
887
|
+
pagesRotation,
|
|
888
|
+
previousPage,
|
|
889
|
+
print,
|
|
890
|
+
resetZoom,
|
|
891
|
+
rotate,
|
|
892
|
+
scale,
|
|
893
|
+
searchQuery,
|
|
894
|
+
zoomIn,
|
|
895
|
+
zoomOut
|
|
896
|
+
};
|
|
897
|
+
};
|
|
898
|
+
export { PDF_EDITOR_KEY_PREFIX, PDF_FREE_TEXT_COLOR, PDF_FREE_TEXT_FONT_SIZE, PDF_FULL_TURN, PDF_MAX_CANVAS_AREA, PDF_MAX_SCALE, PDF_MIME_TYPE, PDF_MIN_SCALE, PDF_OPENING_SCALE, PDF_PRINT_BATCH_SIZE, PDF_PRINT_CLASS, PDF_PRINT_SCALE, PDF_PRINT_STYLE, PDF_ROTATION_STEP, PDF_WATERMARK_COLOR, PDF_WATERMARK_FONT_SIZE, PDF_WHEEL_ZOOM_DIVISOR, PDF_ZOOM_STEP, PdfEditorMode, createFreeTextEntry, destroyLoadingTask, getCanvasScale, getDownloadBytes, getPdfPages, getSourceParameters, getZoomedScroll, loadPdfDocument, readXfdf, renderPrintImage, renderPrintSheets, usePdfAnnotations, usePdfDocument, usePdfPage, usePdfThumbnails, usePdfViewer, usePdfZoomGesture, writeXfdf };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vue-pdfjs-headless",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.0",
|
|
4
4
|
"description": "Headless Vue composables over pdfjs-dist: a document's loading, a page's drawing and the viewer's state, with no styling and no renderer of its own.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"headless",
|
|
@@ -46,18 +46,18 @@
|
|
|
46
46
|
"typecheck": "tsrs --fast"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@esposter/shared": "4.
|
|
49
|
+
"@esposter/shared": "4.6.0",
|
|
50
50
|
"neverthrow": "^8.2.0"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
|
-
"@esposter/configuration": "4.
|
|
53
|
+
"@esposter/configuration": "4.6.0",
|
|
54
54
|
"@vue/test-utils": "^2.5.1",
|
|
55
55
|
"happy-dom": "^21.0.4",
|
|
56
|
-
"tsdown": "0.23.
|
|
56
|
+
"tsdown": "^0.23.7"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
|
-
"pdfjs-dist": "^6.
|
|
60
|
-
"vue": "^3.5.
|
|
59
|
+
"pdfjs-dist": "^6.5.438",
|
|
60
|
+
"vue": "^3.5.45"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "0ceea3ffbe12ca69413ea5db2d3cfad197a63cba"
|
|
63
63
|
}
|