@libresign/pdf-elements 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pdf-elements.common.js +25097 -24746
- package/dist/pdf-elements.common.js.map +1 -1
- package/dist/pdf-elements.css +1 -1
- package/dist/pdf-elements.umd.js +25097 -24746
- package/dist/pdf-elements.umd.js.map +1 -1
- package/dist/pdf-elements.umd.min.js +2 -2
- package/dist/pdf-elements.umd.min.js.map +1 -1
- package/package.json +1 -2
- package/src/components/DraggableElement.vue +6 -3
- package/src/components/PDFElements.vue +324 -173
- package/src/components/PDFPage.vue +31 -8
- package/src/index.js +0 -5
- package/src/utils/geometry.js +20 -0
- package/src/utils/measurements.js +16 -0
- package/src/utils/objectStore.js +35 -0
- package/src/utils/pageBounds.js +13 -0
- package/src/utils/zoom.js +8 -0
|
@@ -138,6 +138,11 @@ SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
138
138
|
import PDFPage from './PDFPage.vue'
|
|
139
139
|
import DraggableElement from './DraggableElement.vue'
|
|
140
140
|
import { readAsPDF, readAsArrayBuffer } from '../utils/asyncReader.js'
|
|
141
|
+
import { clampPosition, getVisibleArea } from '../utils/geometry.js'
|
|
142
|
+
import { getViewportWindow, isPageInViewport } from '../utils/pageBounds.js'
|
|
143
|
+
import { applyScaleToDocs } from '../utils/zoom.js'
|
|
144
|
+
import { objectIdExistsInDoc, findObjectPageIndex, updateObjectInDoc, removeObjectFromDoc } from '../utils/objectStore.js'
|
|
145
|
+
import { getCachedMeasurement } from '../utils/measurements.js'
|
|
141
146
|
|
|
142
147
|
export default {
|
|
143
148
|
name: 'PDFElements',
|
|
@@ -208,7 +213,16 @@ export default {
|
|
|
208
213
|
previewPageDocIndex: -1,
|
|
209
214
|
previewPageIndex: -1,
|
|
210
215
|
previewVisible: false,
|
|
211
|
-
|
|
216
|
+
hoverRafId: 0,
|
|
217
|
+
pendingHoverClientPos: null,
|
|
218
|
+
lastHoverRect: null,
|
|
219
|
+
addingListenersAttached: false,
|
|
220
|
+
dragRafId: 0,
|
|
221
|
+
pendingDragClientPos: null,
|
|
222
|
+
pageBoundsVersion: 0,
|
|
223
|
+
lastScrollTop: 0,
|
|
224
|
+
lastClientWidth: 0,
|
|
225
|
+
nextObjectCounter: 0,
|
|
212
226
|
isDraggingElement: false,
|
|
213
227
|
draggingObject: null,
|
|
214
228
|
draggingDocIndex: -1,
|
|
@@ -228,14 +242,16 @@ export default {
|
|
|
228
242
|
lastContainerWidth: 0,
|
|
229
243
|
}
|
|
230
244
|
},
|
|
245
|
+
created() {
|
|
246
|
+
this._pagesBoundingRects = {}
|
|
247
|
+
this._pagesBoundingRectsList = []
|
|
248
|
+
this._pageMeasurementCache = {}
|
|
249
|
+
this._lastPageBoundsScrollTop = 0
|
|
250
|
+
this._lastPageBoundsClientHeight = 0
|
|
251
|
+
},
|
|
231
252
|
mounted() {
|
|
232
253
|
this.boundHandleWheel = this.handleWheel.bind(this)
|
|
233
254
|
this.init()
|
|
234
|
-
document.addEventListener('mousemove', this.handleMouseMove)
|
|
235
|
-
document.addEventListener('touchmove', this.handleMouseMove, { passive: true })
|
|
236
|
-
document.addEventListener('mouseup', this.finishAdding)
|
|
237
|
-
document.addEventListener('touchend', this.finishAdding)
|
|
238
|
-
document.addEventListener('keydown', this.handleKeyDown)
|
|
239
255
|
window.addEventListener('scroll', this.onViewportScroll, { passive: true })
|
|
240
256
|
window.addEventListener('resize', this.onViewportScroll)
|
|
241
257
|
this.$el?.addEventListener('scroll', this.onViewportScroll, { passive: true })
|
|
@@ -252,11 +268,7 @@ export default {
|
|
|
252
268
|
if (this.boundHandleWheel) {
|
|
253
269
|
this.$el?.removeEventListener('wheel', this.boundHandleWheel)
|
|
254
270
|
}
|
|
255
|
-
|
|
256
|
-
document.removeEventListener('touchmove', this.handleMouseMove)
|
|
257
|
-
document.removeEventListener('mouseup', this.finishAdding)
|
|
258
|
-
document.removeEventListener('touchend', this.finishAdding)
|
|
259
|
-
document.removeEventListener('keydown', this.handleKeyDown)
|
|
271
|
+
this.detachAddingListeners()
|
|
260
272
|
window.removeEventListener('scroll', this.onViewportScroll)
|
|
261
273
|
window.removeEventListener('resize', this.onViewportScroll)
|
|
262
274
|
this.$el?.removeEventListener('scroll', this.onViewportScroll)
|
|
@@ -264,6 +276,14 @@ export default {
|
|
|
264
276
|
window.cancelAnimationFrame(this.viewportRafId)
|
|
265
277
|
this.viewportRafId = 0
|
|
266
278
|
}
|
|
279
|
+
if (this.hoverRafId) {
|
|
280
|
+
window.cancelAnimationFrame(this.hoverRafId)
|
|
281
|
+
this.hoverRafId = 0
|
|
282
|
+
}
|
|
283
|
+
if (this.dragRafId) {
|
|
284
|
+
window.cancelAnimationFrame(this.dragRafId)
|
|
285
|
+
this.dragRafId = 0
|
|
286
|
+
}
|
|
267
287
|
},
|
|
268
288
|
methods: {
|
|
269
289
|
async init() {
|
|
@@ -288,7 +308,7 @@ export default {
|
|
|
288
308
|
for (let p = 1; p <= pdfDoc.numPages; p++) {
|
|
289
309
|
const pagePromise = pdfDoc.getPage(p)
|
|
290
310
|
pagePromise.then((page) => {
|
|
291
|
-
pageWidths
|
|
311
|
+
pageWidths.splice(p - 1, 1, page.getViewport({ scale: 1 }).width)
|
|
292
312
|
if (this.autoFitZoom) {
|
|
293
313
|
this.scheduleAutoFitZoom()
|
|
294
314
|
}
|
|
@@ -309,6 +329,7 @@ export default {
|
|
|
309
329
|
}
|
|
310
330
|
|
|
311
331
|
this.pdfDocuments = docs
|
|
332
|
+
this._pageMeasurementCache = {}
|
|
312
333
|
if (docs.length) {
|
|
313
334
|
this.selectedDocIndex = 0
|
|
314
335
|
this.selectedPageIndex = 0
|
|
@@ -338,11 +359,8 @@ export default {
|
|
|
338
359
|
? dragShift
|
|
339
360
|
: { x: 0, y: 0 }
|
|
340
361
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
this.cachePageBounds()
|
|
344
|
-
}
|
|
345
|
-
const pageRect = this.pagesBoundingRects[pageKey]?.rect
|
|
362
|
+
this.cachePageBounds()
|
|
363
|
+
const pageRect = this.getPageRect(docIndex, pageIndex)
|
|
346
364
|
if (pointerOffset && typeof pointerOffset.x === 'number' && typeof pointerOffset.y === 'number') {
|
|
347
365
|
this.draggingInitialMouseOffset.x = pointerOffset.x
|
|
348
366
|
this.draggingInitialMouseOffset.y = pointerOffset.y
|
|
@@ -358,17 +376,22 @@ export default {
|
|
|
358
376
|
this.draggingClientPosition.y = mouseY - this.draggingInitialMouseOffset.y
|
|
359
377
|
}
|
|
360
378
|
|
|
361
|
-
this.cachePageBounds()
|
|
362
379
|
},
|
|
363
380
|
|
|
364
381
|
updateDraggingPosition(clientX, clientY) {
|
|
365
382
|
if (!this.isDraggingElement) return
|
|
366
383
|
|
|
367
|
-
this.
|
|
368
|
-
this.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
384
|
+
this.pendingDragClientPos = { x: clientX, y: clientY }
|
|
385
|
+
if (this.dragRafId) return
|
|
386
|
+
this.dragRafId = window.requestAnimationFrame(() => {
|
|
387
|
+
this.dragRafId = 0
|
|
388
|
+
const pending = this.pendingDragClientPos
|
|
389
|
+
if (!pending) return
|
|
390
|
+
this.lastMouseClientPos.x = pending.x
|
|
391
|
+
this.lastMouseClientPos.y = pending.y
|
|
392
|
+
this.draggingClientPosition.x = pending.x - this.draggingInitialMouseOffset.x
|
|
393
|
+
this.draggingClientPosition.y = pending.y - this.draggingInitialMouseOffset.y
|
|
394
|
+
})
|
|
372
395
|
},
|
|
373
396
|
|
|
374
397
|
stopDraggingElement() {
|
|
@@ -400,10 +423,12 @@ export default {
|
|
|
400
423
|
this.draggingDocIndex = -1
|
|
401
424
|
this.draggingPageIndex = -1
|
|
402
425
|
this.draggingElementShift = { x: 0, y: 0 }
|
|
426
|
+
this.pendingDragClientPos = null
|
|
403
427
|
},
|
|
404
428
|
|
|
405
429
|
startAddingElement(templateObject) {
|
|
406
430
|
if (!this.pdfDocuments.length) return
|
|
431
|
+
this.attachAddingListeners()
|
|
407
432
|
this.isAddingMode = true
|
|
408
433
|
this.previewElement = { ...templateObject }
|
|
409
434
|
this.previewPageDocIndex = 0
|
|
@@ -415,10 +440,29 @@ export default {
|
|
|
415
440
|
|
|
416
441
|
cachePageBounds() {
|
|
417
442
|
const nextRects = {}
|
|
443
|
+
const container = this.$el
|
|
444
|
+
const scrollTop = container?.scrollTop || 0
|
|
445
|
+
const viewHeight = container?.clientHeight || 0
|
|
446
|
+
if (!this.isAddingMode && !this.isDraggingElement &&
|
|
447
|
+
scrollTop === this._lastPageBoundsScrollTop &&
|
|
448
|
+
viewHeight === this._lastPageBoundsClientHeight) {
|
|
449
|
+
return
|
|
450
|
+
}
|
|
451
|
+
this._lastPageBoundsScrollTop = scrollTop
|
|
452
|
+
this._lastPageBoundsClientHeight = viewHeight
|
|
453
|
+
const { minY, maxY } = getViewportWindow(scrollTop, viewHeight)
|
|
418
454
|
for (let docIdx = 0; docIdx < this.pdfDocuments.length; docIdx++) {
|
|
419
455
|
for (let pageIdx = 0; pageIdx < this.pdfDocuments[docIdx].pages.length; pageIdx++) {
|
|
420
456
|
const canvas = this.getPageCanvasElement(docIdx, pageIdx)
|
|
421
457
|
if (!canvas) continue
|
|
458
|
+
if (viewHeight) {
|
|
459
|
+
const wrapper = canvas.closest('.page-wrapper') || canvas
|
|
460
|
+
const offsetTop = wrapper.offsetTop || 0
|
|
461
|
+
const offsetHeight = wrapper.offsetHeight || 0
|
|
462
|
+
if (!isPageInViewport(offsetTop, offsetHeight, minY, maxY)) {
|
|
463
|
+
continue
|
|
464
|
+
}
|
|
465
|
+
}
|
|
422
466
|
const rect = canvas.getBoundingClientRect()
|
|
423
467
|
nextRects[`${docIdx}-${pageIdx}`] = {
|
|
424
468
|
docIndex: docIdx,
|
|
@@ -427,31 +471,71 @@ export default {
|
|
|
427
471
|
}
|
|
428
472
|
}
|
|
429
473
|
}
|
|
430
|
-
this.
|
|
474
|
+
this._pagesBoundingRects = nextRects
|
|
475
|
+
this._pagesBoundingRectsList = Object.values(nextRects)
|
|
476
|
+
this.pageBoundsVersion++
|
|
477
|
+
},
|
|
478
|
+
cachePageBoundsForPage(docIndex, pageIndex) {
|
|
479
|
+
const canvas = this.getPageCanvasElement(docIndex, pageIndex)
|
|
480
|
+
if (!canvas) return
|
|
481
|
+
const rect = canvas.getBoundingClientRect()
|
|
482
|
+
this._pagesBoundingRects = {
|
|
483
|
+
...this._pagesBoundingRects,
|
|
484
|
+
[`${docIndex}-${pageIndex}`]: {
|
|
485
|
+
docIndex,
|
|
486
|
+
pageIndex,
|
|
487
|
+
rect,
|
|
488
|
+
},
|
|
489
|
+
}
|
|
490
|
+
this._pagesBoundingRectsList = Object.values(this._pagesBoundingRects)
|
|
491
|
+
this.pageBoundsVersion++
|
|
492
|
+
},
|
|
493
|
+
getPageBoundsMap() {
|
|
494
|
+
return this._pagesBoundingRects || {}
|
|
495
|
+
},
|
|
496
|
+
getPageBoundsList() {
|
|
497
|
+
return this._pagesBoundingRectsList || []
|
|
498
|
+
},
|
|
499
|
+
getPageRect(docIndex, pageIndex) {
|
|
500
|
+
return this.getPageBoundsMap()[`${docIndex}-${pageIndex}`]?.rect || null
|
|
501
|
+
},
|
|
502
|
+
getPointerPosition(event) {
|
|
503
|
+
if (event?.type?.includes?.('touch')) {
|
|
504
|
+
return {
|
|
505
|
+
x: event.touches?.[0]?.clientX,
|
|
506
|
+
y: event.touches?.[0]?.clientY,
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
x: event?.clientX,
|
|
511
|
+
y: event?.clientY,
|
|
512
|
+
}
|
|
431
513
|
},
|
|
432
514
|
|
|
433
515
|
getDisplayedPageScale(docIndex, pageIndex) {
|
|
516
|
+
this.pageBoundsVersion
|
|
434
517
|
const doc = this.pdfDocuments[docIndex]
|
|
435
518
|
if (!doc) return 1
|
|
436
519
|
const baseWidth = doc.pageWidths?.[pageIndex] || 0
|
|
437
|
-
const
|
|
520
|
+
const pageBoundsMap = this.getPageBoundsMap()
|
|
521
|
+
if (!pageBoundsMap[`${docIndex}-${pageIndex}`]) {
|
|
522
|
+
this.cachePageBoundsForPage(docIndex, pageIndex)
|
|
523
|
+
}
|
|
524
|
+
const rectWidth = this.getPageBoundsMap()[`${docIndex}-${pageIndex}`]?.rect?.width || 0
|
|
438
525
|
if (rectWidth && baseWidth) {
|
|
439
526
|
return rectWidth / baseWidth
|
|
440
527
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
528
|
+
if (this.isAddingMode || this.isDraggingElement) {
|
|
529
|
+
const canvas = this.getPageCanvasElement(docIndex, pageIndex)
|
|
530
|
+
const fallbackRectWidth = canvas?.getBoundingClientRect?.().width || 0
|
|
531
|
+
if (fallbackRectWidth && baseWidth) {
|
|
532
|
+
return fallbackRectWidth / baseWidth
|
|
533
|
+
}
|
|
445
534
|
}
|
|
446
535
|
const base = doc.pagesScale[pageIndex] || 1
|
|
447
536
|
const factor = this.visualScale && this.scale ? (this.visualScale / this.scale) : 1
|
|
448
537
|
return base * factor
|
|
449
538
|
},
|
|
450
|
-
getRenderPageScale(docIndex, pageIndex) {
|
|
451
|
-
const doc = this.pdfDocuments[docIndex]
|
|
452
|
-
if (!doc) return 1
|
|
453
|
-
return doc.pagesScale[pageIndex] || 1
|
|
454
|
-
},
|
|
455
539
|
getPageComponent(docIndex, pageIndex) {
|
|
456
540
|
const pageRef = this.$refs[`page${docIndex}-${pageIndex}`]
|
|
457
541
|
return pageRef && Array.isArray(pageRef) && pageRef[0] ? pageRef[0] : null
|
|
@@ -464,13 +548,22 @@ export default {
|
|
|
464
548
|
onViewportScroll() {
|
|
465
549
|
if (this.viewportRafId) return
|
|
466
550
|
this.viewportRafId = window.requestAnimationFrame(() => {
|
|
551
|
+
const container = this.$el
|
|
552
|
+
const scrollTop = container?.scrollTop || 0
|
|
553
|
+
const clientWidth = container?.clientWidth || 0
|
|
554
|
+
const scrollChanged = scrollTop !== this.lastScrollTop
|
|
555
|
+
const widthChanged = clientWidth !== this.lastClientWidth
|
|
556
|
+
this.lastScrollTop = scrollTop
|
|
557
|
+
this.lastClientWidth = clientWidth
|
|
558
|
+
|
|
467
559
|
if (this.isAddingMode || this.isDraggingElement) {
|
|
468
|
-
|
|
560
|
+
if (scrollChanged || widthChanged) {
|
|
561
|
+
this.cachePageBounds()
|
|
562
|
+
}
|
|
469
563
|
}
|
|
470
564
|
if (this.autoFitZoom && !this.isAddingMode && !this.isDraggingElement) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
this.lastContainerWidth = containerWidth
|
|
565
|
+
if (clientWidth && widthChanged) {
|
|
566
|
+
this.lastContainerWidth = clientWidth
|
|
474
567
|
this.autoFitApplied = false
|
|
475
568
|
this.scheduleAutoFitZoom()
|
|
476
569
|
}
|
|
@@ -481,49 +574,75 @@ export default {
|
|
|
481
574
|
|
|
482
575
|
handleMouseMove(event) {
|
|
483
576
|
if (!this.isAddingMode || !this.previewElement) return
|
|
577
|
+
const { x, y } = this.getPointerPosition(event)
|
|
578
|
+
if (x === undefined || y === undefined) return
|
|
579
|
+
this.pendingHoverClientPos = { x, y }
|
|
580
|
+
if (this.hoverRafId) return
|
|
581
|
+
this.hoverRafId = window.requestAnimationFrame(() => {
|
|
582
|
+
this.hoverRafId = 0
|
|
583
|
+
const pending = this.pendingHoverClientPos
|
|
584
|
+
if (!pending) return
|
|
585
|
+
|
|
586
|
+
const cursorX = pending.x
|
|
587
|
+
const cursorY = pending.y
|
|
588
|
+
let target = null
|
|
589
|
+
|
|
590
|
+
if (this.lastHoverRect &&
|
|
591
|
+
cursorX >= this.lastHoverRect.left && cursorX <= this.lastHoverRect.right &&
|
|
592
|
+
cursorY >= this.lastHoverRect.top && cursorY <= this.lastHoverRect.bottom) {
|
|
593
|
+
target = {
|
|
594
|
+
docIndex: this.previewPageDocIndex,
|
|
595
|
+
pageIndex: this.previewPageIndex,
|
|
596
|
+
rect: this.lastHoverRect,
|
|
597
|
+
}
|
|
598
|
+
} else {
|
|
599
|
+
const rects = this.getPageBoundsList().length
|
|
600
|
+
? this.getPageBoundsList()
|
|
601
|
+
: Object.values(this.getPageBoundsMap())
|
|
602
|
+
for (let i = 0; i < rects.length; i++) {
|
|
603
|
+
const entry = rects[i]
|
|
604
|
+
const rect = entry.rect
|
|
605
|
+
if (cursorX >= rect.left && cursorX <= rect.right &&
|
|
606
|
+
cursorY >= rect.top && cursorY <= rect.bottom) {
|
|
607
|
+
target = entry
|
|
608
|
+
break
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
484
612
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
const { docIndex, pageIndex, rect } = this.pagesBoundingRects[key]
|
|
491
|
-
if (clientX >= rect.left && clientX <= rect.right &&
|
|
492
|
-
clientY >= rect.top && clientY <= rect.bottom) {
|
|
493
|
-
this.previewPageDocIndex = docIndex
|
|
494
|
-
this.previewPageIndex = pageIndex
|
|
495
|
-
foundPage = true
|
|
496
|
-
|
|
497
|
-
const canvasEl = this.getPageCanvasElement(docIndex, pageIndex)
|
|
498
|
-
const pagesScale = this.pdfDocuments[docIndex]?.pagesScale?.[pageIndex] || 1
|
|
499
|
-
const renderWidth = canvasEl?.width || rect.width
|
|
500
|
-
const renderHeight = canvasEl?.height || rect.height
|
|
501
|
-
const layoutScaleX = renderWidth ? rect.width / renderWidth : 1
|
|
502
|
-
const layoutScaleY = renderHeight ? rect.height / renderHeight : 1
|
|
503
|
-
const relX = (clientX - rect.left) / layoutScaleX / pagesScale
|
|
504
|
-
const relY = (clientY - rect.top) / layoutScaleY / pagesScale
|
|
505
|
-
|
|
506
|
-
const pageWidth = renderWidth / pagesScale
|
|
507
|
-
const pageHeight = renderHeight / pagesScale
|
|
508
|
-
this.previewScale.x = pagesScale
|
|
509
|
-
this.previewScale.y = pagesScale
|
|
510
|
-
let x = relX - this.previewElement.width / 2
|
|
511
|
-
let y = relY - this.previewElement.height / 2
|
|
512
|
-
|
|
513
|
-
x = Math.max(0, Math.min(x, pageWidth - this.previewElement.width))
|
|
514
|
-
y = Math.max(0, Math.min(y, pageHeight - this.previewElement.height))
|
|
515
|
-
|
|
516
|
-
this.previewPosition.x = x
|
|
517
|
-
this.previewPosition.y = y
|
|
518
|
-
this.previewVisible = true
|
|
519
|
-
break
|
|
613
|
+
if (!target) {
|
|
614
|
+
this.previewVisible = false
|
|
615
|
+
this.previewScale = { x: 1, y: 1 }
|
|
616
|
+
this.lastHoverRect = null
|
|
617
|
+
return
|
|
520
618
|
}
|
|
521
|
-
}
|
|
522
619
|
|
|
523
|
-
|
|
524
|
-
this.
|
|
525
|
-
this.
|
|
526
|
-
|
|
620
|
+
this.previewPageDocIndex = target.docIndex
|
|
621
|
+
this.previewPageIndex = target.pageIndex
|
|
622
|
+
this.lastHoverRect = target.rect
|
|
623
|
+
const canvasEl = this.getPageCanvasElement(target.docIndex, target.pageIndex)
|
|
624
|
+
const pagesScale = this.pdfDocuments[target.docIndex]?.pagesScale?.[target.pageIndex] || 1
|
|
625
|
+
const renderWidth = canvasEl?.width || target.rect.width
|
|
626
|
+
const renderHeight = canvasEl?.height || target.rect.height
|
|
627
|
+
const layoutScaleX = renderWidth ? target.rect.width / renderWidth : 1
|
|
628
|
+
const layoutScaleY = renderHeight ? target.rect.height / renderHeight : 1
|
|
629
|
+
const relX = (cursorX - target.rect.left) / layoutScaleX / pagesScale
|
|
630
|
+
const relY = (cursorY - target.rect.top) / layoutScaleY / pagesScale
|
|
631
|
+
|
|
632
|
+
const pageWidth = renderWidth / pagesScale
|
|
633
|
+
const pageHeight = renderHeight / pagesScale
|
|
634
|
+
this.previewScale.x = pagesScale
|
|
635
|
+
this.previewScale.y = pagesScale
|
|
636
|
+
let x = relX - this.previewElement.width / 2
|
|
637
|
+
let y = relY - this.previewElement.height / 2
|
|
638
|
+
|
|
639
|
+
x = Math.max(0, Math.min(x, pageWidth - this.previewElement.width))
|
|
640
|
+
y = Math.max(0, Math.min(y, pageHeight - this.previewElement.height))
|
|
641
|
+
|
|
642
|
+
this.previewPosition.x = x
|
|
643
|
+
this.previewPosition.y = y
|
|
644
|
+
this.previewVisible = true
|
|
645
|
+
})
|
|
527
646
|
},
|
|
528
647
|
|
|
529
648
|
handleKeyDown(event) {
|
|
@@ -551,10 +670,9 @@ export default {
|
|
|
551
670
|
|
|
552
671
|
this.scale = newScale
|
|
553
672
|
|
|
554
|
-
this.pdfDocuments.
|
|
555
|
-
doc.pagesScale = doc.pagesScale.map(() => this.scale)
|
|
556
|
-
})
|
|
673
|
+
applyScaleToDocs(this.pdfDocuments, this.scale)
|
|
557
674
|
|
|
675
|
+
this._pageMeasurementCache = {}
|
|
558
676
|
this.cachePageBounds()
|
|
559
677
|
},
|
|
560
678
|
|
|
@@ -564,7 +682,7 @@ export default {
|
|
|
564
682
|
|
|
565
683
|
const objectToAdd = {
|
|
566
684
|
...this.previewElement,
|
|
567
|
-
id:
|
|
685
|
+
id: this.generateObjectId(),
|
|
568
686
|
x: Math.round(this.previewPosition.x),
|
|
569
687
|
y: Math.round(this.previewPosition.y),
|
|
570
688
|
}
|
|
@@ -601,6 +719,30 @@ export default {
|
|
|
601
719
|
this.isAddingMode = false
|
|
602
720
|
this.previewElement = null
|
|
603
721
|
this.previewVisible = false
|
|
722
|
+
this.detachAddingListeners()
|
|
723
|
+
},
|
|
724
|
+
generateObjectId() {
|
|
725
|
+
const counter = this.nextObjectCounter++
|
|
726
|
+
const rand = Math.random().toString(36).slice(2, 8)
|
|
727
|
+
return `obj-${Date.now()}-${counter}-${rand}`
|
|
728
|
+
},
|
|
729
|
+
attachAddingListeners() {
|
|
730
|
+
if (this.addingListenersAttached) return
|
|
731
|
+
this.addingListenersAttached = true
|
|
732
|
+
document.addEventListener('mousemove', this.handleMouseMove)
|
|
733
|
+
document.addEventListener('touchmove', this.handleMouseMove, { passive: true })
|
|
734
|
+
document.addEventListener('mouseup', this.finishAdding)
|
|
735
|
+
document.addEventListener('touchend', this.finishAdding)
|
|
736
|
+
document.addEventListener('keydown', this.handleKeyDown)
|
|
737
|
+
},
|
|
738
|
+
detachAddingListeners() {
|
|
739
|
+
if (!this.addingListenersAttached) return
|
|
740
|
+
this.addingListenersAttached = false
|
|
741
|
+
document.removeEventListener('mousemove', this.handleMouseMove)
|
|
742
|
+
document.removeEventListener('touchmove', this.handleMouseMove, { passive: true })
|
|
743
|
+
document.removeEventListener('mouseup', this.finishAdding)
|
|
744
|
+
document.removeEventListener('touchend', this.finishAdding)
|
|
745
|
+
document.removeEventListener('keydown', this.handleKeyDown)
|
|
604
746
|
},
|
|
605
747
|
|
|
606
748
|
addObjectToPage(object, pageIndex = this.selectedPageIndex, docIndex = this.selectedDocIndex) {
|
|
@@ -611,20 +753,39 @@ export default {
|
|
|
611
753
|
const pageRef = this.getPageComponent(docIndex, pageIndex)
|
|
612
754
|
if (!pageRef) return false
|
|
613
755
|
|
|
756
|
+
let objectToAdd = object
|
|
757
|
+
if (!objectToAdd.id || this.objectIdExists(docIndex, objectToAdd.id)) {
|
|
758
|
+
objectToAdd = { ...objectToAdd, id: this.generateObjectId() }
|
|
759
|
+
}
|
|
760
|
+
|
|
614
761
|
const pageWidth = this.getPageWidth(docIndex, pageIndex)
|
|
615
762
|
const pageHeight = this.getPageHeight(docIndex, pageIndex)
|
|
616
763
|
|
|
617
|
-
if (
|
|
618
|
-
|
|
619
|
-
|
|
764
|
+
if (objectToAdd.x < 0 || objectToAdd.y < 0 ||
|
|
765
|
+
objectToAdd.x + objectToAdd.width > pageWidth ||
|
|
766
|
+
objectToAdd.y + objectToAdd.height > pageHeight) {
|
|
620
767
|
return false
|
|
621
768
|
}
|
|
622
769
|
|
|
623
|
-
doc.allObjects
|
|
624
|
-
|
|
625
|
-
)
|
|
770
|
+
doc.allObjects[pageIndex].push(objectToAdd)
|
|
771
|
+
this.objectIndexCache[`${docIndex}-${objectToAdd.id}`] = pageIndex
|
|
626
772
|
return true
|
|
627
773
|
},
|
|
774
|
+
objectIdExists(docIndex, objectId) {
|
|
775
|
+
if (!objectId) return false
|
|
776
|
+
const cacheKey = `${docIndex}-${objectId}`
|
|
777
|
+
if (this.objectIndexCache[cacheKey] !== undefined) return true
|
|
778
|
+
const doc = this.pdfDocuments[docIndex]
|
|
779
|
+
return objectIdExistsInDoc(doc, objectId)
|
|
780
|
+
},
|
|
781
|
+
updateObjectInPage(docIndex, pageIndex, objectId, payload) {
|
|
782
|
+
const doc = this.pdfDocuments[docIndex]
|
|
783
|
+
updateObjectInDoc(doc, pageIndex, objectId, payload)
|
|
784
|
+
},
|
|
785
|
+
removeObjectFromPage(docIndex, pageIndex, objectId) {
|
|
786
|
+
const doc = this.pdfDocuments[docIndex]
|
|
787
|
+
removeObjectFromDoc(doc, pageIndex, objectId)
|
|
788
|
+
},
|
|
628
789
|
|
|
629
790
|
getAllObjects(docIndex = this.selectedDocIndex) {
|
|
630
791
|
if (docIndex < 0 || docIndex >= this.pdfDocuments.length) return []
|
|
@@ -635,9 +796,9 @@ export default {
|
|
|
635
796
|
doc.allObjects.forEach((pageObjects, pageIndex) => {
|
|
636
797
|
const pageRef = this.getPageComponent(docIndex, pageIndex)
|
|
637
798
|
if (!pageRef) return
|
|
638
|
-
const measurement =
|
|
799
|
+
const measurement = this.getCachedMeasurement(docIndex, pageIndex, pageRef)
|
|
800
|
+
const normalizedCanvasHeight = measurement.height
|
|
639
801
|
const pagesScale = doc.pagesScale[pageIndex] || 1
|
|
640
|
-
const normalizedCanvasHeight = measurement.canvasHeight / pagesScale
|
|
641
802
|
|
|
642
803
|
pageObjects.forEach(object => {
|
|
643
804
|
result.push({
|
|
@@ -667,12 +828,10 @@ export default {
|
|
|
667
828
|
let currentPageIndex = this.objectIndexCache[cacheKey]
|
|
668
829
|
|
|
669
830
|
if (currentPageIndex === undefined) {
|
|
670
|
-
doc
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}
|
|
675
|
-
})
|
|
831
|
+
currentPageIndex = findObjectPageIndex(doc, objectId)
|
|
832
|
+
if (currentPageIndex !== undefined) {
|
|
833
|
+
this.objectIndexCache[cacheKey] = currentPageIndex
|
|
834
|
+
}
|
|
676
835
|
}
|
|
677
836
|
|
|
678
837
|
if (currentPageIndex === undefined) return
|
|
@@ -684,19 +843,18 @@ export default {
|
|
|
684
843
|
const mouseX = payload._mouseX
|
|
685
844
|
const mouseY = payload._mouseY
|
|
686
845
|
|
|
687
|
-
|
|
846
|
+
const pageBoundsMap = this.getPageBoundsMap()
|
|
847
|
+
if (!pageBoundsMap || Object.keys(pageBoundsMap).length === 0) {
|
|
688
848
|
this.cachePageBounds()
|
|
689
849
|
}
|
|
690
850
|
|
|
691
|
-
const currentPageRect = this.
|
|
851
|
+
const currentPageRect = this.getPageRect(docIndex, currentPageIndex)
|
|
692
852
|
if (currentPageRect) {
|
|
693
853
|
const pagesScale = this.getDisplayedPageScale(docIndex, currentPageIndex)
|
|
694
854
|
const relX = (mouseX - currentPageRect.left - this.draggingElementShift.x) / pagesScale - (this.draggingInitialMouseOffset.x / pagesScale)
|
|
695
855
|
const relY = (mouseY - currentPageRect.top - this.draggingElementShift.y) / pagesScale - (this.draggingInitialMouseOffset.y / pagesScale)
|
|
696
856
|
|
|
697
|
-
|
|
698
|
-
obj.id === objectId ? { ...obj, x: relX, y: relY } : obj
|
|
699
|
-
)
|
|
857
|
+
this.updateObjectInPage(docIndex, currentPageIndex, objectId, { x: relX, y: relY })
|
|
700
858
|
}
|
|
701
859
|
return
|
|
702
860
|
}
|
|
@@ -707,6 +865,14 @@ export default {
|
|
|
707
865
|
const objWidth = payload.width !== undefined ? payload.width : targetObject.width
|
|
708
866
|
const objHeight = payload.height !== undefined ? payload.height : targetObject.height
|
|
709
867
|
|
|
868
|
+
const { width: currentPageWidth, height: currentPageHeight } = this.getPageSize(docIndex, currentPageIndex)
|
|
869
|
+
if (newX >= 0 && newY >= 0 &&
|
|
870
|
+
newX + objWidth <= currentPageWidth &&
|
|
871
|
+
newY + objHeight <= currentPageHeight) {
|
|
872
|
+
this.updateObjectInPage(docIndex, currentPageIndex, objectId, payload)
|
|
873
|
+
return
|
|
874
|
+
}
|
|
875
|
+
|
|
710
876
|
let bestPageIndex = currentPageIndex
|
|
711
877
|
let maxVisibleArea = 0
|
|
712
878
|
|
|
@@ -714,30 +880,18 @@ export default {
|
|
|
714
880
|
const pageWidth = this.getPageWidth(docIndex, pIndex)
|
|
715
881
|
const pageHeight = this.getPageHeight(docIndex, pIndex)
|
|
716
882
|
|
|
717
|
-
const
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
if (visibleRight > visibleLeft && visibleBottom > visibleTop) {
|
|
723
|
-
const visibleArea = (visibleRight - visibleLeft) * (visibleBottom - visibleTop)
|
|
724
|
-
if (visibleArea > maxVisibleArea) {
|
|
725
|
-
maxVisibleArea = visibleArea
|
|
726
|
-
bestPageIndex = pIndex
|
|
727
|
-
}
|
|
883
|
+
const visibleArea = getVisibleArea(newX, newY, objWidth, objHeight, pageWidth, pageHeight)
|
|
884
|
+
if (visibleArea > maxVisibleArea) {
|
|
885
|
+
maxVisibleArea = visibleArea
|
|
886
|
+
bestPageIndex = pIndex
|
|
728
887
|
}
|
|
729
888
|
}
|
|
730
889
|
|
|
731
890
|
if (bestPageIndex !== currentPageIndex) {
|
|
732
|
-
const pageWidth = this.
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
const adjustedX = Math.max(0, Math.min(newX, pageWidth - objWidth))
|
|
736
|
-
const adjustedY = Math.max(0, Math.min(newY, pageHeight - objHeight))
|
|
891
|
+
const { width: pageWidth, height: pageHeight } = this.getPageSize(docIndex, bestPageIndex)
|
|
892
|
+
const { x: adjustedX, y: adjustedY } = clampPosition(newX, newY, objWidth, objHeight, pageWidth, pageHeight)
|
|
737
893
|
|
|
738
|
-
|
|
739
|
-
obj => obj.id !== objectId
|
|
740
|
-
)
|
|
894
|
+
this.removeObjectFromPage(docIndex, currentPageIndex, objectId)
|
|
741
895
|
const updatedObject = {
|
|
742
896
|
...targetObject,
|
|
743
897
|
...payload,
|
|
@@ -749,8 +903,7 @@ export default {
|
|
|
749
903
|
return
|
|
750
904
|
}
|
|
751
905
|
|
|
752
|
-
const pageWidth = this.
|
|
753
|
-
const pageHeight = this.getPageHeight(docIndex, currentPageIndex)
|
|
906
|
+
const { width: pageWidth, height: pageHeight } = this.getPageSize(docIndex, currentPageIndex)
|
|
754
907
|
|
|
755
908
|
if (newX < 0 || newY < 0 ||
|
|
756
909
|
newX + objWidth > pageWidth ||
|
|
@@ -759,9 +912,7 @@ export default {
|
|
|
759
912
|
}
|
|
760
913
|
}
|
|
761
914
|
|
|
762
|
-
|
|
763
|
-
objects.map(object => (object.id === objectId ? { ...object, ...payload } : object)),
|
|
764
|
-
)
|
|
915
|
+
this.updateObjectInPage(docIndex, currentPageIndex, objectId, payload)
|
|
765
916
|
},
|
|
766
917
|
|
|
767
918
|
deleteObject(docIndex, objectId) {
|
|
@@ -770,16 +921,16 @@ export default {
|
|
|
770
921
|
let deletedObject = null
|
|
771
922
|
let deletedPageIndex = -1
|
|
772
923
|
|
|
773
|
-
doc.allObjects
|
|
774
|
-
objects.
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
)
|
|
924
|
+
doc.allObjects.some((objects, pageIndex) => {
|
|
925
|
+
const objectIndex = objects.findIndex(object => object.id === objectId)
|
|
926
|
+
if (objectIndex === -1) {
|
|
927
|
+
return false
|
|
928
|
+
}
|
|
929
|
+
deletedObject = objects[objectIndex]
|
|
930
|
+
deletedPageIndex = pageIndex
|
|
931
|
+
objects.splice(objectIndex, 1)
|
|
932
|
+
return true
|
|
933
|
+
})
|
|
783
934
|
delete this.objectIndexCache[`${docIndex}-${objectId}`]
|
|
784
935
|
if (deletedObject) {
|
|
785
936
|
this.$emit('pdf-elements:delete-object', {
|
|
@@ -798,12 +949,10 @@ export default {
|
|
|
798
949
|
let currentPageIndex = this.objectIndexCache[cacheKey]
|
|
799
950
|
|
|
800
951
|
if (currentPageIndex === undefined) {
|
|
801
|
-
doc
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
}
|
|
806
|
-
})
|
|
952
|
+
currentPageIndex = findObjectPageIndex(doc, objectId)
|
|
953
|
+
if (currentPageIndex !== undefined) {
|
|
954
|
+
this.objectIndexCache[cacheKey] = currentPageIndex
|
|
955
|
+
}
|
|
807
956
|
}
|
|
808
957
|
|
|
809
958
|
if (currentPageIndex === undefined) return undefined
|
|
@@ -812,8 +961,9 @@ export default {
|
|
|
812
961
|
if (!targetObject) return currentPageIndex
|
|
813
962
|
|
|
814
963
|
let targetPageIndex = currentPageIndex
|
|
815
|
-
|
|
816
|
-
|
|
964
|
+
const pageBoundsMap = this.getPageBoundsMap()
|
|
965
|
+
for (const key in pageBoundsMap) {
|
|
966
|
+
const { docIndex: rectDocIndex, pageIndex, rect } = pageBoundsMap[key]
|
|
817
967
|
if (rectDocIndex === docIndex &&
|
|
818
968
|
mouseX >= rect.left && mouseX <= rect.right &&
|
|
819
969
|
mouseY >= rect.top && mouseY <= rect.bottom) {
|
|
@@ -822,23 +972,25 @@ export default {
|
|
|
822
972
|
}
|
|
823
973
|
}
|
|
824
974
|
|
|
825
|
-
const targetPageRect = this.
|
|
975
|
+
const targetPageRect = this.getPageRect(docIndex, targetPageIndex)
|
|
826
976
|
if (!targetPageRect) return currentPageIndex
|
|
827
977
|
|
|
828
978
|
const pagesScale = this.getDisplayedPageScale(docIndex, targetPageIndex)
|
|
829
979
|
const relX = (mouseX - targetPageRect.left - this.draggingElementShift.x) / pagesScale - (this.draggingInitialMouseOffset.x / pagesScale)
|
|
830
980
|
const relY = (mouseY - targetPageRect.top - this.draggingElementShift.y) / pagesScale - (this.draggingInitialMouseOffset.y / pagesScale)
|
|
831
981
|
|
|
832
|
-
const pageWidth = this.
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
982
|
+
const { width: pageWidth, height: pageHeight } = this.getPageSize(docIndex, targetPageIndex)
|
|
983
|
+
const { x: clampedX, y: clampedY } = clampPosition(
|
|
984
|
+
relX,
|
|
985
|
+
relY,
|
|
986
|
+
targetObject.width,
|
|
987
|
+
targetObject.height,
|
|
988
|
+
pageWidth,
|
|
989
|
+
pageHeight,
|
|
990
|
+
)
|
|
837
991
|
|
|
838
992
|
if (targetPageIndex !== currentPageIndex) {
|
|
839
|
-
|
|
840
|
-
obj => obj.id !== objectId
|
|
841
|
-
)
|
|
993
|
+
this.removeObjectFromPage(docIndex, currentPageIndex, objectId)
|
|
842
994
|
doc.allObjects[targetPageIndex].push({
|
|
843
995
|
...targetObject,
|
|
844
996
|
x: clampedX,
|
|
@@ -846,9 +998,7 @@ export default {
|
|
|
846
998
|
})
|
|
847
999
|
this.objectIndexCache[cacheKey] = targetPageIndex
|
|
848
1000
|
} else if (clampedX !== targetObject.x || clampedY !== targetObject.y) {
|
|
849
|
-
|
|
850
|
-
obj.id === objectId ? { ...obj, x: clampedX, y: clampedY } : obj
|
|
851
|
-
)
|
|
1001
|
+
this.updateObjectInPage(docIndex, currentPageIndex, objectId, { x: clampedX, y: clampedY })
|
|
852
1002
|
}
|
|
853
1003
|
|
|
854
1004
|
return targetPageIndex
|
|
@@ -856,8 +1006,9 @@ export default {
|
|
|
856
1006
|
|
|
857
1007
|
onMeasure(e, docIndex, pageIndex) {
|
|
858
1008
|
if (docIndex < 0 || docIndex >= this.pdfDocuments.length) return
|
|
859
|
-
this.pdfDocuments[docIndex].pagesScale
|
|
860
|
-
this.
|
|
1009
|
+
this.pdfDocuments[docIndex].pagesScale.splice(pageIndex, 1, e.scale)
|
|
1010
|
+
this._pageMeasurementCache[`${docIndex}-${pageIndex}`] = null
|
|
1011
|
+
this.cachePageBoundsForPage(docIndex, pageIndex)
|
|
861
1012
|
if (this.autoFitZoom) {
|
|
862
1013
|
this.scheduleAutoFitZoom()
|
|
863
1014
|
}
|
|
@@ -871,16 +1022,26 @@ export default {
|
|
|
871
1022
|
getPageWidth(docIndex, pageIndex) {
|
|
872
1023
|
const pageRef = this.getPageComponent(docIndex, pageIndex)
|
|
873
1024
|
if (!pageRef) return 0
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
return pageRef.getCanvasMeasurement().canvasWidth / pagesScale
|
|
1025
|
+
const measurement = this.getCachedMeasurement(docIndex, pageIndex, pageRef)
|
|
1026
|
+
return measurement.width
|
|
877
1027
|
},
|
|
878
1028
|
getPageHeight(docIndex, pageIndex) {
|
|
879
1029
|
const pageRef = this.getPageComponent(docIndex, pageIndex)
|
|
880
1030
|
if (!pageRef) return 0
|
|
1031
|
+
const measurement = this.getCachedMeasurement(docIndex, pageIndex, pageRef)
|
|
1032
|
+
return measurement.height
|
|
1033
|
+
},
|
|
1034
|
+
getPageSize(docIndex, pageIndex) {
|
|
1035
|
+
return {
|
|
1036
|
+
width: this.getPageWidth(docIndex, pageIndex),
|
|
1037
|
+
height: this.getPageHeight(docIndex, pageIndex),
|
|
1038
|
+
}
|
|
1039
|
+
},
|
|
1040
|
+
getCachedMeasurement(docIndex, pageIndex, pageRef) {
|
|
1041
|
+
const cacheKey = `${docIndex}-${pageIndex}`
|
|
881
1042
|
const doc = this.pdfDocuments[docIndex]
|
|
882
1043
|
const pagesScale = doc.pagesScale[pageIndex] || 1
|
|
883
|
-
return
|
|
1044
|
+
return getCachedMeasurement(this._pageMeasurementCache, cacheKey, pageRef, pagesScale)
|
|
884
1045
|
},
|
|
885
1046
|
calculateOptimalScale(maxPageWidth) {
|
|
886
1047
|
const containerWidth = this.$el?.clientWidth || 0
|
|
@@ -924,9 +1085,8 @@ export default {
|
|
|
924
1085
|
if (Math.abs(optimalScale - this.scale) > 0.01) {
|
|
925
1086
|
this.scale = optimalScale
|
|
926
1087
|
this.visualScale = optimalScale
|
|
927
|
-
this.pdfDocuments.
|
|
928
|
-
|
|
929
|
-
})
|
|
1088
|
+
applyScaleToDocs(this.pdfDocuments, this.scale)
|
|
1089
|
+
this._pageMeasurementCache = {}
|
|
930
1090
|
this.cachePageBounds()
|
|
931
1091
|
}
|
|
932
1092
|
},
|
|
@@ -968,15 +1128,6 @@ export default {
|
|
|
968
1128
|
opacity: 0.7;
|
|
969
1129
|
pointer-events: none;
|
|
970
1130
|
}
|
|
971
|
-
.drag-ghost {
|
|
972
|
-
position: fixed;
|
|
973
|
-
opacity: 0.9;
|
|
974
|
-
pointer-events: none;
|
|
975
|
-
z-index: 10000;
|
|
976
|
-
transform-origin: top left;
|
|
977
|
-
transition: none;
|
|
978
|
-
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
|
|
979
|
-
}
|
|
980
1131
|
.overlay {
|
|
981
1132
|
position: absolute;
|
|
982
1133
|
top: 0;
|