Closed atown45 closed 7 years ago
So I guess you want to alter the content during processing? What exactly is the use case?
Yes, that is correct. I'd like to alter the content during processing. Basically, the user can select a number of items for which to have information printed. The header on each page should contain the item number (along with some other static data such as date, which I have working). During processing, when the item number changes I create a new page using "document.add(ControlElement.NEWPAGE);" so that every item starts on new page. The problem I've run into is only the last item number is being displayed in all of the page headers.
Thanks for any advice you can offer.
Well, if you are doing this inside of the RenderListener, you could just call RenderContext.newPage(). This will immediately trigger a new page. Would that be possible for you?
I'm calling document.add(ControlElement.NEWPAGE); from the java code that is building the report, not inside the RenderListener. I am, however, setting the header info in the RenderListener. I'm not sure I understand how calling RenderContext.newPage() within the RenderListener will allow me to have the dynamic content I need in the header. Can you explain how that would work?
So I guess I totally misunderstood your purpose, sorry for that. Could you send me some example code? I'm not getting the point when exactly you are trying add content resp. a new page. Is that after you called document.write(), means during rendering, or before?
Hopfully this helps explain it a little better.
Again, I need the corresponding item number to be displayed in the header of each page. For example, for item number 1, the header would be "Item No: 1" on all pages with info for item number 1. When all of the info for item number 1 has been include in the document a document.add(ControlElement.NEWPAGE); is done so that info for item number 2 will start on a new page. At that point the header would be "Item No: 2", etc.
Here's a code snippet and part of my class that implements RenderListener:
PageFormat letter_portrait = PageFormat.with().margins(5, 5, 25, 40).letter().portrait().build(); Document document = new Document(letter_portrait);
ReportHeaderFooter headerFooter = new ReportHeaderFooter(); document.addRenderListener(headerFooter);
<...code to get items based on user supplied parameters...>
while (itemIterator.hasNext()) { <...code to get item info...>
// set header and footer info for the item
headerFooter.setHeader(headerText, itemInfo.getItemNumber());
headerFooter.setFooter(footerText, true);
generateItemPDF(itemInfo, document);
document.add(ControlElement.NEWPAGE);
}
===============================================================================================================================
public class ReportHeaderFooter implements RenderListener { private static final SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss a z");
String header;
String footer;
Integer itemNumber;
boolean includeTimestamp = false;
// Change the content of the header
public void setHeader(String header, Integer itemNumber) {
this.header = header;
this.itemNumber = itemNumber;
}
// Change the content of the footer
public void setFooter(String footer, boolean includeTimestamp) {
this.footer = footer;
this.includeTimestamp = includeTimestamp;
}
@Override
public void beforePage(RenderContext renderContext) throws IOException {
}
@Override
public void afterPage(RenderContext renderContext) throws IOException {
// Create Header
TextFlow headerText = TextFlowUtil.createTextFlow(this.header, 12, PDType1Font.TIMES_ROMAN);
float headerOffset = renderContext.getPageFormat().getMarginLeft() + TextSequenceUtil.getOffset(headerText, renderContext.getWidth(), Alignment.Left);
headerText.drawText(renderContext.getContentStream(), new Position(headerOffset, (renderContext.getUpperLeft().getY() + 20)), Alignment.Left, null);
if ( itemNumber != null ) {
headerText = TextFlowUtil.createTextFlow("Item No: " + this.itemNumber, 12, PDType1Font.TIMES_ROMAN);
}
headerOffset = renderContext.getPageFormat().getMarginLeft() + TextSequenceUtil.getOffset(headerText, renderContext.getWidth(), Alignment.Center);
headerText.drawText(renderContext.getContentStream(), new Position(headerOffset, (renderContext.getUpperLeft().getY() + 20)), Alignment.Center, null);
headerText = TextFlowUtil.createTextFlow(this.header, 12, PDType1Font.TIMES_ROMAN);
headerOffset = renderContext.getPageFormat().getMarginLeft() + TextSequenceUtil.getOffset(headerText, renderContext.getWidth(), Alignment.Right);
headerText.drawText(renderContext.getContentStream(), new Position(headerOffset, (renderContext.getUpperLeft().getY() + 20)), Alignment.Right, null);
// Create Footer
TextFlow footerText = TextFlowUtil.createTextFlow(this.footer + "\n", 12, PDType1Font.TIMES_ROMAN);
float footerOffset = renderContext.getPageFormat().getMarginLeft() + TextSequenceUtil.getOffset(footerText, renderContext.getWidth(), Alignment.Left);
footerText.drawText(renderContext.getContentStream(), new Position(footerOffset, 30), Alignment.Left, null);
String pg = String.format("%s", renderContext.getPageIndex() + 1);
footerText = TextFlowUtil.createTextFlow(pg, 12, PDType1Font.TIMES_ROMAN);
footerOffset = renderContext.getPageFormat().getMarginLeft() + TextSequenceUtil.getOffset(footerText, renderContext.getWidth(), Alignment.Center);
footerText.drawText(renderContext.getContentStream(), new Position(footerOffset, 30), Alignment.Center, null);
String dateTimestamp = "";
if (this.includeTimestamp) {
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
dateTimestamp = sdf.format(timestamp);
}
footerText = TextFlowUtil.createTextFlow(this.footer + "\n" + dateTimestamp, 12, PDType1Font.TIMES_ROMAN);
footerOffset = renderContext.getPageFormat().getMarginLeft() + TextSequenceUtil.getOffset(footerText, renderContext.getWidth(), Alignment.Right);
footerText.drawText(renderContext.getContentStream(), new Position(footerOffset, 30), Alignment.Right, null);
}
}
is there a way, during the rendering, to detect the new page added with the call to document.add(ControlElement.NEWPAGE;? If I can detect that I should be able to get my header issue resolved.
Well, every new page triggers the RenderListener, but I guess that does not help you...
Let's see if I got it right this time: when building the document, you need to add some metadata (the item number, which changes with the content), and the RenderListener must evaluate this data to print the corresponding header/footer data. So it is a bit like the layout or the page format, which can also change from page to page. Is that correct, am I on the right track now?
Yes, that is correct.
I will pass you a draft (jar) the next days, so you can test if my solution fits your needs. Are you using PDFBox 1.8.x or 2.x ?
Great, thanks. I'm using PDFBox 2.x.
Ok, I added an API for registering custom renderer, so you may now extend elements like paragraph or event invent your own elements and render them. If you can not render an element, just return false, so the default (or other regsitered renderer) will try. Here is an example where I have my own section element, which will trigger a new page on every new section, and the section number will be printed along with the page number. Let me know, if something like this will fit your needs.
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.pdfbox.pdmodel.font.PDType1Font;
import rst.pdfbox.layout.elements.Document;
import rst.pdfbox.layout.elements.Element;
import rst.pdfbox.layout.elements.Paragraph;
import rst.pdfbox.layout.elements.render.LayoutHint;
import rst.pdfbox.layout.elements.render.RenderContext;
import rst.pdfbox.layout.elements.render.RenderListener;
import rst.pdfbox.layout.elements.render.Renderer;
import rst.pdfbox.layout.text.Alignment;
import rst.pdfbox.layout.text.BaseFont;
import rst.pdfbox.layout.text.Position;
import rst.pdfbox.layout.text.TextFlow;
import rst.pdfbox.layout.text.TextFlowUtil;
import rst.pdfbox.layout.text.TextSequenceUtil;
public class Listener {
public static void main(String[] args) throws Exception {
String text1 = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, "
+ "sed diam nonumy eirmod tempor invidunt ut labore et dolore magna "
+ "aliquyam erat, _sed diam_ voluptua. At vero eos et *accusam et justo* "
+ "duo dolores et ea rebum.\n\nStet clita kasd gubergren, no sea takimata "
+ "sanctus est *Lorem ipsum _dolor* sit_ amet. Lorem ipsum dolor sit amet, "
+ "consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt "
+ "ut labore et dolore magna aliquyam erat, *sed diam voluptua.\n\n"
+ "At vero eos et accusam* et justo duo dolores et ea rebum. Stet clita kasd "
+ "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n\n";
String text2 = "At *vero eos et accusam* et justo duo dolores et ea rebum."
+ "Stet clita kasd gubergren, no sea takimata\n\n"
+ "sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, "
+ "_consetetur sadipscing elitr_, sed diam nonumy eirmod tempor invidunt "
+ "ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero "
+ "eos et _accusam et *justo* duo dolores_ et ea rebum. Stet clita kasd "
+ "gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.\n";
Document document = new Document(40, 60, 40, 60);
SectionRenderer sectionRenderer = new SectionRenderer();
document.addRenderer(sectionRenderer);
document.addRenderListener(sectionRenderer);
Paragraph paragraph = new Paragraph();
paragraph.addMarkup(text1, 11, BaseFont.Times);
paragraph.addMarkup(text2, 12, BaseFont.Helvetica);
paragraph.addMarkup(text1, 8, BaseFont.Courier);
document.add(new Section(1));
document.add(paragraph);
document.add(paragraph);
document.add(paragraph);
document.add(new Section(2));
document.add(paragraph);
document.add(paragraph);
document.add(paragraph);
document.add(new Section(3));
document.add(paragraph);
document.add(paragraph);
final OutputStream outputStream = new FileOutputStream("listener.pdf");
document.save(outputStream);
}
public static class SectionRenderer implements Renderer, RenderListener {
private int sectionNumber;
@Override
public boolean render(RenderContext renderContext, Element element,
LayoutHint layoutHint) throws IOException {
if (element instanceof Section) {
if (renderContext.getPageIndex() > 1) {
// no new page on first page ;-)
renderContext.newPage();
}
sectionNumber = ((Section)element).getNumber();
return true;
}
return false;
}
@Override
public void beforePage(RenderContext renderContext) {
}
@Override
public void afterPage(RenderContext renderContext) throws IOException {
String content = String.format("Section %s, Page %s",
sectionNumber, renderContext.getPageIndex() + 1);
TextFlow text = TextFlowUtil.createTextFlow(content, 11,
PDType1Font.TIMES_ROMAN);
float offset = renderContext.getPageFormat().getMarginLeft()
+ TextSequenceUtil.getOffset(text,
renderContext.getWidth(), Alignment.Right);
text.drawText(renderContext.getContentStream(), new Position(
offset, 30), Alignment.Right, null);
}
}
public static class Section implements Element {
private int number;
public Section(int number) {
super();
this.number = number;
}
public int getNumber() {
return number;
}
}
}
Any feedback on this?
Added custom renderer support to 1.0.0, closing this issue
Is there a way to have dynamic content in a page header/footer? I setup a RenderListener that I'm using to print a header and footer on my report. It works great except for one place in the header where I need some dynamic content. I tried creating a new RenderListener and adding it to the document when I do a document.add(ControlElement.NEWPAGE) but that didn't work.
Any thoughts?
Thanks.