Issue
I am trying to add pdf page dynamically when content exceeds the limit of page. It gives this error.Please review the below code and help me with a solution.
{ Exception in thread "main" java.lang.IllegalStateException: Error: You must call beginText() before calling endText. at org.apache.pdfbox.pdmodel.PDPageContentStream.endText(PDPageContentStream.java:385) at PDFUtility.content(PDFUtility.java:123) at StudyConfigImpl.getStudyConfigPDF(StudyConfigImpl.java:39) at Main.main(Main.java:6) }
private float addParagraph(PDPageContentStream contentStream, float width, float sx,
float sy, String text, boolean justify,float initY,float pageHeight,PDDocument document) throws IOException {
PDFont FONT = PDType1Font.HELVETICA;
float FONT_SIZE = 7;
float LEADING = -1.5f * FONT_SIZE;
java.util.List<String> lines = parseLines(text, width);
contentStream.setFont(FONT, FONT_SIZE);
contentStream.newLineAtOffset(sx, sy);
float totalHeight = initY;
for (String line: lines) {
float charSpacing = 0;
if (justify){
if (line.length() > 1) {
float size = FONT_SIZE * FONT.getStringWidth(line) / 1000;
float free = width - size;
if (free > 0 && !lines.get(lines.size() - 1).equals(line)) {
charSpacing = free / (line.length() - 1);
}
}
}
contentStream.setCharacterSpacing(charSpacing);
float fontHeight = FONT.getFontDescriptor().getFontBoundingBox().getHeight() / 1000*FONT_SIZE;
totalHeight -=fontHeight;
System.out.println(totalHeight);
if(totalHeight - StudyConfigImpl.PAGE_MARGIN <= 0){
PDPage nextPage = new PDPage();
nextPage.setMediaBox(PDRectangle.A4);
document.addPage(nextPage);
totalHeight = pageHeight - StudyConfigImpl.PAGE_MARGIN;
contentStream.endText();
contentStream.close();
contentStream = new PDPageContentStream(document,nextPage);
contentStream.beginText();
contentStream.setFont(FONT,FONT_SIZE);
contentStream.newLineAtOffset(StudyConfigImpl.INIT_POSITION_X,StudyConfigImpl.INIT_POSITION_Y);
}
contentStream.showText(line);
contentStream.newLineAtOffset(0, LEADING);
}
return (lines.size()*(7+3));
}
public float content(
PDPageContentStream contentStream,
float initX,
float initY,
float pageHeight,
float width,
float height,
Map<Object, Object> studyData,
PDDocument document
) throws IOException {
int counter = 0;
for(Map.Entry<Object, Object> entry : studyData.entrySet()){
if(entry.getKey().equals("Additional Notes") || entry.getKey().equals("Study Desc.")){
initY-=25;
contentStream.setFont(PDType1Font.HELVETICA_BOLD,7);
contentStream.beginText();
initX = 20;
contentStream.newLineAtOffset(initX,initY);
contentStream.showText(entry.getKey().toString());
float sy = addParagraph(contentStream,555,0,-10,entry.getValue().toString(),true,initY,pageHeight,document);
contentStream.endText();
initY -=sy;
}else{
contentStream.addRect(initX, pageHeight - initY, width, height);
contentStream.setFont(PDType1Font.HELVETICA_BOLD, 7);
contentStream.beginText();
contentStream.newLineAtOffset(initX, initY);
contentStream.showText(entry.getKey().toString());
contentStream.newLine();
contentStream.setFont(PDType1Font.HELVETICA, 7);
contentStream.showText(entry.getValue().toString());
contentStream.endText();
initX += width;
counter++;
if (counter == 5) {
initX = 20;
initY = initY - 20;
counter = 0;
}
}
}
return initY;
}
Solution
The cause
In addParagraph()
you conditionally assign a new PDPageContentStream
to contentStream
but this new value is not returned to the caller content()
:
private float addParagraph(PDPageContentStream contentStream, float width, float sx, float sy, String text, boolean justify, float initY, float pageHeight, PDDocument document) throws IOException {
...
contentStream.endText();
contentStream.close();
contentStream = new PDPageContentStream(document,nextPage);
contentStream.beginText();
...
return (lines.size()*(7+3));
}
Before re-assigning contentStream
you in particular call endText()
for the old stream.
Thus, content() continues working with the old content stream and calls endText()
for it again:
public float content(PDPageContentStream contentStream, float initX, float initY, float pageHeight, float width, float height, Map<Object, Object> studyData, PDDocument document) throws IOException {
...
float sy = addParagraph(contentStream,555,0,-10,entry.getValue().toString(),true,initY,pageHeight,document);
contentStream.endText();
...
return initY;
}
Calling endText
for a content stream that has no open text object triggers the exception you observed.
A work-around
Both addParagraph
and content
appear to be methods of the same class (or at least in two classes one of which is derived from the other). Thus, you can make contentStream
a member variable of that class instead of forwarding it as parameter:
public class PDFUtility {
public PDPageContentStream contentStream = null;
private float addParagraph(float width, float sx, float sy, String text, boolean justify, float initY, float pageHeight, PDDocument document) throws IOException {
...
}
public float content(float initX, float initY, float pageHeight, float width, float height, Map<Object, Object> studyData, PDDocument document) throws IOException {
...
}
...
}
and in StudyConfigImpl.getStudyConfigPDF
instead of
float y = pdfUtility.content(contentStream, initX, ...);
you do
pdfUtility.contentStream = contentStream;
float y = pdfUtility.content(initX, ...);
contentStream = pdfUtility.contentStream;
This work-around obviously requires that the same instance of your class is not used concurrently from different threads.
Answered By - mkl
Answer Checked By - Timothy Miller (JavaFixing Admin)