Render image from servlet in flyingsaucer generated pdf - java

I'm using flyingsaucer to render an xhtml document to pdf through a servlet which returns the generated pdf document. The xhtml document features an image which is requested from another servlet. The image servlet checks who is logged in before returning the appropriate image. The code below shows how the image is requested:
<img height="140" width="140" src="http://localhost:8080/myapp/servlet/DisplayPic" />
My problem is that the http request for the image is from the pdf renderer and not the logged in user so the image servlet doesn't know who's logged in and therefore the desired image is not returned.
I'm currently using the code below to render the xhtml document:
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(xhtmlDocumentAsString);
renderer.layout();
os = response.getOutputStream();
renderer.createPDF(os);
I need to either maintain the user's session when the image servlet is requested or provide the renderer with the image to use for that specific xhtml element. I think the latter can be done using a ReplacedElementFactory but I haven't been able to dig out any example code that can help me.

I've got this working very nicely now. Here's the code.
In my xhtml document i have:
<div class="profile_picture" style="display:block;width:140px;height:140px;" />
(I'm using a div element instead of img as the factory is only used for block level elements)
I render my document using:
ITextRenderer renderer = new ITextRenderer();
renderer.getSharedContext().setReplacedElementFactory(new ProfileImageReplacedElementFactory(renderer.getSharedContext().getReplacedElementFactory()));
renderer.setDocumentFromString(xhtmlDocumentAsString);
renderer.layout();
os = response.getOutputStream();
renderer.createPDF(os);
And i have my own ReplacedElementFactory as below:
public class ProfileImageReplacedElementFactory implements ReplacedElementFactory {
private final ReplacedElementFactory superFactory;
public ProfileImageReplacedElementFactory(ReplacedElementFactory superFactory) {
this.superFactory = superFactory;
}
#Override
public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockBox blockBox,
UserAgentCallback userAgentCallback, int cssWidth, int cssHeight) {
Element element = blockBox.getElement();
if (element == null) {
return null;
}
String nodeName = element.getNodeName();
String className = element.getAttribute("class");
if ("div".equals(nodeName) && className.contains("profile_picture")) {
InputStream input = null;
try {
input = ...;
byte[] bytes = IOUtils.toByteArray(input);
Image image = Image.getInstance(bytes);
FSImage fsImage = new ITextFSImage(image);
if (fsImage != null) {
if ((cssWidth != -1) || (cssHeight != -1)) {
fsImage.scale(cssWidth, cssHeight);
}
return new ITextImageElement(fsImage);
}
} catch (IOException e) {
getLogger().error(ExceptionUtils.getStackTrace(e));
} catch (BadElementException e) {
getLogger().error(ExceptionUtils.getStackTrace(e));
} finally {
IOUtils.closeQuietly(input);
}
}
return superFactory.createReplacedElement(layoutContext, blockBox, userAgentCallback, cssWidth, cssHeight);
}
#Override
public void reset() {
superFactory.reset();
}
#Override
public void remove(Element e) {
superFactory.remove(e);
}
#Override
public void setFormSubmissionListener(FormSubmissionListener listener) {
superFactory.setFormSubmissionListener(listener);
}
}

Related

Convert HTML to PDF with Header and Footer

We have header and footer String as HTML content type but how to append both on every page.
In below java method we are passing three parameter htmlcontent, HeaderContent, FooterContent and return number of pages created in html, But where do we have to attach the header and footer content?
public static int generatePDF(String strFileName, String htmlContent,String headerHtml,String footerHtml) throws PDFNetException {
PDFDoc doc = new PDFDoc();
HTML2PDF converter = new HTML2PDF();
int nPages = 0;
try {
converter = new HTML2PDF();
doc = new PDFDoc();
converter.insertFromHtmlString(htmlContent);
try {
if (converter.convert(doc)) {
doc.save(strFileName, SDFDoc.e_linearized, null);
nPages = doc.getPageCount();
}
} catch (Exception ex) {
ex.printStackTrace();
}
} catch (Exception e) {
ex.printStackTrace();
} finally {
converter.destroy();
doc.close();
}
return nPages;
}
One option is to post-process the PDF, by using the Stamper class, to add headers/footers.
See the following sample code on how to use Stamper call
https://www.pdftron.com/documentation/samples/#stamper
The HTML2PDF converter appends pages to the PDFDoc object passed in, so you can do the following.
call HTML2PDF.InsertFromURL(url)
call HTML2PDF.Convert(pdfdoc)
run Stamper on pages x-y stamp
and repeat to keep appending pages to pdfdoc.

Itext7 generate pdf with Exception "Pdf indirect object belongs to other PDF document. Copy object to current pdf document."

i want to generate a pdf with itext 7,but some wrong happens to us:
com.itextpdf.kernel.PdfException: Pdf indirect object belongs to other PDF document. Copy object to current pdf document.
at com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:195) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:185) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:115) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:187) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:115) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:187) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfOutputStream.write(PdfOutputStream.java:115) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfWriter.writeToBody(PdfWriter.java:383) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfWriter.flushObject(PdfWriter.java:289) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfDocument.flushObject(PdfDocument.java:1572) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfObject.flush(PdfObject.java:159) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfObject.flush(PdfObject.java:127) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfObjectWrapper.flush(PdfObjectWrapper.java:94) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfPage.flush(PdfPage.java:495) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfPage.flush(PdfPage.java:454) ~[kernel-7.0.2.jar:na]
at com.itextpdf.kernel.pdf.PdfDocument.close(PdfDocument.java:785) ~[kernel-7.0.2.jar:na]
at com.itextpdf.layout.Document.close(Document.java:120) ~[layout-7.0.2.jar:na]
at com.xcz.afbp.thirdparty.service.impl.GeneratePDFService.generatePDF(GeneratePDFService.java:160) ~[classes/:na]
my generate code :
public void generatePDF(CreditQueryData creditQueryData, Map<String, UserCreditContentView> contentViewMap, List<PackageCreditContentView> needRetrievedCreditContentList, File pdfFile, BigDecimal score) throws Exception {
if (!pdfFile.exists()) {
boolean x = pdfFile.createNewFile();
if (!x) {
LOG.error("生成文件出错" + pdfFile.getPath());
return;
}
}
PdfDocument pdf = new PdfDocument(new PdfWriter(new FileOutputStream(pdfFile)));
Document document = new Document(pdf, PageSize.A4);
document.setRenderer(new DocumentRenderer(document));
pdf.addEventHandler(PdfDocumentEvent.END_PAGE, new WatermarkingEventHandler());
try {
//operate code just add tableA tableB tableC...
} catch (Exception e) {
LOG.info();
} finally {
document.close(); //exception throws here
}
}
my only style code in itext7:
private PdfFont bfChinese = null;
will be init in service constructor invoked:
public GeneratePDFService() {
String PdfFontPath = EnvironmentUtils.getClasspathFilePath("font/MSYH.TTF");
try {
bfChinese = PdfFontFactory.createFont(PdfFontPath, "Identity-H", true);
} catch (Exception e) {
e.printStackTrace();
}
}
i have tried set my font to static,but not works.
this is the place throw exception:
private void write(PdfIndirectReference indirectReference) {
if (document != null && !indirectReference.getDocument().equals(document)) {
throw new PdfException(PdfException.PdfIndirectObjectBelongsToOtherPdfDocument);
}
if (indirectReference.getRefersTo() == null) {
write(PdfNull.PDF_NULL);
} else if (indirectReference.getGenNumber() == 0) {
writeInteger(indirectReference.getObjNumber()).
writeBytes(endIndirectWithZeroGenNr);
} else {
writeInteger(indirectReference.getObjNumber()).
writeSpace().
writeInteger(indirectReference.getGenNumber()).
writeBytes(endIndirect);
}
}
it's means i have two different document ,but i do not know when i have create another document.
Thanks in advance for suggestions.
I have experienced the same problem myself (and it took me hours to discover what I was doing wrong). As it turns out, you can use a specific PdfFont instance for only one document. As soon as you use a PdfFont instance it is linked to that document, and you can no longer use it in another document.
For instance:
class ThisGoesWrong {
protected PdfFont font;
public ThisGoesWrong() {
font = PdfFontFactory.createFont(...);
}
public void createPdf() {
...
Paragraph p = new Paragraph("test").setFont(font);
document.add(p);
...
}
}
The class ThisGoesWrong creates a correct PDF the first time you call createPdf(), but it shows the exception you have when you call it a second time.
I discovered that this solves the problem:
class ThisWorksOK {
public ThisWorksOK() {
}
public void createPdf() {
...
PdfFont font = PdfFontFactory.createFont(...);
Paragraph p = new Paragraph("test").setFont(font);
document.add(p);
...
}
}
I don't know if this is a bug (it certainly feels like a bug), so I will create an internal ticket at iText Group.
To improve preformance you should reuse FontProgram:
private FontProgram bfChinese = null;
public GeneratePDFService() {
String PdfFontPath = EnvironmentUtils.getClasspathFilePath("font/MSYH.TTF");
try {
bfChinese = FontProgramFactory.createFont(PdfFontPath);
} catch (Exception e) {
e.printStackTrace();
}
}
And then:
public void createPdf() {
...
PdfFont font = PdfFontFactory.createFont(bfChinese, "Identity-H", true);
Paragraph p = new Paragraph("test").setFont(font);
document.add(p);
...
}
This is how i solved the issue.
Created an DocumentUtil class
Add a static method
public class DocumentUtils {
public static PdfFont setFont() throws Exception {
return PdfFontFactory.createFont(StandardFonts.TIMES_ROMAN);
}
}
Use the font like:
PDfFont font = DocumentUtil.setFont();

Stop p:commandLink from scrolling to top of page when clicked

I have a JSF2 commandlink with an image. When the image is clicked, the server will download a PDF file. While the file is downloaded after the image link is clicked, it also causes the entire page to scroll to the top of the page. the code snippet for the link is as follows:
<p:commandLink ajax="false"
action="#{refereeAssessmentSummaryBean.stateLatestFormInPAVer(project.appId)}">
<p:graphicImage name="images/pdf.png"
title="#{msg['label.downloadpdf']}" />
</p:commandLink>
How can I use the commandlink to download the PDF file, without the webpage scrolling to the top of the page every time I click on it?
Edit: FWIW, added PDF download code. This code is called as a shared method from the backing bean. As you can see, the code will set the content type before streaming the PDF data to the client.
public void downloadEformPdf(Integer appId, Integer revNo, Integer meetingId,
String password, boolean showSaveDialog, boolean getEditedIfAvailable, boolean showVersionInfo) {
User user = WebUtils.getCurrentUser();
PermissionResult permissionResult = ServiceProxy.getPermissionService().checkViewOnlineProposalPermission(user, appId, meetingId);
if (permissionResult != PermissionResult.GRANTED) {
if (!(permissionResult == PermissionResult.REJECTED_GRBE_COI_NOT_APPROVED
|| permissionResult == PermissionResult.REJECTED_GRBE_COI_NOT_DECLARED)) {
throw new PermissionDeniedException("Permission Denied");
}
}
Application appl = ServiceProxy.getAppService().getApplication(appId);
String scheme = appl.getScheme();
boolean withNomination = false;
boolean isEditedVersion = false;
byte[] pdfData;
if (getEditedIfAvailable) {
if (revNo == null) {
Appmatching appMatching = ServiceProxy.getAppFormService().getLatestAppMatching(appId,false);
revNo = appMatching.getMainRevno();
}
Appattacheditedeform editedEntry = ServiceProxy.getAppService().getEditedProposalForApplication(appId, revNo, true);
// give GRB, ER the edited version if it exists
if (editedEntry != null) {
Filestorage storage = editedEntry.getFilestorage();
pdfData = storage.getContent();
isEditedVersion = true;
} else {
pdfData = ServiceProxy.getReportService().getHMRFReportContentByRevNo(
appId.intValue(), revNo, withNomination);
}
} else { //Get the unedited version
//Get latest rev no.
if (revNo == null) {
Appmatching appMatching = ServiceProxy.getAppFormService().getLatestAppMatching(appId,false);
revNo = appMatching.getMainRevno();
}
pdfData = ServiceProxy.getReportService().getHMRFReportContentByRevNo(
appId.intValue(), revNo, withNomination);
}
FacesContext context = FacesContext.getCurrentInstance();
ExternalContext extContext = context.getExternalContext();
extContext.responseReset();
PDDocument doc = null;
try {
if (pdfData != null) {
PDFParser parser = new PDFParser(new ByteArrayInputStream(pdfData));
parser.parse();
doc = parser.getPDDocument();
AccessPermission ap = new AccessPermission();
ap.setReadOnly();
if (password != null) {
StandardProtectionPolicy spp = new StandardProtectionPolicy(password, password, ap);
spp.setEncryptionKeyLength(128);
doc.protect(spp);
}
ByteArrayOutputStream bos = new ByteArrayOutputStream();
doc.save(bos);
doc.close();
byte[] docbuff = bos.toByteArray();
String refNo = appl.getRefNo();
String filename = null;
if (showVersionInfo) {
if (isEditedVersion) {
filename = scheme.toLowerCase() + "_eform_" + refNo + "_(v" + revNo + ")_(Edited).pdf";
} else {
filename = scheme.toLowerCase() + "_eform_" + refNo + "_(v" + revNo + ")_(PA).pdf";
}
} else {
filename = scheme.toLowerCase() + "_eform_" + refNo + ".pdf";
}
extContext.setResponseContentType("application/pdf");
extContext.setResponseContentLength(docbuff.length);
extContext.setResponseHeader("Content-Disposition", (!showSaveDialog) ? "inline"
: "attachment" + "; filename=\"" + filename + "\"");
OutputStream os = extContext.getResponseOutputStream();
os.write(docbuff);
os.close();
context.responseComplete();
} else {
extContext.setResponseContentType("text/html");
Writer writer = extContext.getResponseOutputWriter();
writer.write("Cannot retrieve PDF form for this proposal.");
writer.close();
context.responseComplete();
}
} catch (IOException e) {
logger.log(Level.ERROR, e.getMessage(), e);
} catch (COSVisitorException e) {
logger.log(Level.ERROR, e.getMessage(), e);
} catch (BadSecurityHandlerException e) {
logger.log(Level.ERROR, e.getMessage(), e);
} finally {
}
}
How do you generate the PDF?
Did you set a mimetype so that the brower will recognize that you respond with a pdf?
Did you also prevent primefaces from continuing the response after you have written your PDF file to it? (use facesContext.responseComplete(); for that)
When you use the default HTML link tag <a />, you have to set href='javascript:void(0)' to avoid the current page to scroll to the top.
Maybe there is a way with a p:commandLink to do the same thing
<p:commandLink url="javascript:void(0)" ... /> ??
Hope this will help you to resolve your problem
I think it's because you are using ajax=false.
If you are not using ajax the whole page will be reloaded.
Either remove it or change to ajax=true and give it a try.
Edit:
I was wrong. ajax=false is required when downloading files.

Reload EditorPane

I found here: what I was searching for but still I have some issues.
This is my action code:
private void jMenuItem1ActionPerformed(java.awt.event.ActionEvent evt) throws IOException {
jEditorPane1.setContentType("text/html");
int returnVal = FileChooser1.showOpenDialog(this);
if (returnVal == FileChooser1.APPROVE_OPTION) {
String image = String.format("<img src=\"%s\">", FileChooser1.getSelectedFile());
jEditorPane1.setText(image);
}
}
Here is a screenshot of what happens, as you can see the image is not loaded.
http://postimg.org/image/agc665ih1/
But if I save the file (with save button) and reopen the same file (with open button), the image is there and is perfectly loaded.
I already tried the .repaint() and .revalidate() methods, but is not working..
Any idea?
This may be problem in setting up path in JEditorPane page. use this:
String image = String.format("<img src=\"%s\">", FileChooser1.getSelectedFile().getPath());
I am assuming that you have already selected appropriate editorKit for JEditorPane.
So now I can answer with my code. I am using this class for the file chooser:
import java.io.File;
import javax.swing.filechooser.FileFilter;
class jpgfilter extends FileFilter {
public boolean accept(File file) {
return file.isDirectory() || file.getAbsolutePath().endsWith(".jpg");
}
public String getDescription() {
return "JPG image (*.jpg)";
}
}
And in my main class I have this:
FileChooser1 = new javax.swing.JFileChooser();
FileChooser1.setDialogTitle("Choose your image:");
FileChooser1.setFileFilter(new jpgfilter());
And that's it.
So I actually found some kind of a solution but I think there is really too much code and that I should do it easily..I actually insert the image and simultaneously save and open the content of the EditorPane as .html file.
The code:
jEditorPane1.setContentType("text/html");
int returnVal = FileChooser1.showOpenDialog(this);
if (returnVal == FileChooser1.APPROVE_OPTION) {
String image = String.format("<img src=\"%s\">", FileChooser1
.getSelectedFile().getPath());
jEditorPane1.setText(image);
String type = jEditorPane1.getContentType();
OutputStream os = new BufferedOutputStream(new FileOutputStream(
"/Library/java_test/temp" + ".html"));
Document doc = jEditorPane1.getDocument();
int length = doc.getLength();
if (type.endsWith("/rtf")) {
// Saving RTF - use the OutputStream
try {
jEditorPane1.getEditorKit().write(os, doc, 0, length);
os.close();
} catch (BadLocationException ex) {
}
} else {
// Not RTF - use a Writer.
Writer w = new OutputStreamWriter(os);
jEditorPane1.write(w);
w.close();
}
String url = "file:///" + "/Library/java_test/temp" + ".html";
jEditorPane1.setPage(url);
}

LWUIT: How to open XHTML file from resource?

I want to open XHTML file which resides in resources. I tried with HTMLComponent of LWUIT. But it is opening only HTML files only. What would be correct way to open XHTML file using any LWUIT component.?
I tried with below code. Its working for HTML files with simple tags. Not working for all tags and attributes.
String htmlFileName = "index.html";
HTMLComponent htmlC = new HTMLComponent(new FileRequestHandler(HtmlScreen.this));
htmlC.setHTMLCallback(new SimpleHTMLCallback(this));
htmlC.setPage("jar://"+"/res/"+htmlFileName.trim());
FileRequestHandler.java:
class FileRequestHandler implements com.divaa.app.DocumentRequestHandler {
HtmlScreen htmlDemo;
static final String DEFAULT_RES = "images";
public FileRequestHandler(HtmlScreen htmlDemo) {
this.htmlDemo=htmlDemo;
}
public InputStream resourceRequested(DocumentInfo docInfo) {
String url=docInfo.getUrl();
// If a from was submitted on a local file, just display the parameters
if ((docInfo.getParams()!=null) && (!docInfo.getParams().equals(""))) {
String method="GET";
if (docInfo.isPostRequest()) {
method="POST";
}
String params=docInfo.getParams();
String newParams="";
if (params!=null) {
for(int i=0;i<params.length();i++) {
char c=params.charAt(i);
if (c=='&') {
newParams+=", ";
} else {
newParams+=c;
}
}
}
return getStream("<h2>Form submitted locally.</h2><b>Method:</b> "+method+"<br><br><b>Parameters:</b><br>"+newParams+"<hr>Continue to local URL","Form Results");
}
url=url.substring(6); // Cut the jar://
byte[] buf;
try {
buf = getBuffer(Display.getInstance().getResourceAsStream(getClass(), url));
if (url.endsWith(".html")) { //only set source to HTML files (not images)
htmlDemo.setSource(new String(getBuffer(new ByteArrayInputStream(buf))));
}
return new ByteArrayInputStream(buf);
} catch (Exception ex) {
System.out.println("ex.toString exception........ "+ex.toString());
ex.printStackTrace();
}
return null;
}
SimpleHTMLCallBack.java:
class SimpleHTMLCallback extends DefaultHTMLCallback {
HtmlScreen htmlDemo;
public SimpleHTMLCallback(HtmlScreen htmlDemo) {
this.htmlDemo=htmlDemo;
}
public boolean linkClicked(HTMLComponent htmlC, String url) {
return true; // Signals the HTMLComponent to prcoess this link as usual (i.e. call DocumentRequestHandler.resourceRequested)
}
public void titleUpdated(HTMLComponent htmlC, String title) {
htmlDemo.setTitle(title);
}
}
Thanks...

Categories