Exporting Images/PDF

Size

The exported image size in pixel is calculated by:

chart width x scale = exported width

chart height x scale = exported height

If you do not specify a chart’s width and height, ZK Charts exports with the default size:

  • width, 600 pixels
  • height, 400 pixels

Because the default scale is 2, the exported image will be 1200 pixels (width) x 800 pixels (height).

If you specify a chart’s width/height (or hflex/vflex), e.g. 400 pixels wide and 200 pixels high, then the exported image will be 800 pixels wide and 400 pixels high.

Differences between a Chart on Screen and Exported One

Because the exported size might be different from the chart’s size on your screen, so the exported chart image might look different from your screen.

For example, export-images.zul, the original chart on the screen:

The exported result:

Differences:

  1. All x-axis labels are rotated.
  2. The y-axis tick interval changes.

The differences are caused by the different size between an exported image (600 x 400) and on-screen one (1,540 × 286). Because the exported one is smaller, ZK Charts automatically adjusts axis tick labels according to the width/height, so

  1. It rotates all x-axis labels because the exported chart is narrower.
  2. The y-axis tick interval changes because the exported chart is shorter. ZK Charts automatically removes some axis labels if they are too dense to be drawn.

To avoid such image difference generated by auto calculation upon size, you may specify explicit chart options like:

chart.getExporting().setSourceWidth(1500); //fix difference 1.
chart.getYAxis().setTickInterval(10); //fix difference 2.

Limitation

ZK Charts cannot export an HTML image inserted in a chart.

By JSON, Offline

Highcharts provides a node.js export server, and it accepts Charts JSON to produce an image/PDF. You can set the server up in your machine. By using ChartsEngine, you can get a JSON string after setting data. Besides, using ChartsEngine doesn’t require ZK Execution, so you can call its setter API in a separate working thread instead of a servlet thread, no need a HTTP request to a zul. Please see ChartsEngineComposer.

Since Highcharts 12.3, PNG, JPEG, and SVG exports are generated locally by default. Set exporting.local to false only when you want to send export requests to an export server. PDF export still requires the offline-exporting.js module for local export; otherwise Highcharts falls back to an export server.

Export with ImageMagick

Export Multiple Charts to One File

Custom Exporting Button

By default, ZK Charts provides export menu items for full screen, print, PNG, JPEG, and SVG. Highcharts 12 does not include PDF in the default menu. To provide PDF export, add downloadPDF to the menu and make sure the local offline exporting module or an export server is available.

To create custom exporting button, you have to use exporting buttons options. Since it also replaces the default button, you need to create default options yourself and add extra custom options. Please see the example below:

public class ExportComposer extends SelectorComposer<Component> {

    @Wire
    private Charts mychart;
    private static final String ON_MY_CUSTOM_ITEM = "onMyCustomItem";
    
    private SingleValueCategoryModel model = new DefaultSingleValueCategoryModel();
    
    @Override
    public void doAfterCompose(Component comp) throws Exception {
        //omitted code...
        
        createCustomExportItems();
    }
    
    private void createCustomExportItems() {
        Exporting exporting = mychart.getExporting();
        // Use local export. Set local to false if you want to post to an export server.
		exporting.setLocal(true);
        // Disable export-server fallback when local export is unavailable.
        exporting.addExtraAttr("fallbackToExportServer", new AnyVal(false));
		ExportingButton buttons = exporting.getButtons();
		List<MenuItem> menuItems = new ArrayList<>();

        // Optional: rebuild the default menu items; otherwise they are replaced.
        menuItems.add(defaultMenuItem("viewFullscreen", "this.fullscreen.toggle();"));
        menuItems.add(defaultMenuItem("printChart", "this.exporting.print();"));
        menuItems.add(separator());
        menuItems.add(defaultMenuItem("downloadPNG", "this.exporting.exportChart();"));
        menuItems.add(defaultMenuItem("downloadJPEG", "this.exporting.exportChart({type: \"image/jpeg\"});"));
        menuItems.add(defaultMenuItem("downloadPDF", "this.exporting.exportChart({type: \"application/pdf\"});"));
        menuItems.add(defaultMenuItem("downloadSVG", "this.exporting.exportChart({type: \"image/svg+xml\"});"));
        menuItems.add(separator());
        //add advanced export options - csv and xls exports
        menuItems.add(defaultMenuItem("downloadCSV", "this.downloadCSV()"));
        menuItems.add(defaultMenuItem("downloadXLS", "this.downloadXLS()"));
        menuItems.add(defaultMenuItem("viewData", "this.viewData()"));
        menuItems.add(defaultMenuItem("openInCloud", "this.openInCloud()"));
        menuItems.add(separator());
        //add custom menu items (possible at any position in the list)
        menuItems.add(customMenuItem("My Custom Item (at Client)", "alert('custom menu item clicked, handled in browser')"));
        menuItems.add(customMenuItem("My Custom Item (to Server)", fireServerEventScript(ON_MY_CUSTOM_ITEM)));
        buttons.setMenuItems(menuItems);        
    }

    private String fireServerEventScript(String eventName) {
        return  "var chartsWidget = zk(evt.target).$(); " +
                "chartsWidget.fire('" + eventName + "', null, {toServer: true});";
    }
     
    @Listen(ON_MY_CUSTOM_ITEM + " = #mychart")
    public void handleMyCustomItem() {
        Clients.showNotification("custom menu item clicked, handled on server");
    }
 
    private MenuItem customMenuItem(String text, String onclickJS) {
        MenuItem menuItem = new MenuItem();
        menuItem.setText(text);
        menuItem.setOnclick(new JavaScriptValue("function(evt) {" + onclickJS + "}"));
        return menuItem;
    }
 
    private MenuItem defaultMenuItem(String textKey, String onclickJS) {
        MenuItem menuItem = new MenuItem();
        menuItem.addExtraAttr("textKey", new AnyVal<String>(textKey));
        menuItem.setOnclick(new JavaScriptValue("function(evt) {" + onclickJS + "}"));
        return menuItem;
    }
 
    private MenuItem separator() {
        MenuItem menuItem = new MenuItem();
        menuItem.addExtraAttr("separator", new AnyVal<Boolean>(true));
        return menuItem;
    }
   
}
  • The defaultMenuItem() calls rebuild the default exporting options.
  • The first customMenuItem() call adds a custom item that executes JavaScript code.
  • The second customMenuItem() call fires a ZK event to the server and handles it with the handleMyCustomItem() event listener.

The result is:

Known Issues