Use orca in node application to export images in pdf

I am making a node-electron application that uses plotly.js for plotting. Now to export as pdf or eps I need to use orca. But I cant figure out how to use orca programmetically to save an image. I have tried using https://github.com/plotly/orca#api-usage but it shows

Uncaught TypeError: Cannot read property 'commandLine' of undefined

So, how do I use this?
Also I have some additional queries:

  1. Do I have to use electron@1.8.4 to use orca ?
  2. I just want orca to save the image and its already in a electron application, so do I have to use the full orca suit, or is there something just can just let me use the pdf exporter?

How are you running your script? Using node main.js or electron main.js? You should be using the latter.

No, other electron versions should work. Version 1.8.4 is the one we’ve done our performance testing on, so it’s the one we recommend.

I’m not sure what you mean by using, using as in installing from npm, or using as in invoked at runtime?

I’m running as (using a npm script) as yarn electron .. Now let’s say I’m on the renderer process and graphDiv is my plotly plot container so graphDiv.data and graphDiv.layout has my data and layout. So, what should I do to export it using orca. Is this okay to do ,

const orca = require('orca/src')
stt = `{data: ${JSON.stringify(graphDiv.data)}, layout: ${JSON.stringify(graphDiv.layout)}}`
const app = orca.run({
  component: 'plotly-graph',
  input: stt
  debug: true
})

app.on('after-export', (info) => {
  fs.writeFile('output.pdf', info.body, (err) => console.warn(err))
})

How do I define the options here like width, height etc.?

Instead of

stt = `{data: ${JSON.stringify(graphDiv.data)}, layout: ${JSON.stringify(graphDiv.layout)}}`

try

stt = {
  figure: {
    data: graphDiv.data,
    layout: graphDiv.layout
  },
  format: 'pdf',
  width: /* your width */,
  height: /* your height */
}

Still getting the same error
Uncaught TypeError: Cannot read property 'commandLine' of undefined

Here’s a simple demo

that should help you get started.

So I did some checks. I have two js files main.js and myfile.js. Electron runs the main.js in start (as usual) and also opens the actual app window which (i.e. the renderer process) runs the myfile.js. Now it turns out If I put

const orca = require('orca/src')
const fs = require('fs')

const app = orca.run({
  component: 'plotly-graph',
  input: JSON.stringify({
    figure: {
      data: [{y: [2, 1, 2]}],
      layout: {title: {text: 'My graph'}}
    },
    format: 'pdf',
    width: 800,
    height: 500
  }),
  debug: process.env.DEBUG
})

app.on('after-export', (info) => {
  fs.writeFile('out.pdf', info.body, (err) => { if(err) console.warn(err) })
})

// other available events:
app.on('after-export-all', () => {})
app.on('export-error', (info) => console.warn(info.msg))
app.on('renderer-error', (info) => console.warn(info.msg))

this part of the code directly in main.js then electron opens the window, outputs the output.pdf closes the window quickly.
If I put the code in myfile.js (i.e in renderer process) it doesn’t work throws the prvious error.
If I keep the part of the code (in a function) in the index.js and calls the function from the renderer process using ipcRenderer.send it doesn’t gives any output but also doesn’t throws any error.
So, I need keep that code block in main process because it doesn’t work in renderer process, also I can’t run this using inter processes communication calls. So, how do I do it? Am I doing something wrong?

Nice, that’s what orca.run is expected to do.

orca.run is intended to be called in the main process. No surprises both these trials didn’t work.

If you’re looking to set your own IPC callbacks and just want to use orca’s plotly-graph renderer, you can try importing and calling

But please note that you’re beyond the current official API.

Well, I will try to check this out but probably wait for this to resolve. You should consider this situation with orca

I’m not sure what this refers to here. We would appreciate if you wrote down a bit more details about the application you’re trying to build. Thanks!

You may want to open a feature request: https://github.com/plotly/orca/issues

I am developing a data analysis application (for my research work in computational chemistry) and it uses Plotly for visualization purposes. The application is basically a simple web page with plotly.js and I bundled up everything in an electron application for easy sharing with my colleagues. Now plotly.js can save images in jpg, png, svg and webp. That’s fine but everyone wants to save the image in pdf or eps as that’s like an unofficial standard for plots in my research field. Orca can export in pdf and eps so I wanted to integrate orca in my application for that purpose. But orca seems to only work in main process, whereas the electron application does all the analysis and visualization in the renderer process. So, I can’t use orca in my application, not now at least.

Wow, that’s cool. Like I said previously, in this case I would try to be recycle orca’s plotly-graph render routine for this purpose.

In brief, to export a PDF from an electron render process, you need:

  • call Plotly.image(gd, {fomat: 'svg'})
  • then call
  • grab the PDF data

I will definitely give this a try and let you know here. Thanks.

Well I managed to export the pdf with the following code


function exportPDF({ width=1920, height=1080}={}){
    return new Promise((resolve,reject)=>{ 
        var printOpts = {
            marginsType: 1,
            printBackground: true,
            pageSize: {
                width: (width + 6) / 0.00377957517575025,
                height: (height + 6) / 0.00377957517575025
            }
        }
        var html =  window.encodeURIComponent(`<!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <style>
            body {
                margin: 0;
                padding: 0;
                background-color: 'rgba(0,0,0,0)'
            }
            </style>
        </head>
        <body><img/></body>
        </html>`);
        Plotly.toImage(figurecontainer, {fomat: 'svg', width, height}).then(imgdata => {
            var win = new remote.BrowserWindow({width, height, show: false})
            win.on('closed', () => {win = null})
            win.loadURL(`data:text/html,${html}`);

            win.webContents.executeJavaScript(`new Promise((resolve, reject) => {
                const img = document.body.firstChild
                img.onload = resolve
                img.onerror = reject
                img.src = "${imgdata}"
                setTimeout(() => reject(new Error('too long to load image')), ${3000})
                })`).then(()=>{
                    win.webContents.printToPDF(printOpts, (err, pdfData) => { 
                        win.close();
                        if(err) {
                            reject(err)
                        }else{
                            // fs.writeFileSync(fileName, pdfData)
                            // resolve(`PDF file ${fileName} export done`)
                            resolve(pdfData)
                        }
                    })
                }).catch((err) => {
                    reject(err)
                    win.close()
                })
        })
    })
}

But the pdfs generated seems to be of low quality and raster file. They get pixelated after a simple zoom. Is this how it should be ? or am I exporting it wrongly?

There’s a typo in this line:

Plotly.toImage(figurecontainer, {fomat: 'svg', width, height})

This should be format: 'svg' - without this toImage returns a PNG.

Well, that was pretty embarrassing :sweat_smile:, now it works perfectly. Thanks for resolving this.

No worries!! Thank you for taking orca to new heights!

Note to any one stumbling upon this, from version 6.0.0 electron’s printToPDF returns a promise