Browse Source

fix: 一轮测试修复

wzl 4 months ago
parent
commit
fe215ef580
35 changed files with 253 additions and 209 deletions
  1. 35 24
      packages/core/src/TextSelection.ts
  2. 1 1
      packages/core/src/annotation/freetext.js
  3. 1 1
      packages/core/src/annotation/ink.js
  4. 0 1
      packages/core/src/annotation/layer.js
  5. 1 1
      packages/core/src/annotation/line.js
  6. 1 1
      packages/core/src/annotation/link.js
  7. 86 68
      packages/core/src/annotation/redaction.js
  8. 1 1
      packages/core/src/annotation/shape.js
  9. 1 1
      packages/core/src/annotation/stamp.js
  10. 1 1
      packages/core/src/annotation/text.js
  11. 1 1
      packages/core/src/form/check_box.js
  12. 1 1
      packages/core/src/form/combo_box.js
  13. 1 1
      packages/core/src/form/list_box.js
  14. 1 1
      packages/core/src/form/push_button.js
  15. 1 1
      packages/core/src/form/radio_button.js
  16. 1 1
      packages/core/src/form/signature_fields.js
  17. 1 1
      packages/core/src/form/text_field.js
  18. 15 7
      packages/core/src/index.js
  19. 1 18
      packages/core/src/markup/text_annotation.js
  20. 0 1
      packages/core/src/pdf_annotation_layer.js
  21. 9 1
      packages/core/src/worker/compdfkit_worker.js
  22. 5 5
      packages/webview/locales/en.json
  23. 6 6
      packages/webview/locales/zh-CN.json
  24. 1 1
      packages/webview/src/apis/setActiveTool.js
  25. 6 0
      packages/webview/src/assets/main.scss
  26. 4 4
      packages/webview/src/components/Dialogs/Dialog.vue
  27. 19 2
      packages/webview/src/components/DocumentContainer/DocumentContainer.vue
  28. 31 24
      packages/webview/src/components/Header/Header.vue
  29. 0 5
      packages/webview/src/components/HeaderItems/HeaderItems.vue
  30. 2 3
      packages/webview/src/components/SecurityToolbar/RedactionConfirmDialog.vue
  31. 1 8
      packages/webview/src/components/SecurityToolbar/SecurityApplyBar.vue
  32. 7 6
      packages/webview/src/components/SecurityToolbar/SecurityToolbar.vue
  33. 1 1
      packages/webview/src/components/SignatureToolBar/SignatureVerifyBar.vue
  34. 1 1
      packages/webview/src/components/Toolbar/Toolbar.vue
  35. 9 9
      packages/webview/src/stores/modules/viewer.js

+ 35 - 24
packages/core/src/TextSelection.ts

@@ -227,6 +227,7 @@ export default class TextSelection {
       return
     }
     if (!this.selecting) return
+    this.container?.classList.add('selecting')
 
     const selection = await this.messageHandler.sendWithPromise('GetCharsRangeAtPos', {
       pagePtr: this.#pagePtr,
@@ -260,7 +261,28 @@ export default class TextSelection {
     this._selection = selection
     this.updateSelection()
 
-    if (this._selection && this.textContainer) {
+    if (this._selection && this.textContainer && ['redaction', 'remove'].includes(this.tool)) {
+      const redactData = {
+        operate: 'add-annot',
+        type: 'redact',
+        pageIndex: this.pageIndex,
+        date: new Date(),
+        rect: this.rect,
+        rects: this.rects,
+        color: this.color,
+        erasure: this.tool === 'remove',
+        quadPoints: this.quadPoints,
+        contents: this._selection.textContent || undefined
+      }
+      this.tool === 'redaction' && (redactData.fillColor = 'rgb(0, 0, 0)')
+
+      this.eventBus.dispatch('annotationChange', {
+        type: 'add',
+        annotation: redactData
+      })
+      this.cleanSelection()
+
+    } else if (this._selection && this.textContainer) {
       this.showTextPopup()
     }
   }
@@ -280,35 +302,24 @@ export default class TextSelection {
       }
 
       if (this.tool === 'redaction' || this.tool === 'remove') {
-        for (let i = 0; i < this.rects.length; i++) {
-          const rect = this.rects[i]
-          const redactData = {
-            operate: 'add-annot',
-            type: 'redact',
-            pageIndex: this.pageIndex,
-            date: new Date(),
-            rect,
-            color: this.color,
-            erasure: this.tool === 'remove'
-          }
-          this.tool === 'redaction' && (redactData.fillColor = 'rgb(0, 0, 0)')
-
-          this.eventBus.dispatch('annotationChange', {
-            type: 'add',
-            annotation: redactData
-          })
-        }
-      } else {
-        this.eventBus.dispatch('annotationChange', {
-          type: 'add',
-          annotation: annotationData
-        })
+        annotationData.type = 'redact'
+        annotationData.rects = this.rects
+        annotationData.erasure = this.tool === 'remove'
+        this.tool === 'redaction' && (annotationData.fillColor = 'rgb(0, 0, 0)')
+        delete annotationData.opacity
       }
+
+      this.eventBus.dispatch('annotationChange', {
+        type: 'add',
+        annotation: annotationData
+      })
       this.cleanSelection()
     }
     this.startPoint = null
     this.endPoint = null
     this.selecting = false
+    this.container?.classList.remove('selecting')
+
     document.removeEventListener('mouseup', this.handleMouseUp)
     document.removeEventListener('touchend', this.handleMouseUp)
 

+ 1 - 1
packages/core/src/annotation/freetext.js

@@ -581,7 +581,7 @@ export default class Shape extends Base {
 
   handleContainerClick (event) {
     event && event.stopPropagation()
-    if (!this.hidden || this.layer.annotationStore.creating) return
+    if (!this.hidden || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     this.hidden = false
     this.updateTool()
     onClickOutside([this.outerLine, this.freetextElement, this.deletetButton, this.replyButton, document.querySelector('.toggle-button')], this.handleOutside.bind(this))

+ 1 - 1
packages/core/src/annotation/ink.js

@@ -115,7 +115,7 @@ export default class Ink extends Base {
     if (this.layer.tool) {
       event && event.stopPropagation()
     }
-    if (!this.hidden || this.layer.annotationStore.creating) return
+    if (!this.hidden || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     this.hidden = false
     this.updateTool()
     onClickOutside([this.svgElement, this.outerLine, this.deletetButton, this.replyButton, document.querySelector('.toggle-button')], this.handleOutside.bind(this))

+ 0 - 1
packages/core/src/annotation/layer.js

@@ -613,7 +613,6 @@ class ComPDFAnnotationLayer {
   renderContainer() {
     const div = document.createElement("div")
     div.className = "annotationContainer"
-    div.tabIndex = 0
     this.scene.append(div)
     this.div = div
   }

+ 1 - 1
packages/core/src/annotation/line.js

@@ -245,7 +245,7 @@ export default class Line extends Base {
   }
 
   handleClick () {
-    if (!this.hidden || this.layer.annotationStore.creating) return
+    if (!this.hidden || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     this.hidden = false
     this.updateTool()
     onClickOutside([this.svgElement, this.outerLine, this.deletetButton, this.replyButton, document.querySelector('.toggle-button')], this.handleOutside.bind(this))

+ 1 - 1
packages/core/src/annotation/link.js

@@ -415,7 +415,7 @@ export default class Link extends Base {
   }
 
   handleClick () {
-    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating || this.layer.toolMode === 'editor') return
+    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool) || this.layer.toolMode === 'editor') return
     if (this.layer.toolMode === 'annotation' && this.layer.tool === 'link') {
       this.hidden = false
       this.updateTool()

+ 86 - 68
packages/core/src/annotation/redaction.js

@@ -44,6 +44,7 @@ export default class Redaction extends Base {
     this.onMousedown = this.handleMouseDown.bind(this)
     this.onMouseup = this.handleMouseUp.bind(this)
     this.onDelete = this.handleDelete.bind(this)
+    this.onKeydown = this.handleKeyDown.bind(this)
     this.render()
   }
 
@@ -58,66 +59,54 @@ export default class Redaction extends Base {
     this.start = start
     this.end = end
 
-    const rect = this.calculate(start, end)
+    const wholeRect = this.calculate(start, end)
+
+    const rects = []
+    if (annotation.rects && annotation.rects.length) {
+      annotation.rects.forEach(rect => {
+        const { start, end } = this.getActualRect(this.viewport, this.scale, rect)
+        rects.push(this.calculate(start, end))
+      })
+    } else {
+      rects.push(wholeRect)
+    }
     
-    const annotationContainer = document.createElement('div')
-    annotationContainer.id = annotation.name
-    annotationContainer.className = 'annotation'
-    annotationContainer.style.top = rect.top + 'px'
-    annotationContainer.style.left = rect.left + 'px'
-    annotationContainer.style.width = rect.width + 'px'
-    annotationContainer.style.height = rect.height + 'px'
+    const annotationContainer = createElement(
+      'div',
+      {
+        left: wholeRect.left + 'px',
+        top: wholeRect.top + 'px',
+        width: `${Math.abs(wholeRect.width)}px`,
+        height: `${Math.abs(wholeRect.height)}px`,
+      },
+      {
+        id: annotation.name,
+        class: 'annotation redact'
+      }
+    )
     this.annotationContainer = annotationContainer
 
     const actualbdwidth = (annotation.borderWidth || 2) * this.scale
-    let shapeElement
-    if (annotation.erasure) {
-      shapeElement = document.createElementNS("http://www.w3.org/2000/svg", "svg")
-      shapeElement.setAttribute("viewBox", `0 0 ${rect.width} ${rect.height}`)
-      shapeElement.addEventListener('mousedown', this.handleClick.bind(this))
-
-      const rectElement = createSvg(
-        "rect",
-        {
-          x: actualbdwidth / 2,
-          y: actualbdwidth / 2,
-          width: `${Math.abs(rect.width - actualbdwidth)}px`,
-          height: `${Math.abs(rect.height - actualbdwidth)}px`,
-          stroke: annotation.borderColor || '#FF0000',
-          'stroke-width': actualbdwidth,
-          'stroke-opacity': annotation.opacity || 1,
-          fill: annotation.fillColor || 'none',
-          'fill-opacity': annotation.opacity || 1,
-        }
-      )
-      shapeElement.append(rectElement)
-    } else {
-      shapeElement = createElement(
+    const shapeElements = []
+    rects.forEach(rect => {
+      const shapeElement = createElement(
         'div',
         {
-          left: 0,
-          top: 0,
-          width: `${Math.abs(rect.width)}px`,
+          left: rect.left - wholeRect.left - (annotation.erasure ? actualbdwidth / 2 : actualbdwidth) + 'px',
+          top: rect.top - wholeRect.top + 'px',
+          width: `${Math.abs(rect.width) + (annotation.erasure ? actualbdwidth : actualbdwidth * 2) }px`,
           height: `${Math.abs(rect.height)}px`,
-          position: 'relative',
+          position: 'absolute',
           border: `${actualbdwidth}px solid #FF0000`,
           transition: 'none'
         }
       )
-
-      this.annotationContainer.addEventListener('mousedown', this.handleClick.bind(this))
-      this.annotationContainer.addEventListener('mouseenter', () => {
-        shapeElement.style.backgroundColor = '#000000'
-        shapeElement.style.border = ''
-      })
-      this.annotationContainer.addEventListener('mouseleave', () => {
-        shapeElement.style.backgroundColor = ''
-        shapeElement.style.border = `${actualbdwidth}px solid #FF0000`
-      })
-    }
-    this.shapeElement = shapeElement
-    this.annotationContainer.append(this.shapeElement)
+      annotation.erasure && (shapeElement.style.backgroundColor = 'rgba(255, 255, 255)')
+      shapeElements.push(shapeElement)
+      this.annotationContainer.append(shapeElement)
+    })
     this.container.append(this.annotationContainer)
+    this.annotationContainer.addEventListener('mousedown', this.handleClick.bind(this))
 
     this.outerLineContainer = document.createElement('div')
     this.outerLineContainer.className = 'outline-container'
@@ -132,14 +121,15 @@ export default class Redaction extends Base {
       },
       {
         width: '30px',
-        height: '30px'
+        height: '30px',
+        zIndex: 1
       }
     );
     this.deletetButton.addEventListener('click', this.onDelete)
     this.deletetButton.innerHTML = this.deleteSvgStr
 
-    const left = (rect.left + rect.width - 30) + 'px'
-    const top = (rect.top + rect.height + 8) + 'px'
+    const left = (wholeRect.left + wholeRect.width - 30) + 'px'
+    const top = (wholeRect.top + wholeRect.height + 8) + 'px'
     this.deletetButton.style.left = left
     this.deletetButton.style.top = top
     
@@ -147,10 +137,10 @@ export default class Redaction extends Base {
 
     outerLine.style.position = 'absolute'
     outerLine.style.zIndex = 1
-    outerLine.style.left = `${rect.left - 8}px`
-    outerLine.style.top = `${rect.top - 8}px`
-    outerLine.style.width = `${rect.width + 8 * 2}px`
-    outerLine.style.height = `${rect.height + 8 * 2}px`
+    outerLine.style.left = `${wholeRect.left - 8 - (annotation.erasure ? actualbdwidth / 2 : actualbdwidth)}px`
+    outerLine.style.top = `${wholeRect.top - 8}px`
+    outerLine.style.width = `${wholeRect.width + 8 * 2 + (annotation.erasure ? actualbdwidth : actualbdwidth * 2)}px`
+    outerLine.style.height = `${wholeRect.height + 8 * 2}px`
     this.outerLine = outerLine
     
     this.moveRect = createSvg(
@@ -161,8 +151,8 @@ export default class Redaction extends Base {
         stroke: "#4982E6",
         'stroke-width': 1,
         'fill-opacity': 0,
-        width: rect.width + 8,
-        height: rect.height + 8,
+        width: wholeRect.width + 8 + (annotation.erasure ? actualbdwidth : actualbdwidth * 2),
+        height: wholeRect.height + 8,
         x: 4,
         y: 4
       }, {
@@ -174,21 +164,40 @@ export default class Redaction extends Base {
     this.outerLineContainer.append(this.outerLine)
     this.outerLineContainer.append(this.deletetButton)
 
-    !annotation.erasure && this.moveRect.addEventListener('mouseenter', () => {
-      this.shapeElement.style.backgroundColor = '#000000'
-      this.shapeElement.style.border = ''
-    })
-    !annotation.erasure && this.moveRect.addEventListener('mouseleave', () => {
-      this.shapeElement.style.backgroundColor = ''
-      this.shapeElement.style.border = '2px solid #FF0000'
-    })
+    if (!annotation.erasure) {
+      this.annotationContainer.addEventListener('mouseenter', () => {
+        if (this.layer.pageDiv.classList.contains('selecting')) return
+        shapeElements.forEach(el => {
+          el.style.backgroundColor = '#000000'
+          el.style.border = ''
+        })
+      })
+      this.annotationContainer.addEventListener('mouseleave', () => {
+        shapeElements.forEach(el => {
+          el.style.backgroundColor = ''
+          el.style.border = `${actualbdwidth}px solid #FF0000`
+        })
+      })
+
+      this.moveRect.addEventListener('mouseenter', () => {
+        shapeElements.forEach(el => {
+          el.style.backgroundColor = '#000000'
+          el.style.border = ''
+        })
+      })
+      this.moveRect.addEventListener('mouseleave', () => {
+        shapeElements.forEach(el => {
+          el.style.backgroundColor = ''
+          el.style.border = '2px solid #FF0000'
+        })
+      })
+    }
 
     this.initial = true
   }
 
-  getActualRect (viewport, s,) {
-    const annotation = this.annotation
-    const { left: x1, top: y1, right: x2, bottom: y2 } = annotation.rect
+  getActualRect (viewport, s, rect) {
+    const { left: x1, top: y1, right: x2, bottom: y2 } = rect || this.annotation.rect
     
     const start = getActualPoint(
       {
@@ -270,6 +279,7 @@ export default class Redaction extends Base {
       this.container.append(this.outerLineContainer)
       this.outerLine.addEventListener('mousedown', this.onMousedown)
       this.outerLine.addEventListener('touchstart', this.onMousedown)
+      document.addEventListener('keydown', this.onKeydown)
     }
   }
 
@@ -296,6 +306,7 @@ export default class Redaction extends Base {
     this.outerLine.removeEventListener('mousedown', this.onMousedown)
     this.outerLine.removeEventListener('touchstart', this.onMousedown)
     this.outerLineContainer.remove()
+    document.removeEventListener('keydown', this.onKeydown)
   }
 
   handleMouseDown (event) {
@@ -378,4 +389,11 @@ export default class Redaction extends Base {
       }
     })
   }
+
+  handleKeyDown (event) {
+    const keyCode = event.keyCode || event.which
+    if (keyCode === 8 || keyCode === 46) { // 8 delete键,46 backspace键
+      this.handleDelete(event)
+    }
+  }
 }

+ 1 - 1
packages/core/src/annotation/shape.js

@@ -411,7 +411,7 @@ export default class Shape extends Base {
   }
 
   handleClick () {
-    if (!this.hidden || this.layer.annotationStore.creating) return
+    if (!this.hidden || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     this.hidden = false
     this.updateTool()
     onClickOutside([this.svgElement, this.outerLine, this.deletetButton, this.replyButton, document.querySelector('.toggle-button')], this.handleOutside.bind(this))

+ 1 - 1
packages/core/src/annotation/stamp.js

@@ -387,7 +387,7 @@ export default class Stamp extends Base {
     if (this.layer.tool) {
       event && event.preventDefault()
     }
-    if (!this.hidden || this.layer.annotationStore.creating) return
+    if (!this.hidden || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     this.hidden = false
     this.updateTool()
     onClickOutside([this.annotationContainer, this.outerLine, this.deletetButton, this.replyButton, document.querySelector('.toggle-button')], this.handleOutside.bind(this))

+ 1 - 1
packages/core/src/annotation/text.js

@@ -306,7 +306,7 @@ export default class Text extends Base {
   }
 
   handleClick (event) {
-    if (this.layer.addMode || this.layer.annotationStore.creating) return
+    if (this.layer.addMode || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     if (this.layer.tool) {
       event && event.stopPropagation()
     }

+ 1 - 1
packages/core/src/form/check_box.js

@@ -492,7 +492,7 @@ export default class CheckBox extends Base {
   }
 
   handleClick () {
-    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating) return
+    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     if (this.layer.toolMode === 'form') {
       this.hidden = false
       this.updateTool()

+ 1 - 1
packages/core/src/form/combo_box.js

@@ -502,7 +502,7 @@ export default class ComboBox extends Base {
   }
 
   handleClick () {
-    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating) return
+    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     if (this.layer.toolMode === 'form') {
       this.hidden = false
       this.updateTool()

+ 1 - 1
packages/core/src/form/list_box.js

@@ -467,7 +467,7 @@ export default class ListBox extends Base {
   }
 
   handleClick(event) {
-    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating) return
+    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     if (this.layer.toolMode === 'form') {
       this.hidden = false
       this.updateTool()

+ 1 - 1
packages/core/src/form/push_button.js

@@ -445,7 +445,7 @@ export default class PushButton extends Base {
   }
 
   handleClick () {
-    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating) return
+    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     if (this.layer.toolMode === 'form') {
       this.hidden = false
       this.updateTool()

+ 1 - 1
packages/core/src/form/radio_button.js

@@ -493,7 +493,7 @@ export default class RadioButton extends Base {
   }
 
   handleClick () {
-    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating) return
+    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     if (this.layer.toolMode === 'form') {
       this.hidden = false
       this.updateTool()

+ 1 - 1
packages/core/src/form/signature_fields.js

@@ -373,7 +373,7 @@ export default class SignatureFields extends Base {
   }
 
   handleClick () {
-    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating || this.layer.toolMode === 'editor' || document.getElementById("sign-image-save")) return
+    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool) || this.layer.toolMode === 'editor' || document.getElementById("sign-image-save")) return
     if (this.isDigital) return
     if (this.layer.pageDiv.querySelector('.annotationLayer svg').style.cursor === 'crosshair' && this.layer.toolMode !== 'sign') return
 

+ 1 - 1
packages/core/src/form/text_field.js

@@ -426,7 +426,7 @@ export default class TextField extends Base {
   }
 
   handleClick () {
-    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating) return
+    if (!this.hidden || document.fullscreenElement || this.layer.annotationStore.creating || ['redaction', 'remove'].includes(this.layer.tool)) return
     if (this.layer.toolMode === 'form') {
       this.hidden = false
       this.updateTool()

+ 15 - 7
packages/core/src/index.js

@@ -1779,8 +1779,6 @@ class ComPDFKitViewer {
       await this.#postAnnotations(annotateHandles)
     }
 
-    this.eventBus.dispatch('showRedactionApplyBar', this.redactionList.length ? true : false)
-
     this.isAnnotationModified = true
     return true
   }
@@ -2864,6 +2862,10 @@ class ComPDFKitViewer {
     if (mode === 'editor' && !this.fontFileInited) {
       // await this.initFontFile()
     }
+    if (['redaction', 'remove'].includes(this.activeTool) && this.toolMode !== 'security') {
+      this.redactionList.length && this.applyRedactions()
+      this.setTool({ color: '', tool: '' })
+    }
     this.eventBus.dispatch("toolModeChanged", mode);
 
     if (oldMode === 'editor' && this.toolMode !== 'editor') {
@@ -2882,12 +2884,12 @@ class ComPDFKitViewer {
               element.style.display = 'none';
               element.style.pointerEvents = 'none';
             } else if (annot.isHidden == 0) {
-              element.style.display = 'block';
-              element.style.pointerEvents = 'auto';
+              element.style.display = '';
+              element.style.pointerEvents = '';
             }
           } else {
-            element.style.display = 'block';
-            element.style.pointerEvents = 'auto';
+            element.style.display = '';
+            element.style.pointerEvents = '';
           }
         }
       }
@@ -4306,7 +4308,13 @@ class ComPDFKitViewer {
       }
       redactionList.forEach(async item => {
         if (item.erasure) {
-          await this.messageHandler.sendWithPromise('ErasureRedactionFromRect', { pagePtr: item.pagePtr, rect: item.rect })
+          if (item.rects && item.rects.length) {
+            item.rects.forEach(async rect => {
+              await this.messageHandler.sendWithPromise('ErasureRedactionFromRect', { pagePtr: item.pagePtr, rect })
+            })
+          } else {
+            await this.messageHandler.sendWithPromise('ErasureRedactionFromRect', { pagePtr: item.pagePtr, rect: item.rect })
+          }
         } else {
           await this.messageHandler.sendWithPromise('ApplyRedaction', { annotPtr: item.annotPtr })
         }

+ 1 - 18
packages/core/src/markup/text_annotation.js

@@ -204,7 +204,7 @@ class TextAnnotation extends BaseAnnotation {
   }
 
   handleClick () {
-    if (!this.hidden) return
+    if (!this.hidden || ['redaction', 'remove'].includes(this.layer.tool)) return
     this.hidden = false
     this.updateTool()
     const data = [this.markupContainer, this.outerLineContainer, this.deletetButton, this.replyButton]
@@ -270,11 +270,6 @@ class TextAnnotation extends BaseAnnotation {
     const annotation = this.annotation
     const markupContainer = document.createElement('div')
     markupContainer.className = 'markup'
-    this.setCss(markupContainer, {
-      mixBlendMode: 'multiply',
-      cursor: 'pointer',
-      pointerEvents: 'auto'
-    })
     for (let i = 0; i < positionArray.length; i++) {
       const currentPositionArray = positionArray[i]
       const container = document.createElement('div')
@@ -297,10 +292,6 @@ class TextAnnotation extends BaseAnnotation {
     const annotation = this.annotation
     const markupContainer = document.createElement('div')
     markupContainer.className = 'markup'
-    this.setCss(markupContainer, {
-      cursor: 'pointer',
-      pointerEvents: 'auto'
-    })
     for (let i = 0; i < positionArray.length; i++) {
       const currentPositionArray = positionArray[i]
       const container = document.createElement('div')
@@ -324,10 +315,6 @@ class TextAnnotation extends BaseAnnotation {
     const annotation = this.annotation
     const markupContainer = document.createElement('div')
     markupContainer.className = 'markup'
-    this.setCss(markupContainer, {
-      cursor: 'pointer',
-      pointerEvents: 'auto'
-    })
     for (let i = 0; i < positionArray.length; i++) {
       const currentPositionArray = positionArray[i]
       const container = document.createElement('div')
@@ -377,10 +364,6 @@ class TextAnnotation extends BaseAnnotation {
     const annotation = this.annotation
     const markupContainer = document.createElement('div')
     markupContainer.className = 'markup'
-    this.setCss(markupContainer, {
-      cursor: 'pointer',
-      pointerEvents: 'auto'
-    })
     for (let i = 0; i < positionArray.length; i++) {
       const currentPositionArray = positionArray[i]
       const container = document.createElement('div')

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

@@ -489,7 +489,6 @@ class PDFAnnotationLayer {
     // Create an AnnotationEditor layer div
     this.div = document.createElement("div");
     this.div.className = "annotationLayer";
-    this.div.tabIndex = 0;
     this.pageDiv.append(this.div);
   }
 

+ 9 - 1
packages/core/src/worker/compdfkit_worker.js

@@ -2048,7 +2048,7 @@ function createAnnotation(doc, pagePtr, annotation) {
   }
 
   annotation.annotPtr = annotPtr
-  const { color, borderWidth, contents, date, opacity, fillTransparency, rect, author, fillColor } = annotation
+  const { color, borderWidth, contents, date, opacity, fillTransparency, rect, author, fillColor, quadPoints } = annotation
   if (color) {
     setAnnotRGBColor({
       annotPtr,
@@ -2106,6 +2106,14 @@ function createAnnotation(doc, pagePtr, annotation) {
     })
   }
 
+  if (quadPoints) {
+    setQuadPoints({
+      pagePtr,
+      annotPtr,
+      quadPoints
+    })
+  }
+
   switch (typeInt) {
     case AnnotationType.TEXT:
       break

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

@@ -473,18 +473,18 @@
     "removePassword": "Remove Password",
 
     "redaction": "Redaction",
-    "remove": "Remove",
+    "fillBlack": "Fill Black",
+    "fillWhite": "Fill White",
     "caution": "Caution",
     "enterTip": "The redacted content will be permanently removed from this document. Please confirm your action.",
     "understand": "Understand and Start",
-    "applyTip": "After applying redactions, the redacted content will be permanently removed. The redacted document will be saved as a local file.",
+    "applyTip": "After applying redactions, the redacted content will be permanently removed.",
     "exit": "Exit",
     "apply": "Apply",
     "note": "Note",
     "exitTip": "Your redactions have not been applied. Are you sure to exit?",
     "exitConfirm": "Exit",
-    "saveAs": "Save As",
-    "saveTip": ["All redacted content in this document will be permanently deleted and cannot be recovered.", "Are you sure you want to redact them and save as?"],
-    "saveAsConfirm": "Save As"
+    "saveTip": ["All redacted content in this document will be permanently deleted and cannot be recovered.", "Are you sure you want to redact them and save the file?"],
+    "save": "Save"
   }
 }

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

@@ -473,18 +473,18 @@
     "removePassword": "移除密码",
 
     "redaction": "标记密文",
-    "remove": "移除",
+    "fillBlack": "填充黑色",
+    "fillWhite": "填充白色",
     "caution": "提示",
     "enterTip": "本文档中已被标记的内容将被永久删除。请确认您的操作。",
     "understand": "我已知晓,开始使用",
-    "applyTip": "应用标记密文后,被标记的内容将被永久删除。修改后的文档将被另存为本地文档。",
-    "exit": "取消",
+    "applyTip": "应用标记密文后,被标记的内容将被永久删除。",
+    "exit": "退出",
     "apply": "应用",
     "note": "提示",
     "exitTip": "您的标记内容尚未生效。确定要退出吗?",
     "exitConfirm": "确认",
-    "saveAs": "另存为",
-    "saveTip": ["提示:文档中被标记的内容将被永久删除且不可恢复。", "确定删除所有标记内容并另存文档?"],
-    "saveAsConfirm": "确认"
+    "saveTip": ["提示:文档中被标记的内容将被永久删除且不可恢复。", "确定删除所有标记内容并保存文档?"],
+    "save": "确认"
   }
 }

+ 1 - 1
packages/webview/src/apis/setActiveTool.js

@@ -22,7 +22,7 @@ export default (useViewer, useDocument) => (dataElement) => {
     if (tool.hidden) {
       tool.hidden = false;
     }
-    useDocument.setToolState(tool.element);
+    useDocument.setToolState(tool.type);
   } else {
     console.warn('DataElement not found.');
   }

+ 6 - 0
packages/webview/src/assets/main.scss

@@ -245,6 +245,11 @@ input, textarea, select {
   &.markup .strikeout {
     top: calc(50% + 1px);
   }
+  &.markup {
+    mix-blend-mode: multiply;
+    cursor: pointer;
+    pointer-events: auto;
+  }
 }
 .document.selection .annotationContainer * {
   pointer-events: none !important;
@@ -258,6 +263,7 @@ input, textarea, select {
 .annotationContainer > .outline-container svg {
   pointer-events: none;
   &.delete-button {
+    cursor: pointer;
     pointer-events: auto;
   }
   line {

+ 4 - 4
packages/webview/src/components/Dialogs/Dialog.vue

@@ -1,10 +1,10 @@
 <template>
   <div v-if="show" class="dialog">
     <div class="dialog-container">
-      <div class="close" v-if="close" @click="handleClose">
-        <CloseA v-if="close === 'A'" />
-        <CloseB v-if="close === 'B'" />
-        <Close v-else />
+      <div class="close" v-if="close">
+        <CloseA v-if="close === 'A'" @click="handleClose" />
+        <CloseB v-if="close === 'B'" @click="handleClose" />
+        <Close v-else @click="handleClose" />
       </div>
 
       <header>

+ 19 - 2
packages/webview/src/components/DocumentContainer/DocumentContainer.vue

@@ -3,7 +3,8 @@
     :class="{
       'no-select': isHandActive,
       'under': (toolMode === 'compare' && compareStatus !== 'finished') || toolMode === 'document',
-      'no-touch': ['selectText', 'highlight', 'underline', 'squiggly', 'strikeout'].includes(activeTool)
+      'no-touch': ['selectText', 'highlight', 'underline', 'squiggly', 'strikeout'].includes(activeTool),
+      'redact': ['redaction', 'remove'].includes(activeTool)
     }"
     :style="{
       'width': `calc(100% - ${leftPanelSpace}px - ${rightPanelSpace}px)`,
@@ -105,7 +106,10 @@ const rightPanelSpace = computed(() => {
 const toolMode = computed(() => useViewer.getToolMode)
 const compareStatus = computed(() => useViewer.getCompareStatus)
 const topSpace = computed(() => {
-  return isHeaderDisabled || ['view', 'document'].includes(useViewer.getToolMode) || (toolMode.value === 'compare' && compareStatus.value !== 'finished') ? 0 : 44
+  if (isHeaderDisabled || ['view', 'document'].includes(useViewer.getToolMode) || (toolMode.value === 'compare' && compareStatus.value !== 'finished')) return 0
+  if (['redaction', 'remove'].includes(activeTool.value) && window.innerWidth > 500) return 44 + 48
+  if (signatureVerify.value && toolMode.value === 'sign') return 44 + 44
+  return 44
 })
 const loading = computed(() => useViewer.getUploadLoading && useViewer.getUpload)
 const activePanelTab = computed(() => useViewer.getActiveElementTab('leftPanel'))
@@ -113,6 +117,7 @@ const load = computed(() => useViewer.getUpload)
 const isPageNavOverlayDisabled = computed(() => useViewer.getDisabledElements('pageNavOverlay'))
 const activeTool = computed(() => useDocument.getActiveTool)
 const scaleValue = computed(() => useViewer.getScale)
+const signatureVerify = computed(() => useViewer.getSignatureVerify)
 
 let originScale = 100
 let lastClickTime = 0 // 用于手动检测双击
@@ -236,6 +241,12 @@ window.instance.initOptions = async (options) => {
     useViewer.setActiveToolMode(mode)
   }
   core.addEvent('toolModeChanged', toolModeChanged)
+
+  const toolChanged = ({ tool }) => {
+    useDocument.setActiveTool(tool)
+  }
+  core.addEvent('toolChanged', toolChanged)
+
   useViewer.setVierified(res)
   useViewer.setLicense(getDocumentViewer()._license)
   const thumbnailView = document.querySelector('.thumbnail-view')
@@ -853,4 +864,10 @@ window.instances.UI.loadDocument = async (file, {
     color: transparent;
   }
 }
+
+.redact .annotationContainer > div:not(.redact),
+.redact .annotationContainer > div:not(.redact).annotation > div {
+  cursor: inherit;
+  pointer-events: none;
+}
 </style>

+ 31 - 24
packages/webview/src/components/Header/Header.vue

@@ -1,10 +1,10 @@
 <template>
-  <div class="header" :class="{ disabled: isDisabledHeader }" data-element="header">
+  <div class="header" :class="{ disabled: isDisabledHeader || ['redaction', 'remove'].includes(activeTool), 'redact-mobile-hidden': ['redaction', 'remove'].includes(activeTool) }" data-element="header">
     <HeaderItems :items="items" :rightItems="rightItems" :toolMode="toolMode" />
   </div>
   <Toolbar />
   <SignatureVerifyBar v-if="signatureVerify && toolMode === 'sign'" />
-  <SecurityApplyBar v-show="toolMode === 'security' && showRedactionApplyBar" />
+  <SecurityApplyBar v-show="['redaction', 'remove'].includes(activeTool)" />
 </template>
 
 <script setup>
@@ -20,32 +20,39 @@ const rightItems = useViewer.getActiveRightHeaderItems
 const toolMode = computed(() => useViewer.getToolMode)
 const signatureVerify = computed(() => useViewer.getSignatureVerify)
 const isDisabledHeader = computed(() => useViewer.getDisabledHeader)
-const showRedactionApplyBar = computed(() => useViewer.getShowRedactionApplyBar)
+const activeTool = computed(() => useDocument.getActiveTool)
 </script>
 
 <style lang="scss">
-  .header {
-    position: relative;
-    box-sizing: content-box;
-    display: flex;
-    align-items: center;
-    width: 100%;
-    height: 44px;
-    padding: 0 20px;
-    background-color: var(--c-header-bg);
-    border-bottom: 1px solid var(--c-header-border);
-    z-index: 4;
-    overflow-x: auto;
-    overflow-y: hidden;
+.header {
+  position: relative;
+  box-sizing: content-box;
+  display: flex;
+  align-items: center;
+  width: 100%;
+  height: 44px;
+  padding: 0 20px;
+  background-color: var(--c-header-bg);
+  border-bottom: 1px solid var(--c-header-border);
+  z-index: 4;
+  overflow-x: auto;
+  overflow-y: hidden;
 
-    &.disabled {
-      opacity: 0.5;
-      pointer-events: none;
-    }
+  &.disabled {
+    opacity: 0.5;
+    pointer-events: none;
   }
-  @media screen and (max-width: 767.9px) {
-    .header {
-      padding: 10px;
-    }
+}
+
+@media screen and (max-width: 767.9px) {
+  .header {
+    padding: 10px;
+  }
+}
+
+@media screen and (max-width: 500px) {
+  .redact-mobile-hidden {
+    display: none !important;
   }
+}
 </style>

+ 0 - 5
packages/webview/src/components/HeaderItems/HeaderItems.vue

@@ -166,7 +166,6 @@ let showToolMode = computed(()=>{
 const webviewerMode = computed(() => useViewer.getWebviewerMode)
 const activeTool = computed(() => useDocument.getActiveTool)
 const showDropdown = computed(() => popoverTool.value && popoverTool?.value[0].getMergedShow())
-const showRedactionApplyBar = computed(() => useViewer.getShowRedactionApplyBar)
 
 const changeToolMode = (mode) => {
   if (webviewerMode.value !== 'Standalone' && ['editor', 'document'].includes(mode)) {
@@ -174,10 +173,6 @@ const changeToolMode = (mode) => {
     else useViewer.openElement('docEditorPreventDialog')
     return
   }
-  if (prop.toolMode !== mode && prop.toolMode === 'security' && (activeTool.value === 'redaction' || activeTool.value === 'remove') && showRedactionApplyBar.value) {
-    useViewer.setRedactionConfirmDialog(useViewer.getShowRedactionApplyBar ? 1 : 0)
-    return
-  }
   if (mode === 'sign') {
     window.$message.info($t('signatures.tip'), {
       duration: 3000,

+ 2 - 3
packages/webview/src/components/SecurityToolbar/RedactionConfirmDialog.vue

@@ -2,7 +2,7 @@
   <div class="redaction-confirm-dialog" v-if="show">
     <Dialog :show="show" :close="closeDialog" class="confirm-dialog">
       <template #header>
-        <p>{{ dialogType === 1 ? $t('security.saveAs') : $t('security.note') }}</p>
+        <p>{{ dialogType === 1 ? $t('documentEditor.save') : $t('security.note') }}</p>
       </template>
 
       <template v-if="dialogType === 1">
@@ -13,7 +13,7 @@
 
       <template #footer>
         <div class="rect-button white" @click="closeDialog">{{ $t('cancel') }}</div>
-        <div class="rect-button blue" @click="confirm">{{ dialogType === 1 ? $t('security.saveAsConfirm') : $t('security.exitConfirm') }}</div>
+        <div class="rect-button blue" @click="confirm">{{ dialogType === 1 ? $t('security.save') : $t('security.exitConfirm') }}</div>
       </template>
     </Dialog>
   </div>
@@ -39,7 +39,6 @@ const confirm = () => {
   core.applyRedactions(dialogType.value === 1 ? 'all' : null)
   useDocument.setToolState('')
   useViewer.setActiveToolMode('view')
-  useViewer.setShowRedactionApplyBar(false)
   closeDialog()
   useViewer.closeElement('leftPanel')
 }

+ 1 - 8
packages/webview/src/components/SecurityToolbar/SecurityApplyBar.vue

@@ -17,18 +17,10 @@ import core from '@/core'
 const useViewer = useViewerStore()
 const useDocument = useDocumentStore()
 
-const showRedactionApplyBar = computed(() => useViewer.getShowRedactionApplyBar)
-
 const showDialog = (type) => {
   useViewer.setRedactionConfirmDialog(type)
 }
 
-const showBar = (value) => {
-  if (showRedactionApplyBar.value !== value) {
-    useViewer.setShowRedactionApplyBar(value)
-  }
-}
-core.addEvent('showRedactionApplyBar', showBar)
 </script>
 
 <style lang="scss">
@@ -85,6 +77,7 @@ core.addEvent('showRedactionApplyBar', showBar)
 @media screen and (max-width: 500px) {
   .security-apply-bar {
     flex-direction: column;
+    align-items: flex-end;
 
     .buttons {
       margin-left: 0;

+ 7 - 6
packages/webview/src/components/SecurityToolbar/SecurityToolbar.vue

@@ -11,7 +11,7 @@
       class="security-popover"
     >
       <template #trigger>
-        <Button :class="{ active: showSecurityPopover }">
+        <Button :class="{ active: showSecurityPopover, disabled: ['redaction', 'remove'].includes(activeTool) }">
           <span>{{ $t('header.security') }}</span>
           <Arrow class="arrow-left" />
         </Button>
@@ -33,14 +33,14 @@
       class="security-popover"
     >
       <template #trigger>
-        <Button :class="{ active: showRedactionPopover || activeTool === 'redaction' || activeTool === 'remove' }">
-          <span>{{ activeTool === 'remove' ? $t('security.remove') : $t('security.redaction') }}</span>
+        <Button :class="{ active: showRedactionPopover }">
+          <span>{{ $t('security.redaction') }}</span>
           <Arrow class="arrow-left" />
         </Button>
       </template>
       <div class="drop-down">
-        <div class="drop-item" @click="handleTool('redaction')">{{ $t('security.redaction') }}</div>
-        <div class="drop-item" @click="handleTool('remove')">{{ $t('security.remove') }}</div>
+        <div class="drop-item" @click="handleTool('redaction')" data-element="redaction">{{ $t('security.fillBlack') }}</div>
+        <div class="drop-item" @click="handleTool('remove')" data-element="remove">{{ $t('security.fillWhite') }}</div>
       </div>
     </n-popover>
 
@@ -122,7 +122,7 @@ const handleTool = (tool) => {
   if (!redactionPrompted.value) {
     firstEnterTool = tool
     showTipDialog.value = true
-  } else {
+  } else if (activeTool.value !== tool) {
     useDocument.setToolState(tool)
   }
   redactionPopover.value.setShow(false)
@@ -132,6 +132,7 @@ const closeDialog = () => {
   showTipDialog.value = false
   useDocument.setRedactionPrompted(true)
   useDocument.setToolState(firstEnterTool)
+  useViewer.setDisableHeader(true)
 }
 </script>
 

+ 1 - 1
packages/webview/src/components/SignatureToolBar/SignatureVerifyBar.vue

@@ -46,7 +46,7 @@
   display: flex;
   align-items: center;
   justify-content: space-between;
-  z-index: 2;
+  z-index: 4;
   height: 48px;
   background: #DDE9FF;
   animation: ease-out 1s;

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

@@ -1,5 +1,5 @@
 <template>
-  <div class="toolbar" v-show="isShowToolbar" :class="{ security: toolMode === 'security', editor: toolMode === 'editor', disabled: isDisabledHeader}">
+  <div class="toolbar" v-show="isShowToolbar" :class="{ security: toolMode === 'security', editor: toolMode === 'editor', disabled: isDisabledHeader, 'redact-mobile-hidden': ['redaction', 'remove'].includes(activeTool)}">
     <div v-show="isMobileDevice && isShowLeftBtn" class="scroll-container left">
       <Button class="flip-button" @click="scrollLeft"><ArrowPrev /></Button>
     </div>

+ 9 - 9
packages/webview/src/stores/modules/viewer.js

@@ -1,6 +1,5 @@
 import { defineStore } from 'pinia'
 import core from '@/core'
-import CropPageButton from '@/components/CropPageButton/CropPageButton.vue'
 
 export const useViewerStore = defineStore({
   id: 'viewer',
@@ -359,7 +358,15 @@ export const useViewerStore = defineStore({
           dataElement: 'watermarkSelect',
           element: 'watermarkSelect',
           title: 'Watermark'
-        }
+        },
+        {
+          type: 'redaction',
+          dataElement: 'redaction',
+        },
+        {
+          type: 'remove',
+          dataElement: 'remove',
+        },
       ],
       compare: [
         {
@@ -434,7 +441,6 @@ export const useViewerStore = defineStore({
     ],
     isDisabledHeader: false,
     rightPanelButtonDisabled: true,
-    showRedactionApplyBar: false,
     redactionConfirmDialog: -1, // 0: exit, 1: apply
   }),
   getters: {
@@ -561,9 +567,6 @@ export const useViewerStore = defineStore({
     getCustomFonts () {
       return this.customFonts
     },
-    getShowRedactionApplyBar () {
-      return this.showRedactionApplyBar
-    },
     getRedactionConfirmDialog () {
       return this.redactionConfirmDialog
     },
@@ -791,9 +794,6 @@ export const useViewerStore = defineStore({
         }
       }
     },
-    setShowRedactionApplyBar (val) {
-      this.showRedactionApplyBar = val
-    },
     setRedactionConfirmDialog (val) {
       this.redactionConfirmDialog = val
     },