org.smap.sdal.managers.PDFTableManager.java Source code

Java tutorial

Introduction

Here is the source code for org.smap.sdal.managers.PDFTableManager.java

Source

package org.smap.sdal.managers;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.smap.sdal.Utilities.GeneralUtilityMethods;
import org.smap.sdal.Utilities.PdfPageSizer;
import org.smap.sdal.Utilities.PdfUtilities;
import org.smap.sdal.model.DisplayItem;
import org.smap.sdal.model.KeyValue;
import org.smap.sdal.model.Label;
import org.smap.sdal.model.SurveyViewDefn;
import org.smap.sdal.model.Option;
import org.smap.sdal.model.Result;
import org.smap.sdal.model.TableColumn;
import org.smap.sdal.model.User;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Chunk;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Font;
import com.itextpdf.text.Font.FontFamily;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.Image;
import com.itextpdf.text.List;
import com.itextpdf.text.ListItem;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.BarcodeQRCode;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import com.itextpdf.tool.xml.ElementList;
import com.itextpdf.tool.xml.XMLWorker;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import com.itextpdf.tool.xml.css.CssFile;
import com.itextpdf.tool.xml.css.StyleAttrCSSResolver;
import com.itextpdf.tool.xml.html.Tags;
import com.itextpdf.tool.xml.parser.XMLParser;
import com.itextpdf.tool.xml.pipeline.css.CSSResolver;
import com.itextpdf.tool.xml.pipeline.css.CssResolverPipeline;
import com.itextpdf.tool.xml.pipeline.end.ElementHandlerPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipeline;
import com.itextpdf.tool.xml.pipeline.html.HtmlPipelineContext;

/*****************************************************************************
    
This file is part of SMAP.
    
SMAP is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
SMAP is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with SMAP.  If not, see <http://www.gnu.org/licenses/>.
    
 ******************************************************************************/

/*
 * Manage the table that stores details on the forwarding of data onto other systems
 */
public class PDFTableManager {

    private static Logger log = Logger.getLogger(PDFTableManager.class.getName());

    public static Font Symbols = null;
    public static Font defaultFont = null;
    private static final String DEFAULT_CSS = "/smap_bin/resources/css/default_pdf.css";
    private static int NUMBER_QUESTION_COLS = 10;

    Font font = new Font(FontFamily.HELVETICA, 10);
    Font fontbold = new Font(FontFamily.HELVETICA, 10, Font.BOLD);

    private class Parser {
        XMLParser xmlParser = null;
        ElementList elements = null;
    }

    private class PdfColumn {
        String displayName;
        int dataIndex;
        boolean barcode = false;
        String type;

        public PdfColumn(ResourceBundle localisation, int dataIndex, String n, boolean barcode, String type) {
            this.dataIndex = dataIndex;
            this.displayName = n;
            this.barcode = barcode;
            this.type = type;
        }

        // Return the width of this column
        public int getWidth() {
            int width = 256 * 20; // 20 characters is default
            return width;
        }

    }

    int marginLeft = 50;
    int marginRight = 50;
    int marginTop_1 = 130;
    int marginBottom_1 = 100;
    int marginTop_2 = 50;
    int marginBottom_2 = 100;

    private class GlobalVariables { // Level descended in form hierarchy
        //HashMap<String, Integer> count = new HashMap<String, Integer> ();      // Record number at a location given by depth_length as a string
        int[] cols = { NUMBER_QUESTION_COLS }; // Current Array of columns
        boolean hasAppendix = false;

        // Map of questions that need to have the results of another question appended to their results in a pdf report
        HashMap<String, ArrayList<String>> addToList = new HashMap<String, ArrayList<String>>();
    }

    /*
     * Call this function to create a PDF
     * Return a suggested name for the PDF file derived from the results
     */
    public void createPdf(Connection sd, OutputStream outputStream, ArrayList<ArrayList<KeyValue>> dArray,
            SurveyViewDefn mfc, ResourceBundle localisation, String tz, boolean landscape, String remoteUser,
            String basePath, String title, String project) {

        User user = null;
        UserManager um = new UserManager(localisation);

        try {

            user = um.getByIdent(sd, remoteUser);

            // Get fonts and embed them
            String os = System.getProperty("os.name");
            log.info("Operating System:" + os);

            if (os.startsWith("Mac")) {
                FontFactory.register("/Library/Fonts/fontawesome-webfont.ttf", "Symbols");
                FontFactory.register("/Library/Fonts/Arial Unicode.ttf", "default");
                FontFactory.register("/Library/Fonts/NotoNaskhArabic-Regular.ttf", "arabic");
                FontFactory.register("/Library/Fonts/NotoSans-Regular.ttf", "notosans");
            } else if (os.indexOf("nix") >= 0 || os.indexOf("nux") >= 0 || os.indexOf("aix") > 0) {
                // Linux / Unix
                FontFactory.register("/usr/share/fonts/truetype/fontawesome-webfont.ttf", "Symbols");
                FontFactory.register("/usr/share/fonts/truetype/ttf-dejavu/DejaVuSans.ttf", "default");
                FontFactory.register("/usr/share/fonts/truetype/NotoNaskhArabic-Regular.ttf", "arabic");
                FontFactory.register("/usr/share/fonts/truetype/NotoSans-Regular.ttf", "notosans");
            }

            Symbols = FontFactory.getFont("Symbols", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 12);
            defaultFont = FontFactory.getFont("default", BaseFont.IDENTITY_H, BaseFont.EMBEDDED, 10);

            ArrayList<PdfColumn> cols = getPdfColumnList(mfc, dArray, localisation);
            ArrayList<String> tableHeader = new ArrayList<String>();
            for (PdfColumn col : cols) {
                tableHeader.add(col.displayName);
            }
            /*
             * Create a PDF without the stationary
             */
            PdfWriter writer = null;

            /*
             * If we need to add a letter head then create document in two passes, the second pass adds the letter head
             * Else just create the document directly in a single pass
             */
            Parser parser = getXMLParser();

            // Step 1 - Create the underlying document as a byte array
            Document document = null;
            if (landscape) {
                document = new Document(PageSize.A4.rotate());
            } else {
                document = new Document(PageSize.A4);
            }
            document.setMargins(marginLeft, marginRight, marginTop_1, marginBottom_1);
            writer = PdfWriter.getInstance(document, outputStream);

            writer.setInitialLeading(12);
            writer.setPageEvent(new PdfPageSizer(title, project, user, basePath, tableHeader, marginLeft,
                    marginRight, marginTop_2, marginBottom_2));

            document.open();
            document.add(new Chunk("")); // Ensure there is something in the page so at least a blank document will be created
            processResults(parser, document, dArray, cols, basePath);
            document.close();

        } catch (SQLException e) {
            log.log(Level.SEVERE, "SQL Error", e);

        } catch (Exception e) {
            log.log(Level.SEVERE, "Exception", e);

        }

    }

    private class UserSettings {
        String title;
        String license;
    }

    /*
     * Get an XML Parser
     */
    private Parser getXMLParser() {

        Parser parser = new Parser();

        // CSS
        CSSResolver cssResolver = new StyleAttrCSSResolver();
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(DEFAULT_CSS);
            CssFile cssFile = XMLWorkerHelper.getCSS(fis);
            cssResolver.addCss(cssFile);
        } catch (Exception e) {
            log.log(Level.SEVERE, "Failed to get CSS file", e);
            cssResolver = XMLWorkerHelper.getInstance().getDefaultCssResolver(true);
        } finally {
            try {
                fis.close();
            } catch (Exception e) {
            }
        }

        // HTML
        HtmlPipelineContext htmlContext = new HtmlPipelineContext(null);
        htmlContext.setTagFactory(Tags.getHtmlTagProcessorFactory());
        htmlContext.autoBookmark(false);

        // Pipelines
        parser.elements = new ElementList();
        ElementHandlerPipeline end = new ElementHandlerPipeline(parser.elements, null);
        HtmlPipeline html = new HtmlPipeline(htmlContext, end);
        CssResolverPipeline css = new CssResolverPipeline(cssResolver, html);

        // XML Worker
        XMLWorker worker = new XMLWorker(css, true);
        parser.xmlParser = new XMLParser(worker);

        return parser;

    }

    /*
     * Process the results and write to a table
     */
    private void processResults(Parser parser, Document document, ArrayList<ArrayList<KeyValue>> dArray,
            ArrayList<PdfColumn> cols, String basePath) throws DocumentException, IOException {

        for (int index = 0; index < dArray.size(); index++) {

            ArrayList<KeyValue> record = dArray.get(index);
            PdfPTable newTable = processRow(parser, record, cols, basePath);
            newTable.setWidthPercentage(100);
            document.add(newTable);

        }

        return;
    }

    /*
     * Add the table row to the document
     */
    PdfPTable processRow(Parser parser, ArrayList<KeyValue> record, ArrayList<PdfColumn> cols, String basePath)
            throws BadElementException, MalformedURLException, IOException {

        PdfPTable table = new PdfPTable(cols.size());

        for (PdfColumn col : cols) {

            if (col.dataIndex >= 0) {
                PdfPCell cell = addDisplayItem(parser, record.get(col.dataIndex), basePath, col.barcode, col.type);
                cell.setBorderColor(BaseColor.LIGHT_GRAY);

                table.addCell(cell);
            }

        }
        return table;
    }

    /*
     * Get the columns for the Pdf file
     */
    private ArrayList<PdfColumn> getPdfColumnList(SurveyViewDefn mfc, ArrayList<ArrayList<KeyValue>> dArray,
            ResourceBundle localisation) {

        ArrayList<PdfColumn> cols = new ArrayList<PdfColumn>();
        ArrayList<KeyValue> record = null;

        if (dArray.size() > 0) {
            record = dArray.get(0);
        }

        for (int i = 0; i < mfc.columns.size(); i++) {
            TableColumn tc = mfc.columns.get(i);
            if (!tc.hide && tc.include) {
                int dataIndex = -1;
                if (record != null) {
                    dataIndex = getDataIndex(record, tc.question_name);
                }
                cols.add(new PdfColumn(localisation, dataIndex, tc.question_name, tc.barcode, tc.type));
            }
        }

        return cols;
    }

    /*
     * Get the index into the data set for a column
     */
    private int getDataIndex(ArrayList<KeyValue> record, String name) {
        int idx = -1;

        for (int i = 0; i < record.size(); i++) {
            if (record.get(i).k.equals(name)) {
                idx = i;
                break;
            }
        }
        return idx;
    }

    /*
     * Get the number of blank repeats to generate
     */
    int getBlankRepeats(String appearance) {
        int repeats = 1;

        if (appearance != null) {
            String[] appValues = appearance.split(" ");
            if (appearance != null) {
                for (int i = 0; i < appValues.length; i++) {
                    if (appValues[i].startsWith("pdfrepeat")) {
                        String[] parts = appValues[i].split("_");
                        if (parts.length >= 2) {
                            repeats = Integer.valueOf(parts[1]);
                        }
                        break;
                    }
                }
            }
        }

        return repeats;
    }

    /*
     * Set the attributes for this question from keys set in the appearance column
     */
    void setQuestionFormats(String appearance, DisplayItem di) {

        if (appearance != null) {
            String[] appValues = appearance.split(" ");
            if (appearance != null) {
                for (int i = 0; i < appValues.length; i++) {
                    if (appValues[i].startsWith("pdflabelbg")) {
                        setColor(appValues[i], di, true);
                    } else if (appValues[i].startsWith("pdfvaluebg")) {
                        setColor(appValues[i], di, false);
                    } else if (appValues[i].startsWith("pdflabelw")) {
                        setWidths(appValues[i], di);
                    } else if (appValues[i].startsWith("pdfheight")) {
                        setHeight(appValues[i], di);
                    } else if (appValues[i].startsWith("pdfspace")) {
                        setSpace(appValues[i], di);
                    } else if (appValues[i].equals("pdflabelcaps")) {
                        di.labelcaps = true;
                    } else if (appValues[i].equals("pdflabelbold")) {
                        di.labelbold = true;
                    }
                }
            }
        }
    }

    /*
     * Get the color values for a single appearance value
     * Format is:  xxxx_0Xrr_0Xgg_0xbb
     */
    void setColor(String aValue, DisplayItem di, boolean isLabel) {

        BaseColor color = null;

        String[] parts = aValue.split("_");
        if (parts.length >= 4) {
            if (parts[1].startsWith("0x")) {
                color = new BaseColor(Integer.decode(parts[1]), Integer.decode(parts[2]), Integer.decode(parts[3]));
            } else {
                color = new BaseColor(Integer.decode("0x" + parts[1]), Integer.decode("0x" + parts[2]),
                        Integer.decode("0x" + parts[3]));
            }
        }

        if (isLabel) {
            di.labelbg = color;
        } else {
            di.valuebg = color;
        }

    }

    /*
     * Set the widths of the label and the value
     * Appearance is:  pdflabelw_## where ## is a number from 0 to 10
     */
    void setWidths(String aValue, DisplayItem di) {

        String[] parts = aValue.split("_");
        if (parts.length >= 2) {
            di.widthLabel = Integer.valueOf(parts[1]);
        }

        // Do bounds checking
        if (di.widthLabel < 0 || di.widthLabel > 10) {
            di.widthLabel = 5;
        }

    }

    /*
     * Set the height of the value
     * Appearance is:  pdfheight_## where ## is the height
     */
    void setHeight(String aValue, DisplayItem di) {

        String[] parts = aValue.split("_");
        if (parts.length >= 2) {
            di.valueHeight = Double.valueOf(parts[1]);
        }

    }

    /*
     * Set space before this item
     * Appearance is:  pdfheight_## where ## is the height
     */
    void setSpace(String aValue, DisplayItem di) {

        String[] parts = aValue.split("_");
        if (parts.length >= 2) {
            di.space = Integer.valueOf(parts[1]);
        }

    }

    /*
     * Add the question label, hint, and any media
     */
    private PdfPCell addDisplayItem(Parser parser, KeyValue kv, String basePath, boolean barcode, String type)
            throws BadElementException, MalformedURLException, IOException {

        PdfPCell valueCell = new PdfPCell();
        valueCell.setBorderColor(BaseColor.LIGHT_GRAY);

        // Set the content of the value cell
        try {
            if (type != null && type.equals("image")) {
                Image img = Image.getInstance(kv.v);
                valueCell.addElement(img);
            } else if (barcode && kv.v.trim().length() > 0) {
                BarcodeQRCode qrcode = new BarcodeQRCode(kv.v.trim(), 1, 1, null);
                Image qrcodeImage = qrcode.getImage();
                qrcodeImage.setAbsolutePosition(10, 500);
                qrcodeImage.scalePercent(200);
                valueCell.addElement((qrcodeImage));
            } else {
                valueCell.addElement(getPara(kv.v));
            }
        } catch (Exception e) {
            log.info("Error updating value cell, continuing: " + basePath + " : " + kv.v);
            log.log(Level.SEVERE, "Exception", e);
        }

        return valueCell;
    }

    /*
     * Set the contents of the value cell
     *
    private void updateValueCell(PdfPCell valueCell, 
     String value,
     String basePath
     ) throws BadElementException, MalformedURLException, IOException {
        
         
       valueCell.addElement(getPara(value));
    }
    */

    private Paragraph getPara(String value) {

        Paragraph para = new Paragraph("", font);

        if (value != null && value.trim().length() > 0) {
            para.add(new Chunk(GeneralUtilityMethods.unesc(value), font));
        }

        return para;
    }

    /*
    private void processSelect(PdfPCell cell, DisplayItem di,
     boolean generateBlank,
     GlobalVariables gv) {
        
       // If generating blank template
       List list = new List();
       list.setAutoindent(false);
       list.setSymbolIndent(24);
           
       String stringValue = null;
           
       boolean isSelectMultiple = di.type.equals("select") ? true : false;
           
       // Questions that append their values to this question
       ArrayList<String> deps = gv.addToList.get(di.fIdx + "_" + di.rec_number + "_" + di.name);
           
           
       if(generateBlank) {
     for(DisplayItem aChoice : di.choices) {
        ListItem item = new ListItem(GeneralUtilityMethods.unesc(aChoice.text), font);
         
        if(isSelectMultiple) {
           if(aChoice.isSet) {
              item.setListSymbol(new Chunk("\uf046", Symbols)); 
              list.add(item);   
           } else {
               
              item.setListSymbol(new Chunk("\uf096", Symbols)); 
              list.add(item);
           }
            
        } else {
           if(aChoice.isSet) {
              item.setListSymbol(new Chunk("\uf111", Symbols)); 
              list.add(item);
        
           } else {
              //item.setListSymbol(new Chunk("\241", Symbols)); 
              item.setListSymbol(new Chunk("\uf10c", Symbols)); 
              list.add(item);
           }
        }
     }
         
     cell.addElement(list);
         
       } else {
     stringValue = getSelectValue(isSelectMultiple, di, deps);
     cell.addElement(getPara(stringValue));
       }
        
    }
    */

    /*
     * Get the value of a select question
     *
    String getSelectValue(boolean isSelectMultiple, DisplayItem di, ArrayList<String> deps) {
       StringBuffer sb = new StringBuffer("");
           
       for(DisplayItem aChoice : di.choices) {
     ListItem item = new ListItem(GeneralUtilityMethods.unesc(aChoice.text));
         
     if(isSelectMultiple) {
        if(aChoice.isSet) {
            
           if(deps == null || (aChoice.name != null && !aChoice.name.trim().toLowerCase().equals("other"))) {
              if(sb.length() > 0) {
                 sb.append(", ");
              }
              sb.append(aChoice.text);
           }
               
        } 
     } else {
        if(aChoice.isSet) {
               
           if(deps == null || (aChoice.name != null && !aChoice.name.trim().toLowerCase().equals("other"))) {
              if(sb.length() > 0) {
                 sb.append(", ");
              }
              sb.append(aChoice.text);
           }
        
        }
     }
        
         
       }
         
       return sb.toString();
           
    }
    */

    /*
     * Fill in user details for the output when their is no template
     */
    private void fillNonTemplateUserDetails(Document document, User user, String basePath)
            throws IOException, DocumentException {

        String settings = user.settings;
        Type type = new TypeToken<UserSettings>() {
        }.getType();
        Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd").create();
        UserSettings us = gson.fromJson(settings, type);

        float indent = (float) 20.0;
        addValue(document, "Completed by:", (float) 0.0);
        if (user.signature != null && user.signature.trim().length() > 0) {
            String fileName = null;
            try {
                fileName = basePath + File.separator + user.signature;

                Image img = Image.getInstance(fileName);
                img.scaleToFit(200, 50);
                img.setIndentationLeft(indent);

                document.add(img);

            } catch (Exception e) {
                log.info("Error: Failed to add image " + fileName + " to pdf");
            }
        }
        addValue(document, user.name, indent);
        addValue(document, user.company_name, indent);
        if (us != null) {
            addValue(document, us.title, indent);
            addValue(document, us.license, indent);
        }

    }

    /*
     * Format a key value pair into a paragraph
     */
    private void addKeyValuePair(Document document, String key, String value) throws DocumentException {
        Paragraph para = new Paragraph("", font);

        para.add(new Chunk(GeneralUtilityMethods.unesc(key), fontbold));
        para.add(new Chunk(GeneralUtilityMethods.unesc(value), font));

        document.add(para);
    }

    /*
     * Format a single value into a paragraph
     */
    private void addValue(Document document, String value, float indent) throws DocumentException {

        if (value != null && value.trim().length() > 0) {
            Paragraph para = new Paragraph("", font);
            para.setIndentationLeft(indent);
            para.add(new Chunk(GeneralUtilityMethods.unesc(value), font));
            document.add(para);
        }
    }
}