using apache fop with PipedOutputStream - java

I want to build a pdf-file from a jaxb-object using apache fop to generate and itext PdfStamper to modify it. since fop writes to an outputStream and PdfStamper reads from InputStream my idea was to use Piped[I|O]Streams for this. here is what I tried:
public void transform2XSLFO_onthefly(Medium medium, OutputStream out) throws Exception {
PipedInputStream pInputPipe = new PipedInputStream();
PipedOutputStream outputTemp = new PipedOutputStream(pInputPipe);
try {
JAXBSource source = new JAXBSource( JAXBContext.newInstance(medium.getClass()) , medium );
FOUserAgent userAgent = fopFactory.newFOUserAgent();
// settings
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, userAgent ,outputTemp);
InputStream XSLinputStream = xslfoStylesheet.getInputStream();
StreamSource XSLsource = new StreamSource(XSLinputStream);
Result res = new SAXResult(fop.getDefaultHandler());
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer(XSLsource);
// run transformation
t.transform(source, res);
// does not come so far, no use closing the stream
outputTemp.close();
PdfReader reader = new PdfReader(pInputPipe);
pdfStamper = new PdfStamper(reader, out);
//..... postProcess...
pdfStamper.close();
} catch (Exception ex) {
log.error("ERROR", ex);
}
However it hangs in the line "t.transform(source, res);", looks like he is waiting for something in the middle of the fop-transformation. It works using BypeArrayOutputStream and convert it to inputstream and use it for PdfStamper input:
InputStream pdfInput = new ByteArrayInputStream(((ByteArrayOutputStream) outputTemp).toByteArray());
but the files can get quite large (few MB) so i think the piped version would perform better! what do you think?

You should read up on how to use PipeInput/OutputStream. FOP and the PdfStamper will need to run in separate threads. Basically, this has nothing to do with FOP per se. I'm sure you'll find various examples on the net on how this works. If you're not comfortable with multi-threaded programming, I suggest you just buffer FOP's output in a byte[] or a temporary file.

Related

How to Read and write InputStream to a pdf file using iText?

I have an InputStream. I am trying to read the InputStream and write it to a pdf file using iText.
I tried the following :
FileOutputStream fileout = new FileOutputStream(file);
Document document = new Document();
PdfWriter.getInstance(document, fileout);
InputStream is=null;
is = myInfo.getInputStream();
String result = IOUtils.toString(is, "UTF-8");
Paragraph paragraph=new Paragraph();
paragraph.add(result);
document.add(paragraph);
is.close();
document.close();
Here the output file contains so many unwanted characters, some XML tags etc. The output pdf file is dumped with a lot of things which are non-readable.
And I am able to write it to pdf file using the following code:
OutputStream ostream = new FileOutputStream("c:\\test\\newfile1.pdf");
byte[] data = new byte[4096];
int r = 0;
while((r = is.read(data, 0, data.length)) != -1)
{
ostream.write(data, 0, r);
}
ostream.flush();
ostream.close();
The above code helps me to write the inputstream to the pdf file. But I want to do the same thing with itext.
I am using iText for the first time and confused how to use it properly. Could someone please help me with this? Thanks.
Solution :
I changed the inputstream to bytearraay.
PdfReader pdfreader;
pdfreader = new PdfReader(myInfo.getByteArray());
PdfStamper pdfStamper = new PdfStamper(pdfreader, fileout);
pdfStamper.close();
pdfreader.close();
In this way, I am able to read the inputsteam and write to pdf using itext. Thanks everyone.

Batik - Modify and saving of SVG results in not well formed XML

I'm trying to edit an existing SVG and save it afterwards using Batik (I need different formats and DOM-Manipulation).
What I do:
Adobe Illustrator Source File (CS 4.0) saved as SVG 1.0 with all Glyphs (two embedded Fonts)
Loaded with Batik (using SAXSVGDocumentFactory, source below)
DOM-Modifications (even without modifications the problem occurs)
Saving using SVGTranscoder
After transcoding I get a new SVG-File, which is filled with XML, but is not able to render properly in Firefox or Illustrator.
In Firefox I get the message that the XML is not well formed e.g.,
<glyph horiz-adv-x="249" unicode=""/>
My Code:
///////////////
// Load Template File (with embedded Fonts)
///////////////
File file = new File(SVGFilePath);
FileInputStream svgInputStream = new FileInputStream(file);
////////////////////
// Load SVG into DOM-Tree
////////////////////
String parser = XMLResourceDescriptor.getXMLParserClassName();
SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(parser);
Document doc = factory.createDocument(parser, svgInputStream);
//...
///////////////////////
// Generate Output File
///////////////////////
String savepath = "test.svg";
byte[] fileData = transcodeToSVG(doc);
FileOutputStream fileSave = new FileOutputStream(savepath);
fileSave.write(fileData);
fileSave.close();
My Transcoding Code:
public byte[] transcodeToSVG(Document doc) throws TranscoderException {
try {
//Determine output type:
SVGTranscoder t = new SVGTranscoder();
//Set transcoder input/output
TranscoderInput input = new TranscoderInput(doc);
ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
OutputStreamWriter ostream = new OutputStreamWriter(bytestream);
TranscoderOutput output = new TranscoderOutput(ostream);
//Perform transcoding
t.transcode(input, output);
ostream.flush();
ostream.close();
return bytestream.toByteArray();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
David Conrads hint with the Unicode character lead me to the solution:
I was missing the right encoding of the file.
After changing
OutputStreamWriter ostream = new OutputStreamWriter(bytestream);
to
OutputStreamWriter ostream = new OutputStreamWriter(bytestream, "UTF-8");
it works well.

Java Transformer: How do you make its result into an OutputStream?

I am new to javax.xml.transform.Transformer.
I am applying an XSLT on an XML document and It works fine.
What I want to achieve is to be able to write the output of that tranformation to an OutputStream.
This is my code:
OutputStream outputStream = null;
InputStream agent = new FileInputStream("src/res/testxmlfile.xml");
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(new StreamSource("src/res/trans.xslt"));
transformer.transform(new StreamSource(agent), outputStream ????????);
I know it can be used to write a file like this, but I want to write it to a OutputStream Object.
transformer.transform(new StreamSource(agent),
new StreamResult(new FileOutputStream("/result.xml")));
How can I pass an OutputStream to be used here?
This is the error I am getting when I am passing the Outputstream:
Exception in thread "main" javax.xml.transform.TransformerException:
Result object passed to ''{0}'' is invalid.
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl
.getOutputHandler(TransformerImpl.java:468)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl
.transform(TransformerImpl.java:344)
at com.gohealth.TestXmlStream.main(TestXmlStream.java:75)
Use a StreamResult. It provides constructors to write to a File or an OutputStream:
Example using File:
transformer.transform(new StreamSource(agent), new StreamResult(file));
Example using FileOutputStream:
FileOutputStream outputStream = new FileOutputStream(new File("outputfile.xml"));
transformer.transform(new StreamSource(agent), new StreamResult(outputStream));
Example using ByteArrayOutputStream:
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
transformer.transform(new StreamSource(agent), new StreamResult(outputStream));
byte[] bytes = outputStream.toByteArray();`
Use a "StreamResult" constructed with an object that represents where you want the output. See http://docs.oracle.com/javase/7/docs/api/javax/xml/transform/stream/StreamResult.html

iText/flying-saucer PdfStamper using an OutputStream

I'm almost there (I think) on being able to render a PDF with a servlet without saving it first. I've been able to successfully set it up, but I'm stuck at trying to make the PDF open in the client's browser with a Print Dialog initially.
I've been able to send my PDF to the client successfully with the following:
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new StringBufferInputStream(buf.toString()));
OutputStream os = resp.getOutputStream();
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(doc, null);
renderer.layout();
renderer.createPDF(os);
os.close();
But, I'm not sure how to put a print dialog on open of it.
I've used this code for a physical PDF file, but I need to be able to read the contents of the OutputStream in as a byte array for input to the PdfReader (I think):
PdfReader reader = new PdfReader("a_physical_file.pdf");
PdfStamper stamper = new PdfStamper(reader, os);
stamper.setPageAction(PdfWriter.PAGE_OPEN, new PdfAction(PdfAction.PRINTDIALOG), 1);
stamper.close();
Not sure how to do this with an OutputStream rather than an actual file...
I've also created an iText chat room if you would like to post there: https://chat.stackoverflow.com/rooms/8945/itext
Warning : I use an old version of Itext, so my experience may not be applicable.
PdfReader can use a byte array. so you could use a ByteArrayOutputStream as your first output stream, then use it to get the reader, instead of a filename.
Regards
Edit : Regarding your question :
i'm doing it the others way around : i'm working on a ByteArrayOutputStream and then writing it in the response stream :
ByteArrayOutputStream out = new ByteArrayOutputStream();
// creating / modifying the pdf
...
byte[] pdfoutput = out.toByteArray();
res.setContentLength(pdfoutput.length);
res.getOutputStream().write(pdfoutput);
Edit 2 : the final solution (from the chat room)
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(new StringBufferInputStream(buf.toString()));
OutputStream os = new ByteArrayOutputStream();
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(doc, null);
renderer.layout();
renderer.createPDF(os);
os.close();
PdfReader reader = new PdfReader(((ByteArrayOutputStream)os).toByteArray());
OutputStream out = new ByteArrayOutputStream();
PdfStamper stamper = new PdfStamper(reader, out);
stamper.setPageAction(PdfWriter.PAGE_OPEN, new PdfAction(PdfAction.PRINTDIALOG), 1);
stamper.close();
resp.getOutputStream().write(((ByteArrayOutputStream)out).toByteArray());
Instead of using PdfStamper, why don't you implement PDFCreationListener of flyingsacuer to massage any pdf created. You can get the PdfWriter instance and set the print dialogue from within the implementation class.
From the javadoc of PDFCreationListener
PDFCreationListener is the callback listener for PDF creation. To use this, call ITextRenderer.setListener(PDFCreationListener).Note that with a handle on the ITextRenderer instance (provided in the callback arguments) you can access the com.lowagie.text.pdf.PdfWriter instance being used to create the document, using
ITextRenderer.getOutputDevice(), then calling ITextOutputDevice.getWriter().
The related thread is here.

how to create an InputStream from a Document or Node

How can I create an InputStream object from a XML Document or Node object to be used in xstream? I need to replace the ??? with some meaningful code. Thanks.
Document doc = getDocument();
InputStream is = ???;
MyObject obj = (MyObject) xstream.fromXML(is);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Source xmlSource = new DOMSource(doc);
Result outputTarget = new StreamResult(outputStream);
TransformerFactory.newInstance().newTransformer().transform(xmlSource, outputTarget);
InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
If you are using Java without any Third Party Libraries, you can create InputStream using below code:
/*
* Convert a w3c dom node to a InputStream
*/
private InputStream nodeToInputStream(Node node) throws TransformerException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
Result outputTarget = new StreamResult(outputStream);
Transformer t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.transform(new DOMSource(node), outputTarget);
return new ByteArrayInputStream(outputStream.toByteArray());
}
One way to do it: Adapt the Document to a Source with DOMSource. Create a StreamResult to adapt a ByteArrayOutputStream. Use a Transformer from TransformerFactory.newTransformer to copy across the data. Retrieve your byte[] and stream with ByteArrayInputStream.
Putting the code together is left as an exercise.
public static InputStream document2InputStream(Document document) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
OutputFormat outputFormat = new OutputFormat(document);
XMLSerializer serializer = new XMLSerializer(outputStream, outputFormat);
serializer.serialize(document);
return new ByteArrayInputStream(outputStream.toByteArray());
}
This works if you are using apache Xerces implementation, you can also set format parameter with the output format.
public static InputStream documentToPrettyInputStream(Document doc) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
XMLWriter xmlWriter = new XMLWriter(outputStream, OutputFormat.createPrettyPrint());
xmlWriter.write(doc);
xmlWriter.close();
InputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
return inputStream;
}
If you happen to use DOM4j and you need to print it pretty!

Categories

Resources