I am generating a PDF file via fop 1.0 out of a java library. The unit tests are running fine and the PDF is rendered as expected, including an external graphic:
<fo:external-graphic content-width="20mm" src="url('images/image.png')" />
If I render this within a Java EE application in glassfish 3.1, I always get the following error:
Image not found. URI: images/image.png. (No context info available)
I double-checked whether the image is available. It is available within the .jar file in the .ear file and should therfore be available by the ClasspathUriResolver. This is a code-snipplet of how I setup the fop-factory:
FopFactory fopFactory = FopFactory.newInstance();
URIResolver uriResolver = new ClasspathUriResolver();
fopFactory.setURIResolver(uriResolver);
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
...
I also assigned the URI resolver to the TransformerFactory and the Transformer with no success. Would be great if someone can help me out.
-- Wintermute
Btw: the ClasspathUriResolver() looks like this
public class ClasspathUriResolver implements URIResolver {
#Override
public Source resolve(String href, String base) throws TransformerException {
Source source = null;
InputStream inputStream = ClassLoader.getSystemResourceAsStream(href);
if (inputStream != null) {
source = new StreamSource(inputStream);
}
return source;
}
}
You consider a different class loader then ClassLoader.getSystemResourceAsStream(href);
Try InputStream inputStream = getClass().getResourceAsStream(href); or something else, maybe.
Does it work, then?
Related
I'm using bamboo and bamboo java-spec using the pipeline as java code in a bitbucket hosted project.
I'm trying to use a json file as a configuration file to specify which stages I want to run in my pipeline.
So I've created a configuration.json file. And i've added the following code in my #BambooSpec annotated PlanSpec class.
private static Map<?, ?> getConfiguration(String configurationFile) throws Exception {
System.out.println("Does this work at all?");
Map<?, ?> map = null;
try {
// create Gson instance
Gson gson = new Gson();
// create a reader
Reader reader = Files.newBufferedReader(Paths.get(configurationFile));
// convert JSON file to map
map = gson.fromJson(reader, Map.class);
// print map entries
for (Map.Entry<?, ?> entry : map.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
// close reader
reader.close();
} catch (Exception ex) {
ex.printStackTrace();
throw ex;
}
return map;
}
But bamboo only shows logs for what's run as part of the plan spec. The System.out.println's, are not visable.
Is there a way to debug my code at runtime?
Edit: in the mean time I found out I can just run my code locally in the IDE. And then it'll complain about not finding a .credentials file. But that doesn't matter. I can at least test the code, from before it publishes the plan.
I'm following this guide in Chapter 6 of iText 7: Converting HTML to PDF with pdfHTML on adding extra fonts:
public static final String FONT = "src/main/resources/fonts/cardo/Cardo-Regular.ttf";
public void createPdf(String src, String font, String dest) throws IOException {
ConverterProperties properties = new ConverterProperties();
FontProvider fontProvider = new DefaultFontProvider(false, false, false);
FontProgram fontProgram = FontProgramFactory.createFont(font);
fontProvider.addFont(fontProgram, "Winansi");
properties.setFontProvider(fontProvider);
HtmlConverter.convertToPdf(new File(src), new File(dest), properties);
}
While it's working as expected and embedding subsets of the fonts being used, I'm wondering if there is a way for the resulting PDF document to not embed the fonts at all. This is possible when creating BaseFont instances and setting the embedded property to false and using them to build various PDF building blocks. What I'm looking for is this same behavior when using the HtmlConverter.convertToPdf().
What you should normally do is override FontProvider:
FontProvider fontProvider = new DefaultFontProvider(false, false, false) {
#Override
public boolean getDefaultEmbeddingFlag() {
return false;
}
};
However, the problem is that at the moment this font provider would be overwritten by pdfHTML further into the pipeline in ProcessorContext#reset.
While this issue is not fixed in iText you can build a custom version of pdfHTML for your needs. The repo is located at https://github.com/itext/i7j-pdfhtml and you are interested in this line. Just replace it with the overload as above and build the jar.
UPD The fix is available starting from pdfHTML 2.1.3. From that version on you can use custom font providers freely.
I'd like to open a PDF in a new Page from JSF2, and display a certain page in this pdf on load. I have a kind of TOC in my jsf page, and want to jump from there to the page in the PDF directly.
What I know (this is not, what I need, just an example of giving adobe reader and other pdf readers the page I want to jump to):
Something like this will open the page (chose something from the internet):
https://www.cdc.gov/diabetes/pdfs/data/statistics/national-diabetes-statistics-report.pdf#page=10
The #page=10 makes the pdf plugin of the browser display page 10.
Requirements for selecting the PDF:
PDF is dynamically downloaded from a webservice according to an ID that must only reside in the ManagedBeans, since it's secret, and should not be passed to others (like Session ID...) (below given anser by me passes the ID in the GET-Parameter, which should not be done)
PDF should not reside in the Filesystem, sinc I don't want the handling of temporary files (below given answer by me actually utilizes PDFs on FS, with stream only it does not work)
Now my real problem: I have to change the URL beeing displayed/used in JSF, but can't use the normal way with and includeViewParams, because this will insert a "?", and not a "#" in the URL.
Also, I have a backing bean, that gets the content of the PDF from a backend service, based on some other parameters I'm giving, so a solution with would be cool, but I'm aware that this is probably not possible...
Does anyone have an idea, how to solve this?
I didn't include any code, since it doesn't work anyways, and I probably need a completely new way to solve this anyways...
Turns out, Primefaces has this already implemented (although the implementation has it's restrictions):
<p:media player="pdf" value="#{viewerBean.media}" width="100%" height="100%">
<f:param name="#page" value="#{viewerBean.pageNumber}"/>
<f:param name="toolbar" value="1"/>
<!--<f:param name="search" value="#{viewerBean.queryText}"/>-->
</p:media>
https://www.primefaces.org/showcase/ui/multimedia/media.xhtml
Restriction: Can't read from a stream, at least not very stable. Save your energy, and write a stream to a temp file, and set this filename dynamically. Not sure, whether this is complete, but you should get the idea:
import javax.faces.bean.ManagedProperty;
import javax.faces.bean.RequestScoped;
import java.io.*;
import javax.annotation.PostConstruct;
import java.nio.file.Files;
import java.nio.file.Paths;
#ManagedBean
#RequestScoped
public class ViewerBean implements Serializable {
#ManagedProperty(value = "#{param.page}")
private String pageNumber;
private File media;
#PostConstruct
public void init() {
try {
media = Files.createTempFile("car", ".pdf").toFile();
try (FileOutputStream outputStream = new FileOutputStream(media)) {
IOUtils.copy(getStreamedContent().getStream(), outputStream);
}
} catch (IOException e) {
LOGGER.error(e);
throw new RuntimeException("Error creating temp file", e);
}
}
public StreamedContent getMedia() {
try {
return new DefaultStreamedContent(new FileInputStream(media), "application/pdf");
} catch (FileNotFoundException e) {
String message = "Error reading file " + media.getAbsolutePath();
LOGGER.error(message, e);
throw new RuntimeException(message, e);
}
}
}
If the pagename is not needed, you could use this:
http://balusc.omnifaces.org/2006/05/pdf-handling.html
Maybe if you can utilize outputLink for this you'll be lucky, but I ran out of time to test this option.
Found the (THE) solution; above answher mentions , but this cannot cope with #ViewScope beans, and sends many requests to the underlying bean for reading only one InputStream. I found this not acceptable for load reasons.
So here we go:
Create JSF page with <f:event type="preRenderView" listener="#{documentDownloadBean.writeIntpuStreamToResponseOutputStream}"/>
Put neccessary data for dynamic retrieval of the PDF into flash scope
redirect to above JSF page like so: return "document_search/view_pdf.xhtml?faces-redirect=true#page=" + page;
#ManagedBean
#ViewScoped
public class DocumentDownloadBean implements Serializable {
#ManagedProperty(value = "#{documentSearchBean}")
private DocumentSearchBean documentSearchBean;
public String activeDocumentToFlashScope(String page) {
Document document = documentSearchBean.getSelectedDocument();
FacesContext.getCurrentInstance().getExternalContext().getFlash().put("document", document);
// everything preapared now, redirect to viewing JSF page, with page=xxx parameter in URL, which will be evaluated by adobe pdf reader (and other readers, too)
return "document_search/view_pdf.xhtml?faces-redirect=true#page=" + page;
}
public void download() {
Document document = (Document) FacesContext.getCurrentInstance().getExternalContext().getFlash().get("document");
InputStream inputStream = getInputstreamFromBackingWebserviceSomehow(document);
FacesUtils.writeToResponseStream(FacesContext.getCurrentInstance().getExternalContext(), inputStream, document.getFileName());
}
}
Calling JSF Page:
<p:commandLink id="outputText" action="#{documentDownloadBean.activeDocumentToFlashScope(selectedDocument, page)}"
target="_blank" ajax="false">
<h:outputText value="View PDF"/>
</p:commandLink>
I'm trying to show inline PDF which is opened in new browser window. I have following scenario:
In some ActionListen which is called by ajax I generate PDF content, put data in session, and send Javascript to be executed (window.open to open new page to show PDF)
On opened page I just have p:media tag inside h:body with value pointing to StreamedContent:
Now, on that page my PDF is not generated. In log I can see these two lines:
org.primefaces.application.PrimeResourceHandler handleResourceRequest
SEVERE: Error in streaming dynamic resource. Expression cannot be null
I started to debug and find out a few things.
First, I added breakpoint to #PostConstruct method of my RequestScoped bean. What is interesting is that breakpoint is reached twice, and to my big surprise after that PDF is shown perfectly?!
After some debugging through PrimeResourceHandler I figure out that in some cases ValueExpression is not calculated, in fact it throws NullPointerException, and again while debugging I saw that two requests are sent, and second request fails because dynamicContentId is removed in first request, and second call to handleResourceRequest doesn't have sense.
Through Firebug I can see two requests, first which is good with PDF data, and second which is also with content-type application/pdf but empty, with size 0.
xhtml page:
<html>
<h:head></h:head>
<h:body>
<p:media value="#{reportBean.streamedContent}" player="pdf" width="500" height="500"/>
</h:body>
</html>
backing bean:
#RequestScoped
public class StampaListeBackingBean implements Serializable {
private static final long serialVersionUID = 1L;
private StreamedContent streamedContent;
#PostConstruct
public void init() {
Map<String, Object> session = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
byte[] b = (byte[]) session.get("reportBytes");
if (b != null) {
streamedContent = new DefaultStreamedContent(new ByteArrayInputStream(b), "application/pdf");
}
}
public StreamedContent getStreamedContent() {
if (FacesContext.getCurrentInstance().getRenderResponse()) {
return new DefaultStreamedContent();
} else {
return streamedContent;
}
}
public void setStreamedContent(StreamedContent streamedContent) {
this.streamedContent = streamedContent;
}
}
I need to understand why two requests are sent on page with p:media tag, and to figure out how to make this work. Backing bean is request scoped, it creates StreamedContent in #PostConstruct method, and has getter and setter for that field. Primefaces version is 3.4.2, with Mojarra 2.1.14.
ADDED:
It is easy to reproduce my problem. If code in init method is replaced with following:
FileInputStream fis = new FileInputStream(new File("C:\\samplexxx.pdf"));
streamedContent = new DefaultStreamedContent(fis, "application/pdf");
problem can be reproduced.
I can reproduce your problem. It indeed doesn't work in Firefox (nor in IE9, but it works in Chrome). PrimeFaces lead Cagatay has also mentioned that several times.
I'm not sure if this is a bug in the PrimeFaces resource handler or in the browser. I'll leave it in the middle.
In the meanwhile, your best bet is a simple web servlet for the job. Just create this class:
#WebServlet("/report.pdf")
public class PdfReportServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
byte[] content = (byte[]) request.getSession().getAttribute("reportBytes");
response.setContentType("application/pdf");
response.setContentLength(content.length);
response.getOutputStream().write(content);
}
}
And invoke it as follows:
<p:media value="/report.pdf" ... />
That's it. No XML config necessary. It works for me in all browsers. Depending on the functional requirements, you may want to further finetune response headers related to browser caching.
It is not a browser or primefaces problem, just a funny getter problem.
The getter is called twice by p:media (or if you refresh page than more times), but only the 1st call gets the correct data. StreamedContent encapsulates an InputStream, which has the property that it will give no bytes if the stream is at the end of the file. First time it is read to its end (data is ok), but every next call will get no data. :)
javadoc of inputStream.read():
If no byte is available because the stream is at the end of the file, the value -1 is returned; otherwise, at least one byte is read and stored into b.
Solution:
private StreamedContent streamedContent;
private InputStream stream;
public void somewhere(){
byte[] b = ...
stream = new ByteArrayInputStream( b );
stream.mark(0); //remember to this position!
streamedContent = new DefaultStreamedContent(stream, "application/pdf");
}
public StreamedContent getStreamedContent() {
if (streamedContent != null)
streamedContent.getStream().reset(); //reset stream to the start position!
return streamedContent;
}
I hope my little contribution can help anyone who can't display pdf preview in Firefox. I was using Primefaces 6 + Spring and I had the same problem but maybe not due the same reason. Indeed, I tried the proposed solution by Balus C. It helped me to display the pdf in Chrome and IE11 but it still was not working in Firefox 52.
I noticed an error in the Firefox console: Load denied by X-Frame-Options: http://localhost:8080/myapp/ does not permit framing
In my case, it was because spring-security configuration and the solution was edit spring-context.xml in this way:
<sec:http ...>
...
<sec:headers>
<sec:frame-options policy="SAMEORIGIN" />
</sec:headers>
...
</sec:http>
I've been using Flying Saucer for a while now with awesome results.
I can set a document via uri like so
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(xhtmlUri);
Which is nice, as it will resolve all relative css resources etc relative to the given URI. However, I'm now generating the xhtml, and want to render it directly to a PDF (without saving a file). The appropriate methods in ITextRenderer seem to be:
private Document loadDocument(final String uri) {
return _sharedContext.getUac().getXMLResource(uri).getDocument();
}
public void setDocument(String uri) {
setDocument(loadDocument(uri), uri);
}
public void setDocument(Document doc, String url) {
setDocument(doc, url, new XhtmlNamespaceHandler());
}
As you can see, my existing code just gives the uri and ITextRenderer does the work of creating the Document for me.
What's the shortest way of creating the Document from my formatted xhtml String? I'd prefer to use the existing Flying Saucer libs without having to import another XML parsing jar (just for the sake of consistent bugs and functionality).
The following works:
Document document = XMLResource.load(new ByteArrayInputStream(templateString.getBytes())).getDocument();
Previously, I had tried
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setValidating(false);
final DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
Document document = documentBuilder.parse(new ByteArrayInputStream(templateString.getBytes()));
but that fails as it attempts to download the HTML docType from http://www.w3.org (which returns 503's for the java libs).
I use the following without problem:
final DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setValidating(false);
DocumentBuilder builder = documentBuilderFactory.newDocumentBuilder();
builder.setEntityResolver(FSEntityResolver.instance());
org.w3c.dom.Document document = builder.parse(new ByteArrayInputStream(doc.toString().getBytes()));
ITextRenderer renderer = new ITextRenderer();
renderer.setDocument(document, null);
renderer.layout();
renderer.createPDF(os);
The key differences here are passing in a null URI, and also provided the DocumentBuilder with an entity resolver.