/**
*
* Copyright
* 2009-2015 Jayway Products AB
* 2016-2017 Föreningen Sambruk
*
* Licensed under AGPL, Version 3.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/agpl.txt
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package se.streamsource.streamflow.web.application.pdf;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg;
public class PdfDocument
{
private PDDocument pdf = null;
private PDPageContentStream contentStream = null;
private float y = -1;
private ArrayList<LineObject> pageLines = new ArrayList<>();
private float maxStringLength;
private PDRectangle pageSize;
static float DEFAULT_HEADER_MARGIN = 70;
static float DEFAULT_FOOTER_MARGIN = 50;
static float DEFAULT_LEFT_MARGIN = 50;
static float DEFAULT_RIGHT_MARGIN = 50;
private float headerMargin = DEFAULT_HEADER_MARGIN;
private float footerMargin = DEFAULT_FOOTER_MARGIN;
private float leftMargin = DEFAULT_LEFT_MARGIN;
private float rightMargin = DEFAULT_RIGHT_MARGIN;
public PdfDocument()
{
this( PDPage.PAGE_SIZE_A4, DEFAULT_HEADER_MARGIN, DEFAULT_FOOTER_MARGIN, DEFAULT_LEFT_MARGIN, DEFAULT_RIGHT_MARGIN );
}
public PdfDocument( PDRectangle pageSize, float headerMargin, float footerMargin )
{
this.pageSize = pageSize;
this.headerMargin = headerMargin;
this.footerMargin = footerMargin;
}
public PdfDocument( PDRectangle pageSize, float headerMargin, float footerMargin, float leftMargin, float rightMargin )
{
this.pageSize = pageSize;
this.headerMargin = headerMargin;
this.footerMargin = footerMargin + 30;
this.leftMargin = leftMargin;
this.rightMargin = rightMargin;
}
public void init()
{
try
{
pdf = new PDDocument();
PDPage page = new PDPage();
pdf.addPage( page );
page.setMediaBox( pageSize );
maxStringLength = page.getMediaBox().getWidth() - ( leftMargin + rightMargin );
contentStream = new PDPageContentStream( pdf, page );
contentStream.beginText();
y = page.getMediaBox().getHeight() - headerMargin;
contentStream.moveTextPositionByAmount( leftMargin, y );
} catch (IOException e)
{
try
{
if (contentStream != null)
{
contentStream.close();
}
} catch (IOException ioe)
{
contentStream = null;
}
throw new IllegalStateException( e.getMessage() );
}
}
public PdfDocument println( String text, PdfFont font ) throws IOException
{
return print( text + '\n', font );
}
public PdfDocument printAlignedRight( String text, PdfFont font ) throws IOException
{
pageBreakIfNeeded( font );
float stringLength = (font.font.getStringWidth( text ) / 1000) * font.size;
float startFrom = maxStringLength - stringLength;
contentStream.moveTextPositionByAmount( startFrom, 0 );
contentStream.setFont( font.font, font.size );
contentStream.drawString( text );
contentStream.moveTextPositionByAmount( -startFrom, -font.height );
y -= font.height;
return this;
}
public PdfDocument print( String text, PdfFont font) throws IOException
{
List<String> lines = createLinesFromText( text, font );
for (String line : lines)
{
pageBreakIfNeeded( font );
contentStream.setFont( font.font, font.size );
if (contentStream == null)
{
throw new IOException( "Error:Expected non-null content stream." );
}
contentStream.moveTextPositionByAmount( 0, -font.height );
y -= font.height;
contentStream.drawString( line );
}
return this;
}
public PdfDocument moveUpOneRow(PdfFont font) throws IOException{
contentStream.moveTextPositionByAmount( 0, font.height );
return this;
}
public PdfDocument insertImage(BufferedImage image) throws IOException
{
// contentStream.endText();
PDJpeg pdfImage = new PDJpeg(pdf, image);
contentStream.drawXObject(pdfImage, leftMargin, y, 100, 100);
// contentStream.beginText();
contentStream.moveTextPositionByAmount(0, -110);
y -= 100+10;
return this;
}
private void pageBreakIfNeeded( PdfFont font )
throws IOException
{
if (y - font.height < footerMargin )
{
PDPage newPage = new PDPage();
newPage.setMediaBox( pageSize );
if (contentStream != null)
{
contentStream.endText();
//Drawing all lines here after complete filling page
if (this.pageLines.size() > 0) {
for (ListIterator<LineObject> itr = pageLines.listIterator(); itr.hasNext(); ) {
LineObject lineObject = itr.next();
this.line(lineObject.getEndX(), lineObject.getyPosition(), lineObject.getColor());
itr.remove();
}
}
contentStream.close();
}
pdf.addPage( newPage );
contentStream = new PDPageContentStream( pdf, newPage );
y = newPage.getMediaBox().getHeight() - headerMargin - font.height;
contentStream.beginText();
contentStream.moveTextPositionByAmount( leftMargin, y );
}
}
public PdfDocument printLabelAndIndentedText( String label, PdfFont labelFont, String text, PdfFont font , float indentation)
throws IOException
{
pageBreakIfNeeded( labelFont );
contentStream.moveTextPositionByAmount( 0, 0 );
contentStream.setFont( labelFont.font, labelFont.size );
float oldMaxStringLenght = maxStringLength;
maxStringLength = maxStringLength - 20;
print( label, labelFont );
contentStream.moveTextPositionByAmount( indentation, 0 );
print( text, font );
contentStream.moveTextPositionByAmount( -indentation, -font.height );
maxStringLength = oldMaxStringLenght;
y -=font.height;
return this;
}
public PdfDocument printLabelAndTextWithTabStop( String label, PdfFont labelFont, String text, PdfFont font, float tabStop )
throws IOException
{
pageBreakIfNeeded( labelFont );
contentStream.moveTextPositionByAmount( 0, 0 );
contentStream.setFont( labelFont.font, labelFont.size );
contentStream.drawString( label );
contentStream.moveTextPositionByAmount( tabStop, font.height );
float oldMaxStringLength = maxStringLength;
maxStringLength = maxStringLength - tabStop;
print( text, font );
maxStringLength = oldMaxStringLength;
contentStream.moveTextPositionByAmount( -tabStop, -font.height );
return this;
}
private List<String> createLinesFromText( String text, PdfFont font) throws IOException
{
ArrayList<String> resultLines = new ArrayList<String>();
String[] lines = text.trim().split( "\n" );
int lineIndex = 0;
while (lineIndex < lines.length)
{
String[] words = lines[lineIndex].trim().split( " " );
int wordIndex = 0;
while (wordIndex < words.length)
{
StringBuffer nextLineToDraw = new StringBuffer();
float lengthIfUsingNextWord = 0;
do
{
nextLineToDraw.append( words[wordIndex] );
nextLineToDraw.append( " " );
wordIndex++;
if (wordIndex < words.length)
{
String lineWithNextWord = nextLineToDraw.toString() + words[wordIndex];
lengthIfUsingNextWord =
(font.font.getStringWidth( lineWithNextWord ) / 1000) * font.size;
}
}
while (wordIndex < words.length &&
lengthIfUsingNextWord < maxStringLength );
resultLines.add( nextLineToDraw.toString() );
}
lineIndex++;
}
return resultLines;
}
public PdfDocument underLine( String string, PdfFont font ) throws IOException
{
pageLines.add(new LineObject(y, (font.font.getStringWidth(string) / 1000) * font.size, Color.BLACK));
return this;
}
public PdfDocument line() throws IOException
{
return line(Color.BLACK);
}
public PdfDocument line(Color color) throws IOException
{
//Saving line y coordinate to draw before creating new page
pageLines.add(new LineObject(y - 4, maxStringLength, color));
//Leaving space for line
contentStream.moveTextPositionByAmount(0, -8);
y -= 8;
return this;
}
public PDDocument generateHeaderAndPageNumbers( PdfFont font, String... headers )
{
try
{
int pageTotal = pdf.getNumberOfPages();
int pageCount = 1;
float stringWidth = 0.0f;
float positionX = 0.0f;
for (Object o : pdf.getDocumentCatalog().getAllPages())
{
String numbering = "" + pageCount + " (" + pageTotal + ")";
PDPage page = (PDPage) o;
PDRectangle pageSize = page.findMediaBox();
float positionY = pageSize.getHeight() - headerMargin + font.height;
PDPageContentStream stream = new PDPageContentStream( pdf, page, true, true );
stream.beginText();
stream.setFont( font.font, font.size );
stream.moveTextPositionByAmount( 0, positionY );
for( String header : headers )
{
stringWidth = font.font.getStringWidth( header );
positionX = (pageSize.getWidth() - rightMargin - (stringWidth*font.size)/1000f);
stream.moveTextPositionByAmount( positionX, 0 );
stream.drawString( header );
stream.moveTextPositionByAmount( -positionX, -font.height );
positionY -= font.height;
}
stringWidth = font.font.getStringWidth( numbering );
positionX = (pageSize.getWidth() - rightMargin - (stringWidth*font.size)/1000f);
stream.moveTo( pageSize.getLowerLeftX(), pageSize.getLowerLeftY() );
stream.moveTextPositionByAmount( positionX, 30 - positionY );
stream.drawString( numbering );
stream.endText();
stream.close();
pageCount++;
}
} catch (IOException ioe)
{
close();
}
return closeAndReturn();
}
private PdfDocument line(float endX, float yPos, Color color) throws IOException {
changeColor(color);
contentStream.drawLine(leftMargin, yPos, endX + leftMargin, yPos);
changeColor(Color.BLACK);
return this;
}
public float calculateTabStop( PdfFont font, String... strings )
throws IOException
{
float tabStop = 0.0f;
for (int i = 0; i < strings.length; i++)
{
String string = strings[i];
float length = (font.font.getStringWidth( string ) / 1000) * font.size;
tabStop = tabStop < length ? length : tabStop;
}
return tabStop + 20;
}
public PdfDocument changeColor( Color color )
throws IOException
{
contentStream.setStrokingColor( color );
contentStream.setNonStrokingColor( color );
return this;
}
public PDDocument closeAndReturn()
{
close();
return pdf;
}
private void close()
{
if (contentStream != null) {
try {
contentStream.endText();
//Draw lines if document only one page
if (pdf.getNumberOfPages() < 2) {
for (ListIterator<LineObject> itr = pageLines.listIterator(); itr.hasNext(); ) {
LineObject lineObject = itr.next();
this.line(lineObject.getEndX(), lineObject.getyPosition(), lineObject.getColor());
itr.remove();
}
}
} catch (IOException e) {
} finally {
try {
contentStream.close();
} catch (IOException e) {
contentStream = null;
}
}
}
}
}