the class that generates PDFs
[mir.git] / source / mircoders / pdf / PDFGenerator.java
1 /*
2  * Copyright (C) 2001, 2002  The Mir-coders group
3  *
4  * This file is part of Mir.
5  *
6  * Mir is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * Mir is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Mir; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * In addition, as a special exception, The Mir-coders gives permission to link
21  * the code of this program with the com.oreilly.servlet library, any library
22  * licensed under the Apache Software License, The Sun (tm) Java Advanced
23  * Imaging library (JAI), The Sun JIMI library (or with modified versions of
24  * the above that use the same license as the above), and distribute linked
25  * combinations including the two.  You must obey the GNU General Public
26  * License in all respects for all of the code used other than the above
27  * mentioned libraries.  If you modify this file, you may extend this exception
28  * to your version of the file, but you are not obligated to do so.  If you do
29  * not wish to do so, delete this exception statement from your version.
30  */
31
32 package mircoders.pdf;
33
34 import mir.log.LoggerWrapper;
35
36 import java.io.ByteArrayOutputStream;
37 import java.io.IOException;
38 import java.net.MalformedURLException;
39
40
41 import gnu.regexp.RE;
42 import gnu.regexp.REException;
43
44 import com.lowagie.text.*;
45 import com.lowagie.text.pdf.*;
46
47 import mir.entity.EntityList;
48
49 import mircoders.entity.EntityContent;
50 import mircoders.entity.EntityImages;
51 import mircoders.storage.DatabaseContentToMedia;
52
53 import mir.config.MirPropertiesConfiguration;
54 import mir.config.MirPropertiesConfiguration.PropertiesConfigExc;
55
56
57
58 public class PDFGenerator{
59
60   public Document document;
61   public PdfWriter writer;
62   public PdfContentByte cb;
63   public String localImageDir;
64   public float currentYPosition;
65   public int currentPage;
66   public float pageWidth;
67   public float pageHeight;
68   public float verticalMargin;
69   public float horizontalMargin;
70   public float topEdge;
71   public float bottomEdge;
72   public float rightEdge;
73   public float leftEdge;
74   public float footerHeight;
75   public String footerText;
76
77   protected LoggerWrapper logger;
78
79
80   protected MirPropertiesConfiguration configuration;
81
82
83   public PDFGenerator(ByteArrayOutputStream out){
84     logger = new LoggerWrapper("PDF.PDFGenerator");
85     try {
86       configuration = MirPropertiesConfiguration.instance();
87     }
88     catch (PropertiesConfigExc e) {
89       throw new RuntimeException("Can't get configuration: " + e.getMessage());
90     }
91     localImageDir=configuration.getString("Producer.Image.Path");
92     
93     // step 1: make a document
94     
95     String pageSize = configuration.getString("PDF.Pagesize");
96     
97     if (pageSize.equals("LETTER")){
98       document = new Document(PageSize.LETTER);
99       pageWidth = 612;
100       pageHeight = 792;
101     }
102     else {
103        document = new Document(PageSize.A4);
104        pageWidth=595;
105        pageHeight=842;
106     }
107
108     try {
109       
110       verticalMargin = 20;
111       horizontalMargin = 20;
112       
113       footerHeight = 54;
114
115       topEdge=pageHeight-verticalMargin;
116       bottomEdge=verticalMargin;
117       rightEdge=pageWidth-horizontalMargin;
118       leftEdge=horizontalMargin;
119       
120       currentYPosition=topEdge;
121       currentPage = 1;
122       
123       String headerText = configuration.getString("PDF.Title");
124       footerText = configuration.getString("PDF.Footer");
125
126       writer = PdfWriter.getInstance(document, out);
127       cb = writer.getDirectContent();
128
129       document.open();      
130
131       
132
133       ColumnText ct = new ColumnText(cb);   
134       //add a basic header
135       ct.addText(new Phrase(headerText, FontFactory.getFont(FontFactory.COURIER, 24,Font.BOLD)));
136       
137       float[] rightCol = {rightEdge,topEdge,rightEdge,topEdge-28};
138       float[] leftCol = {leftEdge,topEdge,leftEdge,topEdge-28};
139       //      int status = ct.go();
140       ct.setColumns(leftCol,rightCol);
141       //      int status=ct.go();
142       ct.setYLine(topEdge);
143       ct.setAlignment(Element.ALIGN_CENTER);
144       ct.go();
145       
146       currentYPosition = currentYPosition - 28;
147       
148     }
149     catch(DocumentException de) {
150       logger.error(de.getMessage());
151     }
152   }
153   
154   public void stop(){
155     addFooter();
156     document.close();
157   }
158
159   public void addIndexItem(EntityContent entityContent){
160     try {
161     float indexLineHeight = 16;
162     ColumnText ict = new ColumnText(cb);
163     String theTitle = entityContent.getValue("title");
164     String theCreator = entityContent.getValue("creator");
165     Phrase titleP=new Phrase(" - " +  theTitle,new Font(Font.HELVETICA,12,Font.BOLD));
166     Phrase creatorP=new Phrase( " :: " + theCreator,new Font(Font.HELVETICA,12));
167     float toYPosition = currentYPosition - indexLineHeight;
168     float[] leftIndexCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
169     float[] rightIndexCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
170     ict.addText(titleP);
171     ict.addText(creatorP);
172     ict.setColumns(leftIndexCols,rightIndexCols);
173     ict.setYLine(currentYPosition);
174     ict.setAlignment(Element.ALIGN_LEFT);
175     int status=ict.go();
176     currentYPosition = toYPosition;
177     }
178     catch(DocumentException de) {
179       logger.error(de.getMessage());
180     }
181     
182
183
184   }
185
186
187   public void addLine(){
188     cb.setLineWidth(1f);
189     cb.moveTo(rightEdge, currentYPosition-5);
190     cb.lineTo(leftEdge, currentYPosition-5);
191     cb.stroke();
192     currentYPosition = currentYPosition - 10;
193     
194   }
195   
196   public void addFooter(){
197     try{
198     ColumnText fct = new ColumnText(cb);
199     cb.setLineWidth(1f);
200     cb.moveTo(rightEdge, bottomEdge+footerHeight-5);
201     cb.lineTo(leftEdge, bottomEdge+footerHeight-5);
202     cb.stroke();
203     float[] leftFooterCols = {leftEdge,bottomEdge+footerHeight-1,leftEdge,bottomEdge};
204     float[] rightFooterCols = {rightEdge,bottomEdge+footerHeight-1,rightEdge,bottomEdge};
205     
206     Paragraph footerP=new Paragraph(footerText,new Font(Font.HELVETICA,12));
207     fct.addText(footerP);
208     
209     fct.setColumns(leftFooterCols,rightFooterCols);
210     fct.setYLine(bottomEdge+footerHeight-1);
211     fct.setAlignment(Element.ALIGN_JUSTIFIED);
212     int status=fct.go();
213
214     Paragraph numberP=new Paragraph((new Integer(currentPage)).toString(),new Font(Font.HELVETICA,12,Font.BOLD));
215     fct.addText(numberP);
216     fct.setAlignment(Element.ALIGN_RIGHT);
217     status=fct.go();
218
219     }
220     catch (DocumentException de){
221       logger.error(de.getMessage());
222     }
223
224
225   }
226
227   public void newPage(){
228     try{
229       //add a footer
230       addFooter();
231       document.newPage();   
232       currentPage++;
233       currentYPosition=topEdge;
234     }
235     catch(DocumentException de) {
236       logger.error(de.getMessage());
237     }
238   }
239   
240   public boolean enoughY(int heightOfBlockToAdd){
241     if ((currentYPosition - heightOfBlockToAdd - footerHeight) < bottomEdge )
242       return false;
243     else 
244       return true;
245   }
246   
247   
248   public void add(EntityContent entityContent){
249     logger.error("adding a content Entity");
250     EntityList images=DatabaseContentToMedia.getInstance().getImages(entityContent);
251     String theTitle = entityContent.getValue("title");
252     String theCreator = entityContent.getValue("creator");
253     String theDate = entityContent.getValue("webdb_create_formatted");
254     String theDescriptionRaw = entityContent.getValue("description");
255     String theContentRaw = entityContent.getValue("content_data");
256     String theSource =  configuration.getString("Producer.ProductionHost") + "/" + configuration.getString("StandardLanguage") + entityContent.getValue("publish_path") + entityContent.getValue("id") + ".shtml";
257
258     boolean addImageOnLeft = true;
259     
260     String theContent = "";
261     String theDescription = "";
262     
263     try { 
264       RE re1 = new RE("\r?\n\r?\n");
265       String theContent1 = re1.substituteAll(theContentRaw,"BREAKHERE");
266       String theDescription1 = re1.substituteAll(theDescriptionRaw,"BREAKHERE");
267       
268       RE re2 = new RE("\r?\n");
269       String theContent2 = re2.substituteAll(theContent1," ");
270       String theDescription2 = re2.substituteAll(theDescription1," ");
271       
272       RE re3 = new RE("BREAKHERE");
273       theContent = "    " + re3.substituteAll(theContent2,"\n    ");
274       theDescription = re3.substituteAll(theDescription2,"\n    ");
275
276     }
277     catch(REException ree){
278       logger.error(ree.getMessage());
279     }
280
281
282     try { 
283       
284       // make a line (don't worry about the bottomEdge, we should actually write in that area(it's a thin line!)
285       cb.setLineWidth(1f);
286       cb.moveTo(rightEdge, currentYPosition-5);
287       cb.lineTo(leftEdge, currentYPosition-5);
288       cb.stroke();
289       
290       currentYPosition = currentYPosition - 10;
291       
292
293       //see if we have room for the author and title
294       if (! enoughY(36)){
295         newPage();
296       }
297
298       ColumnText ct = new ColumnText(cb);
299
300       Paragraph titleP=new Paragraph(theTitle+"\n",new Font(Font.HELVETICA,14,Font.BOLD));
301       Paragraph whowhenP=new Paragraph(theCreator + "  " + theDate ,new Font(Font.HELVETICA,14));
302       ct.addText(titleP);
303       ct.addText(whowhenP);
304       
305       ct.setYLine(currentYPosition);
306       ct.setAlignment(Element.ALIGN_LEFT);
307       
308       float toYPosition = currentYPosition - 36;
309       float[] leftHeadCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
310       float[] rightHeadCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
311
312       ct.setColumns(leftHeadCols,rightHeadCols);
313       ct.go();
314       currentYPosition = toYPosition;
315     
316       
317       // Now add the description, one line at a time, the ct should be empty at this point
318             
319       Paragraph descP=new Paragraph(theDescription,new Font(Font.HELVETICA,12,Font.BOLD));
320
321       ct.addText(descP);
322       
323       // every article has a description, so we can assume that:
324       int status = ColumnText.NO_MORE_COLUMN;
325       
326       int descriptionLineHeight=16;
327       
328       while ((status & ColumnText.NO_MORE_TEXT) == 0){
329         //there is still text left in the description.
330         logger.error("adding a line to the description");
331         
332         //but is there room on the page?
333         if (! enoughY(descriptionLineHeight)){
334           newPage();
335         }
336         
337         //now we are set to add a line of descriptive text
338         toYPosition = currentYPosition - descriptionLineHeight;
339         float[] leftDescCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
340         float[] rightDescCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
341         ct.setColumns(leftDescCols,rightDescCols);
342         ct.setYLine(currentYPosition);
343         ct.setAlignment(Element.ALIGN_JUSTIFIED);
344         status=ct.go();
345         currentYPosition = toYPosition;
346       }
347       
348       // ok, now on to the meat of the job ahead
349       
350       int contentLineHeight=16;
351
352       //let's go ahead and add in all the body text
353       Paragraph contentP=new Paragraph(theContent,new Font(Font.HELVETICA,10));
354       ct.addText(contentP);
355       //and assume we have at least one line of text in the content
356       status = ColumnText.NO_MORE_COLUMN;
357
358       int maxImageHeight=250;
359       int maxImageWidth=250;
360
361       
362       // let's add a little bit of text, like a couple of lines
363       int x = 0;
364       while (((status & ColumnText.NO_MORE_TEXT) == 0) && x<3){
365         //in case we have some text left
366         
367         logger.error("adding a line to the content");
368
369         //but is there room on the page?
370         if (! enoughY(contentLineHeight)){
371           newPage();
372         }
373         
374         //now we are set to add a line of content text
375         toYPosition = currentYPosition - contentLineHeight;
376         float[] leftContentCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
377         float[] rightContentCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
378         ct.setColumns(leftContentCols,rightContentCols);
379         ct.setYLine(currentYPosition);
380         ct.setAlignment(Element.ALIGN_JUSTIFIED);
381         status=ct.go();
382         currentYPosition = toYPosition;
383         x++;
384       }
385
386
387
388       while (images.hasNext()){
389         
390         EntityImages currentImage=(EntityImages) images.next();
391         float img_width=(new Float(currentImage.getValue("img_width"))).floatValue();
392         float img_height=(new Float(currentImage.getValue("img_height"))).floatValue();
393         if (img_height>maxImageHeight){
394           img_width=(new Float((new Float(img_width*(maxImageHeight/img_height))).intValue())).floatValue();
395           img_height=maxImageHeight;
396         }
397         if (img_width>maxImageWidth){
398           img_height=(new Float((new Float(img_height*(maxImageWidth/img_width))).intValue())).floatValue();
399           img_width=maxImageWidth;
400         }
401
402         String img_title=currentImage.getValue("title");
403         String img_path=currentImage.getValue("publish_path");
404         
405           
406         
407
408         if ((status & ColumnText.NO_MORE_TEXT) == 0){ 
409           // there is still text, so add an image which will have text wrapped around it, then add the text which 
410           // will be on the left or the right of the image
411
412           float templateMinimumHeight = img_height+20;
413           float templateWidth = img_width+10;
414           
415           float templateHeight = templateMinimumHeight+contentLineHeight-(templateMinimumHeight % contentLineHeight);
416           
417           
418           PdfTemplate template = cb.createTemplate(templateWidth,templateHeight); 
419         
420           
421           //here we need a page check
422           if (! enoughY((new Float(templateHeight)).intValue())){
423             //ok, well just fill text to the bottom then
424             toYPosition = bottomEdge+footerHeight;
425             float[] leftBottomCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
426             float[] rightBottomCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
427             ct.setColumns(leftBottomCols,rightBottomCols);
428             ct.setYLine(currentYPosition);
429             ct.setAlignment(Element.ALIGN_JUSTIFIED);
430             status=ct.go();
431             newPage();
432           }
433
434           toYPosition=currentYPosition - templateHeight;
435           
436           Image theImage = Image.getInstance(localImageDir+img_path);
437           theImage.scaleAbsolute(img_width,img_height);
438           theImage.setAbsolutePosition(5,13); 
439           template.addImage(theImage);
440           template.beginText();
441           BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
442           template.setFontAndSize(bf, 8);
443           template.setTextMatrix(5, 3);
444           template.showText(img_title);
445           template.endText();
446           
447           
448           float leftImageTextEdge=leftEdge;
449           float rightImageTextEdge=rightEdge;
450           
451
452           if (addImageOnLeft){
453             cb.addTemplate(template,leftEdge,toYPosition);
454             leftImageTextEdge=leftEdge+templateWidth+5;
455             addImageOnLeft = false;
456           }         
457           else {
458             cb.addTemplate(template,rightEdge-templateWidth,toYPosition);
459             rightImageTextEdge = rightEdge-templateWidth-5;
460             addImageOnLeft = true;
461           }
462
463           logger.error("adding template at " + leftEdge + "," + toYPosition);
464           
465           //and fill some text while we are at it
466           
467           float[] leftBottomCols = {leftImageTextEdge,currentYPosition,leftImageTextEdge,toYPosition};
468           float[] rightBottomCols = {rightImageTextEdge,currentYPosition,rightImageTextEdge,toYPosition};
469           ct.setColumns(leftBottomCols,rightBottomCols);
470           ct.setYLine(currentYPosition);
471           ct.setAlignment(Element.ALIGN_JUSTIFIED);
472           status=ct.go();
473           
474           currentYPosition=toYPosition;
475           
476           
477           }
478         else { 
479           //add an image on the left with a big caption to the right
480           currentYPosition = currentYPosition - 10;
481           float templateMinimumHeight = img_height;
482           float templateWidth = img_width;
483           float templateHeight = templateMinimumHeight+contentLineHeight-(templateMinimumHeight % contentLineHeight);
484           PdfTemplate template = cb.createTemplate(templateWidth,templateHeight); 
485           if (! enoughY((new Float(templateHeight)).intValue())){
486             newPage();
487           }
488           toYPosition=currentYPosition - templateHeight;
489           Image theImage = Image.getInstance(localImageDir+img_path);
490           theImage.setAbsolutePosition(0,13); 
491           theImage.scaleAbsolute(img_width,img_height);
492           template.addImage(theImage);
493           
494           cb.addTemplate(template,leftEdge,toYPosition);
495           
496           // add a big caption
497           ColumnText cct = new ColumnText(cb);   
498           float[] leftCaptionCols = {leftEdge+templateWidth+5,currentYPosition-5,leftEdge+templateWidth+5,toYPosition};
499           float[] rightCaptionCols = {rightEdge,currentYPosition-5,rightEdge,toYPosition};
500           
501           Paragraph captionP=new Paragraph(img_title,new Font(Font.HELVETICA,16,Font.BOLD));
502           cct.addText(captionP);
503           cct.setColumns(leftCaptionCols,rightCaptionCols);
504           cct.setYLine(currentYPosition-5);
505           cct.setAlignment(Element.ALIGN_LEFT);
506           cct.go();
507
508           
509
510           currentYPosition=toYPosition;
511           
512
513         }
514         
515       }
516
517       
518       while ((status & ColumnText.NO_MORE_TEXT) == 0){
519         //in case we have some text left
520         
521         logger.error("adding a line to the content");
522
523         //but is there room on the page?
524         if (! enoughY(contentLineHeight)){
525           newPage();
526         }
527         
528         //now we are set to add a line of content text
529         toYPosition = currentYPosition - contentLineHeight;
530         float[] leftContentCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
531         float[] rightContentCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
532         ct.setColumns(leftContentCols,rightContentCols);
533         ct.setYLine(currentYPosition);
534         ct.setAlignment(Element.ALIGN_JUSTIFIED);
535         status=ct.go();
536         currentYPosition = toYPosition;
537
538       }
539       
540       // and add the source link 
541       
542       toYPosition = currentYPosition - contentLineHeight;
543       float[] leftSourceCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
544       float[] rightSourceCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
545       ct.setColumns(leftSourceCols,rightSourceCols);
546       Paragraph sourceP = new Paragraph(theSource,new Font(Font.COURIER,12,Font.BOLD));
547       ct.addText(sourceP);
548       ct.setYLine(currentYPosition);
549       ct.setAlignment(Element.ALIGN_RIGHT);
550       status=ct.go();
551       currentYPosition = toYPosition;
552
553
554       
555       
556       
557     }
558     catch(DocumentException de) {
559       logger.error(de.getMessage());
560     }
561     catch(MalformedURLException de) {
562       logger.error(de.getMessage());
563     }
564     catch(IOException de) {
565       logger.error(de.getMessage());
566     }
567     /*    while(images.hasNext()){
568       
569      EntityImages currentImage=(EntityImages) images.next();
570      float img_width=(new Float(currentImage.getValue("img_width"))).floatValue();
571      float img_height=(new Float(currentImage.getValue("img_height"))).floatValue();
572       if (img_width>250){
573         img_height=(new Float((new Float(img_height*(250.0F/img_width))).intValue())).floatValue();
574         img_width=250.0F;
575       }
576       String img_title=currentImage.getValue("title");
577       String img_path=currentImage.getValue("publish_path");
578
579       PdfTemplate template = cb.createTemplate(img_width+10, img_height+20); 
580       try{
581         Image theImage = Image.getInstance(localImageDir+img_path);
582         theImage.setAbsolutePosition(5,13); 
583         template.addImage(theImage);
584         template.beginText();
585         BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
586         template.setFontAndSize(bf, 8);
587         template.setTextMatrix(5, 3);
588         template.showText(img_title);
589         template.endText();
590         ImgTemplate it = new ImgTemplate(template);
591         it.setAlignment(Image.RIGHT | Image.TEXTWRAP);
592         document.add(it);
593         
594         document.add(new Paragraph("this is a test of adding some text to see if we bump down a line when there is some text"));
595         
596       }
597       catch(DocumentException de) {
598         System.err.println(de.getMessage());
599       }
600       catch(IOException ioe) {
601         System.err.println(ioe.getMessage());
602       }
603       } */
604
605     /*
606     try { 
607
608       
609       Paragraph p=new Paragraph(theContent,new Font(Font.HELVETICA,11));
610       p.setAlignment(Paragraph.ALIGN_JUSTIFIED);
611       
612       document.add(p);
613     }
614     catch(DocumentException de) {
615       System.err.println(de.getMessage());
616     }
617
618     */
619
620   }
621
622 }
623
624