Launching ZetaJS for ZetaOffice

Today allotropia has launched the ZetaOffice range of products at the SFSCON in South Tyrol. ZetaOffice is a LibreOffice Technology built & designed for professional use in the browser, on the desktop and on mobile.

We are excited to additionally announce a massively improved way for which LibreOffice Technology can be used fully client-side on the web. As an additional building block, we have developed the ZetaJS wrapper, which enables convenient embedding and automating WASM (WebAssembly) builds of ZetaOffice via JavaScript. With that, all of the LibreOffice Technology APIs and features are available to web applications – and by leveraging WASM, which runs ZetaOffice client-side, no server or cloud services are needed. All processing is taking place on the client browser, which minimizes latencies & load (of course, a minimal static delivery of web application code, assets and the WASM binary is still needed, but that’s extremely light-weight). 

Examples

Let’s look at some simple examples to give you an idea, how easy ZetaOffice integration is. All comprise of an HTML and a JavaScript file. A ZetaOffice WASM build will automatically be included from the following URL. To replace it with a custom WASM build see config.sample.js of each demo.

https://cdn.zetaoffice.net/zetaoffice_latest/

Next you need to upload the zetajs/ folder onto a webserver of your choice, which sets the following HTTP headers (see developer.mozilla.org for further details):

Cross-Origin-Opener-Policy "same-origin"
Cross-Origin-Embedder-Policy "require-corp"

So back to the example code. The HTML files for all examples embed ZetaOffice and some JavaScript loading code. Please check the actual JavaScript file for the code interacting with ZetaOffice.

Lets have a look at the simple.html (see live). ZetaOffice displays its content using an HTML canvas. So in line 14 we initialize this canvas. Currently a list of attributes like is needed for the canvas. But we will migrate those attributes to the ZetaJS wrapper, so they won’t be needed anymore in the HTML code.

<canvas
  id="qtcanvas" contenteditable="true"
  oncontextmenu="event.preventDefault()" onkeydown="event.preventDefault()"
  style="height:100%; width:100%; border:0px none; padding:0;"/>

The Module variable on line 30 passes the information needed to initialize WASM binaries. First is the canvas. And second is an array of JavaScript files which will be executed in the main Web Worker running the WASM binary. Web Workers are a process like feature of the browsers WASM runtime environment. We pass the ZetaJS wrapper and a file with custom JavaScript code, in this example the simple.js. You may need to ensure, that the zeta.js is reachable under the given URL path.

Line 33 to 39 preload the soffice.js file to ensure, it’s not being blocked by the browsers origin policy when loaded from a foreign origin. Line 42 triggers a website resize event, to make ZetaOffice display nicely inside the canvas. This can be done more precise, as shown in the more complex demos. But for the start the resize event will be triggered after a fixed interval. And finally the soffice.js document is finally loaded which triggers the start of the WASM binary.

Second is the simple.js file. It’s running inside the same Web Worker as the WASM binary to enable interaction. When running in Chromium / Google Chrome you will find a dropdown list labeled “top” at the upper left of the “Console” tab in the developer tools. There you can select the em-pthread_1 Web Worker to debug code in the simple.js file.

Inside the simple.js you will find pretty much the same code as when controlling a LibreOffice running naively on Linux, Windows or any other native OS. It is using LibreOffice’s UNO interface. Most existing examples using UNO via Python or Basic can be easily moved to JavaScript.

The control flow is being passed by the Module.zetajs.thenwhich gets called as soon as the WASM binary is loaded. It passes the zetajs object from which we first get the common com.sun.star object (do not confuse it’s abbreviation css with HTML CSS). In the lines 11 to 21 we get some control objects via UNO, which allow us to trigger the load of an example office document example.odt which is embedded in the WASM binary.

Module.zetajs.then(function(zetajs) {
  function getTextDocument() {
    const css = zetajs.uno.com.sun.star;
    const context = zetajs.getUnoComponentContext();
    const desktop = css.frame.Desktop.create(context);
    let xModel = desktop.getCurrentFrame().getController().getModel();
    if (xModel === null
      || !zetajs.fromAny(
        xModel.queryInterface(zetajs.type.interface(css.text.XTextDocument))))
    {
      xModel = desktop.loadComponentFromURL(
        'file:///android/default-document/example.odt', '_default', 0, []);
    }
    const toolkit = css.awt.Toolkit.create(context);

Line 27 is where the actual application logic starts. In this simple example we get a cursor object from the document to insert the text string here! at the top. In the final section from line 32 to 38 each paragraph of the office document becomes colored in a random color.

    const xText = xModel.getText();
    const xTextCursor = xText.createTextCursor();
    xTextCursor.setString("string here!");
  }
  {
    const xModel = getTextDocument();
    const xText = xModel.getText();
    const xParaEnumeration = xText.createEnumeration();
    for (const next of xParaEnumeration) {
      const xParagraph = zetajs.fromAny(next);
      const color = Math.floor(Math.random() * 0xFFFFFF);
      xParagraph.setPropertyValue("CharColor", color);
    }

This other simple-examples/ show you a little more interesting tasks you can do with the same basic techniques as shown here. While the HTML files are all the same, the simple_key_handler.js (see live) shows you how to register to ZetaOffice event handlers. And finally rainbow_writer.js (see live) uses this to implement a small tool coloring text as you write it.

More Complex Examples

The next big step is in the standalone/ (see live) example. It adds a nice loading animation and shows you how to pass messages between the WASM Web Worker and the browsers main thread, handling the HTML page. This is being used to implement some simple controls on the HTML page for formatting text inside ZetaOffice. The demo is build as a npm package and can be run according to the contained README.md. Don’t forget to pass an URL to the soffice_base_url variable as explained above!

Additional examples are vuejs3-ping-tool/ (see live) and letter-address-tool/ (see live). The vuejs3-ping-tool/is again a npm package, and show-cases how to automatically fill spreadsheets documents with values, displaying them in nicely animated Calc charts. The other letter-address-tool/ example gives you an impression how to connect ZetaOffice with external data sources to automatically create letters from templates, and export the result as office document or PDF file.

Please share your feedback as a comment in the blog, or use the GitHub issue tracker for suggestions or bugs in the code!

Leave a comment