importScripts("./ComPDFKit.js")
// importScripts("./pdfium.js")

import { parseAdobePDFTimestamp, convertToPDFTimestamp, roundToDecimalPlaces, convertColorToCppFormat, convertCppRGBToHex, convertColorToRGB } from '../ui_utils';
import { AnnotationType, WidgetType, LineTypeString, StampType, StampTypeString, TextStampShapeString, ActionTypeString, WidgetTypeString, AnnotationFlags, BorderStyleInt, BorderStyleString, ALIGN, ALIGNMAP, AnnotationState, AnnotationStateString } from '../../constants'

let ComPDFKitJS = {
  opened_Font: [],
  opened_DefFont: [],
  ComPDFKitDownloadFont,
  ComPDFKitDownloadFontA,
  FontNameList: [],
  FontCount: 1,
  opened_image: [],
  opened_files: [],
  opened_cert: [],
}

let DataArray = []
let LineType = {}
let BorderStyle = {}
let PageSize = {}
let Rect = {}
let RGBColor = {}
let FontDa = {}
let PDFDestination = {}
let WidgetItem = {}
let WidgetItemsArray = {}
let password = ''
let TextFindItemArray = []
let EditCharPlace = {}
let EndEditCharPlace = {}
let RectArray = []
let CursorPoints = {}
let EditTextStyle = {}
let U8StringData = ''
let PDFRange = {}
let TextRectArray = []
let ImageAreaInfo = {}
let SignerArray = []
let CertArray = []
let AccessInfoArray = []
let PolicyArray = []
let CRLPointArray = []
let OCSPUrlArray = []
let DiffInfoArray = []
let AnnotArray = []
let fontsJson = null
let fontFile = null
let webFontURL = 'https://www.compdf.com/fonts/'

import MessageHandler from "../message_handler"
import { convertFileToBuffer, convertBase64ToBytes } from '../fileHandler';

async function sleep(delay) {
  await new Promise((resolve) => setTimeout(resolve, delay))
}

function readFileAsync(url) {
  const xhr = new XMLHttpRequest;
  xhr.open('GET', url, false)
  xhr.responseType = 'arraybuffer'
  try {
    xhr.send()
  } catch (err) {
    return 0
  }
  if (xhr.status == 200 || xhr.status == 206) {
    const responseArray = new Uint8Array(xhr.response)
    return responseArray
  }
  return 0
}

function ComPDFKitDownloadFont(index){
  if (ComPDFKitJS.opened_Font[index]) {
    return ComPDFKitJS.opened_Font[index]
  }
  return fontFile
}
function ComPDFKitDownloadFontA(index){
  if (ComPDFKitJS.opened_Font[index]) {
    return ComPDFKitJS.opened_Font[index]
  }
  const font = fontsJson[index - 1]
  fontFile = readFileAsync(webFontURL + font.filePath)
  CPDFWorker.MessageHandler.send('LoadFont', { fontFamily: font.family })
  ComPDFKitJS.opened_Font[index] = fontFile
  return fontFile
}

function hexToRGB(hexColor) {
  const color = parseInt(hexColor.slice(1), 16);

  const red = (color >> 16) & 0xFF;
  const green = (color >> 8) & 0xFF;
  const blue = color & 0xFF;
  return {
    R: roundToDecimalPlaces(red / 255),
    G: roundToDecimalPlaces(green / 255),
    B: roundToDecimalPlaces(blue / 255)
  }
}

class CPDFWorker {
  static async setUp() {
    const messageHandler = new MessageHandler('worker', 'main', self)
    this.MessageHandler = messageHandler

    let testMessageProcessed = false

    messageHandler.on("test", function (data) {
      if (testMessageProcessed) {
        return; // we already processed 'test' message once
      }
      testMessageProcessed = true;

      // Ensure that `TypedArray`s can be sent to the worker.
      messageHandler.send("test", data instanceof Uint8Array);
    });

    messageHandler.send("ready", null)

    messageHandler.on("Ready", function (data) {
      return CPDFWorker.createDocumentHandler(self)
    })
  }

  static createDocumentHandler() {
    const messageHandler = this.MessageHandler

    messageHandler.on('InitConfig', (data) => {
      const { license, appId } = data
      let licensePtr = stringToNewUTF8(license)
      const devicePtr = stringToNewUTF8('')
      let appIdPtr = stringToNewUTF8(appId)
      Module._InitComPDFkit(licensePtr, false, devicePtr, appIdPtr, 0)
      const result = licenseErrorMessage()
      return result
    })

    messageHandler.on('LoadFile', async (data) => {
      ComPDFKitJS.opened_files = []
      ComPDFKitJS.opened_files[0] = data.buffer
      if (data.pagesPtr && data.pagesPtr.length > 0) {
        data.pagesPtr.forEach(item => {
          Module._ClearPage(item.pagePtr)
        })
      }

      if (!data.doc) {
        Module._ClearDocument(data.doc)
      }

      if (data.fileList) {
        ComPDFKitJS.opened_Font[0] = data.fileList
      }

      if (data.webFontURL !== 'https://www.compdf.com/fonts/') {
        webFontURL = data.webFontURL
      }

      if (data.fontsJson) {
        fontsJson = data.fontsJson

        const compress = !!ComPDFKitJS.opened_Font[0]
        ComPDFKitJS.FontCount = (compress ? (data.fontsJson.length + 1) : data.fontsJson.length)
        if (!compress && data.fontsJson.length === 1) {
          const fontFile = readFileAsync(webFontURL + data.fontsJson[0].filePath)
          ComPDFKitJS.opened_Font[0] = fontFile
        }
        Module._InitComPDFkitFont(compress)
      }

      if (data.enableDefaultFont && !ComPDFKitJS.opened_DefFont[0]) {
        const fontFile = readFileAsync('./DroidSansFallbackFull.ttf')
        ComPDFKitJS.opened_DefFont[0] = fontFile
      }
      const doc = Module._InitDocument()

      return doc
    })

    messageHandler.on('LoadDocumentByStream', loadDocumentByStream)

    messageHandler.on('GetPageCount', (data) => {
      const { doc } = data
      return Module._GetPageCount(doc)
    })

    messageHandler.on('GetPageSize', (data) => {
      const { doc, pageIndex } = data

      PageSize.Width = 0
      PageSize.Height = 0

      Module._GetPageSizeForIndex(doc, pageIndex)
      const pagePtr = Module._GetPage(doc, pageIndex)

      return {
        pagePtr,
        width: PageSize.Width,
        height: PageSize.Height
      }
    })

    messageHandler.on('InitAnnot', (data) => {
      const { pagePtr } = data
      const annotCount = Module._GetAnnotCount(pagePtr)
      const annotations = []
      let replyCount = 0

      for (let i = 0; i < annotCount; i++) {
        const annotPtr = Module._InitAnnot(pagePtr, i)
        if (!annotPtr) continue
        const isReply = Module._IsReplyAnnot(annotPtr)

        if (isReply) {
          replyCount++
          continue
        }

        const type = Module._GetAnnotType(annotPtr)
        const annotation = {
          annotPtr,
          type,
          pagePtr
        }
        annotations.push(annotation)
      }

      return {
        annotCount: annotations.length,
        annotations
      }
    })

    messageHandler.on('GetAnnotation', (data) => {
      const { doc, pagePtr, annotPtr, typeInt, scale } = data

      return getAnnotation({
        doc,
        pagePtr,
        annotPtr,
        typeInt,
        scale
      })
    })

    messageHandler.on('GetRenderAnnot', (data) => {
      const { annotPtr, width, height } = data
      let pixelNum = width * height

      let imageBytes = pixelNum * 4
      let imageptr = _malloc(imageBytes)
      for (var i = 0; i < pixelNum; i++) {
        Module.HEAP32[imageptr / 4 + i] = 0
      }

      Module._RenderAnnot(annotPtr, width, height, imageptr, 0)
      let imageArray = new Uint8Array(imageBytes)
      for (var i = 0; i < imageBytes; i += 4) {
        //bgra 转 rgba
        imageArray[i] = Module.HEAPU8[imageptr + i + 2]
        imageArray[i + 1] = Module.HEAPU8[imageptr + i + 1]
        imageArray[i + 2] = Module.HEAPU8[imageptr + i]
        imageArray[i + 3] = Module.HEAPU8[imageptr + i + 3]
      }
      _free(imageptr)
      return imageArray
    })

    messageHandler.on('GetWidgetAnnotation', (data) => {
      const { doc, pagePtr, annotPtr, typeInt, signaturePtrList } = data

      return getWidgetAnnotation({
        doc,
        pagePtr,
        annotPtr,
        typeInt,
        signaturePtrList
      })
    })

    messageHandler.on('GetSignatures', (data) => {
      return getSignatures(data)
    })

    messageHandler.on('UpdateDigitalSignAp', async (data) => {
      const exImageBase64 = 'data:image/png;base64, '
      const exImageData = convertBase64ToBytes(exImageBase64)
      ComPDFKitJS.opened_image = []
      ComPDFKitJS.opened_image[0] = exImageData
      updateDigitalSignAp(data)

      const { pkcs12Buffer, password, annotPtr, doc } = data
      ComPDFKitJS.opened_cert[0] = pkcs12Buffer
      const pass = stringToNewUTF8(password)
      const certPtr = Module._GetPKCS12Cert(pass, 0, pkcs12Buffer.length)

      DataArray = []
      DataArray.push(ComPDFKitJS.opened_files[0])

      const signaturePtr = Module._InitSignature2(annotPtr)

      const addResult = Module._AddSignSaveDocument(doc, signaturePtr, certPtr)

      let pdfData = []
      for (let i = 0; i < DataArray.length; i++) {
        let subArray = new Uint8Array(DataArray[i])
        pdfData.push(subArray)
      }
      const blobData = new Blob(pdfData, { type: "application/pdf" })
      const buffer = await convertFileToBuffer(blobData)
      const length = ComPDFKitJS.opened_files.length
      ComPDFKitJS.opened_files[length] = buffer

      DataArray = []
      const modifyResult = Module._AddSignModifyDocument(signaturePtr, certPtr, length, buffer.length)

      pdfData = []
      for (let i = 0; i < DataArray.length; i++) {
        let subArray = new Uint8Array(DataArray[i])
        pdfData.push(subArray)
      }
      ComPDFKitJS.opened_files[0] = pdfData
      ComPDFKitJS.opened_files.splice(length, 1)
      const pdfBlob = new Blob(pdfData, { type: "application/pdf" })
      return pdfBlob
    })

    messageHandler.on('CreateSignatureAP', async (data) => {
      const rect = data.rect

      const newDoc = Module._InitDocument()
      Module._CreateDocument(newDoc)

      Module._InsertPage(newDoc, 0, 595, 842)
      const count = Module._GetPageCount(newDoc)

      const pagePtr = Module._GetPage(newDoc, 0)
      const annotPtr = Module._CreateWidget(newDoc, pagePtr, 6)
      setAnnotCreationDate({
        annotPtr,
        date: new Date()
      })

      setAnnotRect({
        pagePtr,
        annotPtr,
        rect
      })

      Module._UpdateFormAp(annotPtr)

      data.annotPtr = annotPtr
      updateDigitalSignAp(data)

      const { width, height } = rect
      let pixelNum = width * height

      let imageBytes = pixelNum * 4
      let imageptr = _malloc(imageBytes)
      for (var i = 0; i < pixelNum; i++) {
        Module.HEAP32[imageptr / 4 + i] = 0
      }

      Module._RenderAnnot(annotPtr, width, height, imageptr, 0)
      let imageArray = new Uint8Array(imageBytes)
      for (var i = 0; i < imageBytes; i += 4) {
        //bgra 转 rgba
        imageArray[i] = Module.HEAPU8[imageptr + i + 2]
        imageArray[i + 1] = Module.HEAPU8[imageptr + i + 1]
        imageArray[i + 2] = Module.HEAPU8[imageptr + i]
        imageArray[i + 3] = Module.HEAPU8[imageptr + i + 3]
      }
      _free(imageptr)
      return imageArray
    })

    messageHandler.on('UpdateSignatureAP', async (data) => {
      const { annotPtr, rect, pagePtr, imageBase64, imageArray } = data

      setAnnotRect({
        pagePtr,
        annotPtr,
        rect
      })

      const imageData = imageArray || (imageBase64 && convertBase64ToBytes(imageBase64))

      ComPDFKitJS.opened_image = []
      ComPDFKitJS.opened_image[0] = imageData

      Module._RemoveAP(annotPtr)
      Module._SetImageStampByStream(annotPtr, 0, imageData.length, 0)
      Module._SignWithName(annotPtr)
    })

    messageHandler.on('TestAddSignature', async (data) => {
      const doc = data.doc
      const buf = await convertFileToBuffer(data.certFile)
      ComPDFKitJS.opened_cert[0] = buf
      const pass = stringToNewUTF8('ComPDFKit')
      const certPtr = Module._GetX509ByPKCS12Cert(0, buf.length, pass)

      const signaturePtr = Module._InitSignature2(annotPtr)

      DataArray = []

      const embedded = !!fontsJson || !!ComPDFKitJS.opened_DefFont[0]
      Module._SaveDocumentByStream(doc, 2, embedded)

      const addResult = Module._AddSignSaveDocument(doc, signaturePtr, certPtr)

      let pdfData = []
      for (let i = 0; i < DataArray.length; i++) {
        let subArray = new Uint8Array(DataArray[i])
        pdfData.push(subArray)
      }
      const blobData = new Blob(pdfData, { type: "application/pdf" })
      const buffer = await convertFileToBuffer(blobData)
      const length = ComPDFKitJS.opened_files.length
      ComPDFKitJS.opened_files[length] = buffer
      const newDoc = Module._InitDocument()
      const passwordPtr = stringToNewUTF8('')
      Module._LoadDocumentByStream(newDoc, length, buffer.length, passwordPtr)
    })

    messageHandler.on('LoadCertificates', async (data) => {
      const { pkcs12Buffer, password } = data
      ComPDFKitJS.opened_cert[0] = pkcs12Buffer
      const pass = stringToNewUTF8(password)

      const certPtr = Module._GetX509ByPKCS12Cert(0,  pkcs12Buffer.length, pass)
      if (certPtr === 0) return false
      const certData = await getSignCertData(certPtr)

      return certData
    })

    messageHandler.on('AddSignature', async (data) => {
      const doc = data.doc
      const pagePtr = data.pagePtr.pagePtr
      const buf = await convertFileToBuffer(data.certFile)

      ComPDFKitJS.opened_cert[0] = buf
      const pass = stringToNewUTF8('ComPDFKit')
      const certPtr = Module._GetPKCS12Cert(pass, 0, buf.length)
      const rect = {
        left: 50,
        top: 442,
        right: 100,
        bottom: 642
      }
      DataArray = []
      DataArray.push(ComPDFKitJS.opened_files[0])

      const annotPtr = Module._CreateWidget(doc, pagePtr, 6)
      setAnnotCreationDate({
        annotPtr,
        date: new Date()
      })
      setWidgetFieldName({
        annotPtr,
        fieldName: 'signatureField1'
      })

      setAnnotRect({
        pagePtr,
        annotPtr,
        rect
      })

      const signaturePtr = Module._InitSignature2(annotPtr)

      const addResult = Module._AddSignSaveDocument(doc, signaturePtr, certPtr)

      let pdfData = []
      for (let i = 0; i < DataArray.length; i++) {
        let subArray = new Uint8Array(DataArray[i])
        pdfData.push(subArray)
      }
      const blobData = new Blob(pdfData, { type: "application/pdf" })
      const buffer = await convertFileToBuffer(blobData)
      const length = ComPDFKitJS.opened_files.length
      ComPDFKitJS.opened_files[length] = buffer

      DataArray = []
      const modifyResult = Module._AddSignModifyDocument(signaturePtr, certPtr, 1, ComPDFKitJS.opened_files[1].length)

      pdfData = []
      for (let i = 0; i < DataArray.length; i++) {
        let subArray = new Uint8Array(DataArray[i])
        pdfData.push(subArray)
      }
      const blobData2 = new Blob(pdfData, { type: "application/pdf" })
      return blobData2
    })

    messageHandler.on('PushRenderTask', (data) => {
      const { pagePtr, scale, left, right, bottom, top, mode = -1, flag = 0, form = 0 } = data

      let param = {
        pagePtr,
        scale,
        left,
        right,
        bottom,
        top,
        mode,
        flag,
        form
      }
      let result = RenderPageBitmapWithMatrix(param)
      return result
    })

    messageHandler.on('RenderPageBitmap', (data) => {
      const { pagePtr, x, y, width, height } = data

      let param = {
        pagePtr,
        x,
        y,
        width,
        height,
        mode: -1,
        flag: 1,
        form: 1
      }
      const result = RenderPageBitmap(param)

      return result
    })

    messageHandler.on('EditAnnotation', (data) => {
      const { annotation } = data
      if (annotation.rect) {
        setAnnotRect(annotation)
      }
      if (annotation.color) {
        setAnnotRGBColor(annotation)
      }
      if (annotation.opacity) {
        setAnnotTransparency(annotation)
      }
      if (annotation.contents && annotation.operate === 'mod-form') {
        const result = setTextFieldContent(annotation)
        if (result) {
          return result
        }
      }
      if (annotation.contents && annotation.operate === 'mod-annot') {
        setAnnotContent(annotation)
      }
      if (annotation.linePoints) {
        setLinePoint(annotation)
      }
      if (annotation.inkPointes) {
        setInkPointes(annotation)
      }
      if (annotation.destPage && annotation.operate === 'mod-annot') {
        setLinkDest(annotation)
      }
      if (annotation.destPage && annotation.operate === 'mod-form') {
        setPushButtonDest(annotation)
      }
      if (annotation.url && annotation.operate === 'mod-annot') {
        setLinkUri(annotation)
      }
      if (annotation.url && annotation.operate === 'mod-form') {
        setPushButtonUri(annotation)
      }
      if (annotation.isHidden || annotation.isHidden === 0) {
        setAnnotHidden(annotation)
      }
      if (annotation.isChecked || annotation.isChecked === 0) {
        if (annotation.type === 'checkbox') {
          const result = setCheckBoxIsChecked(annotation)
          if (result) {
            return result
          }
        } else if (annotation.type === 'radiobutton') {
          const result = setRadioButtonIsChecked(annotation)
          if (result) {
            return result
          }
        }
      }
      if (annotation.checkStyle) {
        setCheckStyle(annotation)
      }
      if (annotation.backgroundColor && annotation.operate === 'mod-form') {
        setWidgetBackgroundColor(annotation)
      }
      if (annotation.title && annotation.operate === 'mod-form') {
        const { annotPtr, title: rawTitle } = annotation
        const title = stringToNewUTF8(rawTitle)
        Module._SetButtonTitle(annotPtr, title)
      }
      if (annotation.fieldName) {
        setWidgetFieldName(annotation)
      }
      if (annotation.fontName || annotation.fontSize || annotation.color) {
        setAnnotFontData(annotation)
      }
      if (annotation.item) {
        addWidgetItem(annotation)
      }
      if (annotation.moveOption) {
        setWidgetItems(annotation)
      }
      if (annotation.selected || annotation.selected === 0) {
        const result = setAnnotSelected(annotation)
        if (result) {
          return result
        }
      }
      if (annotation.hasOwnProperty('deleteItemIndex')) {
        deleteWidgetItem(annotation)
      }
      if (annotation.textAlignment) {
        const { annotPtr, textAlignment } = annotation
        const textAlignmentInt = ALIGNMAP[textAlignment.toUpperCase()]
        Module._SetWidgetTextJustification(annotPtr, textAlignmentInt)
      }
      if (annotation.hasOwnProperty('isMultiLine')) {
        const { annotPtr, isMultiLine } = annotation
        Module._SetMultiLine(annotPtr, isMultiLine)
      }
      if (annotation.author) {
        setAnnotAuthor(annotation)
      }

      if (annotation.operate === 'mod-form' && annotation.type !== 'signatureFields') {
        Module._UpdateFormAp(annotation.annotPtr)
      } else {
        Module._UpdateAnnotAp(annotation.annotPtr, 0)
      }
    })

    messageHandler.on('FlattenPage', (data) => {
      const { doc, saveType, password, oldPassword } = data
      const result = flattenPdfDownload({
        doc,
        saveType,
        password,
        oldPassword
      })
      return result
    })

    messageHandler.on('CreateAnnotation', (data) => {
      const { doc, pagePtr, annotation } = data
      if (annotation.operate === 'add-form') {
        return createWidget(doc, pagePtr, annotation)
      }
      return createAnnotation(doc, pagePtr, annotation)
    })

    messageHandler.on('GetAnnotRect', (data) => {
      const { pagePtr, annotPtr } = data

      Rect = {}

      Module._GetAnnotRect(pagePtr, annotPtr)

      return {
        left: Rect.Left,
        top: Rect.Top,
        right: Rect.Right,
        bottom: Rect.Bottom
      }
    })

    messageHandler.on('SetAnnotRect', (data) => {
      const { pagePtr, annotPtr, rect } = data

      Module._SetAnnotRect(pagePtr, annotPtr, rect.left, rect.right, rect.bottom, rect.top)
    })

    messageHandler.on('RemoveAnnot', (data) => {
      const { annotPtr } = data
      Module._RemoveAnnot(annotPtr)
      const result = licenseErrorMessage('PDF Annotation')
      if (result.code) {
        return result
      }
    })

    messageHandler.on('RemoveFromPageIncludeReplyAnnot', (data) => {
      const { pagePtr, annotPtr } = data
      const res = Module._RemoveFromPageIncludeReplyAnnot(pagePtr, annotPtr)
      const result = licenseErrorMessage('PDF Annotation')
      if (result.code) {
        return result
      }
    })

    messageHandler.on('GetCharIndexAtPos', (data) => {
      const { pagePtr, textPtr, point } = data
      const { x, y } = point
      const index = Module._GetCharIndexAtPos(pagePtr, textPtr, x, y, 5, 5)
      return index !== -1
    })

    messageHandler.on('GetCharsRangeAtPos', (data) => {
      const { pagePtr, textPtr, start, end } = data
      PDFRange = {}
      Module._GetCharsRangeAtPos(pagePtr, textPtr, start.x, start.y, end.x, end.y, 5, 5)
      const rawTextContent = Module._GetTextContent(textPtr, PDFRange.Location, PDFRange.Length)
      const textContent = UTF8ToString(rawTextContent)
      TextRectArray = []
      Module._GetRects(pagePtr, textPtr, PDFRange.Location, PDFRange.Length)
      return {
        textContent,
        textRects: TextRectArray
      }
    })

    messageHandler.on('GetSelectionForWordAtPos', (data) => {
      const { pagePtr, textPtr, start, end } = data
      Rect = {}
      PDFRange = {}
      const rawTextContent = Module._GetSelectionForWordAtPos(pagePtr, textPtr, start.x, start.y, 5, 5)

      const textContent = UTF8ToString(rawTextContent)
      TextRectArray = []
      Module._GetRects(pagePtr, textPtr, PDFRange.Location, PDFRange.Length)
      return {
        textContent,
        textRects: TextRectArray
      }
    })

    messageHandler.on('XFDFExportAnnotations', (data) => {
      const { doc } = data
      const result = exportXFDFByStream(doc)
      return result
    })

    messageHandler.on('XFDFImportAnnotations', (data) => {
      const { doc, xfdfBuffer } = data
      let xfdfBufferPtr = _malloc(xfdfBuffer.length)
      Module.HEAPU8.set(xfdfBuffer, xfdfBufferPtr)
      Module._XFDFImportAnnotaitons(doc, xfdfBufferPtr, xfdfBuffer.length)
      _free(xfdfBufferPtr)

      const result = licenseErrorMessage('PDF Annotation XFDF')
      if (result.code) {
        return result
      }
    })

    messageHandler.on('SaveDocumentByStream', (data) => {
      return saveDocumentByStream(data)
    })

    messageHandler.on('SetPassword', (data) => {
      const { doc, password: rawPassword } = data
      const password = stringToNewUTF8(rawPassword)
      const res = Module._SetPassword(doc, password, '', '', '', '', '', '')
    })

    messageHandler.on('CompareDocument', async ({ data }) => {
      const { leftFile, rightFile, leftFilePassword: rawLeftFilePassword, rightFilePassword: rawRightFilePassword } = data
      const oldBuffer = await convertFileToBuffer(leftFile)
      const oldDocPtr = Module._InitDocument()
      ComPDFKitJS.opened_files[1] = oldBuffer
      const leftFilePassword = stringToNewUTF8(rawLeftFilePassword)
      Module._LoadDocumentByStream(oldDocPtr, 1, oldBuffer.length, leftFilePassword)
      const oldPagesCount = Module._GetPageCount(oldDocPtr)

      const newBuffer = await convertFileToBuffer(rightFile)
      const newDocPtr = Module._InitDocument()
      ComPDFKitJS.opened_files[2] = newBuffer
      const rightFilePassword = stringToNewUTF8(rawRightFilePassword)
      Module._LoadDocumentByStream(newDocPtr, 2, newBuffer.length, rightFilePassword)
      const newPagesCount = Module._GetPageCount(newDocPtr)

      const minPageCount = Math.min(oldPagesCount, newPagesCount)

      // type 1: 内容对比 2: 覆盖对比
      if (data.type === 1) {
        const { replaceColor: rawReplaceColor, insertColor: rawInsertColor, deleteColor: rawDeleteColor, textCompare, imgCompare } = data

        const comparePtr = Module._InitCompare(oldDocPtr, newDocPtr)

        // 对比类型 0: text 1: img 2: text & img
        let compareType = 0
        if (textCompare && imgCompare) {
          compareType = 2
        } else if (imgCompare) {
          compareType = 1
        }

        // 对比的颜色
        const replaceColor = hexToRGB(rawReplaceColor)
        const insertColor = hexToRGB(rawInsertColor)
        const deleteColor = hexToRGB(rawDeleteColor)
        Module._SetReplaceResColor(comparePtr, replaceColor.R * 255, replaceColor.G * 255, replaceColor.B * 255)
        Module._SetInsertResColor(comparePtr, insertColor.R * 255, insertColor.G * 255, insertColor.B * 255)
        Module._SetDeleteResColor(comparePtr, deleteColor.R * 255, deleteColor.G * 255, deleteColor.B * 255)

        // 对比的透明度
        Module._SetReplaceResTransparency(comparePtr, 0.5 * 255)
        Module._SetInsertResTransparency(comparePtr, 0.5 * 255)
        Module._SetDeleteResTransparency(comparePtr, 0.5 * 255)
        for (let i = 0; i < minPageCount; i++) {
          const compareResultsPtr = Module._DoCompare(comparePtr, i, i, compareType, 1)

          const result = licenseErrorMessage('PDF COMPARE')
          if (result && result.code) {
            return result
          }

          // 对比的结果数量
          Module._GetCompareResultsCount(compareResultsPtr, 1)

          // 对比的结果位置
          CompareResultArray = []
          Module._GetCompareResults(oldDocPtr, newDocPtr, compareResultsPtr, 1)

          // 对比的replace、insert、delete的数量
          ResultsCountParams = {}
          Module._GetResultsCountParams(compareResultsPtr)
        }

        // 获取对比结果文档 buffer
        DataArray = []
        Module._GenerateNewDoc(comparePtr)
        let pdfData = []
        for (let i = 0; i < DataArray.length; i++) {
          let subArray = new Uint8Array(DataArray[i])
          pdfData.push(subArray)
        }
        DataArray = []
        const blobData = new Blob(pdfData, { type: "application/pdf" })
        const oldDocument = saveDocument(oldDocPtr, 2)
        const newDocument = saveDocument(newDocPtr, 2)
        return {
          oldDocument,
          newDocument,
          blobData
        }
      } else if (data.type === 2) {
        const { inColor: rawInColor, newColor: rawNewColor, inTransparency: rawInTransparency, newTransparency: rawNewTransparency, coverType } = data

        const compareDrawingsPtr = Module._InitCompareDrawing(oldDocPtr, newDocPtr)

        // 设置对比的类型
        Module._SetBlendMode(compareDrawingsPtr, coverType)
        Module._SetNoFill(compareDrawingsPtr, 1)
        // 透明度值为0-1
        const inTransparency = rawInTransparency / 100
        const newTransparency = rawNewTransparency / 100
        // 设置对比的透明度
        Module._SetStrokeAlphaCompare(compareDrawingsPtr, inTransparency)
        Module._SetStrokeAlphaCompared(compareDrawingsPtr, newTransparency)
        Module._SetFillAlphaCompare(compareDrawingsPtr, inTransparency)
        Module._SetFillAlphaCompared(compareDrawingsPtr, newTransparency)
        // 设置对比的颜色
        const inColor = hexToRGB(rawInColor)
        const newColor = hexToRGB(rawNewColor)
        Module._SetColorCompare(compareDrawingsPtr, inColor.R * 255, inColor.G * 255, inColor.B * 255)
        Module._SetColorCompared(compareDrawingsPtr, newColor.R * 255, newColor.G * 255, newColor.B * 255)

        Module._CompareContent(compareDrawingsPtr)

        const result = licenseErrorMessage('PDF COMPARE')
        if (result && result.code) {
          return result
        }

        // 获取对比结果文档 buffer
        const docPtr = Module._GetDocGenerate(compareDrawingsPtr)
        const blobData = await saveDocumentByStream({
          doc: docPtr,
        })
        return {
          blobData
        }
      }
    })

    messageHandler.on('GetOutlines', (data) => {
      const { doc } = data

      const rootOutlinePtr = Module._GetOutlineRoot(doc)

      const outlines = []
      getOutlines(doc, rootOutlinePtr, outlines, -1)
      return outlines
    })

    messageHandler.on('InitText', (data) => {
      const { pagePtr } = data
      const textPtr = Module._InitText(pagePtr)

      return textPtr
    })

    messageHandler.on('ClearText', (data) => {
      const { textPtr } = data
      Module._ClearText(textPtr)
    })

    messageHandler.on('ClearPage', (data) => {
      const { pagePtr } = data
      Module._ClearPage(pagePtr)
    })

    messageHandler.on('Search', (data) => {
      const { pagesPtr, value } = data
      const text = stringToNewUTF8(value)

      let result = null

      const searchResults = []
      for (let i = 0; i < pagesPtr.length; i++) {
        const ptr = pagesPtr[i]
        const pagePtr = ptr.pagePtr
        const textPtr = ptr.textPtr
        const textFindPtr = Module._InitTextFind()
        Module._FindStart(textPtr, textFindPtr, text, 0, 0)
        result = licenseErrorMessage('PDF VIEWER SEARCH')
        if (result.code) {
          return result
        }

        let resFind = 1
        let pageSearchIndex = 0
        do {
          TextFindItemArray = []
          resFind = Module._FindNext(pagePtr, textPtr, textFindPtr)

          if (resFind) {
            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,
              quads,
              content: TextFindItemArray[0].Content.replace(/\r?\n/gm, ' '),
              pageSearchIndex,
              searchValue: value
            })
            pageSearchIndex++
          }
        } while (resFind)
      }

      return {
        searchResults
      }
    })

    messageHandler.on('InitEditPage', (data) => {
      const { pagePtr } = data
      const editPagePtr = Module._InitEditPage(pagePtr)
      return { editPagePtr, pagePtr }
    })

    messageHandler.on('ClearEditPage', (editPagePtr) => {
      Module._ClearEditPage(editPagePtr)
    })

    messageHandler.on('BeginEdit', (data) => {
      const { editPagePtr, type } = data
      Module._BeginEdit(editPagePtr, type)
      const result = licenseErrorMessage('PDF CONTENT EDIT')
      return result
    })

    messageHandler.on('EndEdit', (editPagePtr) => {
      Module._EndEdit(editPagePtr)
    })

    messageHandler.on('GetEditAreaCount', (editPagePtr) => {
      return Module._GetEditAreaCount(editPagePtr)
    })

    messageHandler.on('GetEditArea', (data) => {
      const { editPagePtr, index } = data
      return Module._GetEditArea(editPagePtr, index)
    })

    messageHandler.on('IsTextArea', (editAreaPtr) => {
      return Module._IsTextArea(editAreaPtr)
    })

    messageHandler.on('IsImageArea', (editAreaPtr) => {
      return Module._IsImageArea(editAreaPtr)
    })

    messageHandler.on('SetFrame', (data) => {
      const { pagePtr, editAreaPtr, rect } = data

      Module._SetFrame(
        pagePtr,
        editAreaPtr,
        rect.left,
        rect.right,
        rect.bottom,
        rect.top,
        true
      )
    })

    messageHandler.on('GetFrame', (data) => {
      const { pagePtr, editAreaPtr } = data

      Rect = {}

      Module._GetFrame(pagePtr, editAreaPtr)

      return {
        left: Rect.Left,
        top: Rect.Top,
        right: Rect.Right,
        bottom: Rect.Bottom
      }
    })

    messageHandler.on('GetBeginAndEndCharPlace', (editAreaPtr) => {
      EditCharPlace = {}
      EndEditCharPlace = {}

      Module._GetBeginAndEndCharPlace(editAreaPtr)

      return {
        start: EditCharPlace,
        end: EndEditCharPlace
      }
    })

    messageHandler.on('GetTextByRange', (data) => {
      const { editAreaPtr, start, end } = data
      Module._GetTextByRange(
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex,
      )
      return U8StringData
    })

    messageHandler.on('GetText', async (editAreaPtr) => {

      EditCharPlace = {}
      EndEditCharPlace = {}

      await Module._GetBeginAndEndCharPlace(editAreaPtr)

      await Module._GetTextByRange(
        editAreaPtr,
        EditCharPlace.SectionIndex, EditCharPlace.LineIndex, EditCharPlace.RunIndex, EditCharPlace.CharIndex,
        EndEditCharPlace.SectionIndex, EndEditCharPlace.LineIndex, EndEditCharPlace.RunIndex, EndEditCharPlace.CharIndex,
      )
      return U8StringData
    })

    messageHandler.on('SelectCharsRangeAtPos', (data) => {
      const { pagePtr, editAreaPtr, startPoint, endPoint } = data

      EditCharPlace = {}
      EndEditCharPlace = {}

      Module._SelectCharsRangeAtPos(
        pagePtr,
        editAreaPtr,
        startPoint.x, startPoint.y,
        endPoint.x, endPoint.y,
      )
      return { start: EditCharPlace, end: EndEditCharPlace }
    })

    messageHandler.on('GetSectionAtPos', (data) => {
      const { pagePtr, editAreaPtr, pointX, pointY } = data

      EditCharPlace = {}
      EndEditCharPlace = {}

      Module._GetSectionAtPos(
        pagePtr,
        editAreaPtr,
        pointX,
        pointY
      )
      return { start: EditCharPlace, end: EndEditCharPlace }
    })

    messageHandler.on('GetTextCursorPoints', (data) => {
      const { pagePtr, editAreaPtr, char } = data

      CursorPoints = {}

      Module._GetTextCursorPoints(
        pagePtr,
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
      )
      return CursorPoints
    })

    messageHandler.on('GetPrevCharPlace', (data) => {
      const { editAreaPtr, char } = data

      EditCharPlace = {}
      EndEditCharPlace = {}

      Module._GetPrevCharPlace(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
      )
      return { start: EditCharPlace, end: EndEditCharPlace }
    })

    messageHandler.on('GetNextCharPlace', (data) => {
      const { editAreaPtr, char } = data

      EditCharPlace = {}
      EndEditCharPlace = {}

      Module._GetNextCharPlace(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
      )
      return { start: EditCharPlace, end: EndEditCharPlace }
    })

    messageHandler.on('GetUpCharPlace', (data) => {
      const { editAreaPtr, char } = data

      EditCharPlace = {}
      EndEditCharPlace = {}

      Module._GetUpCharPlace(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
      )
      return { start: EditCharPlace, end: EndEditCharPlace }
    })

    messageHandler.on('GetDownCharPlace', (data) => {
      const { editAreaPtr, char } = data

      EditCharPlace = {}
      EndEditCharPlace = {}

      Module._GetDownCharPlace(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
      )
      return { start: EditCharPlace, end: EndEditCharPlace }
    })

    messageHandler.on('InsertText', (data) => {
      const { editAreaPtr, char, text } = data

      EditCharPlace = {}

      let textPtr = stringToNewUTF8(text)

      Module._InsertText(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
        textPtr,
        true
      )

      return EditCharPlace
    })

    messageHandler.on('BackSpaceChar', (data) => {
      const { editAreaPtr, char } = data

      EditCharPlace = {}

      Module._BackSpaceChar(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
        true
      )
      return EditCharPlace
    })

    messageHandler.on('DeleteChar', (data) => {
      const { editAreaPtr, char } = data

      EditCharPlace = {}

      Module._DeleteChar(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
        true
      )
      return EditCharPlace
    })

    messageHandler.on('DeleteChars', (data) => {
      const { editAreaPtr, start, end } = data

      EditCharPlace = {}

      Module._DeleteChars(
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex,
        true
      )
      return EditCharPlace
    })

    messageHandler.on('GetSelectLineRectsCounts', (data) => {
      const { editAreaPtr, start, end } = data
      return Module._GetSelectLineRectsCounts(
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex,
      )
    })

    messageHandler.on('GetSelectLineRects', (data) => {
      const { pagePtr, editAreaPtr, start, end } = data

      RectArray = []

      Module._GetSelectLineRects(
        pagePtr,
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex
      )
      return RectArray
    })

    messageHandler.on('SetCharsFontColor', (data) => {
      const { editAreaPtr, start, end, color: rawColor } = data
      const color = convertColorToRGB(rawColor)

      Module._SetCharsFontColor(
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex,
        color.R, color.G, color.B,
        true
      )
    })

    messageHandler.on('SetCharsFontTransparency', (data) => {
      const { editAreaPtr, start, end, opacity } = data
      Module._SetCharsFontTransparency(
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex,
        opacity,
        true
      )
    })

    messageHandler.on('SetTextAligningSection', (data) => {
      const { editAreaPtr, char, alignType } = data
      Module._SetTextAligningSection(
        editAreaPtr,
        alignType,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
        true
      )
    })

    messageHandler.on('SetTextAligningRange', (data) => {
      const { editAreaPtr, start, end, alignType } = data
      Module._SetTextAligningRange(
        editAreaPtr,
        alignType,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex,
        true
      )
    })

    messageHandler.on('SetCharsFontSize', (data) => {
      const { editAreaPtr, start, end, fontSize } = data
      Module._SetCharsFontSize(
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex,
        fontSize,
        true,
        true
      )
    })

    messageHandler.on('SetFontFromNativeTrueTypeFont', (data) => {
      const { editAreaPtr, start, end, fontFamily } = data

      const font = stringToNewUTF8(fontFamily)
      Module._SetFontFromNativeTrueTypeFont(
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex,
        font,
        true
      )
    })

    messageHandler.on('RefreshRange', (data) => {
      const { editAreaPtr, start, end } = data
      EditCharPlace = {}
      EndEditCharPlace = {}
      Module._RefreshRange(
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex
      )

      return {
        start: EditCharPlace,
        end: EndEditCharPlace
      }
    })

    messageHandler.on('SetCharsFontStyle', async (data) => {
      const { editAreaPtr, start, end, fontStyle } = data

      let action = '_' + fontStyle

      Module[action](
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex,
        true
      )
    })

    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,
        true
      )
    })

    messageHandler.on('RemoveEditAreaByIndex', (data) => {
      const { editPagePtr, editAreaIndex } = data
      Module._RemoveEditAreaByIndex(
        editPagePtr,
        editAreaIndex,
        true
      )
    })

    messageHandler.on('RemoveEditArea', (data) => {
      const { editPagePtr, editAreaPtr } = data
      Module._RemoveEditArea(
        editPagePtr,
        editAreaPtr,
        true
      )
    })

    messageHandler.on('CopyArea', (data) => {
      const { editPagePtr, editAreaPtr } = data
      return Module._CopyArea(
        editPagePtr,
        editAreaPtr
      )
    })

    messageHandler.on('GetCopyAreaContent', (data) => {
      const { editPagePtr, editAreaInfoPtr } = data
      Module._GetCopyAreaContent(
        editPagePtr,
        editAreaInfoPtr
      )
      return U8StringData
    })

    messageHandler.on('GetTextStyle', (data) => {
      const { editAreaPtr, char } = data

      EditTextStyle = {}

      Module._GetTextStyle(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
      )
      return EditTextStyle
    })

    messageHandler.on('GetBaseFontName', (data) => {
      const { editAreaPtr, char } = data

      Module._GetBaseFontName(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
      )
      return U8StringData
    })

    messageHandler.on('createEditorDoc', async (data) => {
      const { doc, password } = data
      return await copyDocument(doc, password)
    })

    messageHandler.on('GetTextSectionAlignType', async (data) => {
      const { editAreaPtr, char } = data
      return Module._GetTextSectionAlignType(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
      )
    })

    messageHandler.on('InsertPage', (data) => {
      const { doc, index, width, height } = data
      const res = Module._InsertPage(doc, index, width, height)

      const result = licenseErrorMessage('PDF EDITOR PAGE')
      if (result.code) {
        return result
      }

      return {
        status: res
      }
    })

    messageHandler.on('ImportPagesAtIndex', async (data) => {
      const { doc, file, range, index, password } = data

      const buffer = await convertFileToBuffer(file)
      const importDoc = Module._InitDocument()
      const length = ComPDFKitJS.opened_files.length || 0
      ComPDFKitJS.opened_files[length] = buffer
      const passwordPtr = stringToNewUTF8(password)
      Module._LoadDocumentByStream(importDoc, length, buffer.length, passwordPtr)
      const pagesCount = Module._GetPageCount(importDoc)
      let importRange
      let hasZero = false

      if (range === 'all') {
        importRange = '1-' + pagesCount
        // importRange = ''
      } else if (range === 'odd') {
        importRange = Array.from({ length: pagesCount }, (_, i) => i + 1).filter(num => num % 2 !== 0).join(",")
      } else if (range === 'even') {
        importRange = Array.from({ length: pagesCount }, (_, i) => i + 1).filter(num => num % 2 === 0).join(",")
      } else {
        const max = pagesCount
        let str = range.replace(/\s/g, '')
        let parts = str.split(',')
        let result = []

        parts.forEach(part => {
          if (part.includes('-')) {
            let [start, end] = part.split('-').map(num => parseInt(num))
            if (start <= 0) hasZero = true
            if (start > max) {
              return
            } else if (end > max) {
              end = max
            }
            result.push((start === max) ? start.toString() : `${start}-${end}`)
          } else {
            let num = parseInt(part)
            if (num <= 0) hasZero = true
            if (num <= max) {
              result.push(num.toString())
            }
          }
        })
        importRange = result.join(',')
      }
      // console.log('导入文档的页面范围：' + importRange)
      if (!importRange || hasZero) return 0

      importRange = stringToNewUTF8(importRange)

      const res = Module._ImportPagesAtIndex(
        doc,
        importDoc,
        importRange,
        index
      )

      const result = licenseErrorMessage('PDF EDITOR PAGE')
      if (result.code) {
        return result
      }

      ComPDFKitJS.opened_files.pop()
      return {
        status: res
      }
    })

    messageHandler.on('RemovePage', (data) => {
      const { doc, index } = data
      const res = Module._RemovePage(doc, index)

      const result = licenseErrorMessage('PDF EDITOR PAGE')
      if (result.code) {
        return result
      }
      return {
        status: res
      }
    })

    messageHandler.on('RotatePage', (data) => {
      const { pagePtr, rotation } = data
      Module._RotatePage(pagePtr, rotation)

      const result = licenseErrorMessage('PDF EDITOR PAGE')
      if (result.code) {
        return result
      }
    })

    messageHandler.on('GetPageWithoutParse', (data) => {
      const { doc, index } = data
      const pagePtr = Module._GetPageWithoutParse(doc, index)
      return pagePtr
    })

    messageHandler.on('CopyPage', (data) => {
      const { doc, index } = data

      const importRange = stringToNewUTF8(index + 1 + '')
      const res = Module._ImportPagesAtIndex(
        doc,
        doc,
        importRange,
        index + 1
      )

      const result = licenseErrorMessage('PDF EDITOR PAGE')
      if (result.code) {
        return result
      }

      return {
        status: res
      }
    })

    messageHandler.on('GetPageRotation', (pagePtr) => {
      return Module._GetPageRotation(pagePtr)
    })

    messageHandler.on('ClearDocument', (doc) => {
      Module._ClearDocument(doc)
    })

    messageHandler.on('ExtractPage', async (data) => {
      const { doc, range, separateFile } = data

      let blobDataList = []

      if (separateFile) {
        for (let i = 0; i < range.length; i++) {
          const pageNum = range[i] + 1 + ''
          
          const page = stringToNewUTF8(pageNum)
          const newDoc = Module._InitDocument()
          Module._CreateDocument(newDoc)
          Module._ImportPagesAtIndex(
            newDoc,
            doc,
            page,
            0
          )

          const result = licenseErrorMessage('PDF EDITOR PAGE')
          if (result.code) {
            return result
          }

          const blobData = saveDocument(newDoc, 2)
          blobDataList.push({ name: pageNum, blobData })
        }
      } else {
        const exportRange = stringToNewUTF8(range)
        const newDoc = Module._InitDocument()
        Module._CreateDocument(newDoc)
        Module._ImportPagesAtIndex(
          newDoc,
          doc,
          exportRange,
          0
        )

        const result = licenseErrorMessage('PDF EDITOR PAGE')
        if (result.code) {
          return result
        }

        const blobData = saveDocument(newDoc, 2)
        blobDataList.push(blobData)
      }
      return { 
        blobDataList
       }
    })

    messageHandler.on('MovePage', (data) => {
      const { doc, index, targetIndex } = data

      const res = Module._MovePage(
        doc,
        index,
        targetIndex
      )
      const result = licenseErrorMessage('PDF EDITOR PAGE')
      if (result.code) {
        return result
      }

      return {
        status: res
      }
    })

    // messageHandler.on('CreateNewImageArea', (data) => {
    //   const { pagePtr, editPagePtr, imageDataArray, width, height } = data

    //   return Module._CreateNewTextArea(
    //     pagePtr,
    //     editPagePtr,
    //     imageDataArray,
    //     width,
    //     height
    //   )
    // })
    messageHandler.on('CreateNewImageAreaByStream', async (data) => {
      const { pagePtr, editPagePtr, rect, imageBase64 } = data

      const imageData = convertBase64ToBytes(imageBase64)

      ComPDFKitJS.opened_image = []
      ComPDFKitJS.opened_image[0] = imageData

      return Module._CreateNewImageAreaByStream(
        pagePtr,
        editPagePtr,
        rect.left,
        rect.right,
        rect.bottom,
        rect.top,
        0,
        imageData.length,
        true
      )
    })

    messageHandler.on('ReplaceImageAreaByStream', async (data) => {
      const { pagePtr, editPagePtr, editAreaPtr, rect, imageBase64 } = data

      const imageData = convertBase64ToBytes(imageBase64)

      ComPDFKitJS.opened_image = []
      ComPDFKitJS.opened_image[0] = imageData

      return Module._ReplaceImageAreaByStream(
        pagePtr,
        editPagePtr,
        editAreaPtr,
        rect.left,
        rect.right,
        rect.bottom,
        rect.top,
        0,
        imageData.length,
        true
      )
    })

    messageHandler.on('RotateImage', (data) => {
      const { editAreaPtr, angle } = data
      return Module._RotateImage(editAreaPtr, angle, true)
    })

    messageHandler.on('HorizontalMirrorImage', (editAreaPtr) => {
      return Module._HorizontalMirrorImage(editAreaPtr, true)
    })

    messageHandler.on('VerticalMirrorImage', (editAreaPtr) => {
      return Module._VerticalMirrorImage(editAreaPtr, true)
    })

    messageHandler.on('SetImageTransparency', (data) => {
      const { editAreaPtr, opacity } = data
      return Module._SetImageTransparency(editAreaPtr, opacity, true)
    })

    messageHandler.on('GetImageTransparency', (editAreaPtr) => {
      return Module._GetImageTransparency(editAreaPtr)
    })

    messageHandler.on('GetImageAreaSize', (editAreaPtr) => {
      ImageAreaInfo = {}
      Module._GetImageAreaSize(editAreaPtr)
      return { width: ImageAreaInfo.Width, height: ImageAreaInfo.Height, stride: ImageAreaInfo.Stride }
    })

    messageHandler.on('ExtractOriginalImage', (data) => {
      // const { editAreaPtr, width, height } = data
      const imageArray = extractImage(data)
      return { imageArray }
    })

    messageHandler.on('CutWithRect', (data) => {
      const { pagePtr, editAreaPtr, rect } = data

      Module._CutWithRect(
        pagePtr,
        editAreaPtr,
        rect.left,
        rect.right,
        rect.bottom,
        rect.top,
        true
      )
    })

    messageHandler.on('GetClipRect', (data) => {
      const { pagePtr, editAreaPtr } = data

      Rect = {}

      Module._GetClipRect(pagePtr, editAreaPtr)

      return {
        left: Rect.Left,
        top: Rect.Top,
        right: Rect.Right,
        bottom: Rect.Bottom
      }
    })

    messageHandler.on('SetClipRect', (data) => {
      const { pagePtr, editAreaPtr, rect } = data

      Module._SetClipRect(
        pagePtr,
        editAreaPtr,
        rect.left,
        rect.right,
        rect.bottom,
        rect.top,
        true
      )
    })

    messageHandler.on('RemoveSignature', (data) => {
      const { doc, signaturePtr } = data

      return Module._RemoveSignature(doc, signaturePtr, true)
    })

    messageHandler.on('AddToTrustedCertificates', data => {
      return addToTrustedCertificates(data)
    })
    
    messageHandler.on('CreateReplyAnnotation', (data) => {
      const { pagePtr, annotPtr, reply } = data
      createReplyAnnotation(pagePtr, annotPtr, reply)
    })

    messageHandler.on('CreateReplyStateAnnotation', (data) => {
      const { pagePtr, annotPtr, state } = data

      const stateInt = AnnotationState[state]
      const replyPtr = Module._CreateReplyStateAnnotation(pagePtr, annotPtr, stateInt)
      newStateInt = Module._GetState(replyPtr)
      
      return {
        annotPtr: replyPtr,
        state: AnnotationStateString[newStateInt]
      }
    })

    messageHandler.on('GetReplyAnnotation', (data) => {
      const { pagePtr, annotPtr } = data
      return getReplyArray(pagePtr, annotPtr)
    })
    
    messageHandler.on('EditReplyAnnotation', (data) => {
      const { reply } = data

      if (reply.author) {
        setAnnotAuthor(reply)
      }
      if (reply.contents) {
        setAnnotContent(reply)
      }
      if (reply.date) {
        setAnnotCreationDate(reply)
      }
      if (reply.title) {
        setAnnotTitle(reply)
      }
    })

    messageHandler.on('SetState', (data) => {
      const { reviewAnnotState, markedAnnotState } = data

      if (markedAnnotState) {
        const stateInt = AnnotationState[markedAnnotState.state.toUpperCase()]
        Module._SetState(markedAnnotState.annotPtr, stateInt)
        const newStateInt = Module._GetState(markedAnnotState.annotPtr)
        return {
          annotPtr: markedAnnotState.annotPtr,
          state: AnnotationStateString[newStateInt]
        }
      }
      if (reviewAnnotState) {
        const stateInt = AnnotationState[reviewAnnotState.state.toUpperCase()]
        Module._SetState(reviewAnnotState.annotPtr, stateInt)
        const newStateInt = Module._GetState(reviewAnnotState.annotPtr)
        return {
          annotPtr: reviewAnnotState.annotPtr,
          state: AnnotationStateString[newStateInt]
        }
      }
    })

    messageHandler.on('CanRedo', (editPagePtr) => {
      return Module._CanRedo(editPagePtr)
    })

    messageHandler.on('CanUndo', (editPagePtr) => {
      return Module._CanUndo(editPagePtr)
    })

    messageHandler.on('Redo', (editPagePtr) => {
      EditCharPlace = {}
      const editAreaPtr = Module._Redo(editPagePtr)

      return {
        editAreaPtr,
        editCharPlace: EditCharPlace
      }
    })

    messageHandler.on('Undo', (editPagePtr) => {
      EditCharPlace = {}
      const editAreaPtr = Module._Undo(editPagePtr)

      return {
        editAreaPtr,
        editCharPlace: EditCharPlace
      }
    })

    messageHandler.on('GetLineLeading', (data) => {
      const { editAreaPtr, char } = data

      return Module._GetLineLeading(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex
      )
    })

    messageHandler.on('SetLineLeading', (data) => {
      const { editAreaPtr, char, spacing } = data

      Module._SetLineLeading(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
        spacing
      )
    })

    messageHandler.on('GetSectionAfterLeading', (data) => {
      const { editAreaPtr, char } = data

      return Module._GetSectionAfterLeading(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex
      )
    })

    messageHandler.on('SetSectionAfterLeading', (data) => {
      const { editAreaPtr, char, spacing } = data

      Module._SetSectionAfterLeading(
        editAreaPtr,
        char.SectionIndex, char.LineIndex, char.RunIndex, char.CharIndex,
        spacing
      )
    })

    messageHandler.on('GetCharacterSpacing', (data) => {
      const { editAreaPtr, start, end } = data

      return Module._GetCharacterSpacing(
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex,
      )
    })

    messageHandler.on('SetCharacterSpacing', (data) => {
      const { editAreaPtr, start, end, spacing } = data

      Module._SetCharacterSpacing(
        editAreaPtr,
        start.SectionIndex, start.LineIndex, start.RunIndex, start.CharIndex,
        end.SectionIndex, end.LineIndex, end.RunIndex, end.CharIndex,
        spacing
      )
    })

    messageHandler.on('ApplyRedaction', (data) => {
      const { annotPtr } = data
      return Module._ApplyRedaction(annotPtr)
    })

    messageHandler.on('ApplyRedactionPage', (data) => {
      const { pagePtr } = data
      return Module._ApplyRedactionPage(pagePtr)
    })

    messageHandler.on('ErasureRedactionFromRect', (data) => {
      const { pagePtr, rect } = data
      return Module._ErasureRedactionFromRect(
        pagePtr,
        rect.left,
        rect.right,
        rect.bottom,
        rect.top
      )
    })

    messageHandler.on('GetStampRect', (data) => {
      const { doc, pagePtr, annotation } = data
      const { contents, time, stampShape, stampColor } = annotation
      const typeInt = AnnotationType[annotation.type.toUpperCase()]
      const annotPtr = Module._CreateAnnot(doc, pagePtr, typeInt)

      const contentsPtr = stringToNewUTF8(contents)
      const timePtr = stringToNewUTF8(time)

      Module._SetTextStamp(annotPtr, contentsPtr, timePtr, stampShape, stampColor, 0)
      const result = licenseErrorMessage('PDF Annotation STAMPS')
      if (result.code) {
        return result
      }

      Module._UpdateAnnotAp(annotPtr, 0)
      Module._GetAnnotRect(pagePtr, annotPtr)
      Module._RemoveAnnot(annotPtr)
      return {
        left: Rect.Left,
        top: Rect.Top,
        right: Rect.Right,
        bottom: Rect.Bottom
      }
    })
  }
}

async function setUp() {
  while (!runtimeInitialized) {
    await sleep(100)
  }
  return new Promise((resolve, reject) => {
    resolve()
  })
}

setUp().then(() => {
  CPDFWorker.setUp()
})

function loadDocumentByStream(data) {
  const { doc, fileId, length, password: rawPassword } = data

  const password = stringToNewUTF8(rawPassword)
  return Module._LoadDocumentByStream(doc, fileId, length, password)
}

function setPassword(data) {
  const { doc, password: rawPassword } = data
  const password = stringToNewUTF8(rawPassword)
  Module._SetPassword(doc, password, '', '', '', '', '', '')
  const result = licenseErrorMessage('PDF SECURITY ENCRYPT')
  return result
}

function RenderPageBitmapWithMatrix(data) {
  const { pagePtr, scale, left, right, bottom, top, mode, flag, form } = data
  let pixelNum = parseInt(right - left) * parseInt(bottom - top)
  let imageBytes = pixelNum * 4
  let imageptr = _malloc(imageBytes)
  for (var i = 0; i < pixelNum; i++) {
    Module.HEAP32[imageptr / 4 + i] = 0
  }

  Module._RenderPageBitmapWithMatrix(pagePtr, scale, left, right, bottom, top, mode, imageptr, flag, form)
  const result = licenseErrorMessage('PDF VIEWER RENDER')
  if (result.code) {
    _free(imageptr)
    return result
  }

  let imageArray = new Uint8Array(imageBytes)
  for (var i = 0; i < imageBytes; i += 4) {
    //bgra 转 rgba
    imageArray[i] = Module.HEAPU8[imageptr + i + 2]
    imageArray[i + 1] = Module.HEAPU8[imageptr + i + 1]
    imageArray[i + 2] = Module.HEAPU8[imageptr + i]
    imageArray[i + 3] = Module.HEAPU8[imageptr + i + 3]
  }
  _free(imageptr)

  return {
    imageArray
  }
}

function RenderPageBitmap(data) {
  const { pagePtr, x, y, width, height, mode, flag, form } = data
  let pixelNum = parseInt(width) * parseInt(height)
  let imageBytes = pixelNum * 4
  let imageptr = _malloc(imageBytes)
  for (var i = 0; i < pixelNum; i++) {
    Module.HEAP32[imageptr / 4 + i] = 0
  }

  Module._RenderPageBitmap(pagePtr, x, y, width, height, mode, imageptr, flag, form)
  const result = licenseErrorMessage('PDF VIEWER RENDER')
  if (result.code) {
    _free(imageptr)
    return result
  }

  let imageArray = new Uint8Array(imageBytes)
  for (var i = 0; i < imageBytes; i += 4) {
    //bgra 转 rgba
    imageArray[i] = Module.HEAPU8[imageptr + i + 2]
    imageArray[i + 1] = Module.HEAPU8[imageptr + i + 1]
    imageArray[i + 2] = Module.HEAPU8[imageptr + i]
    imageArray[i + 3] = Module.HEAPU8[imageptr + i + 3]
  }
  _free(imageptr)

  return {
    imageArray
  }
}

function createAnnotation(doc, pagePtr, annotation) {
  const typeInt = AnnotationType[annotation.type.toUpperCase()]

  const annotPtr = Module._CreateAnnot(doc, pagePtr, typeInt)
  const result = licenseErrorMessage('PDF Annotation')
  if (result.code) {
    return result
  }

  annotation.annotPtr = annotPtr
  const { color, borderWidth, contents, date, opacity, fillTransparency, rect, author, fillColor, quadPoints } = annotation
  if (color) {
    setAnnotRGBColor({
      annotPtr,
      color
    })
  }

  if (fillColor) {
    setAnnotFilledRGBColor({
      annotPtr,
      fillColor
    })
  }

  if (contents) {
    setAnnotContent({
      annotPtr,
      contents
    })
  }

  if (date) {
    setAnnotCreationDate({
      annotPtr,
      date
    })
  }

  if (opacity) {
    setAnnotTransparency({
      annotPtr,
      opacity
    })
  }

  if (fillTransparency) {
    SetAnnotFilledTransparency({
      annotPtr,
      fillTransparency
    })
  }

  if (rect) {
    setAnnotRect({
      pagePtr,
      annotPtr,
      rect
    })
  }

  if (author) {
    setAnnotAuthor({
      annotPtr,
      author
    })
  }

  if (quadPoints) {
    setQuadPoints({
      pagePtr,
      annotPtr,
      quadPoints
    })
  }

  switch (typeInt) {
    case AnnotationType.TEXT:
      break
    case AnnotationType.LINK:
      if (annotation.destPage ) {
        setLinkDest(annotation)
      }
      if (annotation.url) {
        setLinkUri(annotation)
      }
      break
    case AnnotationType.FREETEXT:
      const { bgColor, fontName, fontSize, textAlignment, color } = annotation
      if (bgColor) {
        Module._SetFreeTextBgColor(annotPtr, bgColor.R / 255, bgColor.G / 255, bgColor.B / 255)
      } else {
        Module._ClearFreeTextBgColor(annotPtr)
      }

      setFreeText({
        annotPtr,
        fontName,
        fontSize,
        color,
        textAlignment,
        contents
      })
      break
    case AnnotationType.LINE:
      const { tail, linePoints } = annotation
      if (tail) {
        Module._SetLineType(annotPtr, 0, 1)
      }
      Module._SetLinePoint(pagePtr, annotPtr, linePoints[0], linePoints[1], linePoints[2], linePoints[3])
    case AnnotationType.SQUARE:
    case AnnotationType.CIRCLE:
      createShape({
        annotPtr,
        borderWidth,
      })

      setAnnotRGBColor({
        annotPtr,
        color: annotation.borderColor
      })
      break
    case AnnotationType.HIGHLIGHT:
    case AnnotationType.UNDERLINE:
    case AnnotationType.SQUIGGLY:
    case AnnotationType.STRIKEOUT: {
      const { quadPoints, color } = annotation
      createMarkup({
        pagePtr,
        annotPtr,
        color,
        quadPoints
      })
      break
    }
    case AnnotationType.STAMP:
      const { stampType } = annotation
      if (stampType === 'standard') {
        const result = createStandardStamp({
          annotPtr,
          contents
        })
        if (result) {
          return result
        }
      } else if (stampType === 'text') {
        annotation.annotPtr = annotPtr
        const result = createTextStamp(annotation)
        if (result) {
          return result
        }
      } else if (stampType === 'image') {
        annotation.annotPtr = annotPtr
        annotation.doc = doc
        const result = createImageStamp(annotation)
        if (result) {
          return result
        }
      }
      break
    case AnnotationType.INK:
      const { inkPointes, borderColor } = annotation
      if (borderColor) {
        setAnnotRGBColor({
          annotPtr,
          color: borderColor
        })
      }
      setAnnotBorderWidth({
        annotPtr,
        borderWidth
      })
      setInkPointes({
        pagePtr,
        annotPtr,
        inkPointes
      })
  }

  if (rect) {
    setAnnotRect({
      pagePtr,
      annotPtr,
      rect
    })
  }

  if (annotation.operate === 'mod-form') Module._UpdateFormAp(annotation.annotPtr)
  else Module._UpdateAnnotAp(annotation.annotPtr, 0)

  return {
    annotPtr
  }
}

function createWidget(doc, pagePtr, annotation) {
  const { type, date, fieldName, backgroundColor, checkStyle, rect } = annotation

  const typeInt = WidgetType[type.toUpperCase()]
  const annotPtr = Module._CreateWidget(doc, pagePtr, typeInt)
  const result = licenseErrorMessage('PDF FORM')
  if (result.code) {
    return result
  }

  annotation.annotPtr = annotPtr

  if (date) {
    setAnnotCreationDate({
      annotPtr,
      date
    })
  }

  if (backgroundColor) {
    setWidgetBackgroundColor({
      annotPtr,
      backgroundColor
    })
  }

  if (checkStyle) {
    setCheckStyle({
      annotPtr,
      checkStyle
    })
  }

  if (fieldName) {
    setWidgetFieldName({
      annotPtr,
      fieldName
    })
  }

  let { borderColor, borderWidth, borderStyle, isChecked, color, fontName, fontSize } = annotation

  if (borderColor || borderStyle || borderWidth) {
    !borderColor && (borderColor = '#43474D')
    !borderStyle && (borderStyle = 'solid')
    !borderWidth && borderWidth !== 0 && (borderWidth = 1)

    setWidgetBorder({
      annotPtr,
      borderColor,
      borderWidth,
      borderStyle
    })
  }

  switch (type) {
    case 'checkbox':
      if (isChecked || isChecked === 0) {
        setCheckBoxIsChecked({
          annotPtr,
          isChecked
        })
      }
      break
    case 'radiobutton':
      if (isChecked || isChecked === 0) {
        setRadioButtonIsChecked({
          annotPtr,
          isChecked
        })
      }
      break
    case 'listbox':
    case 'combobox':
      const { items, selected } = annotation
      for (let i = 0; i < items.length; i++) {
        addWidgetItem({
          annotPtr,
          item: {
            value: items[i].Value,
            index: i
          }
        })
      }
      setAnnotFontData({
        annotPtr,
        fontName,
        fontSize,
        color
      })
      if (selected || selected === 0) {
        setAnnotSelected({
          annotPtr,
          selected
        })
      }
      break
    case 'pushbutton':
      setAnnotFontData({
        annotPtr,
        fontName,
        fontSize,
        color
      })
      const { title: rawTitle } = annotation
      const title = stringToNewUTF8(rawTitle)
      Module._SetButtonTitle(annotPtr, title)

      if (annotation.destPage) {
        setPushButtonDest(annotation)
      }
      if (annotation.url) {
        setPushButtonUri(annotation)
      }
      break
    case 'textfield':
      const { isMultiLine, textAlignment } = annotation
      const textAlignmentInt = ALIGNMAP[textAlignment.toUpperCase()]
      Module._SetMultiLine(annotPtr, isMultiLine)
      Module._SetWidgetTextJustification(annotPtr, textAlignmentInt)
      Module._SetTextFieldSpe(annotPtr, 0)
      if ('contents' in annotation) {
        setTextFieldContent({
          annotPtr,
          contents: annotation.contents
        })
      }
      setAnnotFontData({
        annotPtr,
        fontName,
        fontSize,
        color
      })
      break
  }

  setAnnotRect({
    pagePtr,
    annotPtr,
    rect
  })

  Module._UpdateFormAp(annotPtr)

  if (type === 'signatureFields') {
    const { imageBase64 } = annotation
    if (imageBase64) {
      const imageData = convertBase64ToBytes(imageBase64)

      ComPDFKitJS.opened_image = []
      ComPDFKitJS.opened_image[0] = imageData

      Module._RemoveAP(annotPtr)
      Module._SetImageStampByStream(annotPtr, 0, imageData.length, 0)
      Module._SignWithName(annotPtr)
    }
  }
  return {
    annotPtr
  }
}

function getOutlines(doc, rootOutlinePtr, outlines, level) {
  ++level
  const ptrs = []
  const isValid = Module._IsValid(rootOutlinePtr)
  if (!isValid) {
    return outlines
  }

  const firstOutlinePtr = Module._GetFirst(rootOutlinePtr)
  if (!Module._IsValid(firstOutlinePtr)) {
    return outlines
  }

  let outlinePtr = createOutline(doc, ptrs, firstOutlinePtr, outlines, level)
  while (Module._IsValid(outlinePtr)) {
    outlinePtr = createOutline(doc, ptrs, outlinePtr, outlines, level)
  }
  for (let i = 0; i < ptrs.length; i++) {
    getOutlines(doc, ptrs[i], outlines[i].children, level)
  }
}

function createOutline(doc, ptrs, outlinePtr, outlines, level) {
  ptrs.push(outlinePtr)
  Module._GetTitle(outlinePtr)
  const outline = {
    title: U8StringData,
    children: [],
    level
  }

  const actionType = Module._GetOutlineActionType(doc, outlinePtr)

  getOutlineAction(doc, outlinePtr, actionType, outline)
  outlines.push(outline)

  return Module._GetNext(outlinePtr)
}

function getOutlineAction(doc, outlinePtr, actionType, outline) {
  if (actionType === 1) {
    PDFDestination = {}
    Module._GetOutlineDestination(doc, outlinePtr)
    outline.pageNumber = PDFDestination.PageIndex + 1
    outline.posX = PDFDestination.PosX < 0 ? 0 : PDFDestination.PosX
    outline.posY = PDFDestination.PosY < 0 ? 0 : PDFDestination.PosY
  } else if (actionType === 6) {
    Module._GetUri(outlinePtr)
    outline.url = U8StringData
  }
}

function getCommonAnntationAttr({
  pagePtr,
  annotPtr
}) {
  const borderWidth = Module._GetAnnotBorderWidth(annotPtr)
  const annotation = {
    borderWidth,
  }

  return annotation
}

function getMarkup({
  pagePtr,
  annotPtr
}) {
  RGBColor = {}
  const annotationAttr = {}
  const color = {}
  const hasColor = Module._GetAnnotRGBColor(annotPtr)
  if (hasColor) {
    const { R, G, B } = RGBColor
    const hex = convertCppRGBToHex(R, G, B)

    annotationAttr.color = hex
  }

  InkPointArray = []
  Module._GetQuadPoints(pagePtr, annotPtr)
  annotationAttr.quadPoints = InkPointArray

  return annotationAttr
}

function getInk({
  pagePtr,
  annotPtr
}) {
  const count = Module._GetInkPathCount(annotPtr)

  const inkPointes = []
  for (let i = 0; i < count; i++) {
    InkPointArray = []
    Module._GetInkPoints(pagePtr, annotPtr, i)
    inkPointes.push(InkPointArray)
  }

  const borderWidth = Module._GetAnnotBorderWidth(annotPtr)

  return {
    borderWidth,
    inkPointes
  }
}

function getAnnotation({
  doc,
  pagePtr,
  annotPtr,
  typeInt,
  scale
}) {
  let annotationAttr = null
  Rect = {}
  Module._GetAnnotRect(pagePtr, annotPtr)
  const rect = {
    left: roundToDecimalPlaces(Rect.Left),
    top: roundToDecimalPlaces(Rect.Top),
    right: roundToDecimalPlaces(Rect.Right),
    bottom: roundToDecimalPlaces(Rect.Bottom)
  }

  Module._GetAnnotCreationDate(annotPtr)
  const rawDate = U8StringData
  const date = parseAdobePDFTimestamp(rawDate)
  Module._GetAnnotContent(annotPtr)
  const contents = U8StringData
  Module._GetAnnotTitle(annotPtr)
  const title = U8StringData
  Module._GetAnnotAuthor(annotPtr)
  const author = U8StringData

  const opacity = roundToDecimalPlaces(Module._GetAnnotTransparency(annotPtr))

  const annotation = {
    date,
    contents,
    rect,
    opacity,
    title,
    author
  }

  const { replies, reviewAnnotState, markedAnnotState } = getReplyArray(pagePtr, annotPtr)
  if (replies.length) annotation.replies = replies
  if (reviewAnnotState) annotation.reviewAnnotState = reviewAnnotState
  if (markedAnnotState) annotation.markedAnnotState = markedAnnotState

  let hasColor = 0
  let isFilled = 0
  let hasBgColor = 0
  let fillTransparency = null
  switch (typeInt) {
    case AnnotationType.TEXT:
      RGBColor = {}
      hasColor = Module._GetAnnotRGBColor(annotPtr)
      if (hasColor) {
        const { R, G, B } = RGBColor
        const hex = convertCppRGBToHex(R, G, B)
        annotation.textColor = hex
      }

      return annotation
    case AnnotationType.LINK:
      const actionType = Module._GetLinkActionType(annotPtr)
      annotation.actionType = ActionTypeString[actionType]
      RGBColor = {}
      isFilled = Module._GetAnnotFilledRGBColor(annotPtr)
      if (isFilled) {
        const { R, G, B } = RGBColor
        const hex = convertCppRGBToHex(R, G, B)
        annotation.fillColor = hex
      }
      if (actionType === 1) {
        PDFDestination = {}
        Module._GetLinkDestination(doc, annotPtr)
        annotation.destPage = PDFDestination.PageIndex + 1
        annotation.url = ''
      } else if (actionType === 6) {
        Module._GetLinkUri(annotPtr)

        annotation.url = U8StringData
        annotation.destPage = ''
      }
      return annotation
    case AnnotationType.FREETEXT:
      FontDa = {}
      Module._GetFreeTextFont(annotPtr)
      const fontName = U8StringData
      annotation.fontName = fontName
      annotation.fontSize = FontDa.fontSize

      const textAlignment = Module._GetTextAlignment(annotPtr)
      annotation.textAlignment = ALIGN[textAlignment]

      const { R, G, B } = FontDa
      const hex = convertCppRGBToHex(R, G, B)
      annotation.textColor = hex

      Module._GetFreeTextContent(annotPtr)
      const freetextContent = U8StringData
      annotation.contents = freetextContent

      RGBColor = {}
      hasBgColor = Module._GetFreeTextBgColor(annotPtr)
      if (hasBgColor) {
        const { R, G, B } = RGBColor
        const hex = convertCppRGBToHex(R, G, B)
        annotation.bgColor = hex
      }
      return annotation
    case AnnotationType.LINE:
      LinePoint = {}
      Module._GetLinePoint(pagePtr, annotPtr)
      const linePoints = [LinePoint.StartPointX, LinePoint.StartPointY, LinePoint.EndPointX, LinePoint.EndPointY]
      annotation.linePoints = linePoints

      LineType = {}
      Module._GetLineType(annotPtr)
      annotation.tail = LineTypeString[LineType.Tail],
        annotation.head = LineTypeString[LineType.Head]

      RGBColor = {}
      hasColor = Module._GetAnnotRGBColor(annotPtr)
      if (hasColor) {
        const { R, G, B } = RGBColor
        const hex = convertCppRGBToHex(R, G, B)

        annotation.borderColor = hex
      }

      annotation.borderWidth = Module._GetAnnotBorderWidth(annotPtr)
      return annotation
    case AnnotationType.SQUARE:
    case AnnotationType.CIRCLE:
      const borderWidth = Module._GetAnnotBorderWidth(annotPtr)

      annotation.borderWidth = borderWidth

      RGBColor = {}
      hasColor = Module._GetAnnotRGBColor(annotPtr)
      if (hasColor) {
        const { R, G, B } = RGBColor
        const hex = convertCppRGBToHex(R, G, B)

        annotation.borderColor = hex
      }

      RGBColor = {}
      isFilled = Module._GetAnnotFilledRGBColor(annotPtr)
      if (isFilled) {
        const { R, G, B } = RGBColor
        const hex = convertCppRGBToHex(R, G, B)

        annotation.fillColor = hex
      }

      fillTransparency = Module._GetAnnotFilledTransparency(annotPtr)
      annotation.fillTransparency = fillTransparency

      return annotation
    case AnnotationType.HIGHLIGHT:
    case AnnotationType.UNDERLINE:
    case AnnotationType.SQUIGGLY:
    case AnnotationType.STRIKEOUT:
      annotationAttr = getMarkup({
        pagePtr,
        annotPtr
      })
      Object.assign(annotation, annotationAttr)
      return annotation
    case AnnotationType.STAMP:
      const stampType = Module._GetStampType(annotPtr)
      annotation.stampType = StampTypeString[stampType]
      if (stampType === 0) {
        annotation.stampType = 'image'
      } else {
        TextStamp = {}
        Module._GetTextStamp(annotPtr)
        annotation.contents = TextStamp.Content
        annotation.stampColor = TextStamp.StampColor
        annotation.stampShape = TextStampShapeString[TextStamp.StampType]
        if (TextStamp.Date) {
          annotation.time = TextStamp.Date
        }
      }
      return annotation
    case AnnotationType.INK:
      RGBColor = {}
      hasColor = Module._GetAnnotRGBColor(annotPtr)
      if (hasColor) {
        const { R, G, B } = RGBColor
        const hex = convertCppRGBToHex(R, G, B)
        annotation.borderColor = hex
      }

      annotationAttr = getInk({
        pagePtr,
        annotPtr
      })
      Object.assign(annotation, annotationAttr)
      return annotation
  }

  return
}

function getWidgetAnnotation({
  doc,
  pagePtr,
  annotPtr,
  typeInt,
  signaturePtrList
}) {
  // WidgetType
  let annotationAttr = null
  Rect = {}
  Module._GetAnnotRect(pagePtr, annotPtr)
  const rect = {
    left: roundToDecimalPlaces(Rect.Left),
    top: roundToDecimalPlaces(Rect.Top),
    right: roundToDecimalPlaces(Rect.Right),
    bottom: roundToDecimalPlaces(Rect.Bottom)
  }

  Module._GetAnnotCreationDate(annotPtr)
  const rawDate = U8StringData
  const date = parseAdobePDFTimestamp(rawDate)
  Module._GetAnnotContent(annotPtr)
  const contents = U8StringData

  const type = Module._GetWidgetType(annotPtr)

  const annotation = {
    date,
    contents,
    rect,
    type: WidgetTypeString[type]
  }

  RGBColor = {}
  const hasColor = Module._GetWidgetBackgroundRGBColor(annotPtr)
  if (hasColor) {
    const { R, G, B } = RGBColor
    const hex = convertCppRGBToHex(R, G, B)
    annotation.backgroundColor = hex
  } else {
    annotation.backgroundColor = null
  }

  RGBColor = {}
  const hasBorderColor = Module._GetWidgetBorderRGBColor(annotPtr)
  if (hasBorderColor) {
    const { R, G, B } = RGBColor
    const hex = convertCppRGBToHex(R, G, B)
    annotation.borderColor = hex
  } else {
    annotation.borderColor = null
  }

  const borderStyleType = Module._GetWidgetBorderStyle(annotPtr)
  annotation.borderStyle = BorderStyleString[borderStyleType]

  Module._GetWidgetFieldName(annotPtr)
  const fieldName = U8StringData
  annotation.fieldName = fieldName

  const borderWidth = Module._GetAnnotBorderWidth(annotPtr)
  annotation.borderWidth = borderWidth

  const flag = Module._GetAnnotFlag(annotPtr)

  annotation.flag = flag
  if (flag === 0) {
    annotation.isHidden = 0
  } else {
    const invisible = !(flag & AnnotationFlags.AnnotationFlagInvisible)
    const noView = !(flag & AnnotationFlags.AnnotationFlagNoView)
    const hidden = !(flag & AnnotationFlags.AnnotationFlagHidden)
    annotation.isHidden = !(invisible && noView && hidden) ? 1 : 0
  }
  if (type === 1) {
    const checkStyle = Module._GetWidgetCheckStyle(annotPtr)
    annotation.checkStyle = checkStyle

    RGBColor = {}
    const hasColor = Module._GetWidgetCheckRGBColor(annotPtr)
    if (hasColor) {
      const { R, G, B } = RGBColor
      const hex = convertCppRGBToHex(R, G, B)

      annotation.checkRGBColor = hex
    }

    const isChecked = Module._GetCheckBoxIsChecked(annotPtr)
    annotation.isChecked = isChecked
  }

  if (type === 2) {
    const checkStyle = Module._GetWidgetCheckStyle(annotPtr)
    annotation.checkStyle = checkStyle
    const isChecked = Module._GetRadioButtonIsChecked(annotPtr)
    annotation.isChecked = isChecked
  }

  if (type === 3) {
    const textFieldSpe = Module._GetTextFieldSpe(annotPtr)
    if (textFieldSpe === 1) {
      return null
    }

    Module._GetTextFieldContent(annotPtr)
    const contents = U8StringData
    annotation.contents = contents

    const isMultiLine = Module._IsMultiLine(annotPtr)
    annotation.isMultiLine = isMultiLine

    const align = Module._GetWidgetTextJustification(annotPtr)
    annotation.textAlignment = ALIGN[align]

    FontDa = {}
    Module._GetAnnotFontDa(annotPtr)
    const fontName = U8StringData
    const isBold = /bold/i.test(fontName)
    const isItalic = /(oblique|italic)/i.test(fontName)
    const textStyle = {
      fontName: fontName.split('-')[0],
      fontSize: FontDa.fontSize,
      isBold,
      isItalic
    }
    Object.assign(annotation, textStyle)

    const { R, G, B } = FontDa
    const hex = convertCppRGBToHex(R, G, B)
    annotation.color = hex
  }

  if (type === 0) {
    Module._GetButtonTitle(annotPtr)
    const title = U8StringData
    annotation.title = title

    const actionType = Module._GetBtnActionType(annotPtr)

    Module._GetBtnActionNamedAction(annotPtr)
    const actionName = U8StringData
    annotation.actionName = actionName
    if (actionType === 1) {
      const destPage = Module._GetBtnActionDestPage(doc, annotPtr)
      annotation.destPage = destPage + 1

    } else if (actionType === 6) {
      Module._GetBtnActionURI(annotPtr)
      const actionURI = U8StringData
      annotation.url = actionURI
    }
    annotation.actionType = actionType

    FontDa = {}
    Module._GetAnnotFontDa(annotPtr)
    const fontName = U8StringData
    const isBold = /bold/i.test(fontName)
    const isItalic = /(oblique|italic)/i.test(fontName)
    const textStyle = {
      fontName: fontName.split('-')[0],
      fontSize: FontDa.fontSize,
      isBold,
      isItalic
    }
    Object.assign(annotation, textStyle)

    const { R, G, B } = FontDa
    const hex = convertCppRGBToHex(R, G, B)
    annotation.color = hex
  }

  if (type === 4 || type === 5) {
    WidgetItem = {}
    Module._GetItemsSelectedItem(annotPtr)
    annotation.selectedItem = {
      values: WidgetItem.Values,
      strings: WidgetItem.Strings
    }

    const itemsCount = Module._GetItemsCount(annotPtr)
    annotation.itemsCount = itemsCount
    WidgetItemsArray = []
    Module._GetWidgetItems(annotPtr)
    annotation.items = WidgetItemsArray

    Module._GetItemsSelectedValue(annotPtr)
    const selectedValue = U8StringData
    if (selectedValue) {
      for (let i = 0; i < WidgetItemsArray.length; i++) {
        if (WidgetItemsArray[i].Value === selectedValue) {
          annotation.selected = i
          break
        }
      }
    } else {
      annotation.selected = null
    }

    FontDa = {}
    Module._GetAnnotFontDa(annotPtr)
    const fontName = U8StringData
    const isBold = /bold/i.test(fontName)
    const isItalic = /(oblique|italic)/i.test(fontName)
    const textStyle = {
      fontName: fontName.split('-')[0],
      fontSize: FontDa.fontSize,
      isBold,
      isItalic
    }
    Object.assign(annotation, textStyle)

    const { R, G, B } = FontDa
    const hex = convertCppRGBToHex(R, G, B)
    annotation.color = hex
  }

  if (type === 6) {
    for (let i = 0; i < signaturePtrList.length; i++) {
      const signaturePtr = signaturePtrList[i]
      const res = Module._IsSameAnnotDict(signaturePtr, annotPtr)
      if (res) annotation.signaturePtr = signaturePtr
      annotation.isSigned = Module._IsSigned(annotPtr)
    }
  }

  return annotation
}

async function getSignatures({
  doc,
}) {
  const signatures = []
  const signaturePtrList = []
  const count = Module._GetSignatureCount(doc)

  for (let i = 0; i < count; i++) {
    // 获取签名指针
    const signaturePtr = Module._InitSignature(doc, i)
    if (signaturePtr) {
      signaturePtrList.push(signaturePtr)
      const signerList = []
      const signature = {
        signaturePtr,
        signerList
      }
      // 获取签名者信息
      SignerArray = []
      Module._GetSigner(signaturePtr)

      let needCheck = false
      const certificateList = []

      for (let j = 0; j < SignerArray.length; j++) {
        const signerPtr = SignerArray[j]

        if (signerPtr) {
          const signer = {
            signerPtr,
            isSignVerified: false,
            isCertTrusted: false,
            certificateList
          }

          const certPtr = Module._GetSignCert(signerPtr)
          if (certPtr) {
            // 自动导出并验证证书
            CertArray = []
            const a = Module._ExportToFilePath(certPtr, 1)
            ComPDFKitJS.opened_cert[0] = CertArray[0]

            CertArray = []
            Module._GetSignCertChain(certPtr)
            CertArray.push(certPtr)
            for (let k = 0; k < CertArray.length; k++) {
              const certPtr = CertArray[k]
              const certData = await getSignCertData(certPtr)

              const certificate = {
                certPtr,
                ...certData
              }
              certificateList.push(certificate)
            }

            const certLength = ComPDFKitJS.opened_cert[0].length
            const result = Module._VerifyGetChain(signaturePtr, certPtr, 0, certLength)

            let b
            if (result) b = null
            else b = Module._bEqualCert(certPtr, 0, certLength)

            if (result || b) {
              signer.isCertTrusted = true
              for (let f = 0; f < signer.certificateList.length; f++) {
                signer.certificateList[f].isTrusted = true
              }
            } else {
              needCheck = true
            }

            const fileLength = ComPDFKitJS.opened_files[0].length

            const signerResult = Module._VerifySigner(signaturePtr, signerPtr, 0, fileLength)
            if (signerResult === 1) {
              const signatureResult = Module._VerifyDocument(doc, signaturePtr, 0, fileLength)
              if (signatureResult === 1) {
                signer.isSignVerified = true
              } else {
                signer.isSignVerified = false
              }
            }
            DiffInfoArray = []
            Module._GetSignatureDiffInfo(signaturePtr)
            signer.diffInfo = DiffInfoArray
          }

          const timeSpanPtr = Module._GetTimeStampSignature(signerPtr)
          if (timeSpanPtr) {
            // TODO: 验证时间戳
            // result = Module._VerifyTimestamp(signaturePtr, signerPtr, 0, 0)
            // if (result == 1) {
            //   signer.IsSignVerified = true;
            // } else {
            //   signer.IsSignVerified = false;
            // }
          }

          signerList.push(signer)
        }
      }

      // 获取签名信息
      if (SignerArray.length) {
        Rect = {}
        Module._GetSignatureRect(doc, signaturePtr)
        signature.rect = {
          left: roundToDecimalPlaces(Rect.Left),
          top: roundToDecimalPlaces(Rect.Top),
          right: roundToDecimalPlaces(Rect.Right),
          bottom: roundToDecimalPlaces(Rect.Bottom)
        }
        Module._GetSignatureFieldName(signaturePtr)
        signature.fieldName = U8StringData
        Module._GetSignatureName(signaturePtr)
        signature.name = U8StringData || certificateList[0].subject[5] || ''
        Module._GetSignatureTime(signaturePtr)
        const rawDate = U8StringData
        signature.date = parseAdobePDFTimestamp(rawDate)
        Module._GetSignatureReason(signaturePtr)
        signature.reason = U8StringData
        Module._GetSignatureLocation(signaturePtr)
        signature.location = U8StringData
        signature.pageIndex = Module._GetSignaturePageIndex(doc, signaturePtr)
        signature.isDigital = 1
      } else {
        signature.isDigital = 0
      }

      signatures.push(signature)
    }
  }
  return { signatures, signaturePtrList }
}

function addToTrustedCertificates(data) {
  const { signerPtr } = data
  const certPtr = Module._GetSignCert(signerPtr)
  if (certPtr) {
    // 自动导出并验证证书
    CertArray = []
    const res = Module._ExportToFilePath(certPtr, 0)
    return res ? CertArray[0] : false
  }
  return false
}

async function copyDocument(doc, password) {
  DataArray = []

  const embedded = !!fontsJson || !!ComPDFKitJS.opened_DefFont[0]
  Module._SaveDocumentByStream(doc, 2, embedded);
  let pdfData = []
  for (let i = 0; i < DataArray.length; i++) {
    let subArray = new Uint8Array(DataArray[i])
    pdfData.push(subArray)
  }
  DataArray = []
  const blobData = new Blob(pdfData, { type: "application/pdf" })

  const buffer = await convertFileToBuffer(blobData)

  const newDoc = Module._InitDocument()
  const length = ComPDFKitJS.opened_files.length
  ComPDFKitJS.opened_files[length] = buffer

  const passwordPtr = stringToNewUTF8(password)
  Module._LoadDocumentByStream(newDoc, length, buffer.length, passwordPtr)
  return newDoc
}

function saveDocument(doc, saveType) {
  DataArray = []
  saveType === 1 && DataArray.push(ComPDFKitJS.opened_files[0])

  const embedded = !!fontsJson || !!ComPDFKitJS.opened_DefFont[0]
  Module._SaveDocumentByStream(doc, saveType, embedded)
  let pdfData = []
  for (let i = 0; i < DataArray.length; i++) {
    let subArray = new Uint8Array(DataArray[i])
    pdfData.push(subArray)
  }
  DataArray = []
  const blobData = new Blob(pdfData, { type: "application/pdf" })
  return blobData
}

async function saveDocumentByStream(data) {
  const { doc, saveType = 2, password, oldPassword } = data
  let blobData = null
  if (saveType === 2) {
    if (oldPassword === password || !password) {
      blobData = saveDocument(doc, saveType)
    } else if (password) {
      const newDoc = await copyDocument(doc, oldPassword)
      const result = setPassword({
        doc: newDoc,
        password
      })
      if (result && result.code) {
        return result
      }
      blobData = saveDocument(newDoc, saveType)
      ComPDFKitJS.opened_files.pop()
    } else {
      blobData = saveDocument(doc, saveType)
    }
  } else {
    blobData = saveDocument(doc, saveType)
  }
  return blobData
}

function licenseErrorMessage(feature) {
  const code = Module._GetComPDFkitLicenseErrorcode()
  let result = {
    code,
  }
  if (code) {
    switch (code) {
      case -100:
        result.message = 'The license is invalid'
        break
      case -101:
        result.message = 'The license is expired'
        break
      case -102:
        result.message = 'The license does not support the current platform'
        break
      case -103:
      case -104:
        result.message = 'The license does not support your domain'
        break
      case -105:
        result.message = 'The license does not support the ' + feature
        break
      case -106:
        result.message = 'License has not been initialized'
        break
      case -1000:
        result.message = 'Illegal access to the API interface'
        break
      case -1001:
        result.message = 'Failed to read license file'
        break
    }
  }
  return result
}

async function flattenPdfDownload(data) {
  const { doc, saveType = 2, password = '', oldPassword = '' } = data
  
  const newDoc = await copyDocument(doc, oldPassword)

  const pagesCount = Module._GetPageCount(newDoc)
  const pagesPtr = []
  for (let i = 0; i < pagesCount; i++) {
    const pagePtr = Module._GetPage(newDoc, i)
    pagesPtr.push(pagePtr)
  }

  for (let i = 0; i < pagesPtr.length; i++) {
    Module._FlattenPage(pagesPtr[i])
    const result = licenseErrorMessage('PDF Annotation FLATTEN')
    if (result.code) {
      ComPDFKitJS.opened_files.pop()
      return result
    }
  }

  let blobData = null

  if (password && password !== oldPassword) {
    const result = setPassword({
      doc: newDoc,
      password
    })
    if (result && result.code) {
      ComPDFKitJS.opened_files.pop()
      return result
    }

    blobData = saveDocument(newDoc, saveType)
  } else {
    blobData = saveDocument(newDoc, saveType)
  }
  ComPDFKitJS.opened_files.pop()
  return { blobData }
}

function exportXFDFByStream(doc) {
  XFDFData = {}
  Module._XFDFExportAnnotations(doc)
  const result = licenseErrorMessage('PDF Annotation XFDF')
  if (result.code) {
    return result
  }
  return XFDFData
}

function setAnnotCreationDate(data) {
  const { annotPtr, date: rawDate } = data

  const date = stringToNewUTF8(convertToPDFTimestamp(rawDate))
  const res = Module._SetAnnotCreationDate(annotPtr, date)
}

function setAnnotRect(data) {
  const { pagePtr, annotPtr, rect } = data
  const { left, right, bottom, top } = rect

  const result = Module._SetAnnotRect(pagePtr, annotPtr, left, right, bottom, top)
  return result
}

function setAnnotAuthor(data) {
  const { annotPtr, author: rawAuthor } = data

  const author = stringToNewUTF8(rawAuthor)
  const result = Module._SetAnnotAuthor(annotPtr, author)

  return result
}

function setAnnotTitle(data) {
  const { annotPtr, title: rawTitle } = data

  const title = stringToNewUTF8(rawTitle)
  const result = Module._SetAnnotTitle(annotPtr, title)

  return result
}

function setAnnotContent(data) {
  const { annotPtr, contents: rawContents } = data

  const contents = stringToNewUTF8(rawContents)
  const result = Module._SetAnnotContent(annotPtr, contents)

  return result
}

function setAnnotRGBColor(data) {
  const { annotPtr, color: rawColor } = data
  const color = convertColorToCppFormat(rawColor)

  const isSetColorSuccess = Module._SetAnnotRGBColor(annotPtr, color.R, color.G, color.B)
  return !!isSetColorSuccess
}

function setAnnotFilledRGBColor(data) {
  const { annotPtr, fillColor: rawColor } = data
  const color = convertColorToCppFormat(rawColor)

  const isSetColorSuccess = Module._SetAnnotFilledRGBColor(annotPtr, color.R, color.G, color.B)
  return !!isSetColorSuccess
}

function setAnnotTransparency(data) {
  const { annotPtr, opacity = 1 } = data

  const result = Module._SetAnnotTransparency(annotPtr, opacity)
  return result
}

function setAnnotBorderWidth(data) {
  const { annotPtr, borderWidth } = data

  Module._SetAnnotBorderWidth(annotPtr, borderWidth)
}

function SetAnnotFilledTransparency(data) {
  const { annotPtr, transparency = 1 } = data
  const result = Module._SetAnnotFilledTransparency(annotPtr, transparency)
  return result
}

function setQuadPoints(data) {
  const { pagePtr, annotPtr, quadPoints } = data
  const quadPointsArray = []
  for (let i = 0; i < quadPoints.length; i++) {
    const quadPoint = quadPoints[i]
    quadPointsArray.push(quadPoint.PointX, quadPoint.PointY)
  }

  const quadPointsArrayLength = quadPointsArray.length
  let quadPointsArrayBytes = quadPointsArrayLength * 4
  let quadPointsArrayPtr = _malloc(quadPointsArrayBytes)
  for (var i = 0; i < quadPointsArrayLength; i++) {
    Module.HEAPF32[quadPointsArrayPtr / 4 + i] = quadPointsArray[i]
  }

  const result = Module._SetQuadPoints(pagePtr, annotPtr, quadPointsArrayPtr, quadPointsArrayLength)
  _free(quadPointsArrayPtr)
  return result
}

function setLinePoint(data) {
  const { pagePtr, annotPtr, linePoints } = data

  const result = Module._SetLinePoint(pagePtr, annotPtr, linePoints[0], linePoints[1], linePoints[2], linePoints[3])
  return result
}

function setInkPointes(data) {
  const { pagePtr, annotPtr, inkPointes } = data

  const inkPointesArray = []
  const inklistArray = []
  const lineLength = inkPointes.length
  for (let i = 0; i < lineLength; i++) {
    const length = inkPointes[i].length
    for (let j = 0; j < length; j++) {
      inkPointesArray.push(inkPointes[i][j].PointX, inkPointes[i][j].PointY)
    }
    inklistArray.push(length)
  }

  const inklistArrayLength = inklistArray.length
  let inklistArrayBytes = inklistArrayLength * 4
  let inklistArrayPtr = _malloc(inklistArrayBytes)
  for (var i = 0; i < inklistArrayLength; i++) {
    Module.HEAP32[inklistArrayPtr / 4 + i] = inklistArray[i]
  }

  const inkPointesArrayLength = inkPointesArray.length
  let inkPointesArrayBytes = inkPointesArrayLength * 4
  let inkPointesArrayPtr = _malloc(inkPointesArrayBytes)
  for (var i = 0; i < inkPointesArrayLength; i++) {
    Module.HEAPF32[inkPointesArrayPtr / 4 + i] = inkPointesArray[i]
  }

  const res = Module._SetInkPath(pagePtr, annotPtr, inklistArrayPtr, lineLength, inkPointesArrayPtr)
  _free(inkPointesArrayPtr)
}

function setLinkDest(data) {
  const { doc, annotPtr, destPage, pageHeight } = data
  Module._SetLinkDestination(doc, annotPtr, destPage - 1, 0, pageHeight, 0)
}

function setLinkUri(data) {
  const { annotPtr, url: rawUrl } = data
  const url = stringToNewUTF8(rawUrl)
  const res = Module._SetLinkUri(annotPtr, url)
}

function setAnnotHidden(data) {
  const { annotPtr, isHidden } = data

  let flag = 0
  if (isHidden == 1) {
    flag = 2
  } else {
    flag = 4
  }
  const result = Module._SetAnnotFlag(annotPtr, flag)
  return result
}

function setCheckBoxIsChecked(data) {
  const { annotPtr, isChecked } = data

  Module._SetCheckBoxIsChecked(annotPtr, isChecked)

  const result = licenseErrorMessage('PDF FORM FILL')
  if (result.code) {
    return result
  }
}

function setRadioButtonIsChecked(data) {
  const { annotPtr, isChecked } = data

  Module._SetRadioButtonIsChecked(annotPtr, isChecked)

  const result = licenseErrorMessage('PDF FORM FILL')
  if (result.code) {
    return result
  }
}

function setCheckStyle(data) {
  const { annotPtr, checkStyle } = data

  const result = Module._SetWidgetCheckStyle(annotPtr, checkStyle)
}

function setWidgetBackgroundColor(data) {
  const { annotPtr, backgroundColor: rawBackgroundColor } = data
  const backgroundColor = convertColorToCppFormat(rawBackgroundColor)

  const result = Module._SetWidgetBackgroundRGBColor(annotPtr, backgroundColor.R, backgroundColor.G, backgroundColor.B)
}

function setWidgetBorder(data) {
  const { annotPtr, borderColor: rawBorderColor, borderWidth, borderStyle } = data
  if (rawBorderColor) {
    const borderColor = convertColorToCppFormat(rawBorderColor)
    Module._SetWidgetBorderRGBColor(annotPtr, borderColor.R, borderColor.G, borderColor.B)
  }
  if (borderStyle) {
    const borderStyleEnum = BorderStyleInt[borderStyle]
    Module._SetWidgetBorderStyle(annotPtr, borderStyleEnum)
  }
   (borderWidth === 0 || borderWidth) && setAnnotBorderWidth({
    annotPtr,
    borderWidth
  })
}

function setWidgetFieldName(data) {
  const { annotPtr, fieldName: rawFieldName } = data
  const fieldName = stringToNewUTF8(rawFieldName)

  Module._SetWidgetFieldName(annotPtr, fieldName)
}

function setAnnotFontData(data) {
  const { annotPtr, fontName: rawFontName, fontSize, color: rawColor } = data
  const fontName = stringToNewUTF8(rawFontName)
  const color = convertColorToCppFormat(rawColor)

  const result = Module._SetAnnotFontDa(annotPtr, fontName, fontSize, color.R, color.G, color.B)
}

function setAnnotSelected(data) {
  const { annotPtr, selected } = data

  Module._SetItemsSelectedIndex(annotPtr, selected)

  const result = licenseErrorMessage('PDF FORM FILL')
  if (result.code) {
    return result
  }
}

function setPushButtonDest(data) {
  const { doc, annotPtr, destPage, pageHeight, actionType } = data
  const actionPtr = Module._InitAction()
  Module._SetActionType(actionPtr, actionType)
  if (destPage && actionType == 1) {
    Module._SetDestination(actionPtr, doc, destPage - 1, 0, pageHeight, 0)
    Module._SetButtonAction(annotPtr, actionPtr)
    Module._ClearAction(actionPtr)
  }
}
function setPushButtonUri(data) {
  const { annotPtr, url, actionType } = data
  const actionPtr = Module._InitAction()
  Module._SetActionType(actionPtr, actionType)
  if (url && actionType == 6) {
    const UriPath = stringToNewUTF8(url)
    Module._SetUriPath(actionPtr, UriPath)
    Module._SetButtonAction(annotPtr, actionPtr)
    Module._ClearAction(actionPtr)
  }
}

function setTextFieldContent(data) {
  const { annotPtr, contents: rawContents } = data
  const contents = stringToNewUTF8(rawContents)
  Module._SetTextFieldContent(annotPtr, contents)

  const result = licenseErrorMessage('PDF FORM FILL')
  if (result.code) {
    return result
  }
}

function addWidgetItem(data) {
  const { annotPtr, item } = data
  const { index, value: rawValue } = item
  const value = stringToNewUTF8(rawValue)
  Module._AddOptionItem(annotPtr, index, value, value)
}

function removeWidgetItem(data) {
  const { annotPtr, index } = data
  Module._RemoveOptionItem(annotPtr, index)
}

function setWidgetItems(data) {
  const { annotPtr, moveOption } = data
  const { fromIndex, toIndex, item } = moveOption
  removeWidgetItem({
    annotPtr,
    index: fromIndex
  })

  addWidgetItem({
    annotPtr,
    item: {
      value: item.Value,
      index: toIndex
    }
  })
}

function deleteWidgetItem(data) {
  const { annotPtr, deleteItemIndex } = data
  const res = Module._RemoveOptionItem(annotPtr, deleteItemIndex)
}

function createMarkup(data) {
  const { pagePtr, annotPtr, color, quadPoints } = data
  setAnnotRGBColor({
    annotPtr,
    color
  })

  setQuadPoints({
    pagePtr,
    annotPtr,
    quadPoints
  })
}

function setFreeText(data) {
  const { annotPtr, fontName: rawFontName, fontSize, color: rawColor, textAlignment, contents: rawContents } = data
  const textAlignmentInt = ALIGNMAP[textAlignment.toUpperCase()]
  const fontName = stringToNewUTF8(rawFontName)
  const contents = stringToNewUTF8(rawContents)

  const color = convertColorToCppFormat(rawColor)

  Module._SetFreeText(annotPtr, fontName, fontSize, color.R / 255, color.G / 255, color.B / 255, textAlignmentInt, contents)
}

function createShape(data) {
  const { annotPtr, borderWidth } = data
  setAnnotBorderWidth({
    annotPtr,
    borderWidth
  })
}

function createStandardStamp(data) {
  const { annotPtr, contents } = data

  const rawContents = stringToNewUTF8(contents.replace(/ /g, ''))
  Module._SetStandardStamp(annotPtr, rawContents, 0)
  const result = licenseErrorMessage('PDF Annotation STAMPS')
  if (result.code) {
    return result
  }
}

function createTextStamp(data) {
  const { annotPtr, contents: rawContents, time: rawTime, stampShape, stampColor } = data

  const contents = stringToNewUTF8(rawContents)
  const time = stringToNewUTF8(rawTime)

  Module._SetTextStamp(annotPtr, contents, time, stampShape, stampColor, 0)
  const result = licenseErrorMessage('PDF Annotation STAMPS')
  if (result.code) {
    return result
  }
}

function createImageStamp(data) {
  const { annotPtr, imageBase64, stampShape, rect, doc } = data
  const imageData = convertBase64ToBytes(imageBase64)

  setAnnotContent({
    annotPtr,
    contents: 'image'
  })
  // const width = parseInt(564)
  // const height = parseInt(82)
  // let imageBufferPtr = _malloc(imageData.length)
  // Module.HEAPU8.set(imageData, imageBufferPtr)

  // const res = Module._SetImageStampWithPixel(annotPtr, imageBufferPtr, width, height, 0)

  ComPDFKitJS.opened_image = []
  ComPDFKitJS.opened_image[0] = imageData
  // const res = Module._InsertPageWithImageByStream(doc, 0, width, height, imageData, imageData.length, 0)
  Module._SetImageStampByStream(annotPtr, imageData, imageData.length, 0)
  
  const result = licenseErrorMessage('PDF Annotation STAMPS')
  if (result.code) {
    return result
  }
  // _free()
}

function extractImage(data) {
  const { editAreaPtr, width, height, stride } = data
  let pixelNum = parseInt(width) * parseInt(height)
  let imageptr = _malloc(stride * height)
  for (var i = 0; i < pixelNum; i++) {
    Module.HEAP32[imageptr / 4 + i] = 0
  }

  Module._GetImageAreaData(editAreaPtr, imageptr)

  let imageArray = new Uint8Array(stride * height)
  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      let srcOffset = y * stride + x * 4 // assuming 4 bytes per pixel (RGBA)
      let destOffset = y * width * 4 + x * 4 // for RGBA format
      imageArray[destOffset] = Module.HEAPU8[imageptr + srcOffset + 2] // R
      imageArray[destOffset + 1] = Module.HEAPU8[imageptr + srcOffset + 1] // G
      imageArray[destOffset + 2] = Module.HEAPU8[imageptr + srcOffset] // B
      imageArray[destOffset + 3] = Module.HEAPU8[imageptr + srcOffset + 3] // A
    }
  }

  _free(imageptr)
  return imageArray
}

function getSignatureAlgorithmType(signAlgOid) {
  switch (signAlgOid) {
    case "1.2.840.113549.1.1.1":
      return 'RSA_RSA';

    case "1.2.840.113549.1.1.2":
      return 'MD2RSA';

    case "1.2.840.113549.1.1.3":
      return 'MD4RSA';

    case "1.2.840.113549.1.1.4":
      return 'MD5RSA';

    case "1.2.840.113549.1.1.5":
      return 'SHA1RSA';

    case "1.2.840.113549.1.1.11":
      return 'SHA256RSA';

    case "1.2.156.10197.1.501":
      return 'SM3SM2';

    default:
      return 'RSA_RSA';
  }
}

async function getSignCertData(certPtr) {
  const certDataKey = ["C", "ST", "L", "O", "OU", "CN", "email"]
  const version = Module._GetSignVersion(certPtr)
  Module._GetSignAlgOid(certPtr)
  const signAlgOid = U8StringData
  const signAlgOidType = getSignatureAlgorithmType(signAlgOid)
  const subject = {}
  for (let i = 0; i < 7; i++) {
    Module._GetSignSubject(certPtr, i)
    subject[certDataKey[i]] = U8StringData
  }
  const issuer = []
  for (let i = 0; i < 7; i++) {
    Module._GetSignIssuer(certPtr, i)
    issuer[certDataKey[i]] = U8StringData
  }
  Module._GetSignSerialNumber(certPtr)
  const serialNumber = U8StringData
  Module._GetSignValidDate(certPtr, 0)
  const validDateStart = U8StringData
  Module._GetSignValidDate(certPtr, 1)
  const validDateEnd = U8StringData

  AccessInfoArray = []
  Module._GetAuthorityInfoAccess(certPtr)
  const sccessInfo = AccessInfoArray
  Module._GetSubjectKeyIdentifier(certPtr)
  const subjectKeyIdentifier = U8StringData
  const keyUsage = Module._GetKeyUsage(certPtr)
  PolicyArray = []
  Module._GetCertificatePolicies(certPtr)
  const certificatePolicies = PolicyArray
  Module._GetAuthorityKeyIdentifier(certPtr)
  const authorityKeyIdentifier = U8StringData
  CRLPointArray = []
  Module._GetCRLDistributionPoints(certPtr)
  const CRLDistributionPoints = CRLPointArray

  Module._GetBasicConstraints(certPtr)
  const basicConstraints = U8StringData
  Module._GetPublicKey(certPtr)
  const publicKey = U8StringData
  Module._GetX509Data(certPtr)
  const X509Data = U8StringData
  Module._GetSHA1Digest(certPtr)
  const SHA1Digest = U8StringData
  Module._GetMD5Digest(certPtr)
  const MD5Digest = U8StringData

  OCSPUrlArray = []
  Module._GetOCSPUrl(certPtr)
  const ocspURL = OCSPUrlArray

  return {
    version,
    signAlgOid,
    signAlgOidType,
    subject,
    issuer,
    serialNumber,
    validDateStart,
    validDateEnd,
    sccessInfo,
    subjectKeyIdentifier,
    keyUsage,
    certificatePolicies,
    authorityKeyIdentifier,
    CRLDistributionPoints,
    basicConstraints,
    publicKey,
    X509Data,
    SHA1Digest,
    MD5Digest,
    ocspURL
  }
}

function updateDigitalSignAp (data) {
  const { annotPtr, content: rawContent, isDrawOnlyContent = false, isDrowLogo = false, isContentAlginLeft = false, text: rawText, logoBase64 = '', imageBase64 = '', } = data

  const content = stringToNewUTF8(rawContent)
  const text = stringToNewUTF8(rawText)
  ComPDFKitJS.opened_image = []
  let logoId = ComPDFKitJS.opened_image.length
  let logoBufferLen = 0
  if (logoBase64) {
    const logoBuffer = convertBase64ToBytes(logoBase64)
    ComPDFKitJS.opened_image[logoId] = logoBuffer
    logoBufferLen = logoBuffer.length
  }
  let imageId = ComPDFKitJS.opened_image.length
  let imageBufferLen = 0
  if (imageBase64) {
    const imageBuffer = convertBase64ToBytes(imageBase64)
    ComPDFKitJS.opened_image[imageId] = imageBuffer
    imageBufferLen = imageBuffer.length
  }
  const res = Module._UpdateApWithDigitalSigConfig(annotPtr, content, isDrawOnlyContent, isDrowLogo, isContentAlginLeft, text, logoId, logoBufferLen, imageId, imageBufferLen, 0, 0, 0, 0, 0, 0)
}
function getReplyArray(pagePtr, annotPtr) {
  AnnotArray = []
  const replies = []

  Module._GetReplies(pagePtr, annotPtr)

  let reviewAnnotState, markedAnnotState
  for (let i = 0; i < AnnotArray.length; i++) {
    const replyPtr = AnnotArray[i]
    const reply = {
      annotPtr: replyPtr,
      repliesIndex: i + 1,
    }
    if (Module._IsReviewStateAnnot(replyPtr)) {
      stateInt = Module._GetState(replyPtr)
      reviewAnnotState = {
        annotPtr: replyPtr,
        state: AnnotationStateString[stateInt]
      }
      continue
    }
    if (Module._IsMarkedStateAnnot(replyPtr)) {
      stateInt = Module._GetState(replyPtr)
      markedAnnotState = {
        annotPtr: replyPtr,
        state: AnnotationStateString[stateInt]
      }
      continue
    }

    Module._GetAnnotAuthor(replyPtr)
    reply.author = U8StringData
    Module._GetAnnotCreationDate(replyPtr)
    const rawDate = U8StringData
    reply.date = parseAdobePDFTimestamp(rawDate)
    Module._GetAnnotContent(replyPtr)
    reply.contents = U8StringData
    Module._GetAnnotTitle(replyPtr)
    reply.title = U8StringData
    
    replies.push(reply)
  }

  return { replies, reviewAnnotState, markedAnnotState }
}

function createReplyAnnotation(pagePtr, annotPtr, reply) {
  const { author, date, contents, title } = reply

  const replyPtr = Module._CreateReplyAnnotation(pagePtr, annotPtr)

  if (author) {
    setAnnotAuthor({
      annotPtr: replyPtr,
      author
    })
  }
  if (contents) {
    setAnnotContent({
      annotPtr: replyPtr,
      contents
    })
  }
  if (date) {
    setAnnotCreationDate({
      annotPtr: replyPtr,
      date
    })
  }
  if (title) {
    setAnnotTitle({
      annotPtr: replyPtr,
      title
    })
  }
}
