I am working with Java, Jasper Reports, and JSF. I want to generate a report in PDF format and can display it in an iframe something like this because on the same page to be able to put filters to reports and so on .. the report is generated correctly as a temp file even when I paste the path in the browser will display correct. but to put that route in an iframe I get the following error:
Not allowed to load the local resource: file :/ / / C :/ Users / Juanes ~ 1/AppData/Local/Temp/reportePedidosTemporal1922509630584311367.pdf
this is my code:
public void prueba(AjaxBehaviorEvent evento)
{
try
{
Map<String, Object> parametros = new HashMap<String, Object>();
parametros.put("autor", "Juan Esteban");
parametros.put("titulo", "Reporte de Pedidos");
List<PedidosVO> listaPedidos = new ArrayList<PedidosVO>();
for (int i = 1; i <= 10; i++)
{
PedidosVO pedido = new PedidosVO(""+i,"Cliente:"+i,(i+1));
pedido.setPuntos(i);
listaPedidos.add(pedido);
}
JasperDesign design = JRXmlLoader.load("C:\\Reportes\\Reporte2Pedidos.jrxml");
JasperReport reporte = JasperCompileManager.compileReport(design);
//-**-**-/*-/
JasperPrint jasperPrint = JasperFillManager.fillReport(reporte, parametros,new JRBeanCollectionDataSource(listaPedidos));
byte[] flujo = JasperExportManager.exportReportToPdf(jasperPrint);
File tempFile = File.createTempFile("reportePedidosTemporal",".pdf");
System.out.println(tempFile.setReadable(true));
escribirByte(tempFile,flujo);
tempFile.deleteOnExit();
if(tempFile.exists())
{
System.out.println("RUTA : "+tempFile.getPath());
url = tempFile.getPath();
}
}
catch(Exception e)
{
e.printStackTrace();
}
}
Any ideas, suggestions would be very grateful
You need to have a URL that points to the PDF that you load into the iframe. As it stands, you're attempting to load a file directly from the filesystem. This means your site will attempt to load a file from the end user's filesystem directly, which is not allowed for obvious reasons.
Generate the PDF on your server, then generate a URL that points to it. Then set that as the iframe source.
Related
i made a Java application whose purpose is to offer a Print Preview for PS files.
My program uses Ghostscript and Ghost4J to load the Post Script file and produces a list of Images (one for each page) using the SimpleRenderer.render method. Then using a simple JList i show only the image corresponding to the page the user selected in JList.
This worked fine until a really big PS file occurred, causing an OutOfMemoryError when executing the code
PSDocument pdocument = new PSDocument(new File(filename));
I know that is possibile to read a file a little at a time using InputStreams, the problem is that i can't think of a way to connect the bytes that i read with the actual pages of the document.
Example, i tried to read from PS file 100 MB at a time
int buffer_size = 100000000;
byte[] buffer = new byte[buffer_size];
FileInputStream partial = new FileInputStream(filename);
partial.read(buffer, 0, buffer_size);
document.load(new ByteArrayInputStream(buffer));
SimpleRenderer renderer = new SimpleRenderer();
//how many pages do i have to read?
List<Image> images = renderer.render(document, firstpage ??, lastpage ??);
Am i missing some Ghost4J functionality to read partially a file?
Or has someone other suggestions / approaches about how to solve this problem in different ways?
I am really struggling
I found out I can use Ghost4J Core API to retrieve from a Post Script file a reduced set of pages as Images.
Ghostscript gs = Ghostscript.getInstance();
String[] gsArgs = new String[9];
gsArgs[0] = "-dQUIET";
gsArgs[1] = "-dNOPAUSE";
gsArgs[2] = "-dBATCH";
gsArgs[3] = "-dSAFER";
gsArgs[4] = "-sDEVICE=display";
gsArgs[5] = "-sDisplayHandle=0";
gsArgs[6] = "-dDisplayFormat=16#804";
gsArgs[7] = "-sPageList="+firstPage+"-"+lastPage;
gsArgs[8] = "-f"+filename;
//create display callback (capture display output pages as images)
ImageWriterDisplayCallback displayCallback = new ImageWriterDisplayCallback();
//set display callback
gs.setDisplayCallback(displayCallback);
//run PostScript (also works with PDF) and exit interpreter
try {
gs.initialize(gsArgs);
gs.exit();
Ghostscript.deleteInstance();
} catch (GhostscriptException e) {
System.out.println("ERROR: " + e.getMessage());
e.printStackTrace();
}
return displayCallback.getImages(); //return List<Images>
This solve the problem of rendering page as images in the preview.
However, i could not find a way to use Ghost4J to know total number of pages of PS file (in case the file is too big for opening it with Document.load()).
So, i am still here needing some help
USECASE:
I have a document stored on HELLOSIGN which is supposed to be sent to a signer after prepopulating it with some data. Additionally, I have a field in the document where in I should be able to upload the signer image from my DB.
What I have done:
TemplateSignatureRequest request = new TemplateSignatureRequest();
request.setTitle(title);
request.setSubject(emailSubject);
request.setMessage(message);
request.setSigner("ROLE", "<<email_id>>", name);
request.setClientId(CLIENT_ID);
request.setTemplateId(TEMPLATE_ID);
request.setTestMode(true);
request.setCustomFields(customFields);
HelloSignClient client = new HelloSignClient(API_KEY);
client.sendTemplateSignatureRequest(request);
QUESTION : Is there a way I can directly populate the image in the request object by using something like:
request.setDocuments(docs);
Or is there any other way I can achieve this?
Note: I could not mark the image part in the doc as a custom field since I could not find an option to do it on HelloSign
I am trying to replace the Picture section in the image below
The TemplateSignatureRequest extends AbstractRequest which has a function for adding a file
public void addFile(File file) throws HelloSignException {
this.addFile(file, (Integer)null);
}
This was taken from the library. So you can simply use
request.addFile(file);
I reached out to apisupport#hellosign.com to ask them if there is any way to achieve this, and this is the response I got:
"This is currently not available, However, We're always looking for ways to improve HelloSign API and we regularly release new versions of our products with better performance, additional features, and security enhancements. I'll reach out to our product team and pass this idea along as a feature enhancement for them to review to see if this is something we can place on our roadmap"
So, I figured out a work around using PDF stamper
private byte[] stampImageToDoc() throws Exception {
try {
PdfReader pdfReader = new PdfReader(<<template_pdf_path>>);
ByteArrayOutputStream os = new ByteArrayOutputStream();
PdfStamper pdfStamper = new PdfStamper(pdfReader, os);
PdfContentByte cb = pdfStamper.getOverContent(1);
File file = new File(<<imagePath>>);
byte[] imageFile = FileUtils.readFileToByteArray(file);
if (imageFile != null) {
Image image = Image.getInstance(imageFile);
image.scaleAbsoluteHeight(150);
image.scaleAbsoluteWidth(150);
image.setAbsolutePosition(29, 500); //position
cb.addImage(image);
}
pdfStamper.close();
return os.toByteArray();
} catch (DocumentException e) {
e.printStackTrace();
throw e;
} catch (IOException e) {
e.printStackTrace();
throw e;
}
}
}
Instead of using TemplateSignatureRequest we will be using SignatureRequest and add the stamped doc to send request::
SignatureRequest request = new SignatureRequest();
List<Signer> signers = new ArrayList<>();
Signer signer = new Signer(req.getStudentEmail(), "DME");
signers.add(signer);
request.setTitle(title);
request.setSubject(emailSubject);
request.setMessage(message);
request.setSigners(signers);
request.setClientId(CLIENT_ID);
request.setTestMode(true);
// Image
byte[] docBytes = stampImageToDoc();
List<Document> docs = new ArrayList<>();
Document d = new Document();
File tempFile = new File(<<temporary_path>>);
FileUtils.writeByteArrayToFile(tempFile, docBytes);
d.setFile(tempFile);
docs.add(d);
request.setDocuments(docs);
HelloSignClient client = new HelloSignClient(API_KEY);
client.sendSignatureRequest(request);
Note: This might not be the best solution, but its just a workaround i could think of
I have a shape file and i need to read the shape file from my java code. I used below code for reading shape file.
public class App {
public static void main(String[] args) {
File file = new File("C:\\Test\\sample.shp");
Map<String, Object> map = new HashMap<>();//
try {
map.put("url", URLs.fileToUrl(file));
DataStore dataStore = DataStoreFinder.getDataStore(map);
String typeName = dataStore.getTypeNames()[0];
SimpleFeatureSource source = dataStore.getFeatureSource(typeName);
SimpleFeatureCollection collection = source.getFeatures();
try (FeatureIterator<SimpleFeature> features = collection.features()) {
while (features.hasNext()) {
SimpleFeature feature = features.next();
SimpleFeatureType schema = feature.getFeatureType();
Class<?> geomType = schema.getGeometryDescriptor().getType().getBinding();
String type = "";
if (Polygon.class.isAssignableFrom(geomType) || MultiPolygon.class.isAssignableFrom(geomType)) {
MultiPolygon geom = (MultiPolygon) feature.getDefaultGeometry();
type = "Polygon";
if (geom.getNumGeometries() > 1) {
type = "MultiPolygon";
}
} else if (LineString.class.isAssignableFrom(geomType)
|| MultiLineString.class.isAssignableFrom(geomType)) {
} else {
}
System.out.println(feature.getDefaultGeometryProperty().getValue().toString());
}
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
I got the desired output. But my requirement is write an aws lambda function to read shape file. For this
1. I created a Lambda java project of s3 event. I wrote the same code inside the handleRequest. I uploaded the java lambda project as a lanbda function and added one trigger. When I am uploading a .shp file to as s3 bucket lmbda function will automatically invoked. But I am getting an error like below
java.lang.RuntimeException: java.io.FileNotFoundException: /sample.shp (No such file or directory)
I have sample.shp file inside my s3 bucket. I go through below link.
How to write an S3 object to a file?
I am getting the same error. I tried to change my code like below
S3Object object = s3.getObject(new GetObjectRequest(bucket, key));
InputStream objectData = object.getObjectContent();
map.put("url", objectData );
instead of
File file = new File("C:\\Test\\sample.shp");
map.put("url", URLs.fileToUrl(file));
:-( Now i am getting an error like below
java.lang.NullPointerException
Also I tried the below code
DataStore dataStore = DataStoreFinder.getDataStore(objectData);
instead of
DataStore dataStore = DataStoreFinder.getDataStore(map);
the error was like below
java.lang.ClassCastException:
com.amazonaws.services.s3.model.S3ObjectInputStream cannot be cast to
java.util.Map
Also I tried to add key directly to the map and also as DataStore object. Everything went wrong..:-(
Is there anyone who can help me?
It will be very helpful if someone can do it for me...
The DataStoreFinder.getDataStore method in geotools requires you to provide a map containing a key/value pair with key "url". The value associated with that "url" key needs to be a file URL like "file://host/path/my.shp".
You're trying to insert a Java input stream into the map. That won't work, because it's not a file URL.
The geotools library does not accept http/https URLs (see the geotools code here and here), so you need a file:// URL. That means you will need to download the file from S3 to the local Lambda filesystem and then provide a file:// URL pointing to that local file. To do that, here's Java code that should work:
// get the shape file from S3 to local filesystem
File localshp = new File("/tmp/download.shp");
s3.getObject(new GetObjectRequest(bucket, key), localshp);
// now store file:// URL in the map
map.put("url", localshp.getURI().getURL().toString());
If the geotools library had accepted real URLs (not just file:// URLs) then you could have avoided the download and simply created a time-limited, pre-signed URL for the S3 object and put that URL into the map.
Here's an example of how to do that:
// get current time and add one hour
java.util.Date expiration = new java.util.Date();
long msec = expiration.getTime();
msec += 1000 * 60 * 60;
expiration.setTime(msec);
// request pre-signed URL that will allow bearer to GET the object
GeneratePresignedUrlRequest gpur = new GeneratePresignedUrlRequest(bucket, key);
gpur.setMethod(HttpMethod.GET);
gpur.setExpiration(expiration);
// get URL that will expire in one hour
URL url = s3.generatePresignedUrl(gpur);
I am testing my method with this form https://help.adobe.com/en_US/Acrobat/9.0/Samples/interactiveform_enabled.pdf
It is being called like so:
Pdf.editForm("./src/main/resources/pdfs/interactiveform_enabled.pdf", "./src/main/resources/pdfs/FILLEDOUT.pdf"));
where Pdf is just a worker class and editForm is a static method.
The editForm method looks like this:
public static int editForm(String inputPath, String outputPath) {
try {
PdfDocument pdf = new PdfDocument(new PdfReader(inputPath), new PdfWriter(outputPath));
PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);
Map<String, PdfFormField> m = form.getFormFields();
for (String s : m.keySet()) {
if (s.equals("Name_First")) {
m.get(s).setValue("Tristan");
}
if (s.equals("BACHELORS DEGREE")) {
m.get(s).setValue("Off"); // On or Off
}
if (s.equals("Sex")) {
m.get(s).setValue("FEMALE");
}
System.out.println(s);
}
pdf.close();
logger.info("Completed");
} catch (IOException e) {
logger.error("Unable to fill form " + outputPath + "\n\t" + e);
return 1;
}
return 0;
}
Unfortunately the FILLEDOUT.pdf file is no longer a form after calling this method. Am I doing something wrong?
I was using this resource for guidance. Notice how I am not calling the form.flattenFields(). If I do call that method however, I get an error of java.lang.IllegalArgumentException.
Thank you for your time.
Your form is Reader-enabled, i.e. it contains a usage rights digital signature by a key and certificate issued by Adobe to indicate to a regular Adobe Reader that it shall activate a number of additional features when operating on that very PDF.
If you stamp the file as in your original code, the existing PDF objects will get re-arranged and slightly changed. This breaks the usage rights signature, and Adobe Reader, recognizing that, disclaims "The document has been changed since it was created and use of extended features is no longer available."
If you stamp the file in append mode, though, the changes are appended to the PDF as an incremental update. Thus, the signature still correctly signs its original byte range and Adobe Reader does not complain.
To activate append mode, use StampingProperties when you create your PdfDocument:
PdfDocument pdf = new PdfDocument(new PdfReader(inputPath), new PdfWriter(outputPath), new StampingProperties().useAppendMode());
(Tested with iText 7.1.1-SNAPSHOT and Adobe Acrobat Reader DC version 2018.009.20050)
By the way, Adobe Reader does not merely check the signature, it also tries to determine whether the changes in the incremental update don't go beyond the scope of the additional features activated by the usage rights signature.
Otherwise you could simply take a small Reader-enabled PDF and in append mode replace all existing pages by your own content of choice. This of course is not in Adobe's interest...
The filled in PDF is still an AcroForm, otherwise the example below would result in the same PDF twice.
public class Main {
public static final String SRC = "src/main/resources/interactiveform_enabled.pdf";
public static final String DEST = "results/filled_form.pdf";
public static final String DEST2 = "results/filled_form_second_time.pdf";
public static void main(String[] args) throws Exception {
File file = new File(DEST);
file.getParentFile().mkdirs();
Main main = new Main();
Map<String, String> data1 = new HashMap<>();
data1.put("Name_First", "Tristan");
data1.put("BACHELORS DEGREE", "Off");
main.fillPdf(SRC, DEST, data1, false);
Map<String, String> data2 = new HashMap<>();
data2.put("Sex", "FEMALE");
main.fillPdf(DEST, DEST2, data2, false);
}
private void fillPdf(String src, String dest, Map<String, String> data, boolean flatten) {
try {
PdfDocument pdf = new PdfDocument(new PdfReader(src), new PdfWriter(dest));
PdfAcroForm form = PdfAcroForm.getAcroForm(pdf, true);
//Delete print field from acroform because it is defined in the contentstream not in the formfields
form.removeField("Print");
Map<String, PdfFormField> m = form.getFormFields();
for (String d : data.keySet()) {
for (String s : m.keySet()) {
if(s.equals(d)){
m.get(s).setValue(data.get(d));
}
}
}
if(flatten){
form.flattenFields();
}
pdf.close();
System.out.println("Completed");
} catch (IOException e) {
System.out.println("Unable to fill form " + dest + "\n\t" + e);
}
}
}
The issue you are facing has to do with the 'reader enabled forms'.
What it boils down to is that the PDF file that is initially fed to your program is reader enabled. Hence you can open the PDF in Adobe Reader and fill in the form. This allows Acrobat users to extend the behaviour of Adobe Reader.
Once the PDF is filled in and closed using iText it saves the PDF as 'not reader-extended'.
This makes it so that the AcroForm can still be filled using iText but when you open the PDF using Adobe Reader the extended functionality you see in the original PDF is gone. But this does not mean the form is flattened.
iText cannot make a form reader enabled, as a matter of fact, the only way to create a reader enabled form is using Acrobat Professional. This is how Acrobat and Adobe Reader interact and it is not something iText can imitate or solve. You can find some more info and a possible solution on this link.
The IllegalArgumentException you get when you call the form.flattenFields() method is because of the way the PDF document was constructed.
The "Print form" button should have been defined in the AcroForm, yet it is defined in the contentstream of the PDF, meaning the button in the AcroForm has an empty text value, and this is what causes the exception.
You can fix this by removing the print field from the AcroForm before you flatten.
IllegalArgumentException issue has been fixed in iText 7.1.5.
I have two JRXML File with two different data source.
in first jasper report data source is JRXmlDataSource and in second jasper report data source is JRResultSetDataSource
try
{
conn= objConnector.getConnection();
conn.setAutoCommit(false);
PreparedStatement ps = conn.prepareCall("{ call Sp_DEMO(?) }");
ps.setString(1,condition);
ResultSet rs = ps.executeQuery();
JasperReport jreport1 = JasperCompileManager.compileReport("d:\\JRXML\\ECGImage.jrxml");
JasperPrint jprint1 = JasperFillManager.fillReport(jreport1, new HashMap(), new JRResultSetDataSource(rs));
jprintlist.add(jprint1);
JasperReport jasperReport = JasperCompileManager.compileReport("d:\\JRXML\\RadiologyReport.jrxml");
JRXmlDataSource xmlDataSource = new JRXmlDataSource("d:\\abc.xml"+, "/X-RayReport/Type");
JasperPrint jasperPrint = JasperFillManager.fillReport(jasperReport, new HashMap(),xmlDataSource);
jprintlist.add(jasperPrint);
File file = new File("d:\\demo.pdf");
if(file.exists())
{
file.delete();
}
JRExporter exporter = new JRPdfExporter();
exporter.setParameter(JRPdfExporterParameter.JASPER_PRINT_LIST, jprintlist);
OutputStream output = new FileOutputStream(new File("d:\\demo.pdf"));
exporter.setParameter(JRPdfExporterParameter.OUTPUT_STREAM, output);
exporter.exportReport();
}
catch(Exception e)
{
e.printStackTrace();
}
i want to create single pdf file as output from both jrxml file.
You can merge the above two pdf like this
List pages = jasperPrint.getPages();
for (int j = 0; j < pages.size(); j++) {
JRPrintPage object = (JRPrintPage)pages.get(j);
jprint1.addPage(object);
}
And jprint1 will be your single output .
Well, that is what JasperReport Subreports are meant to do. You have to create another .jrxml which would be the master report, and include the other existing two in this one as "Subreports". So you'll have a single output.
To create subreports (if you don't know), please refer to these tutoriale: JasperReports - Create SubReports, SubReports.
From the fact that you have two different data sources, I think that you might as well need to read this too: Pass parameter to subreports.
You might need this to pass the different data sources as parameters to the subreports, thus not in your JasperPrint object instance.
I have no factual defence or argumentation about how I do it normally and all that is just matter of personal preference and the ease that I have during the merging and post-processing stages for adding some general additional stuff to the merged report i.e. page numbers, header, footer, etc.
The correct jasper-report way is to stay away from joining reports in Java and instead, to make a main report and then adding subreports to it. However, if you really want to do that in Java, you can use markers and afterwards post-process everything again, check here.
Still, I just don't do that.
Most of the time I prefer to first generate and export different sections of my final report in pdf separately and afterward merge them all together utilizing PDFBox. By not following the jasper report's way, usually I avoid a hell of subreports hierarchy as normally each section of the final report on their own contain couple of other subreports. So, I just find it better to focus on each section separately and later doing the final merging.
I normally do something like this:
public final class Report
{
List<File> MergingFiles;
ReportData data;
public Report(ReportData data)
{
// I prefer to pack all the data like, jrxml files path or
// the data that fills the report in a separate object -> ReportData
this.data = data;
MergingFiles = new ArrayList<>();
}
private void generateCoverPage() throws JRException
{
/* Setting up the data which needs to be passed to cover page
reading the cover page jrxml file
compiling the report */
// and Exporting - I normally use JRPdfExporter for that
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(data.getCoverPageExportPath()));
exporter.exportReport();
MergingFiles.add(new File(data.getCoverPageExportPath()));
}
private void generateSecondPart() throws JRException
{
/* similar to generateCoverPage() to create another part of the report */
}
public void generateReport()
{
generateCoverPage();
generateSecondPart();
mergePDFFiles(MergingFiles, data.getPrintFileName());
/* Do additional general post process i.e. page numbers, header, footer, etc. here
and then clean up temp files */
}
private void mergePDFFiles(List<File> files, String mergedFileName)
{
// all classes are imported from "org.apache.pdfbox"
try
{
PDFMergerUtility pdfmerger = new PDFMergerUtility();
for (File file : files)
{
PDDocument document;
document = PDDocument.load(file);
pdfmerger.setDestinationFileName(mergedFileName);
pdfmerger.addSource(file);
pdfmerger.mergeDocuments(MemoryUsageSetting.setupTempFileOnly());
document.close();
}
}
catch (IOException e)
{
System.out.println("Error to merge files. Error: " + e.getMessage());
}
}
}
// are just my side notes, however comments surrender with /* */ are the parts which you have to take care of them in case that you wanted to use my approach.