Browse Source

Merge branch 'feature/webassembly'

liutian 1 năm trước cách đây
mục cha
commit
9c36e6fe5a
62 tập tin đã thay đổi với 1101 bổ sung1300 xóa
  1. 1 1
      packages/core/rollup.config.js
  2. 29 25
      packages/core/src/TextSearch.ts
  3. 1 1
      packages/core/src/TextSelection.ts
  4. 15 25
      packages/core/src/annotation/freetext.js
  5. 6 0
      packages/core/src/annotation/index.ts
  6. 12 3
      packages/core/src/annotation/ink.js
  7. 38 8
      packages/core/src/annotation/layer.js
  8. 18 46
      packages/core/src/annotation/line.js
  9. 14 24
      packages/core/src/annotation/link.js
  10. 6 7
      packages/core/src/annotation/paint/freetext.js
  11. 3 0
      packages/core/src/annotation/paint/link.js
  12. 6 0
      packages/core/src/annotation/paint/shape.js
  13. 2 0
      packages/core/src/annotation/paint/text.js
  14. 14 25
      packages/core/src/annotation/shape.js
  15. 14 36
      packages/core/src/annotation/stamp.js
  16. 38 11
      packages/core/src/annotation/text.js
  17. 5 28
      packages/core/src/annotation/utils.js
  18. 1 1
      packages/core/src/color.js
  19. 22 14
      packages/core/src/editor/content_container.js
  20. 3 2
      packages/core/src/fileHandler.ts
  21. 3 0
      packages/core/src/form/add_check_box.js
  22. 3 0
      packages/core/src/form/add_combo_box.js
  23. 3 0
      packages/core/src/form/add_list_box.js
  24. 3 0
      packages/core/src/form/add_push_button.js
  25. 3 0
      packages/core/src/form/add_radio_button.js
  26. 3 0
      packages/core/src/form/add_text_field.js
  27. 15 6
      packages/core/src/form/check_box.js
  28. 17 7
      packages/core/src/form/combo_box.js
  29. 17 7
      packages/core/src/form/list_box.js
  30. 15 6
      packages/core/src/form/push_button.js
  31. 15 6
      packages/core/src/form/radio_button.js
  32. 19 6
      packages/core/src/form/text_field.js
  33. 63 33
      packages/core/src/index.js
  34. 1 0
      packages/core/src/ink_sign.js
  35. 7 3
      packages/core/src/markup/text_annotation.js
  36. 0 11
      packages/core/src/pdf_annotation_layer.js
  37. 2 561
      packages/core/src/pdf_link_service.js
  38. 406 285
      packages/core/src/pdf_page_view.js
  39. 3 1
      packages/core/src/pdf_presentation_mode.js
  40. 2 2
      packages/core/src/pdf_rendering_queue.js
  41. 4 8
      packages/core/src/pdf_thumbnail_view.js
  42. 1 0
      packages/core/src/pdf_thumbnail_viewer.js
  43. 79 52
      packages/core/src/pdf_viewer.js
  44. 48 13
      packages/core/src/ui_utils.js
  45. 16 8
      packages/core/src/worker/compdfkit_worker.js
  46. 1 1
      packages/webview/locales/en.json
  47. 1 1
      packages/webview/locales/zh-CN.json
  48. 9 2
      packages/webview/src/assets/main.scss
  49. 5 1
      packages/webview/src/components/Annotate/Annotate.vue
  50. 1 1
      packages/webview/src/components/AnnotationContainer/AnnotationContent.vue
  51. 0 1
      packages/webview/src/components/CompareDocumentContainer/CompareDocumentContainer.vue
  52. 32 5
      packages/webview/src/components/DocumentContainer/DocumentContainer.vue
  53. 2 0
      packages/webview/src/components/RightPanel/RightPanel.vue
  54. 1 1
      packages/webview/src/components/RightPanelPageMode/RightPanelPageMode.vue
  55. 4 4
      packages/webview/src/components/SearchContainer/SearchHeader.vue
  56. 7 0
      packages/webview/src/components/ToggleRightPanelButton/index.vue
  57. 3 0
      packages/webview/src/core/getSelectedText.js
  58. 5 1
      packages/webview/src/core/index.js
  59. 3 0
      packages/webview/src/core/removeEvent.js
  60. 22 0
      packages/webview/src/helpers/hotkeysManager.js
  61. 1 0
      packages/webview/src/helpers/utils.js
  62. 8 10
      packages/webview/src/stores/modules/document.js

+ 1 - 1
packages/core/rollup.config.js

@@ -6,7 +6,7 @@ import json from '@rollup/plugin-json'
 import ts from "rollup-plugin-typescript2"
 
 const extensions = [".js", ".ts"]
-const reserved = ['ComPDFKitJS', 'DataArray', 'LineType', 'PageSize', 'Rect', 'RGBColor', 'FontDa', 'PDFDestination', 'WidgetItem', 'WidgetItemsArray', 'TextFindItem', 'EditCharPlace', 'EndEditCharPlace', 'RectArray', 'CursorPoints', 'EditTextStyle', 'U8StringData', 'PDFRange', 'TextRectArray']
+const reserved = ['ComPDFKitJS', 'DataArray', 'LineType', 'PageSize', 'Rect', 'RGBColor', 'FontDa', 'PDFDestination', 'WidgetItem', 'WidgetItemsArray', 'TextFindItemArray', 'EditCharPlace', 'EndEditCharPlace', 'RectArray', 'CursorPoints', 'EditTextStyle', 'U8StringData', 'PDFRange', 'TextRectArray']
 
 const plugins = [
   json(),

+ 29 - 25
packages/core/src/TextSearch.ts

@@ -1,18 +1,20 @@
 type TextSearchResult = {
-  bottom: number
   content: string
-  left: number
+  quads: quad[]
   pageNum: number
-  right: number
   pageSearchIndex: number
   searchValue: string
+}
+type quad = {
+  bottom: number
+  left: number
+  right: number
   top: number
 }
 
 export default class TextSearch {
   searchContainer: HTMLDivElement | null = null
   activeIndex = 0
-  activeSearchResult: TextSearchResult | null = null
   container: HTMLDivElement | null = null
   viewport: any
   scale: number = 0
@@ -52,15 +54,13 @@ export default class TextSearch {
   }
 
   setActiveSearchResult(search: TextSearchResult) {
-    const { pageNum, left, top, right, bottom} = search
-    const activeSearchResult = this.activeSearchResult
+    const { pageNum } = search
 
-    if (search && ((activeSearchResult && (activeSearchResult.pageNum !== pageNum || left !== activeSearchResult.left || top !== activeSearchResult.top || right !== activeSearchResult.right || bottom !== activeSearchResult.bottom)) || !activeSearchResult)) {
+    if (search) {
       if (this.searchContainer) {
-        const searchEles = this.searchContainer.querySelectorAll('div')
+        const searchEles = this.searchContainer.querySelectorAll('.searchItems')
         searchEles[search.pageSearchIndex].classList.add('selected')
       }
-      this.activeSearchResult = search
       this.activeIndex = pageNum - 1
     }
   }
@@ -68,10 +68,9 @@ export default class TextSearch {
   clearActiveSearchResult(result: TextSearchResult) {
     if (this.searchContainer) {
       const index = result.pageSearchIndex
-      const searchEle = this.searchContainer.querySelectorAll('div')[index]
+      const searchEle = this.searchContainer.querySelectorAll('.searchItems')[index]
       searchEle.classList.remove('selected')
     }
-    this.activeSearchResult = null
   }
 
   renderSearchResults() {
@@ -88,21 +87,26 @@ export default class TextSearch {
         continue
       }
       const result = this.searchResults[i]
-      const { left, top, right, bottom } = result
-      const bounds = {
-        left: left * scale,
-        top: top * scale,
-        width: right * scale - left * scale,
-        height: bottom * scale - top * scale
+      const searchItems = document.createElement('div')
+      searchItems.className = 'searchItems'
+      for (let j = 0; j < result.quads.length; j++) {
+        const { left, top, right, bottom } = result.quads[j]
+        const bounds = {
+          left: left * scale,
+          top: top * scale,
+          width: right * scale - left * scale,
+          height: bottom * scale - top * scale
+        }
+        const div = document.createElement('div')
+        div.className = 'highlight'
+        div.style.position = 'absolute'
+        div.style.left = `${bounds.left}px`
+        div.style.top = `${bounds.top}px`
+        div.style.width = `${bounds.width}px`
+        div.style.height = `${bounds.height}px`
+        searchItems.appendChild(div)
       }
-      const div = document.createElement('div')
-      div.className = 'highlight'
-      div.style.position = 'absolute'
-      div.style.left = `${bounds.left}px`
-      div.style.top = `${bounds.top}px`
-      div.style.width = `${bounds.width}px`
-      div.style.height = `${bounds.height}px`
-      this.searchContainer!.appendChild(div)
+      this.searchContainer!.appendChild(searchItems)
     }
   }
 

+ 1 - 1
packages/core/src/TextSelection.ts

@@ -134,7 +134,7 @@ export default class TextSelection {
   }
 
   handleKeyDown(event: KeyboardEvent) {
-    if (event.key.toLocaleLowerCase() === 'c' && event.ctrlKey) {
+    if (event.key.toLocaleLowerCase() === 'c' && (event.ctrlKey || event.metaKey)) {
       const text = this._selection?.textContent
       if (text) {
         copy(text)

+ 15 - 25
packages/core/src/annotation/freetext.js

@@ -520,29 +520,12 @@ export default class Shape extends Base {
     const right = this.rightBottom.x
     const bottom = this.rightBottom.y
 
-    const { width, height, rotation, scale: s  } = this.viewport;
+    const { scale: s  } = this.viewport;
     let x1, y1, x2, y2;
-    if (rotation === 0) {
-      x1 = left / s;
-      y1 = top / s; 
-      x2 = right / s;
-      y2 = bottom / s;
-    } else if (rotation === 90) {
-      x1 = bottom / s;
-      y1 = right / s;
-      x2 = top / s;
-      y2 = left / s;
-    } else if (rotation === 180) {
-      x1 = (width - left) / s;
-      y1 = top / s;
-      x2 = (width - right) / s;
-      y2 = bottom / s;
-    } else {
-      x1 = (height - bottom) / s
-      y1 = (width - left) / s
-      x2 = (height - top) / s
-      y2 = (width - right)  / s
-    }
+    x1 = left / s;
+    y1 = top / s; 
+    x2 = right / s;
+    y2 = bottom / s;
     return {
       leftTop: {
         x: x1,
@@ -598,6 +581,7 @@ export default class Shape extends Base {
   }
 
   handleMouseDown (event) {
+    if (event.button !== 0 && event.type === 'mousedown') return
     if (this.layer.tool) {
       event.stopPropagation()
     }
@@ -616,6 +600,7 @@ export default class Shape extends Base {
   };
 
   handleMouseMove (event) {
+    if (event.button !== 0 && event.type === 'mousemove') return
     if (event.type === 'touchmove') {
       document.body.style.overscrollBehavior = 'none';
     }
@@ -752,6 +737,7 @@ export default class Shape extends Base {
   }
 
   handleMouseUp (event) {
+    if (event.button !== 0 && event.type === 'mouseup') return
     if (this.layer.tool) {
       event.stopPropagation()
     }
@@ -855,13 +841,13 @@ export default class Shape extends Base {
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.annotationContainer.remove()
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -869,6 +855,10 @@ export default class Shape extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page,
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 }

+ 6 - 0
packages/core/src/annotation/index.ts

@@ -0,0 +1,6 @@
+// @ts-nocheck
+class Annotation {
+  constructor() {
+    
+  }
+}

+ 12 - 3
packages/core/src/annotation/ink.js

@@ -130,6 +130,7 @@ export default class Ink extends Base {
 
 
   handleMouseDown (event) {
+    if (event.button !== 0 && event.type === 'mousedown') return
     this.ratioRateX = this.rect.height / this.rect.width
     this.ratioRateY=this.rect.width/this.rect.height
 
@@ -152,6 +153,7 @@ export default class Ink extends Base {
   };
 
   handleMouseMove (event) {
+    if (event.button !== 0 && event.type === 'mousemove') return
     if (event.type === 'touchmove') {
       document.body.style.overscrollBehavior = 'none';
     }
@@ -398,13 +400,13 @@ export default class Ink extends Base {
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.annotationContainer.remove()
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -412,7 +414,11 @@ export default class Ink extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   handleKeydown (event) {
@@ -422,6 +428,7 @@ export default class Ink extends Base {
   }
 
   handleMouseUp (event) {
+    if (event.button !== 0 && event.type === 'mouseup') return
     if (this.layer.tool) {
       event.stopPropagation()
     }
@@ -611,6 +618,8 @@ export default class Ink extends Base {
       }
       const color = annotation.color
       annotation.color = new Color(color.R,color. G, color.B)
+    } else {
+      annotation.color = new Color()
     }
 
     const inkPointes = annotation.inkPointes

+ 38 - 8
packages/core/src/annotation/layer.js

@@ -78,6 +78,12 @@ class ComPDFAnnotationLayer {
     this.color = color
     this.pageViewer.tool = tool
     this.pageViewer.color = color
+
+    if (!this.svgElement) {
+      this.destroy()
+      this.renderPaintScene()
+    }
+
     const markup = ['highlight', 'underline', 'squiggly', 'strikeout']
     if (!tool || markup.includes(tool)) {
       document.querySelector('.document').classList.remove('annotation-edit')
@@ -647,6 +653,7 @@ class ComPDFAnnotationLayer {
     if (this._cancelled) {
       return;
     }
+    this.renderPaintScene()
     this.eventBus._on('toolChanged', this.onHandleTool)
     this.eventBus._on('toolModeChanged', this.onHandleToolMode)
     this.eventBus._on('setDefaultSelect', this.onHandleDefaultSelect)
@@ -688,11 +695,9 @@ class ComPDFAnnotationLayer {
         this.linkManager.viewport = clonedViewport
       }
     }
-    this.renderPaintScene()
-    this.div = document.createElement("div");
-    this.div.className = "annotationContainer";
-    this.div.tabIndex = 0;
-    this.pageDiv.append(this.div);
+    if (!this.div) {
+      this.renderContainer()
+    }
     if (this.annotations && this.annotations.length) {
       for (let i = 0; i < this.annotations.length; i++) {
         const annotation = this.annotations[i]
@@ -891,6 +896,23 @@ class ComPDFAnnotationLayer {
     }
   }
 
+  renderContainer() {
+    const div = document.createElement("div")
+    div.className = "annotationContainer"
+    div.tabIndex = 0
+    this.pageDiv.append(div)
+    this.div = div
+  }
+
+  emptyAnnotations() {
+    const length = this.annotationsArray.length
+    for (let i = 0; i < length; i++) {
+      this.annotationsArray[i].handleDelete()
+    }
+
+    this.annotationsArray = []
+  }
+
   // 取消渲染
   cancel() {
     this._cancelled = true;
@@ -899,9 +921,15 @@ class ComPDFAnnotationLayer {
 
   // 画布重置
   destroy () {
-    this.svgElement.remove()
+    if (!this.svgElement) {
+      this.svgElement = this.pageDiv.querySelector('.annotationLayer svg')
+    }
+    this.svgElement?.remove()
     this.svgElement = null
-    this.div.remove()
+    if (!this.div) {
+      this.div = this.pageDiv.querySelector('.annotationContainer')
+    }
+    this.div?.remove()
     this.div = null
     if (this.freetextManager) {
       this.freetextManager.reset()
@@ -973,7 +1001,9 @@ class ComPDFAnnotationLayer {
 
   // 渲染注释
   renderAnnotation (annotation, show) {
-    
+    if (!this.div) {
+      this.renderContainer()
+    }
     const markupType = ['highlight', 'underline', 'squiggly', 'strikeout']
     if (markupType.includes(annotation.type)) {
       const markup = new MarkupTextAnnotation({

+ 18 - 46
packages/core/src/annotation/line.js

@@ -252,27 +252,10 @@ export default class Line extends Base {
 
     const { width, height, rotation  } = viewport;
     let x1, y1, x2, y2;
-    if (rotation === 0) {
-      x1 = startX * s;
-      y1 = startY * s;
-      x2 = endX * s;
-      y2 = endY * s;
-    } else if (rotation === 90) {
-      x1 = startY * s;
-      y1 = startX * s;
-      x2 = endY * s;
-      y2 = endX * s;
-    } else if (rotation === 180) {
-      x1 = width - startX * s;
-      y1 = startY * s;
-      x2 = width - endX * s;
-      y2 = endY * s;
-    } else {
-      x1 = width - startY * s;
-      y1 = height - startX * s;
-      x2 = width - endY * s;
-      y2 = height - endX * s;
-    }
+    x1 = startX * s;
+    y1 = startY * s;
+    x2 = endX * s;
+    y2 = endY * s;
     return {
       start: {
         x: x1,
@@ -292,29 +275,11 @@ export default class Line extends Base {
     const endX = this.end.x
     const endY = this.end.y
 
-    const { width, height, rotation  } = this.viewport;
     let x1, y1, x2, y2;
-    if (rotation === 0) {
-      x1 = startX / s;
-      y1 = startY / s;
-      x2 = endX / s;
-      y2 = endY / s;
-    } else if (rotation === 90) {
-      x1 = startY / s;
-      y1 = startX / s;
-      x2 = endY / s;
-      y2 = endX / s;
-    } else if (rotation === 180) {
-      x1 =( width - startX) / s;
-      y1 = startY / s;
-      x2 = (width - endX) / s;
-      y2 = endY / s;
-    } else {
-      x1 = (height - startY) / s
-      y1 = (width - startX) / s
-      x2 = (height - endY) / s
-      y2 = (width - endX)  / s
-    }
+    x1 = startX / s;
+    y1 = startY / s;
+    x2 = endX / s;
+    y2 = endY / s;
     return {
       start: {
         x: x1,
@@ -376,6 +341,7 @@ export default class Line extends Base {
   }
 
   handleMouseDown (event) {
+    if (event.button !== 0 && event.type === 'mousedown') return
     if (this.layer.tool) {
       event.stopPropagation()
     }
@@ -395,6 +361,7 @@ export default class Line extends Base {
   };
 
   handleMouseMove (event) {
+    if (event.button !== 0 && event.type === 'mousemove') return
     if (event.type === 'touchmove') {
       document.body.style.overscrollBehavior = 'none';
     }
@@ -470,13 +437,13 @@ export default class Line extends Base {
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.annotationContainer.remove()
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -484,7 +451,11 @@ export default class Line extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   handleKeydown (event) {
@@ -495,6 +466,7 @@ export default class Line extends Base {
 
 
   handleMouseUp (event) {
+    if (event.button !== 0 && event.type === 'mouseup') return
     if (this.layer.tool) {
       event.stopPropagation()
     }

+ 14 - 24
packages/core/src/annotation/link.js

@@ -375,29 +375,11 @@ export default class Link extends Base {
     const endX = this.end.x
     const endY = this.end.y
 
-    const { width, height, rotation  } = this.viewport;
     let x1, y1, x2, y2;
-    if (rotation === 0) {
-      x1 = startX / s;
-      y1 = startY / s; 
-      x2 = endX / s;
-      y2 = endY / s;
-    } else if (rotation === 90) {
-      x1 = startY / s;
-      y1 = startX / s;
-      x2 = endY / s;
-      y2 = endX / s;
-    } else if (rotation === 180) {
-      x1 =( width - startX) / s;
-      y1 = startY / s;
-      x2 = (width - endX) / s;
-      y2 = endY / s;
-    } else {
-      x1 = (height - startY) / s
-      y1 = (width - startX) / s
-      x2 = (height - endY) / s
-      y2 = (width - endX)  / s
-    }
+    x1 = startX / s;
+    y1 = startY / s; 
+    x2 = endX / s;
+    y2 = endY / s;
     return {
       start: {
         x: x1,
@@ -467,6 +449,7 @@ export default class Link extends Base {
   }
 
   handleMouseDown (event) {
+    if (event.button !== 0 && event.type === 'mousedown') return
     if (this.layer.tool) {
       event.stopPropagation()
     }
@@ -488,6 +471,7 @@ export default class Link extends Base {
   };
 
   handleMouseMove (event) {
+    if (event.button !== 0 && event.type === 'mousemove') return
     if (event.type === 'touchmove') {
       document.body.style.overscrollBehavior = 'none';
     }
@@ -619,6 +603,7 @@ export default class Link extends Base {
   }
 
   handleDelete (event) {
+    if (!this.annotationContainer) return
     if (this.layer.tool && event) {
       event.stopPropagation()
     }
@@ -626,7 +611,7 @@ export default class Link extends Base {
     this.annotationContainer.remove()
 
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -634,10 +619,15 @@ export default class Link extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   handleMouseUp (event) {
+    if (event.button !== 0 && event.type === 'mouseup') return
     if (this.layer.tool) {
       event.stopPropagation()
     }

+ 6 - 7
packages/core/src/annotation/paint/freetext.js

@@ -48,20 +48,20 @@ export default class Freetext {
   // FreeText 画布重置
   reset() {
     const freetextElement = this.freetextElement
-    this.start = null
-    this.end = null
 
     this.svgElement.style.cursor = 'default'
     this.container.removeEventListener('click', this.onClick)
     if (freetextElement) {
-      freetextElement.remove()
-      freetextElement.removeEventListener('blur', this.onBlur)
-      this.freetextElement = null
+      this.handleFreetext()
     }
+    this.start = null
+    this.end = null
   }
 
   // FreeText 画布初始化
   init() {
+    this.container.removeEventListener('click', this.onClick)
+
     this.container.addEventListener('click', this.onClick)
     this.svgElement.style.cursor = 'crosshair'
   }
@@ -135,9 +135,8 @@ export default class Freetext {
       })
       this.layer.renderAnnotation(annotation, true)
     }
-
-    freetextElement.remove()
     freetextElement.removeEventListener('blur', this.onBlur)
+    freetextElement.remove()
     this.freetextElement = null
   }
 

+ 3 - 0
packages/core/src/annotation/paint/link.js

@@ -37,6 +37,9 @@ export default class PaintLink {
   }
 
   init () {
+    this.container.removeEventListener('mousedown', this.onMousedown)
+    this.container.removeEventListener('touchstart', this.onMousedown)
+
     this.container.addEventListener('mousedown', this.onMousedown)
     this.container.addEventListener('touchstart', this.onMousedown)
     this.svgElement.style.cursor = 'crosshair'

+ 6 - 0
packages/core/src/annotation/paint/shape.js

@@ -43,6 +43,11 @@ export default class PaintShape {
 
   // Line, Square, Circle, Ink 画布初始化
   init() {
+    
+    this.container.removeEventListener('mousedown', this.onMousedown)
+    this.container.removeEventListener('touchstart', this.onMousedown)
+
+    console.log('init', this.page)
     this.container.addEventListener('mousedown', this.onMousedown)
     this.container.addEventListener('touchstart', this.onMousedown)
     this.svgElement.style.cursor = 'crosshair'
@@ -69,6 +74,7 @@ export default class PaintShape {
 
   // Line, Square, Circle, Ink 画布重置
   reset() {
+    console.log('reset', this.page)
     this.initStartPoint = null
     this.initEndPoint = null
     this.shapeEle = null

+ 2 - 0
packages/core/src/annotation/paint/text.js

@@ -75,6 +75,8 @@ export default class Text {
 
   // Text 画布初始化
   init () {
+    this.container.removeEventListener('click', this.onClick)
+
     this.container.addEventListener('click', this.onClick)
     this.svgElement.style.cursor = 'crosshair'
   }

+ 14 - 25
packages/core/src/annotation/shape.js

@@ -364,29 +364,11 @@ export default class Shape extends Base {
     const endX = this.end.x
     const endY = this.end.y
 
-    const { width, height, rotation  } = this.viewport;
     let x1, y1, x2, y2;
-    if (rotation === 0) {
-      x1 = startX / s;
-      y1 = startY / s; 
-      x2 = endX / s;
-      y2 = endY / s;
-    } else if (rotation === 90) {
-      x1 = startY / s;
-      y1 = startX / s;
-      x2 = endY / s;
-      y2 = endX / s;
-    } else if (rotation === 180) {
-      x1 =( width - startX) / s;
-      y1 = startY / s;
-      x2 = (width - endX) / s;
-      y2 = endY / s;
-    } else {
-      x1 = (height - startY) / s
-      y1 = (width - startX) / s
-      x2 = (height - endY) / s
-      y2 = (width - endX)  / s
-    }
+    x1 = startX / s;
+    y1 = startY / s; 
+    x2 = endX / s;
+    y2 = endY / s;
     return {
       start: {
         x: x1,
@@ -434,6 +416,7 @@ export default class Shape extends Base {
   }
 
   handleMouseDown (event) {
+    if (event.button !== 0 && event.type === 'mousedown') return
     if (this.layer.tool) {
       event.stopPropagation()
     }
@@ -453,6 +436,7 @@ export default class Shape extends Base {
   };
 
   handleMouseMove (event) {
+    if (event.button !== 0 && event.type === 'mousemove') return
     if (event.type === 'touchmove') {
       document.body.style.overscrollBehavior = 'none';
     }
@@ -584,14 +568,14 @@ export default class Shape extends Base {
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.annotationContainer.remove()
 
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -599,10 +583,15 @@ export default class Shape extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   handleMouseUp (event) {
+    if (event.button !== 0 && event.type === 'mouseup') return
     if (this.layer.tool) {
       event.stopPropagation()
     }

+ 14 - 36
packages/core/src/annotation/stamp.js

@@ -198,8 +198,6 @@ export default class Stamp extends Base {
         lineHeight: '16px',
         overflow: 'hidden',
         whiteSpace: 'nowrap',
-        // transform: `scale(${this.scale})`,
-        // transformOrigin: 'left top'
       })
       if (annotation.stampColor === 1) {
         imgEle.style.color = '#9A1C04'
@@ -414,27 +412,10 @@ export default class Stamp extends Base {
 
     const { width, height, rotation  } = this.viewport;
     let x1, y1, x2, y2;
-    if (rotation === 0) {
-      x1 = startX / s;
-      y1 = startY / s; 
-      x2 = endX / s;
-      y2 = endY / s;
-    } else if (rotation === 90) {
-      x1 = startY / s;
-      y1 = startX / s;
-      x2 = endY / s;
-      y2 = endX / s;
-    } else if (rotation === 180) {
-      x1 =( width - startX) / s;
-      y1 = startY / s;
-      x2 = (width - endX) / s;
-      y2 = endY / s;
-    } else {
-      x1 = (height - startY) / s
-      y1 = (width - startX) / s
-      x2 = (height - endY) / s
-      y2 = (width - endX)  / s
-    }
+    x1 = startX / s;
+    y1 = startY / s; 
+    x2 = endX / s;
+    y2 = endY / s;
     return {
       start: {
         x: x1,
@@ -486,6 +467,7 @@ export default class Stamp extends Base {
   }
 
   handleMouseDown (event) {
+    if (event.button !== 0 && event.type === 'mousedown') return
     if (this.layer.tool) {
       event.stopPropagation()
     }
@@ -511,6 +493,7 @@ export default class Stamp extends Base {
   };
 
   handleMouseMove (event) {
+    if (event.button !== 0 && event.type === 'mousemove') return
     if (this.layer.tool) {
       event.stopPropagation()
     }
@@ -643,14 +626,14 @@ export default class Stamp extends Base {
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.annotationContainer.remove()
 
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -658,10 +641,15 @@ export default class Stamp extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   handleMouseUp (event) {
+    if (event.button !== 0 && event.type === 'mouseup') return
     if (this.layer.tool) {
       event.stopPropagation()
     }
@@ -717,16 +705,6 @@ export default class Stamp extends Base {
     this.annotationContainer.style.width = rect.width + 'px'
     this.annotationContainer.style.height = rect.height + 'px'
 
-    // if (this.annotation.type === 'stamp' && this.annotation.stampType === 'text') {
-    //   // const originWidth = this.imgEle.offsetWidth
-    //   const ratio = rect.width / 190
-    //   // this.imgEle.style.transform = `scale(${ratio})`
-    //   this.imgEle.style.borderRadius = 6 * ratio + 'px'
-    //   this.text.style.transform = `scale(${ratio})`
-    //   this.text.style.marginLeft = 16 * ratio + 'px'
-    //   this.time.style.transform = `scale(${ratio})`
-    //   this.time.style.marginLeft = 16 * ratio + 'px'
-    // }
     this.imgEle.style.width = rect.width + 'px'
     this.imgEle.style.height = rect.height + 'px'
 

+ 38 - 11
packages/core/src/annotation/text.js

@@ -36,6 +36,7 @@ export default class Text extends Base {
     this.initial = false
     this.outline = null
 
+    this.rect = null
     this.start = null
     this.end = null
 
@@ -52,27 +53,32 @@ export default class Text extends Base {
     this.onClick = this.handleClick.bind(this)
     this.onBlur = this.handleBlur.bind(this)
     this.onDelete = this.handleDelete.bind(this)
-    
+
     this.onMousedown = this.handleMouseDown.bind(this)
     this.onMousemove = this.handleMouseMove.bind(this)
     this.onMouseup = this.handleMouseUp.bind(this)
+    this.handleStopPropagation = this.handlestopPropagation.bind(this)
     this.render()
   }
 
   render () {
     this.renderTextElement()
     this.renderTextEditor()
-    
+
     this.container.append(this.annotationContainer)
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
+
+    this.textEditorContainer.removeEventListener('click', this.handleStopPropagation)
+
     this.annotationContainer.remove()
+    this.textEditorContainer.remove()
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -80,7 +86,15 @@ export default class Text extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
+  }
+
+  handlestopPropagation(event) {
+    event.stopPropagation()
   }
 
   handleBlur () {
@@ -102,10 +116,12 @@ export default class Text extends Base {
       annotation.contents = annotation.content = contents
     }
 
+    this.textEditorContainer.removeEventListener('click', this.handleStopPropagation)
     this.textEditorContainer.remove()
   }
 
   handleMouseDown (event) {
+    if (event.button !== 0 && event.type === 'mousedown') return
     if (this.layer.addMode) return
     if (this.layer.tool) {
       event.stopPropagation()
@@ -123,6 +139,7 @@ export default class Text extends Base {
   };
 
   handleMouseMove (event) {
+    if (event.button !== 0 && event.type === 'mousemove') return
     if (event.type === 'touchmove') {
       document.body.style.overscrollBehavior = 'none';
     }
@@ -175,6 +192,7 @@ export default class Text extends Base {
   }
 
   handleMouseUp (event) {
+    if (event.button !== 0 && event.type === 'mouseup') return
     if (this.layer.tool) {
       event.stopPropagation()
     }
@@ -224,6 +242,9 @@ export default class Text extends Base {
 
     this.annotationContainer.style.top = rect.top + 'px'
     this.annotationContainer.style.left = rect.left + 'px'
+
+    this.textEditorContainer.style.top = rect.top + 28 + 'px'
+    this.textEditorContainer.style.left = rect.left + 16 + 'px'
   }
 
   rectCalc ({start, end}) {
@@ -264,7 +285,7 @@ export default class Text extends Base {
   handleClickOutside (element) {
     if (element === this.annotationContainer || !this.popShow) return
     this.popShow = false
-    this.textEditorContainer.remove()
+
     this.annotationContainer.style.zIndex = 6
     this.handleBlur()
   }
@@ -287,11 +308,13 @@ export default class Text extends Base {
     this.eventBus.dispatch('closeTextEditor', event.target.closest('.annotation.text'))
     if (this.popShow) return
     this.popShow = true
-    this.annotationContainer.append(this.textEditorContainer)
-    this.annotationContainer.style.zIndex = 7
+    this.layer.pageDiv.append(this.textEditorContainer)
+
+    this.textEditorContainer.addEventListener('click', this.handleStopPropagation)
+
     this.textEditorElement.focus()
     keepLastIndex(this.textEditorElement)
-    onClickOutside([this.annotationContainer], this.handleClickOutside.bind(this))
+    onClickOutside([this.annotationContainer, this.textEditorContainer], this.handleClickOutside.bind(this))
   }
 
   updateTool () {
@@ -330,6 +353,8 @@ export default class Text extends Base {
 
     const rect = this.rectCalc({start, end})
 
+    this.rect = rect
+
     let annotationContainer = document.createElement('div')
     annotationContainer.id = annotation.name
     annotationContainer.className = 'annotation text'
@@ -353,8 +378,10 @@ export default class Text extends Base {
     this.textEditorContainer = textEditorContainer
     this.setCss(textEditorContainer, {
       position: 'absolute',
-      left: '16px',
-      top: '28px',
+      left: this.rect.left + 16 + 'px',
+      top: this.rect.top + 28 + 'px',
+      zIndex: 2,
+      color: this.annotation.color.getColor() || '#FF0000',
       border: '2px solid',
       borderColor: this.annotation.color || '#FF0000',
       outline: 'none',

+ 5 - 28
packages/core/src/annotation/utils.js

@@ -49,21 +49,10 @@ export function createSvg (type, attribute, style) {
 }
 
 export function getActualPoint (point, viewport) {
-  const { width, height, rotation, scale: s } = viewport
+  const { scale: s } = viewport
   let x, y;
-  if (rotation === 0) {
-    x = point.x * s;
-    y = point.y * s;
-  } else if (rotation === 90) {
-    x = point.y * s;
-    y = point.x * s;
-  } else if (rotation === 180) {
-    x = width - point.x * s;
-    y = point.y * s;
-  } else {
-    x = width - point.y * s;
-    y = height - point.x * s;
-  }
+  x = point.x * s;
+  y = point.y * s;
   return {
     x,
     y,
@@ -71,21 +60,9 @@ export function getActualPoint (point, viewport) {
 }
 
 export function getInitialPoint (point, viewport, s) {
-  const { width, height, rotation } = viewport
   let x, y;
-  if (rotation === 0) {
-    x = point.x / s;
-    y = point.y / s; 
-  } else if (rotation === 90) {
-    x = point.y / s;
-    y = point.x / s;
-  } else if (rotation === 180) {
-    x =( width - point.x) / s;
-    y = point.y / s;
-  } else {
-    x = (height - point.y) / s
-    y = (width - point.x) / s
-  }
+  x = point.x / s;
+  y = point.y / s; 
   return {
     x,
     y,

+ 1 - 1
packages/core/src/color.js

@@ -1,7 +1,7 @@
 import { roundToDecimalPlaces } from './ui_utils'
 
 export default class Color {
-  constructor(r = 255, g = 0, b = 0, a = 1) {
+  constructor(r = 0, g = 0, b = 0, a = 1) {
     this.R = r
     this.G = g
     this.B = b

+ 22 - 14
packages/core/src/editor/content_container.js

@@ -83,6 +83,10 @@ export class ContentContainer {
       }
     }
 
+    if (this.tool === 'addText') {
+      this.addTextManager()
+    }
+
     this.destroyed = false
     this.rendered = true
   }
@@ -134,20 +138,24 @@ export class ContentContainer {
     }
 
     if (tool === 'addText') {
-      if (this.textManager) {
-        this.textManager.init()
-      } else {
-        this.textManager = new AddText({
-          tool,
-          color,
-          container: this.pageDiv,
-          viewport: this.viewport,
-          scale: this.scale,
-          page: this.page,
-          eventBus: this.eventBus,
-          contentContainer: this
-        })
-      }
+      this.addTextManager()
+    }
+  }
+
+  addTextManager () {
+    if (this.textManager) {
+      this.textManager.init()
+    } else {
+      this.textManager = new AddText({
+        tool: this.tool,
+        color: this.color,
+        container: this.pageDiv,
+        viewport: this.viewport,
+        scale: this.scale,
+        page: this.page,
+        eventBus: this.eventBus,
+        contentContainer: this
+      })
     }
   }
 

+ 3 - 2
packages/core/src/fileHandler.ts

@@ -52,7 +52,7 @@ function convertbase64ToJpgBuffer(imageBase64: string, width: number, height: nu
         return
       }
       ctx.drawImage(image, 0, 0, width, height)
-      const jpgBuffer = canvas.toDataURL('image/jpeg')
+      const jpgBuffer = canvas.toDataURL()
 
       const bytes = convertBase64ToBytes(jpgBuffer)
       resolve(bytes)
@@ -62,5 +62,6 @@ function convertbase64ToJpgBuffer(imageBase64: string, width: number, height: nu
 
 export {
   convertFileToBuffer,
-  convertbase64ToJpgBuffer
+  convertbase64ToJpgBuffer,
+  convertBase64ToBytes
 }

+ 3 - 0
packages/core/src/form/add_check_box.js

@@ -38,6 +38,9 @@ export default class AddCheckBox {
   }
 
   init () {
+    this.container.removeEventListener('mousedown', this.onMousedown)
+    this.container.removeEventListener('touchstart', this.onMousedown)
+
     this.container.addEventListener('mousedown', this.onMousedown)
     this.container.addEventListener('touchstart', this.onMousedown)
     this.svgElement.style.cursor = 'crosshair'

+ 3 - 0
packages/core/src/form/add_combo_box.js

@@ -38,6 +38,9 @@ export default class AddComboBox {
   }
 
   init () {
+    this.container.removeEventListener('mousedown', this.onMousedown)
+    this.container.removeEventListener('touchstart', this.onMousedown)
+
     this.container.addEventListener('mousedown', this.onMousedown)
     this.container.addEventListener('touchstart', this.onMousedown)
     this.svgElement.style.cursor = 'crosshair'

+ 3 - 0
packages/core/src/form/add_list_box.js

@@ -38,6 +38,9 @@ export default class AddListBox {
   }
 
   init () {
+    this.container.removeEventListener('mousedown', this.onMousedown)
+    this.container.removeEventListener('touchstart', this.onMousedown)
+
     this.container.addEventListener('mousedown', this.onMousedown)
     this.container.addEventListener('touchstart', this.onMousedown)
     this.svgElement.style.cursor = 'crosshair'

+ 3 - 0
packages/core/src/form/add_push_button.js

@@ -38,6 +38,9 @@ export default class AddPushButton {
   }
 
   init () {
+    this.container.removeEventListener('mousedown', this.onMousedown)
+    this.container.removeEventListener('touchstart', this.onMousedown)
+
     this.container.addEventListener('mousedown', this.onMousedown)
     this.container.addEventListener('touchstart', this.onMousedown)
     this.svgElement.style.cursor = 'crosshair'

+ 3 - 0
packages/core/src/form/add_radio_button.js

@@ -38,6 +38,9 @@ export default class AddRadioButton {
   }
 
   init () {
+    this.container.removeEventListener('mousedown', this.onMousedown)
+    this.container.removeEventListener('touchstart', this.onMousedown)
+
     this.container.addEventListener('mousedown', this.onMousedown)
     this.container.addEventListener('touchstart', this.onMousedown)
     this.svgElement.style.cursor = 'crosshair'

+ 3 - 0
packages/core/src/form/add_text_field.js

@@ -38,6 +38,9 @@ export default class AddTextField {
   }
 
   init () {
+    this.container.removeEventListener('mousedown', this.onMousedown)
+    this.container.removeEventListener('touchstart', this.onMousedown)
+
     this.container.addEventListener('mousedown', this.onMousedown)
     this.container.addEventListener('touchstart', this.onMousedown)
     this.svgElement.style.cursor = 'crosshair'

+ 15 - 6
packages/core/src/form/check_box.js

@@ -44,12 +44,14 @@ export default class CheckBox extends Base {
     this.onMouseup = this.handleMouseUp.bind(this)
     this.onMousemove = this.handleMouseMove.bind(this)
     this.onDelete = this.handleDelete.bind(this)
+
+    this.handlePropertyPanel = this.handlePropertyPanel.bind(this)
+    this.setProperty = this.setProperty.bind(this)
     this.render()
   }
 
   render () {
-    this.eventBus._on('checkboxPropertyPanelChanged', this.handlePropertyPanel.bind(this))
-    this.eventBus._on('setProperty', this.setProperty.bind(this))
+    this.eventBus._on('setProperty', this.setProperty)
 
     const annotation = this.annotation
 
@@ -373,7 +375,6 @@ export default class CheckBox extends Base {
         this.annotationContainer.style.pointerEvents = 'auto'
       }
     }
-    this.eventBus.dispatch('propertyChange', annotation)
   }
 
   getActualRect (viewport, s,) {
@@ -495,6 +496,8 @@ export default class CheckBox extends Base {
       if (!this.hidden && document.getElementsByClassName('right-panel')[0].className.indexOf('closed') !== -1) {
         document.getElementById('propertyPanelButton').click()
       }
+
+      this.eventBus._on('checkboxPropertyPanelChanged', this.handlePropertyPanel)
       onClickOutsideUp([this.annotationContainer, this.outerLine, this.deletetButton, document.getElementById('propertyPanelButton'), document.getElementsByClassName('right-panel')[0]], this.handleOutside.bind(this))
 
     } else {
@@ -533,6 +536,8 @@ export default class CheckBox extends Base {
     this.outerLine.removeEventListener('mousedown', this.onMousedown)
     this.outerLine.removeEventListener('touchstart', this.onMousedown)
     this.outerLineContainer.remove()
+
+    this.eventBus._on('checkboxPropertyPanelChanged', this.handlePropertyPanel)
   }
 
   handleMouseDown (event) {
@@ -688,14 +693,14 @@ export default class CheckBox extends Base {
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.annotationContainer.remove()
 
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -703,7 +708,11 @@ export default class CheckBox extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   handleMouseUp (event) {

+ 17 - 7
packages/core/src/form/combo_box.js

@@ -45,13 +45,16 @@ export default class ComboBox extends Base {
     this.onMouseup = this.handleMouseUp.bind(this)
     this.onMousemove = this.handleMouseMove.bind(this)
     this.onDelete = this.handleDelete.bind(this)
+
+    this.handlePropertyPanel = this.handlePropertyPanel.bind(this)
+    this.setProperty = this.setProperty.bind(this)
+    this.changeSelectAvailable = this.changeSelectAvailable.bind(this)
     this.render()
   }
 
   render () {
-    this.eventBus._on('comboBoxPropertyPanelChanged', this.handlePropertyPanel.bind(this))
-    this.eventBus._on('setProperty', this.setProperty.bind(this))
-    this.eventBus._on('changeSelectAvailable', this.changeSelectAvailable.bind(this))
+    this.eventBus._on('setProperty', this.setProperty)
+    this.eventBus._on('changeSelectAvailable', this.changeSelectAvailable)
     const annotation = this.annotation
     if (!annotation.items) {
       annotation.items = []
@@ -375,7 +378,6 @@ export default class ComboBox extends Base {
         this.annotationContainer.style.pointerEvents = 'auto'
       }
     }
-    this.eventBus.dispatch('propertyChange', this.annotation)
 
     if (this.show) this.handleClick()
   }
@@ -497,6 +499,8 @@ export default class ComboBox extends Base {
       if (!this.hidden && document.getElementsByClassName('right-panel')[0].className.indexOf('closed') !== -1) {
         document.getElementById('propertyPanelButton').click()
       }
+
+      this.eventBus._on('comboBoxPropertyPanelChanged', this.handlePropertyPanel)
       onClickOutsideUp([this.annotationContainer, this.outerLine, this.deletetButton, document.getElementById('propertyPanelButton'), document.getElementsByClassName('right-panel')[0]], this.handleOutside.bind(this))
     }
   }
@@ -514,6 +518,8 @@ export default class ComboBox extends Base {
     this.outerLine.removeEventListener('mousedown', this.onMousedown)
     this.outerLine.removeEventListener('touchstart', this.onMousedown)
     this.outerLineContainer.remove()
+
+    this.eventBus._off('comboBoxPropertyPanelChanged', this.handlePropertyPanel)
   }
 
   handleMouseDown (event) {
@@ -669,14 +675,14 @@ export default class ComboBox extends Base {
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.annotationContainer.remove()
 
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -684,7 +690,11 @@ export default class ComboBox extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   handleMouseUp (event) {

+ 17 - 7
packages/core/src/form/list_box.js

@@ -44,13 +44,16 @@ export default class ListBox extends Base {
     this.onMouseup = this.handleMouseUp.bind(this)
     this.onMousemove = this.handleMouseMove.bind(this)
     this.onDelete = this.handleDelete.bind(this)
+
+    this.setProperty = this.setProperty.bind(this)
+    this.changeSelectAvailable = this.changeSelectAvailable.bind(this)
+    this.handlePropertyPanel = this.handlePropertyPanel.bind(this)
     this.render()
   }
 
   render() {
-    this.eventBus._on('listBoxPropertyPanelChanged', this.handlePropertyPanel.bind(this))
-    this.eventBus._on('setProperty', this.setProperty.bind(this))
-    this.eventBus._on('changeSelectAvailable', this.changeSelectAvailable.bind(this))
+    this.eventBus._on('setProperty', this.setProperty)
+    this.eventBus._on('changeSelectAvailable', this.changeSelectAvailable)
 
     const annotation = this.annotation
 
@@ -345,7 +348,6 @@ export default class ListBox extends Base {
         this.annotationContainer.style.pointerEvents = 'auto'
       }
     }
-    this.eventBus.dispatch('propertyChange', this.annotation)
 
     if (this.show) this.handleClick()
   }
@@ -467,6 +469,8 @@ export default class ListBox extends Base {
       if (!this.hidden && document.getElementsByClassName('right-panel')[0].className.indexOf('closed') !== -1) {
         document.getElementById('propertyPanelButton').click()
       }
+
+      this.eventBus._on('listBoxPropertyPanelChanged', this.handlePropertyPanel)
       onClickOutsideUp([this.annotationContainer, this.outerLine, this.deletetButton, document.getElementById('propertyPanelButton'), document.getElementsByClassName('right-panel')[0]], this.handleOutside.bind(this))
     } else {
       if (event.target && event.target.className === 'option') {
@@ -495,6 +499,8 @@ export default class ListBox extends Base {
     this.outerLine.removeEventListener('mousedown', this.onMousedown)
     this.outerLine.removeEventListener('touchstart', this.onMousedown)
     this.outerLineContainer.remove()
+
+    this.eventBus._off('listBoxPropertyPanelChanged', this.handlePropertyPanel)
   }
 
   handleMouseDown(event) {
@@ -650,14 +656,14 @@ export default class ListBox extends Base {
   }
 
   handleDelete(event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.annotationContainer.remove()
 
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -665,7 +671,11 @@ export default class ListBox extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   handleMouseUp(event) {

+ 15 - 6
packages/core/src/form/push_button.js

@@ -44,12 +44,14 @@ export default class PushButton extends Base {
     this.onMouseup = this.handleMouseUp.bind(this)
     this.onMousemove = this.handleMouseMove.bind(this)
     this.onDelete = this.handleDelete.bind(this)
+
+    this.handlePropertyPanel = this.handlePropertyPanel.bind(this)
+    this.setProperty = this.setProperty.bind(this)
     this.render()
   }
 
   render () {
-    this.eventBus._on('pushButtonPropertyPanelChanged', this.handlePropertyPanel.bind(this))
-    this.eventBus._on('setProperty', this.setProperty.bind(this))
+    this.eventBus._on('setProperty', this.setProperty)
     const annotation = this.annotation
 
     if (!this.annotation.backgroundColor) {
@@ -327,7 +329,6 @@ export default class PushButton extends Base {
     if (this.annotation.backgroundColor === 'transparent') {
       this.annotationContainer.style.backgroundColor = '#DDE9FF'
     }
-    this.eventBus.dispatch('propertyChange', this.annotation)
 
     if (this.show) this.handleClick()
   }
@@ -451,6 +452,8 @@ export default class PushButton extends Base {
       }
 
       const ignoreList = [this.annotationContainer, this.outerLine, this.deletetButton, document.getElementById('propertyPanelButton'), document.getElementsByClassName('right-panel')[0]]
+
+      this.eventBus._on('pushButtonPropertyPanelChanged', this.handlePropertyPanel)
       onClickOutsideUp(ignoreList, this.handleOutside.bind(this))
     } else {
       if (this.annotation.actionType == 1 && this.annotation.destPage) {
@@ -477,6 +480,8 @@ export default class PushButton extends Base {
     this.outerLine.removeEventListener('mousedown', this.onMousedown)
     this.outerLine.removeEventListener('touchstart', this.onMousedown)
     this.outerLineContainer.remove()
+
+    this.eventBus._off('pushButtonPropertyPanelChanged', this.handlePropertyPanel)
   }
 
   handleMouseDown (event) {
@@ -632,14 +637,14 @@ export default class PushButton extends Base {
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.annotationContainer.remove()
 
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -647,7 +652,11 @@ export default class PushButton extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   handleMouseUp (event) {

+ 15 - 6
packages/core/src/form/radio_button.js

@@ -44,12 +44,14 @@ export default class RadioButton extends Base {
     this.onMouseup = this.handleMouseUp.bind(this)
     this.onMousemove = this.handleMouseMove.bind(this)
     this.onDelete = this.handleDelete.bind(this)
+
+    this.handlePropertyPanel = this.handlePropertyPanel.bind(this)
+    this.setProperty = this.setProperty.bind(this)
     this.render()
   }
 
   render () {
-    this.eventBus._on('radioButtonPropertyPanelChanged', this.handlePropertyPanel.bind(this))
-    this.eventBus._on('setProperty', this.setProperty.bind(this))
+    this.eventBus._on('setProperty', this.setProperty)
 
     const annotation = this.annotation
 
@@ -374,7 +376,6 @@ export default class RadioButton extends Base {
         this.annotationContainer.style.pointerEvents = 'auto'
       }
     }
-    this.eventBus.dispatch('propertyChange', this.annotation)
   }
 
   getActualRect (viewport, s,) {
@@ -496,6 +497,8 @@ export default class RadioButton extends Base {
       if (!this.hidden && document.getElementsByClassName('right-panel')[0].className.indexOf('closed') !== -1) {
         document.getElementById('propertyPanelButton').click()
       }
+
+      this.eventBus._on('radioButtonPropertyPanelChanged', this.handlePropertyPanel)
       onClickOutsideUp([this.annotationContainer, this.outerLine, this.deletetButton, document.getElementById('propertyPanelButton'), document.getElementsByClassName('right-panel')[0]], this.handleOutside.bind(this))
 
     } else {
@@ -536,6 +539,8 @@ export default class RadioButton extends Base {
     this.outerLine.removeEventListener('mousedown', this.onMousedown)
     this.outerLine.removeEventListener('touchstart', this.onMousedown)
     this.outerLineContainer.remove()
+
+    this.eventBus._off('radioButtonPropertyPanelChanged', this.handlePropertyPanel)
   }
 
   handleMouseDown (event) {
@@ -691,14 +696,14 @@ export default class RadioButton extends Base {
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.annotationContainer.remove()
 
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -706,7 +711,11 @@ export default class RadioButton extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   handleMouseUp (event) {

+ 19 - 6
packages/core/src/form/text_field.js

@@ -46,12 +46,14 @@ export default class TextField extends Base {
     this.onMousemove = this.handleMouseMove.bind(this)
     this.onDelete = this.handleDelete.bind(this)
     this.onBlur = this.handleBlur.bind(this)
+
+    this.handlePropertyPanel = this.handlePropertyPanel.bind(this)
+    this.setProperty = this.setProperty.bind(this)
     this.render()
   }
 
   render () {
-    this.eventBus._on('textfieldPropertyPanelChanged', this.handlePropertyPanel.bind(this))
-    this.eventBus._on('setProperty', this.setProperty.bind(this))
+    this.eventBus._on('setProperty', this.setProperty)
     const annotation = this.annotation
     if (!annotation.content) {
       annotation.content = ''
@@ -309,7 +311,6 @@ export default class TextField extends Base {
     if (annotation.backgroundColor === 'transparent') {
       this.annotationContainer.style.backgroundColor = '#DDE9FF'
     }
-    this.eventBus.dispatch('propertyChange', annotation)
   }
 
   getActualRect (viewport, s,) {
@@ -431,6 +432,8 @@ export default class TextField extends Base {
       }
 
       const ignoreList = [this.annotationContainer, this.outerLine, this.deletetButton, document.getElementById('propertyPanelButton'), document.getElementsByClassName('right-panel')[0]]
+
+      this.eventBus._on('textfieldPropertyPanelChanged', this.handlePropertyPanel)
       onClickOutsideUp(ignoreList, this.handleOutside.bind(this))
 
       this.textContainer.style.pointerEvents = 'none'
@@ -460,6 +463,8 @@ export default class TextField extends Base {
     this.outerLine.removeEventListener('mousedown', this.onMousedown)
     this.outerLine.removeEventListener('touchstart', this.onMousedown)
     this.outerLineContainer.remove()
+
+    this.eventBus._off('textfieldPropertyPanelChanged', this.handlePropertyPanel)
   }
 
   handleBlur () {
@@ -634,14 +639,14 @@ export default class TextField extends Base {
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.annotationContainer.remove()
 
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -649,7 +654,11 @@ export default class TextField extends Base {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   handleMouseUp (event) {
@@ -886,6 +895,8 @@ export default class TextField extends Base {
         'textarea',
         {
           position: 'absolute',
+          left: 0,
+          top: 0,
           width: '100%',
           height: '100%',
           outline: 'none',
@@ -909,6 +920,8 @@ export default class TextField extends Base {
         'input',
         {
           position: 'absolute',
+          left: 0,
+          top: 0,
           width: '100%',
           height: '100%',
           outline: 'none',

+ 63 - 33
packages/core/src/index.js

@@ -262,14 +262,21 @@ class ComPDFKitViewer {
     })
   }
 
-  delAnnotations() {
-
+  delAnnotations(annotation) {
+    this.handleAnnotationChange({
+      type: 'delete',
+      annotation: [annotation]
+    })
   }
 
   addEvent(eventName, fn) {
     this.eventBus._on(eventName, fn);
   }
 
+  removeEvent(eventName, fn) {
+    this.eventBus._off(eventName, fn);
+  }
+
   async loadDocument(file, options = {}) {
     if (this.pdfLoadingTask) {
       await this.close()
@@ -305,8 +312,6 @@ class ComPDFKitViewer {
     this._docName = options.filename || getPdfFilenameFromUrl(parameters.url)
     const getPwd = (pwd) => this.#pwd = pwd
     const handlePassword = (updateCallback, reason) => {
-      this.pdfLinkService.externalLinkEnabled = false;
-
       if (this.#oldPwd && options.notUpdatePwd) {
         getPwd(this.#oldPwd);
         updateCallback(this.#oldPwd);
@@ -394,7 +399,6 @@ class ComPDFKitViewer {
             const pageView = this.pdfViewer._pages[pageIndex]
             if (pageView) {
               const editAnnotation = await pageView.getEditAnnotation()
-              console.log(editAnnotation)
               return editAnnotation
             }
           }
@@ -465,6 +469,20 @@ class ComPDFKitViewer {
     )
   }
 
+  getSelectedText(pageNumber) {
+    if (pageNumber) {
+      const page = this.pdfViewer._pages[pageNumber - 1]
+      if (page) {
+        return page.selectedText
+      }
+    }
+    return this.pdfViewer._pages.map(function(page) {
+      return page.selectedText
+    }).filter(function(text) {
+      return text !== null
+    }).join('\n')
+  }
+
   getOutlines() {
     if (this.outlines) {
       return this.outlines
@@ -497,8 +515,9 @@ class ComPDFKitViewer {
   setActiveSearchResult(result = null, activeSearchIndex = 0) {
     if (!this.searchResults) return
     const activeResult = result ? result : this.searchResults[0]
-    const left = activeResult.left * this.scale
-    const top = activeResult.top * this.scale
+    const quad = activeResult.quads[0]
+    const left = quad.left * this.scale
+    const top = quad.top * this.scale
 
     const pageView = this.pdfViewer._pages[activeResult.pageNum - 1]
     this.pdfViewer.currentPageNumber = activeResult.pageNum
@@ -673,7 +692,7 @@ class ComPDFKitViewer {
         xfdfBuffer
       })
     }
-
+    this.emptyAnnotations()
     this.reRenderAnnotations()
     // for (let i = 0; i < len; i++) {
     //   const item = annotationsXml[i]
@@ -897,7 +916,6 @@ class ComPDFKitViewer {
     eventBus._on("pagechanging", this.webViewerPageChanging.bind(this));
     eventBus._on("rotationchanging", this.webViewerRotationChanging.bind(this));
     eventBus._on("sidebarviewchanged", this.webViewerSidebarViewChanged.bind(this));
-    eventBus._on("namedaction", this.webViewerNamedAction.bind(this));
     eventBus._on("presentationmodechanged", this.webViewerPresentationModeChanged.bind(this));
     eventBus._on("presentationmode", this.webViewerPresentationMode.bind(this));
     eventBus._on("nextpage", this.webViewerNextPage.bind(this));
@@ -970,7 +988,6 @@ class ComPDFKitViewer {
       this.pdfViewer.setDocument(null);
       this.pdfLinkService.setDocument(null);
     }
-    this.pdfLinkService.externalLinkEnabled = true;
     this.isInitialViewSet = false;
     this.downloadComplete = false;
     this.url = "";
@@ -1016,15 +1033,17 @@ class ComPDFKitViewer {
     const annotation = data.annotation
     this.annotationHistory.push(annotation)
     const annotateHandles = []
-
-    const formType = ['text-field', 'check-box', 'radio-button', 'list-box', 'combo-box', 'push-button']
-
+    const formTypes = ['textfield', 'checkbox', 'radiobutton', 'listbox', 'combobox', 'pushbutton']
     if (data.type === 'add') {
       if (Array.isArray(annotation)) {
         const length = annotation.length
         for (let i = 0; i < length; i++) {
           if (Number(annotation[i].pageIndex) + 1 > this.pagesCount) continue
-          annotation[i].operate = "add-annot"
+          if (formTypes.includes(annotation[i].type)) {
+            annotation[i].operate = "add-form"
+          } else {
+            annotation[i].operate = "add-annot"
+          }
           await this.handleAnnotations(annotation[i])
           if (this.pdfViewer) {
             this.pdfViewer.renderAnnotation(annotation[i])
@@ -1050,6 +1069,7 @@ class ComPDFKitViewer {
       }
     } else {
       const annotations = this.annotations[annotation.pageIndex]
+      if (!annotations) return
       const index = findIndex(annotation.name, annotations)
       if (data.type === 'delete') {
         annotations.splice(index, 1)
@@ -1059,6 +1079,12 @@ class ComPDFKitViewer {
         this.messageHandler.sendWithPromise('RemoveAnnot', {
           annotPtr: annotation.annotPtr
         })
+      } else if (data.type === 'empty') {
+        annotations.splice(index, 1)
+        if (!annotations.length) {
+          delete this.annotations[annotation.pageIndex]
+        }
+        return
       } else {
         annotation.doc = this.doc
         this.messageHandler.sendWithPromise('EditAnnotation', {
@@ -1107,6 +1133,15 @@ class ComPDFKitViewer {
     return true
   }
 
+  emptyAnnotations() {
+    const annotations = this.annotations
+    if (this.annotations) {
+      for (let pageIndex in annotations) {
+        this.pdfViewer._pages[pageIndex].emptyAnnotations()
+      }
+    }
+  }
+
   handleCreateSignature(data) {
     this.addAnnotations(data)
   }
@@ -1181,7 +1216,7 @@ class ComPDFKitViewer {
     }
   }
 
-  async download(download = true, data, type) {
+  async download(download = false, data, type) {
     if (data) {
       data.forEach(file => {
         saveAs(file.url, file.fileName)
@@ -1385,23 +1420,13 @@ class ComPDFKitViewer {
 
     setViewerModes(scrollMode, spreadMode);
 
-    if (this.initialBookmark) {
-      setRotation(this.initialRotation);
-      delete this.initialRotation;
-
-      this.pdfLinkService.setHash(this.initialBookmark);
-      this.initialBookmark = null;
-    } else if (storedHash) {
-      setRotation(rotation);
-
-      this.pdfLinkService.setHash(storedHash);
-    }
-
     if (!this.pdfViewer.currentScaleValue) {
       // Scale was not initialized: invalid bookmark or scale was not specified.
       // Setting the default one.
       this.pdfViewer.currentScaleValue = DEFAULT_SCALE_VALUE;
     }
+
+    this.pdfViewer.forceRendering()
   }
 
   requestFullScreenMode() {
@@ -1431,14 +1456,18 @@ class ComPDFKitViewer {
     if (this.pdfViewer.isInPresentationMode) {
       return;
     }
-    this.pdfViewer.increaseScale(steps);
+    this.pdfViewer.increaseScale({
+      drawingDelay: 400
+    });
   }
 
   zoomOut(steps) {
     if (this.pdfViewer.isInPresentationMode) {
       return;
     }
-    this.pdfViewer.decreaseScale(steps);
+    this.pdfViewer.decreaseScale({
+      drawingDelay: 400
+    });
   }
 
   nextPage() {
@@ -1501,6 +1530,7 @@ class ComPDFKitViewer {
     if (!pdfDocument) {
       return;
     }
+    console.log('resize')
     const currentScaleValue = pdfViewer.currentScaleValue;
     if (
       currentScaleValue === "auto" ||
@@ -1508,9 +1538,9 @@ class ComPDFKitViewer {
       currentScaleValue === "page-width"
     ) {
       // Note: the scale is constant for 'page-actual'.
-      pdfViewer.currentScaleValue = currentScaleValue;
+      pdfViewer.resize(currentScaleValue)
     }
-    pdfViewer.update();
+    // pdfViewer.update();
   }
 
   webViewerPageRendered({ pageNumber, error }) {
@@ -1582,9 +1612,10 @@ class ComPDFKitViewer {
       });
     }
   }
+
   webViewerScaleChanging(evt) {
     // this.toolbar.setPageScale(evt.presetValue, evt.scale);
-    this.pdfViewer.update();
+    // this.pdfViewer.update();
     this.scaleChangedCallback && this.scaleChangedCallback(evt.scale)
   }
 
@@ -2093,7 +2124,6 @@ class ComPDFKitViewer {
     this.#pwd = ''
     const getPwd = (pwd) => this.#pwd = pwd
     loadingTask.onPassword = (updateCallback, reason) => {
-      this.pdfLinkService.externalLinkEnabled = false;
       this.passwordPrompt.setUpdateCallback(updateCallback, reason, getPwd);
       this.passwordPrompt.open();
     };

+ 1 - 0
packages/core/src/ink_sign.js

@@ -255,6 +255,7 @@ class InkSign {
       type: "image",
       pageIndex: this.layerData.page + 1,
       imageBase64: this.dataURL,
+      createDate: new Date(),
       // targetPage: pageNum,
       // date: new Date(),
       // name: uuidv4()

+ 7 - 3
packages/core/src/markup/text_annotation.js

@@ -209,13 +209,13 @@ class TextAnnotation extends BaseAnnotation {
   }
 
   handleDelete (event) {
-    if (this.layer.tool) {
+    if (this.layer.tool && event) {
       event.stopPropagation()
     }
     this.handleOutside()
     this.markupContainer.remove()
     this.annotation.isDelete = true
-    this.eventBus.dispatch('annotationChange', {
+    const annotationData = {
       type: 'delete',
       annotation: {
         operate: "del-annot",
@@ -223,7 +223,11 @@ class TextAnnotation extends BaseAnnotation {
         annotPtr: this.annotation.annotPtr,
         pageIndex: this.page
       }
-    })
+    }
+    if (!event) {
+      annotationData.type = 'empty'
+    }
+    this.eventBus.dispatch('annotationChange', annotationData)
   }
 
   setCss (ele, cssText) {

+ 0 - 11
packages/core/src/pdf_annotation_layer.js

@@ -491,17 +491,6 @@ class PDFAnnotationLayer {
     this.div.className = "annotationLayer";
     this.div.tabIndex = 0;
     this.pageDiv.append(this.div);
-
-    this._uiManager.addLayer(this);
-
-    this.viewport = clonedViewport
-    bindEvents(this, this.pageDiv, ["dragover", "drop"]);
-
-    this.setDimensions();
-    for (const editor of this._uiManager.getEditors(this.pageIndex)) {
-      this.add(editor);
-    }
-    this.updateMode();
   }
 
   cancel() {

+ 2 - 561
packages/core/src/pdf_link_service.js

@@ -21,57 +21,12 @@ const LinkTarget = {
  *   default value is true.
  */
 
-/**
- * Adds various attributes (href, title, target, rel) to hyperlinks.
- * @param {HTMLAnchorElement} link - The link element.
- * @param {ExternalLinkParameters} params
- */
-function addLinkAttributes(link, { url, target, rel, enabled = true } = {}) {
-  if (!url || typeof url !== "string") {
-    throw new Error('A valid "url" parameter must provided.');
-  }
-
-  const urlNullRemoved = removeNullCharacters(url);
-  if (enabled) {
-    link.href = link.title = urlNullRemoved;
-  } else {
-    link.href = "";
-    link.title = `Disabled: ${urlNullRemoved}`;
-    link.onclick = () => {
-      return false;
-    };
-  }
-
-  let targetStr = ""; // LinkTarget.NONE
-  switch (target) {
-    case LinkTarget.NONE:
-      break;
-    case LinkTarget.SELF:
-      targetStr = "_self";
-      break;
-    case LinkTarget.BLANK:
-      targetStr = "_blank";
-      break;
-    case LinkTarget.PARENT:
-      targetStr = "_parent";
-      break;
-    case LinkTarget.TOP:
-      targetStr = "_top";
-      break;
-  }
-  link.target = targetStr;
-
-  link.rel = typeof rel === "string" ? rel : DEFAULT_LINK_REL;
-}
-
 /**
  * @typedef {Object} PDFLinkServiceOptions
  * @property {EventBus} eventBus - The application event bus.
  * @property {number} [externalLinkTarget] - Specifies the `target` attribute
  *   for external links. Must use one of the values from {LinkTarget}.
  *   Defaults to using no target.
- * @property {string} [externalLinkRel] - Specifies the `rel` attribute for
- *   external links. Defaults to stripping the referrer.
  * @property {boolean} [ignoreDestinationZoom] - Ignores the zoom argument,
  *   thus preserving the current zoom level in the viewer, when navigating
  *   to internal destinations. The default value is `false`.
@@ -88,20 +43,10 @@ class PDFLinkService {
   /**
    * @param {PDFLinkServiceOptions} options
    */
-  constructor({
-    externalLinkTarget = null,
-    externalLinkRel = null,
-    ignoreDestinationZoom = false,
-  } = {}) {
-    this.externalLinkTarget = externalLinkTarget;
-    this.externalLinkRel = externalLinkRel;
-    this.externalLinkEnabled = true;
-    this._ignoreDestinationZoom = ignoreDestinationZoom;
-
+  constructor() {
     this.baseUrl = null;
     this.pdfDocument = null;
     this.pdfViewer = null;
-    this.pdfHistory = null;
   }
 
   setDocument(pdfDocument, baseUrl = null) {
@@ -114,10 +59,6 @@ class PDFLinkService {
     this.pdfViewer = pdfViewer;
   }
 
-  setHistory(pdfHistory) {
-    this.pdfHistory = pdfHistory;
-  }
-
   /**
    * @type {number}
    */
@@ -153,97 +94,6 @@ class PDFLinkService {
     this.pdfViewer.pagesRotation = value;
   }
 
-  #goToDestinationHelper(rawDest, namedDest = null, explicitDest, changeScale = true) {
-    // Dest array looks like that: <page-ref> </XYZ|/FitXXX> <args..>
-    const destRef = explicitDest[0];
-    let pageNumber;
-
-    if (typeof destRef === "object" && destRef !== null) {
-      pageNumber = this._cachedPageNumber(destRef);
-
-      if (!pageNumber) {
-        // Fetch the page reference if it's not yet available. This could
-        // only occur during loading, before all pages have been resolved.
-        this.pdfDocument
-          .getPageIndex(destRef)
-          .then(pageIndex => {
-            this.cachePageRef(pageIndex + 1, destRef);
-            this.#goToDestinationHelper(rawDest, namedDest, explicitDest);
-          })
-          .catch(() => {
-            console.error(
-              `PDFLinkService.#goToDestinationHelper: "${destRef}" is not ` +
-                `a valid page reference, for dest="${rawDest}".`
-            );
-          });
-        return;
-      }
-    } else if (Number.isInteger(destRef)) {
-      pageNumber = destRef + 1;
-    } else {
-      console.error(
-        `PDFLinkService.#goToDestinationHelper: "${destRef}" is not ` +
-          `a valid destination reference, for dest="${rawDest}".`
-      );
-      return;
-    }
-    if (!pageNumber || pageNumber < 1 || pageNumber > this.pagesCount) {
-      console.error(
-        `PDFLinkService.#goToDestinationHelper: "${pageNumber}" is not ` +
-          `a valid page number, for dest="${rawDest}".`
-      );
-      return;
-    }
-
-    if (this.pdfHistory) {
-      // Update the browser history before scrolling the new destination into
-      // view, to be able to accurately capture the current document position.
-      this.pdfHistory.pushCurrentPosition();
-      this.pdfHistory.push({ namedDest, explicitDest, pageNumber });
-    }
-
-    let scrollData = null
-    if (changeScale) {
-      scrollData = {
-        pageNumber,
-        destArray: explicitDest,
-        ignoreDestinationZoom: this._ignoreDestinationZoom,
-      }
-    } else {
-      scrollData = {
-        pageNumber,
-      }
-    }
-    this.pdfViewer.scrollPageIntoView(scrollData);
-  }
-
-  /**
-   * This method will, when available, also update the browser history.
-   *
-   * @param {string|Array} dest - The named, or explicit, PDF destination.
-   */
-  async goToDestination(dest, changeScale = true) {
-    if (!this.pdfDocument) {
-      return;
-    }
-    let namedDest, explicitDest;
-    if (typeof dest === "string") {
-      namedDest = dest;
-      explicitDest = await this.pdfDocument.getDestination(dest);
-    } else {
-      namedDest = null;
-      explicitDest = await dest;
-    }
-    if (!Array.isArray(explicitDest)) {
-      console.error(
-        `PDFLinkService.goToDestination: "${explicitDest}" is not ` +
-          `a valid destination array, for dest="${dest}".`
-      );
-      return;
-    }
-    this.#goToDestinationHelper(dest, namedDest, explicitDest, changeScale);
-  }
-
   /**
    * This method will, when available, also update the browser history.
    *
@@ -267,249 +117,9 @@ class PDFLinkService {
       return;
     }
 
-    if (this.pdfHistory) {
-      // Update the browser history before scrolling the new page into view,
-      // to be able to accurately capture the current document position.
-      this.pdfHistory.pushCurrentPosition();
-      this.pdfHistory.pushPage(pageNumber);
-    }
-
     this.pdfViewer.scrollPageIntoView({ pageNumber });
   }
 
-  /**
-   * Wrapper around the `addLinkAttributes` helper function.
-   * @param {HTMLAnchorElement} link
-   * @param {string} url
-   * @param {boolean} [newWindow]
-   */
-  addLinkAttributes(link, url, newWindow = false) {
-    addLinkAttributes(link, {
-      url,
-      target: newWindow ? LinkTarget.BLANK : this.externalLinkTarget,
-      rel: this.externalLinkRel,
-      enabled: this.externalLinkEnabled,
-    });
-  }
-
-  /**
-   * @param {string|Array} dest - The PDF destination object.
-   * @returns {string} The hyperlink to the PDF object.
-   */
-  getDestinationHash(dest) {
-    if (typeof dest === "string") {
-      if (dest.length > 0) {
-        return this.getAnchorUrl("#" + escape(dest));
-      }
-    } else if (Array.isArray(dest)) {
-      const str = JSON.stringify(dest);
-      if (str.length > 0) {
-        return this.getAnchorUrl("#" + escape(str));
-      }
-    }
-    return this.getAnchorUrl("");
-  }
-
-  /**
-   * Prefix the full url on anchor links to make sure that links are resolved
-   * relative to the current URL instead of the one defined in <base href>.
-   * @param {string} anchor - The anchor hash, including the #.
-   * @returns {string} The hyperlink to the PDF object.
-   */
-  getAnchorUrl(anchor) {
-    return (this.baseUrl || "") + anchor;
-  }
-
-  /**
-   * @param {string} hash
-   */
-  setHash(hash) {
-    if (!this.pdfDocument) {
-      return;
-    }
-    let pageNumber, dest;
-    if (hash.includes("=")) {
-      const params = parseQueryString(hash);
-      // borrowing syntax from "Parameters for Opening PDF Files"
-      if (params.has("page")) {
-        pageNumber = params.get("page") | 0 || 1;
-      }
-      if (params.has("zoom")) {
-        // Build the destination array.
-        const zoomArgs = params.get("zoom").split(","); // scale,left,top
-        const zoomArg = zoomArgs[0];
-        const zoomArgNumber = parseFloat(zoomArg);
-
-        if (!zoomArg.includes("Fit")) {
-          // If the zoomArg is a number, it has to get divided by 100. If it's
-          // a string, it should stay as it is.
-          dest = [
-            null,
-            { name: "XYZ" },
-            zoomArgs.length > 1 ? zoomArgs[1] | 0 : null,
-            zoomArgs.length > 2 ? zoomArgs[2] | 0 : null,
-            zoomArgNumber ? zoomArgNumber / 100 : zoomArg,
-          ];
-        } else {
-          if (zoomArg === "Fit" || zoomArg === "FitB") {
-            dest = [null, { name: zoomArg }];
-          } else if (
-            zoomArg === "FitH" ||
-            zoomArg === "FitBH" ||
-            zoomArg === "FitV" ||
-            zoomArg === "FitBV"
-          ) {
-            dest = [
-              null,
-              { name: zoomArg },
-              zoomArgs.length > 1 ? zoomArgs[1] | 0 : null,
-            ];
-          } else if (zoomArg === "FitR") {
-            if (zoomArgs.length !== 5) {
-              console.error(
-                'PDFLinkService.setHash: Not enough parameters for "FitR".'
-              );
-            } else {
-              dest = [
-                null,
-                { name: zoomArg },
-                zoomArgs[1] | 0,
-                zoomArgs[2] | 0,
-                zoomArgs[3] | 0,
-                zoomArgs[4] | 0,
-              ];
-            }
-          } else {
-            console.error(
-              `PDFLinkService.setHash: "${zoomArg}" is not a valid zoom value.`
-            );
-          }
-        }
-      }
-      if (dest) {
-        this.pdfViewer.scrollPageIntoView({
-          pageNumber: pageNumber || this.page,
-          destArray: dest,
-          allowNegativeOffset: true,
-        });
-      } else if (pageNumber) {
-        this.page = pageNumber; // simple page
-      }
-      // Ensure that this parameter is *always* handled last, in order to
-      // guarantee that it won't be overridden (e.g. by the "page" parameter).
-      if (params.has("nameddest")) {
-        this.goToDestination(params.get("nameddest"));
-      }
-    } else {
-      // Named (or explicit) destination.
-      dest = unescape(hash);
-      try {
-        dest = JSON.parse(dest);
-
-        if (!Array.isArray(dest)) {
-          // Avoid incorrectly rejecting a valid named destination, such as
-          // e.g. "4.3" or "true", because `JSON.parse` converted its type.
-          dest = dest.toString();
-        }
-      } catch (ex) {}
-
-      if (
-        typeof dest === "string" ||
-        PDFLinkService.#isValidExplicitDestination(dest)
-      ) {
-        this.goToDestination(dest);
-        return;
-      }
-      console.error(
-        `PDFLinkService.setHash: "${unescape(
-          hash
-        )}" is not a valid destination.`
-      );
-    }
-  }
-
-  /**
-   * @param {string} action
-   */
-  executeNamedAction(action) {
-    // See PDF reference, table 8.45 - Named action
-    switch (action) {
-      case "GoBack":
-        this.pdfHistory?.back();
-        break;
-
-      case "GoForward":
-        this.pdfHistory?.forward();
-        break;
-
-      case "NextPage":
-        this.pdfViewer.nextPage();
-        break;
-
-      case "PrevPage":
-        this.pdfViewer.previousPage();
-        break;
-
-      case "LastPage":
-        this.page = this.pagesCount;
-        break;
-
-      case "FirstPage":
-        this.page = 1;
-        break;
-
-      default:
-        break; // No action according to spec
-    }
-
-    this.eventBus.dispatch("namedaction", {
-      source: this,
-      action,
-    });
-  }
-
-  /**
-   * @param {Object} action
-   */
-  async executeSetOCGState(action) {
-    const pdfDocument = this.pdfDocument;
-    const optionalContentConfig = await this.pdfViewer
-      .optionalContentConfigPromise;
-
-    if (pdfDocument !== this.pdfDocument) {
-      return; // The document was closed while the optional content resolved.
-    }
-    let operator;
-
-    for (const elem of action.state) {
-      switch (elem) {
-        case "ON":
-        case "OFF":
-        case "Toggle":
-          operator = elem;
-          continue;
-      }
-      switch (operator) {
-        case "ON":
-          optionalContentConfig.setVisibility(elem, true);
-          break;
-        case "OFF":
-          optionalContentConfig.setVisibility(elem, false);
-          break;
-        case "Toggle":
-          const group = optionalContentConfig.getGroup(elem);
-          if (group) {
-            optionalContentConfig.setVisibility(elem, !group.visible);
-          }
-          break;
-      }
-    }
-
-    this.pdfViewer.optionalContentConfigPromise = Promise.resolve(
-      optionalContentConfig
-    );
-  }
-
   /**
    * @param {number} pageNum - page number.
    * @param {Object} pageRef - reference to the page.
@@ -548,175 +158,6 @@ class PDFLinkService {
   isPageCached(pageNumber) {
     return this.pdfViewer.isPageCached(pageNumber);
   }
-
-  static #isValidExplicitDestination(dest) {
-    if (!Array.isArray(dest)) {
-      return false;
-    }
-    const destLength = dest.length;
-    if (destLength < 2) {
-      return false;
-    }
-    const page = dest[0];
-    if (
-      !(
-        typeof page === "object" &&
-        Number.isInteger(page.num) &&
-        Number.isInteger(page.gen)
-      ) &&
-      !(Number.isInteger(page) && page >= 0)
-    ) {
-      return false;
-    }
-    const zoom = dest[1];
-    if (!(typeof zoom === "object" && typeof zoom.name === "string")) {
-      return false;
-    }
-    let allowNull = true;
-    switch (zoom.name) {
-      case "XYZ":
-        if (destLength !== 5) {
-          return false;
-        }
-        break;
-      case "Fit":
-      case "FitB":
-        return destLength === 2;
-      case "FitH":
-      case "FitBH":
-      case "FitV":
-      case "FitBV":
-        if (destLength !== 3) {
-          return false;
-        }
-        break;
-      case "FitR":
-        if (destLength !== 6) {
-          return false;
-        }
-        allowNull = false;
-        break;
-      default:
-        return false;
-    }
-    for (let i = 2; i < destLength; i++) {
-      const param = dest[i];
-      if (!(typeof param === "number" || (allowNull && param === null))) {
-        return false;
-      }
-    }
-    return true;
-  }
-}
-
-/**
- * @implements {IPDFLinkService}
- */
-class SimpleLinkService {
-  constructor() {
-    this.externalLinkEnabled = true;
-  }
-
-  /**
-   * @type {number}
-   */
-  get pagesCount() {
-    return 0;
-  }
-
-  /**
-   * @type {number}
-   */
-  get page() {
-    return 0;
-  }
-
-  /**
-   * @param {number} value
-   */
-  set page(value) {}
-
-  /**
-   * @type {number}
-   */
-  get rotation() {
-    return 0;
-  }
-
-  /**
-   * @param {number} value
-   */
-  set rotation(value) {}
-
-  /**
-   * @param {string|Array} dest - The named, or explicit, PDF destination.
-   */
-  async goToDestination(dest) {}
-
-  /**
-   * @param {number|string} val - The page number, or page label.
-   */
-  goToPage(val) {}
-
-  /**
-   * @param {HTMLAnchorElement} link
-   * @param {string} url
-   * @param {boolean} [newWindow]
-   */
-  addLinkAttributes(link, url, newWindow = false) {
-    addLinkAttributes(link, { url, enabled: this.externalLinkEnabled });
-  }
-
-  /**
-   * @param dest - The PDF destination object.
-   * @returns {string} The hyperlink to the PDF object.
-   */
-  getDestinationHash(dest) {
-    return "#";
-  }
-
-  /**
-   * @param hash - The PDF parameters/hash.
-   * @returns {string} The hyperlink to the PDF object.
-   */
-  getAnchorUrl(hash) {
-    return "#";
-  }
-
-  /**
-   * @param {string} hash
-   */
-  setHash(hash) {}
-
-  /**
-   * @param {string} action
-   */
-  executeNamedAction(action) {}
-
-  /**
-   * @param {Object} action
-   */
-  executeSetOCGState(action) {}
-
-  /**
-   * @param {number} pageNum - page number.
-   * @param {Object} pageRef - reference to the page.
-   */
-  cachePageRef(pageNum, pageRef) {}
-
-  /**
-   * @param {number} pageNumber
-   */
-  isPageVisible(pageNumber) {
-    return true;
-  }
-
-  /**
-   * @param {number} pageNumber
-   */
-  isPageCached(pageNumber) {
-    return true;
-  }
 }
 
-export { LinkTarget, PDFLinkService, SimpleLinkService };
+export { LinkTarget, PDFLinkService };

+ 406 - 285
packages/core/src/pdf_page_view.js

@@ -2,11 +2,11 @@ import {
   createPromiseCapability,
   RenderingCancelledException,
   setLayerDimensions,
-  SVGGraphics,
   AbortException
 } from "pdfjs-dist/legacy/build/pdf";
 import {
   DEFAULT_SCALE,
+  PromiseExt,
   RendererType,
   RenderingStates,
   TextLayerMode,
@@ -23,7 +23,7 @@ import TextSearch from './TextSearch'
 
 import AnnotationManager from "./annotations";
 import { v4 as uuidv4 } from 'uuid';
-import { convertbase64ToJpgBuffer } from './fileHandler.ts'
+import { convertBase64ToBytes } from './fileHandler.ts'
 /**
  * @typedef {Object} PDFPageViewOptions
  * @property {HTMLDivElement} [container] - The viewer element.
@@ -34,7 +34,6 @@ import { convertbase64ToJpgBuffer } from './fileHandler.ts'
  * @property {Promise<OptionalContentConfig>} [optionalContentConfigPromise] -
  *   A promise that is resolved with an {@link OptionalContentConfig} instance.
  *   The default value is `null`.
- * @property {PDFRenderingQueue} [renderingQueue] - The rendering queue object.
  * @property {number} [textLayerMode] - Controls if the text layer used for
  *   selection and searching is created. The constants from {TextLayerMode}
  *   should be used. The default value is `TextLayerMode.ENABLE`.
@@ -73,6 +72,8 @@ class PDFPageView {
     regularAnnotations: true,
   };
 
+  #activeSearch = null
+
   /**
    * @param {PDFPageViewOptions} options
    */
@@ -112,7 +113,6 @@ class PDFPageView {
     this.pageColors = options.pageColors || null;
 
     this.eventBus = options.eventBus;
-    this.renderingQueue = options.renderingQueue;
     this._annotations = options.annotations
     this._annotationsAll = options.annotationsAll
 
@@ -130,12 +130,6 @@ class PDFPageView {
     this._renderingState = RenderingStates.INITIAL
     this.resume = null;
     this._renderError = null;
-    if (
-      typeof PDFJSDev === "undefined" ||
-      PDFJSDev.test("!PRODUCTION || GENERIC")
-    ) {
-      this._isStandalone = !this.renderingQueue?.hasViewer();
-    }
 
     this._annotationCanvasMap = null;
 
@@ -172,18 +166,10 @@ class PDFPageView {
 
     this.mode = null
 
-    if (
-      (typeof PDFJSDev === "undefined" ||
-        PDFJSDev.test("!PRODUCTION || GENERIC")) &&
-      this._isStandalone
-    ) {
-      // Ensure that the various layers always get the correct initial size,
-      // see issue 15795.
-      container?.style.setProperty(
-        "--scale-factor",
-        this.scale * PixelsPerInch.PDF_TO_CSS_UNITS
-      );
-    }
+    container?.style.setProperty(
+      "--scale-factor",
+      this.scale * PixelsPerInch.PDF_TO_CSS_UNITS
+    )
   }
 
   async pagePressHandle(evt) {
@@ -238,10 +224,11 @@ class PDFPageView {
             lineWidth: signImg.inkParam.width || 1,
             color: signImg.inkParam.color || "#000",
             date: new Date(),
+            createDate: new Date(),
             name: uuidv4()
           }
         } else {
-          const imageData = await convertbase64ToJpgBuffer(imgData)
+          const imageData = await convertBase64ToBytes(imgData)
           annotation = {
             operate: "add-annot",
             type: "stamp",
@@ -256,6 +243,7 @@ class PDFPageView {
             },
             targetPage: pageNum,
             date: new Date(),
+            createDate: new Date(),
             name: uuidv4()
           }
         }
@@ -360,6 +348,12 @@ class PDFPageView {
     }
   }
 
+  get selectedText() {
+    if (!this.textSelection) return null
+    const selection = this.textSelection._selection
+    return selection ? selection.textContent : null
+  }
+
   #setDimensions() {
     const { viewport } = this;
     if (this.pdfPage) {
@@ -396,6 +390,7 @@ class PDFPageView {
   }
 
   clearSearchResults() {
+    this.searchResults = null
     if (this.textSearch) {
       this.textSearch.destroy()
     }
@@ -420,22 +415,265 @@ class PDFPageView {
     }
   }
 
+  emptyAnnotations() {
+    if (this.compdfAnnotationLayer) {
+      this.compdfAnnotationLayer.emptyAnnotations()
+    }
+  }
+
   /**
    * @private
    */
-  async _renderAnnotationEditorLayer() {
-    let error = null;
-    try {
-      await this.annotationEditorLayer.render(this.viewport, "display");
-    } catch (ex) {
-      console.error(`_renderAnnotationEditorLayer: "${ex}".`);
-      error = ex;
-    } finally {
-      this.eventBus.dispatch("annotationeditorlayerrendered", {
-        source: this,
-        pageNumber: this.id,
-        error,
-      });
+  _renderAnnotationEditorLayer() {
+    this.annotationEditorLayer.render(this.viewport, "display");
+  }
+
+  renderPage() {
+    if (this.renderingState !== RenderingStates.INITIAL) {
+      console.error("Must be in new state before drawing");
+      this.reset(); // Ensure that we reset all state to prevent issues.
+    }
+    const { div, viewport, pdfPage, annotations, annotationsAll } = this;
+
+    if (!pdfPage) {
+      this.renderingState = RenderingStates.FINISHED;
+      throw new Error("pdfPage is not loaded");
+    }
+
+    this.renderingState = RenderingStates.RUNNING;
+
+    const canvasWrapper = document.createElement("div");
+    canvasWrapper.classList.add("canvasWrapper");
+    div.append(canvasWrapper)
+
+    const canvas = document.createElement("canvas");
+    canvas.setAttribute("role", "presentation");
+
+    canvas.hidden = true;
+    let isCanvasHidden = true;
+    const showCanvas = function () {
+      if (isCanvasHidden) {
+        canvas.hidden = false;
+        isCanvasHidden = false;
+      }
+    }
+
+    canvasWrapper.append(canvas);
+    this.canvas = canvas
+
+    const radio = window.devicePixelRatio || 1
+    this.radio = radio
+    const { width, height, scale } = viewport
+
+    const canvasWidth = Math.round(width * radio)
+    const canvasHeight = Math.round(height * radio)
+
+    canvas.width = canvasWidth
+    canvas.height = canvasHeight
+    const { style } = canvas;
+    style.width = Math.round(width) + "px"
+    style.height = Math.round(height) + "px"
+
+    this.paintedViewportMap.set(canvas, viewport)
+
+    const renderTask = (this.renderTask = this.internalRenderTask({
+      width,
+      height,
+      scale: scale * radio
+    }))
+
+    const resultPromise = renderTask.promise.then(
+      async () => {
+        showCanvas();
+        await this.finishRenderTask();
+
+        this.#renderTextLayer();
+
+          if (this.annotationLayer) {
+            await this._renderAnnotationLayer();
+          }
+
+          if (!this.annotationEditorLayer) {
+            const { annotationEditorUIManager, annotationStorage } = this.layerProperties();
+
+            if (!annotationEditorUIManager) {
+              return;
+            }
+            this.annotationEditorLayer = new PDFAnnotationLayer({
+              uiManager: annotationEditorUIManager,
+              annotationStorage,
+              pageIndex: this.pageIndex,
+              pageDiv: div,
+              viewport: this.viewport,
+              scale: this.scale,
+              annotations,
+              accessibilityManager: this._accessibilityManager,
+            });
+          }
+          this._renderAnnotationEditorLayer();
+
+          if (!this.compdfAnnotationLayer) {
+            this.compdfAnnotationLayer = new ComPDFAnnotationLayer({
+              annotationStore: this.annotationStore,
+              messageHandler: this.messageHandler,
+              pageViewer: this,
+              annotations,
+              annotationsAll,
+              viewport: this.viewport,
+              scale: this.scale,
+              pageIndex: this.pageIndex,
+              pageDiv: div,
+              eventBus: this.eventBus,
+              selected: this.selected,
+              $t: this.$t
+            })
+          }
+          this.compdfAnnotationLayer.render(this.viewport)
+
+          // this.AnnotationManager = new AnnotationManager({
+          //   annotations,
+          //   container: div,
+          //   viewport: this.viewport,
+          // })
+
+          if (!this.contentContainer) {
+            this.contentContainer = new ContentContainer({
+              annotationStore: this.annotationStore,
+              pageViewer: this,
+              annotations,
+              annotationsAll,
+              viewport: this.viewport,
+              scale: this.scale,
+              pageIndex: this.pageIndex,
+              pageDiv: div,
+              eventBus: this.eventBus,
+              selected: this.selected,
+              pagePtr: this.pagesPtr[this.pageIndex].pagePtr,
+              messageHandler: this.messageHandler
+            })
+          }
+          if (this.mode === 'editor') {
+            await this.contentContainer.render()
+          }
+
+          if (!this.textSelection) {
+            this.textSelection = new TextSelection({
+              viewport: this.viewport,
+              scale: this.scale,
+              pageIndex: this.pageIndex,
+              container: div,
+              eventBus: this.eventBus,
+              selected: this.selected,
+              pagePtr: this.pagesPtr[this.pageIndex],
+              messageHandler: this.messageHandler,
+              tool: this.tool,
+              toolMode: this.toolMode,
+              color: this.color,
+              pageViewer: this
+            })
+          }
+
+          if (!this.textSearch) {
+            this.textSearch = new TextSearch({
+              viewport: this.viewport,
+              scale: this.scale,
+              pageIndex: this.pageIndex,
+              container: div,
+              results: this.searchResults,
+            })
+            if (this.#activeSearch) {
+              this.textSearch.setActiveSearchResult(this.#activeSearch)
+            }
+          }
+      },
+      error => {
+        console.log(error)
+        return this.finishRenderTask(error);
+      }
+    )
+
+    div.setAttribute("data-loaded", true);
+
+    this.eventBus.dispatch("pagerender", {
+      source: this,
+      pageNumber: this.id,
+    });
+    return resultPromise
+  }
+
+  internalRenderTask({
+    width,
+    height,
+    scale
+  }) {
+    let renderTask = this.renderTask
+
+    if (renderTask) {
+      renderTask.cancel()
+    }
+
+    renderTask = this.render({
+      width,
+      height,
+      scale
+    })
+
+    return renderTask
+  }
+
+  render({
+    width,
+    height,
+    scale
+  }) {
+    const renderCapability = new PromiseExt()
+    renderCapability.cancel = function () {
+      renderCapability.reject('Cancel')
+    }
+    const { canvas, pagesPtr, pageIndex } = this
+
+    this.messageHandler.sendWithPromise('PushRenderTask', {
+      pagePtr: pagesPtr[pageIndex].pagePtr,
+      left: 0,
+      top: 0,
+      right: Math.round(width),
+      bottom: Math.round(height),
+      scale
+    })
+      .then((data) => {
+        const ctx = canvas.getContext("2d", { alpha: false });
+        const { imageArray } = data
+        ctx.clearRect(0, 0, width, height)
+        let imageData = ctx.createImageData(width, height);
+        imageData.data.set(imageArray)
+        ctx.putImageData(imageData, 0, 0)
+
+        renderCapability.resolve()
+      })
+      .catch((err) => {
+        console.log(err)
+      })
+    return renderCapability
+  }
+
+  finishRenderTask (error = null) {
+    this.renderTask = null
+
+    this._renderError = error;
+
+    this.renderingState = RenderingStates.FINISHED;
+    this._resetZoomLayer(/* removeFromDOM = */ true);
+
+    this.eventBus.dispatch("pagerendered", {
+      source: this,
+      pageNumber: this.id,
+      cssTransform: false,
+      timestamp: performance.now(),
+      error: this._renderError,
+    });
+
+    if (error) {
+      throw error;
     }
   }
 
@@ -513,18 +751,16 @@ class PDFPageView {
         (keepAnnotationLayer && this.annotationLayer?.div) || null,
       annotationEditorLayerNode =
         (keepAnnotationEditorLayer && this.annotationEditorLayer?.div) || null,
-      xfaLayerNode = (keepXfaLayer && this.xfaLayer?.div) || null,
       textLayerNode = (keepTextLayer && this.textLayer?.div) || null
+
     for (let i = childNodes.length - 1; i >= 0; i--) {
       const node = childNodes[i];
       switch (node) {
         case zoomLayerNode:
-        case xfaLayerNode:
         case textLayerNode:
           continue;
         case annotationLayerNode:
         case annotationEditorLayerNode:
-          const annotationChildNodes = node.childNodes
           continue;
       }
       node.remove();
@@ -542,11 +778,6 @@ class PDFPageView {
     } else {
       this.annotationEditorLayer?.destroy();
     }
-    if (xfaLayerNode) {
-      // Hide the XFA layer until all elements are resized
-      // so they are not displayed on the already resized page.
-      this.xfaLayer.hide();
-    }
     if (textLayerNode) {
       this.textLayer.hide();
     }
@@ -563,14 +794,6 @@ class PDFPageView {
       }
       this._resetZoomLayer();
     }
-    if (
-      (typeof PDFJSDev === "undefined" ||
-        PDFJSDev.test("!PRODUCTION || GENERIC")) &&
-      this.svg
-    ) {
-      this.paintedViewportMap.delete(this.svg);
-      delete this.svg;
-    }
   }
 
   update({
@@ -590,40 +813,10 @@ class PDFPageView {
     });
     this.#setDimensions();
 
-    if (
-      (typeof PDFJSDev === "undefined" ||
-        PDFJSDev.test("!PRODUCTION || GENERIC")) &&
-      this._isStandalone
-    ) {
-      this.div.parentNode?.style.setProperty(
-        "--scale-factor",
-        this.viewport.scale
-      );
-    }
-
-    if (
-      (typeof PDFJSDev === "undefined" ||
-        PDFJSDev.test("!PRODUCTION || GENERIC")) &&
-      this.svg
-    ) {
-      this.cssTransform({
-        target: this.svg,
-        redrawAnnotationLayer: true,
-        redrawAnnotationEditorLayer: true,
-        redrawXfaLayer: true,
-        redrawTextLayer: true,
-      });
-
-      this.eventBus.dispatch("pagerendered", {
-        source: this,
-        pageNumber: this.id,
-        cssTransform: true,
-        timestamp: performance.now(),
-        error: this._renderError,
-      });
-      return;
-    }
-
+    this.div.parentNode?.style.setProperty(
+      "--scale-factor",
+      this.viewport.scale
+    )
     const postponeDrawing = drawingDelay >= 0 && drawingDelay < 1000;
 
     if (this.canvas) {
@@ -701,8 +894,6 @@ class PDFPageView {
   cancelRendering({
     keepAnnotationLayer = false,
     keepAnnotationEditorLayer = false,
-    keepXfaLayer = false,
-    keepTextLayer = false,
     cancelExtraDelay = 0,
   } = {}) {
     if (this.paintTask) {
@@ -898,19 +1089,6 @@ class PDFPageView {
     div.append(canvasWrapper);
 
     let renderContinueCallback = null;
-    if (this.renderingQueue) {
-      renderContinueCallback = cont => {
-        if (!this.renderingQueue.isHighestPriority(this)) {
-          this.renderingState = RenderingStates.PAUSED;
-          this.resume = () => {
-            this.renderingState = RenderingStates.RUNNING;
-            cont();
-          };
-          return;
-        }
-        cont();
-      };
-    }
 
     const finishPaintTask = async (error = null) => {
       // The paintTask may have been replaced by a new one, so only remove
@@ -946,107 +1124,114 @@ class PDFPageView {
       }
     };
 
-    const paintTask = this.paintOnCanvas(canvasWrapper);
+    const paintTask = this.paintOnCanvas(canvasWrapper)
     paintTask.onRenderContinue = renderContinueCallback;
     this.paintTask = paintTask;
-    const resultPromise = paintTask.then(
-      async () => {
-        await finishPaintTask(null)
-        this.#renderTextLayer();
 
-        if (this.annotationLayer) {
-          await this._renderAnnotationLayer();
-        }
+    const resultPromise = paintTask.promise.then(
+      () => {
+        return finishPaintTask(null).then(async () => {
+          this.#renderTextLayer();
 
-        if (!this.annotationEditorLayer) {
-          const { annotationEditorUIManager, annotationStorage } = this.layerProperties();
+          if (this.annotationLayer) {
+            await this._renderAnnotationLayer();
+          }
 
-          if (!annotationEditorUIManager) {
-            return;
+          if (!this.annotationEditorLayer) {
+            const { annotationEditorUIManager, annotationStorage } = this.layerProperties();
+
+            if (!annotationEditorUIManager) {
+              return;
+            }
+            this.annotationEditorLayer = new PDFAnnotationLayer({
+              uiManager: annotationEditorUIManager,
+              annotationStorage,
+              pageIndex: this.pageIndex,
+              pageDiv: div,
+              viewport: this.viewport,
+              scale: this.scale,
+              annotations,
+              accessibilityManager: this._accessibilityManager,
+            });
+          }
+          this._renderAnnotationEditorLayer();
+
+          if (!this.compdfAnnotationLayer) {
+            this.compdfAnnotationLayer = new ComPDFAnnotationLayer({
+              annotationStore: this.annotationStore,
+              messageHandler: this.messageHandler,
+              pageViewer: this,
+              annotations,
+              annotationsAll,
+              viewport: this.viewport,
+              scale: this.scale,
+              pageIndex: this.pageIndex,
+              pageDiv: div,
+              eventBus: this.eventBus,
+              selected: this.selected,
+              $t: this.$t
+            })
+          } else {
+            this.compdfAnnotationLayer.destroy()
+          }
+          this.compdfAnnotationLayer.render(this.viewport)
+
+          // this.AnnotationManager = new AnnotationManager({
+          //   annotations,
+          //   container: div,
+          //   viewport: this.viewport,
+          // })
+
+          if (!this.contentContainer) {
+            this.contentContainer = new ContentContainer({
+              annotationStore: this.annotationStore,
+              pageViewer: this,
+              annotations,
+              annotationsAll,
+              viewport: this.viewport,
+              scale: this.scale,
+              pageIndex: this.pageIndex,
+              pageDiv: div,
+              eventBus: this.eventBus,
+              selected: this.selected,
+              pagePtr: this.pagesPtr[this.pageIndex].pagePtr,
+              messageHandler: this.messageHandler
+            })
+          }
+          if (this.mode === 'editor') {
+            await this.contentContainer.render()
           }
-          this.annotationEditorLayer = new PDFAnnotationLayer({
-            uiManager: annotationEditorUIManager,
-            annotationStorage,
-            pageIndex: this.pageIndex,
-            pageDiv: div,
-            viewport: this.viewport,
-            scale: this.scale,
-            annotations,
-            accessibilityManager: this._accessibilityManager,
-          });
-        }
-        this._renderAnnotationEditorLayer();
-
-        if (!this.compdfAnnotationLayer) {
-          this.compdfAnnotationLayer = new ComPDFAnnotationLayer({
-            annotationStore: this.annotationStore,
-            messageHandler: this.messageHandler,
-            pageViewer: this,
-            annotations,
-            annotationsAll,
-            viewport: this.viewport,
-            scale: this.scale,
-            pageIndex: this.pageIndex,
-            pageDiv: div,
-            eventBus: this.eventBus,
-            selected: this.selected,
-            $t: this.$t
-          })
-        }
-        this.compdfAnnotationLayer.render(this.viewport)
-
-        // this.AnnotationManager = new AnnotationManager({
-        //   annotations,
-        //   container: div,
-        //   viewport: this.viewport,
-        // })
-
-        if (!this.contentContainer) {
-          this.contentContainer = new ContentContainer({
-            annotationStore: this.annotationStore,
-            pageViewer: this,
-            annotations,
-            annotationsAll,
-            viewport: this.viewport,
-            scale: this.scale,
-            pageIndex: this.pageIndex,
-            pageDiv: div,
-            eventBus: this.eventBus,
-            selected: this.selected,
-            pagePtr: this.pagesPtr[this.pageIndex].pagePtr,
-            messageHandler: this.messageHandler
-          })
-        }
-        if (this.mode === 'editor') {
-          await this.contentContainer.render()
-        }
 
-        if (!this.textSelection) {
-          this.textSelection = new TextSelection({
-            viewport: this.viewport,
-            scale: this.scale,
-            pageIndex: this.pageIndex,
-            container: div,
-            eventBus: this.eventBus,
-            selected: this.selected,
-            pagePtr: this.pagesPtr[this.pageIndex],
-            messageHandler: this.messageHandler,
-            tool: this.tool,
-            toolMode: this.toolMode,
-            color: this.color,
-            pageViewer: this
-          })
-        }
+          if (!this.textSelection) {
+            this.textSelection = new TextSelection({
+              viewport: this.viewport,
+              scale: this.scale,
+              pageIndex: this.pageIndex,
+              container: div,
+              eventBus: this.eventBus,
+              selected: this.selected,
+              pagePtr: this.pagesPtr[this.pageIndex],
+              messageHandler: this.messageHandler,
+              tool: this.tool,
+              toolMode: this.toolMode,
+              color: this.color,
+              pageViewer: this
+            })
+          }
 
-        if (!this.textSearch) {
-          this.textSearch = new TextSearch({
-            viewport: this.viewport,
-            scale: this.scale,
-            pageIndex: this.pageIndex,
-            container: div,
-            results: this.searchResults,
-          })
-        }
+          if (!this.textSearch) {
+            this.textSearch = new TextSearch({
+              viewport: this.viewport,
+              scale: this.scale,
+              pageIndex: this.pageIndex,
+              container: div,
+              results: this.searchResults,
+            })
+            if (this.#activeSearch) {
+              this.textSearch.setActiveSearchResult(this.#activeSearch)
+            }
+          }
+        });
       },
       function (reason) {
         return finishPaintTask(reason);
@@ -1055,29 +1240,13 @@ class PDFPageView {
 
     div.setAttribute("data-loaded", true);
 
-    this.eventBus.dispatch("pagerender", {
-      source: this,
-      pageNumber: this.id,
-    });
     return resultPromise;
   }
 
-  setActiveSearchResult(search) {
-    if (this.textSearch) {
-      this.textSearch.setActiveSearchResult(search)
-    }
-  }
-
-  clearActiveSearchResult(searchResult) {
-    if (this.textSearch) {
-      this.textSearch.clearActiveSearchResult(searchResult)
-    }
-  }
-
-  async paintOnCanvas(canvasWrapper) {
+  paintOnCanvas(canvasWrapper) {
     const renderCapability = createPromiseCapability();
     const result = {
-      promise: renderCapability,
+      promise: renderCapability.promise,
       onRenderContinue(cont) {
         cont();
       },
@@ -1126,38 +1295,32 @@ class PDFPageView {
     // Add the viewport so it's known what it was originally drawn with.
     this.paintedViewportMap.set(canvas, viewport);
 
-    // Rendering area
-    const renderTask = this.messageHandler.sendWithPromise('PushRenderTask', {
-      pagePtr: this.pagesPtr[this.pageIndex].pagePtr,
-      left: 0,
-      top: 0,
-      right: Math.round(canvasWidth),
-      bottom: Math.round(canvasHeight),
-      scale: scale * radio
-    })
-
-    renderTask.onContinue = function (cont) {
-      showCanvas();
-      if (result.onRenderContinue) {
-        result.onRenderContinue(cont);
-      } else {
-        cont();
-      }
-    };
+    const renderTask = new PromiseExt()
 
     renderTask.cancel = function() {
     }
 
-    renderTask.then(
-      function ({ imageArray }) {
+    renderTask.resolve()
+    renderTask.promise.then(
+      async () => {
+        const { imageArray } = await this.messageHandler.sendWithPromise('PushRenderTask', {
+          pagePtr: this.pagesPtr[this.pageIndex].pagePtr,
+          left: 0,
+          top: 0,
+          right: Math.round(canvasWidth),
+          bottom: Math.round(canvasHeight),
+          scale: scale * radio
+        })
         ctx.clearRect(0, 0, canvasWidth, canvasHeight)
         let imageData = ctx.createImageData(canvasWidth, canvasHeight);
         imageData.data.set(imageArray)
         ctx.putImageData(imageData, 0, 0)
+
         showCanvas();
         renderCapability.resolve();
       },
       function (error) {
+        console.log(error)
         // When zooming with a `drawingDelay` set, avoid temporarily showing
         // a black canvas if rendering was cancelled before the `onContinue`-
         // callback had been invoked at least once.
@@ -1170,58 +1333,18 @@ class PDFPageView {
     return result;
   }
 
-  paintOnSvg(wrapper) {
-    if (
-      !(
-        typeof PDFJSDev === "undefined" ||
-        PDFJSDev.test("!PRODUCTION || GENERIC")
-      )
-    ) {
-      throw new Error("Not implemented: paintOnSvg");
+  setActiveSearchResult(search) {
+    this.#activeSearch = search
+    if (this.textSearch) {
+      this.textSearch.setActiveSearchResult(search)
     }
-    let cancelled = false;
-    const ensureNotCancelled = () => {
-      if (cancelled) {
-        throw new RenderingCancelledException(
-          `Rendering cancelled, page ${this.id}`,
-          "svg"
-        );
-      }
-    };
-
-    const pdfPage = this.pdfPage;
-    const actualSizeViewport = this.viewport.clone({
-      scale: PixelsPerInch.PDF_TO_CSS_UNITS,
-    });
-    const promise = pdfPage
-      .getOperatorList()
-      .then(opList => {
-        ensureNotCancelled();
-        const svgGfx = new SVGGraphics(pdfPage.commonObjs, pdfPage.objs);
-        return svgGfx.getSVG(opList, actualSizeViewport).then(svg => {
-          ensureNotCancelled();
-          this.svg = svg;
-          this.paintedViewportMap.set(svg, actualSizeViewport);
-
-          svg.style.width = wrapper.style.width;
-          svg.style.height = wrapper.style.height;
-          this.renderingState = RenderingStates.FINISHED;
-          wrapper.append(svg);
-        });
-      });
+  }
 
-    return {
-      promise,
-      onRenderContinue(cont) {
-        cont();
-      },
-      cancel() {
-        cancelled = true;
-      },
-      get separateAnnots() {
-        return false;
-      },
-    };
+  clearActiveSearchResult(searchResult) {
+    this.#activeSearch = null
+    if (this.textSearch) {
+      this.textSearch.clearActiveSearchResult(searchResult)
+    }
   }
 
   /**
@@ -1242,9 +1365,7 @@ class PDFPageView {
    * @ignore
    */
   get thumbnailCanvas() {
-    const { initialOptionalContent, regularAnnotations } =
-      this.#useThumbnailCanvas;
-    return initialOptionalContent && regularAnnotations ? this.canvas : null;
+    return this.canvas
   }
 
   get fontFile() {

+ 3 - 1
packages/core/src/pdf_presentation_mode.js

@@ -209,8 +209,9 @@ class PDFPresentationMode {
       if (this.#args.spreadMode !== null) {
         this.pdfViewer.spreadMode = this.#args.spreadMode;
       }
-      this.pdfViewer.currentScaleValue = this.#args.scaleValue;
+      this.pdfViewer.exitPresentationMode = true
       this.pdfViewer.currentPageNumber = pageNumber;
+      this.pdfViewer.currentScaleValue = this.#args.scaleValue;
 
       if (this.#args.annotationEditorMode !== null) {
         this.pdfViewer.annotationEditorMode = this.#args.annotationEditorMode;
@@ -383,6 +384,7 @@ class PDFPresentationMode {
   }
 
   #fullscreenChange() {
+    console.log('fullscreen', document.fullscreenElement)
     if (/* isFullscreen = */ document.fullscreenElement) {
       this.#enter();
     } else {

+ 2 - 2
packages/core/src/pdf_rendering_queue.js

@@ -50,14 +50,14 @@ class PDFRenderingQueue {
   /**
    * @param {Object} currentlyVisiblePages
    */
-  renderHighestPriority(currentlyVisiblePages) {
+  async renderHighestPriority(currentlyVisiblePages) {
     if (this.idleTimeout) {
       clearTimeout(this.idleTimeout);
       this.idleTimeout = null;
     }
 
     // Pages have a higher priority than thumbnails, so check them first.
-    if (this.pdfViewer.forceRendering(currentlyVisiblePages)) {
+    if (await this.pdfViewer.forceRendering(currentlyVisiblePages)) {
       return;
     }
     // No pages needed rendering, so check thumbnails.

+ 4 - 8
packages/core/src/pdf_thumbnail_view.js

@@ -76,15 +76,11 @@ class PDFThumbnailView {
     this.canvasHeight = (this.canvasWidth / pageRatio) | 0;
     this.scale = this.canvasWidth / pageWidth;
 
-    const anchor = document.createElement("a");
-    anchor.href = linkService.getAnchorUrl("#page=" + id);
-    // this._thumbPageTitle.then(msg => {
-    //   anchor.title = msg;
-    // });
+    const anchor = document.createElement("div");
     anchor.onclick = function () {
-      linkService.goToPage(id);
-      return false;
-    };
+      linkService.goToPage(id)
+      return false
+    }
     this.anchor = anchor;
 
     const div = document.createElement("div");

+ 1 - 0
packages/core/src/pdf_thumbnail_viewer.js

@@ -116,6 +116,7 @@ class PDFThumbnailViewer {
           break;
         }
       }
+      this._scrollUpdated()
       if (shouldScroll) {
         scrollIntoView(thumbnailView.div, { top: THUMBNAIL_SCROLL_MARGIN });
       }

+ 79 - 52
packages/core/src/pdf_viewer.js

@@ -37,10 +37,8 @@ import {
 } from "./ui_utils.js";
 
 import { PDFPageView } from "./pdf_page_view.js";
-import { PDFRenderingQueue } from "./pdf_rendering_queue.js";
-import { SimpleLinkService } from "./pdf_link_service.js";
 
-const DEFAULT_CACHE_SIZE = 10;
+const DEFAULT_CACHE_SIZE = 3;
 const ENABLE_PERMISSIONS_CLASS = "enablePermissions";
 
 const PagesCountLimit = {
@@ -135,6 +133,12 @@ class PDFViewer {
 
   #scaleTimeoutId = null;
 
+  #renderedPagesView = []
+
+  #rendering = false
+
+  #cacheTask = []
+
   /**
    * @param {PDFViewerOptions} options
    */
@@ -145,6 +149,7 @@ class PDFViewer {
     this.viewer = options.viewer || options.container.firstElementChild;
     this.annotationStore = options.annotationStore
     this.messageHandler = options.messageHandler
+    this.exitPresentationMode = false
     if (this.container?.tagName !== "DIV" || this.viewer?.tagName !== "DIV") {
       throw new Error("Invalid `container` and/or `viewer` option.");
     }
@@ -157,7 +162,7 @@ class PDFViewer {
     }*/
     // this.#resizeObserver.observe(this.container);
 
-    this.linkService = options.linkService || new SimpleLinkService();
+    this.linkService = options.linkService
     this.removePageBorders = false;
     this.textLayerMode = options.textLayerMode ?? TextLayerMode.ENABLE;
     this._annotationEditorMode =
@@ -191,14 +196,7 @@ class PDFViewer {
       this.pageColors = null;
     }
 
-    this.defaultRenderingQueue = !options.renderingQueue;
-    if (this.defaultRenderingQueue) {
-      // Custom rendering queue is not specified, using default one
-      this.renderingQueue = new PDFRenderingQueue();
-      this.renderingQueue.setViewer(this);
-    } else {
-      this.renderingQueue = options.renderingQueue;
-    }
+    this.renderingQueue = options.renderingQueue;
 
     this.scroll = watchScroll(this.container, this._scrollUpdate.bind(this));
     this.presentationModeState = PresentationModeState.UNKNOWN;
@@ -395,10 +393,6 @@ class PDFViewer {
       pagesRotation: rotation,
       pageNumber,
     });
-
-    if (this.defaultRenderingQueue) {
-      this.update();
-    }
   }
 
   get firstPagePromise() {
@@ -564,10 +558,7 @@ class PDFViewer {
       }
       // Add the page to the buffer at the start of drawing. That way it can be
       // evicted from the buffer and destroyed even if we pause its rendering.
-      this.#buffer.push(pageView);
     };
-    this.eventBus._on("pagerender", this._onBeforeDraw);
-
     this._onAfterDraw = evt => {
       if (evt.cssTransform || this._onePageRenderedCapability.settled) {
         return;
@@ -626,6 +617,9 @@ class PDFViewer {
         const viewport = firstPdfPage.getViewport({
           scale: scale * PixelsPerInch.PDF_TO_CSS_UNITS,
         });
+        if (viewport.rotation !== 0) {
+          viewport.rotation = 0
+        }
         // Ensure that the various layers always get the correct initial size,
         // see issue 15795.
         this.viewer.style.setProperty("--scale-factor", viewport.scale);
@@ -748,9 +742,6 @@ class PDFViewer {
           }
         });
 
-        if (this.defaultRenderingQueue) {
-          this.update();
-        }
       })
       .catch(reason => {
         console.error("Unable to initialize viewer", reason);
@@ -813,7 +804,6 @@ class PDFViewer {
     };
 
     if (this._onBeforeDraw) {
-      this.eventBus._off("pagerender", this._onBeforeDraw);
       this._onBeforeDraw = null;
     }
     if (this._onAfterDraw) {
@@ -985,12 +975,12 @@ class PDFViewer {
     this.refresh(true, {
       scale: newScale,
       drawingDelay: postponeDrawing ? drawingDelay : -1,
-    });
-
+    })
     if (postponeDrawing) {
       this.#scaleTimeoutId = setTimeout(() => {
         this.#scaleTimeoutId = null;
         this.refresh();
+        this.exitPresentationMode = false
       }, drawingDelay);
     }
 
@@ -1024,10 +1014,6 @@ class PDFViewer {
       scale: newScale,
       presetValue: preset ? newValue : undefined,
     });
-
-    if (this.defaultRenderingQueue) {
-      this.update();
-    }
   }
 
   /**
@@ -1316,7 +1302,7 @@ class PDFViewer {
     };
   }
 
-  update() {
+  cleanupPages() {
     const visible = this._getVisiblePages();
     const visiblePages = visible.views,
       numVisiblePages = visiblePages.length;
@@ -1324,11 +1310,13 @@ class PDFViewer {
     if (numVisiblePages === 0) {
       return;
     }
-    const newCacheSize = Math.max(DEFAULT_CACHE_SIZE, 2 * numVisiblePages + 1);
-    this.#buffer.resize(newCacheSize, visible.ids);
-
-    this.renderingQueue.renderHighestPriority(visible);
+    for (let i = 0; i < this.#renderedPagesView.length; i++) {
+      const pageView = this.#renderedPagesView[i];
 
+      if (!visible.ids.has(pageView.id)) {
+        pageView.destroy()
+      }
+    }
     const isSimpleLayout =
       this._spreadMode === SpreadMode.NONE &&
       (this._scrollMode === ScrollMode.PAGE ||
@@ -1356,6 +1344,25 @@ class PDFViewer {
     });
   }
 
+  async update() {
+    if (this.#scaleTimeoutId !== null && (!this.isInPresentationMode && !this.exitPresentationMode)) {
+      clearTimeout(this.#scaleTimeoutId);
+      this.#scaleTimeoutId = null;
+    }
+    const visible = this._getVisiblePages();
+    const visiblePages = visible.views,
+      numVisiblePages = visiblePages.length;
+
+    if (numVisiblePages === 0) {
+      return;
+    }
+    const newCacheSize = DEFAULT_CACHE_SIZE
+    this.#buffer.resize(newCacheSize, visible.ids);
+    await this.forceRendering(visible)
+
+    this.cleanupPages()
+  }
+
   containsElement(element) {
     return this.container.contains(element);
   }
@@ -1501,24 +1508,37 @@ class PDFViewer {
     return this.scroll.down;
   }
 
-  forceRendering(currentlyVisiblePages) {
+  async forceRendering(currentlyVisiblePages) {
     const visiblePages = currentlyVisiblePages || this._getVisiblePages();
-    const scrollAhead = this.#getScrollAhead(visiblePages);
-    const preRenderExtra =
-      this._spreadMode !== SpreadMode.NONE &&
-      this._scrollMode !== ScrollMode.HORIZONTAL;
 
-    const pageView = this.renderingQueue.getHighestPriority(
-      visiblePages,
-      this._pages,
-      scrollAhead,
-      preRenderExtra
-    );
+    if (this.#rendering) {
+      this.#cacheTask.push({
+        visiblePages
+      })
+      return
+    }
+    this.#rendering = true
+    const renderingTask = []
+    for (let i = 0; i < visiblePages.views.length; i++) {
+      const pageView = visiblePages.views[i].view
+      if (pageView.renderingState === RenderingStates.FINISHED && pageView.canvas) {
+        continue
+      } else if (pageView.renderingState === RenderingStates.FINISHED) {
+        pageView.reset()
+      }
+      await this.#ensurePdfPageLoaded(pageView).then(() => {
+        renderingTask.push(pageView.draw())
+        this.#renderedPagesView.push(pageView)
+      })
+    }
+    await Promise.all(renderingTask)
 
-    if (pageView) {
-      this.#ensurePdfPageLoaded(pageView).then(() => {
-        this.renderingQueue.renderView(pageView);
-      });
+    this.#rendering = false
+
+    if (this.#cacheTask.length) {
+      const task = this.#cacheTask.pop()
+      this.#cacheTask = []
+      await this.forceRendering(task.visiblePages)
       return true;
     }
     return false;
@@ -1901,8 +1921,9 @@ class PDFViewer {
       } while (--steps > 0 && newScale < MAX_SCALE);
     }
 
-    options.noScroll = false;
+    options.noScroll = true;
     this._setScale(newScale, options);
+    this.#rendering = false;
   }
 
   /**
@@ -1938,8 +1959,14 @@ class PDFViewer {
       } while (--steps > 0 && newScale > MIN_SCALE);
     }
 
-    options.noScroll = false;
+    options.noScroll = true;
     this._setScale(newScale, options);
+    this.#rendering = false;
+  }
+
+  resize(val) {
+    this._setScale(val, { noScroll: false, drawingDelay: 400 })
+    this.#rendering = false;
   }
 
   /**
@@ -1984,7 +2011,7 @@ class PDFViewer {
     for (const pageView of this._pages) {
       pageView.update(updateArgs);
     }
-    if (this.#scaleTimeoutId !== null) {
+    if (this.#scaleTimeoutId !== null && (!this.isInPresentationMode && !this.exitPresentationMode)) {
       clearTimeout(this.#scaleTimeoutId);
       this.#scaleTimeoutId = null;
     }

+ 48 - 13
packages/core/src/ui_utils.js

@@ -132,22 +132,19 @@ export function parseAdobePDFTimestamp(timestamp) {
     const [, year, month, day, hour, minute, second, offset] = match;
     // const offsetHours = parseInt(offset.slice(1, 3));
     // const offsetMinutes = parseInt(offset.slice(4, 6));
-
     // 构建 Date 对象,注意月份要减 1
     const date = new Date(
-      Date.UTC(
-        parseInt(year),
-        parseInt(month) - 1,
-        parseInt(day),
-        parseInt(hour),
-        parseInt(minute),
-        parseInt(second)
-      )
+      parseInt(year),
+      parseInt(month) - 1,
+      parseInt(day),
+      parseInt(hour),
+      parseInt(minute),
+      parseInt(second)
     );
 
     // 考虑时区偏移
-    date.setHours(date.getUTCHours());
-    date.setMinutes(date.getUTCMinutes());
+    // date.setHours(date.getUTCHours());
+    // date.setMinutes(date.getUTCMinutes());
 
     return date;
   } else {
@@ -690,8 +687,8 @@ function getVisibleElements({
     return rtl ? elementLeft < right : elementRight > left;
   }
 
-  const visible = [],
-    ids = new Set(),
+  let visible = []
+  const ids = new Set(),
     numViews = views.length;
   let firstVisibleElementInd = binarySearchFirstItem(
     views,
@@ -792,9 +789,47 @@ function getVisibleElements({
       return a.id - b.id; // ensure stability
     });
   }
+
+  getExpandKeepRange(visible, ids, views, 2)
+
   return { first, last, views: visible, ids };
 }
 
+function getExpandKeepRange (visible, ids, views, size) {
+  if (!visible.length) return
+  if (visible) {
+    const firstPageIndex = visible[0].view.pageIndex
+    const endPageIndex = visible[visible.length - 1].view.pageIndex
+    let start = Math.min(firstPageIndex, endPageIndex)
+    let end = Math.max(firstPageIndex, endPageIndex)
+    const pagesCount = views.length
+    if (start > 1) {
+      for (let i = Math.max(1, start - size); i < start && i < pagesCount; i++) {
+        const view = views[i]
+        visible.push({
+          id: view.id,
+          view,
+          percent: 0,
+          widthPercent: 0,
+        })
+        ids.add(view.id);
+      }
+    }
+    if (end < pagesCount - 1) {
+      for (let i = end + 1; i < pagesCount && i <= end + size; i++) {
+        const view = views[i]
+        visible.push({
+          id: view.id,
+          view,
+          percent: 0,
+          widthPercent: 0,
+        })
+        ids.add(view.id);
+      }
+    }
+  }
+}
+
 /**
  * Event handler to suppress context menu.
  */

+ 16 - 8
packages/core/src/worker/compdfkit_worker.js

@@ -15,7 +15,7 @@ let PDFDestination = {}
 let WidgetItem = {}
 let WidgetItemsArray = {}
 let password = ''
-let TextFindItem = {}
+let TextFindItemArray = []
 let EditCharPlace = {}
 let EndEditCharPlace = {}
 let RectArray = []
@@ -518,18 +518,26 @@ class CPDFWorker {
         let resFind = 1
         let pageSearchIndex = 0
         do {
-          TextFindItem = {}
+          TextFindItemArray = []
           resFind = Module._FindNext(pagePtr, textPtr, textFindPtr)
 
           if (resFind) {
-            const { Left: left, Top: top, Right: right, Bottom: bottom, Content: content } = TextFindItem
+            const length = TextFindItemArray.length
+            const quads = []
+            for (let i = 0; i < length; i++) {
+              const item = TextFindItemArray[i]
+              const { Left: left, Top: top, Right: right, Bottom: bottom } = item
+              quads.push({
+                left,
+                top,
+                right,
+                bottom,
+              })
+            }
             searchResults.push({
               pageNum: i + 1,
-              left,
-              top,
-              right,
-              bottom,
-              content,
+              quads,
+              content: TextFindItemArray[0].Content.replace(/\r?\n/gm, ' '),
               pageSearchIndex,
               searchValue: value
             })

+ 1 - 1
packages/webview/locales/en.json

@@ -88,7 +88,7 @@
     "displayMode": "Display Mode",
     "singlePage": "Single Page",
     "twoPages": "Two Pages",
-    "bookMode": "Book Mode",
+    "coverMode": "Cover Mode",
 
     "splitView": "Scroll",
     "vertical": "Vertical Scrolling",

+ 1 - 1
packages/webview/locales/zh-CN.json

@@ -92,7 +92,7 @@
     "splitView": "分屏视图",
     "singlePage": "单页",
     "twoPages": "双页",
-    "bookMode": "书本模式",
+    "coverMode": "封面模式",
 
     "themes": "主题"
   },

+ 9 - 2
packages/webview/src/assets/main.scss

@@ -6,9 +6,9 @@
 
 * {
   &::-webkit-scrollbar {
-    width: 4px;
+    width: 7px;
     &:horizontal {
-      height: 4px;
+      height: 7px;
     }
   }
 
@@ -195,6 +195,13 @@ input, textarea, select {
   }
 }
 
+.annotationContainer {
+  overflow: hidden;
+  position: relative;
+  width: 100%;
+  height: 100%;
+}
+
 .annotationContainer > div {
   position: absolute;
   cursor: pointer;

+ 5 - 1
packages/webview/src/components/Annotate/Annotate.vue

@@ -57,7 +57,7 @@
 </template>
 
 <script setup>
-  import { computed } from 'vue'
+  import { computed, onUnmounted } from 'vue'
   import { useViewerStore } from '@/stores/modules/viewer'
   import { useDocumentStore } from '@/stores/modules/document'
   import core from '@/core'
@@ -126,6 +126,10 @@
     }
   }
   core.addEvent('imageChange', unselectImage)
+
+  onUnmounted(() => {
+    core.removeEvent('imageChange', unselectImage)
+  })
 </script>
 
 <style lang="scss">

+ 1 - 1
packages/webview/src/components/AnnotationContainer/AnnotationContent.vue

@@ -15,7 +15,7 @@
                 <Strikeout v-else-if="item.type === 'strikeout'" />
                 <Underline v-else-if="item.type === 'underline'" />
                 <Ink v-else-if="item.type === 'ink'" />
-                <LineTool v-else-if="item.type === 'line' && ((item.tail === 'None' && item.head === 'None') || (!item.tail && !item.head))" />
+                <LineTool v-else-if="item.type === 'line' && (((item.tail === 'None' || item.tail === 'Unknown') && (item.head === 'None' || item.head === 'Unknown')) || (!item.tail && !item.head))" />
                 <ArrowTool v-else-if="item.type === 'line' && (item.tail === 'OpenArrow' || item.head === 'OpenArrow' || item.arrow)" />
                 <RectangleTool v-else-if="item.type === 'square'" />
                 <EllipseTool v-else-if="item.type === 'circle'" />

+ 0 - 1
packages/webview/src/components/CompareDocumentContainer/CompareDocumentContainer.vue

@@ -132,7 +132,6 @@ watch(() => toolMode.value, (newValue, oldValue) => {
   }
   if (newValue === 'compare' && newValue !== oldValue) {
     setTimeout(() => {
-      console.log(verified)
       if (!verified.value) {
         alert('Invalid license')
       }

+ 32 - 5
packages/webview/src/components/DocumentContainer/DocumentContainer.vue

@@ -71,8 +71,9 @@ const isHandActive = computed(() => {
   return useViewer.getActiveHand
 })
 
+const isLeftPanelOpen = computed(() => useViewer.isElementOpen('leftPanel'))
 const leftPanelSpace = computed(() => {
-  return useViewer.isElementOpen('leftPanel') ? 260 : 0
+  return isLeftPanelOpen.value ? 260 : 0
 })
 const rightPanelSpace = computed(() => {
   return (useViewer.isElementOpen('rightPanel') || useViewer.isElementOpen('pageModePanel') || useViewer.isElementOpen('stampPanel') || useViewer.isElementOpen('linkPanel')) ? 220 : 0
@@ -89,6 +90,9 @@ const load = computed(() => useViewer.getUpload)
 async function handleUpload(evt) {
   const file = evt.target.files[0];
   if (!file) return
+  if (isLeftPanelOpen.value) {
+    core.toggleSidebar()
+  }
   useViewer.$patch({
     fullMode: false,
     currentPage: 0,
@@ -421,7 +425,6 @@ window.instance.initOptions = async (options) => {
 .document .page {
   position: relative;
   margin: 20px auto;
-  overflow: visible;
   background-clip: content-box;
   background-color: rgba(255, 255, 255, 1);
 
@@ -450,7 +453,6 @@ window.instance.initOptions = async (options) => {
   }
 }
 
-.document.scrollHorizontal,
 .document.scrollHorizontal,
 .spread {
   white-space: nowrap;
@@ -463,6 +465,11 @@ window.instance.initOptions = async (options) => {
     vertical-align: middle;
   }
 }
+.pdfPresentationMode .page {
+  margin: 2px;
+  display: inline-block;
+  vertical-align: middle;
+}
 
 .document.scrollHorizontal .spread {
   margin: 0px;
@@ -559,7 +566,9 @@ window.instance.initOptions = async (options) => {
 }
 
 .searchContainer {
-  position: relative;
+  position: absolute;
+  top: 0;
+  left: 0;
   z-index: 3;
   pointer-events: none;
 }
@@ -568,7 +577,7 @@ window.instance.initOptions = async (options) => {
   background-color: rgba(255, 255, 0, 0.25);
 }
 
-.searchContainer .highlight.selected {
+.searchContainer .selected .highlight {
   background-color: rgba(255, 255, 0, 0.7);
 }
 
@@ -673,6 +682,24 @@ window.instance.initOptions = async (options) => {
   display: none !important;
 }
 
+.document-container.pdfPresentationMode {
+  top: 0;
+  background-color: rgb(0 0 0);
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  cursor: none;
+  user-select: none;
+}
+
+.document .dummyPage {
+  position: relative;
+  width: 0;
+  height: 100vh;
+  display: inline-block;
+  vertical-align: middle;
+}
+
 @keyframes caretanimation {
   0% {
     color: #000;

+ 2 - 0
packages/webview/src/components/RightPanel/RightPanel.vue

@@ -442,6 +442,7 @@
   }
 
   const moveUp = () => {
+    if (!selectedItemIndex.value) return
     for (var i = 0; i < items.value.length; i++) {
       if (items.value[i] === items.value[selectedItemIndex.value]) {
         items.value.splice(i - 1, 2, items.value[i], items.value[i - 1])
@@ -458,6 +459,7 @@
   }
 
   const moveDown = () => {
+    if (selectedItemIndex.value === items.value.length - 1) return
     for (var i = 0; i < items.value.length; i++) {
       if (items.value[i] === items.value[selectedItemIndex.value] && i < items.value.length - 1) {
         items.value.splice(i, 2, items.value[i + 1], items.value[i])

+ 1 - 1
packages/webview/src/components/RightPanelPageMode/RightPanelPageMode.vue

@@ -11,7 +11,7 @@
         <div class="mode-tabs">
           <Button :class="{ active: pageMode === 0 }" @click="handleSpreadMode(0)" :title="$t('pageModePanel.singlePage')"><SinglePage /></Button>
           <Button :class="{ active: pageMode === 1 }" @click="handleSpreadMode(1)" :title="$t('pageModePanel.twoPages')"><DoublePage /></Button>
-          <Button :class="{ active: pageMode === 2 }" @click="handleSpreadMode(2)" :title="$t('pageModePanel.bookMode')"><Pagecover /></Button>
+          <Button :class="{ active: pageMode === 2 }" @click="handleSpreadMode(2)" :title="$t('pageModePanel.coverMode')"><Pagecover /></Button>
         </div>
       </div>
       <div class="mode-item">

+ 4 - 4
packages/webview/src/components/SearchContainer/SearchHeader.vue

@@ -31,7 +31,7 @@
 
 <script setup>
   import debounce from 'lodash.debounce'
-  import { ref, watch, computed } from "vue"
+  import { ref, watch, computed, toRaw } from "vue"
   import core from '@/core'
   import { useDocumentStore } from '@/stores/modules/document'
   import { useViewerStore } from '@/stores/modules/viewer'
@@ -49,7 +49,7 @@
     const activeIndex = rawActiveIndex.value
     if (reseults.length > 0) {
       const prevIndex = activeIndex <= 0 ? reseults.length - 1 : activeIndex - 1
-      core.setActiveSearchResult(reseults[prevIndex], prevIndex)
+      core.setActiveSearchResult(toRaw(reseults[prevIndex]), prevIndex)
       rawActiveIndex.value = prevIndex
     }
   }
@@ -59,7 +59,7 @@
     const activeIndex = rawActiveIndex.value
     if (reseults.length > 0) {
       const nextIndex = activeIndex === reseults.length - 1 ? 0 : activeIndex + 1
-      core.setActiveSearchResult(reseults[nextIndex], nextIndex)
+      core.setActiveSearchResult(toRaw(reseults[nextIndex]), nextIndex)
       rawActiveIndex.value = nextIndex
     }
   }
@@ -124,7 +124,7 @@
       width: 0;
       height: 30px;
       outline: none;
-      padding: 5px 8px;
+      padding: 5px 20px 5px 0px;
       box-shadow: none;
       border: none;
       background-color: var(--c-findbar-input-bg);

+ 7 - 0
packages/webview/src/components/ToggleRightPanelButton/index.vue

@@ -25,6 +25,13 @@
     isEditorPanelDisabled.value = !editTextPanelIsActive.value && !document.querySelector(".frame-container.editing")
   })
 
+  watch(toolMode, (newValue, oldValue) => {
+    if (oldValue === 'form' && newValue !== 'form') {
+      useViewer.closeElement(item.element)
+      core.toggleSidebar()
+    }
+  })
+
   const onClick = () => {
     setTimeout(() => {
       if (toolMode.value === 'form') {

+ 3 - 0
packages/webview/src/core/getSelectedText.js

@@ -0,0 +1,3 @@
+import core from '@/core'
+
+export default () => core.getDocumentViewer().getSelectedText()

+ 5 - 1
packages/webview/src/core/index.js

@@ -26,6 +26,7 @@ import switchScrollMode from './switchScrollMode'
 import initConfig from './initConfig'
 import download from './download'
 import addEvent from './addEvent'
+import removeEvent from './removeEvent'
 import setTool from './setTool'
 import exportXfdf from './exportXfdf'
 import webViewerPageMode from './webViewerPageMode'
@@ -49,6 +50,7 @@ import search from './search'
 import setActiveSearchResult from './setActiveSearchResult'
 import clearSearchResults from './clearSearchResults'
 import init from './init'
+import getSelectedText from './getSelectedText'
 
 export default {
   getDocumentViewer,
@@ -82,6 +84,7 @@ export default {
   initConfig,
   download,
   addEvent,
+  removeEvent,
   setTool,
   exportXfdf,
   webViewerPageMode,
@@ -104,5 +107,6 @@ export default {
   search,
   setActiveSearchResult,
   clearSearchResults,
-  init
+  init,
+  getSelectedText
 }

+ 3 - 0
packages/webview/src/core/removeEvent.js

@@ -0,0 +1,3 @@
+import core from '@/core'
+
+export default (eventName, fn) => core.getDocumentViewer().removeEvent(eventName, fn)

+ 22 - 0
packages/webview/src/helpers/hotkeysManager.js

@@ -0,0 +1,22 @@
+const Shortcuts = {
+  COPY: 'copy',
+}
+
+const keys = {
+  CTRL_C: 'ctrl+c',
+  COMMAND_C: 'command+c'
+}
+
+function concatKeys(...keys) {
+  return keys.join(', ');
+}
+
+const shortcutKeys = {
+  [Shortcuts.COPY]: concatKeys(Keys.CTRL_C, Keys.COMMAND_C)
+}
+
+const hotkeysManager = {
+  initialize() {
+    createKeyHandlerMap
+  }
+}

+ 1 - 0
packages/webview/src/helpers/utils.js

@@ -2,6 +2,7 @@ const uploadFile = (extension) =>
   new Promise((resolve, reject) => {
     const fileInput = document.createElement('input');
     fileInput.type = 'file';
+    fileInput.style.display = 'none'
     fileInput.accept = extension;
     fileInput.onchange = ()=> {
       if (fileInput.files) {

+ 8 - 10
packages/webview/src/stores/modules/document.js

@@ -187,26 +187,24 @@ export const useDocumentStore = defineStore({
       const annotations = this.annotations
       for (const page in annotations) {
         const pageAnnotations = annotations[page]
-        const  len = pageAnnotations.length
+        const len = pageAnnotations.length
         if (len) {
           for (let i = 0; i < len; i++) {
            // if (pageAnnotations[i].type === 'line' && pageAnnotations[i].tail === 'OpenArrow') continue
             if (pageAnnotations[i] && pageAnnotations[i].type === 'line') {
-              let supportLine=true
+              let supportLine = true
               if(pageAnnotations[i].tail || pageAnnotations[i].head)
               {
                 if (pageAnnotations[i].tail === 'None' && pageAnnotations[i].head === 'None') {
-                  supportLine=true
-                }
-                else
-                {
-                  if (pageAnnotations[i].head && pageAnnotations[i].head !== 'OpenArrow' && pageAnnotations[i].head !== 'None') {
-                    supportLine=false
+                  supportLine = true
+                } else {
+                  if (pageAnnotations[i].head && pageAnnotations[i].head !== 'OpenArrow' && pageAnnotations[i].head !== 'None' && pageAnnotations[i].head !== 'Unknown') {
+                    supportLine = false
                     continue
                   }
 
-                  if (pageAnnotations[i].tail && pageAnnotations[i].tail !== 'OpenArrow' && pageAnnotations[i].tail !== 'None') {
-                    supportLine=false
+                  if (pageAnnotations[i].tail && pageAnnotations[i].tail !== 'OpenArrow' && pageAnnotations[i].tail !== 'None' && pageAnnotations[i].tail !== 'Unknown') {
+                    supportLine = false
                     continue
                   }
                 }