Xpr2PdfWorker.java
/*
** Module : Xpr2PdfWorker.java
** Abstract : Class to generate PDF file from collected XPR objects.
**
** Copyright (c) 2018-2024, Golden Code Development Corporation.
**
** -#- -I- --Date-- ---------------------------------Description----------------------------------
** 001 EVL 20180516 Created initial version.
** 002 EVL 20180522 Adding multi-page support. Images can use bottom right coort as starting
** point. Now supported.
** 003 EVL 20180527 Clean up and code optimization. Make rectangle to be transparent.
** 004 EVL 20180531 Fixes for object's X coordinate calculation. Adding filled rectangle support.
** 005 EVL 20180625 Changed logging level to disable message box for report compile and fill.
** 006 EVL 20181007 Adding ability to get image from application jar file.
** EVL 20181016 Fix for different font sizing in different OS. Need to take into account
** when computing bounding rectangle.
** 007 EVL 20190514 Changing source type to common interface to be use with different possible
** PDF sources. Typo error fix.
** 008 EVL 20190617 Adding line style attribute support.
** 009 EVL 20211026 Changing text font size type to float.
** EVL 20220519 For image consistency check we have to test image bytes size in addition to verify the
** image byte pointer array is not null.
** 010 GBB 20240419 Ghost4j lib replaced by Apache pdfbox.
*/
/*
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU Affero General Public License as
** published by the Free Software Foundation, either version 3 of the
** License, or (at your option) any later version.
**
** This program 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 Affero General Public License for more details.
**
** You may find a copy of the GNU Affero GPL version 3 at the following
** location: https://www.gnu.org/licenses/agpl-3.0.en.html
**
** Additional terms under GNU Affero GPL version 3 section 7:
**
** Under Section 7 of the GNU Affero GPL version 3, the following additional
** terms apply to the works covered under the License. These additional terms
** are non-permissive additional terms allowed under Section 7 of the GNU
** Affero GPL version 3 and may not be removed by you.
**
** 0. Attribution Requirement.
**
** You must preserve all legal notices or author attributions in the covered
** work or Appropriate Legal Notices displayed by works containing the covered
** work. You may not remove from the covered work any author or developer
** credit already included within the covered work.
**
** 1. No License To Use Trademarks.
**
** This license does not grant any license or rights to use the trademarks
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks
** of Golden Code Development Corporation. You are not authorized to use the
** name Golden Code, FWD, or the names of any author or contributor, for
** publicity purposes without written authorization.
**
** 2. No Misrepresentation of Affiliation.
**
** You may not represent yourself as Golden Code Development Corporation or FWD.
**
** You may not represent yourself for publicity purposes as associated with
** Golden Code Development Corporation, FWD, or any author or contributor to
** the covered work, without written authorization.
**
** 3. No Misrepresentation of Source or Origin.
**
** You may not represent the covered work as solely your work. All modified
** versions of the covered work must be marked in a reasonable way to make it
** clear that the modified work is not originating from Golden Code Development
** Corporation or FWD. All modified versions must contain the notices of
** attribution required in this license.
*/
package com.goldencode.p2j.reporting;
import com.goldencode.p2j.ui.client.*;
import com.goldencode.p2j.ui.client.gui.driver.*;
import com.goldencode.p2j.util.*;
import net.sf.jasperreports.components.*;
import net.sf.jasperreports.components.barbecue.*;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.component.*;
import net.sf.jasperreports.engine.design.*;
import net.sf.jasperreports.engine.export.*;
import net.sf.jasperreports.engine.type.*;
import net.sf.jasperreports.export.*;
import org.apache.pdfbox.cos.*;
import org.apache.pdfbox.pdmodel.*;
import org.apache.pdfbox.pdmodel.graphics.*;
import javax.imageio.*;
import java.awt.image.BufferedImage;
import java.awt.Color;
import java.io.*;
import java.util.*;
/**
* The object prepares the data structures required to build consistent Jasper report object and
* initiates the process of generating output PDF file via Jasper API.
*/
public class Xpr2PdfWorker
{
/** Font style normal. */
public static final int FONT_STYLE_NORMAL = 0;
/** Font style bold. */
public static final int FONT_STYLE_BOLD = 1;
/** Font style bold and italic. */
public static final int FONT_STYLE_BOLD_ITALIC = 2;
/** Font style italic. */
public static final int FONT_STYLE_ITALIC = 3;
/** Default DPI value for screen report. */
public static final int DPI_DEFAULT = 72;
/** The XPR base object to get all required info about objects to include in output. */
private PdfProvider xprBaseInput = null;
/** The output PDF file name to generate. */
private String pdfFileName = null;
/** Parameters for Jasper export. */
private Map<String, Object> jspParameters = new HashMap<String, Object>();
/** Row modifier to convert XPR helper row coords to pixel. */
private double pixPerRow = XprHelper.getRow2Pix(DPI_DEFAULT, XprEntity.LPI_DEFAULT);
/** Column modifier to convert XPR helper character coords to pixel. */
private double pixPerCol = XprHelper.getCol2Pix(DPI_DEFAULT, XprEntity.CPI_DEFAULT);
/** Variable to keep tracking the next parameter number. */
private long parameterCounter = 0;
/** Map to match OS font prefix names to PDF generic font names. */
private static Map<String, String[]> pdfFontMap = new HashMap<String, String[]>();
/** Extra size for the font cell, depending on OS behind. */
private static int fntExtra = !XprHelper.isSimpleUseMode() &&
EnvironmentOps.isUnderWindowsFamily() ? 2 : 5;
/** One time font mapping init. */
static
{
// font map
pdfFontMap.put("Arial", new String[] {"Helvetica", "Helvetica-Bold",
"Helvetica-BoldOblique", "Helvetica-Oblique" });
pdfFontMap.put("Times", new String[] {"Times-Roman", "Times-Bold",
"Times-BoldItalic", "Times-Italic"});
pdfFontMap.put("Courier", new String[] {"Courier", "Courier-Bold",
"Courier-BoldOblique", "Courier-Oblique"});
pdfFontMap.put("Symbol", new String[] {"Symbol"});
pdfFontMap.put("Dingbats", new String[] {"ZapfDingbats"});
}
/**
* Creates new instance of the object with the given file name to export.
*
* @param xprBase
* The XPR object to be the base for Jasper report creation.
* @param pdfName
* The PDF file object to export.
*/
public Xpr2PdfWorker(PdfProvider xprBase, String pdfName)
{
pdfFileName = pdfName;
xprBaseInput = xprBase;
}
/**
* Maps the given OS dependent font name to OS independent PDF font.
*
* @param fntName
* The name of the OS dependent font name to be mapped to PDF font.
* @param fntAttr
* The desired font attributes, normal, bold or italic.
*
* @return The PDF font name to use or <code>NULL</code> if mapping is not possible.
*/
public static String getMappedPdfFontName(String fntName, int fntAttr)
{
// NPE check
if (fntName == null || fntName.isEmpty())
{
return null;
}
// walk through map to find the first good match
for (String fntKey : pdfFontMap.keySet())
{
// fond close enough match
if (fntName.indexOf(fntKey) != -1)
{
// get available pdf font falily array
String[] pfdFontArr = pdfFontMap.get(fntKey);
// if we have fons other than just normal
if (pfdFontArr.length > fntAttr)
{
// return PDF font name to use according requested attributes
return pfdFontArr[fntAttr];
}
else
{
// return PDF font name to use, simple normal version
return pfdFontArr[0];
}
}
}
// nothing has found
return null;
}
/**
* Creates on the fly Jasper report and exports into PDF.
*
* @param simpleUsage
* <code>TRUE</code> means simple using as standalone application for debugging.
*
* @return <code>TRUE</code> if success, <code>FALSE</code> otherwise.
*/
public boolean export(boolean simpleUsage)
{
boolean result = false;
// multi page support
List<JasperPrint> jspPrintList = new ArrayList<JasperPrint>();
for (int i = 0; i < xprBaseInput.getTotalPageNumbers(); i++)
{
// create report design based on current XPR data
JasperDesign jspDesign = makeJasperDesign(i);
if (jspDesign == null)
{
return result;
}
// compile next page report
JasperReport jspReport = null;
try
{
jspReport = JasperCompileManager.compileReport(jspDesign);
}
catch (JRException jre)
{
XprHelper.displayOrLogError(String.format("Report compiling error for page %d", i),
jre);
return result;
}
// fill next page report with data
JasperPrint jspPrint = null;
try
{
jspPrint = JasperFillManager.fillReport(jspReport, jspParameters,
new JREmptyDataSource());
}
catch (JRException jre)
{
XprHelper.displayOrLogError(String.format("Report fill error for page %d", i), jre);
return result;
}
// collect all filled pages
jspPrintList.add(jspPrint);
}
// export report
Stream rptStream = null;
OutputStream outStreamBuff = null;
if (!simpleUsage)
{
try
{
rptStream = StreamFactory.openFileStream(pdfFileName, true, false);
}
catch (ErrorConditionException ece)
{
XprHelper.displayOrLogError(
String.format("Error opening output stream for PDF file %s", pdfFileName), ece);
return result;
}
outStreamBuff = new BufferedOutputStream(new OutputStreamWrapper(rptStream), 16384);
}
// only PDF exporter is in action for now
try
{
JRPdfExporter exporter = new JRPdfExporter();
if (simpleUsage)
{
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(pdfFileName));
}
else
{
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(outStreamBuff));
}
// pass oredered pages as input source
exporter.setExporterInput(SimpleExporterInput.getInstance(jspPrintList));
exporter.exportReport();
}
catch (JRException jre)
{
XprHelper.displayOrLogError(
String.format("Report export error for file %s", pdfFileName), jre);
return result;
}
finally
{
try
{
if (!simpleUsage)
{
outStreamBuff.flush();
outStreamBuff.close();
}
}
catch (IOException ioe)
{
XprHelper.displayOrLogError(
String.format("Stream closing error for file %s", pdfFileName), ioe);
return result;
}
}
result = true;
return result;
}
/**
* Creates on the fly Jasper report.
*
* @param pageNum
* 0 based page number to prepare on the fly report.
*
* @return jasper design object for current XPR data.
*/
private JasperDesign makeJasperDesign(int pageNum)
{
// temporary variables used while design creation
JRDesignBand band = null;
// start using new Jasper design
JasperDesign resDesign = new JasperDesign();
resDesign.setName(pdfFileName.concat("_InMemoryReport"));
// set up page properties
// page size, the page margins to be defined in directory
int pageWidth = xprBaseInput.getPageWidth(pageNum);
// error while getting current page width
if (pageWidth == -1)
{
pageWidth = XprHelper.getPdfPageWidth();
}
int pageHeight = xprBaseInput.getPageHeight(pageNum);
// error while getting current page height
if (pageHeight == -1)
{
pageHeight = XprHelper.getPdfPageHeight();
}
resDesign.setPageWidth(pageWidth);
resDesign.setPageHeight(pageHeight);
// 0 for all margins and spacing
resDesign.setColumnSpacing(0);
resDesign.setLeftMargin(0);
resDesign.setRightMargin(0);
resDesign.setTopMargin(0);
resDesign.setBottomMargin(0);
// title
band = new JRDesignBand();
resDesign.setTitle(band);
// page header
band = new JRDesignBand();
resDesign.setPageHeader(band);
// column header
band = new JRDesignBand();
resDesign.setColumnHeader(band);
// detail
band = new JRDesignBand();
band.setHeight(pageHeight);
// we will use only one band for all objects in page
setObjectsInBand(band, xprBaseInput.getObjectsInPage(pageNum), resDesign);
// adding completed band to the report
((JRDesignSection)resDesign.getDetailSection()).addBand(band);
// column footer
band = new JRDesignBand();
resDesign.setColumnFooter(band);
// page footer
band = new JRDesignBand();
resDesign.setPageFooter(band);
// summary
band = new JRDesignBand();
resDesign.setSummary(band);
return resDesign;
}
/**
* Creates new unique parameter name for Jasper report.
*
* @return jasper design parameter name.
*/
private String getNewParameterName()
{
StringBuilder sb = new StringBuilder();
// same prefix for all parameters
sb.append("Xpr2PdfParameter");
// but different suffix
sb.append(++parameterCounter);
return sb.toString();
}
/**
* Adding different objects to the given Jasper report band keeping Z-order.
*
* @param objBand
* The Jasper report band to prepare.
* @param objectsToAdd
* The list of objects to add.
* @param jspDesign
* The current design in use.
*/
private void setObjectsInBand(JRDesignBand objBand, List<XprObjBase> objectsToAdd,
JasperDesign jspDesign)
{
// walk through the objects from start to end
for (int i = 0; i < objectsToAdd.size(); i++)
{
XprObjBase xprObj = objectsToAdd.get(i);
// this is object dependent set up procedure
switch (xprObj.getType())
{
case XprObjBase.TYPE_LINE:
setLineInBand(objBand, (XprObjLine)xprObj);
break;
case XprObjBase.TYPE_RECTANGLE:
setRectangleInBand(objBand, (XprObjRectangle)xprObj, false);
break;
case XprObjBase.TYPE_FILLED_RECTANGLE:
setRectangleInBand(objBand, (XprObjFilledRectangle)xprObj, true);
break;
case XprObjBase.TYPE_TEXT:
setTextInBand(objBand, (XprObjText)xprObj, jspDesign);
break;
case XprObjBase.TYPE_IMAGE:
setImageInBand(objBand, (XprObjImage)xprObj, jspDesign);
break;
case XprObjBase.TYPE_BARCODE:
setBarcodeInBand(objBand, (XprObjBarcode)xprObj, jspDesign);
break;
}
}
}
/**
* Adding line object to the given Jasper report band as next one.
*
* @param objBand
* The Jasper report band to prepare.
* @param objLineToAdd
* The line object to add.
*/
private void setLineInBand(JRDesignBand objBand, XprObjLine objLineToAdd)
{
// reconstruct the line
JRDesignLine line = new JRDesignLine();
// coordinates and size
double x1 = objLineToAdd.getColumn() - 1.0;
double x2 = objLineToAdd.getColumnEnd() - 1.0;
double y1 = objLineToAdd.getRow() - 1.0;
double y2 = objLineToAdd.getRowEnd() - 1.0;
// x, y requires floor() to compute, may be dx, dy too
int x = (int)Math.floor((x1 + objLineToAdd.getLeftMargin()) * pixPerCol);
int y = (int)Math.floor((y1 + objLineToAdd.getTopMargin()) * pixPerRow);
int dx = (int)Math.round((x2-x1) * pixPerCol);
int dy = (int)Math.round((y2-y1) * pixPerRow);
line.setX(x);
line.setY(y);
line.setWidth(dx);
line.setHeight(dy);
line.setForecolor(new Color(objLineToAdd.getColor()));
// do we really need the line width, ignore for now?
line.getLinePen().setLineWidth(objLineToAdd.getLineWidth());
line.getLinePen().setLineStyle(objLineToAdd.getLineStyle());
// add new line to the band
objBand.addElement(line);
}
/**
* Adding line object to the given Jasper report band as next one.
*
* @param objBand
* The Jasper report band to prepare.
* @param objRectangleToAdd
* The rectangle object to add.
* @param isFilled
* <code>TRUE</code> for filled recteangle, <code>FALSE</code> for frame.
*/
private void setRectangleInBand(JRDesignBand objBand, XprObjRectangle objRectangleToAdd,
boolean isFilled)
{
// reconstruct the rect
JRDesignRectangle rect = new JRDesignRectangle();
// coordinates and size
double x1 = objRectangleToAdd.getColumn() - 1.0;
double x2 = objRectangleToAdd.getColumnEnd() - 1.0;
double y1 = objRectangleToAdd.getRow() - 1.0;
double y2 = objRectangleToAdd.getRowEnd() - 1.0;
// x, y requires floor() to compute, may be dx, dy too
int x = (int)Math.floor((x1 + objRectangleToAdd.getLeftMargin()) * pixPerCol);
int y = (int)Math.floor((y1 + objRectangleToAdd.getTopMargin()) * pixPerRow);
int dx = (int)Math.round((x2-x1) * pixPerCol);
int dy = (int)Math.round((y2-y1) * pixPerRow);
rect.setX(x);
rect.setY(y);
rect.setWidth(dx);
rect.setHeight(dy);
rect.setForecolor(new Color(objRectangleToAdd.getColor()));
// line width
rect.getLinePen().setLineWidth(objRectangleToAdd.getRectLineWidth());
rect.getLinePen().setLineStyle(objRectangleToAdd.getRectLineStyle());
// disable fill for framed rerctangle
if (isFilled)
{
rect.setBackcolor(new Color(((XprObjFilledRectangle)objRectangleToAdd).getFillColor()));
}
else
{
rect.setMode(ModeEnum.TRANSPARENT);
}
// set up rounded rectangle
if (objRectangleToAdd.isRounded())
{
// the rounded radius computing approach:
// - get the minimum af both directions
// - take 1/4 of the size as radius
rect.setRadius((int)Math.round((double)Math.min(dx, dy) * XprObjRectangle.RADIUS_SCALE));
}
// add new line to the band
objBand.addElement(rect);
}
/**
* Adding text object to the given Jasper report band as next one.
*
* @param objBand
* The Jasper report band to prepare.
* @param objTextToAdd
* The text object to add.
* @param jspDesign
* The current design in use.
*/
private void setTextInBand(JRDesignBand objBand, XprObjText objTextToAdd,
JasperDesign jspDesign)
{
// reconstruct the text as static one
double x1 = objTextToAdd.getColumn() - 1.0;
double y1 = objTextToAdd.getRow() - 1.0;
// x, y requires floor() to compute, may be dx, dy too
int x = (int)(Math.floor((x1 + objTextToAdd.getLeftMargin()) * pixPerCol));
int y = (int)(Math.floor((y1 + objTextToAdd.getTopMargin()) * pixPerRow));
// we need extra pixels to make text visible if font size is bigger than row
// compute them as 1/fntExtra of the font size
float fontSizeFloat = objTextToAdd.getFontSize();
int fntSize = (int)Math.round(fontSizeFloat);
int dy = Math.max((int)Math.round(pixPerRow), fntSize + (fntSize + fntExtra)/fntExtra);
int dx = (int)Math.max((int)Math.round(pixPerCol),
(fntSize + fntExtra)/fntExtra + (int)(pixPerCol*(double)fntSize/pixPerRow));
// text to display
String textLine = objTextToAdd.getValue();
// need to exclude text that outside the page area
if (x > jspDesign.getPageWidth() - 1 || y > jspDesign.getPageHeight() - 1 ||
y + dy > jspDesign.getPageHeight() - 1)
{
XprHelper.displayOrLogError(
String.format("Text object %s is out of the page area.", textLine));
return;
}
JRDesignStaticText txt = new JRDesignStaticText();
txt.setX(x);
txt.setY(y);
txt.setHeight(dy);
txt.setWidth(dx * textLine.length());
// try to use OS independent font name
boolean isBold = objTextToAdd.isBold();
boolean isItalic = objTextToAdd.isItalic();
int fontStyle = isBold && isItalic ? FONT_STYLE_BOLD_ITALIC
: isBold ? FONT_STYLE_BOLD
: isItalic ? FONT_STYLE_ITALIC
: FONT_STYLE_NORMAL;
String pdfFntName = getMappedPdfFontName(objTextToAdd.getFontName(), fontStyle);
if (pdfFntName != null)
{
txt.setPdfFontName(pdfFntName);
}
txt.setFontSize(fontSizeFloat);
txt.setForecolor(new Color(objTextToAdd.getColor()));
txt.setText(textLine);
objBand.addElement(txt);
}
/**
* Adding image object to the given Jasper report band as next one.
*
* @param objBand
* The Jasper report band to prepare.
* @param objImageToAdd
* The image object to add.
* @param jspDesign
* The current design in use.
*/
private void setImageInBand(JRDesignBand objBand, XprObjImage objImageToAdd,
JasperDesign jspDesign)
{
boolean isPDFImage = false;
// reconstruct the image
JRDesignImage img = new JRDesignImage(jspDesign);
// coordinates and size
double x1 = objImageToAdd.getColumn() - 1.0;
double x2 = objImageToAdd.getColumnEnd() - 1.0;
double y1 = objImageToAdd.getRow() - 1.0;
double y2 = objImageToAdd.getRowEnd() - 1.0;
// need to support possible coord reverse
// x, y requires floor() to compute, may be dx, dy too
int x = (int)Math.floor((Math.min(x1, x2) + objImageToAdd.getLeftMargin()) * pixPerCol);
int y = (int)Math.floor((Math.min(y1, y2) + objImageToAdd.getTopMargin()) * pixPerRow);
int dx = (int)Math.round(Math.abs(x2-x1) * pixPerCol);
int dy = (int)Math.round(Math.abs(y2-y1) * pixPerRow);
img.setX(x);
img.setY(y);
int pageHeight = jspDesign.getPageHeight();
int pageWidth = jspDesign.getPageWidth();
// check if the image is not outside the page and compute scaling coeff
int exDy = y + dy - pageHeight + 1;
double kYCut = 1.0;
if (exDy > 0)
{
kYCut = (double)(dy - exDy) / (double)(dy);
dy -= exDy;
}
int exDx = x + dx - pageWidth + 1;
double kXCut = 1.0;
if (exDx > 0)
{
kXCut = (double)(dx - exDx) / (double)(dx);
dx -= exDx;
}
img.setWidth(dx);
img.setHeight(dy);
img.setScaleImage(ScaleImageEnum.RETAIN_SHAPE);
// images should be handled via report parameter
String imagePath = objImageToAdd.getLocationName();
// find out if embedded image is pdf
int extNdx = imagePath.lastIndexOf(".");
if (extNdx != -1 && extNdx < imagePath.length() - 3)
{
String imgExtension = imagePath.substring(extNdx + 1);
if (imgExtension != null && imgExtension.toUpperCase().equals("PDF"))
{
isPDFImage = true;
}
}
String paramName = getNewParameterName();
// insert parameter into design
JRDesignParameter param = new JRDesignParameter();
param.setName(paramName);
// call is deprecated, no more required according to Jaspersoft
BufferedImage imgToUse = null;
if (isPDFImage)
{
param.setValueClass(java.awt.Image.class);
}
else
{
// try to get the image stream from application jar
InputStream isImg = FwdJasperExtensionRegistry.getRepositoryService()
.getInputStream(imagePath);
if (isImg != null)
{
byte[] imgBytes = UiUtils.getData(isImg);
if (imgBytes != null && imgBytes.length > 0)
{
imgToUse = AbstractGuiDriver.createImage(imgBytes).getImage();
}
}
if (imgToUse != null)
{
param.setValueClass(java.awt.Image.class);
}
else
{
param.setValueClass(java.lang.String.class);
}
}
try
{
jspDesign.addParameter(param);
}
catch (JRException jre)
{
XprHelper.displayOrLogError("Image parameter adding error for JasperDesign", jre);
return;
}
// insert expression into design
JRDesignExpression expr = new JRDesignExpression();
// call is deprecated, no more required according to Jaspersoft
// expr.setValueClass(java.lang.String.class);
expr.setText("$P{".concat(paramName).concat("}"));
img.setExpression(expr);
if (XprHelper.onErrorImageSilentMode())
{
img.setOnErrorType(OnErrorTypeEnum.ICON);
}
// add new line to the band
objBand.addElement(img);
// set up new parameter
if(isPDFImage)
{
// get the input stream from helper
InputStream is = XprHelper.getInputStream(imagePath);
if (is == null)
{
XprHelper.displayOrLogError(
String.format("Unable to get input stream from image path %s", imagePath));
return;
}
// set up PDF reader
try
{
PDDocument document = PDDocument.load(is);
Iterator<PDPage> pdPageIterator = document.getDocumentCatalog().getPages().iterator();
List<BufferedImage> images = new ArrayList<>();
while (pdPageIterator.hasNext())
{
PDPage page = pdPageIterator.next();
PDResources resources = page.getResources();
for (COSName nextObjName : resources.getXObjectNames())
{
if (resources.isImageXObject(nextObjName))
{
PDXObject pdxImage = resources.getXObject(nextObjName);
byte[] pdxImageBytes = pdxImage.getStream().toByteArray();
BufferedImage image = ImageIO.read(new ByteArrayInputStream(pdxImageBytes));
images.add(image);
}
}
}
// take a first image with truncating
if ( kXCut != 1.0 || kYCut != 1.0 )
{
// we need to truncate the image a bit
BufferedImage bi = images.get(0);
imgToUse = bi.getSubimage(0, 0, (int)(kXCut * (double)bi.getWidth()),
(int)(kYCut * (double)bi.getHeight()));
}
else
{
imgToUse = images.get(0);
}
jspParameters.put(paramName, imgToUse);
}
catch (Exception e)
{
XprHelper.displayOrLogError(String.format("PDF file %s loading error", imagePath), e);
return;
}
}
else
{
if (imgToUse != null)
{
jspParameters.put(paramName, imgToUse);
}
else
{
jspParameters.put(paramName, imagePath);
}
}
}
/**
* Adding barcode object to the given Jasper report band as next one.
*
* @param objBand
* The Jasper report band to prepare.
* @param objBarcodeToAdd
* The barcode object to add.
* @param jspDesign
* The current design in use.
*/
private void setBarcodeInBand(JRDesignBand objBand, XprObjBarcode objBarcodeToAdd,
JasperDesign jspDesign)
{
// check for barcode type
String type = objBarcodeToAdd.getBarcodeType();
if (type == null || type.isEmpty())
{
// barcode type is invalid, no reasons to continue
XprHelper.displayOrLogError("Barcode type is invalid");
return;
}
// reconstruct the barcode
StandardBarbecueComponent bcBBCComp = new StandardBarbecueComponent();
// set type
bcBBCComp.setType(type);
// barcodes should be handled via report parameter
String barcodeValue = objBarcodeToAdd.getParameterValue("VALUE");
String paramName = getNewParameterName();
// insert parameter into design
JRDesignParameter param = new JRDesignParameter();
param.setName(paramName);
param.setValueClass(java.lang.String.class);
try
{
jspDesign.addParameter(param);
}
catch (JRException jre)
{
XprHelper.displayOrLogError("Barcode parameter adding error for JasperDesign", jre);
return;
}
// checksum and show text
bcBBCComp.setChecksumRequired(objBarcodeToAdd.isChecksum());
bcBBCComp.setDrawText(objBarcodeToAdd.isShow());
// insert expression into design
JRDesignExpression expr = new JRDesignExpression();
// call is deprecated, no more required according to Jaspersoft doc
// expr.setValueClass(java.lang.String.class);
expr.setText("$P{".concat(paramName).concat("}"));
bcBBCComp.setCodeExpression(expr);
// coordinates and size
double x1 = objBarcodeToAdd.getColumn() - 1.0;
double x2 = objBarcodeToAdd.getColumnEnd() - 1.0;
double y1 = objBarcodeToAdd.getRow() - 1.0;
double y2 = objBarcodeToAdd.getRowEnd() - 1.0;
// x, y requires floor() to compute, may be dx, dy too
int x = (int)Math.floor((x1 + objBarcodeToAdd.getLeftMargin()) * pixPerCol);
int y = (int)Math.floor((y1 + objBarcodeToAdd.getTopMargin()) * pixPerRow);
int dx = (int)Math.round((x2-x1) * pixPerCol);
int dy = (int)Math.round((y2-y1) * pixPerRow);
// dimensions for barcode
// the bar width option is the minimum element(single line) width, assume to be 1 pixel
// or more depending on size ratio and barcode type
int bWidth = objBarcodeToAdd.getBarWidth(dx, dy);
if (bWidth > 0)
{
bcBBCComp.setBarWidth(bWidth);
}
// barcode width means the height might be adjusted too
if (bWidth > 1)
{
bcBBCComp.setBarHeight(objBarcodeToAdd.getBarHeight(dx, dy));
}
else
{
bcBBCComp.setBarHeight(dy);
}
// create design element wrapper
JRDesignComponentElement jrComp = new JRDesignComponentElement();
// location and size
jrComp.setX(x);
jrComp.setY(y);
// set up barcode rotation and enclosed element size
String angle = objBarcodeToAdd.getParameterValue("ANGLE");
if (!angle.isEmpty())
{
if (angle.equals("90"))
{
jrComp.setWidth(dy);
jrComp.setHeight(dx);
bcBBCComp.setRotation(RotationEnum.LEFT);
}
else if (angle.equals("180"))
{
jrComp.setWidth(dx);
jrComp.setHeight(dy);
bcBBCComp.setRotation(RotationEnum.UPSIDE_DOWN);
}
else if (angle.equals("270"))
{
jrComp.setWidth(dy);
jrComp.setHeight(dx);
bcBBCComp.setRotation(RotationEnum.RIGHT);
}
else if (!angle.equals("0") && !angle.equals("360"))
{
// unsupported angle here
XprHelper.displayOrLogError(
String.format("Barcode angle %s is not supported.", angle));
}
}
else
{
jrComp.setWidth(dx);
jrComp.setHeight(dy);
}
// insert barcode into design component to be added
jrComp.setComponent(bcBBCComp);
jrComp.setComponentKey(
new ComponentKey(ComponentsExtensionsRegistryFactory.NAMESPACE, "c",
ComponentsExtensionsRegistryFactory.BARBECUE_COMPONENT_NAME));
// add new line to the band
objBand.addElement(jrComp);
// set up new parameter
jspParameters.put(paramName, barcodeValue);
}
}