Explorar o código

add: 添加文本编辑区域

wzl hai 1 ano
pai
achega
e419098cfb

+ 214 - 0
packages/core/src/editor/add_text.js

@@ -0,0 +1,214 @@
+import {
+  getAbsoluteCoordinate,
+  createElement,
+  getInitialPoint,
+  getSvgPathFromStroke
+} from '../annotation/utils';
+
+export default class AddText {
+  constructor ({
+    tool,
+    color,
+    container,
+    viewport,
+    page,
+    eventBus,
+    contentContainer
+  }) {
+    this._tool = tool
+    this.color = color
+    this.container = container
+    this.viewport = viewport
+    this.page = page
+    this.eventBus = eventBus
+    this.contentContainer = contentContainer
+
+    this.path = []
+    this.initStartPoint = null
+    this.initEndPoint = null
+    this.shapeEle = null
+
+    this.onMousedown = this.handleMouseDown.bind(this)
+    this.onMouseup = this.handleMouseUp.bind(this)
+    this.onMousemove = this.handleMouseMove.bind(this)
+
+    this.init()
+  }
+
+  init () {
+    this.container.addEventListener('mousedown', this.onMousedown)
+    this.container.addEventListener('touchstart', this.onMousedown)
+    this.container.style.cursor = 'crosshair'
+    this.container.style.touchAction = 'none'
+    document.removeEventListener('mousemove', this.onMousemove)
+    document.removeEventListener('mouseup', this.onMouseup)
+    document.removeEventListener('touchmove', this.onMousemove)
+    document.removeEventListener('touchend', this.onMouseup)
+  }
+
+  get tool () {
+    return this._tool
+  }
+
+  set tool (toolType) {
+    if (toolType === this._tool) return
+    if (!toolType) {
+      this.reset()
+      this.container.style.touchAction = ''
+
+      this.container.removeEventListener('mousedown', this.onMousedown)
+      this.container.removeEventListener('touchstart', this.onMousedown)
+    }
+    this._tool = toolType
+  }
+
+  reset () {
+    this.initStartPoint = null
+    this.initEndPoint = null
+    if (this.shapeEle) this.shapeEle.remove()
+    this.shapeEle = null
+    this.path = []
+    document.removeEventListener('mousemove', this.onMousemove)
+    document.removeEventListener('mouseup', this.onMouseup)
+    
+    document.removeEventListener('touchmove', this.onMousemove)
+    document.removeEventListener('touchend', this.onMouseup)
+
+    this.container.removeEventListener('mousedown', this.onMousedown)
+    this.container.removeEventListener('touchstart', this.onMousedown)
+  }
+
+  handleMouseDown (event) {
+    if (!event.target.className && !event.target.className.includes('contentContainer')) return
+    const { x, y } = getAbsoluteCoordinate(this.container, event)
+    this.initStartPoint = { x, y }
+
+    document.addEventListener('mousemove', this.onMousemove)
+    document.addEventListener('mouseup', this.onMouseup)
+    
+    document.addEventListener('touchmove', this.onMousemove)
+    document.addEventListener('touchend', this.onMouseup)
+  }
+
+  handleMouseUp () {
+    let initStartPoint = this.initStartPoint
+    let initEndPoint = this.initEndPoint
+    if (initEndPoint && initStartPoint.x !== initEndPoint.x && initStartPoint.y !== initEndPoint.y) {
+      let start = getInitialPoint(initStartPoint, this.viewport, this.viewport.scale)
+      let end = getInitialPoint(initEndPoint, this.viewport, this.viewport.scale)
+      
+      const newStart = {
+        x: Math.min(start.x, end.x),
+        y: Math.min(start.y, end.y)
+      }
+      const newEnd = {
+        x: Math.max(start.x, end.x),
+        y: Math.max(start.y, end.y)
+      }
+      
+      const rect = {
+        left: newStart.x,
+        top: newStart.y,
+        right: newEnd.x,
+        bottom: newEnd.y
+      }
+
+      const data = {
+        rect,
+        fontData: {
+          fontName: 'Helvetica',
+          fontSize: 14,
+          r: 0,
+          g: 0,
+          b: 0,
+          opacity: 1,
+          isBold: 0,
+          italic: 0,
+        },
+        alignType: 2
+      }
+      this.contentContainer.addTextEditor(data)
+    }
+    // this.reset()
+    this.initStartPoint = null
+    this.initEndPoint = null
+    if (this.shapeEle) this.shapeEle.remove()
+    this.shapeEle = null
+    this.path = []
+    document.removeEventListener('mousemove', this.onMousemove)
+    document.removeEventListener('mouseup', this.onMouseup)
+    
+    document.removeEventListener('touchmove', this.onMousemove)
+    document.removeEventListener('touchend', this.onMouseup)
+  }
+
+  handleMouseMove (event) {
+    let { x, y } = getAbsoluteCoordinate(this.container, event)
+    const { width, height } = this.viewport
+    const scale = this.viewport.scale
+    x = Math.min(width, x)
+    x = Math.max(0, x)
+    y = Math.min(height, y)
+    y = Math.max(0, y)
+    this.initEndPoint = { x, y }
+    if (this.initStartPoint.x !== this.initEndPoint.x && this.initStartPoint.y !== this.initEndPoint.y) {
+      if (!this.shapeEle) {
+        const shapeEle = createElement('div', {
+          'backgroundColor': '#DDE9FF',
+          'position': "absolute",
+          'z-index': 3
+        },
+        {
+          class: 'add-text'
+        })
+        this.shapeEle = shapeEle
+        this.container.append(this.shapeEle)
+      } else {
+        if (event.shiftKey) {
+          const { initStartPoint, initEndPoint } = this
+          const distanceX = Math.abs(initEndPoint.x - initStartPoint.x)
+          const distanceY = Math.abs(initEndPoint.y - initStartPoint.y)
+          const distance = Math.min(distanceX, distanceY)
+          if (initStartPoint.x < initEndPoint.x) {
+            initEndPoint.x = initStartPoint.x + distance
+          } else {
+            initEndPoint.x = initStartPoint.x - distance
+          }
+          if (initStartPoint.y < initEndPoint.y) {
+            initEndPoint.y = initStartPoint.y + distance
+          } else {
+            initEndPoint.y = initStartPoint.y - distance
+          }
+        }
+        const rect = this.calculate(this.initStartPoint, this.initEndPoint)
+
+        const shapeEle = this.shapeEle
+        shapeEle.style.top = `${rect.top}px`;
+        shapeEle.style.left = `${rect.left}px`;
+        shapeEle.style.width = `${Math.abs(rect.width - 1)}px`;
+        shapeEle.style.height = `${Math.abs(rect.height - 1)}px`;
+      }
+    }
+  }
+  
+  rectCalc ({ start, end }) {
+    return {
+      top: start.y < end.y ? start.y : end.y,
+      left: start.x < end.x ? start.x : end.x,
+      width: Math.abs(end.x - start.x),
+      height: Math.abs(end.y - start.y),
+    };
+  };
+
+  calculate (start, end) {
+    const initRect = this.rectCalc({ start, end });
+    const actualbdwidth = 1 * this.viewport.scale
+
+    return {
+      top: initRect.top - actualbdwidth,
+      left: initRect.left - actualbdwidth,
+      width: initRect.width + actualbdwidth * 2,
+      height: initRect.height + actualbdwidth * 2,
+    }
+  }
+}

+ 65 - 3
packages/core/src/editor/content_container.js

@@ -1,8 +1,9 @@
 import { TextEditor } from './text_editor';
-import { setCss } from '../ui_utils';
+import AddText from './add_text.js';
 
 export class ContentContainer {
   constructor(options) {
+    this.pageViewer = options.pageViewer;
     this.eventBus = options.eventBus;
     this.pageIndex = options.pageIndex;
     this.doc = options.doc;
@@ -13,7 +14,10 @@ export class ContentContainer {
 
     this.destroyed = false;
     this.frameEditorList = [];
+    this.tool = this.pageViewer.tool || ''
+    this.color = this.pageViewer.color || ''
 
+    this.onHandleTool = this.handleTool.bind(this)
   }
 
   async init () {
@@ -32,6 +36,9 @@ export class ContentContainer {
       this.contentContainer.style.display = 'block'
       return
     }
+    
+    this.eventBus._on('toolChanged', this.onHandleTool)
+
     const contentContainer = document.createElement('div')
     contentContainer.className = 'contentContainer'
     this.contentContainer = contentContainer
@@ -76,9 +83,64 @@ export class ContentContainer {
     if (this.contentContainer && this.contentContainer.style.display !== 'none') {
       this.contentContainer.style.display = 'none'
     }
-    console.log(this.frameEditorList)
+    // console.log(this.frameEditorList)
+
+    if (this.textManager) {
+      this.textManager.reset()
+    }
 
-    if (this.destroyed) return;
     this.destroyed = true;
+
+    this.eventBus._off('toolChanged', this.onHandleTool)
+  }
+
+  handleTool({
+    tool,
+    color
+  }) {
+    this.tool = tool
+    this.color = color
+
+    if (tool === 'addText') {
+      this.textManager = new AddText({
+        tool,
+        color,
+        container: this.pageDiv,
+        viewport: this.viewport,
+        scale: this.scale,
+        page: this.page,
+        eventBus: this.eventBus,
+        contentContainer: this
+      })
+    }
+    if (this.textManager) {
+      this.textManager.tool = tool
+      this.textManager.color = color
+    }
+  }
+
+  async addTextEditor (data) {
+    const { rect, fontData, alignType } = data
+    const param = {
+      pagePtr: this.pagePtr,
+      editPagePtr: this.editPagePtr,
+      rect,
+      fontData,
+      alignType
+    }
+    const editAreaPtr = await this.messageHandler.sendWithPromise('CreateNewTextArea', param)
+
+    let frameEditor = new TextEditor({
+      eventBus: this.eventBus,
+      container: this.contentContainer,
+      pagePtr: this.pagePtr,
+      editPagePtr: this.editPagePtr,
+      editAreaPtr,
+      editAreaIndex: this.frameEditorList.length + 1,
+      viewport: this.viewport,
+      scale: this.scale,
+      messageHandler: this.messageHandler
+    })
+    this.frameEditorList.push(frameEditor)
   }
 }

+ 13 - 8
packages/core/src/editor/text_editor.js

@@ -34,6 +34,7 @@ export class TextEditor {
     this.selectedRects = null
     this.selectedCharRange = null
     this.state = 0 // 0 未选中;1 已选中;2 编辑状态
+    this.mouseDown = false
 
     this.start = null
     this.end = null
@@ -129,10 +130,6 @@ export class TextEditor {
       top: this.rect.top - this.borderWidth + 'px',
       width: this.rect.width + this.borderWidth + this.pointWidth / 2 + 'px',
       height: this.rect.height + this.borderWidth + this.pointHeight / 2 + 'px',
-      // left: -(this.borderWidth / 2 + this.pointWidth / 2) + 'px',
-      // top: -(this.borderWidth / 2 + this.pointHeight / 2) + 'px',
-      // width: `calc(100% + ${this.pointWidth + this.borderWidth}px)`,
-      // height: `calc(100% + ${this.pointHeight + this.borderWidth}px)`,
       zIndex: 2
     })
 
@@ -386,19 +383,20 @@ export class TextEditor {
         x: (e.offsetX + this.rect.left) / this.scale,
         y: (e.offsetY + this.rect.top) / this.scale
       }
-      this.textarea.addEventListener('blur', this.onBlur)
-      this.textarea.addEventListener('keydown', this.onKeydown)
-      this.textarea.addEventListener('textInput', this.onTextInput)
 
       this.textContainer.addEventListener('mousemove', this.onMousemove)
     }
 
     this.selectedCharRange = null
     this.mouseMoved = false
+    this.mouseDown = true
   }
 
   async handleMouseUp (e) {
     // e.stopPropagation()
+    if (!this.mouseDown) return
+    this.mouseDown = false
+
     if (e.type === 'touchend') {
       document.body.style.overscrollBehavior = 'auto';
     }
@@ -478,6 +476,10 @@ export class TextEditor {
       if (this.selectedRects && !this.mouseMoved) {
         this.clearSelectText()
       }
+      
+      this.textarea.addEventListener('blur', this.onBlur)
+      this.textarea.addEventListener('keydown', this.onKeydown)
+      this.textarea.addEventListener('textInput', this.onTextInput)
     }
 
     this.frameContainer.classList.add('selected')
@@ -675,8 +677,10 @@ export class TextEditor {
   }
 
   async handleKeyDown (e) {
+    // e.stopPropagation()
     let keyCode = e.keyCode || e.which
 
+    console.log(keyCode)
     let isToolKey = (keyCode === 9 || keyCode === 27 || keyCode === 20 || keyCode === 16 || keyCode === 17 || keyCode === 18 || keyCode === 91 || keyCode === 93 || keyCode === 37 || keyCode === 38 || keyCode === 39 || keyCode === 40)
     if (this.selectedCharRange && !isToolKey) {
       const newChar = await this.getCharPlace('DeleteChars')
@@ -711,6 +715,7 @@ export class TextEditor {
   }
 
   async handleTextInput (e) {
+    // e.stopPropagation()
     // console.log(e.data)
     const newChar = await this.messageHandler.sendWithPromise('InsertText', {
       editAreaPtr: this.editAreaPtr,
@@ -1023,7 +1028,7 @@ export class TextEditor {
           editAreaPtr: this.editAreaPtr,
           start: this.selectedCharRange.start,
           end: this.selectedCharRange.end,
-          transparency: props.opacity / 100
+          opacity: props.opacity / 100
         })
       }
 

+ 22 - 0
packages/core/src/worker/compdfkit_worker.js

@@ -1070,6 +1070,28 @@ debugger
         end.SectionIndex,end.LineIndex,end.RunIndex,end.CharIndex
       )
     })
+
+    messageHandler.on('CreateNewTextArea', (data) => {
+      const { pagePtr, editPagePtr, rect, fontData, alignType } = data
+
+      return Module._CreateNewTextArea(
+        pagePtr,
+        editPagePtr,
+        rect.left,
+        rect.right,
+        rect.bottom,
+        rect.top,
+        stringToNewUTF8(fontData.fontName),
+        fontData.fontSize,
+        fontData.r,
+        fontData.g,
+        fontData.b,
+        fontData.opacity,
+        fontData.isBold,
+        fontData.italic,
+        alignType
+      )
+    })
   }
 
 }

+ 6 - 0
packages/webview/src/components/DocumentContainer/DocumentContainer.vue

@@ -568,6 +568,9 @@
     .frame-container {
       .text-container {
         border-style: dashed;
+        textarea {
+          cursor: default;
+        }
       }
       &.selected {
         z-index: 1;
@@ -576,6 +579,9 @@
         .text-container {
           border-style: solid;
           z-index: 1;
+          textarea {
+            cursor: text;
+          }
         }
       }
     }

+ 14 - 7
packages/webview/src/components/EditToolbar/EditToolBar.vue

@@ -1,9 +1,9 @@
 <template>
   <div class="edit-tool">  
-    <Button class="operate" :class="{ active: activeTool === 'editTextPanel' }" @click="changeActiveTool('editTextPanel')" title="Add Text">
+    <Button class="operate" :class="{ active: activeTool === 'addText' }" @click="changeActiveTool('addText')" title="Add Text">
       <AddText /> Add Text
     </Button>
-    <Button class="operate" :class="{ active: activeTool === 'editImagePanel' }" @click="changeActiveTool('editImagePanel')" title="Add Image">
+    <Button class="operate" :class="{ active: activeTool === 'addImage' }" @click="changeActiveTool('addImage')" title="Add Image">
       <AddImage />Add Image
     </Button>
     <div class="divider pc"></div>
@@ -20,6 +20,8 @@
   import { computed, ref } from 'vue'
   import { useViewerStore } from '@/stores/modules/viewer'
   import { useDocumentStore } from '@/stores/modules/document'
+  import core from '@/core'
+  const { switchTool, switchAnnotationEditorMode } = core
 
   const useDocument = useDocumentStore()
   const useViewer = useViewerStore()
@@ -28,12 +30,17 @@
 
   const changeActiveTool = (tool) => {
     useDocument.setToolState(tool)
+
+    useViewer.toggleActiveHand(false)
+    switchTool(0)
     
-    if (tool === 'editTextPanel') {
-      useViewer.toggleElement('editTextPanel')
-    } else {
-      useViewer.closeElement('editTextPanel')
-    }
+    switchAnnotationEditorMode(0)
+    
+    // if (tool === 'addText') {
+    //   useViewer.toggleElement('editTextPanel')
+    // } else {
+    //   useViewer.closeElement('editTextPanel')
+    // }
   }
 </script>