Implementando Suporte a Colagem de Documentos Word no Editor WANGEDITOR

Na indústria automotiva, a necessidade de gerenciar documentação técnica em formatos do Word é comum. Muitas vezes, engenheiros e técnicos precisam colar conteúdo diretamente de documentos do Word em editores online, mantendo a formatação e tratando as imagens adequadamente.

Desafio Técnico

O editor WANGEDITOR, embora poderoso, não oferece suporte nativo para colar conteúdo do Word com todas as imagens automaticamente enviadas para o servidor. Este desafio se torna particularmente crítico em ambientes onde a docuemntação técnica contém numerosas imagens e diagramas.

Solução Proposta

A solução envolve a criação de um plugin personalizado que:

  • Intercepta o evento de colagem
  • Processa o HTML do Word
  • Extrai todas as imagens embutidas
  • Envia as imagens para o servidor
  • Substitui os URLs base64 pelos URLs das imagens no servidor

Implementação

Configuração Básica do Editor

// config/editor.js
import { Editor } from 'wangeditor'
import { Boot } from '@wangeditor/core'

// Registro do módulo de processamento de colagem
Boot.registerModule({
  name: 'wordPasteHandler',
  editor: Editor,
  config: (editor) => {
    editor.on('paste', async (event) => {
      const { type, data } = event
      
      if (type === 'text/html') {
        // Processar HTML do Word
        const processedContent = await processWordHTML(data)
        event.preventDefault()
        
        // Inserir conteúdo processado
        editor.insertHtml(processedContent)
      }
    })
  }
})

async function processWordHTML(html) {
  // Extrair e processar imagens
  const imageProcessor = new ImageProcessor()
  const { processedHTML, imageUrls } = await imageProcessor.extractAndUploadImages(html)
  
  return processedHTML
}

Processamento de Imagens

// utils/imageProcessor.js
export class ImageProcessor {
  constructor() {
    this.uploadEndpoint = '/api/upload-images'
    this.imageContainer = []
  }
  
  async extractAndUploadImages(html) {
    // Usar DOMParser para analisar HTML
    const parser = new DOMParser()
    const doc = parser.parseFromString(html, 'text/html')
    
    // Encontrar todas as imagens
    const images = Array.from(doc.querySelectorAll('img'))
    const processedImages = []
    
    for (const img of images) {
      const src = img.getAttribute('src')
      
      if (src.startsWith('data:image')) {
        // Converter base64 para blob
        const blob = this.base64ToBlob(src)
        
        // Enviar para servidor
        const serverUrl = await this.uploadImage(blob)
        
        // Substituir URL
        img.setAttribute('src', serverUrl)
        processedImages.push(img.outerHTML)
      } else {
        processedImages.push(img.outerHTML)
      }
    }
    
    // Substituir imagens no HTML
    const processedHTML = html.replace(/<img></img>]+>/g, () => {
      return processedImages.shift()
    })
    
    return {
      processedHTML,
      imageUrls: processedImages.map(img => img.match(/src="([^"]+)"/)[1])
    }
  }
  
  base64ToBlob(base64) {
    const arr = base64.split(',')
    const mime = arr[0].match(/:(.*?);/)[1]
    const bstr = atob(arr[1])
    let n = bstr.length
    const u8arr = new Uint8Array(n)
    
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n)
    }
    
    return new Blob([u8arr], { type: mime })
  }
  
  async uploadImage(blob) {
    const formData = new FormData()
    formData.append('image', blob, `image_${Date.now()}.${this.getFileExtension(blob)}`)
    
    const response = await fetch(this.uploadEndpoint, {
      method: 'POST',
      body: formData
    })
    
    const result = await response.json()
    return result.url
  }
  
  getFileExtension(blob) {
    const mime = blob.type
    switch (mime) {
      case 'image/jpeg': return 'jpg'
      case 'image/png': return 'png'
      case 'image/gif': return 'gif'
      case 'image/webp': return 'webp'
      default: return 'png'
    }
  }
}

Backend para Upload de Imagens

// routes/upload.js
const express = require('express')
const multer = require('multer')
const path = require('path')
const router = express.Router()

// Configuração do multer para armazenamento
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, 'public/uploads/')
  },
  filename: (req, file, cb) => {
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9)
    cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname))
  }
})

const upload = multer({ 
  storage: storage,
  limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
  fileFilter: (req, file, cb) => {
    const allowedTypes = /jpeg|jpg|png|gif|webp/
    const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase())
    const mimetype = allowedTypes.test(file.mimetype)
    
    if (mimetype && extname) {
      return cb(null, true)
    } else {
      cb(new Error('Apenas imagens são permitidas!'))
    }
  }
})

// Rota de upload
router.post('/upload-images', upload.single('image'), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ error: 'Nenhum arquivo foi enviado' })
  }
  
  const imageUrl = `/uploads/${req.file.filename}`
  res.json({ 
    success: true, 
    url: imageUrl,
    filename: req.file.filename
  })
})

// Tratamento de erros
router.use((err, req, res, next) => {
  if (err instanceof multer.MulterError) {
    if (err.code === 'LIMIT_FILE_SIZE') {
      return res.status(400).json({ error: 'O tamanho do arquivo excede o limite de 5MB' })
    }
  } else if (err) {
    return res.status(400).json({ error: err.message })
  }
  
  next()
})

module.exports = router

Integração com Vue.js

// components/WordEditor.vue
<template>
  <div class="word-editor-container">
    <div ref="editorElem" style="height: 500px;"></div>
  </div>
</template>

<script>
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { Boot } from '@wangeditor/core'
import { ImageProcessor } from '@/utils/imageProcessor'

export default {
  components: { Editor, Toolbar },
  data() {
    return {
      editor: null,
      editorConfig: {
        placeholder: 'Cole conteúdo do Word aqui...',
        MENU_CONF: {
          uploadImage: {
            server: '/api/upload-images',
            fieldName: 'image',
            maxFileSize: 5 * 1024 * 1024,
            allowedFileTypes: ['image/*'],
            customInsert: (res, insertFn) => {
              insertFn(res.url)
            }
          }
        }
      },
      toolbarConfig: {
        excludeKeys: [
          'uploadVideo'
        ]
      }
    }
  },
  mounted() {
    this.initEditor()
  },
  beforeDestroy() {
    const editor = this.editor
    if (editor == null) return
    editor.destroy()
    this.editor = null
  },
  methods: {
    initEditor() {
      // Criar instância do editor
      this.editor = new Editor({
        selector: this.$refs.editorElem,
        config: this.editorConfig,
        toolbarConfig: this.toolbarConfig
      })
      
      // Registrar módulo de processamento de colagem
      Boot.registerModule({
        name: 'wordPasteHandler',
        editor: this.editor.constructor,
        config: (editor) => {
          editor.on('paste', async (event) => {
            const { type, data } = event
            
            if (type === 'text/html') {
              // Processar HTML do Word
              const imageProcessor = new ImageProcessor()
              const { processedHTML } = await imageProcessor.extractAndUploadImages(data)
              
              event.preventDefault()
              
              // Inserir conteúdo processado
              editor.insertHtml(processedHTML)
            }
          })
        }
      })
      
      // Criar editor
      this.editor.create()
    }
  }
}
</script>

<style>
.word-editor-container {
  border: 1px solid #ccc;
  z-index: 100;
  position: relative;
}
</style>

Considerações de Desempenho

Para documentos do Word com muitas imagens, é importante cosniderar:

  • Implementar compressão de imagens no frontend antes do upload
  • Usar upload em chunks para arquivos grandes
  • Implementar uma fila de processamento para lidar com múltiplos uploads simultâneos
  • Adicionar indicadores de progresso para melher experiência do usuário

Extensões para Formatos Adicionais

Para ambientes da indústria automotiva que lidam com diversos formatos de documentos, é possível estender a solução para:

  • PDFs: converter páginas em imagens
  • Arquivos Excel: extrair gráficos como imagens
  • Apresentações PowerPoint: converter slides em imagens

Segurança

É fundamental implementar medidas de segurança:

  • Validação rigorosa de todos os uploads
  • Escaneamento de malware em arquivos enviados
  • Limitação de tipos de arquivos permitidos
  • Implementação de rate limiting para evitar abusos

Conclusão

A implementação de suporte à colagem de documentos do Word no WANGEDITOR, com processamento automático de imagens, oferece uma solução robusta para ambientes que gerenciam documentação técnica. A abordagem modular permite extensibilidade para outros formatos e garantida segurança contra ameaças comuns.

Tags: wangeditor word-processing automotive-industry document-management image-upload

Publicado em 7-3 17:44