Jump to content
We've recently updated our Privacy Statement, available here ×

eluzgin

Members
  • Posts

    11
  • Joined

  • Last visited

eluzgin's Achievements

  1. TIBCO JasperReports® is great for creating custom PDF templates, compiling them and then filling them with data (CSV, Query, etc). However often the developer has a requirement to create dynamic PDF with any number of columns and data size and making it look well formatted too. I have faced this same requirement at two different companies and this post is sharing my approach to solving this problem. If you look at XML source of JasperReports template you will see a pattern there: <?xml version="1.0" encoding="UTF-8"?> <jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="PDF Report" pageWidth="940" pageHeight="842" columnWidth="900" leftMargin="20" rightMargin="20" topMargin="20" bottomMargin="20" uuid="0a409d27-e584-406c-9358-429837f98f4c"> <property name="com.jaspersoft.studio.data.defaultdataadapter" value="CSV Adapter"/> <queryString><![CDATA[]]></queryString> <field name="Column Name 1" class="java.lang.String"/> <field name="Column Name 2" class="java.lang.String"/> ... <background> <band splitType="Stretch"/> </background> <title> <band height="79" splitType="Stretch"/> </title> <pageHeader> <band height="35" splitType="Stretch"/> </pageHeader> <columnHeader> <band height="35" splitType="Stretch"> <staticText> <reportElement x="0" y="0" width="60" height="20" uuid="ad347294-52c2-4aaf-b08b-859ff13fde71"> <property name="com.jaspersoft.studio.spreadsheet.connectionID" value="48369fd4-1575-4e1a-a998-0d5e583d6232"/> </reportElement> <text><![CDATA[Column Name 1]></text> </staticText> <staticText> <reportElement x="70" y="0" width="70" height="20" uuid="c36b238b-1879-4272-9f4a-20d62cdf72d3"> <property name="com.jaspersoft.studio.spreadsheet.connectionID" value="09356d5f-9603-402b-93b3-2805cbc5ce6f"/> </reportElement> <text><![CDATA[Column Name 2]]></text> </staticText> ... </band> </columnHeader> <detail> <band height="47" splitType="Stretch"> <textField> <reportElement x="0" y="0" width="60" height="20" uuid="7004bb57-f42f-438f-9da2-fb9613e8a86b"> <property name="com.jaspersoft.studio.spreadsheet.connectionID" value="48369fd4-1575-4e1a-a998-0d5e583d6232"/> </reportElement> <textFieldExpression> <![CDATA[$F{Column Name 1}]]> </textFieldExpression> </textField> <textField> <reportElement x="70" y="0" width="70" height="20" uuid="e1330b87-e5aa-4202-bdf5-9972555f8c20"> <property name="com.jaspersoft.studio.spreadsheet.connectionID" value="09356d5f-9603-402b-93b3-2805cbc5ce6f"/> </reportElement> <textFieldExpression> <![CDATA[$F{Column Name 2}]]> </textFieldExpression> </textField> ... </band> </detail> <pageFooter> <band height="54" splitType="Stretch"/> </pageFooter> <summary> <band height="42" splitType="Stretch"/> </summary> </jasperReport>You have staticText tag for each column and textField for a value for that corresponding column Also notice that property/@value attribute shares the same UUID value. Then next column x attribute is incremented by width of first column + some padding value. Once you understand how XML is formed - it’s easy to write a logic for dynamically generating similar looking XML template dynamically based on any data provided. I am just posting the end result in Java that generates, compiles and fills the PDF report from CSV data also taking into account resizing columns width based on data size provided (just inspecting first 10 rows for performance reasons). Feel free to use this example, extend it and modify to fit your needs: package com.jasper.generator; import net.sf.jasperreports.engine.*; import net.sf.jasperreports.engine.data.JRCsvDataSource; import org.apache.log4j.Logger; import java.io.*; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * Generates PDF report using JasperReports lib * * @author Eugene Luzgin, August 2019 */ public class JasperReportGenerator { static final Logger logger = Logger.getLogger(JasperReportGenerator.class); private static final int min_page_width = 555; private static final int col_width = 80; private static final int col_pad = 10; private static final int col_height = 20; /** copy the file to the output stream */ public void writePDF(OutputStream pdfOut, File csvFile) throws IOException { try { // Read first line from CSV file as columns: BufferedReader csvReader = new BufferedReader(new FileReader(csvFile)); String columnsLine = csvReader.readLine(); String[] columns = columnsLine.split(","); Map<String, Integer> colAvgLen = new HashMap<>(columns.length); for (String column : columns) { colAvgLen.put(column, column.length()); } String line; int counter = 0; while ((line = csvReader.readLine()) != null && counter < 10) { String[] values = line.split(","); for (int i = 0; i < columns.length; i++) { String col = columns[i]; Integer len = (values[i] != null) ? values[i].length() : 0; if (colAvgLen.get(col) < len) colAvgLen.put(col, len); } counter++; } csvReader.close(); Map<String, Float> colAvgPerWidth = convertAbsoluteAveragesToPercents(colAvgLen); // construct and run JasperReports: JasperPrint jprint = createJasperReport(csvFile, columns, colAvgPerWidth); JasperExportManager.exportReportToPdfStream(jprint, pdfOut); } catch (JRException jrex) { logger.error("JasperReports error", jrex); throw new IOException("Error generating PDF report", jrex); } } private Map<String, Float> convertAbsoluteAveragesToPercents(Map<String, Integer> colAvgLen) { Map<String, Float> colAvgPer = new HashMap<>(colAvgLen.size()); Integer total = 0; for (String col : colAvgLen.keySet()) { total += colAvgLen.get(col); } for (String col : colAvgLen.keySet()) { Float percent = (100.0F * colAvgLen.get(col).floatValue()) / total; colAvgPer.put(col, percent); } return colAvgPer; } private JasperPrint createJasperReport( File csvFile, String[] columns, Map<String, Float> colAvgPerWidth) throws JRException, IOException { InputStream csvIn = new FileInputStream(csvFile); JRCsvDataSource jrCsvDataSource = new JRCsvDataSource(csvIn); jrCsvDataSource.setUseFirstRowAsHeader(true); jrCsvDataSource.setColumnNames(columns); String xmlReport = createJasperXMLReport(columns, colAvgPerWidth); InputStream inputStream = new ByteArrayInputStream(xmlReport.getBytes(Charset.forName("UTF-8"))); JasperReport report = JasperCompileManager.compileReport(inputStream); Map<String, Object> parameters = new HashMap<>(); return JasperFillManager.fillReport(report, parameters, jrCsvDataSource); } private String createJasperXMLReport(String[] columns, Map<String, Float> colAvgPerWidth) { Map<String, String> propUUIDMap = new HashMap<>(columns.length); int columnsTotalWidth = (col_width + col_pad) * columns.length; Map<String, Integer> columnWidthMap = new HashMap<>(columns.length); for (String column : colAvgPerWidth.keySet()) { Float ratio = colAvgPerWidth.get(column) / 100; Integer colWidth = Math.round(columnsTotalWidth * ratio); columnWidthMap.put(column, colWidth); } int pageWidth = columnsTotalWidth + 40; if (pageWidth < min_page_width) pageWidth = min_page_width; UUID reportUUID = UUID.randomUUID(); StringBuffer sb = new StringBuffer(); sb.append( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<!-- Created with Jaspersoft Studio version 6.9.0.final using JasperReports Library version 6.9.0-cb8f9004be492ccc537180b49c026951f4220bf3 -->\n" + "<jasperReport xmlns=\"http://jasperreports.sourceforge.net/jasperreports\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " + "xsi:schemaLocation=\"http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd\" name=\"PDF Report\" " + "pageWidth=\"" + pageWidth + "\" pageHeight=\"842\" columnWidth=\"" + columnsTotalWidth + "\" leftMargin=\"20\" rightMargin=\"20\" topMargin=\"20\" bottomMargin=\"20\" uuid=\"" + reportUUID + "\">\n" + "\t<property name=\"com.jaspersoft.studio.data.defaultdataadapter\" value=\"CSV Adapter\"/>\n" + "\t<queryString>\n" + "\t\t<![CDATA[]]>\n" + "\t</queryString>\n"); for (String column : columns) { sb.append("<field name=\""); sb.append(column); sb.append("\" class=\"java.lang.String\"/>\n"); } sb.append( "\t<background>\n" + "\t\t<band splitType=\"Stretch\"/>\n" + "\t</background>\n" + "\t<title>\n" + "\t\t<band height=\"79\" splitType=\"Stretch\"/>\n" + "\t</title>\n" + "\t<pageHeader>\n" + "\t\t<band height=\"35\" splitType=\"Stretch\"/>\n" + "\t</pageHeader>\n" + "\t<columnHeader>\n" + "\t\t<band height=\"35\" splitType=\"Stretch\">\n"); int x = 0; for (String column : columns) { UUID elemUUID = UUID.randomUUID(); UUID propUUID = UUID.randomUUID(); propUUIDMap.put(column, propUUID.toString()); sb.append( "\t\t\t<staticText>\n" + "\t\t\t\t<reportElement x=\"" + x + "\" y=\"0\" width=\"" + columnWidthMap.get(column) + "\" height=\"" + col_height + "\" uuid=\""); sb.append(elemUUID.toString()); sb.append("\">\n"); sb.append( "\t\t\t\t\t<property name=\"com.jaspersoft.studio.spreadsheet.connectionID\" value=\""); sb.append(propUUID.toString()); sb.append("\"/>\n"); sb.append("\t\t\t\t</reportElement>\n"); sb.append("\t\t\t\t<text><![CDATA["); sb.append(column); sb.append("]]></text>\n"); sb.append("\t\t\t</staticText>\n"); x += columnWidthMap.get(column) + col_pad; } sb.append( "\t\t</band>\n" + "\t</columnHeader>\n" + "\t<detail>\n" + "\t\t<band height=\"47\" splitType=\"Stretch\">\n"); x = 0; for (String column : columns) { UUID elemUUID = UUID.randomUUID(); String propUUID = propUUIDMap.get(column); sb.append( "\t\t\t<textField>\n" + "\t\t\t\t<reportElement x=\"" + x + "\" y=\"0\" width=\"" + columnWidthMap.get(column) + "\" height=\"" + col_height + "\" uuid=\""); sb.append(elemUUID.toString()); sb.append("\">\n"); sb.append( "\t\t\t\t\t<property name=\"com.jaspersoft.studio.spreadsheet.connectionID\" value=\""); sb.append(propUUID); sb.append("\"/>\n"); sb.append("\t\t\t\t</reportElement>\n" + "\t\t\t\t<textFieldExpression><![CDATA[$F{"); sb.append(column); sb.append("}]]></textFieldExpression>\n" + "\t\t\t</textField>\n"); x += columnWidthMap.get(column) + col_pad; } sb.append( "\t\t</band>\n" + "\t</detail>\n" + "\t<pageFooter>\n" + "\t\t<band height=\"54\" splitType=\"Stretch\"/>\n" + "\t</pageFooter>\n" + "\t<summary>\n" + "\t\t<band height=\"42\" splitType=\"Stretch\"/>\n" + "\t</summary>\n" + "</jasperReport>"); return sb.toString(); } }
  2. Just placing commons-beanutils-1.8.2.jar under WebSphere 8 lib or lib/ext folder will crash server admin console on restart. So you effectively unable to manage server. The only workaround in this case - limiting exposure of beanutils library to the scope of your application only.
  3. I have encountered the same issue. The root cause of this problem is that IBM WebSphere is using internally a completely different version of beanutils library, which has a different set of public methods in the same class - PropertyUtils. By placing your commons-beanutils library under ext, which is a global location for all applications - it takes precedence over library used by IBM admin console and thus breaking it as it's trying to call non-existent method. This is a classical jar libraries conflict - when you have two or more jars which have different definitions of the same class name on a classpath. You have to use some file tool which allows you to search for class name across all jar files recursively in IBM Web Sphere folder structure. I use Far Manager (http://www.farmanager.com/) for it and found multiple jars used internally in IBM WEB Sphere which come in conflict with the commons-beanutils library used by iReport library. The only logical solution for this problem is to place these libraries in the scope of visibility of your application only and remove it from global lib/ext folder. Good luck!
  4. Changed Assigned User from - to @User_306070 It sounds like this bug is inherited from netbeans: https://netbeans.org/bugzilla/show_bug.cgi?id=164529 So not sure if JasperSoft can do anything about it.
  5. It sounds like this bug is inherited from netbeans: https://netbeans.org/bugzilla/show_bug.cgi?id=164529 So not sure if JasperSoft can do anything about it.
  6. Related to ticket: [#2984] - Designer showing UI window outside of a single screen boundaries It would help to others if you indicated which config file stores coordinates and how to reset it.
  7. In case you will need to use plsql - you may find this article useful: http://community.jaspersoft.com/wiki/how-execute-oracle-stored-procedure-jasper-report-web-application-environment
  8. This article builds on top of the following article: How to execute the Oracle Stored Procedure in jasper report in iReport editor environment As a new developer who is on a learning curve of using Jasper Reports in web applications, you will discover that there is very limited information about more advanced features of Jasper Reports like calling stored procedures and using ORACLE_REF_CURSOR sa result set. And the above linked article does a great job in describing how you go about building report which calls stored procedure and uses ref cursor. However once you take this report and deploy it to your web application - you will learn that it will not work in the web application environment. This article is the result of hours of research and trial and error attempts to make it work. And I share it with the hope that it will save time and trouble for others going the same route. Here are the steps you need to take to make Jasper report work in your web application: Download JasperReports Library from http://sourceforge.net/projects/jasperreports/files/jasperreports/ Copy jasperreports-{version}.jar, iText-{version}.js1.jar, commons-digester-{version}.jar and groovy-all-{version}.jar under your web application lib folder (WEB-INF/lib). Download JasperReports Extension Library from http://www.java2s.com/Code/Jar/j/Downloadjasperreportsextensions353jar.htm; Then unzip and copy jasperreports-extensions-3.5.3.jar under your web application lib folder (WEB-INF/lib). NOTE: You will need this extension library in order to execute plsql calls to stored procedure and without this extension your report which calls stored procedure will not work. Then as an example of web application code to create and return PDF report will look something like this: try { // Custom code to get DB Connection Connection conn = getDataSource().getConnection(); // This will be output PDF stream ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); // You will need to specify relative path to your Jasper report: URL fileUrl = getClass().getClassLoader().getResource("path to report.jasper"); JasperReport jasperReport = (JasperReport) JRLoader.loadObject(fileUrl); /* This line sets correct factory to parse and process PLSQL procedure calls. The PlSqlQueryExecuterFactory class is located in jasperreports-extensions-3.5.3.jar */ jasperReport.setProperty( "net.sf.jasperreports.query.executer.factory.plsql" ,"com.jaspersoft.jrx.query.PlSqlQueryExecuterFactory"); /* You may need to execute following line just once in your application. You can try to move it into initialization or static code block. Also you may notice that line below is marked as deprecated. However in my case the export to pdf fails without this line being executed. */ JRProperties.setProperty( JRQueryExecuterFactory.QUERY_EXECUTER_FACTORY_PREFIX+"plsql" ,"com.jaspersoft.jrx.query.PlSqlQueryExecuterFactory"); /* Prepare Jasper print and exporter objects in lines below */ JasperPrint print = JasperFillManager.fillReport(jasperReport, params, conn); JRExporter exporter = new JRPdfExporter(); exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, outputStream); exporter.setParameter(JRExporterParameter.JASPER_PRINT, print); exporter.exportReport(); // Generate PDF report return outputStream; // Return stream result to your application code // to wrap in response object. } catch (Exception ex) { log.severe("Exception loading pdf: " + ex.getMessage()); } This should be all you need. If you find this article useful - please mark it as such. - Eugene Luzgin (eluzgin@gmail.com)
×
×
  • Create New...