2 * Copyright (C) 2001, 2002 The Mir-coders group
4 * This file is part of Mir.
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.
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.
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
20 * In addition, as a special exception, The Mir-coders gives permission to link
21 * the code of this program with any library licensed under the Apache Software License,
22 * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
23 * (or with modified versions of the above that use the same license as the above),
24 * and distribute linked combinations including the two. You must obey the
25 * GNU General Public License in all respects for all of the code used other than
26 * the above mentioned libraries. If you modify this file, you may extend this
27 * exception to your version of the file, but you are not obligated to do so.
28 * If you do not wish to do so, delete this exception statement from your version.
30 package mircoders.pdf;
33 import gnu.regexp.REException;
35 import java.io.ByteArrayOutputStream;
36 import java.io.IOException;
37 import java.net.MalformedURLException;
39 import mir.config.MirPropertiesConfiguration;
40 import mir.config.MirPropertiesConfiguration.PropertiesConfigExc;
41 import mir.entity.EntityList;
42 import mir.log.LoggerWrapper;
43 import mircoders.entity.EntityContent;
44 import mircoders.entity.EntityImages;
45 import mircoders.storage.DatabaseContentToMedia;
47 import com.lowagie.text.BadElementException;
48 import com.lowagie.text.Document;
49 import com.lowagie.text.DocumentException;
50 import com.lowagie.text.Element;
51 import com.lowagie.text.Font;
52 import com.lowagie.text.Image;
53 import com.lowagie.text.PageSize;
54 import com.lowagie.text.Paragraph;
55 import com.lowagie.text.Phrase;
56 import com.lowagie.text.pdf.BaseFont;
57 import com.lowagie.text.pdf.ColumnText;
58 import com.lowagie.text.pdf.PdfContentByte;
59 import com.lowagie.text.pdf.PdfTemplate;
60 import com.lowagie.text.pdf.PdfWriter;
64 public class PDFGenerator{
66 public Document document;
67 public PdfWriter writer;
68 public PdfContentByte cb;
69 public String localImageDir;
70 public float currentYPosition;
71 public int currentPage;
72 public float pageWidth;
73 public float pageHeight;
74 public float verticalMargin;
75 public float horizontalMargin;
77 public float bottomEdge;
78 public float rightEdge;
79 public float leftEdge;
81 public int maxImageHeight;
82 public int maxImageWidth;
83 protected LoggerWrapper logger;
85 public int indexFontSize;
86 public int indexLineHeight;
87 public int indexFontFamily;
89 public float footerHeight;
90 public String footerText;
91 public int footerFontSize;
92 public int footerFontFamily;
94 public int metaHeight;
95 public int metaFontSize;
96 public int metaFontFamily;
98 public int descriptionLineHeight;
99 public int descriptionFontSize;
100 public int descriptionFontFamily;
102 public int contentLineHeight;
103 public int contentFontSize;
104 public int contentFontFamily;
106 public int sourceLineHeight;
107 public int sourceFontSize;
108 public int sourceFontFamily;
110 public int bigImageCaptionFontSize;
111 public int bigImageCaptionFontFamily;
113 protected MirPropertiesConfiguration configuration;
116 public PDFGenerator(ByteArrayOutputStream out){
117 logger = new LoggerWrapper("PDFGenerator");
119 configuration = MirPropertiesConfiguration.instance();
121 catch (PropertiesConfigExc e) {
122 throw new RuntimeException("Can't get configuration: " + e.getMessage());
124 localImageDir=configuration.getString("Producer.Image.Path");
127 indexFontSize = Integer.parseInt(configuration.getString("PDF.Index.FontSize"));
128 indexLineHeight = Integer.parseInt(configuration.getString("PDF.Index.LineHeight"));
129 indexFontFamily = getFontByName(configuration.getString("PDF.Index.FontFamily"));
131 footerText = configuration.getString("PDF.Footer.String");
132 footerFontSize = Integer.parseInt(configuration.getString("PDF.Footer.FontSize"));
133 footerFontFamily = getFontByName(configuration.getString("PDF.Footer.FontFamily"));
134 footerHeight = Integer.parseInt(configuration.getString("PDF.Footer.Height"));;
136 metaFontSize = Integer.parseInt(configuration.getString("PDF.Meta.FontSize"));
137 metaFontFamily = getFontByName(configuration.getString("PDF.Meta.FontFamily"));
138 metaHeight = Integer.parseInt(configuration.getString("PDF.Meta.Height"));;
140 descriptionFontSize = Integer.parseInt(configuration.getString("PDF.Description.FontSize"));
141 descriptionLineHeight = Integer.parseInt(configuration.getString("PDF.Description.LineHeight"));
142 descriptionFontFamily = getFontByName(configuration.getString("PDF.Description.FontFamily"));
144 contentFontSize = Integer.parseInt(configuration.getString("PDF.Content.FontSize"));
145 contentLineHeight = Integer.parseInt(configuration.getString("PDF.Content.LineHeight"));
146 contentFontFamily = getFontByName(configuration.getString("PDF.Content.FontFamily"));
148 sourceFontSize = Integer.parseInt(configuration.getString("PDF.Source.FontSize"));
149 sourceLineHeight = Integer.parseInt(configuration.getString("PDF.Source.LineHeight"));
150 sourceFontFamily = getFontByName(configuration.getString("PDF.Source.FontFamily"));
152 bigImageCaptionFontSize = Integer.parseInt(configuration.getString("PDF.BigImageCaption.FontSize"));
153 bigImageCaptionFontFamily = getFontByName(configuration.getString("PDF.BigImageCaption.FontFamily"));
156 catch (NumberFormatException e){
160 // step 1: make a document
162 String pageSize = configuration.getString("PDF.Pagesize");
164 if (pageSize.equals("LETTER")){
165 document = new Document(PageSize.LETTER);
170 document = new Document(PageSize.A4);
175 maxImageHeight = 250;
180 horizontalMargin = 20;
184 topEdge=pageHeight-verticalMargin;
185 bottomEdge=verticalMargin;
186 rightEdge=pageWidth-horizontalMargin;
187 leftEdge=horizontalMargin;
189 currentYPosition=topEdge;
192 String headerText = configuration.getString("PDF.Title.String");
195 writer = PdfWriter.getInstance(document, out);
196 cb = writer.getDirectContent();
199 addHeader(headerText);
201 catch(DocumentException de) {
202 logger.error(de.getMessage());
211 public void addHeader(String headerText){
212 int titleFontSize=Integer.parseInt(configuration.getString("PDF.Title.FontSize"));
213 int titleLineHeight=Integer.parseInt(configuration.getString("PDF.Title.LineHeight"));
214 String titleFontFamily=configuration.getString("PDF.Title.FontFamily");
218 ColumnText ct = new ColumnText(cb);
220 ct.addText(new Phrase(headerText, new Font(getFontByName(titleFontFamily), titleFontSize,Font.BOLD)));
221 float[] rightCol = {rightEdge,topEdge,rightEdge,topEdge-titleLineHeight};
222 float[] leftCol = {leftEdge,topEdge,leftEdge,topEdge-titleLineHeight};
223 ct.setColumns(leftCol,rightCol);
224 ct.setYLine(topEdge);
225 ct.setAlignment(Element.ALIGN_CENTER);
228 currentYPosition = currentYPosition - titleLineHeight;
231 catch(DocumentException de) {
232 logger.error(de.getMessage());
236 public void addIndexItem(EntityContent entityContent){
239 ColumnText ict = new ColumnText(cb);
240 String theTitle = entityContent.getValue("title");
241 String theCreator = entityContent.getValue("creator");
242 Phrase titleP=new Phrase(" - " + theTitle,new Font(indexFontFamily,indexFontSize,Font.BOLD));
243 Phrase creatorP=new Phrase( " :: " + theCreator,new Font(indexFontFamily,indexFontSize));
244 float toYPosition = currentYPosition - indexLineHeight;
245 float[] leftIndexCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
246 float[] rightIndexCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
248 ict.addText(creatorP);
249 ict.setColumns(leftIndexCols,rightIndexCols);
250 ict.setYLine(currentYPosition);
251 ict.setAlignment(Element.ALIGN_LEFT);
253 currentYPosition = toYPosition;
255 catch(DocumentException de) {
256 logger.error(de.getMessage());
263 private int addTextLine(ColumnText ct,int lineHeight,int alignment,float left, float right){
264 logger.debug("adding a line of text");
265 if (! enoughY(lineHeight)){
268 float toYPosition = currentYPosition - lineHeight;
269 float[] leftContentCols = {left,currentYPosition,left,toYPosition};
270 float[] rightContentCols = {right,currentYPosition,right,toYPosition};
271 ct.setColumns(leftContentCols,rightContentCols);
272 ct.setYLine(currentYPosition);
273 ct.setAlignment(alignment);
276 currentYPosition = toYPosition;
279 catch(DocumentException de) {
280 logger.error(de.getMessage());
285 public void addLine(){
287 cb.moveTo(rightEdge, currentYPosition-5);
288 cb.lineTo(leftEdge, currentYPosition-5);
290 currentYPosition = currentYPosition - 10;
294 public void addFooter(){
296 ColumnText fct = new ColumnText(cb);
298 cb.moveTo(rightEdge, bottomEdge+footerHeight-5);
299 cb.lineTo(leftEdge, bottomEdge+footerHeight-5);
301 float[] leftFooterCols = {leftEdge,bottomEdge+footerHeight-1,leftEdge,bottomEdge};
302 float[] rightFooterCols = {rightEdge,bottomEdge+footerHeight-1,rightEdge,bottomEdge};
304 Paragraph footerP=new Paragraph(footerText,new Font(footerFontFamily,footerFontSize));
305 fct.addText(footerP);
307 fct.setColumns(leftFooterCols,rightFooterCols);
308 fct.setYLine(bottomEdge+footerHeight-1);
309 fct.setAlignment(Element.ALIGN_JUSTIFIED);
312 Paragraph numberP=new Paragraph((new Integer(currentPage)).toString(),new Font(footerFontFamily,footerFontSize,Font.BOLD));
313 fct.addText(numberP);
314 fct.setAlignment(Element.ALIGN_RIGHT);
318 catch (DocumentException de){
319 logger.error(de.getMessage());
325 public void newPage(){
331 currentYPosition=topEdge;
333 catch(DocumentException de) {
334 logger.error(de.getMessage());
338 public void addArticleSeparator(){
344 cb.moveTo(rightEdge, currentYPosition-5);
345 cb.lineTo(leftEdge, currentYPosition-5);
347 currentYPosition = currentYPosition - 10;
350 public void addArticleMetaInfo(ColumnText ct,String theTitle,String theCreator,String theDate){
351 //see if we have room for the author and title
353 if (! enoughY(metaHeight)){
357 Paragraph titleP=new Paragraph(theTitle+"\n",new Font(metaFontFamily,metaFontSize,Font.BOLD));
358 Paragraph whowhenP=new Paragraph(theCreator + " " + theDate ,new Font(metaFontFamily,metaFontSize));
360 ct.addText(whowhenP);
362 ct.setYLine(currentYPosition);
363 ct.setAlignment(Element.ALIGN_LEFT);
365 float toYPosition = currentYPosition - metaHeight;
366 float[] leftHeadCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
367 float[] rightHeadCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
369 ct.setColumns(leftHeadCols,rightHeadCols);
372 currentYPosition = toYPosition;
374 catch(DocumentException de) {
375 logger.error(de.getMessage());
380 public void addArticleDescription(ColumnText ct,String theDescription){
381 // Now we add the description, one line at a time, the ct should be empty at this point
384 Paragraph descP=new Paragraph(theDescription,new Font(descriptionFontFamily,descriptionFontSize,Font.BOLD));
387 // every article has a description, so we can assume that:
388 int status = ColumnText.NO_MORE_COLUMN;
391 while ((status & ColumnText.NO_MORE_TEXT) == 0 && brake >0){
392 //there is still text left in the description.
393 status = addTextLine(ct,descriptionLineHeight,Element.ALIGN_JUSTIFIED,leftEdge,rightEdge);
397 logger.error("runaway description...try increasing the line height or decreasing the font size");
400 public void addArticleContent(ColumnText ct,String theContent,EntityList images){
401 //let's go ahead and add in all the body text
402 Paragraph contentP=new Paragraph(theContent,new Font(contentFontFamily,contentFontSize));
403 ct.addText(contentP);
405 int contentLinesBeforeImages=3;
406 //and assume we have at least one line of text in the content
407 int status = ColumnText.NO_MORE_COLUMN;
409 // let's add a little bit of text, like a couple of lines
411 while (((status & ColumnText.NO_MORE_TEXT) == 0) && x<contentLinesBeforeImages){
412 status=addTextLine(ct,contentLineHeight,Element.ALIGN_JUSTIFIED,leftEdge,rightEdge);
416 boolean addImageOnLeft = true; //we will alternate within articles
417 while (images.hasNext()){
419 EntityImages currentImage=(EntityImages) images.next();
420 float img_width=(new Float(currentImage.getValue("img_width"))).floatValue();
421 float img_height=(new Float(currentImage.getValue("img_height"))).floatValue();
422 if (img_height>maxImageHeight){
423 img_width=(new Float((new Float(img_width*(maxImageHeight/img_height))).intValue())).floatValue();
424 img_height=maxImageHeight;
426 if (img_width>maxImageWidth){
427 img_height=(new Float((new Float(img_height*(maxImageWidth/img_width))).intValue())).floatValue();
428 img_width=maxImageWidth;
431 String img_title=currentImage.getValue("title");
432 String img_path=currentImage.getValue("publish_path");
434 if ((status & ColumnText.NO_MORE_TEXT) == 0){
435 // there is still text, so add an image which will have text wrapped around it, then add the text which
436 // will be on the left or the right of the image
438 float templateMinimumHeight = img_height+20;
439 float templateWidth = img_width+10;
440 float templateHeight = templateMinimumHeight+contentLineHeight-(templateMinimumHeight % contentLineHeight);
442 PdfTemplate template = cb.createTemplate(templateWidth,templateHeight);
445 //here we need a page check
446 if (! enoughY((new Float(templateHeight)).intValue())){
447 //ok, well just fill text to the bottom then
448 float toYPosition = bottomEdge+footerHeight;
449 float[] leftBottomCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
450 float[] rightBottomCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
451 ct.setColumns(leftBottomCols,rightBottomCols);
452 ct.setYLine(currentYPosition);
453 ct.setAlignment(Element.ALIGN_JUSTIFIED);
457 catch(DocumentException de) {
458 logger.error(de.getMessage());
463 float toYPosition=currentYPosition - templateHeight;
466 Image theImage = Image.getInstance(localImageDir+img_path);
467 theImage.scaleAbsolute(img_width,img_height);
468 theImage.setAbsolutePosition(5,13);
470 template.addImage(theImage);
471 template.beginText();
472 BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
473 template.setFontAndSize(bf, 8);
474 template.setTextMatrix(5, 3);
475 template.showText(img_title);
478 catch(BadElementException de) {
479 logger.error(de.getMessage());
481 catch(DocumentException de) {
482 logger.error(de.getMessage());
484 catch (MalformedURLException de){
485 logger.error(de.getMessage());
487 catch (IOException de){
488 logger.error(de.getMessage());
492 float leftImageTextEdge=leftEdge;
493 float rightImageTextEdge=rightEdge;
497 cb.addTemplate(template,leftEdge,toYPosition);
498 leftImageTextEdge=leftEdge+templateWidth+5;
499 addImageOnLeft = false;
502 cb.addTemplate(template,rightEdge-templateWidth,toYPosition);
503 rightImageTextEdge = rightEdge-templateWidth-5;
504 addImageOnLeft = true;
507 logger.debug("adding template at " + leftEdge + "," + toYPosition);
509 //and fill some text while we are at it
511 float[] leftBottomCols = {leftImageTextEdge,currentYPosition,leftImageTextEdge,toYPosition};
512 float[] rightBottomCols = {rightImageTextEdge,currentYPosition,rightImageTextEdge,toYPosition};
513 ct.setColumns(leftBottomCols,rightBottomCols);
514 ct.setYLine(currentYPosition);
515 ct.setAlignment(Element.ALIGN_JUSTIFIED);
518 currentYPosition=toYPosition;
520 catch(DocumentException de) {
521 logger.error(de.getMessage());
527 //add an image on the left with a big caption to the right
528 currentYPosition = currentYPosition - 10;
529 float templateMinimumHeight = img_height;
530 float templateWidth = img_width;
531 float templateHeight = templateMinimumHeight+contentLineHeight-(templateMinimumHeight % contentLineHeight);
532 PdfTemplate template = cb.createTemplate(templateWidth,templateHeight);
533 if (! enoughY((new Float(templateHeight)).intValue())){
536 float toYPosition=currentYPosition - templateHeight;
538 Image theImage = Image.getInstance(localImageDir+img_path);
539 theImage.setAbsolutePosition(0,13);
540 theImage.scaleAbsolute(img_width,img_height);
541 template.addImage(theImage);
543 catch(BadElementException de) {
544 logger.error(de.getMessage());
546 catch(DocumentException de) {
547 logger.error(de.getMessage());
549 catch(MalformedURLException de) {
550 logger.error(de.getMessage());
552 catch(IOException de) {
553 logger.error(de.getMessage());
556 cb.addTemplate(template,leftEdge,toYPosition);
559 ColumnText cct = new ColumnText(cb);
560 float[] leftCaptionCols = {leftEdge+templateWidth+5,currentYPosition-5,leftEdge+templateWidth+5,toYPosition};
561 float[] rightCaptionCols = {rightEdge,currentYPosition-5,rightEdge,toYPosition};
563 Paragraph captionP=new Paragraph(img_title,new Font(bigImageCaptionFontFamily,bigImageCaptionFontSize,Font.BOLD));
564 cct.addText(captionP);
565 cct.setColumns(leftCaptionCols,rightCaptionCols);
566 cct.setYLine(currentYPosition-5);
567 cct.setAlignment(Element.ALIGN_LEFT);
570 currentYPosition=toYPosition;
572 catch(DocumentException de) {
573 logger.error(de.getMessage());
578 //add the rest of the text which might be left
580 while ((status & ColumnText.NO_MORE_TEXT) == 0 && brake > 0){
581 status=addTextLine(ct,contentLineHeight,Element.ALIGN_JUSTIFIED,leftEdge,rightEdge);
585 logger.error("runaway content....try decreasing font size or increasing line height");
588 private void addArticleSource(ColumnText ct,String theSource){
589 Paragraph sourceP = new Paragraph(theSource,new Font(sourceFontFamily,sourceFontSize,Font.BOLD));
591 addTextLine(ct,sourceLineHeight,Element.ALIGN_RIGHT,leftEdge,rightEdge);
595 private boolean enoughY(int heightOfBlockToAdd){
596 if ((currentYPosition - heightOfBlockToAdd - footerHeight) < bottomEdge )
603 public void add(EntityContent entityContent){
604 logger.error("adding a content Entity");
614 * --image without text
618 EntityList images=DatabaseContentToMedia.getInstance().getImages(entityContent);
619 String theTitle = entityContent.getValue("title");
620 String theCreator = entityContent.getValue("creator");
621 String theDate = entityContent.getValue("webdb_create_formatted");
622 String theDescriptionRaw = entityContent.getValue("description");
623 String theContentRaw = entityContent.getValue("content_data");
624 String theSource = configuration.getString("Producer.ProductionHost") + "/" + configuration.getString("StandardLanguage") + entityContent.getValue("publish_path") + entityContent.getValue("id") + ".shtml";
628 String theContent = "";
629 String theDescription = "";
632 RE re1 = new RE("\r?\n\r?\n");
633 String theContent1 = re1.substituteAll(theContentRaw,"BREAKHERE");
634 String theDescription1 = re1.substituteAll(theDescriptionRaw,"BREAKHERE");
636 RE re2 = new RE("\r?\n");
637 String theContent2 = re2.substituteAll(theContent1," ");
638 String theDescription2 = re2.substituteAll(theDescription1," ");
640 RE re3 = new RE("BREAKHERE");
641 theContent = " " + re3.substituteAll(theContent2,"\n ");
642 theDescription = re3.substituteAll(theDescription2,"\n ");
645 catch(REException ree){
646 logger.error(ree.getMessage());
650 addArticleSeparator();
652 ColumnText ct = new ColumnText(cb);
654 addArticleMetaInfo(ct,theTitle,theCreator,theDate);
655 addArticleDescription(ct,theDescription);
656 addArticleContent(ct,theContent,images);
657 addArticleSource(ct,theSource);
661 public int getFontByName(String fontName) {
663 if (fontName.equalsIgnoreCase("helvetica")){
664 theFont = Font.HELVETICA;
667 if (fontName.equalsIgnoreCase("courier")) {
668 theFont = Font.COURIER;
671 if (fontName.equalsIgnoreCase("times")) {
672 theFont = Font.TIMES_ROMAN;
675 logger.error("using helvetica because I can't get font for name: "+fontName);
676 theFont = Font.HELVETICA;