In the Java code below, I am creating a *.html report by transforming generated xml data that is stored in a string,
combinedDDIString
... against an XSLT file,
reportXSLT
... and writing the result to a physical file,
tmpReportHTML.
The code then reads the file back into a string to use in other methods. I would like to avoid having to write the results to a file, just transform the results directly into a string.
Is there any way I can write the transform results to a string directly, and avoid writing the results out to a physical file?
String reportString = null;
FileInputStream stream = null;
ByteArrayOutputStream reportBAOS = new ByteArrayOutputStream();
try {
System.setProperty("javax.xml.transform.TransformerFactory", "net.sf.saxon.TransformerFactoryImpl");
transformerFactory = TransformerFactory.newInstance();
transformer = transformerFactory.newTransformer(new StreamSource(reportXSLT));
transformer.setOutputProperty(OutputKeys.ENCODING, "US-ASCII");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
/*
* Create a new report file time-stamped for uniqueness, to avoid concurrency issues
*/
DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
Date date = new Date();
File tmpReportHTML = new File(reportHTML + dateFormat.format(date) + ".html");
/*
* Perform the transform to get the report
*/
FileOutputStream reportFOS = new FileOutputStream(tmpReportHTML);
OutputStreamWriter osw = new OutputStreamWriter(reportFOS, "US-ASCII");//(reportBAOS), "US-ASCII");
transformer.transform(new StreamSource(new StringReader(combinedDDIString)), new StreamResult(osw));
osw.close();
/*
* Get the report as a string from the temp report file
*/
//FileInputStream stream = new FileInputStream(new File(REPORT_OUTPUT));
stream = new FileInputStream(tmpReportHTML); //(new File(reportXML));
FileChannel fc = stream.getChannel();
MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
reportString = Charset.defaultCharset().decode(bb).toString();
/*
* Delete the temp report file
*/
tmpReportHTML.delete();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (Exception ex) {
ex.printStackTrace();
}
finally {
stream.close();
Thanks in advance for the help.
Try using a StringWriter to create the StreamResult. (You should be able to obtain a String from the StringWriter simply using the toString method.)
Related
I was trying to generate 2 PDF data and put it inside a ZIP file for download (through response.getOutputStream) but I don't have idea how to do it properly:
public void export() {
String fileName = "B2B_Price_List.zip";
String fileNameUSD = "B2B_Price_List_USD.pdf";
String fileNameEU = "B2B_Price_List_EU.pdf";
String contentTypePDF = "application/pdf";
String[] headerPDF = new String[2];
headerPDF[0] = "Content-disposition";
headerPDF[1] = "attachment; filename=\"" + fileNameUSD + "\"";
headerPDF[2] = "attachment; filename=\"" + fileNameEU + "\"";
String contentTypeZIP = "application/zip";
String[] headerZIP = new String[1];
headerZIP[0] = "Content-disposition";
headerZIP[1] = "attachment; filename=\"" + fileName + "\"";
ByteArrayOutputStream outUSD = new ByteArrayOutputStream();
outUSD = CSVHandler.downloadPriceListPDF(outUSD, fileNameUSD, ListToPDFMap(productsUSD), true);
ByteArrayOutputStream outEU = new ByteArrayOutputStream();
outEU = CSVHandler.downloadPriceListPDF(outEU, fileNameEU, ListToPDFMap(productsEU), false);
// ZIP CODING GOES HERE
}
This function returns ByteArrayOutputStream to be used later:
public static ByteArrayOutputStream downloadPriceListPDF
(ByteArrayOutputStream output, final String filename,
Map<String, Map<String, List<B2BProductData>>> datas,
boolean amerCustomer) {
try {
PdfDocument pdfDoc = null;
try {
pdfDoc = new PdfDocument(new PdfWriter(output));
PageSize pageSize = new PageSize(PageSize.A4);
Document doc = new Document(pdfDoc, pageSize, false);
PdfCanvas canvas = new PdfCanvas(pdfDoc.addNewPage());
String coverImage = COVER_IMAGE;
if(!amerCustomer) {
coverImage = COVER_IMAGE_1;
}
canvas.addImage(ImageDataFactory.create(CSVHandler.class.getClassLoader().getResource(coverImage).getPath()), pageSize, false);
// loop thru category
int pageNo = 2;
Map<String, List<B2BProductData>> inputDatas = new LinkedHashMap<>();
for(String category : datas.keySet()) {
Map<String, List<B2BProductData>> prods = datas.get(category);
while(true) {
inputDatas = new LinkedHashMap<>();
Map<String, List<B2BProductData>> remaindatas = filterDatas(inputDatas, prods);
if(inputDatas.size() > 0) {
createPDFPage(pdfDoc, doc, category, inputDatas, pageNo ++, amerCustomer);
}
if(remaindatas.size() > 0) {
prods = remaindatas;
} else {
break;
}
}
}
doc.close();
return output;
} catch (IOException e) {
LOG.error(e.getMessage());
return output;
}
}
catch (final Exception ex) {
LOG.error("Export Products got error: " + ex.getMessage());
return output;
}
}
I made it like this:
Declared the Filenames to be used later.
String fileName = "B2B_Price_List.zip";
String fileNameUSD = "B2B_Price_List_USD.pdf";
String fileNameEU = "B2B_Price_List_EU.pdf";
Declare a new ByteArrawOutputStream class and initialize with "new".
ByteArrayOutputStream outUSD = new ByteArrayOutputStream();
ByteArrayOutputStream outEU = new ByteArrayOutputStream();
After generating a PDF file, return the value ByteArrayOutputStream and assign to the ByteArrayStream declared earlier.
if (hasUSD) outUSD = CSVHandler.generatePriceListPDF(outUSD, ListToPDFMap(productsUSD), true, true);
if (hasEU) outEU = CSVHandler.generatePriceListPDF(outEU, ListToPDFMap(productsEU), false, true);
Declare an outputstream to be used to hold response object's OutputStream.
OutputStream responseOutputStream;
Declare the header strings to be assigned to the response object's header data. In this case the MIME type would be application/zip for zip file. The fileName (B2B_Price_List.zip) is also used to define the download's filename.
String contentTypeZIP = "application/zip";
String[] headerZIP = new String[1];
headerZIP[0] = "Content-disposition";
headerZIP[1] = "attachment; filename=\"" + fileName + "\"";
Set the response object's headers.
response.setContentType(contentTypeZIP);
response.setHeader(headerZIP[0], headerZIP[1]);
Set the responseOutputStream to hold the response object's outputstream.
responseOutputStream = response.getOutputStream();
Declare a ZipOutputStream and initialize new with the response's outputstream as a parameter. The parameter will be used to write to write here the file to be downloaded later, in this case, the ZIP file.
ZipOutputStream zos = new ZipOutputStream(responseOutputStream);
Declare the ZipEntry objects to be put inside the ZIP file. Initialize new with the filename string as a parameter. In this case, we'll put 2 files inside the ZIP file for example.
ZipEntry zipEntryUSD = new ZipEntry(fileNameUSD);
ZipEntry zipEntryEU = new ZipEntry(fileNameEU);
Put each entries (or files) one at a time, after the putNextEntry is called for an entry, it is then assumed that the next .write called will be written to the previously put entry.
In this case we called the .write with the ByteArrayOutputStream.toByteArray() to convert to ByteArray as a parameter. Don't forget to close the entry by calling .closeEntry(), then proceed to the next file(s) with the same procedure earlier.
zos.putNextEntry(zipEntryUSD);
zos.write(outUSD.toByteArray());
zos.closeEntry();
zos.putNextEntry(zipEntryEU);
zos.write(outEU.toByteArray());
zos.closeEntry();
After writing the entries (files) you needed inside the ZIP, don't forget to close the ZipOutputStream (zos in this case).
zos.close();
The file will then proceed for download after you flush / close the response's output stream. You may ignore flush but to be sure, I included it anyway.
responseOutputStream.flush();
responseOutputStream.close();
END OF CODE BLOCK
CSVHandler.generatePriceListPDF
Now this is the function used to generate PDF > to > ByteArrayOutputStream. We passed the output object ByteArrayOutputStream from earlier to be re-assigned to the passed ByteArrayOutputStream object outside this function.
example:
outUSD = CSVHandler.generatePriceListPDF(outUSD, ListToPDFMap(productsUSD), true, true);
FUNCTION BLOCK START
public static ByteArrayOutputStream downloadPriceListPDF
(ByteArrayOutputStream output, final String filename,
Map<String, Map<String, List<B2BProductData>>> datas,
boolean amerCustomer, boolean autoCloseByteArrayOutputStream) {
try {
PdfDocument pdfDoc = null;
try {
Initialize writer as new PdfWriter with the ByteArrayOutputStream as a parameter, in this case the output object from the function parameter.
PdfWriter writer = new PdfWriter(output);
Initialize pdfDoc as new PdfDocument with the PdfWriter object writer in this case as parameter. This instructs the pdfDoc to write directly to the ByteArrayOutputStream (output) object
pdfDoc = new PdfDocument(writer);
Initialize PDF document parameters such as sizes and such.
PageSize pageSize = new PageSize(PageSize.A4);
Document doc = new Document(pdfDoc, pageSize, false);
PdfCanvas canvas = new PdfCanvas(pdfDoc.addNewPage());
This is the part where you write your PDF, with data, images or anything up to you.
// YOUR OWN PDF WRITE OPERATION HERE
Don't forget to close your PDF Document after you finish writing stuff.
doc.close();
The function parameter autoCloseByteArrayOutputStream boolean I added determines if you want to close the ByteArrayOutputStream inside this function, or close it outside if you want to supplement the content outside. Your choice, but don't forget to close ByteArrayOutputStream all the time anyway.
If (autoCloseByteArrayOutputStream) {
output.flush();
output.close();
}
Return the output ByteArrayOutputStream.
return output;
} catch (IOException e) {
If an exception occur, it's important to return an object on all code paths. In this case we return a null value ByteArrayOutputStream in an event of error.
LOG.error(e.getMessage());
return output;
}
}
catch (final Exception ex) {
Same here, errors return null ByteArrayOutputStream in case of error.
LOG.error("Export Products got error: " + ex.getMessage());
return output;
}
}
END OF FUNCTION BLOCK
I found here how I can override the print-out of the XML document to my Eclipse console so that it includes standalone = "no", but how do I write standalone = "no" to a file? I have tried writing the same document to a file, and it still will not print standalone = "no". In other words, when I try writing to a file, the overridden method does not work.
Is there some other method that I should override when writing to a file? What is the issue here?
private static void writeXML() {
try {
Document doc = new Document();
Element theRoot = new Element("tvshows");
doc.setRootElement(theRoot);
Element show = new Element("show");
Element name = new Element("name");
name.setAttribute("show_id", "show_001");
name.addContent(new Text("Life on Mars"));
Element network = new Element("network");
network.setAttribute("country", "US");
network.addContent(new Text("ABC"));
show.addContent(name);
show.addContent(network);
theRoot.addContent(show);
//-----------------------------
Element show2 = new Element("show");
Element name2 = new Element("name");
name2.setAttribute("show_id", "show_002");
name2.addContent(new Text("Life on Mars"));
Element network2 = new Element("network");
network2.setAttribute("country", "UK");
network2.addContent(new Text("BBC"));
show2.addContent(name2);
show2.addContent(network2);
theRoot.addContent(show2);
XMLOutputter xmlOutput = new XMLOutputter(Format.getPrettyFormat(), XMLOUTPUT);
//xmlOutput.output(doc, System.out);
xmlOutput.output(doc, new FileOutputStream(new File("./src/jdomMade.xml")));
System.out.println("The file has been written");
}
catch (Exception ex){
ex.printStackTrace();
}
}
public static final XMLOutputProcessor XMLOUTPUT = new AbstractXMLOutputProcessor() {
#Override
protected void printDeclaration(final Writer out, final FormatStack fstack) throws IOException {
write(out, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?> ");
write(out, fstack.getLineSeparator());
}
};
Your code lies ;-)
xmlOutput.output(doc, new FileOutputStream(new File("./src/jdomMade.xml")));
System.out.println("The file has been written");
The println says the file has been written, but it has not.
The File is only written when the file is flushed, and closed. You do not do that.
You should add a try-with-resources to your code:
try (FileOutputStream fos = new FileOutputStream(new File("./src/jdomMade.xml"))) {
xmlOutput.output(doc, fos);
}
System.out.println("The file has been written");
The link says that it is not "easily" possible to configure fonts from java code. How do I achieve this?
I'm having problems rendering certain HTMLs from international languages like French and Japanese.
WARNING: Font "Symbol,normal,700" not found. Substituting with "Symbol,normal,400".
May 08, 2015 4:45:39 PM org.apache.fop.events.LoggingEventListener processEvent
WARNING: Font "ZapfDingbats,normal,700" not found. Substituting with "ZapfDingbats,normal,400".
The PDF generated is damaged as a result.
update:
My Html contains French words like "Modifié Créée le Propriétaire"
File file = new File("C:\\Users\\me\\Desktop\\Test.html");
fopFactory = FopFactory.newInstance();
foUserAgent = fopFactory.newFOUserAgent();
String fileName = file.getAbsolutePath().substring(file.getAbsolutePath().lastIndexOf("\\")+1,file.getAbsolutePath().lastIndexOf("."));
String workspacePath = file.getAbsolutePath().substring(0,file.getAbsolutePath().lastIndexOf("\\"));
File xsltfile = new File("xhtml2fo.xsl");
StreamSource source = null;
source = new StreamSource(file);
StreamSource transformSource = new StreamSource(xsltfile);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Transformer xslfoTransformer = null;
TransformerFactory fac = TransformerFactory.newInstance();
xslfoTransformer = fac.newTransformer(transformSource);
xslfoTransformer.setErrorListener(this);
Fop fop;
fop = fopFactory.newFop(MimeConstants.MIME_PDF, foUserAgent, outStream);
// Resulting SAX events (the generated FO)
Result res = new SAXResult(fop.getDefaultHandler());
xslfoTransformer.transform(source, res);
output = new File(workspacePath + File.separator + fileName + ".pdf");
OutputStream out = new java.io.FileOutputStream(output);
out = new java.io.BufferedOutputStream(out);
FileOutputStream str = new FileOutputStream(output);
str.write(outStream.toByteArray());
str.close();
I'm using an XSLT provided by Antennahouse to convert HTML tags to FO tags.
sample code.
/** The Constant FOP_CONFIG. */
private static final String FOP_CONFIG = "pdf.fop.cfg.xml";
fopFactory = FopFactory.newInstance();
// for image base URL
String serverPath = request.getSession().getServletContext().getRealPath("/");
//disable strict validatetion
fopFactory.setStrictValidation(false);
fopFactory.setBaseURL(serverPath);
// for fonts base URL
fopFactory.getFontManager().setFontBaseURL(serverPath);
// read custom font setting
StreamSource configSrc = new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(PDFComponentFactory.FOP_CONFIG));
DefaultConfigurationBuilder cfgBuilder = new DefaultConfigurationBuilder();
Configuration cfg = cfgBuilder.build(configSrc.getInputStream());
fopFactory.setUserConfig(cfg);
Hope it may help you.
I have an object which holds data of a person. When a user clicks on the download button the xml needs to be created by (person.dataitems) and after that user should have an option to download the file (like save file or open file)
I have written the code below which creates a xml file when the button is clicked however the file remains empty. I would like to know how I can write data to this file and then download.
response.setHeader( "Content-Disposition", "attachment;filename="+patient.getGivenName()+".xml");
try {
StringWriter r = new StringWriter();
String ccdDoc = r.toString();
ccdDoc = ccdDoc.replaceAll("<", "<");
ccdDoc = ccdDoc.replaceAll(""", "\"");
byte[] res = ccdDoc.getBytes(Charset.forName("UTF-8"));
response.setCharacterEncoding("UTF-8");
response.getOutputStream().write(res);
response.flushBuffer();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Thanks.
You have to write into your StringWriter:
import java.io.*;
public class StringWriterDemo {
public static void main(String[] args) {
String s = "Hello World";
// create a new writer
StringWriter sw = new StringWriter();
// write portions of strings
sw.write(s, 0, 4);
sw.write(s, 5, 6);
// write full string
sw.write(s);
// print result by converting to string
System.out.println("" + sw.toString());
}
}
Do not do:
String ccdDoc = r.toString();
It only creates a copy of the r string. Then you are modifying the copy, but not at all the content of the StringWriter.
Do:
r.write("some content");
and to access the string contained by the writer, do:
String a_string = r.toString();
response.getOutputStream().write(a_string);
EDIT :
OK, so what you are asking is not so far from what you have in the link you provided, excepted that you have to write into a StringWriter instead of into a File.
This can be achieved this way:
1) Do build an xml document:
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
// root elements
Document doc = docBuilder.newDocument();
Element rootElement = doc.createElement("company");
doc.appendChild(rootElement);
// staff elements
Element staff = doc.createElement("Staff");
rootElement.appendChild(staff);
// set attribute to staff element
staff.setAttribute("id", "1");
// firstname elements
Element firstname = doc.createElement("firstname");
firstname.appendChild(doc.createTextNode("yong"));
staff.appendChild(firstname);
:
:
// Then write the doc into a StringWriter
Transformer transformer = TransformerFactory.newInstance().newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
//initialize StreamResult with StringWriter object to save to string
StreamResult result = new StreamResult(new StringWriter());
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
String xmlString = result.getWriter().toString();
System.out.println(xmlString);
// Finally, send the response
byte[] res = xmlString.getBytes(Charset.forName("UTF-8"));
response.setCharacterEncoding("UTF-8");
response.getOutputStream().write(res);
response.flushBuffer();
The point here is to do:
StreamResult result = new StreamResult(new StringWriter());
instead of:
StreamResult result = new StreamResult(new File("C:\\file.xml"));
You tell me if there is still something unclear in this.
it's worked
byte[] res = xmlString.getBytes(Charset.forName("UTF-8"));
response.setCharacterEncoding("UTF-8");
response.setHeader( "Content-Disposition", "attachment;filename=archivo.xml");
response.getOutputStream().write(res);
response.flushBuffer();
Is it possible to render pdf document with play framework 2?
(There is a module that can render pdf for play 1.x. is there a way to render in play 2?)
If you're looking to render view templates as PDF documents, check out this module.
There is the apache fop plugin which create pdf out of fop files.
The fop files are not the most intuitive files, but in the end I always found a way to format complex pdf the way I wanted it.
To add the plug in to your play application add this to build.sbt :
"org.apache.avalon.framework" % "avalon-framework-api" % "4.2.0" from "http://repo1.maven.org/maven2/avalon-framework/avalon-framework-api/4.2.0/avalon-framework-api-4.2.0.jar",
"org.apache.avalon.framework" % "avalon-framework-impl" % "4.2.0" from "http://repo1.maven.org/maven2/avalon-framework/avalon-framework-impl/4.2.0/avalon-framework-impl-4.2.0.jar",
"org.apache.xmlgraphics" % "fop" % "1.1"
This is my function to create a pdf file from a fop string :
private static FopFactory fopFactory = FopFactory.newInstance();
/**
* Wrote according to this example :
* http://xmlgraphics.apache.org/fop/1.1/embedding.html#examples
* #param outputPath Path to the file to create (must end by .pdf).
* #param foString Description of the pdf document to render.
* http://www.w3schools.com/xslfo/default.asp
* #return the output path.
*/
public static String toPdf(String outputPath, String foString)
{
OutputStream out;
try {
File fileOutput = new File(outputPath);
out = new BufferedOutputStream(new FileOutputStream(fileOutput));
} catch (FileNotFoundException e) {
Logger.error("InvoicePdf.invoiceToPdf: " + e.getMessage());
return null;
}
try {
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
Source src = new StreamSource(new StringReader(foString));
Result res = new SAXResult(fop.getDefaultHandler());
transformer.transform(src, res);
}catch (Throwable e){
Logger.error("InvoicePdf.invoiceToPdf: " + e.getMessage());
e.printStackTrace();
return null;
} finally {
try {
out.close();
} catch (Throwable e) {
Logger.error("InvoicePdf.invoiceToPdf: " + e.getMessage());
}
}
return outputPath;
}
When using play with scala you can use the scala library https://github.com/cloudify/sPDF.
Then in your Play 2.x controller you can render pdf with the following code:
import io.github.cloudify.scala.spdf.{Pdf, PdfConfig, Portrait}
def yourAction = Action { implicit request =>
val pdf = Pdf(
executablePath = "/usr/bin/wkhtmltopdfPath",
config = new PdfConfig {
orientation := Portrait
pageSize := "A4"
marginTop := "0.5in"
marginBottom := "0.5in"
marginLeft := "0.5in"
marginRight := "0.5in"
printMediaType := Some(true)
}
)
val outputStream = new ByteArrayOutputStream
pdf.run(
sourceDocument = views.html.yourTemplate().toString(),
destinationDocument = outputStream
)
Ok(outputStream.toByteArray).as("application/pdf")
}