Export Tree to PDF or Excel

From Documentation
DocumentationSmall Talks2014JanuaryExport Tree to PDF or Excel
Export Tree to PDF or Excel

Author
Neil Lee, Engineer, Potix Corporation
Date
January XX, 2014
Version
ZK 6.0 and later

Introduction

In an earlier small talk, PdfExporter and ExcelExporter utilities were introduced to demonstrate how developers can convert ZK Grid or Listbox to a PDF/Excel document.

This small talk is going to extend that library to handle conversions from ZK Tree to PDF/Excel documents as well.

Demo

Let's first take a look at the source Tree and the resulting output before diving into the codes.

We are exporting the Tree below to PDF and Excel
Tree to be exported.png

Resulting PDF
Export Tree to PDF Format.png

Resulting Excel
Export Tree to Excel Format.png

Usage

The two utilities, PdfExporter and ExcelExporter, have been extended from the previous small talk to also handle ZK Tree. Exporting a Tree to PDF or Excel has similar usage to that of ZK Grid/Listbox. There are two ways that can be utilized to export a tree: either by traversing the Tree component directly, or by using a custom RowRenderer.

Export tree data via traversing the component hierarchy

PdfExporter and ExcelExporter both provide an export method that would take the Tree component as a parameter. The resulting outputs are illustrated in the previous section.

PDF

    @Command
    public void exportTreeToPdf(@BindingParam("ref") Tree tree) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        
        PdfExporter exporter = new PdfExporter();
        exporter.export(tree, out);
        
        AMedia amedia = new AMedia("FirstReport.pdf", "pdf", "application/pdf", out.toByteArray());
        Filedownload.save(amedia);
        out.close();
    }

Excel

    @Command
    public void exportTreeToExcel(@BindingParam("ref") Tree tree) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        
        ExcelExporter exporter = new ExcelExporter();
        exporter.export(tree, out);
        
        AMedia amedia = new AMedia("FirstReport.xlsx", "xls", "application/file", out.toByteArray());
        Filedownload.save(amedia);
        out.close();
    }

Export data model via custom renderer

Although exporting data via traversing the component hierarchy is quite straightforward, it does have several limitations. For instance, when ROD (Render on Demand) is enabled, only the expanded part of the tree gets exported as can be seen from the screenshots above. If you need to export the tree in its entirety when ROD is enabled, or if you wish to customize the rendered result, you can use a custom renderer to render the data model to PDF/Excel directly. In the following subsections I will demonstrate the use of a renderer to export the tree.

PDF

PdfExporter is based on iText library to create PDF documents, hence, you would need to be familiar with the iText API in order to write a custom renderer effectively. In addition, some wrapper methods are created to assist in writing a custom renderer. Please see the previous small talk for more details.

  • Render header/footer using Interceptor interface
    @Command
    public void exportTreeToPdfByDataModel() throws Exception {
        final PdfExporter exporter = new PdfExporter();
        ...     
        final String[] headers = new String[]{"Path", "Description", "Size" };
        final String[] footers = new String[]{"footer1", "footer2", "footer3" };

        exporter.setInterceptor(new Interceptor <PdfPTable> () {

            // Render header
            @Override
            public void beforeRendering(PdfPTable table) {
                for (int i = 0; i < headers.length; i++) {
                    String header = headers[i];
                    Font font = exporter.getFontFactory().getFont(FontFactory.FONT_TYPE_HEADER);
                    
                    PdfPCell cell = exporter.getPdfPCellFactory().getHeaderCell();
                    cell.setPhrase(new Phrase(header, font));
                    if ("Size".equals(header)) {
                        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
                    }
                    table.addCell(cell);
                }
                table.completeRow();
            }
            
            // Render footer
            @Override
            public void afterRendering(PdfPTable table) {
                for (String footer : footers) {
                    Font font = exporter.getFontFactory().getFont(FontFactory.FONT_TYPE_FOOTER);
                    
                    PdfPCell cell = exporter.getPdfPCellFactory().getFooterCell();
                    cell.setPhrase(new Phrase(footer, font));
                    table.addCell(cell);
                }
                table.completeRow();
            }
        });
        ...
    }
  • Obtain tree data to export
   It is important to retain the list of tree nodes, not just data itself so that depth information can be inferred in order to perform indentation later.
    private List<TreeNode<PackageData>> getTreeData(TreeNode<PackageData> root) {
        List<TreeNode<PackageData>> data = new ArrayList<TreeNode<PackageData>>();
        
        List<TreeNode<PackageData>> children = root.getChildren();
        for (TreeNode<PackageData> child : children) {
            data.add(child);
            if (!child.isLeaf())
                data.addAll(getTreeData(child));
        }
        
        return data;
    }
  • Indentation

The following two supporting methods are utilized to indent 2 spaces per tree level.

    ...
    private int getLevel(TreeNode<?> node) {
        TreeNode<?> parent = node.getParent();
        if (parent == null)
            return -1;
        else
            return getLevel(parent) + 1;
    }
    ...
    private String indent(int level) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < level; i++)
            sb.append("  ");
        return sb.toString();
    }
  • Render contents using RowRenderer
    @Command
    public void exportTreeToPdfByDataModel() throws Exception {
        ...     
        exporter.export(headers.length, getTreeData(_model.getRoot()), new RowRenderer<PdfPTable, TreeNode<PackageData>>() {
            ...

            @Override
            public void render(PdfPTable table, TreeNode<PackageData> node, boolean isOddRow) {
                Font font = fontFactory.getFont(FontFactory.FONT_TYPE_CELL);
                
                PackageData pkg = node.getData();
                
                PdfPCell cell = cellFactory.getCell(isOddRow);
                cell.setPhrase(new Phrase(indent(getLevel(node)) + pkg.getPath(), font));
                table.addCell(cell);
                
                cell = cellFactory.getCell(isOddRow);
                cell.setPhrase(new Phrase(pkg.getDescription(), font));
                table.addCell(cell);
                
                cell = cellFactory.getCell(isOddRow);
                cell.setPhrase(new Phrase(pkg.getSize(), font));
                cell.setHorizontalAlignment(Element.ALIGN_CENTER);
                table.addCell(cell);
                
                table.completeRow();
            }

        }, out);
        ...
    }

Excel

ExcelExporter is based on Apache POI to create excel documents. Hence, you would need to be familiar with the Apache POI API in order to write a custom renderer effectively. In addition, some wrapper methods are created to assist in writing a custom renderer. Please refer to the quick guide to get started with POI.

  • Render header/footer using Interceptor interface
    @Command
    public void exportTreeToExcelByDataModel() throws Exception {
        final ExcelExporter exporter = new ExcelExporter();

        final String[] headers = new String[]{"Path", "Description", "Size"};
        final String[] footers = new String[]{"footer 1", "footer 2", "footer 3"};
        ...
        exporter.setInterceptor(new Interceptor<XSSFWorkbook>() {
            
            // Render header
            @Override
            public void beforeRendering(XSSFWorkbook target) {
                ExportContext ctx = exporter.getExportContext();
                
                for (String header : headers) {
                    Cell cell = exporter.getOrCreateCell(ctx.moveToNextCell(), ctx.getSheet());
                    cell.setCellValue(header);
                    
                    if ("Size".equals(header)) {
                        CellStyle srcStyle = cell.getCellStyle();
                        if (srcStyle.getAlignment() != CellStyle.ALIGN_CENTER) {
                            XSSFCellStyle newCellStyle = ctx.getSheet().getWorkbook().createCellStyle();
                            newCellStyle.cloneStyleFrom(srcStyle);
                            newCellStyle.setAlignment(CellStyle.ALIGN_CENTER);
                            cell.setCellStyle(newCellStyle);
                        }
                    }
                }
            }
            
            // Render footer
            @Override
            public void afterRendering(XSSFWorkbook target) {
                ExportContext ctx = exporter.getExportContext();

                ctx.moveToNextRow();
                for (String footer : footers) {
                    Cell cell = exporter.getOrCreateCell(ctx.moveToNextCell(), ctx.getSheet());
                    cell.setCellValue(footer);
                }
            }
        });
        ...
    }
  • Obtain tree data to export
   The source code is the same as that for PDF format.
  • Indentation
   The source code is the same as that for PDF format.
  • Render contents using RowRenderer
    @Command
    public void exportTreeToExcelByDataModel() throws Exception {
        final ExcelExporter exporter = new ExcelExporter();
        ...
        exporter.export(headers.length, getTreeData(_model.getRoot()), new RowRenderer<Row, TreeNode<PackageData>>() {
            ...
            @Override
            public void render(Row row, TreeNode<PackageData> node, boolean oddRow) {
                ExportContext ctx = exporter.getExportContext();
                XSSFSheet sheet = ctx.getSheet();
                
                PackageData pkg = node.getData();
                
                exporter
                .getOrCreateCell(ctx.moveToNextCell(), sheet)
                .setCellValue(indent(getLevel(node)) + pkg.getPath());
                
                exporter
                .getOrCreateCell(ctx.moveToNextCell(), sheet)
                .setCellValue(pkg.getDescription());
                
                Cell cell = exporter.getOrCreateCell(ctx.moveToNextCell(), sheet);
                cell.setCellValue(pkg.getSize());
                CellStyle cellStyle = sheet.getWorkbook().createCellStyle();
                cellStyle.setAlignment(CellStyle.ALIGN_CENTER);
                cell.setCellStyle(cellStyle);
            }

        }, out);
        ...
    }

For complete source code, please refer to here

Summary

In this smalltalk, I have showed it is also easy for developers to export a ZK Tree to PDF or Excel documents either from the component itself or from the underlying data model directly. Explore the project and enjoy it.


Comments



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.