KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > jasperreports > engine > export > JRPdfExporter


1 /*
2  * ============================================================================
3  * GNU Lesser General Public License
4  * ============================================================================
5  *
6  * JasperReports - Free Java report-generating library.
7  * Copyright (C) 2001-2006 JasperSoft Corporation http://www.jaspersoft.com
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
22  *
23  * JasperSoft Corporation
24  * 303 Second Street, Suite 450 North
25  * San Francisco, CA 94107
26  * http://www.jaspersoft.com
27  */

28
29 /*
30  * Contributors:
31  * Adrian Jackson - iapetus@users.sourceforge.net
32  * David Taylor - exodussystems@users.sourceforge.net
33  * Lars Kristensen - llk@users.sourceforge.net
34  * Ling Li - lonecatz@users.sourceforge.net
35  * Martin Clough - mtclough@users.sourceforge.net
36  */

37 package net.sf.jasperreports.engine.export;
38
39 import java.awt.Color JavaDoc;
40 import java.awt.Graphics2D JavaDoc;
41 import java.awt.font.TextAttribute JavaDoc;
42 import java.awt.geom.AffineTransform JavaDoc;
43 import java.awt.geom.Dimension2D JavaDoc;
44 import java.awt.geom.Rectangle2D JavaDoc;
45 import java.awt.image.BufferedImage JavaDoc;
46 import java.io.File JavaDoc;
47 import java.io.FileOutputStream JavaDoc;
48 import java.io.IOException JavaDoc;
49 import java.io.OutputStream JavaDoc;
50 import java.text.AttributedCharacterIterator JavaDoc;
51 import java.util.Collection JavaDoc;
52 import java.util.HashMap JavaDoc;
53 import java.util.Iterator JavaDoc;
54 import java.util.LinkedList JavaDoc;
55 import java.util.List JavaDoc;
56 import java.util.Map JavaDoc;
57
58 import net.sf.jasperreports.engine.JRAbstractExporter;
59 import net.sf.jasperreports.engine.JRAlignment;
60 import net.sf.jasperreports.engine.JRAnchor;
61 import net.sf.jasperreports.engine.JRBox;
62 import net.sf.jasperreports.engine.JRElement;
63 import net.sf.jasperreports.engine.JRException;
64 import net.sf.jasperreports.engine.JRExporterParameter;
65 import net.sf.jasperreports.engine.JRFont;
66 import net.sf.jasperreports.engine.JRGraphicElement;
67 import net.sf.jasperreports.engine.JRHyperlink;
68 import net.sf.jasperreports.engine.JRImage;
69 import net.sf.jasperreports.engine.JRImageRenderer;
70 import net.sf.jasperreports.engine.JRLine;
71 import net.sf.jasperreports.engine.JRPrintAnchor;
72 import net.sf.jasperreports.engine.JRPrintElement;
73 import net.sf.jasperreports.engine.JRPrintEllipse;
74 import net.sf.jasperreports.engine.JRPrintFrame;
75 import net.sf.jasperreports.engine.JRPrintGraphicElement;
76 import net.sf.jasperreports.engine.JRPrintHyperlink;
77 import net.sf.jasperreports.engine.JRPrintImage;
78 import net.sf.jasperreports.engine.JRPrintLine;
79 import net.sf.jasperreports.engine.JRPrintPage;
80 import net.sf.jasperreports.engine.JRPrintRectangle;
81 import net.sf.jasperreports.engine.JRPrintText;
82 import net.sf.jasperreports.engine.JRRenderable;
83 import net.sf.jasperreports.engine.JRRuntimeException;
84 import net.sf.jasperreports.engine.JRTextElement;
85 import net.sf.jasperreports.engine.JasperPrint;
86 import net.sf.jasperreports.engine.base.JRBaseFont;
87 import net.sf.jasperreports.engine.util.BreakIteratorSplitCharacter;
88 import net.sf.jasperreports.engine.util.JRLoader;
89 import net.sf.jasperreports.engine.util.JRProperties;
90 import net.sf.jasperreports.engine.util.JRStyledText;
91
92 import com.lowagie.text.Chunk;
93 import com.lowagie.text.Document;
94 import com.lowagie.text.DocumentException;
95 import com.lowagie.text.Element;
96 import com.lowagie.text.Font;
97 import com.lowagie.text.FontFactory;
98 import com.lowagie.text.Image;
99 import com.lowagie.text.Phrase;
100 import com.lowagie.text.Rectangle;
101 import com.lowagie.text.SplitCharacter;
102 import com.lowagie.text.pdf.BaseFont;
103 import com.lowagie.text.pdf.ColumnText;
104 import com.lowagie.text.pdf.FontMapper;
105 import com.lowagie.text.pdf.PdfContentByte;
106 import com.lowagie.text.pdf.PdfDestination;
107 import com.lowagie.text.pdf.PdfOutline;
108 import com.lowagie.text.pdf.PdfTemplate;
109 import com.lowagie.text.pdf.PdfWriter;
110
111
112 /**
113  * Exports a JasperReports document to PDF format. It has binary output type and exports the document to
114  * a free-form layout.
115  * <p>
116  * Since classic AWT fonts can be sometimes very different from PDF fonts, a font mapping feature was added.
117  * By using the {@link JRExporterParameter#FONT_MAP} parameter, a logical font like "sansserif" can be mapped
118  * to a system specific font, like "Helvetica-BoldOblique". PDF font mapping is a little more complicated, because
119  * for a logical font, PDF cand provide two or more fonts, from the same family but with different styles (like
120  * "Helvetica", "Helvetica-Bold", "Helvetica-BoldOblique"). So every key in the map is a simple bean containing
121  * font family, bold and italic flag, and every value is another bean containing the PDF font name, encoding and
122  * embedding flag.
123  * @see FontKey
124  * @see PdfFont
125  * @author Teodor Danciu (teodord@users.sourceforge.net)
126  * @version $Id: JRPdfExporter.java 1511 2006-11-29 19:17:00 +0200 (Wed, 29 Nov 2006) teodord $
127  */

128 public class JRPdfExporter extends JRAbstractExporter
129 {
130
131     /**
132      * Property that provides a default value for the
133      * {@link net.sf.jasperreports.engine.export.JRPdfExporterParameter#FORCE_SVG_SHAPES JRPdfExporterParameter.FORCE_SVG_SHAPES}
134      * PDF exporter parameter.
135      *
136      * @see net.sf.jasperreports.engine.export.JRPdfExporterParameter#FORCE_SVG_SHAPES
137      */

138     public static final String JavaDoc PDF_FORCE_SVG_SHAPES = JRProperties.PROPERTY_PREFIX + "export.pdf.force.svg.shapes";
139
140     private static final String JavaDoc EMPTY_BOOKMARK_TITLE = "";
141
142     /**
143      *
144      */

145     protected static final String JavaDoc JR_PAGE_ANCHOR_PREFIX = "JR_PAGE_ANCHOR_";
146
147     protected static boolean fontsRegistered = false;
148
149     /**
150      *
151      */

152     protected Document document = null;
153     protected PdfContentByte pdfContentByte = null;
154
155     protected Document imageTesterDocument = null;
156     protected PdfContentByte imageTesterPdfContentByte = null;
157
158     protected JRExportProgressMonitor progressMonitor = null;
159
160     protected int reportIndex = 0;
161
162     /**
163      *
164      */

165     protected boolean isCreatingBatchModeBookmarks = false;
166     protected boolean isCompressed = false;
167     protected boolean isEncrypted = false;
168     protected boolean is128BitKey = false;
169     protected String JavaDoc userPassword = null;
170     protected String JavaDoc ownerPassword = null;
171     protected int permissions = 0;
172     protected Character JavaDoc pdfVersion = null;
173
174     /**
175      *
176      */

177     protected Map JavaDoc loadedImagesMap = null;
178     protected Image JavaDoc pxImage = null;
179     
180     private BookmarkStack bookmarkStack = null;
181
182     private Map JavaDoc fontMap = null;
183     
184     private boolean forceSvgShapes = true;
185     private SplitCharacter splitCharacter;
186     protected JRHyperlinkProducerFactory hyperlinkProducerFactory;
187
188     private String JavaDoc pdfJavaScript;
189
190     /**
191      *
192      */

193     protected Image JavaDoc getPxImage()
194     {
195         if (pxImage == null)
196         {
197             try
198             {
199                 pxImage =
200                     Image.getInstance(
201                         JRLoader.loadBytesFromLocation("net/sf/jasperreports/engine/images/pixel.GIF", null)
202                         );
203             }
204             catch(Exception JavaDoc e)
205             {
206                 throw new JRRuntimeException(e);
207             }
208         }
209
210         return pxImage;
211     }
212
213
214     /**
215      *
216      */

217     public void exportReport() throws JRException
218     {
219         registerFonts();
220         
221         progressMonitor = (JRExportProgressMonitor)parameters.get(JRExporterParameter.PROGRESS_MONITOR);
222         
223         /* */
224         setOffset();
225
226         try
227         {
228             /* */
229             setExportContext();
230     
231             /* */
232             setInput();
233     
234             /* */
235             if (!isModeBatch)
236             {
237                 setPageRange();
238             }
239             
240             Boolean JavaDoc isCreatingBatchModeBookmarksParameter = (Boolean JavaDoc)parameters.get(JRPdfExporterParameter.IS_CREATING_BATCH_MODE_BOOKMARKS);
241             if(isCreatingBatchModeBookmarksParameter != null){
242                 isCreatingBatchModeBookmarks = isCreatingBatchModeBookmarksParameter.booleanValue();
243             }
244     
245             Boolean JavaDoc isCompressedParameter = (Boolean JavaDoc)parameters.get(JRPdfExporterParameter.IS_COMPRESSED);
246             if (isCompressedParameter != null)
247             {
248                 isCompressed = isCompressedParameter.booleanValue();
249             }
250             
251             Boolean JavaDoc isEncryptedParameter = (Boolean JavaDoc)parameters.get(JRPdfExporterParameter.IS_ENCRYPTED);
252             if (isEncryptedParameter != null)
253             {
254                 isEncrypted = isEncryptedParameter.booleanValue();
255             }
256             
257             Boolean JavaDoc is128BitKeyParameter = (Boolean JavaDoc)parameters.get(JRPdfExporterParameter.IS_128_BIT_KEY);
258             if (is128BitKeyParameter != null)
259             {
260                 is128BitKey = is128BitKeyParameter.booleanValue();
261             }
262             
263             userPassword = (String JavaDoc)parameters.get(JRPdfExporterParameter.USER_PASSWORD);
264             ownerPassword = (String JavaDoc)parameters.get(JRPdfExporterParameter.OWNER_PASSWORD);
265     
266             Integer JavaDoc permissionsParameter = (Integer JavaDoc)parameters.get(JRPdfExporterParameter.PERMISSIONS);
267             if (permissionsParameter != null)
268             {
269                 permissions = permissionsParameter.intValue();
270             }
271     
272             pdfVersion = (Character JavaDoc) parameters.get(JRPdfExporterParameter.PDF_VERSION);
273     
274             fontMap = (Map JavaDoc) parameters.get(JRExporterParameter.FONT_MAP);
275             
276             setForceSvgShapes();
277             setSplitCharacter();
278             setHyperlinkProducerFactory();
279
280             pdfJavaScript = (String JavaDoc)parameters.get(JRPdfExporterParameter.PDF_JAVASCRIPT);
281
282             OutputStream JavaDoc os = (OutputStream JavaDoc)parameters.get(JRExporterParameter.OUTPUT_STREAM);
283             if (os != null)
284             {
285                 exportReportToStream(os);
286             }
287             else
288             {
289                 File JavaDoc destFile = (File JavaDoc)parameters.get(JRExporterParameter.OUTPUT_FILE);
290                 if (destFile == null)
291                 {
292                     String JavaDoc fileName = (String JavaDoc)parameters.get(JRExporterParameter.OUTPUT_FILE_NAME);
293                     if (fileName != null)
294                     {
295                         destFile = new File JavaDoc(fileName);
296                     }
297                     else
298                     {
299                         throw new JRException("No output specified for the exporter.");
300                     }
301                 }
302     
303                 try
304                 {
305                     os = new FileOutputStream JavaDoc(destFile);
306                     exportReportToStream(os);
307                     os.flush();
308                 }
309                 catch (IOException JavaDoc e)
310                 {
311                     throw new JRException("Error trying to export to file : " + destFile, e);
312                 }
313                 finally
314                 {
315                     if (os != null)
316                     {
317                         try
318                         {
319                             os.close();
320                         }
321                         catch(IOException JavaDoc e)
322                         {
323                         }
324                     }
325                 }
326             }
327         }
328         finally
329         {
330             resetExportContext();
331         }
332     }
333
334
335     protected void setForceSvgShapes()
336     {
337         Boolean JavaDoc forceSvgShapesParam = (Boolean JavaDoc) parameters.get(JRPdfExporterParameter.FORCE_SVG_SHAPES);
338         if (forceSvgShapesParam == null)
339         {
340             forceSvgShapes = JRProperties.getBooleanProperty(PDF_FORCE_SVG_SHAPES);
341         }
342         else
343         {
344             forceSvgShapes = forceSvgShapesParam.booleanValue();
345         }
346     }
347
348
349     protected void setSplitCharacter()
350     {
351         boolean useFillSplitCharacter;
352         Boolean JavaDoc useFillSplitCharacterParam = (Boolean JavaDoc) parameters.get(JRPdfExporterParameter.FORCE_LINEBREAK_POLICY);
353         if (useFillSplitCharacterParam == null)
354         {
355             useFillSplitCharacter = JRProperties.getBooleanProperty(JRProperties.PDF_FORCE_LINEBREAK_POLICY);
356         }
357         else
358         {
359             useFillSplitCharacter = useFillSplitCharacterParam.booleanValue();
360         }
361         
362         if (useFillSplitCharacter)
363         {
364             splitCharacter = new BreakIteratorSplitCharacter();
365         }
366     }
367
368
369     protected void setHyperlinkProducerFactory()
370     {
371         hyperlinkProducerFactory = (JRHyperlinkProducerFactory) parameters.get(JRPdfExporterParameter.HYPERLINK_PRODUCER_FACTORY);
372     }
373
374
375     /**
376      *
377      */

378     protected void exportReportToStream(OutputStream JavaDoc os) throws JRException
379     {
380         //ByteArrayOutputStream baos = new ByteArrayOutputStream();
381

382         document =
383             new Document(
384                 new Rectangle(
385                     jasperPrint.getPageWidth(),
386                     jasperPrint.getPageHeight()
387                 )
388             );
389
390         imageTesterDocument =
391             new Document(
392                 new Rectangle(
393                     10, //jasperPrint.getPageWidth(),
394
10 //jasperPrint.getPageHeight()
395
)
396             );
397
398         try
399         {
400             PdfWriter pdfWriter = PdfWriter.getInstance(document, os);
401             pdfWriter.setCloseStream(false);
402
403             if (pdfVersion != null)
404                 pdfWriter.setPdfVersion(pdfVersion.charValue());
405
406             if (isCompressed)
407                 pdfWriter.setFullCompression();
408             
409             if (isEncrypted)
410             {
411                 pdfWriter.setEncryption(
412                     is128BitKey,
413                     userPassword,
414                     ownerPassword,
415                     permissions
416                     );
417             }
418
419             // Add meta-data parameters to generated PDF document
420
// mtclough@users.sourceforge.net 2005-12-05
421
String JavaDoc title = (String JavaDoc)parameters.get(JRPdfExporterParameter.METADATA_TITLE);
422             if( title != null )
423                 document.addTitle(title);
424             
425             String JavaDoc author = (String JavaDoc)parameters.get(JRPdfExporterParameter.METADATA_AUTHOR);
426             if( author != null )
427                 document.addAuthor(author);
428             
429             String JavaDoc subject = (String JavaDoc)parameters.get(JRPdfExporterParameter.METADATA_SUBJECT);
430             if( subject != null )
431                 document.addSubject(subject);
432             
433             String JavaDoc keywords = (String JavaDoc)parameters.get(JRPdfExporterParameter.METADATA_KEYWORDS);
434             if( keywords != null )
435                 document.addKeywords(keywords);
436             
437             String JavaDoc creator = (String JavaDoc)parameters.get(JRPdfExporterParameter.METADATA_CREATOR);
438             if( creator != null )
439                 document.addCreator(creator);
440             else
441                 document.addCreator("JasperReports (" + jasperPrint.getName() + ")");
442
443             document.open();
444             
445             if(pdfJavaScript != null)
446                 pdfWriter.addJavaScript(pdfJavaScript);
447             
448             pdfContentByte = pdfWriter.getDirectContent();
449             
450             initBookmarks();
451
452             PdfWriter imageTesterPdfWriter =
453                 PdfWriter.getInstance(
454                     imageTesterDocument,
455                     new NullOutputStream() // discard the output
456
);
457             imageTesterDocument.open();
458             imageTesterDocument.newPage();
459             imageTesterPdfContentByte = imageTesterPdfWriter.getDirectContent();
460             imageTesterPdfContentByte.setLiteral("\n");
461
462             for(reportIndex = 0; reportIndex < jasperPrintList.size(); reportIndex++)
463             {
464                 jasperPrint = (JasperPrint)jasperPrintList.get(reportIndex);
465                 loadedImagesMap = new HashMap JavaDoc();
466                 document.setPageSize(new Rectangle(jasperPrint.getPageWidth(), jasperPrint.getPageHeight()));
467                 
468                 List JavaDoc pages = jasperPrint.getPages();
469                 if (pages != null && pages.size() > 0)
470                 {
471                     if (isModeBatch)
472                     {
473                         document.newPage();
474                         
475                         if( isCreatingBatchModeBookmarks ){
476                             //add a new level to our outline for this report
477
addBookmark(0, jasperPrint.getName(), 0, 0);
478                         }
479
480                         startPageIndex = 0;
481                         endPageIndex = pages.size() - 1;
482                     }
483
484                     Chunk chunk = null;
485                     ColumnText colText = null;
486                     JRPrintPage page = null;
487                     for(int pageIndex = startPageIndex; pageIndex <= endPageIndex; pageIndex++)
488                     {
489                         if (Thread.currentThread().isInterrupted())
490                         {
491                             throw new JRException("Current thread interrupted.");
492                         }
493                 
494                         page = (JRPrintPage)pages.get(pageIndex);
495
496                         document.newPage();
497
498                         pdfContentByte = pdfWriter.getDirectContent();
499
500                         pdfContentByte.setLineCap(2);//PdfContentByte.LINE_CAP_PROJECTING_SQUARE since iText 1.02b
501

502                         chunk = new Chunk(" ");
503                         chunk.setLocalDestination(JR_PAGE_ANCHOR_PREFIX + reportIndex + "_" + (pageIndex + 1));
504
505                         colText = new ColumnText(pdfContentByte);
506                         colText.setSimpleColumn(
507                             new Phrase(chunk),
508                             0,
509                             jasperPrint.getPageHeight(),
510                             1,
511                             1,
512                             0,
513                             Element.ALIGN_LEFT
514                             );
515
516                         colText.go();
517
518                         /* */
519                         exportPage(page);
520                     }
521                 }
522                 else
523                 {
524                     document.newPage();
525                     pdfContentByte = pdfWriter.getDirectContent();
526                     pdfContentByte.setLiteral("\n");
527                 }
528             }
529         }
530         catch(DocumentException e)
531         {
532             throw new JRException("PDF Document error : " + jasperPrint.getName(), e);
533         }
534         catch(IOException JavaDoc e)
535         {
536             throw new JRException("Error generating PDF report : " + jasperPrint.getName(), e);
537         }
538         finally
539         {
540             document.close();
541             imageTesterDocument.close();
542         }
543
544         //return os.toByteArray();
545
}
546
547
548     /**
549      *
550      */

551     protected void exportPage(JRPrintPage page) throws JRException, DocumentException, IOException JavaDoc
552     {
553         Collection JavaDoc elements = page.getElements();
554         exportElements(elements);
555         
556         if (progressMonitor != null)
557         {
558             progressMonitor.afterPageExport();
559         }
560     }
561
562
563     protected void exportElements(Collection JavaDoc elements) throws DocumentException, IOException JavaDoc, JRException
564     {
565         if (elements != null && elements.size() > 0)
566         {
567             JRPrintElement element;
568             for(Iterator JavaDoc it = elements.iterator(); it.hasNext();)
569             {
570                 element = (JRPrintElement)it.next();
571
572                 if (element instanceof JRPrintLine)
573                 {
574                     exportLine((JRPrintLine)element);
575                 }
576                 else if (element instanceof JRPrintRectangle)
577                 {
578                     exportRectangle((JRPrintRectangle)element);
579                 }
580                 else if (element instanceof JRPrintEllipse)
581                 {
582                     exportEllipse((JRPrintEllipse)element);
583                 }
584                 else if (element instanceof JRPrintImage)
585                 {
586                     exportImage((JRPrintImage)element);
587                 }
588                 else if (element instanceof JRPrintText)
589                 {
590                     exportText((JRPrintText)element);
591                 }
592                 else if (element instanceof JRPrintFrame)
593                 {
594                     exportFrame((JRPrintFrame) element);
595                 }
596             }
597         }
598     }
599
600
601     /**
602      *
603      */

604     protected void exportLine(JRPrintLine line)
605     {
606         if (line.getPen() != JRGraphicElement.PEN_NONE)
607         {
608             pdfContentByte.setRGBColorStroke(
609                 line.getForecolor().getRed(),
610                 line.getForecolor().getGreen(),
611                 line.getForecolor().getBlue()
612                 );
613
614             switch (line.getPen())
615             {
616                 case JRGraphicElement.PEN_DOTTED :
617                 {
618                     pdfContentByte.setLineWidth(1f);
619                     pdfContentByte.setLineDash(5f, 3f, 0f);
620                     break;
621                 }
622                 case JRGraphicElement.PEN_4_POINT :
623                 {
624                     pdfContentByte.setLineWidth(4f);
625                     pdfContentByte.setLineDash(0f);
626                     break;
627                 }
628                 case JRGraphicElement.PEN_2_POINT :
629                 {
630                     pdfContentByte.setLineWidth(2f);
631                     pdfContentByte.setLineDash(0f);
632                     break;
633                 }
634                 case JRGraphicElement.PEN_NONE :
635                 {
636                     //never reached due to the initial if statement
637
break;
638                 }
639                 case JRGraphicElement.PEN_THIN :
640                 {
641                     pdfContentByte.setLineWidth(0.5f);
642                     pdfContentByte.setLineDash(0f);
643                     break;
644                 }
645                 case JRGraphicElement.PEN_1_POINT :
646                 default :
647                 {
648                     pdfContentByte.setLineWidth(1f);
649                     pdfContentByte.setLineDash(0f);
650                     break;
651                 }
652             }
653
654             if (line.getDirection() == JRLine.DIRECTION_TOP_DOWN)
655             {
656                 pdfContentByte.moveTo(
657                     line.getX() + getOffsetX(),
658                     jasperPrint.getPageHeight() - line.getY() - getOffsetY()
659                     );
660                 pdfContentByte.lineTo(
661                     line.getX() + getOffsetX() + line.getWidth() - 1,
662                     jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight() + 1
663                     );
664             }
665             else
666             {
667                 pdfContentByte.moveTo(
668                     line.getX() + getOffsetX(),
669                     jasperPrint.getPageHeight() - line.getY() - getOffsetY() - line.getHeight() + 1
670                     );
671                 pdfContentByte.lineTo(
672                     line.getX() + getOffsetX() + line.getWidth() - 1,
673                     jasperPrint.getPageHeight() - line.getY() - getOffsetY()
674                     );
675             }
676         
677             pdfContentByte.stroke();
678             
679             pdfContentByte.setLineDash(0f);
680         }
681     }
682
683
684     /**
685      *
686      */

687     protected void exportRectangle(JRPrintRectangle rectangle)
688     {
689         pdfContentByte.setRGBColorStroke(
690             rectangle.getForecolor().getRed(),
691             rectangle.getForecolor().getGreen(),
692             rectangle.getForecolor().getBlue()
693             );
694         pdfContentByte.setRGBColorFill(
695             rectangle.getBackcolor().getRed(),
696             rectangle.getBackcolor().getGreen(),
697             rectangle.getBackcolor().getBlue()
698             );
699
700         float borderCorrection = prepareGraphicElement(rectangle);
701
702         if (rectangle.getMode() == JRElement.MODE_OPAQUE)
703         {
704             pdfContentByte.roundRectangle(
705                     rectangle.getX() + getOffsetX() - borderCorrection,
706                     jasperPrint.getPageHeight() - rectangle.getY() - getOffsetY() - rectangle.getHeight() - borderCorrection + 1,
707                     rectangle.getWidth() + 2 * borderCorrection - 1,
708                     rectangle.getHeight() + 2 * borderCorrection - 1,
709                     rectangle.getRadius()
710                     );
711             
712             if (rectangle.getPen() == JRGraphicElement.PEN_DOTTED)
713             {
714                 pdfContentByte.fill();
715                 
716                 pdfContentByte.roundRectangle(
717                         rectangle.getX() + getOffsetX(),
718                         jasperPrint.getPageHeight() - rectangle.getY() - getOffsetY() - rectangle.getHeight() + 1,
719                         rectangle.getWidth() - 1,
720                         rectangle.getHeight() - 1,
721                         rectangle.getRadius()
722                         );
723                 pdfContentByte.stroke();
724             }
725             else
726             {
727                 pdfContentByte.fillStroke();
728             }
729         }
730         else
731         {
732             if (rectangle.getPen() != JRGraphicElement.PEN_NONE)
733             {
734                 pdfContentByte.roundRectangle(
735                     rectangle.getX() + getOffsetX() - borderCorrection,
736                     jasperPrint.getPageHeight() - rectangle.getY() - getOffsetY() - rectangle.getHeight() - borderCorrection + 1,
737                     rectangle.getWidth() + 2 * borderCorrection - 1,
738                     rectangle.getHeight() + 2 * borderCorrection - 1,
739                     rectangle.getRadius()
740                     );
741
742                 pdfContentByte.stroke();
743             }
744         }
745         
746         pdfContentByte.setLineDash(0f);
747     }
748
749
750     protected float prepareGraphicElement(JRPrintGraphicElement element)
751     {
752         float borderCorrection = 0f;
753
754         switch (element.getPen())
755         {
756             case JRGraphicElement.PEN_DOTTED :
757             {
758                 borderCorrection = element.getMode() == JRElement.MODE_OPAQUE ? 0.5f : 0f;
759                 pdfContentByte.setLineWidth(1f);
760                 pdfContentByte.setLineDash(5f, 3f, 0f);
761                 break;
762             }
763             case JRGraphicElement.PEN_4_POINT :
764             {
765                 borderCorrection = 0.5f;
766                 pdfContentByte.setLineWidth(4f);
767                 pdfContentByte.setLineDash(0f);
768                 break;
769             }
770             case JRGraphicElement.PEN_2_POINT :
771             {
772                 borderCorrection = 0.5f;
773                 pdfContentByte.setLineWidth(2f);
774                 pdfContentByte.setLineDash(0f);
775                 break;
776             }
777             case JRGraphicElement.PEN_THIN :
778             {
779                 borderCorrection = 0.25f;
780                 pdfContentByte.setLineWidth(0.5f);
781                 pdfContentByte.setLineDash(0f);
782                 break;
783             }
784             case JRGraphicElement.PEN_NONE :
785             {
786                 borderCorrection = 0.5f;
787                 pdfContentByte.setLineWidth(0f);
788                 pdfContentByte.setLineDash(0f);
789
790                 pdfContentByte.setRGBColorStroke(
791                     element.getBackcolor().getRed(),
792                     element.getBackcolor().getGreen(),
793                     element.getBackcolor().getBlue()
794                     );
795
796                 break;
797             }
798             case JRGraphicElement.PEN_1_POINT :
799             default :
800             {
801                 borderCorrection = 0f;
802                 pdfContentByte.setLineWidth(1f);
803                 pdfContentByte.setLineDash(0f);
804                 break;
805             }
806         }
807         return borderCorrection;
808     }
809
810
811     /**
812      *
813      */

814     protected void exportEllipse(JRPrintEllipse ellipse)
815     {
816         pdfContentByte.setRGBColorStroke(
817             ellipse.getForecolor().getRed(),
818             ellipse.getForecolor().getGreen(),
819             ellipse.getForecolor().getBlue()
820             );
821         pdfContentByte.setRGBColorFill(
822             ellipse.getBackcolor().getRed(),
823             ellipse.getBackcolor().getGreen(),
824             ellipse.getBackcolor().getBlue()
825             );
826
827         float borderCorrection = prepareGraphicElement(ellipse);
828         
829         if (ellipse.getMode() == JRElement.MODE_OPAQUE)
830         {
831             pdfContentByte.ellipse(
832                 ellipse.getX() + getOffsetX() - borderCorrection,
833                 jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() - ellipse.getHeight() - borderCorrection + 1,
834                 ellipse.getX() + getOffsetX() + ellipse.getWidth() + borderCorrection - 1,
835                 jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() + borderCorrection
836                 );
837
838             if (ellipse.getPen() == JRGraphicElement.PEN_DOTTED)
839             {
840                 pdfContentByte.fill();
841                 
842                 pdfContentByte.ellipse(
843                         ellipse.getX() + getOffsetX(),
844                         jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() - ellipse.getHeight() + 1,
845                         ellipse.getX() + getOffsetX() + ellipse.getWidth() - 1,
846                         jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY()
847                         );
848                 pdfContentByte.stroke();
849             }
850             else
851             {
852                 pdfContentByte.fillStroke();
853             }
854         }
855         else
856         {
857             if (ellipse.getPen() != JRGraphicElement.PEN_NONE)
858             {
859                 pdfContentByte.ellipse(
860                     ellipse.getX() + getOffsetX() - borderCorrection,
861                     jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() - ellipse.getHeight() - borderCorrection + 1,
862                     ellipse.getX() + getOffsetX() + ellipse.getWidth() + borderCorrection - 1,
863                     jasperPrint.getPageHeight() - ellipse.getY() - getOffsetY() + borderCorrection
864                     );
865
866                 pdfContentByte.stroke();
867             }
868         }
869         
870         pdfContentByte.setLineDash(0f);
871     }
872
873
874     /**
875      *
876      */

877     protected void exportImage(JRPrintImage printImage) throws DocumentException, IOException JavaDoc, JRException
878     {
879         pdfContentByte.setRGBColorFill(
880             printImage.getBackcolor().getRed(),
881             printImage.getBackcolor().getGreen(),
882             printImage.getBackcolor().getBlue()
883             );
884
885         if (printImage.getMode() == JRElement.MODE_OPAQUE)
886         {
887             float borderCorrection = .5f;
888             
889             pdfContentByte.setRGBColorStroke(
890                 printImage.getBackcolor().getRed(),
891                 printImage.getBackcolor().getGreen(),
892                 printImage.getBackcolor().getBlue()
893                 );
894             pdfContentByte.setLineWidth(0.1f);
895             pdfContentByte.setLineDash(0f);
896             pdfContentByte.rectangle(
897                 printImage.getX() + getOffsetX() - borderCorrection,
898                 jasperPrint.getPageHeight() - printImage.getY() - getOffsetY() + borderCorrection,
899                 printImage.getWidth() + 2 * borderCorrection - 1,
900                 - printImage.getHeight() - 2 * borderCorrection + 1
901                 );
902             pdfContentByte.fillStroke();
903         }
904
905         int topPadding = printImage.getTopPadding();
906         int leftPadding = printImage.getLeftPadding();
907         int bottomPadding = printImage.getBottomPadding();
908         int rightPadding = printImage.getRightPadding();
909
910         int availableImageWidth = printImage.getWidth() - leftPadding - rightPadding;
911         availableImageWidth = (availableImageWidth < 0)?0:availableImageWidth;
912
913         int availableImageHeight = printImage.getHeight() - topPadding - bottomPadding;
914         availableImageHeight = (availableImageHeight < 0)?0:availableImageHeight;
915
916         JRRenderable renderer = printImage.getRenderer();
917         
918         if (
919             renderer != null &&
920             availableImageWidth > 0 &&
921             availableImageHeight > 0
922             )
923         {
924             int xoffset = 0;
925             int yoffset = 0;
926
927             Chunk chunk = null;
928
929             float scaledWidth = availableImageWidth;
930             float scaledHeight = availableImageHeight;
931
932             if (renderer.getType() == JRRenderable.TYPE_IMAGE)
933             {
934                 //java.awt.Image awtImage = JRImageLoader.loadImage(printImage.getImageData());
935

936                 //com.lowagie.text.Image image = com.lowagie.text.Image.getInstance(awtImage, printImage.getBackcolor());
937
//com.lowagie.text.Image image = com.lowagie.text.Image.getInstance(awtImage, null);
938
com.lowagie.text.Image image = null;
939
940                 float xalignFactor = getXAlignFactor(printImage);
941                 float yalignFactor = getYAlignFactor(printImage);
942
943                 switch(printImage.getScaleImage())
944                 {
945                     case JRImage.SCALE_IMAGE_CLIP :
946                     {
947                         int normalWidth = availableImageWidth;
948                         int normalHeight = availableImageHeight;
949                     
950                         Dimension2D JavaDoc dimension = renderer.getDimension();
951                         if (dimension != null)
952                         {
953                             normalWidth = (int)dimension.getWidth();
954                             normalHeight = (int)dimension.getHeight();
955                         }
956
957                         xoffset = (int)(xalignFactor * (availableImageWidth - normalWidth));
958                         yoffset = (int)(yalignFactor * (availableImageHeight - normalHeight));
959
960                         int minWidth = Math.min(normalWidth, availableImageWidth);
961                         int minHeight = Math.min(normalHeight, availableImageHeight);
962                     
963                         BufferedImage JavaDoc bi =
964                             new BufferedImage JavaDoc(minWidth, minHeight, BufferedImage.TYPE_INT_ARGB);
965
966                         Graphics2D JavaDoc g = bi.createGraphics();
967                         if (printImage.getMode() == JRElement.MODE_OPAQUE)
968                         {
969                             g.setColor(printImage.getBackcolor());
970                             g.fillRect(0, 0, minWidth, minHeight);
971                         }
972                         renderer.render(
973                             g,
974                             new java.awt.Rectangle JavaDoc(
975                                 (xoffset > 0 ? 0 : xoffset),
976                                 (yoffset > 0 ? 0 : yoffset),
977                                 normalWidth,
978                                 normalHeight
979                                 )
980                             );
981                         g.dispose();
982
983                         xoffset = (xoffset < 0 ? 0 : xoffset);
984                         yoffset = (yoffset < 0 ? 0 : yoffset);
985
986                         //awtImage = bi.getSubimage(0, 0, minWidth, minHeight);
987

988                         //image = com.lowagie.text.Image.getInstance(awtImage, printImage.getBackcolor());
989
image = com.lowagie.text.Image.getInstance(bi, null);
990
991                         break;
992                     }
993                     case JRImage.SCALE_IMAGE_FILL_FRAME :
994                     {
995                         if (loadedImagesMap.containsKey(renderer))
996                         {
997                             image = (com.lowagie.text.Image)loadedImagesMap.get(renderer);
998                         }
999                         else
1000                        {
1001                            try
1002                            {
1003                                image = com.lowagie.text.Image.getInstance(renderer.getImageData());
1004                                imageTesterPdfContentByte.addImage(image, 10, 0, 0, 10, 0, 0);
1005                            }
1006                            catch(Exception JavaDoc e)
1007                            {
1008                                java.awt.Image JavaDoc awtImage =
1009                                    JRImageRenderer.getInstance(
1010                                        renderer.getImageData(),
1011                                        printImage.getOnErrorType()
1012                                        ).getImage();
1013                                image = com.lowagie.text.Image.getInstance(awtImage, null);
1014                            }
1015
1016                            loadedImagesMap.put(renderer, image);
1017                        }
1018
1019                        image.scaleAbsolute(availableImageWidth, availableImageHeight);
1020                        break;
1021                    }
1022                    case JRImage.SCALE_IMAGE_RETAIN_SHAPE :
1023                    default :
1024                    {
1025                        if (loadedImagesMap.containsKey(renderer))
1026                        {
1027                            image = (com.lowagie.text.Image)loadedImagesMap.get(renderer);
1028                        }
1029                        else
1030                        {
1031                            try
1032                            {
1033                                image = com.lowagie.text.Image.getInstance(renderer.getImageData());
1034                                imageTesterPdfContentByte.addImage(image, 10, 0, 0, 10, 0, 0);
1035                            }
1036                            catch(Exception JavaDoc e)
1037                            {
1038                                java.awt.Image JavaDoc awtImage =
1039                                    JRImageRenderer.getInstance(
1040                                        renderer.getImageData(),
1041                                        printImage.getOnErrorType()
1042                                        ).getImage();
1043                                image = com.lowagie.text.Image.getInstance(awtImage, null);
1044                            }
1045
1046                            loadedImagesMap.put(renderer, image);
1047                        }
1048
1049                        image.scaleToFit(availableImageWidth, availableImageHeight);
1050                    
1051                        xoffset = (int)(xalignFactor * (availableImageWidth - image.plainWidth()));
1052                        yoffset = (int)(yalignFactor * (availableImageHeight - image.plainHeight()));
1053
1054                        xoffset = (xoffset < 0 ? 0 : xoffset);
1055                        yoffset = (yoffset < 0 ? 0 : yoffset);
1056
1057                        break;
1058                    }
1059                }
1060                
1061                chunk = new Chunk(image, -0.5f, 0.5f);
1062                
1063                scaledWidth = image.scaledWidth();
1064                scaledHeight = image.scaledHeight();
1065            }
1066            else
1067            {
1068                double normalWidth = availableImageWidth;
1069                double normalHeight = availableImageHeight;
1070                
1071                Dimension2D JavaDoc dimension = renderer.getDimension();
1072                if (dimension != null)
1073                {
1074                    float xalignFactor = getXAlignFactor(printImage);
1075                    float yalignFactor = getYAlignFactor(printImage);
1076                    
1077                    switch (printImage.getScaleImage())
1078                    {
1079                        case JRImage.SCALE_IMAGE_CLIP:
1080                        {
1081                            normalWidth = dimension.getWidth();
1082                            normalHeight = dimension.getHeight();
1083                            xoffset = (int) (xalignFactor * (availableImageWidth - normalWidth));
1084                            yoffset = (int) (yalignFactor * (availableImageHeight - normalHeight));
1085                            break;
1086                        }
1087                        case JRImage.SCALE_IMAGE_FILL_FRAME:
1088                        {
1089                            xoffset = 0;
1090                            yoffset = 0;
1091                            break;
1092                        }
1093                        case JRImage.SCALE_IMAGE_RETAIN_SHAPE:
1094                        default:
1095                        {
1096                            normalWidth = dimension.getWidth();
1097                            normalHeight = dimension.getHeight();
1098                            double ratioX = availableImageWidth / normalWidth;
1099                            double ratioY = availableImageHeight / normalHeight;
1100                            double ratio = ratioX < ratioY ? ratioX : ratioY;
1101                            normalWidth *= ratio;
1102                            normalHeight *= ratio;
1103                            xoffset = (int) (xalignFactor * (availableImageWidth - normalWidth));
1104                            yoffset = (int) (yalignFactor * (availableImageHeight - normalHeight));
1105                            break;
1106                        }
1107                    }
1108                }
1109
1110                PdfTemplate template = pdfContentByte.createTemplate(availableImageWidth, availableImageHeight);
1111                
1112                Graphics2D JavaDoc g = forceSvgShapes
1113                    ? template.createGraphicsShapes(availableImageWidth, availableImageHeight)
1114                    : template.createGraphics(availableImageWidth, availableImageHeight, new LocalFontMapper());
1115                
1116                if (printImage.getMode() == JRElement.MODE_OPAQUE)
1117                {
1118                    g.setColor(printImage.getBackcolor());
1119                    g.fillRect(0, 0,
1120                        normalWidth <= availableImageWidth ? (int) normalWidth : availableImageWidth,
1121                        normalHeight <= availableImageHeight ? (int) normalHeight : availableImageHeight);
1122                }
1123
1124                Rectangle2D JavaDoc rectangle = new Rectangle2D.Double JavaDoc(
1125                        (xoffset > 0 ? 0 : xoffset),
1126                        (yoffset > 0 ? 0 : yoffset),
1127                        normalWidth,
1128                        normalHeight);
1129                
1130                renderer.render(g, rectangle);
1131                g.dispose();
1132                
1133                xoffset = (xoffset < 0 ? 0 : xoffset);
1134                yoffset = (yoffset < 0 ? 0 : yoffset);
1135
1136                pdfContentByte.saveState();
1137                pdfContentByte.addTemplate(
1138                    template,
1139                    printImage.getX() + getOffsetX() + xoffset,
1140                    jasperPrint.getPageHeight()
1141                        - printImage.getY() - getOffsetY()
1142                        - availableImageHeight
1143                        - yoffset
1144                    );
1145                pdfContentByte.restoreState();
1146
1147                Image JavaDoc image = getPxImage();
1148                image.scaleAbsolute(availableImageWidth, availableImageHeight);
1149                chunk = new Chunk(image, 0, 0);
1150            }
1151
1152            /*
1153            image.setAbsolutePosition(
1154                printImage.getX() + offsetX + borderOffset,
1155                jasperPrint.getPageHeight() - printImage.getY() - offsetY - image.scaledHeight() - borderOffset
1156                );
1157
1158            pdfContentByte.addImage(image);
1159            */

1160
1161
1162            setAnchor(chunk, printImage, printImage);
1163            setHyperlinkInfo(chunk, printImage);
1164
1165            ColumnText colText = new ColumnText(pdfContentByte);
1166            int upperY = jasperPrint.getPageHeight() - printImage.getY() - topPadding - getOffsetY() - yoffset;
1167            int lowerX = printImage.getX() + leftPadding + getOffsetX() + xoffset;
1168            colText.setSimpleColumn(
1169                new Phrase(chunk),
1170                lowerX,
1171                upperY - scaledHeight,
1172                lowerX + scaledWidth,
1173                upperY,
1174                scaledHeight,
1175                Element.ALIGN_LEFT
1176                );
1177
1178            colText.go();
1179        }
1180
1181
1182        if (
1183            printImage.getTopBorder() == JRGraphicElement.PEN_NONE &&
1184            printImage.getLeftBorder() == JRGraphicElement.PEN_NONE &&
1185            printImage.getBottomBorder() == JRGraphicElement.PEN_NONE &&
1186            printImage.getRightBorder() == JRGraphicElement.PEN_NONE
1187            )
1188        {
1189            if (printImage.getPen() != JRGraphicElement.PEN_NONE)
1190            {
1191                exportBox(getBox(printImage), printImage);
1192            }
1193        }
1194        else
1195        {
1196            /* */
1197            exportBox(
1198                printImage,
1199                printImage
1200                );
1201        }
1202    }
1203
1204
1205    private float getXAlignFactor(JRPrintImage printImage)
1206    {
1207        float xalignFactor = 0f;
1208        switch (printImage.getHorizontalAlignment())
1209        {
1210            case JRAlignment.HORIZONTAL_ALIGN_RIGHT :
1211            {
1212                xalignFactor = 1f;
1213                break;
1214            }
1215            case JRAlignment.HORIZONTAL_ALIGN_CENTER :
1216            {
1217                xalignFactor = 0.5f;
1218                break;
1219            }
1220            case JRAlignment.HORIZONTAL_ALIGN_LEFT :
1221            default :
1222            {
1223                xalignFactor = 0f;
1224                break;
1225            }
1226        }
1227        return xalignFactor;
1228    }
1229
1230
1231    private float getYAlignFactor(JRPrintImage printImage)
1232    {
1233        float yalignFactor = 0f;
1234        switch (printImage.getVerticalAlignment())
1235        {
1236            case JRAlignment.VERTICAL_ALIGN_BOTTOM :
1237            {
1238                yalignFactor = 1f;
1239                break;
1240            }
1241            case JRAlignment.VERTICAL_ALIGN_MIDDLE :
1242            {
1243                yalignFactor = 0.5f;
1244                break;
1245            }
1246            case JRAlignment.VERTICAL_ALIGN_TOP :
1247            default :
1248            {
1249                yalignFactor = 0f;
1250                break;
1251            }
1252        }
1253        return yalignFactor;
1254    }
1255
1256
1257    /**
1258     *
1259     */

1260    protected void setHyperlinkInfo(Chunk chunk, JRPrintHyperlink link)
1261    {
1262        switch(link.getHyperlinkType())
1263        {
1264            case JRHyperlink.HYPERLINK_TYPE_REFERENCE :
1265            {
1266                if (link.getHyperlinkReference() != null)
1267                {
1268                    chunk.setAnchor(link.getHyperlinkReference());
1269                }
1270                break;
1271            }
1272            case JRHyperlink.HYPERLINK_TYPE_LOCAL_ANCHOR :
1273            {
1274                if (link.getHyperlinkAnchor() != null)
1275                {
1276                    chunk.setLocalGoto(link.getHyperlinkAnchor());
1277                }
1278                break;
1279            }
1280            case JRHyperlink.HYPERLINK_TYPE_LOCAL_PAGE :
1281            {
1282                if (link.getHyperlinkPage() != null)
1283                {
1284                    chunk.setLocalGoto(JR_PAGE_ANCHOR_PREFIX + reportIndex + "_" + link.getHyperlinkPage().toString());
1285                }
1286                break;
1287            }
1288            case JRHyperlink.HYPERLINK_TYPE_REMOTE_ANCHOR :
1289            {
1290                if (
1291                    link.getHyperlinkReference() != null &&
1292                    link.getHyperlinkAnchor() != null
1293                    )
1294                {
1295                    chunk.setRemoteGoto(
1296                        link.getHyperlinkReference(),
1297                        link.getHyperlinkAnchor()
1298                        );
1299                }
1300                break;
1301            }
1302            case JRHyperlink.HYPERLINK_TYPE_REMOTE_PAGE :
1303            {
1304                if (
1305                    link.getHyperlinkReference() != null &&
1306                    link.getHyperlinkPage() != null
1307                    )
1308                {
1309                    chunk.setRemoteGoto(
1310                        link.getHyperlinkReference(),
1311                        link.getHyperlinkPage().intValue()
1312                        );
1313                }
1314                break;
1315            }
1316            case JRHyperlink.HYPERLINK_TYPE_CUSTOM :
1317            {
1318                if (hyperlinkProducerFactory != null)
1319                {
1320                    String JavaDoc hyperlink = hyperlinkProducerFactory.produceHyperlink(link);
1321                    if (hyperlink != null)
1322                    {
1323                        chunk.setAnchor(hyperlink);
1324                    }
1325                }
1326            }
1327            case JRHyperlink.HYPERLINK_TYPE_NONE :
1328            default :
1329            {
1330                break;
1331            }
1332        }
1333    }
1334    
1335
1336    /**
1337     *
1338     */

1339    protected Phrase getPhrase(JRStyledText styledText, JRPrintText textElement)
1340    {
1341        Phrase phrase = new Phrase();
1342
1343        String JavaDoc text = styledText.getText();
1344        
1345        int runLimit = 0;
1346
1347        AttributedCharacterIterator JavaDoc iterator = styledText.getAttributedString().getIterator();
1348        
1349        while(runLimit < styledText.length() && (runLimit = iterator.getRunLimit()) <= styledText.length())
1350        {
1351            Chunk chunk = getChunk(iterator.getAttributes(), text.substring(iterator.getIndex(), runLimit));
1352            setAnchor(chunk, textElement, textElement);
1353            setHyperlinkInfo(chunk, textElement);
1354            phrase.add(chunk);
1355
1356            iterator.setIndex(runLimit);
1357        }
1358
1359        return phrase;
1360    }
1361
1362
1363    /**
1364     *
1365     */

1366    protected Chunk getChunk(Map JavaDoc attributes, String JavaDoc text)
1367    {
1368        Font font = getFont(attributes);
1369
1370        Chunk chunk = new Chunk(text, font);
1371        
1372        Color JavaDoc backcolor = (Color JavaDoc)attributes.get(TextAttribute.BACKGROUND);
1373        if (backcolor != null)
1374        {
1375            chunk.setBackground(backcolor);
1376        }
1377        
1378        Object JavaDoc script = attributes.get(TextAttribute.SUPERSCRIPT);
1379        if (script != null)
1380        {
1381            if (TextAttribute.SUPERSCRIPT_SUPER.equals(script))
1382            {
1383                chunk.setTextRise(font.leading(1f)/2);
1384            }
1385            else if (script != null && TextAttribute.SUPERSCRIPT_SUB.equals(script))
1386            {
1387                chunk.setTextRise(-font.leading(1f)/2);
1388            }
1389        }
1390        
1391        if (splitCharacter != null)
1392        {
1393            chunk.setSplitCharacter(splitCharacter);
1394        }
1395        
1396        return chunk;
1397    }
1398
1399
1400    /**
1401     *
1402     */

1403    protected Font getFont(Map JavaDoc attributes)
1404    {
1405        JRFont jrFont = new JRBaseFont(attributes);
1406        
1407        Exception JavaDoc initialException = null;
1408
1409        Color JavaDoc forecolor = (Color JavaDoc)attributes.get(TextAttribute.FOREGROUND);
1410        /*
1411        if (forecolor == null)
1412        {
1413            forecolor = Color.black;
1414        }
1415        */

1416
1417        Font font = null;
1418        PdfFont pdfFont = null;
1419        FontKey key = new FontKey(jrFont.getFontName(), jrFont.isBold(), jrFont.isItalic());
1420
1421        if (fontMap != null && fontMap.containsKey(key))
1422        {
1423            pdfFont = (PdfFont) fontMap.get(key);
1424        }
1425        else
1426        {
1427            pdfFont = new PdfFont(jrFont.getPdfFontName(), jrFont.getPdfEncoding(), jrFont.isPdfEmbedded());
1428        }
1429        
1430        try
1431        {
1432            font = FontFactory.getFont(
1433                pdfFont.getPdfFontName(),
1434                pdfFont.getPdfEncoding(),
1435                pdfFont.isPdfEmbedded(),
1436                jrFont.getFontSize(),
1437                (pdfFont.isPdfSimulatedBold() ? Font.BOLD : 0)
1438                    | (pdfFont.isPdfSimulatedItalic() ? Font.ITALIC : 0)
1439                    | (jrFont.isUnderline() ? Font.UNDERLINE : 0)
1440                    | (jrFont.isStrikeThrough() ? Font.STRIKETHRU : 0),
1441                forecolor
1442                );
1443            
1444            // check if FontFactory didn't find the font
1445
if (font.getBaseFont() == null && font.family() == Font.UNDEFINED)
1446            {
1447                font = null;
1448            }
1449        }
1450        catch(Exception JavaDoc e)
1451        {
1452            initialException = e;
1453        }
1454
1455        if (font == null)
1456        {
1457            byte[] bytes = null;
1458
1459            try
1460            {
1461                bytes = JRLoader.loadBytesFromLocation(pdfFont.getPdfFontName(), classLoader, urlHandlerFactory);
1462            }
1463            catch(JRException e)
1464            {
1465                throw
1466                    new JRRuntimeException(
1467                        "Could not load the following font : "
1468                        + "\npdfFontName : " + pdfFont.getPdfFontName()
1469                        + "\npdfEncoding : " + pdfFont.getPdfEncoding()
1470                        + "\nisPdfEmbedded : " + pdfFont.isPdfEmbedded(),
1471                        initialException
1472                        );
1473            }
1474
1475            BaseFont baseFont = null;
1476            
1477            try
1478            {
1479                baseFont =
1480                    BaseFont.createFont(
1481                        pdfFont.getPdfFontName(),
1482                        pdfFont.getPdfEncoding(),
1483                        pdfFont.isPdfEmbedded(),
1484                        true,
1485                        bytes,
1486                        null
1487                        );
1488            }
1489            catch(DocumentException e)
1490            {
1491                throw new JRRuntimeException(e);
1492            }
1493            catch(IOException JavaDoc e)
1494            {
1495                throw new JRRuntimeException(e);
1496            }
1497            
1498            font =
1499                new Font(
1500                    baseFont,
1501                    jrFont.getFontSize(),
1502                    ((pdfFont.isPdfSimulatedBold()) ? Font.BOLD : 0)
1503                        | ((pdfFont.isPdfSimulatedItalic()) ? Font.ITALIC : 0)
1504                        | (jrFont.isUnderline() ? Font.UNDERLINE : 0)
1505                        | (jrFont.isStrikeThrough() ? Font.STRIKETHRU : 0),
1506                    forecolor
1507                    );
1508        }
1509        
1510        return font;
1511    }
1512
1513
1514    /**
1515     *
1516     */

1517    protected void exportText(JRPrintText text) throws DocumentException
1518    {
1519        JRStyledText styledText = getStyledText(text, false);
1520
1521        if (styledText == null)
1522        {
1523            return;
1524        }
1525
1526        int textLength = styledText.length();
1527        
1528        int x = text.getX() + getOffsetX();
1529        int y = text.getY() + getOffsetY();
1530        int width = text.getWidth();
1531        int height = text.getHeight();
1532        int topPadding = text.getTopPadding();
1533        int leftPadding = text.getLeftPadding();
1534        int bottomPadding = text.getBottomPadding();
1535        int rightPadding = text.getRightPadding();
1536
1537        int xFillCorrection = 0;
1538        int yFillCorrection = 0;
1539        
1540        double angle = 0;
1541        
1542        switch (text.getRotation())
1543        {
1544            case JRTextElement.ROTATION_LEFT :
1545            {
1546                y = text.getY() + getOffsetY() + text.getHeight();
1547                xFillCorrection = 1;
1548                width = text.getHeight();
1549                height = text.getWidth();
1550                int tmpPadding = topPadding;
1551                topPadding = leftPadding;
1552                leftPadding = bottomPadding;
1553                bottomPadding = rightPadding;
1554                rightPadding = tmpPadding;
1555                angle = Math.PI / 2;
1556                break;
1557            }
1558            case JRTextElement.ROTATION_RIGHT :
1559            {
1560                x = text.getX() + getOffsetX() + text.getWidth();
1561                yFillCorrection = -1;
1562                width = text.getHeight();
1563                height = text.getWidth();
1564                int tmpPadding = topPadding;
1565                topPadding = rightPadding;
1566                rightPadding = bottomPadding;
1567                bottomPadding = leftPadding;
1568                leftPadding = tmpPadding;
1569                angle = - Math.PI / 2;
1570                break;
1571            }
1572            case JRTextElement.ROTATION_UPSIDE_DOWN :
1573            {
1574                x = text.getX() + getOffsetX() + text.getWidth();
1575                y = text.getY() + getOffsetY() + text.getHeight();
1576                int tmpPadding = topPadding;
1577                topPadding = bottomPadding;
1578                bottomPadding = tmpPadding;
1579                tmpPadding = leftPadding;
1580                leftPadding = rightPadding;
1581                rightPadding = tmpPadding;
1582                angle = Math.PI;
1583                break;
1584            }
1585            case JRTextElement.ROTATION_NONE :
1586            default :
1587            {
1588            }
1589        }
1590        
1591        AffineTransform JavaDoc atrans = new AffineTransform JavaDoc();
1592        atrans.rotate(angle, x, jasperPrint.getPageHeight() - y);
1593        pdfContentByte.transform(atrans);
1594        
1595        if (text.getMode() == JRElement.MODE_OPAQUE)
1596        {
1597            Color JavaDoc backcolor = text.getBackcolor();
1598            pdfContentByte.setRGBColorStroke(
1599                backcolor.getRed(),
1600                backcolor.getGreen(),
1601                backcolor.getBlue()
1602                );
1603            pdfContentByte.setRGBColorFill(
1604                backcolor.getRed(),
1605                backcolor.getGreen(),
1606                backcolor.getBlue()
1607                );
1608            pdfContentByte.setLineWidth(1f);
1609            pdfContentByte.setLineDash(0f);
1610            pdfContentByte.rectangle(
1611                x + xFillCorrection,
1612                jasperPrint.getPageHeight() - y + yFillCorrection,
1613                width - 1,
1614                - height + 1
1615                );
1616            pdfContentByte.fillStroke();
1617        }
1618        else
1619        {
1620            /*
1621            pdfContentByte.setRGBColorStroke(
1622                text.getForecolor().getRed(),
1623                text.getForecolor().getGreen(),
1624                text.getForecolor().getBlue()
1625                );
1626            pdfContentByte.setLineWidth(0.1f);
1627            pdfContentByte.setLineDash(0f);
1628            pdfContentByte.rectangle(
1629                text.getX() + offsetX,
1630                jasperPrint.getPageHeight() - text.getY() - offsetY,
1631                text.getWidth(),
1632                - text.getHeight()
1633                );
1634            pdfContentByte.stroke();
1635            */

1636        }
1637
1638        if (textLength > 0)
1639        {
1640            int horizontalAlignment = Element.ALIGN_LEFT;
1641            switch (text.getHorizontalAlignment())
1642            {
1643                case JRAlignment.HORIZONTAL_ALIGN_LEFT :
1644                {
1645                    if (text.getRunDirection() == JRPrintText.RUN_DIRECTION_LTR)
1646                    {
1647                        horizontalAlignment = Element.ALIGN_LEFT;
1648                    }
1649                    else
1650                    {
1651                        horizontalAlignment = Element.ALIGN_RIGHT;
1652                    }
1653                    break;
1654                }
1655                case JRAlignment.HORIZONTAL_ALIGN_CENTER :
1656                {
1657                    horizontalAlignment = Element.ALIGN_CENTER;
1658                    break;
1659                }
1660                case JRAlignment.HORIZONTAL_ALIGN_RIGHT :
1661                {
1662                    if (text.getRunDirection() == JRPrintText.RUN_DIRECTION_LTR)
1663                    {
1664                        horizontalAlignment = Element.ALIGN_RIGHT;
1665                    }
1666                    else
1667                    {
1668                        horizontalAlignment = Element.ALIGN_LEFT;
1669                    }
1670                    break;
1671                }
1672                case JRAlignment.HORIZONTAL_ALIGN_JUSTIFIED :
1673                {
1674                    horizontalAlignment = Element.ALIGN_JUSTIFIED;
1675                    break;
1676                }
1677                default :
1678                {
1679                    horizontalAlignment = Element.ALIGN_LEFT;
1680                }
1681            }
1682
1683            float verticalOffset = 0f;
1684            switch (text.getVerticalAlignment())
1685            {
1686                case JRAlignment.VERTICAL_ALIGN_TOP :
1687                {
1688                    verticalOffset = 0f;
1689                    break;
1690                }
1691                case JRAlignment.VERTICAL_ALIGN_MIDDLE :
1692                {
1693                    verticalOffset = (height - topPadding - bottomPadding - text.getTextHeight()) / 2f;
1694                    break;
1695                }
1696                case JRAlignment.VERTICAL_ALIGN_BOTTOM :
1697                {
1698                    verticalOffset = height - topPadding - bottomPadding - text.getTextHeight();
1699                    break;
1700                }
1701                default :
1702                {
1703                    verticalOffset = 0f;
1704                }
1705            }
1706
1707            ColumnText colText = new ColumnText(pdfContentByte);
1708            colText.setSimpleColumn(
1709                getPhrase(styledText, text),
1710                x + leftPadding,
1711                jasperPrint.getPageHeight()
1712                    - y
1713                    - topPadding
1714                    - verticalOffset
1715                    - text.getLeadingOffset(),
1716                    //+ text.getLineSpacingFactor() * text.getFont().getSize(),
1717
x + width - rightPadding,
1718                jasperPrint.getPageHeight()
1719                    - y
1720                    - height
1721                    + bottomPadding,
1722                0,//text.getLineSpacingFactor(),// * text.getFont().getSize(),
1723
horizontalAlignment
1724                );
1725
1726            colText.setLeading(0, text.getLineSpacingFactor());// * text.getFont().getSize());
1727
colText.setRunDirection(
1728                text.getRunDirection() == JRPrintText.RUN_DIRECTION_LTR
1729                ? PdfWriter.RUN_DIRECTION_LTR : PdfWriter.RUN_DIRECTION_RTL
1730                );
1731
1732            colText.go();
1733        }
1734
1735        atrans = new AffineTransform JavaDoc();
1736        atrans.rotate(-angle, x, jasperPrint.getPageHeight() - y);
1737        pdfContentByte.transform(atrans);
1738
1739        /* */
1740        exportBox(
1741            text,
1742            text
1743            );
1744    }
1745
1746        
1747    /**
1748     *
1749     */

1750    protected void exportBox(JRBox box, JRPrintElement element)
1751    {
1752        if (box.getTopBorder() != JRGraphicElement.PEN_NONE)
1753        {
1754            float borderCorrection = prepareBorder(pdfContentByte, box.getTopBorder());
1755            Color JavaDoc color = box.getTopBorderColor() == null ? element.getForecolor() : box.getTopBorderColor();
1756            pdfContentByte.setRGBColorStroke(
1757                    color.getRed(),
1758                    color.getGreen(),
1759                    color.getBlue()
1760                    );
1761            pdfContentByte.moveTo(
1762                element.getX() + getOffsetX() - borderCorrection,
1763                jasperPrint.getPageHeight() - element.getY() - getOffsetY() + borderCorrection
1764                );
1765            pdfContentByte.lineTo(
1766                element.getX() + getOffsetX() + element.getWidth() - 1 + borderCorrection,
1767                jasperPrint.getPageHeight() - element.getY() - getOffsetY() + borderCorrection
1768                );
1769            pdfContentByte.stroke();
1770        }
1771
1772        if (box.getLeftBorder() != JRGraphicElement.PEN_NONE)
1773        {
1774            float borderCorrection = prepareBorder(pdfContentByte, box.getLeftBorder());
1775            Color JavaDoc color = box.getLeftBorderColor() == null ? element.getForecolor() : box.getLeftBorderColor();
1776            pdfContentByte.setRGBColorStroke(
1777                    color.getRed(),
1778                    color.getGreen(),
1779                    color.getBlue()
1780                    );
1781            pdfContentByte.moveTo(
1782                element.getX() + getOffsetX() - borderCorrection,
1783                jasperPrint.getPageHeight() - element.getY() - getOffsetY() + borderCorrection
1784                );
1785            pdfContentByte.lineTo(
1786                element.getX() + getOffsetX() - borderCorrection,
1787                jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() + 1 - borderCorrection
1788                );
1789            pdfContentByte.stroke();
1790        }
1791
1792        if (box.getBottomBorder() != JRGraphicElement.PEN_NONE)
1793        {
1794            float borderCorrection = prepareBorder(pdfContentByte, box.getBottomBorder());
1795            Color JavaDoc color = box.getBottomBorderColor() == null ? element.getForecolor() : box.getBottomBorderColor();
1796            pdfContentByte.setRGBColorStroke(
1797                    color.getRed(),
1798                    color.getGreen(),
1799                    color.getBlue()
1800                    );
1801            pdfContentByte.moveTo(
1802                element.getX() + getOffsetX() - borderCorrection,
1803                jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() + 1 - borderCorrection
1804                );
1805            pdfContentByte.lineTo(
1806                element.getX() + getOffsetX() + element.getWidth() - 1 + borderCorrection,
1807                jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() + 1 - borderCorrection
1808                );
1809            pdfContentByte.stroke();
1810        }
1811
1812        if (box.getRightBorder() != JRGraphicElement.PEN_NONE)
1813        {
1814            float borderCorrection = prepareBorder(pdfContentByte, box.getRightBorder());
1815            Color JavaDoc color = box.getRightBorderColor() == null ? element.getForecolor() : box.getRightBorderColor();
1816            pdfContentByte.setRGBColorStroke(
1817                    color.getRed(),
1818                    color.getGreen(),
1819                    color.getBlue()
1820                    );
1821            pdfContentByte.moveTo(
1822                element.getX() + getOffsetX() + element.getWidth() - 1 + borderCorrection,
1823                jasperPrint.getPageHeight() - element.getY() - getOffsetY() + borderCorrection
1824                );
1825            pdfContentByte.lineTo(
1826                element.getX() + getOffsetX() + element.getWidth() - 1 + borderCorrection,
1827                jasperPrint.getPageHeight() - element.getY() - getOffsetY() - element.getHeight() + 1 - borderCorrection
1828                );
1829            pdfContentByte.stroke();
1830        }
1831        
1832        pdfContentByte.setLineDash(0f);
1833    }
1834
1835        
1836    /**
1837     *
1838     */

1839    private static float prepareBorder(PdfContentByte pdfContentByte, byte border)
1840    {
1841        float borderCorrection = 0f;
1842        
1843        switch (border)
1844        {
1845            case JRGraphicElement.PEN_DOTTED :
1846            {
1847                borderCorrection = 0f;
1848                pdfContentByte.setLineWidth(1f);
1849                pdfContentByte.setLineDash(5f, 3f, 0f);
1850                break;
1851            }
1852            case JRGraphicElement.PEN_4_POINT :
1853            {
1854                borderCorrection = .5f;
1855                pdfContentByte.setLineWidth(4f);
1856                pdfContentByte.setLineDash(0f);
1857                break;
1858            }
1859            case JRGraphicElement.PEN_2_POINT :
1860            {
1861                borderCorrection = .5f;
1862                pdfContentByte.setLineWidth(2f);
1863                pdfContentByte.setLineDash(0f);
1864                break;
1865            }
1866            case JRGraphicElement.PEN_THIN :
1867            {
1868                borderCorrection = 0.25f;
1869                pdfContentByte.setLineWidth(0.5f);
1870                pdfContentByte.setLineDash(0f);
1871                break;
1872            }
1873            case JRGraphicElement.PEN_NONE :
1874            {
1875                borderCorrection = 0.5f;
1876                pdfContentByte.setLineWidth(1f);
1877                pdfContentByte.setLineDash(0f);
1878                break;
1879            }
1880            case JRGraphicElement.PEN_1_POINT :
1881            default :
1882            {
1883                borderCorrection = 0f;
1884                pdfContentByte.setLineWidth(1f);
1885                pdfContentByte.setLineDash(0f);
1886                break;
1887            }
1888        }
1889        
1890        return borderCorrection;
1891    }
1892
1893
1894    protected static synchronized void registerFonts ()
1895    {
1896        if (!fontsRegistered)
1897        {
1898            List JavaDoc fontFiles = JRProperties.getProperties(JRProperties.PDF_FONT_FILES_PREFIX);
1899            if (!fontFiles.isEmpty())
1900            {
1901                for (Iterator JavaDoc i = fontFiles.iterator(); i.hasNext();)
1902                {
1903                    JRProperties.PropertySuffix font = (JRProperties.PropertySuffix) i.next();
1904                    String JavaDoc file = font.getValue();
1905                    if (file.toLowerCase().endsWith(".ttc"))
1906                    {
1907                        FontFactory.register(file);
1908                    }
1909                    else
1910                    {
1911                        String JavaDoc alias = font.getSuffix();
1912                        FontFactory.register(file, alias);
1913                    }
1914                }
1915            }
1916            
1917            List JavaDoc fontDirs = JRProperties.getProperties(JRProperties.PDF_FONT_DIRS_PREFIX);
1918            if (!fontDirs.isEmpty())
1919            {
1920                for (Iterator JavaDoc i = fontDirs.iterator(); i.hasNext();)
1921                {
1922                    JRProperties.PropertySuffix dir = (JRProperties.PropertySuffix) i.next();
1923                    FontFactory.registerDirectory(dir.getValue());
1924                }
1925            }
1926            
1927            fontsRegistered = true;
1928        }
1929    }
1930
1931    
1932    static protected class Bookmark
1933    {
1934        final PdfOutline pdfOutline;
1935        final int level;
1936        
1937        Bookmark(Bookmark parent, int x, int top, String JavaDoc title)
1938        {
1939            this(parent, new PdfDestination(PdfDestination.XYZ, x, top, 0), title);
1940        }
1941        
1942        Bookmark(Bookmark parent, PdfDestination destination, String JavaDoc title)
1943        {
1944            this.pdfOutline = new PdfOutline(parent.pdfOutline, destination, title, false);
1945            this.level = parent.level + 1;
1946        }
1947        
1948        Bookmark(PdfOutline pdfOutline, int level)
1949        {
1950            this.pdfOutline = pdfOutline;
1951            this.level = level;
1952        }
1953    }
1954    
1955    static protected class BookmarkStack
1956    {
1957        LinkedList JavaDoc stack;
1958        
1959        BookmarkStack()
1960        {
1961            stack = new LinkedList JavaDoc();
1962        }
1963        
1964        void push(Bookmark bookmark)
1965        {
1966            stack.add(bookmark);
1967        }
1968        
1969        Bookmark pop()
1970        {
1971            return (Bookmark) stack.removeLast();
1972        }
1973        
1974        Bookmark peek()
1975        {
1976            return (Bookmark) stack.getLast();
1977        }
1978    }
1979    
1980
1981    protected void initBookmarks()
1982    {
1983        bookmarkStack = new BookmarkStack();
1984        
1985        int rootLevel = isModeBatch && isCreatingBatchModeBookmarks ? -1 : 0;
1986        Bookmark bookmark = new Bookmark(pdfContentByte.getRootOutline(), rootLevel);
1987        bookmarkStack.push(bookmark);
1988    }
1989    
1990    
1991    protected void addBookmark(int level, String JavaDoc title, int x, int y)
1992    {
1993        Bookmark parent = bookmarkStack.peek();
1994        while(parent.level > level - 1)
1995        {
1996            bookmarkStack.pop();
1997            parent = bookmarkStack.peek();
1998        }
1999        
2000        for (int i = parent.level + 1; i < level; ++i)
2001        {
2002            Bookmark emptyBookmark = new Bookmark(parent, parent.pdfOutline.getPdfDestination(), EMPTY_BOOKMARK_TITLE);
2003            bookmarkStack.push(emptyBookmark);
2004            parent = emptyBookmark;
2005        }
2006
2007        Bookmark bookmark = new Bookmark(parent, x, jasperPrint.getPageHeight() - y, title);
2008        bookmarkStack.push(bookmark);
2009    }
2010
2011    
2012    protected void setAnchor(Chunk chunk, JRPrintAnchor anchor, JRPrintElement element)
2013    {
2014        String JavaDoc anchorName = anchor.getAnchorName();
2015        if (anchorName != null)
2016        {
2017            chunk.setLocalDestination(anchorName);
2018            
2019            if (anchor.getBookmarkLevel() != JRAnchor.NO_BOOKMARK)
2020            {
2021                addBookmark(anchor.getBookmarkLevel(), anchor.getAnchorName(), element.getX(), element.getY());
2022            }
2023        }
2024    }
2025    
2026
2027    protected void exportFrame(JRPrintFrame frame) throws DocumentException, IOException JavaDoc, JRException
2028    {
2029        if (frame.getMode() == JRElement.MODE_OPAQUE)
2030        {
2031            int x = frame.getX() + getOffsetX();
2032            int y = frame.getY() + getOffsetY();
2033            
2034            Color JavaDoc backcolor = frame.getBackcolor();
2035            pdfContentByte.setRGBColorStroke(
2036                    backcolor.getRed(),
2037                    backcolor.getGreen(),
2038                    backcolor.getBlue()
2039                    );
2040                pdfContentByte.setRGBColorFill(
2041                    backcolor.getRed(),
2042                    backcolor.getGreen(),
2043                    backcolor.getBlue()
2044                    );
2045                pdfContentByte.setLineWidth(1f);
2046                pdfContentByte.setLineDash(0f);
2047                pdfContentByte.rectangle(
2048                    x,
2049                    jasperPrint.getPageHeight() - y,
2050                    frame.getWidth() - 1,
2051                    - frame.getHeight() + 1
2052                    );
2053                pdfContentByte.fillStroke();
2054        }
2055        
2056        setFrameElementsOffset(frame, false);
2057        try
2058        {
2059            exportElements(frame.getElements());
2060        }
2061        finally
2062        {
2063            restoreElementOffsets();
2064        }
2065        
2066        exportBox(frame, frame);
2067    }
2068    
2069    
2070    /**
2071     * Output stream implementation that discards all the data.
2072     */

2073    public static class NullOutputStream extends OutputStream JavaDoc
2074    {
2075        public NullOutputStream()
2076        {
2077        }
2078
2079        public void write(int b)
2080        {
2081            // discard the data
2082
}
2083
2084        public void write(byte[] b, int off, int len)
2085        {
2086            // discard the data
2087
}
2088
2089        public void write(byte[] b)
2090        {
2091            // discard the data
2092
}
2093    }
2094    
2095    
2096    /**
2097     *
2098     */

2099    class LocalFontMapper implements FontMapper
2100    {
2101        public LocalFontMapper()
2102        {
2103        }
2104
2105        public BaseFont awtToPdf(java.awt.Font JavaDoc font)
2106        {
2107            return getFont(font.getAttributes()).getBaseFont();
2108        }
2109        
2110        public java.awt.Font JavaDoc pdfToAwt(BaseFont font, int size)
2111        {
2112            return null;
2113        }
2114    }
2115}
2116
Popular Tags