@libresign/pdf-elements 0.4.0 → 1.0.1
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 +24 -1
- package/dist/components/DraggableElement.vue.d.ts +240 -0
- package/dist/components/PDFElements.vue.d.ts +552 -0
- package/dist/components/PDFPage.vue.d.ts +40 -0
- package/dist/index.css +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +1293 -0
- package/dist/pdf-uHvE5neP.mjs +17593 -0
- package/dist/pdf.worker.min-DmO9Xdfo.mjs +4 -0
- package/dist/types.d.ts +30 -0
- package/dist/utils/asyncReader.d.ts +10 -0
- package/dist/utils/geometry.d.ts +5 -0
- package/dist/utils/measurements.d.ts +12 -0
- package/dist/utils/objectStore.d.ts +5 -0
- package/dist/utils/pageBounds.d.ts +5 -0
- package/dist/utils/zoom.d.ts +2 -0
- package/package.json +48 -21
- package/src/components/DraggableElement.vue +18 -15
- package/src/components/PDFElements.vue +87 -60
- package/src/components/PDFPage.vue +11 -7
- package/src/index.ts +18 -0
- package/src/types.ts +35 -0
- package/src/utils/asyncReader.ts +103 -0
- package/src/utils/{geometry.js → geometry.ts} +16 -2
- package/src/utils/{measurements.js → measurements.ts} +20 -1
- package/src/utils/{objectStore.js → objectStore.ts} +15 -4
- package/src/utils/{pageBounds.js → pageBounds.ts} +2 -2
- package/src/utils/{zoom.js → zoom.ts} +3 -1
- package/dist/demo.html +0 -1
- package/dist/pdf-elements.common.js +0 -31760
- package/dist/pdf-elements.common.js.map +0 -1
- package/dist/pdf-elements.css +0 -1
- package/dist/pdf-elements.umd.js +0 -31772
- package/dist/pdf-elements.umd.js.map +0 -1
- package/dist/pdf-elements.umd.min.js +0 -2
- package/dist/pdf-elements.umd.min.js.map +0 -1
- package/dist/pdf.worker.min.mjs +0 -22
- package/src/index.js +0 -17
- package/src/utils/asyncReader.js +0 -44
|
@@ -7,12 +7,15 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
7
7
|
<canvas ref="canvas" />
|
|
8
8
|
</template>
|
|
9
9
|
|
|
10
|
-
<script>
|
|
11
|
-
|
|
10
|
+
<script lang="ts">
|
|
11
|
+
import { defineComponent, type PropType } from 'vue'
|
|
12
|
+
|
|
13
|
+
export default defineComponent({
|
|
12
14
|
name: 'PDFPage',
|
|
15
|
+
emits: ['onMeasure'],
|
|
13
16
|
props: {
|
|
14
17
|
page: {
|
|
15
|
-
type: Promise
|
|
18
|
+
type: Object as PropType<Promise<unknown>>,
|
|
16
19
|
required: true,
|
|
17
20
|
},
|
|
18
21
|
scale: {
|
|
@@ -25,7 +28,7 @@ export default {
|
|
|
25
28
|
dynamicScale: this.scale,
|
|
26
29
|
isRendering: false,
|
|
27
30
|
pendingRender: false,
|
|
28
|
-
renderTask: null,
|
|
31
|
+
renderTask: null as { cancel: () => void; promise: Promise<void> } | null,
|
|
29
32
|
}
|
|
30
33
|
},
|
|
31
34
|
watch: {
|
|
@@ -41,7 +44,7 @@ export default {
|
|
|
41
44
|
if (this.renderTask) {
|
|
42
45
|
try {
|
|
43
46
|
this.renderTask.cancel()
|
|
44
|
-
} catch
|
|
47
|
+
} catch {
|
|
45
48
|
// Ignore render cancellation errors.
|
|
46
49
|
}
|
|
47
50
|
this.renderTask = null
|
|
@@ -73,12 +76,13 @@ export default {
|
|
|
73
76
|
if (this.renderTask) {
|
|
74
77
|
try {
|
|
75
78
|
this.renderTask.cancel()
|
|
76
|
-
} catch
|
|
79
|
+
} catch {
|
|
77
80
|
// Ignore render cancellation errors.
|
|
78
81
|
}
|
|
79
82
|
this.renderTask = null
|
|
80
83
|
}
|
|
81
84
|
const context = canvas.getContext('2d')
|
|
85
|
+
if (!context) return
|
|
82
86
|
const viewport = _page.getViewport({
|
|
83
87
|
scale: this.dynamicScale,
|
|
84
88
|
})
|
|
@@ -100,7 +104,7 @@ export default {
|
|
|
100
104
|
}
|
|
101
105
|
},
|
|
102
106
|
},
|
|
103
|
-
}
|
|
107
|
+
})
|
|
104
108
|
</script>
|
|
105
109
|
|
|
106
110
|
<style scoped>
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
import type { App } from 'vue'
|
|
5
|
+
import PDFElements from './components/PDFElements.vue'
|
|
6
|
+
|
|
7
|
+
export { setWorkerPath } from './utils/asyncReader'
|
|
8
|
+
export type { PDFDocumentEntry, PDFElementObject, PDFElementsPublicApi } from './types'
|
|
9
|
+
|
|
10
|
+
const install = (app: App) => {
|
|
11
|
+
const name = PDFElements.name || 'PDFElements'
|
|
12
|
+
app.component(name, PDFElements)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
;(PDFElements as { install?: (app: App) => void }).install = install
|
|
16
|
+
|
|
17
|
+
export default PDFElements
|
|
18
|
+
export { PDFElements }
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
export interface PDFElementObject {
|
|
5
|
+
id?: string
|
|
6
|
+
x: number
|
|
7
|
+
y: number
|
|
8
|
+
width: number
|
|
9
|
+
height: number
|
|
10
|
+
type?: string
|
|
11
|
+
label?: string
|
|
12
|
+
icon?: string
|
|
13
|
+
resizable?: boolean
|
|
14
|
+
[key: string]: unknown
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PDFDocumentEntry {
|
|
18
|
+
name: string
|
|
19
|
+
file: unknown
|
|
20
|
+
pdfDoc: unknown
|
|
21
|
+
numPages: number
|
|
22
|
+
pages: Promise<unknown>[]
|
|
23
|
+
pageWidths: number[]
|
|
24
|
+
pagesScale: number[]
|
|
25
|
+
allObjects: PDFElementObject[][]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface PDFElementsPublicApi {
|
|
29
|
+
startAddingElement: (templateObject: PDFElementObject) => void
|
|
30
|
+
addObjectToPage: (object: PDFElementObject, pageIndex?: number, docIndex?: number) => boolean
|
|
31
|
+
getAllObjects: (docIndex?: number) => PDFElementObject[]
|
|
32
|
+
updateObject: (docIndex: number, objectId: string, payload: Partial<PDFElementObject>) => void
|
|
33
|
+
deleteObject: (docIndex: number, objectId: string) => void
|
|
34
|
+
duplicateObject: (docIndex: number, objectId: string) => void
|
|
35
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
|
|
2
|
+
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
import type { PDFDocumentProxy, PDFWorker } from 'pdfjs-dist'
|
|
5
|
+
|
|
6
|
+
let sharedWorker: PDFWorker | null = null
|
|
7
|
+
let pdfjsPromise: Promise<typeof import('pdfjs-dist')> | null = null
|
|
8
|
+
let workerUrlPromise: Promise<string> | null = null
|
|
9
|
+
let workerSrcOverride: string | null = null
|
|
10
|
+
|
|
11
|
+
function loadPdfjs() {
|
|
12
|
+
if (!pdfjsPromise) {
|
|
13
|
+
pdfjsPromise = import('pdfjs-dist')
|
|
14
|
+
}
|
|
15
|
+
return pdfjsPromise
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function loadWorkerUrl() {
|
|
19
|
+
if (!workerUrlPromise) {
|
|
20
|
+
workerUrlPromise = import('pdfjs-dist/build/pdf.worker.min.mjs?url').then(
|
|
21
|
+
(mod) => mod.default as string
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
return workerUrlPromise
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function ensureWorkerSrc(pdfjs: typeof import('pdfjs-dist')) {
|
|
28
|
+
if (workerSrcOverride) {
|
|
29
|
+
pdfjs.GlobalWorkerOptions.workerSrc = workerSrcOverride
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
if (!pdfjs.GlobalWorkerOptions.workerSrc) {
|
|
33
|
+
pdfjs.GlobalWorkerOptions.workerSrc = await loadWorkerUrl()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function getSharedWorker(pdfjs: typeof import('pdfjs-dist')): Promise<PDFWorker> {
|
|
38
|
+
if (!sharedWorker) {
|
|
39
|
+
await ensureWorkerSrc(pdfjs)
|
|
40
|
+
sharedWorker = new pdfjs.PDFWorker({}) as PDFWorker
|
|
41
|
+
}
|
|
42
|
+
return sharedWorker
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function setWorkerPath(path: string) {
|
|
46
|
+
workerSrcOverride = path
|
|
47
|
+
if (pdfjsPromise) {
|
|
48
|
+
pdfjsPromise.then((pdfjs) => {
|
|
49
|
+
pdfjs.GlobalWorkerOptions.workerSrc = path
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function readAsArrayBuffer(file: Blob): Promise<ArrayBuffer> {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
const reader = new FileReader()
|
|
57
|
+
reader.onload = () => resolve(reader.result as ArrayBuffer)
|
|
58
|
+
reader.onerror = reject
|
|
59
|
+
reader.readAsArrayBuffer(file)
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
type PdfInput =
|
|
64
|
+
| string
|
|
65
|
+
| ArrayBuffer
|
|
66
|
+
| ArrayBufferView
|
|
67
|
+
| Blob
|
|
68
|
+
| {
|
|
69
|
+
url?: string
|
|
70
|
+
data?: ArrayBuffer | Uint8Array
|
|
71
|
+
[key: string]: unknown
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function readAsPDF(
|
|
75
|
+
file: PdfInput,
|
|
76
|
+
options: Record<string, unknown> = {}
|
|
77
|
+
): Promise<PDFDocumentProxy> {
|
|
78
|
+
const pdfjs = await loadPdfjs()
|
|
79
|
+
const worker = await getSharedWorker(pdfjs)
|
|
80
|
+
const isArrayBuffer = file instanceof ArrayBuffer
|
|
81
|
+
const isView = ArrayBuffer.isView(file)
|
|
82
|
+
const isBlob = typeof Blob !== 'undefined' && file instanceof Blob
|
|
83
|
+
|
|
84
|
+
if (file && typeof file === 'object' && !isArrayBuffer && !isView && !isBlob) {
|
|
85
|
+
return pdfjs.getDocument({ ...(file as Record<string, unknown>), ...options, worker }).promise
|
|
86
|
+
}
|
|
87
|
+
if (typeof file === 'string') {
|
|
88
|
+
return pdfjs.getDocument({ url: file, ...options, worker }).promise
|
|
89
|
+
}
|
|
90
|
+
if (isBlob) {
|
|
91
|
+
const data = await readAsArrayBuffer(file as Blob)
|
|
92
|
+
return pdfjs.getDocument({ data, ...options, worker }).promise
|
|
93
|
+
}
|
|
94
|
+
const data = isArrayBuffer
|
|
95
|
+
? (file as ArrayBuffer)
|
|
96
|
+
: new Uint8Array(
|
|
97
|
+
(file as ArrayBufferView).buffer,
|
|
98
|
+
(file as ArrayBufferView).byteOffset,
|
|
99
|
+
(file as ArrayBufferView).byteLength
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
return pdfjs.getDocument({ data, ...options, worker }).promise
|
|
103
|
+
}
|
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
-
export function clampPosition(
|
|
4
|
+
export function clampPosition(
|
|
5
|
+
x: number,
|
|
6
|
+
y: number,
|
|
7
|
+
width: number,
|
|
8
|
+
height: number,
|
|
9
|
+
pageWidth: number,
|
|
10
|
+
pageHeight: number
|
|
11
|
+
) {
|
|
5
12
|
return {
|
|
6
13
|
x: Math.max(0, Math.min(x, pageWidth - width)),
|
|
7
14
|
y: Math.max(0, Math.min(y, pageHeight - height)),
|
|
8
15
|
}
|
|
9
16
|
}
|
|
10
17
|
|
|
11
|
-
export function getVisibleArea(
|
|
18
|
+
export function getVisibleArea(
|
|
19
|
+
newX: number,
|
|
20
|
+
newY: number,
|
|
21
|
+
objWidth: number,
|
|
22
|
+
objHeight: number,
|
|
23
|
+
pageWidth: number,
|
|
24
|
+
pageHeight: number
|
|
25
|
+
) {
|
|
12
26
|
const visibleLeft = Math.max(0, newX)
|
|
13
27
|
const visibleTop = Math.max(0, newY)
|
|
14
28
|
const visibleRight = Math.min(pageWidth, newX + objWidth)
|
|
@@ -1,7 +1,26 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export interface CanvasMeasurement {
|
|
5
|
+
canvasWidth: number
|
|
6
|
+
canvasHeight: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface PageMeasurement {
|
|
10
|
+
width: number
|
|
11
|
+
height: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface PageMeasurementProvider {
|
|
15
|
+
getCanvasMeasurement: () => CanvasMeasurement
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getCachedMeasurement(
|
|
19
|
+
cache: Record<string, PageMeasurement>,
|
|
20
|
+
cacheKey: string,
|
|
21
|
+
pageRef: PageMeasurementProvider,
|
|
22
|
+
pagesScale: number
|
|
23
|
+
) {
|
|
5
24
|
const cached = cache[cacheKey]
|
|
6
25
|
if (cached) {
|
|
7
26
|
return cached
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import type { PDFDocumentEntry, PDFElementObject } from '../types'
|
|
5
|
+
|
|
6
|
+
export function objectIdExistsInDoc(doc: PDFDocumentEntry | undefined, objectId: string) {
|
|
5
7
|
if (!doc || !objectId) return false
|
|
6
8
|
return doc.allObjects.some((objects) => objects.some((obj) => obj.id === objectId))
|
|
7
9
|
}
|
|
8
10
|
|
|
9
|
-
export function findObjectPageIndex(doc, objectId) {
|
|
11
|
+
export function findObjectPageIndex(doc: PDFDocumentEntry | undefined, objectId: string) {
|
|
10
12
|
if (!doc || !objectId) return undefined
|
|
11
13
|
for (let pageIndex = 0; pageIndex < doc.allObjects.length; pageIndex++) {
|
|
12
14
|
if (doc.allObjects[pageIndex].some((obj) => obj.id === objectId)) {
|
|
@@ -16,7 +18,12 @@ export function findObjectPageIndex(doc, objectId) {
|
|
|
16
18
|
return undefined
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
export function updateObjectInDoc(
|
|
21
|
+
export function updateObjectInDoc(
|
|
22
|
+
doc: PDFDocumentEntry | undefined,
|
|
23
|
+
pageIndex: number,
|
|
24
|
+
objectId: string,
|
|
25
|
+
payload: Partial<PDFElementObject>
|
|
26
|
+
) {
|
|
20
27
|
const objects = doc?.allObjects?.[pageIndex]
|
|
21
28
|
if (!objects) return false
|
|
22
29
|
const objectIndex = objects.findIndex((obj) => obj.id === objectId)
|
|
@@ -25,7 +32,11 @@ export function updateObjectInDoc(doc, pageIndex, objectId, payload) {
|
|
|
25
32
|
return true
|
|
26
33
|
}
|
|
27
34
|
|
|
28
|
-
export function removeObjectFromDoc(
|
|
35
|
+
export function removeObjectFromDoc(
|
|
36
|
+
doc: PDFDocumentEntry | undefined,
|
|
37
|
+
pageIndex: number,
|
|
38
|
+
objectId: string
|
|
39
|
+
) {
|
|
29
40
|
const objects = doc?.allObjects?.[pageIndex]
|
|
30
41
|
if (!objects) return false
|
|
31
42
|
const objectIndex = objects.findIndex((obj) => obj.id === objectId)
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
-
export function getViewportWindow(scrollTop, viewHeight, margin = 300) {
|
|
4
|
+
export function getViewportWindow(scrollTop: number, viewHeight: number, margin = 300) {
|
|
5
5
|
return {
|
|
6
6
|
minY: Math.max(0, scrollTop - margin),
|
|
7
7
|
maxY: scrollTop + viewHeight + margin,
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function isPageInViewport(offsetTop, offsetHeight, minY, maxY) {
|
|
11
|
+
export function isPageInViewport(offsetTop: number, offsetHeight: number, minY: number, maxY: number) {
|
|
12
12
|
return !(offsetTop + offsetHeight < minY || offsetTop > maxY)
|
|
13
13
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 LibreCode coop and contributors
|
|
2
2
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
import type { PDFDocumentEntry } from '../types'
|
|
5
|
+
|
|
6
|
+
export function applyScaleToDocs(docs: PDFDocumentEntry[], scale: number) {
|
|
5
7
|
docs.forEach((doc) => {
|
|
6
8
|
doc.pagesScale = doc.pagesScale.map(() => scale)
|
|
7
9
|
})
|
package/dist/demo.html
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<!doctype html><meta charset="utf-8"><title>pdf-elements demo</title><script src="./pdf-elements.umd.js"></script><link rel="stylesheet" href="./pdf-elements.css"><script>console.log(pdf-elements)</script>
|