/* ****************************************************************************** * * Copyright 2008-2010 Hans Dijkema * * JRichTextEditor is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * JRichTextEditor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with JRichTextEditor. If not, see <http://www.gnu.org/licenses/>. * * ******************************************************************************/ package nl.dykema.jxmlnote.report.pdf; import java.awt.Image; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import javax.swing.text.AttributeSet; import javax.swing.text.StyleConstants; import nl.dykema.jxmlnote.document.XMLNoteDocument; import nl.dykema.jxmlnote.exceptions.DefaultXMLNoteErrorHandler; import nl.dykema.jxmlnote.interfaces.MarkMarkupProviderMaker; import nl.dykema.jxmlnote.report.Report; import nl.dykema.jxmlnote.report.ReportException; import nl.dykema.jxmlnote.report.XMLNoteToReport; import nl.dykema.jxmlnote.report.ReportProgressBar.Progress; import nl.dykema.jxmlnote.report.elements.Cell; import nl.dykema.jxmlnote.report.elements.Chunk; import nl.dykema.jxmlnote.report.elements.Paragraph; import nl.dykema.jxmlnote.report.elements.Rectangle; import nl.dykema.jxmlnote.report.elements.ReportElement; import nl.dykema.jxmlnote.report.elements.Tab; import nl.dykema.jxmlnote.report.elements.Table; import nl.dykema.jxmlnote.styles.XMLNoteParStyle; import com.lowagie.text.Document; import com.lowagie.text.DocumentException; import com.lowagie.text.Element; import com.lowagie.text.pdf.ColumnText; import com.lowagie.text.pdf.FontMapper; import com.lowagie.text.pdf.PdfContentByte; import com.lowagie.text.pdf.PdfPageEvent; import com.lowagie.text.pdf.PdfWriter; public class PdfReport extends Report { private PdfWriter _writer; private Document _doc; private ColumnText _text; private float _jpegQuality = 0.9f; private FontMapper _mapper; private com.lowagie.text.Rectangle _pdfPageSize; private Orientation _orientation=Orientation.PORTRAIT; private PageSize _pageSize=PageSize.A4; private boolean _canceled = false; private File fontmapcacheFile=null; private InputStream _fontmapcachein=null; private OutputStream _fontmapcacheout=null; private Hashtable<String,com.lowagie.text.Image> _imageInstances; // ///////////////////////////////////////////////////////////////////////////////// // Support methods // ///////////////////////////////////////////////////////////////////////////////// public com.lowagie.text.Image getImageInstance(String key) { return _imageInstances.get(key); } public void putImageInstance(String key,com.lowagie.text.Image inst) { _imageInstances.put(key,inst); } public boolean hasImageInstance(String key) { return _imageInstances.containsKey(key); } public String toString() { Rectangle pr=new Rectangle(0.0f, 0.0f, 0.0f, 0.0f); Rectangle mg=new Rectangle(0.0f, 0.0f, 0.0f, 0.0f); try { mg=getMargins(); pr=getPageRect(); } catch (ReportException E) { } return String.format("PdfReport: PageSize=%s, Margins=%s",pr.toString(),mg.toString()); } public int getPdfAlign(Report.Align a) { if (a.equals(Align.LEFT)) { return Element.ALIGN_LEFT; } else if (a.equals(Align.RIGHT)) { return Element.ALIGN_RIGHT; } else if (a.equals(Align.CENTER)) { return Element.ALIGN_CENTER; } else if (a.equals(Align.JUSTIFY)) { return Element.ALIGN_JUSTIFIED; } else { return Element.ALIGN_LEFT; } } public int getAlign(AttributeSet s) { if (StyleConstants.getAlignment(s) == StyleConstants.ALIGN_LEFT) { return Element.ALIGN_LEFT; } else if (StyleConstants.getAlignment(s) == StyleConstants.ALIGN_CENTER) { return Element.ALIGN_CENTER; } else if (StyleConstants.getAlignment(s) == StyleConstants.ALIGN_RIGHT) { return Element.ALIGN_RIGHT; } else if (StyleConstants.getAlignment(s) == StyleConstants.ALIGN_JUSTIFIED) { return Element.ALIGN_JUSTIFIED; } else { return Element.ALIGN_LEFT; } } com.lowagie.text.Rectangle getPdfPageSize() { return _pdfPageSize; } public FontMapper getFontMapper() { if (_fontmapcacheout!=null) { try { _mapper=PdfFontMapper.createPdfFontMapper(this,_fontmapcachein,_fontmapcacheout); } catch (IOException e) { DefaultXMLNoteErrorHandler.exception(e); _mapper=PdfFontMapper.createPdfFontMapper(this,null); } } else { _mapper = PdfFontMapper.createPdfFontMapper(this,fontmapcacheFile); } return _mapper; } public PdfWriter pdfWriter() { return _writer; } public ColumnText text() { return _text; } public Document document() { return _doc; } public float jpegQuality() { return _jpegQuality; } private void checkWriter() throws ReportException { if (_writer == null) { throw new ReportException("Begin report first"); } } private void checkIsNotOpen() throws ReportException { checkWriter(); if (_doc.isOpen()) { throw new ReportException("Implementation specific: Add meta information before the first ReportElement is added"); } } private void checkDocOpen() { if (!_doc.isOpen()) { _doc.open(); } } String informStatus(String msg) { Progress prg=(Progress) super.getProperty(Report.Property.PROGRESS); if (prg!=null) { return prg.statusMessage(msg); } else { return msg; } } int informProgress(int p) { Progress prg=(Progress) super.getProperty(Report.Property.PROGRESS); if (prg!=null) { return prg.progress(p); } else { return p; } } // ///////////////////////////////////////////////////////////////////////////////// // Interface methods // ///////////////////////////////////////////////////////////////////////////////// public void setMetaAuthor(String author) throws ReportException { checkIsNotOpen(); _doc.addAuthor(author); } public void setMetaTitle(String title) throws ReportException { checkIsNotOpen(); _doc.addTitle(title); } public void setMetaCreator(String creator) throws ReportException { checkIsNotOpen(); _doc.addCreator(creator); } public void setMetaCreationDate() throws ReportException { checkIsNotOpen(); _doc.addCreationDate(); } public void setMetaDescription(String description) throws ReportException { checkIsNotOpen(); _doc.addSubject(description); } public void setMetaKeywords(Vector<String> keys) throws ReportException { checkIsNotOpen(); Iterator<String> it=keys.iterator(); String comma=""; StringBuffer b=new StringBuffer(); while(it.hasNext()) { b.append(comma);b.append(it.next());comma=","; } _doc.addKeywords(b.toString()); } public Chunk createChunk(String txt) { return new PdfChunk(this, txt); } public Chunk createChunk(String txt, boolean bold, boolean italic,boolean underline) throws ReportException { Chunk c = new PdfChunk(this, txt); c.setBold(bold); c.setItalic(italic); c.setUnderline(underline); return c; } public Tab createTab(Paragraph itpar) throws ReportException { checkWriter(); return itpar.getNextTab(); } public Chunk createChunk(Image img) throws ReportException { return createChunk(img,100.0f); } public Chunk createChunk(File imageFile) throws ReportException { return createChunk(imageFile,100.0f); } public Chunk createChunk(Image img,float scalePercentage) throws ReportException { checkWriter(); return new PdfChunk(this, img,scalePercentage,-1); } public Chunk createChunk(File imageFile,float scalePercentage) throws ReportException { checkWriter(); return new PdfChunk(this, imageFile,scalePercentage,-1); } public Chunk createChunkForWidth(Image img,float scaleToWidthInPt) throws ReportException { checkWriter(); return new PdfChunk(this,img,-1.0f,scaleToWidthInPt); } public Chunk createChunkForWidth(File imageFile,float scaleToWidthInPt) throws ReportException { checkWriter(); return new PdfChunk(this,imageFile,-1.0f,scaleToWidthInPt); } public Paragraph createParagraph(XMLNoteParStyle style) throws ReportException { checkWriter(); return new PdfParagraph(this, style); } public Paragraph createParagraph() throws ReportException { return createParagraph(null); } public Table createTable(Report.Align align, float percentageOfWidth, float[] relativeWidths) throws ReportException { checkWriter(); return new PdfTable(this, align, percentageOfWidth, relativeWidths); } public Cell createCell(ReportElement p) throws ReportException { checkWriter(); return new PdfCell(this, p); } public Cell textCell(XMLNoteParStyle st, String txt) throws ReportException { return textCell(st,txt,Report.Align.LEFT); } public Cell textCell(XMLNoteParStyle st, String txt, Align a) throws ReportException { checkWriter(); return createCell(createParagraph(st).setAlignment(a).add(createChunk(txt))); } public void beginReport(File output) throws ReportException { try { _canceled = false; _doc = new Document(); _writer = PdfWriter.getInstance(_doc, new FileOutputStream(output)); setPageSize(PageSize.A4); setOrientation(Orientation.PORTRAIT); setMargins(new Rectangle(72.0f, 72.0f, 72.0f, 72.0f)); _writer.setPageEvent(new PdfPageEvent() { public void onChapter(PdfWriter arg0, Document arg1, float arg2, com.lowagie.text.Paragraph arg3) { } public void onChapterEnd(PdfWriter arg0, Document arg1, float arg2) { } public void onCloseDocument(PdfWriter arg0, Document arg1) { PdfReport.super.getReportListeners().informEndReport(PdfReport.this); } public void onGenericTag(PdfWriter arg0, Document arg1, com.lowagie.text.Rectangle arg2, String arg3) { } public void onOpenDocument(PdfWriter arg0, Document arg1) { } public void onParagraph(PdfWriter arg0, Document arg1, float arg2) { } public void onParagraphEnd(PdfWriter arg0, Document arg1, float arg2) { } public void onSection(PdfWriter arg0, Document arg1, float arg2, int arg3, com.lowagie.text.Paragraph arg4) { } public void onSectionEnd(PdfWriter arg0, Document arg1, float arg2) { } public void onStartPage(PdfWriter wrt, Document doc) { } public void onEndPage(PdfWriter wrt, Document doc) { try { ReportListeners _listeners=PdfReport.super.getReportListeners(); _listeners.informNextPage(PdfReport.this); Vector<ReportElement> vhdr = _listeners.getHeader(PdfReport.this); Vector<ReportElement> vftr = _listeners.getFooter(PdfReport.this); PdfContentByte cb=wrt.getDirectContent(); // write headers on top of each other if (vhdr!=null) { Iterator<ReportElement> it=vhdr.iterator(); while (it.hasNext()) { ReportElement hdr=it.next(); Rectangle pageSize=PdfReport.this.getPageRect(); Rectangle margins=PdfReport.this.getMargins(); float ytop=pageSize.top()-margins.top(); float hdrHeight=PdfReport.this.getHeight(hdr, null); float rpos=(margins.top()-hdrHeight)/2; float hytop=ytop+rpos+hdrHeight; if (hdr instanceof PdfTable) { int firstRow=0,lastRow=-1; PdfTable phdr=(PdfTable) hdr; phdr.writeSelectedRows(firstRow,lastRow,margins.left(),hytop,cb); } else if (hdr instanceof PdfParagraph) { PdfParagraph ppar=(PdfParagraph) hdr; ColumnText ct = new ColumnText(cb); float textWidth=PdfReport.this.getTextWidth(); ct.addElement(ppar); ct.setSimpleColumn(margins.left(), ytop+rpos, margins.left()+textWidth, hytop); ct.go(); } } } // write footers on top of each other if (vftr!=null) { Iterator<ReportElement> it=vftr.iterator(); while (it.hasNext()) { ReportElement ftr=it.next(); Rectangle margins=PdfReport.this.getMargins(); float ytop=margins.bottom(); float hdrHeight=PdfReport.this.getHeight(ftr, null); float rpos=(margins.bottom()-hdrHeight)/2; float hytop=ytop-rpos; if (ftr instanceof PdfTable) { int firstRow=0,lastRow=-1; PdfTable pftr=(PdfTable) ftr; pftr.writeSelectedRows(firstRow,lastRow,margins.left(),hytop,cb); } else if (ftr instanceof PdfParagraph) { PdfParagraph ppar=(PdfParagraph) ftr; ColumnText ct = new ColumnText(cb); float textWidth=PdfReport.this.getTextWidth(); ct.addElement(ppar); ct.setSimpleColumn(margins.left(), rpos, margins.left()+textWidth, hytop); ct.go(); } } } } catch (ReportException e) { DefaultXMLNoteErrorHandler.exception(e); } catch (DocumentException e) { DefaultXMLNoteErrorHandler.exception(e); } } }); } catch (Exception e) { throw new ReportException(e); } } public void endReport() throws ReportException { try { if (keeps!=null && !keeps.isEmpty()) { addToDocument(keeps); } _doc.close(); _imageInstances.clear(); } catch (Exception e) { throw new ReportException(e); } } private void setPageAndOrientation() throws ReportException { _pdfPageSize = (_pageSize.equals(PageSize.A4)) ? com.lowagie.text.PageSize.A4 : com.lowagie.text.PageSize.LETTER; if (_orientation.equals(Orientation.PORTRAIT)) { _doc.setPageSize(_pdfPageSize); } else { _pdfPageSize=_pdfPageSize.rotate(); _doc.setPageSize(_pdfPageSize); } } public void setPageSize(PageSize pg) throws ReportException { checkWriter(); _pageSize=pg; setPageAndOrientation(); } public void setOrientation(Orientation o) throws ReportException { checkWriter(); _orientation=o; setPageAndOrientation(); } public void setMargins(Rectangle m) throws ReportException { _doc.setMargins(m.left(), m.right(), m.top(), m.bottom()); } public Rectangle getPageRect() throws ReportException { checkWriter(); return new Rectangle(_pdfPageSize.getLeft(), _pdfPageSize.getTop(), _pdfPageSize.getRight(), _pdfPageSize.getBottom()); } public Rectangle getMargins() throws ReportException { checkWriter(); Rectangle pr=getPageRect(); return new Rectangle(_doc.left(), pr.top()-_doc.top(), pr.right()-_doc.right(),_doc.bottom()); } public float getTextWidth() throws ReportException { Rectangle m = getMargins(); return getPageRect().width() - m.left() - m.right(); } public int getCurrentPageNumber() throws ReportException { checkWriter(); return _writer.getPageNumber(); } private Vector<ReportElement> keeps=null; public void add(ReportElement el,boolean keepWithNext) throws ReportException { checkWriter(); checkDocOpen(); if (keeps==null) { keeps=new Vector<ReportElement>(); } if (el instanceof PdfPageBreak) { addToDocument(keeps); _doc.newPage(); } else if (el instanceof PdfParagraph) { PdfParagraph par=((PdfParagraph) el); XMLNoteParStyle st=par.getStyle(); //System.out.println("keepwithnext="+st.keepWithNext()+";"+par.getContent().substring(0,Math.min(15, par.getContent().length())).trim()); if (keepWithNext || st.keepWithNext()) { keeps.add(par); } else { keeps.add(par); addToDocument(keeps); } } else if (el instanceof PdfTable) { PdfTable tbl=((PdfTable) el); if (keepWithNext) { keeps.add(tbl); } else { keeps.add(tbl); addToDocument(keeps); } } else { throw new ReportException( "You are trying to mix Report implementations"); } } public void add(ReportElement el) throws ReportException { add(el,false); } public void addPageBreak() throws ReportException { add(new PdfPageBreak()); } private void addToDocument(Vector<ReportElement> keeps) throws ReportException { float totalHeight=0.0f; Iterator<ReportElement> it=keeps.iterator(); ReportElement last=keeps.lastElement(); while (it.hasNext()) { totalHeight+=getHeight(it.next(),last); } if (!willFit(totalHeight)) { _doc.newPage(); } it=keeps.iterator(); while (it.hasNext()) { ReportElement el=it.next(); try { if (el instanceof PdfParagraph) { _doc.add((PdfParagraph) el); } else if (el instanceof PdfTable) { _doc.add((PdfTable) el); } else { throw new ReportException("Unknown ReportElement:"+el.getClass().getName()); } } catch (Exception e) { throw new ReportException(e); } } keeps.clear(); } private float getHeight(ReportElement el,ReportElement last) throws ReportException { try { ColumnText ct=new ColumnText(_writer.getDirectContent()); if (el instanceof PdfParagraph) { PdfParagraph par=((PdfParagraph) el); if ((el!=last) || (el==last && par.hasImage())) { int status = ColumnText.START_COLUMN; Rectangle m=getMargins();//System.out.println(m); Rectangle p=getPageRect();//System.out.println(p); float leading=par.getLeading(); int align=par.getAlignment(); ct.setSimpleColumn(m.left(), m.bottom(),p.width()-m.right(),p.height()-m.top(),leading,align); //,Element.ALIGN_JUSTIFIED); ct.addElement(par); float pos = ct.getYLine(); status = ct.go(true); float npos=ct.getYLine(); return pos-npos; } else { float lineheight=par.getFont().getSize()*par.getMultipliedLeading(); float threelines=3*lineheight; //System.out.println("par:"+par+";lineheight:"+lineheight+";threelines:"+threelines); return threelines; } } else if (el instanceof PdfTable) { PdfTable tbl=((PdfTable) el); int status=ColumnText.START_COLUMN; Rectangle m=getMargins();//System.out.println(m); Rectangle p=getPageRect();//System.out.println(p); float leading=0.0f; //tbl.getLeading(); int align=tbl.getHorizontalAlignment(); ct.setSimpleColumn(m.left(), m.bottom(),p.width()-m.right(),p.height()-m.top(),leading,align); //,Element.ALIGN_JUSTIFIED); ct.addElement(tbl); float pos = ct.getYLine(); status = ct.go(true); float npos=ct.getYLine(); return pos-npos; } else { throw new ReportException("Unknown ReportElement:"+el.getClass().getName()); } } catch (Exception e) { throw new ReportException(e); } } public boolean willFit(float height) throws ReportException { float ypos=_writer.getVerticalPosition(false); float bottomMargin=this.getMargins().bottom(); if ((ypos-bottomMargin)<height) { return false; } else { return true; } } public void addXMLNote(XMLNoteDocument doc, MarkMarkupProviderMaker maker,XMLNoteToReport.MarkTextProvider prov) throws ReportException { checkWriter(); XMLNoteToReport cvt = new XMLNoteToReport(this); cvt.addXMLNote(doc, maker, prov); } public void cancel() throws ReportException { checkWriter(); _canceled = true; } public boolean canceled() throws ReportException { checkWriter(); return _canceled; } // ///////////////////////////////////////////////////////////////////////////////// // Constructors // ///////////////////////////////////////////////////////////////////////////////// public PdfReport(File fontmapcache) { fontmapcacheFile=fontmapcache; _fontmapcachein=null; _fontmapcacheout=null; _imageInstances=new Hashtable<String,com.lowagie.text.Image>(); } public PdfReport(InputStream fontmapCacheIn,OutputStream fontmapCacheOut) { fontmapcacheFile=null; _fontmapcachein=fontmapCacheIn; _fontmapcacheout=fontmapCacheOut; _imageInstances=new Hashtable<String,com.lowagie.text.Image>(); } }