ubjs/packages/ub/virtualStores/fileVirtualWritePDF.js

/**
 * @classdesc
 * File store with automatic conversion MS Word, MS Excel and MS Power Point documents into \*.pdf format.
 *
 * Require MS Office installed on the server - see <a href="https://enviance.softline.kiev.ua/confluence/pages/viewpage.action?pageId=65274525#Installation(requirements)-UsingMicrosoftWordasservice"> requirement </a>
 *
 * @class
 * @extends UB.virtualStores.fileVirtual
 * @singleton
 */
UB.virtualStores.fileVirtualWritePDF = Object.create(UB.virtualStores.fileVirtual)
/**
 * Save content to temp store and convert it to \*.pdf if content is MS Word, MS Excel or MS Power Point document.
 * If file has been converted then original file and original \*.ubfti file is in the same temp directory path with
 * the same name but with \*.orig extension
 *
 * See {@link UB.virtualStores.fileVirtual#saveContentToTempStore} for details
 */
UB.virtualStores.fileVirtualWritePDF.saveContentToTempStore = function (handler) {
  var
    word2Pdf = function (message) {
      var wdExportFormatPDF = 17,
        w = require('UBComBridge').createCOMObject('Word.Application'),
        winApi = require('winApi'),
        winCaption = createGuid(),
        ffi_winCaption = new winApi.types.PWideChar(),
        ffi_processID = new winApi.types.PDWORD(1)

      w.caption = winCaption
      ffi_winCaption.setStr(winCaption)
      winApi.user32.GetWindowThreadProcessId(winApi.user32.FindWindowW(winApi.nil, ffi_winCaption), ffi_processID)
      if (ffi_processID.get() === 0) throw new Error(winApi.kernel32.GetLastError())

      postMessage({processID: ffi_processID.get()})

      w.DisplayAlerts = 0
      w.CheckLanguage = false
      w.Options.CheckSpellingAsYouType = false

      var doc = w.Documents.Open({
        filename: message.inFileName,
        ConfirmConversions: false,
        readonly: true,
        AddToRecentFiles: false,
        Visible: false,
        NoEncodingDialog: true,
        Revert: true,
        OpenAndRepair: true
      })
      doc.ExportAsFixedFormat({
        ExportFormat: wdExportFormatPDF,
        OutputFileName: message.outFileName
      })
      doc.close({SaveChanges: false})
      doc = null
      w.Quit()
      w = null
      postMessage({done: true})
      terminate()
    },
    excel2Pdf = function (message) {
      var
        xlTypePDF = 0, xlQualityStandard = 0,
        e = require('UBComBridge').createCOMObject('Excel.Application'),
        wb,
        winApi = require('winApi'),
        ffi_processID = new winApi.types.PDWORD(1)
      winApi.user32.GetWindowThreadProcessId(e.Hwnd, ffi_processID)
      postMessage({processID: ffi_processID.get()})
      wb = e.Workbooks.Open(message.inFileName)
      wb.ExportAsFixedFormat({
        Type: xlTypePDF,
        Filename: message.outFileName,
        Quality: xlQualityStandard,
        IncludeDocProperties: true,
        IgnorePrintAreas: false,
        OpenAfterPublish: false
      })
      wb.close(false)
      wb = null
      e.quit()
      e = null
      postMessage({done: true})
      terminate()
    },
    pp2Pdf = function (message) {
      var ppSaveAsPDF = 32,
        p = require('UBComBridge').createCOMObject('PowerPoint.Application'),
        winApi = require('winApi'),
        winCaption = createGuid(),
        ffi_winCaption = new winApi.types.PWideChar(),
        ffi_processID = new winApi.types.PDWORD(1)
      p.DisplayAlerts = 0

      p.caption = winCaption
      ffi_winCaption.setStr(winCaption)
      winApi.user32.GetWindowThreadProcessId(winApi.user32.FindWindowW(winApi.nil, ffi_winCaption), ffi_processID)
      if (ffi_processID.get() === 0) throw new Error(winApi.kernel32.GetLastError())

      postMessage({processID: ffi_processID.get()})

      var pr = p.Presentations.Open({
        filename: message.inFileName,
        readonly: true,
        WithWindow: false
      })
      pr.SaveAs({
        Filename: message.outFileName,
        FileFormat: ppSaveAsPDF
      })
      pr.close()
      pr = null
      p.quit()
      p = null
      postMessage({done: true})
      terminate()
    }

  var
    convertors = {
      'application\/msword': word2Pdf,
      'application\/vnd.openxmlformats-officedocument.wordprocessingml.document': word2Pdf,
      'application\/vnd.ms-excel': excel2Pdf,
      'application\/vnd.openxmlformats-officedocument.spreadsheetml.sheet': excel2Pdf,
      'application/vnd.ms-powerpoint': pp2Pdf,
      'application/vnd.openxmlformats-officedocument.presentationml.presentation': pp2Pdf
    },
    fs = require('fs'),
    path = require('path'),
    fn = this.saveContentToTempFileStore(handler),
    fnUbfti = fn + '.ubfti',
    convertor = convertors[handler.content.ct]
  if (convertor) {
    var
      origFn = fn + '.orig',
      origFnUbfti = fnUbfti + '.orig'
    console.debug('fileVirtualWritePDF: Start transformation of file', origFn, 'to PDF')
    moveFile(fn, origFn)
    moveFile(fnUbfti, origFnUbfti)
    var
      Worker = require('@unitybase/base/worker'),
      w = new Worker({
        onmessage: convertor,
        onerror: function (message, error) { postMessage({convertFailed: true}); terminate() },
        message: {
          inFileName: origFn,
          outFileName: fn
        }
      }),
      mes,
      processID,
      processHandle,
      winApi = require('winApi'),
      PROCESS_ALL_ACCESS = 0x1FFFFF
    if (mes = w.waitMessage(5000, 100)) {
      if (mes.convertFailed)
              { throw new Error('<<<convertationToPDFAborted>>>') }
      processID = mes.processID
    }
    if ((mes = w.waitMessage(20000, 1000)) && (mes.done)) {
      console.debug('fileVirtualWritePDF: file', fn, 'successfully converted to PDF. Saving...')
      moveFile(fn + '.pdf', fn)
      handler.request.loadBodyFromFile(fn)
      handler.loadContentFromRequest(0)
      handler.content.fName = handler.content.fName.slice(0, -path.extname(handler.content.fName).length) + '.pdf'
      handler.content.origName = handler.content.fName.slice(0, -path.extname(handler.content.origName).length) + '.pdf'
      handler.content.ct = 'application/pdf'
      handler.content.isDirty = true
      fs.writeFileSync(fnUbfti, handler.content)
      console.debug('fileVirtualWritePDF: file', fn, 'converted to PDF and saved')
      return fn
    }
    if (processID) {
      processHandle = winApi.kernel32.OpenProcess(PROCESS_ALL_ACCESS, 0, processID)
      if (processHandle === 0) {
        console.error(winApi.kernel32.GetLastError())
      }
      winApi.kernel32.TerminateProcess(processHandle, 100)
      winApi.kernel32.CloseHandle(processHandle)
    }
    w.terminate()
    throw new Error('<<<convertationToPDFAborted>>>')
  } else {
    console.debug('fileVirtualWritePDF: SKIP transformation of file', origFn, 'to PDF - unknown format')
  }
}