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 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.
32 package mircoders.pdf;
34 import mir.log.LoggerWrapper;
36 import java.io.ByteArrayOutputStream;
37 import java.io.IOException;
38 import java.net.MalformedURLException;
42 import gnu.regexp.REException;
44 import com.lowagie.text.*;
45 import com.lowagie.text.pdf.*;
47 import mir.entity.EntityList;
49 import mircoders.entity.EntityContent;
50 import mircoders.entity.EntityImages;
51 import mircoders.storage.DatabaseContentToMedia;
53 import mir.config.MirPropertiesConfiguration;
54 import mir.config.MirPropertiesConfiguration.PropertiesConfigExc;
58 public class PDFGenerator{
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;
71 public float bottomEdge;
72 public float rightEdge;
73 public float leftEdge;
75 public int maxImageHeight;
76 public int maxImageWidth;
77 protected LoggerWrapper logger;
79 public int indexFontSize;
80 public int indexLineHeight;
81 public int indexFontFamily;
83 public float footerHeight;
84 public String footerText;
85 public int footerFontSize;
86 public int footerFontFamily;
88 public int metaHeight;
89 public int metaFontSize;
90 public int metaFontFamily;
92 public int descriptionLineHeight;
93 public int descriptionFontSize;
94 public int descriptionFontFamily;
96 public int contentLineHeight;
97 public int contentFontSize;
98 public int contentFontFamily;
100 public int sourceLineHeight;
101 public int sourceFontSize;
102 public int sourceFontFamily;
104 public int bigImageCaptionFontSize;
105 public int bigImageCaptionFontFamily;
107 protected MirPropertiesConfiguration configuration;
110 public PDFGenerator(ByteArrayOutputStream out){
111 logger = new LoggerWrapper("PDFGenerator");
113 configuration = MirPropertiesConfiguration.instance();
115 catch (PropertiesConfigExc e) {
116 throw new RuntimeException("Can't get configuration: " + e.getMessage());
118 localImageDir=configuration.getString("Producer.Image.Path");
121 indexFontSize = Integer.parseInt(configuration.getString("PDF.Index.FontSize"));
122 indexLineHeight = Integer.parseInt(configuration.getString("PDF.Index.LineHeight"));
123 indexFontFamily = getFontByName(configuration.getString("PDF.Index.FontFamily"));
125 footerText = configuration.getString("PDF.Footer.String");
126 footerFontSize = Integer.parseInt(configuration.getString("PDF.Footer.FontSize"));
127 footerFontFamily = getFontByName(configuration.getString("PDF.Footer.FontFamily"));
128 footerHeight = Integer.parseInt(configuration.getString("PDF.Footer.Height"));;
130 metaFontSize = Integer.parseInt(configuration.getString("PDF.Meta.FontSize"));
131 metaFontFamily = getFontByName(configuration.getString("PDF.Meta.FontFamily"));
132 metaHeight = Integer.parseInt(configuration.getString("PDF.Meta.Height"));;
134 descriptionFontSize = Integer.parseInt(configuration.getString("PDF.Description.FontSize"));
135 descriptionLineHeight = Integer.parseInt(configuration.getString("PDF.Description.LineHeight"));
136 descriptionFontFamily = getFontByName(configuration.getString("PDF.Description.FontFamily"));
138 contentFontSize = Integer.parseInt(configuration.getString("PDF.Content.FontSize"));
139 contentLineHeight = Integer.parseInt(configuration.getString("PDF.Content.LineHeight"));
140 contentFontFamily = getFontByName(configuration.getString("PDF.Content.FontFamily"));
142 sourceFontSize = Integer.parseInt(configuration.getString("PDF.Source.FontSize"));
143 sourceLineHeight = Integer.parseInt(configuration.getString("PDF.Source.LineHeight"));
144 sourceFontFamily = getFontByName(configuration.getString("PDF.Source.FontFamily"));
146 bigImageCaptionFontSize = Integer.parseInt(configuration.getString("PDF.BigImageCaption.FontSize"));
147 bigImageCaptionFontFamily = getFontByName(configuration.getString("PDF.BigImageCaption.FontFamily"));
150 catch (NumberFormatException e){
154 // step 1: make a document
156 String pageSize = configuration.getString("PDF.Pagesize");
158 if (pageSize.equals("LETTER")){
159 document = new Document(PageSize.LETTER);
164 document = new Document(PageSize.A4);
169 maxImageHeight = 250;
174 horizontalMargin = 20;
178 topEdge=pageHeight-verticalMargin;
179 bottomEdge=verticalMargin;
180 rightEdge=pageWidth-horizontalMargin;
181 leftEdge=horizontalMargin;
183 currentYPosition=topEdge;
186 String headerText = configuration.getString("PDF.Title.String");
189 writer = PdfWriter.getInstance(document, out);
190 cb = writer.getDirectContent();
193 addHeader(headerText);
195 catch(DocumentException de) {
196 logger.error(de.getMessage());
205 public void addHeader(String headerText){
206 int titleFontSize=Integer.parseInt(configuration.getString("PDF.Title.FontSize"));
207 int titleLineHeight=Integer.parseInt(configuration.getString("PDF.Title.LineHeight"));
208 String titleFontFamily=configuration.getString("PDF.Title.FontFamily");
212 ColumnText ct = new ColumnText(cb);
214 ct.addText(new Phrase(headerText, new Font(getFontByName(titleFontFamily), titleFontSize,Font.BOLD)));
215 float[] rightCol = {rightEdge,topEdge,rightEdge,topEdge-titleLineHeight};
216 float[] leftCol = {leftEdge,topEdge,leftEdge,topEdge-titleLineHeight};
217 ct.setColumns(leftCol,rightCol);
218 ct.setYLine(topEdge);
219 ct.setAlignment(Element.ALIGN_CENTER);
222 currentYPosition = currentYPosition - titleLineHeight;
225 catch(DocumentException de) {
226 logger.error(de.getMessage());
230 public void addIndexItem(EntityContent entityContent){
233 ColumnText ict = new ColumnText(cb);
234 String theTitle = entityContent.getValue("title");
235 String theCreator = entityContent.getValue("creator");
236 Phrase titleP=new Phrase(" - " + theTitle,new Font(indexFontFamily,indexFontSize,Font.BOLD));
237 Phrase creatorP=new Phrase( " :: " + theCreator,new Font(indexFontFamily,indexFontSize));
238 float toYPosition = currentYPosition - indexLineHeight;
239 float[] leftIndexCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
240 float[] rightIndexCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
242 ict.addText(creatorP);
243 ict.setColumns(leftIndexCols,rightIndexCols);
244 ict.setYLine(currentYPosition);
245 ict.setAlignment(Element.ALIGN_LEFT);
247 currentYPosition = toYPosition;
249 catch(DocumentException de) {
250 logger.error(de.getMessage());
257 private int addTextLine(ColumnText ct,int lineHeight,int alignment,float left, float right){
258 logger.debug("adding a line of text");
259 if (! enoughY(lineHeight)){
262 float toYPosition = currentYPosition - lineHeight;
263 float[] leftContentCols = {left,currentYPosition,left,toYPosition};
264 float[] rightContentCols = {right,currentYPosition,right,toYPosition};
265 ct.setColumns(leftContentCols,rightContentCols);
266 ct.setYLine(currentYPosition);
267 ct.setAlignment(alignment);
270 currentYPosition = toYPosition;
273 catch(DocumentException de) {
274 logger.error(de.getMessage());
279 public void addLine(){
281 cb.moveTo(rightEdge, currentYPosition-5);
282 cb.lineTo(leftEdge, currentYPosition-5);
284 currentYPosition = currentYPosition - 10;
288 public void addFooter(){
290 ColumnText fct = new ColumnText(cb);
292 cb.moveTo(rightEdge, bottomEdge+footerHeight-5);
293 cb.lineTo(leftEdge, bottomEdge+footerHeight-5);
295 float[] leftFooterCols = {leftEdge,bottomEdge+footerHeight-1,leftEdge,bottomEdge};
296 float[] rightFooterCols = {rightEdge,bottomEdge+footerHeight-1,rightEdge,bottomEdge};
298 Paragraph footerP=new Paragraph(footerText,new Font(footerFontFamily,footerFontSize));
299 fct.addText(footerP);
301 fct.setColumns(leftFooterCols,rightFooterCols);
302 fct.setYLine(bottomEdge+footerHeight-1);
303 fct.setAlignment(Element.ALIGN_JUSTIFIED);
306 Paragraph numberP=new Paragraph((new Integer(currentPage)).toString(),new Font(footerFontFamily,footerFontSize,Font.BOLD));
307 fct.addText(numberP);
308 fct.setAlignment(Element.ALIGN_RIGHT);
312 catch (DocumentException de){
313 logger.error(de.getMessage());
319 public void newPage(){
325 currentYPosition=topEdge;
327 catch(DocumentException de) {
328 logger.error(de.getMessage());
332 public void addArticleSeparator(){
338 cb.moveTo(rightEdge, currentYPosition-5);
339 cb.lineTo(leftEdge, currentYPosition-5);
341 currentYPosition = currentYPosition - 10;
344 public void addArticleMetaInfo(ColumnText ct,String theTitle,String theCreator,String theDate){
345 //see if we have room for the author and title
347 if (! enoughY(metaHeight)){
351 Paragraph titleP=new Paragraph(theTitle+"\n",new Font(metaFontFamily,metaFontSize,Font.BOLD));
352 Paragraph whowhenP=new Paragraph(theCreator + " " + theDate ,new Font(metaFontFamily,metaFontSize));
354 ct.addText(whowhenP);
356 ct.setYLine(currentYPosition);
357 ct.setAlignment(Element.ALIGN_LEFT);
359 float toYPosition = currentYPosition - metaHeight;
360 float[] leftHeadCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
361 float[] rightHeadCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
363 ct.setColumns(leftHeadCols,rightHeadCols);
366 currentYPosition = toYPosition;
368 catch(DocumentException de) {
369 logger.error(de.getMessage());
374 public void addArticleDescription(ColumnText ct,String theDescription){
375 // Now we add the description, one line at a time, the ct should be empty at this point
378 Paragraph descP=new Paragraph(theDescription,new Font(descriptionFontFamily,descriptionFontSize,Font.BOLD));
381 // every article has a description, so we can assume that:
382 int status = ColumnText.NO_MORE_COLUMN;
385 while ((status & ColumnText.NO_MORE_TEXT) == 0 && brake >0){
386 //there is still text left in the description.
387 status = addTextLine(ct,descriptionLineHeight,Element.ALIGN_JUSTIFIED,leftEdge,rightEdge);
391 logger.error("runaway description...try increasing the line height or decreasing the font size");
394 public void addArticleContent(ColumnText ct,String theContent,EntityList images){
395 //let's go ahead and add in all the body text
396 Paragraph contentP=new Paragraph(theContent,new Font(contentFontFamily,contentFontSize));
397 ct.addText(contentP);
399 int contentLinesBeforeImages=3;
400 //and assume we have at least one line of text in the content
401 int status = ColumnText.NO_MORE_COLUMN;
403 // let's add a little bit of text, like a couple of lines
405 while (((status & ColumnText.NO_MORE_TEXT) == 0) && x<contentLinesBeforeImages){
406 status=addTextLine(ct,contentLineHeight,Element.ALIGN_JUSTIFIED,leftEdge,rightEdge);
410 boolean addImageOnLeft = true; //we will alternate within articles
411 while (images.hasNext()){
413 EntityImages currentImage=(EntityImages) images.next();
414 float img_width=(new Float(currentImage.getValue("img_width"))).floatValue();
415 float img_height=(new Float(currentImage.getValue("img_height"))).floatValue();
416 if (img_height>maxImageHeight){
417 img_width=(new Float((new Float(img_width*(maxImageHeight/img_height))).intValue())).floatValue();
418 img_height=maxImageHeight;
420 if (img_width>maxImageWidth){
421 img_height=(new Float((new Float(img_height*(maxImageWidth/img_width))).intValue())).floatValue();
422 img_width=maxImageWidth;
425 String img_title=currentImage.getValue("title");
426 String img_path=currentImage.getValue("publish_path");
428 if ((status & ColumnText.NO_MORE_TEXT) == 0){
429 // there is still text, so add an image which will have text wrapped around it, then add the text which
430 // will be on the left or the right of the image
432 float templateMinimumHeight = img_height+20;
433 float templateWidth = img_width+10;
434 float templateHeight = templateMinimumHeight+contentLineHeight-(templateMinimumHeight % contentLineHeight);
436 PdfTemplate template = cb.createTemplate(templateWidth,templateHeight);
439 //here we need a page check
440 if (! enoughY((new Float(templateHeight)).intValue())){
441 //ok, well just fill text to the bottom then
442 float toYPosition = bottomEdge+footerHeight;
443 float[] leftBottomCols = {leftEdge,currentYPosition,leftEdge,toYPosition};
444 float[] rightBottomCols = {rightEdge,currentYPosition,rightEdge,toYPosition};
445 ct.setColumns(leftBottomCols,rightBottomCols);
446 ct.setYLine(currentYPosition);
447 ct.setAlignment(Element.ALIGN_JUSTIFIED);
451 catch(DocumentException de) {
452 logger.error(de.getMessage());
457 float toYPosition=currentYPosition - templateHeight;
460 Image theImage = Image.getInstance(localImageDir+img_path);
461 theImage.scaleAbsolute(img_width,img_height);
462 theImage.setAbsolutePosition(5,13);
464 template.addImage(theImage);
465 template.beginText();
466 BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
467 template.setFontAndSize(bf, 8);
468 template.setTextMatrix(5, 3);
469 template.showText(img_title);
472 catch(BadElementException de) {
473 logger.error(de.getMessage());
475 catch(DocumentException de) {
476 logger.error(de.getMessage());
478 catch (MalformedURLException de){
479 logger.error(de.getMessage());
481 catch (IOException de){
482 logger.error(de.getMessage());
486 float leftImageTextEdge=leftEdge;
487 float rightImageTextEdge=rightEdge;
491 cb.addTemplate(template,leftEdge,toYPosition);
492 leftImageTextEdge=leftEdge+templateWidth+5;
493 addImageOnLeft = false;
496 cb.addTemplate(template,rightEdge-templateWidth,toYPosition);
497 rightImageTextEdge = rightEdge-templateWidth-5;
498 addImageOnLeft = true;
501 logger.debug("adding template at " + leftEdge + "," + toYPosition);
503 //and fill some text while we are at it
505 float[] leftBottomCols = {leftImageTextEdge,currentYPosition,leftImageTextEdge,toYPosition};
506 float[] rightBottomCols = {rightImageTextEdge,currentYPosition,rightImageTextEdge,toYPosition};
507 ct.setColumns(leftBottomCols,rightBottomCols);
508 ct.setYLine(currentYPosition);
509 ct.setAlignment(Element.ALIGN_JUSTIFIED);
512 currentYPosition=toYPosition;
514 catch(DocumentException de) {
515 logger.error(de.getMessage());
521 //add an image on the left with a big caption to the right
522 currentYPosition = currentYPosition - 10;
523 float templateMinimumHeight = img_height;
524 float templateWidth = img_width;
525 float templateHeight = templateMinimumHeight+contentLineHeight-(templateMinimumHeight % contentLineHeight);
526 PdfTemplate template = cb.createTemplate(templateWidth,templateHeight);
527 if (! enoughY((new Float(templateHeight)).intValue())){
530 float toYPosition=currentYPosition - templateHeight;
532 Image theImage = Image.getInstance(localImageDir+img_path);
533 theImage.setAbsolutePosition(0,13);
534 theImage.scaleAbsolute(img_width,img_height);
535 template.addImage(theImage);
537 catch(BadElementException de) {
538 logger.error(de.getMessage());
540 catch(DocumentException de) {
541 logger.error(de.getMessage());
543 catch(MalformedURLException de) {
544 logger.error(de.getMessage());
546 catch(IOException de) {
547 logger.error(de.getMessage());
550 cb.addTemplate(template,leftEdge,toYPosition);
553 ColumnText cct = new ColumnText(cb);
554 float[] leftCaptionCols = {leftEdge+templateWidth+5,currentYPosition-5,leftEdge+templateWidth+5,toYPosition};
555 float[] rightCaptionCols = {rightEdge,currentYPosition-5,rightEdge,toYPosition};
557 Paragraph captionP=new Paragraph(img_title,new Font(bigImageCaptionFontFamily,bigImageCaptionFontSize,Font.BOLD));
558 cct.addText(captionP);
559 cct.setColumns(leftCaptionCols,rightCaptionCols);
560 cct.setYLine(currentYPosition-5);
561 cct.setAlignment(Element.ALIGN_LEFT);
564 currentYPosition=toYPosition;
566 catch(DocumentException de) {
567 logger.error(de.getMessage());
572 //add the rest of the text which might be left
574 while ((status & ColumnText.NO_MORE_TEXT) == 0 && brake > 0){
575 status=addTextLine(ct,contentLineHeight,Element.ALIGN_JUSTIFIED,leftEdge,rightEdge);
579 logger.error("runaway content....try decreasing font size or increasing line height");
582 private void addArticleSource(ColumnText ct,String theSource){
583 Paragraph sourceP = new Paragraph(theSource,new Font(sourceFontFamily,sourceFontSize,Font.BOLD));
585 addTextLine(ct,sourceLineHeight,Element.ALIGN_RIGHT,leftEdge,rightEdge);
589 private boolean enoughY(int heightOfBlockToAdd){
590 if ((currentYPosition - heightOfBlockToAdd - footerHeight) < bottomEdge )
597 public void add(EntityContent entityContent){
598 logger.error("adding a content Entity");
608 * --image without text
612 EntityList images=DatabaseContentToMedia.getInstance().getImages(entityContent);
613 String theTitle = entityContent.getValue("title");
614 String theCreator = entityContent.getValue("creator");
615 String theDate = entityContent.getValue("webdb_create_formatted");
616 String theDescriptionRaw = entityContent.getValue("description");
617 String theContentRaw = entityContent.getValue("content_data");
618 String theSource = configuration.getString("Producer.ProductionHost") + "/" + configuration.getString("StandardLanguage") + entityContent.getValue("publish_path") + entityContent.getValue("id") + ".shtml";
622 String theContent = "";
623 String theDescription = "";
626 RE re1 = new RE("\r?\n\r?\n");
627 String theContent1 = re1.substituteAll(theContentRaw,"BREAKHERE");
628 String theDescription1 = re1.substituteAll(theDescriptionRaw,"BREAKHERE");
630 RE re2 = new RE("\r?\n");
631 String theContent2 = re2.substituteAll(theContent1," ");
632 String theDescription2 = re2.substituteAll(theDescription1," ");
634 RE re3 = new RE("BREAKHERE");
635 theContent = " " + re3.substituteAll(theContent2,"\n ");
636 theDescription = re3.substituteAll(theDescription2,"\n ");
639 catch(REException ree){
640 logger.error(ree.getMessage());
644 addArticleSeparator();
646 ColumnText ct = new ColumnText(cb);
648 addArticleMetaInfo(ct,theTitle,theCreator,theDate);
649 addArticleDescription(ct,theDescription);
650 addArticleContent(ct,theContent,images);
651 addArticleSource(ct,theSource);
655 public int getFontByName(String fontName) {
657 if (fontName.equalsIgnoreCase("helvetica")){
658 theFont = Font.HELVETICA;
661 if (fontName.equalsIgnoreCase("courier")) {
662 theFont = Font.COURIER;
665 if (fontName.equalsIgnoreCase("times")) {
666 theFont = Font.TIMES_ROMAN;
669 logger.error("using helvetica because I can't get font for name: "+fontName);
670 theFont = Font.HELVETICA;