1 /*
2  * JasperReports - Free Java Reporting Library.
3  * Copyright (C) 2001 - 2019 TIBCO Software Inc. All rights reserved.
4  * http://www.jaspersoft.com
5  *
6  * Unless you have purchased a commercial license agreement from Jaspersoft,
7  * the following license terms apply:
8  *
9  * This program is part of JasperReports.
10  *
11  * JasperReports is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * JasperReports is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with JasperReports. If not, see <http://www.gnu.org/licenses/>.
23  */

24
25 /*
26  * Contributors:
27  * Adrian Jackson - iapetus@users.sourceforge.net
28  * David Taylor - exodussystems@users.sourceforge.net
29  * Lars Kristensen - llk@users.sourceforge.net
30  * Ling Li - lonecatz@users.sourceforge.net
31  * Martin Clough - mtclough@users.sourceforge.net
32  */

33 package net.sf.jasperreports.engine.export;
34
35 import java.awt.Color;
36 import java.awt.Graphics2D;
37 import java.awt.font.TextAttribute;
38 import java.awt.geom.AffineTransform;
39 import java.awt.geom.Dimension2D;
40 import java.awt.image.BufferedImage;
41 import java.io.IOException;
42 import java.io.InputStream;
43 import java.io.OutputStream;
44 import java.text.AttributedCharacterIterator;
45 import java.text.AttributedCharacterIterator.Attribute;
46 import java.text.AttributedString;
47 import java.util.ArrayList;
48 import java.util.Collection;
49 import java.util.HashMap;
50 import java.util.Iterator;
51 import java.util.LinkedList;
52 import java.util.List;
53 import java.util.Locale;
54 import java.util.Map;
55
56 import org.apache.commons.logging.Log;
57 import org.apache.commons.logging.LogFactory;
58
59 import com.ibm.icu.util.StringTokenizer;
60 import com.lowagie.text.FontFactory;
61 import com.lowagie.text.pdf.PdfWriter;
62
63 import net.sf.jasperreports.annotations.properties.Property;
64 import net.sf.jasperreports.annotations.properties.PropertyScope;
65 import net.sf.jasperreports.engine.DefaultJasperReportsContext;
66 import net.sf.jasperreports.engine.JRAbstractExporter;
67 import net.sf.jasperreports.engine.JRAnchor;
68 import net.sf.jasperreports.engine.JRBoxContainer;
69 import net.sf.jasperreports.engine.JRCommonGraphicElement;
70 import net.sf.jasperreports.engine.JRException;
71 import net.sf.jasperreports.engine.JRFont;
72 import net.sf.jasperreports.engine.JRGenericElementType;
73 import net.sf.jasperreports.engine.JRGenericPrintElement;
74 import net.sf.jasperreports.engine.JRLineBox;
75 import net.sf.jasperreports.engine.JRPen;
76 import net.sf.jasperreports.engine.JRPrintAnchor;
77 import net.sf.jasperreports.engine.JRPrintElement;
78 import net.sf.jasperreports.engine.JRPrintEllipse;
79 import net.sf.jasperreports.engine.JRPrintFrame;
80 import net.sf.jasperreports.engine.JRPrintHyperlink;
81 import net.sf.jasperreports.engine.JRPrintImage;
82 import net.sf.jasperreports.engine.JRPrintLine;
83 import net.sf.jasperreports.engine.JRPrintPage;
84 import net.sf.jasperreports.engine.JRPrintRectangle;
85 import net.sf.jasperreports.engine.JRPrintText;
86 import net.sf.jasperreports.engine.JRPropertiesUtil;
87 import net.sf.jasperreports.engine.JRPropertiesUtil.PropertySuffix;
88 import net.sf.jasperreports.engine.JRRuntimeException;
89 import net.sf.jasperreports.engine.JasperPrint;
90 import net.sf.jasperreports.engine.JasperReportsContext;
91 import net.sf.jasperreports.engine.PrintPageFormat;
92 import net.sf.jasperreports.engine.base.JRBaseFont;
93 import net.sf.jasperreports.engine.base.JRBasePen;
94 import net.sf.jasperreports.engine.base.JRBasePrintText;
95 import net.sf.jasperreports.engine.export.type.PdfFieldBorderStyleEnum;
96 import net.sf.jasperreports.engine.export.type.PdfFieldCheckTypeEnum;
97 import net.sf.jasperreports.engine.export.type.PdfFieldTypeEnum;
98 import net.sf.jasperreports.engine.fonts.FontFace;
99 import net.sf.jasperreports.engine.fonts.FontFamily;
100 import net.sf.jasperreports.engine.fonts.FontInfo;
101 import net.sf.jasperreports.engine.fonts.FontUtil;
102 import net.sf.jasperreports.engine.type.HyperlinkTypeEnum;
103 import net.sf.jasperreports.engine.type.LineDirectionEnum;
104 import net.sf.jasperreports.engine.type.LineStyleEnum;
105 import net.sf.jasperreports.engine.type.ModeEnum;
106 import net.sf.jasperreports.engine.type.OrientationEnum;
107 import net.sf.jasperreports.engine.util.ImageUtil;
108 import net.sf.jasperreports.engine.util.JRImageLoader;
109 import net.sf.jasperreports.engine.util.JRLoader;
110 import net.sf.jasperreports.engine.util.JRPdfaIccProfileNotFoundException;
111 import net.sf.jasperreports.engine.util.JRSingletonCache;
112 import net.sf.jasperreports.engine.util.JRStyledText;
113 import net.sf.jasperreports.engine.util.JRStyledTextUtil;
114 import net.sf.jasperreports.engine.util.JRTextAttribute;
115 import net.sf.jasperreports.export.ExportInterruptedException;
116 import net.sf.jasperreports.export.ExporterInputItem;
117 import net.sf.jasperreports.export.OutputStreamExporterOutput;
118 import net.sf.jasperreports.export.PdfExporterConfiguration;
119 import net.sf.jasperreports.export.PdfReportConfiguration;
120 import net.sf.jasperreports.export.pdf.FontRecipient;
121 import net.sf.jasperreports.export.pdf.LineCapStyle;
122 import net.sf.jasperreports.export.pdf.PdfChunk;
123 import net.sf.jasperreports.export.pdf.PdfContent;
124 import net.sf.jasperreports.export.pdf.PdfDocument;
125 import net.sf.jasperreports.export.pdf.PdfDocumentWriter;
126 import net.sf.jasperreports.export.pdf.PdfFontStyle;
127 import net.sf.jasperreports.export.pdf.PdfImage;
128 import net.sf.jasperreports.export.pdf.PdfOutlineEntry;
129 import net.sf.jasperreports.export.pdf.PdfPhrase;
130 import net.sf.jasperreports.export.pdf.PdfProducer;
131 import net.sf.jasperreports.export.pdf.PdfProducerContext;
132 import net.sf.jasperreports.export.pdf.PdfProducerFactory;
133 import net.sf.jasperreports.export.pdf.PdfRadioCheck;
134 import net.sf.jasperreports.export.pdf.PdfTextAlignment;
135 import net.sf.jasperreports.export.pdf.PdfTextChunk;
136 import net.sf.jasperreports.export.pdf.PdfTextField;
137 import net.sf.jasperreports.export.pdf.TextDirection;
138 import net.sf.jasperreports.export.pdf.classic.ClassicPdfProducer;
139 import net.sf.jasperreports.export.type.PdfPermissionsEnum;
140 import net.sf.jasperreports.export.type.PdfPrintScalingEnum;
141 import net.sf.jasperreports.export.type.PdfVersionEnum;
142 import net.sf.jasperreports.export.type.PdfaConformanceEnum;
143 import net.sf.jasperreports.properties.PropertyConstants;
144 import net.sf.jasperreports.renderers.DataRenderable;
145 import net.sf.jasperreports.renderers.DimensionRenderable;
146 import net.sf.jasperreports.renderers.Graphics2DRenderable;
147 import net.sf.jasperreports.renderers.Renderable;
148 import net.sf.jasperreports.renderers.RenderersCache;
149 import net.sf.jasperreports.renderers.ResourceRenderer;
150 import net.sf.jasperreports.renderers.WrappingImageDataToGraphics2DRenderer;
151 import net.sf.jasperreports.renderers.WrappingSvgDataToGraphics2DRenderer;
152 import net.sf.jasperreports.renderers.util.RendererUtil;
153 import net.sf.jasperreports.repo.RepositoryUtil;
154
155
156 /**
157  * Exports a JasperReports document to PDF format. It has binary output type and exports the document to
158  * a free-form layout.
159  * <p/>
160  * As its name indicates, PDF is a very precise and complex document format that ensures
161  * documents will look and print the same on all platforms.
162  * This is why the PDF exporter implemented by the
163  * {@link net.sf.jasperreports.engine.export.JRPdfExporter} class in JasperReports is
164  * one of the best exporters. The output it produces is almost of the same quality as that
165  * produced by the {@link net.sf.jasperreports.engine.export.JRGraphics2DExporter},
166  * which is always the reference.
167  * <p/>
168  * The {@link net.sf.jasperreports.engine.export.JRPdfExporter} implementation uses iText, 
169  * which is a specialized PDF-generating library. PDF is a binary document format that allows 
170  * absolute positioning of the elements inside a page, so the existing PDF exporter does not 
171  * have the limitations of a grid exporter.
172  * <p/>
173  * It also works very well in batch mode because it allows concatenation of multiple
174  * documents within the same PDF file, even if the files have different page sizes.
175  * <h3>Font Mappings</h3>
176  * Exporting to PDF requires mapping the fonts using three attributes: 
177  * <code>pdfFontName</code>, <code>pdfEncoding</code> and <code>isPdfEmbedded</code>. 
178  * Even though these three attributes are still supported in JRXML and
179  * the API, we recommend making the PDF font mappings at export time using font
180  * extensions. 
181  * <p/>
182  * When exporting documents to PDF, for each combination of the three <code>fontName</code>,
183  * <code>isBold</code>, and <code>isItalic</code> font attributes, there must be an equivalent 
184  * combination of the PDF-related font attributes <code>pdfFontName</code>, <code>pdfEncoding</code> 
185  * and <code>isPdfEmbedded</code>.
186  * <p/>
187  * <i>Equivalent combination</i> means one that causes the text elements to be rendered exactly
188  * the same (or at least as closely as possible) in PDF and the built-in Graphics2D
189  * exporter, which is the reference.
190  * <p/>
191  * In some cases, there is no font file available to use with the pdfFontName attribute in
192  * order to render bold and italic text exactly like the Graphics2D exporter renders it in
193  * AWT. Those fonts might only have a normal style variant and no variants for bold and
194  * italic. In such cases, the PDF exporter (the iText library, to be more precise) is able to
195  * simulate those styles by applying transformations to the normal font glyphs. The 
196  * {@link net.sf.jasperreports.engine.export.JRPdfExporter} internally acquires the needed PDF 
197  * font based on the font extension mechanism (see the <code>getFont(Map, Locale, boolean)</code>
198  * method.
199  * <h3>Batch Mode Bookmarks</h3>
200  * When several JasperPrint documents must be concatenated in the same PDF file by
201  * batch export, one can introduce PDF bookmarks in the resulting PDF document to mark
202  * the beginning of each individual document that was part of the initial document list.
203  * <p/>
204  * These bookmarks have the same name as the original JasperPrint document as
205  * specified by the <code>jasperPrint.getName()</code> property. However, users can turn on and off
206  * the creation of those bookmarks by turning on or off the 
207  * {@link net.sf.jasperreports.export.PdfExporterConfiguration#isCreatingBatchModeBookmarks() isCreatingBatchModeBookmarks()}
208  * exporter configuration setting. The exporter does not create such bookmarks by default.
209  * <h3>Encrypted PDF</h3>
210  * In some cases, users might want to encrypt the PDF documents generated by
211  * JasperReports so that only authorized viewers can have access to those documents.
212  * There are five exporter configuration settings for this (see {@link net.sf.jasperreports.export.PdfExporterConfiguration}):
213  * <ul>
214  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#isEncrypted() isEncrypted()}</li>
215  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#is128BitKey() is128BitKey()}</li>
216  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#getUserPassword() getUserPassword()}</li>
217  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#getOwnerPassword() getOwnerPassword()}</li>
218  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#getPermissions() getPermissions()}</li>
219  * </ul>
220  * <h3>PDF Version and Compression</h3>
221  * Some applications require marking the generated files with a particular PDF
222  * specifications version. Related export configuration settings are the following
223  * (see {@link net.sf.jasperreports.export.PdfExporterConfiguration}):
224  * <ul>
225  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#getPdfVersion() getPdfVersion()}</li>
226  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#isCompressed() isCompressed()}</li>
227  * </ul>
228  * Since version 1.5, the PDF format supports compression. By default, the PDF exporter in
229  * JasperReports does not create compressed PDF documents, but this feature can be turned
230  * on using the {@link net.sf.jasperreports.export.PdfExporterConfiguration#isCompressed() isCompressed()} 
231  * exporter configuration setting. Note that because compressed PDFs
232  * are available only since PDF version 1.5, the PDF version of the resulting document is
233  * set to 1.5 automatically if compression is turned on.
234  * <h3>Word Wrap and Line Break Policy</h3>
235  * By default, the PDF exporter does not guarantee that text with the same style properties
236  * will be rendered exactly as it is using AWT. The word wrap and line break policy is
237  * slightly different, and in some cases it might cause portions of text to disappear at the
238  * end of longer text paragraphs.
239  * <p/>
240  * To make sure this does not happen, one can configure the PDF exporter to use the AWT
241  * word wrap and line break policy by setting the 
242  * {@link net.sf.jasperreports.export.PdfReportConfiguration#isForceLineBreakPolicy() isForceLineBreakPolicy()} 
243  * exporter configuration setting to true. Note that this feature is not turned on by default, because it affects the
244  * exporter performance. This default behavior that applies in the absence of the mentioned
245  * export parameter can be controlled using the
246  * {@link net.sf.jasperreports.export.PdfReportConfiguration#PROPERTY_FORCE_LINEBREAK_POLICY net.sf.jasperreports.export.pdf.force.linebreak.policy} configuration
247  * property
248  * <h3>JavaScript Actions</h3>
249  * The PDF specifications provide a means for the automation of various processes, such as
250  * the automatic printing of the document when it is opened. PDF viewer applications are
251  * able to execute Acrobat JavaScript code that is embedded in the PDF and associated with
252  * different events.
253  * <p/>
254  * JasperReports only allows inserting Acrobat JavaScript code. This code gets executed
255  * when the PDF document is opened in the viewer. This can be achieved using the
256  * {@link net.sf.jasperreports.export.PdfExporterConfiguration#getPdfJavaScript() getPdfJavaScript()} 
257  * configuration setting, which retrieve the Acrobat JavaScript source code. 
258  * Note that Acrobat JavaScript is a programming language based on JavaScript. More
259  * details about this can be found in the iText documentation.
260  * <h3>Metadata Information</h3>
261  * PDF documents can store metadata information such as the author of the document, its
262  * title, and keywords. JasperReports exposes this feature of PDF through special exporter
263  * configuration settings available in the {@link net.sf.jasperreports.export.PdfExporterConfiguration}
264  * class. They are all listed following:
265  * <ul>
266  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#getMetadataAuthor() getMetadataAuthor()}</li>
267  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#getMetadataCreator() getMetadataCreator()}</li>
268  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#getMetadataKeywords() getMetadataKeywords()}</li>
269  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#getMetadataSubject() getMetadataSubject()}</li>
270  * <li>{@link net.sf.jasperreports.export.PdfExporterConfiguration#getMetadataTitle() getMetadataTitle()}</li>
271  * </ul>
272  * <h3>Rendering SVG Using Shapes</h3>
273  * The {@link net.sf.jasperreports.export.PdfReportConfiguration#isForceSvgShapes() isForceSvgShapes()} 
274  * flag is used to force the rendering of SVG images using shapes on the PDF <code>Graphics2D</code> 
275  * context. This allows fonts to be rendered as shapes, thus avoiding any font mapping issues that 
276  * might cause Unicode text to not show up properly; however, it has the disadvantage of producing
277  * larger PDF files.
278  * <p/>
279  * By default, the flag is set to true, mainly due to backward-compatibility reasons. To
280  * reduce PDF file size for documents containing SVG images such as charts, this flag
281  * should be set to false. However, in such a case, the accuracy of the text content
282  * rendered by the SVG element in PDF depends on the correct PDF font information being
283  * available in the SVG implementation itself.
284  * <p/>
285  * In JasperReports, SVG elements are rendered using 
286  * {@link net.sf.jasperreports.renderers.Renderable} implementations,
287  * which are most likely subclasses of the {@link net.sf.jasperreports.renderers.AbstractRenderToImageDataRenderer} 
288  * class. SVG renderer implementations should be concerned only with
289  * implementing the 
290  * <p/>
291  * <code>public void render(JasperReportsContext jasperReportsContext, Graphics2D grx, Rectangle2D rectangle)</code> 
292  * <p/>
293  * method, which should contain all the code
294  * required for rendering the SVG on a Graphics2D context. Correct PDF font information
295  * means that the <code>java.awt.Font</code> objects used to draw text on the <code>Graphics2D</code> 
296  * context should have PDF-related text attributes embedded so that when rendered on a PDF
297  * <code>Graphics2D</code> context, the exporter can make use of them. Embedding PDF-related text
298  * attributes into the SVG means using the following text attributes when creating
299  * <code>java.awt.Font</code> to render text in the SVG renderer implementation:
300  * <ul>
301  * <li>{@link net.sf.jasperreports.engine.util.JRTextAttribute#PDF_FONT_NAME PDF_FONT_NAME}</li>
302  * <li>{@link net.sf.jasperreports.engine.util.JRTextAttribute#PDF_ENCODING PDF_ENCODING}</li>
303  * <li>{@link net.sf.jasperreports.engine.util.JRTextAttribute#IS_PDF_EMBEDDED IS_PDF_EMBEDDED}</li>
304  * </ul>
305  * <p/>
306  * The built-in chart component in JasperReports hides this complexity of dealing with
307  * fonts in a SVG renderer by exposing to the end user the usual three PDF-specific font
308  * attributes (<code>pdfFontName</code>, <code>pdfEncoding</code>, and <code>isPdfEmbedded</code>) 
309  * to be set along with the normal font attributes every time a font setting is made for the chart 
310  * title, subtitle, chart legend, or axis. This feature can be controlled system-wide using the
311  * {@link net.sf.jasperreports.export.PdfReportConfiguration#PROPERTY_FORCE_SVG_SHAPES net.sf.jasperreports.export.pdf.force.svg.shapes} configuration property.
312  * The {@link net.sf.jasperreports.export.PdfReportConfiguration#isForceSvgShapes() isForceSvgShapes()} 
313  * export configuration setting overrides the configuration property value, if present.
314  * <h3>Section 508 Compliance</h3>
315  * PDF files can contain hidden tags that describe the structure of the document. Some of
316  * the tags are used by the automated reader tool that reads PDF documents aloud to people
317  * with disabilities.
318  * <p/>
319  * The PDF tags feature of JasperReports allows adding hidden PDF tags to the files
320  * generated by the JasperReports PDF exporter. The resulting files comply with the
321  * requirements of the Section 508 of the U.S. Rehabilitation Act
322  * (<a href="http://www.section508.gov">http://www.section508.gov/</a>).
323  * <h3>Producing Tagged PDF Files</h3>
324  * By default, the JasperReports exporter does not put any hidden structural tags inside its
325  * generated PDF files. In order to turn on the creation of hidden structural tags, any of the
326  * following can be used:
327  * <ul>
328  * <li>setting to true the {@link net.sf.jasperreports.export.PdfExporterConfiguration#isTagged() isTagged()}
329  * configuration flag</li>
330  * <li>setting to true the {@link net.sf.jasperreports.export.PdfExporterConfiguration#PROPERTY_TAGGED net.sf.jasperreports.export.pdf.tagged} configuration property.</li>
331  * </ul>
332  * <h3>Setting the PDF File Language</h3>
333  * When a full accessibility check is requested from Acrobat Professional, among the things
334  * it determines is whether the PDF file or the various pieces of content inside it have a
335  * language set. JasperReports allows setting the language for the entire content by doing
336  * any one of the following:
337  * <ul>
338  * <li>using the {@link net.sf.jasperreports.export.PdfExporterConfiguration#getTagLanguage() getTagLanguage()}
339  * configuration setting to retrieve the language as a <code>java.lang.String</code> value;</li>
340  * <li>using the {@link net.sf.jasperreports.export.PdfExporterConfiguration#PROPERTY_TAG_LANGUAGE net.sf.jasperreports.export.pdf.tag.language} configuration property 
341  * globally or at report level</li>
342  * </ul>
343  * <h3>Alternate Text for Images</h3>
344  * In tagged PDF files, image elements can be described in alternate text that is read by the
345  * automated reader. The text is specified using the <code>hyperlinkTooltipExpression</code>
346  * property of the image element in JRXML.
347  * <p/>
348  * For more information about tagged PDF documents in JasperReports, 
349  * please consult the {@link net.sf.jasperreports.engine.export.JRPdfExporterTagHelper} class.
350  * 
351  * @see net.sf.jasperreports.export.PdfExporterConfiguration
352  * @see net.sf.jasperreports.export.PdfReportConfiguration
353  * @see net.sf.jasperreports.engine.util.JRTextAttribute#IS_PDF_EMBEDDED
354  * @see net.sf.jasperreports.engine.util.JRTextAttribute#PDF_ENCODING
355  * @see net.sf.jasperreports.engine.util.JRTextAttribute#PDF_FONT_NAME
356  * @author Teodor Danciu (teodord@users.sourceforge.net)
357  */

358 public class JRPdfExporter extends JRAbstractExporter<PdfReportConfiguration, PdfExporterConfiguration, OutputStreamExporterOutput, JRPdfExporterContext>
359 {
360
361     private static final Log log = LogFactory.getLog(JRPdfExporter.class);
362     
363     public static final String PDF_EXPORTER_PROPERTIES_PREFIX = JRPropertiesUtil.PROPERTY_PREFIX + "export.pdf.";
364
365     public static final String EXCEPTION_MESSAGE_KEY_DOCUMENT_ERROR = "export.pdf.document.error";
366     public static final String EXCEPTION_MESSAGE_KEY_FONT_LOADING_ERROR = "export.pdf.font.loading.error";
367     public static final String EXCEPTION_MESSAGE_KEY_REPORT_GENERATION_ERROR = "export.pdf.report.generation.error";
368     
369     /**
370      * Prefix of properties that specify font files for the PDF exporter.
371      */

372     @Property(
373         name = "net.sf.jasperreports.export.pdf.font.{arbitrary_name}",
374         category = PropertyConstants.CATEGORY_EXPORT,
375         scopes = {PropertyScope.GLOBAL},
376         sinceVersion = PropertyConstants.VERSION_1_0_0
377         )
378     public static final String PDF_FONT_FILES_PREFIX = PDF_EXPORTER_PROPERTIES_PREFIX + "font.";
379     
380     /**
381      * Prefix of properties that specify font directories for the PDF exporter.
382      */

383     @Property(
384         name = "net.sf.jasperreports.export.pdf.fontdir.{arbitrary_name}",
385         category = PropertyConstants.CATEGORY_EXPORT,
386         scopes = {PropertyScope.GLOBAL},
387         sinceVersion = PropertyConstants.VERSION_1_0_0
388         )
389     public static final String PDF_FONT_DIRS_PREFIX = PDF_EXPORTER_PROPERTIES_PREFIX + "fontdir.";
390     
391     /**
392      * 
393      */

394     @Property(
395         category = PropertyConstants.CATEGORY_EXPORT,
396         scopes = {PropertyScope.ELEMENT},
397         sinceVersion = PropertyConstants.VERSION_6_12_0,
398         valueType = PdfFieldTypeEnum.class
399         )
400     public static final String PDF_FIELD_TYPE = PDF_EXPORTER_PROPERTIES_PREFIX + "field.type";
401     
402     /**
403      * 
404      */

405     @Property(
406         category = PropertyConstants.CATEGORY_EXPORT,
407         scopes = {PropertyScope.ELEMENT},
408         sinceVersion = PropertyConstants.VERSION_6_12_0,
409         valueType = Boolean.class,
410         defaultValue = PropertyConstants.BOOLEAN_FALSE
411         )
412     public static final String PDF_FIELD_TEXT_MULTILINE = PDF_EXPORTER_PROPERTIES_PREFIX + "field.text.multiline";
413     
414     /**
415      * 
416      */

417     @Property(
418         category = PropertyConstants.CATEGORY_EXPORT,
419         scopes = {PropertyScope.ELEMENT},
420         sinceVersion = PropertyConstants.VERSION_6_12_0
421         )
422     public static final String PDF_FIELD_VALUE = PDF_EXPORTER_PROPERTIES_PREFIX + "field.value";
423     
424     /**
425      * 
426      */

427     @Property(
428         category = PropertyConstants.CATEGORY_EXPORT,
429         scopes = {PropertyScope.ELEMENT},
430         sinceVersion = PropertyConstants.VERSION_6_12_0,
431         valueType = PdfFieldCheckTypeEnum.class
432         )
433     public static final String PDF_FIELD_CHECK_TYPE = PDF_EXPORTER_PROPERTIES_PREFIX + "field.check.type";
434     
435     /**
436      * 
437      */

438     @Property(
439         category = PropertyConstants.CATEGORY_EXPORT,
440         scopes = {PropertyScope.ELEMENT},
441         sinceVersion = PropertyConstants.VERSION_6_12_0
442         )
443     public static final String PDF_FIELD_NAME = PDF_EXPORTER_PROPERTIES_PREFIX + "field.name";
444     
445     /**
446      * 
447      */

448     @Property(
449         category = PropertyConstants.CATEGORY_EXPORT,
450         scopes = {PropertyScope.ELEMENT},
451         sinceVersion = PropertyConstants.VERSION_6_12_0,
452         valueType = Boolean.class
453         )
454     public static final String PDF_FIELD_CHECKED = PDF_EXPORTER_PROPERTIES_PREFIX + "field.checked";
455     
456     /**
457      * 
458      */

459     @Property(
460         category = PropertyConstants.CATEGORY_EXPORT,
461         scopes = {PropertyScope.ELEMENT},
462         sinceVersion = PropertyConstants.VERSION_6_12_0,
463         valueType = Boolean.class
464         )
465     public static final String PDF_FIELD_READ_ONLY = PDF_EXPORTER_PROPERTIES_PREFIX + "field.read.only";
466     
467     /**
468      * 
469      */

470     @Property(
471         category = PropertyConstants.CATEGORY_EXPORT,
472         scopes = {PropertyScope.GLOBAL, PropertyScope.CONTEXT, PropertyScope.REPORT, PropertyScope.ELEMENT},
473         sinceVersion = PropertyConstants.VERSION_6_12_0,
474         valueType = PdfFieldBorderStyleEnum.class
475         )
476     public static final String PDF_FIELD_BORDER_STYLE = PDF_EXPORTER_PROPERTIES_PREFIX + "field.border.style";
477     
478     /**
479      * 
480      */

481     @Property(
482         category = PropertyConstants.CATEGORY_EXPORT,
483         defaultValue = "|",
484         scopes = {PropertyScope.GLOBAL, PropertyScope.CONTEXT, PropertyScope.REPORT, PropertyScope.ELEMENT},
485         sinceVersion = PropertyConstants.VERSION_6_12_0
486         )
487     public static final String PDF_FIELD_CHOICE_SEPARATORS = PDF_EXPORTER_PROPERTIES_PREFIX + "field.choice.separators";
488     
489     /**
490      * 
491      */

492     @Property(
493         category = PropertyConstants.CATEGORY_EXPORT,
494         scopes = {PropertyScope.ELEMENT},
495         sinceVersion = PropertyConstants.VERSION_6_12_0
496         )
497     public static final String PDF_FIELD_CHOICES = PDF_EXPORTER_PROPERTIES_PREFIX + "field.choices";
498     
499     /**
500      * 
501      */

502     @Property(
503         category = PropertyConstants.CATEGORY_EXPORT,
504         scopes = {PropertyScope.GLOBAL, PropertyScope.CONTEXT, PropertyScope.REPORT, PropertyScope.ELEMENT},
505         sinceVersion = PropertyConstants.VERSION_6_12_0,
506         valueType = Boolean.class,
507         defaultValue = PropertyConstants.BOOLEAN_FALSE
508         )
509     public static final String PDF_FIELD_COMBO_EDIT = PDF_EXPORTER_PROPERTIES_PREFIX + "field.combo.edit";
510     
511     /**
512      * The exporter key, as used in
513      * {@link GenericElementHandlerEnviroment#getElementHandler(JRGenericElementType, String)}.
514      */

515     public static final String PDF_EXPORTER_KEY = JRPropertiesUtil.PROPERTY_PREFIX + "pdf";
516     
517     public static final String PDF_PRODUCER_FACTORY_PROPERTY = PDF_EXPORTER_PROPERTIES_PREFIX + "producer.factory";
518     
519     private static final String EMPTY_BOOKMARK_TITLE = "";
520
521     /**
522      *
523      */

524     protected static final String JR_PAGE_ANCHOR_PREFIX = "JR_PAGE_ANCHOR_";
525
526     protected static boolean fontsRegistered;
527     
528     private static final JRSingletonCache<PdfProducerFactory> pdfProducerCache = 
529             new JRSingletonCache<>(PdfProducerFactory.class);
530
531     protected class ExporterContext extends BaseExporterContext implements JRPdfExporterContext
532     {
533         @Override
534         public PdfWriter getPdfWriter()
535         {
536             if (pdfProducer instanceof ClassicPdfProducer)
537             {
538                 return ((ClassicPdfProducer) pdfProducer).getPdfWriter();
539             }
540             
541             throw new JRRuntimeException("Not using the classic PDF producer");
542         }
543
544         @Override
545         public PdfProducer getPdfProducer()
546         {
547             return pdfProducer;
548         }
549     }
550     
551     /**
552      *
553      */

554     protected PdfProducer pdfProducer;
555     protected PdfContent pdfContent;
556     
557     protected JRPdfExporterTagHelper tagHelper = new JRPdfExporterTagHelper(this);
558
559     protected int reportIndex;
560     protected PrintPageFormat pageFormat;
561     protected int crtDocumentPageNumber;
562     
563     protected int permissions;
564
565     /**
566      *
567      */

568     protected RenderersCache renderersCache;
569     protected Map<String,PdfImage> loadedImagesMap;
570     protected PdfImage pxImage;
571
572     private BookmarkStack bookmarkStack;
573
574     private int crtOddPageOffsetX;
575     private int crtOddPageOffsetY;
576     private int crtEvenPageOffsetX;
577     private int crtEvenPageOffsetY;
578     
579     private boolean awtIgnoreMissingFont;
580     private boolean defaultIndentFirstLine;
581     private boolean defaultJustifyLastLine;
582
583     private PdfVersionEnum minimalVersion;
584
585     /**
586      * @see #JRPdfExporter(JasperReportsContext)
587      */

588     public JRPdfExporter()
589     {
590         this(DefaultJasperReportsContext.getInstance());
591     }
592
593     
594     /**
595      *
596      */

597     public JRPdfExporter(JasperReportsContext jasperReportsContext)
598     {
599         super(jasperReportsContext);
600         
601         exporterContext = new ExporterContext();
602     }
603
604
605     @Override
606     protected Class<PdfExporterConfiguration> getConfigurationInterface()
607     {
608         return PdfExporterConfiguration.class;
609     }
610
611
612     @Override
613     protected Class<PdfReportConfiguration> getItemConfigurationInterface()
614     {
615         return PdfReportConfiguration.class;
616     }
617     
618
619     @Override
620     @SuppressWarnings("deprecation")
621     protected void ensureOutput()
622     {
623         if (exporterOutput == null)
624         {
625             exporterOutput = 
626                 new net.sf.jasperreports.export.parameters.ParametersOutputStreamExporterOutput(
627                     getJasperReportsContext(),
628                     getParameters(),
629                     getCurrentJasperPrint()
630                     );
631         }
632     }
633     
634
635     /**
636      *
637      */

638     protected PdfImage getPxImage()
639     {
640         if (pxImage == null)
641         {
642             try
643             {
644                 pxImage = pdfProducer.createImage(JRLoader.loadBytesFromResource(JRImageLoader.PIXEL_IMAGE_RESOURCE), false);
645             }
646             catch(Exception e)
647             {
648                 throw new JRRuntimeException(e);
649             }
650         }
651
652         return pxImage;
653     }
654
655
656     @Override
657     public void exportReport() throws JRException
658     {
659         registerFonts();
660
661         /*   */
662         ensureJasperReportsContext();
663         ensureInput();
664
665         initExport();
666
667         ensureOutput();
668         
669         OutputStream outputStream = getExporterOutput().getOutputStream();
670
671         try
672         {
673             exportReportToStream(outputStream);
674         }
675         finally
676         {
677             getExporterOutput().close();
678             resetExportContext();
679         }
680     }
681
682
683     @Override
684     protected void initExport()
685     {
686         super.initExport();
687         
688         PdfExporterConfiguration configuration = getCurrentConfiguration();
689         
690         Boolean isTagged = configuration.isTagged();
691         if (isTagged != null)
692         {
693             tagHelper.setTagged(isTagged); 
694         }
695
696         tagHelper.setLanguage(configuration.getTagLanguage()); 
697         
698         this.permissions = getIntegerPermissions(configuration.getAllowedPermissions()) & (~getIntegerPermissions(configuration.getDeniedPermissions()));
699         crtDocumentPageNumber = 0;
700         
701         awtIgnoreMissingFont = getPropertiesUtil().getBooleanProperty(
702                 JRStyledText.PROPERTY_AWT_IGNORE_MISSING_FONT);//FIXMECONTEXT replace with getPropertiesUtil in all exporters
703         
704         pdfProducer = createPdfProducer();
705     }
706
707     @Override
708     protected void initReport()
709     {
710         super.initReport();
711
712         PdfReportConfiguration configuration = getCurrentItemConfiguration();
713         
714         pdfProducer.setForceLineBreakPolicy(configuration.isForceLineBreakPolicy());
715         
716         defaultIndentFirstLine = propertiesUtil.getBooleanProperty(jasperPrint, JRPrintText.PROPERTY_AWT_INDENT_FIRST_LINE, true);
717         defaultJustifyLastLine = propertiesUtil.getBooleanProperty(jasperPrint, JRPrintText.PROPERTY_AWT_JUSTIFY_LAST_LINE, false);
718         
719         crtOddPageOffsetX = configuration.getOddPageOffsetX();
720         crtOddPageOffsetY = configuration.getOddPageOffsetY();
721         crtEvenPageOffsetX = configuration.getEvenPageOffsetX();
722         crtEvenPageOffsetY = configuration.getEvenPageOffsetY();
723         
724         pdfProducer.initReport();
725
726         renderersCache = new RenderersCache(getJasperReportsContext());
727         loadedImagesMap = new HashMap<String,PdfImage>();
728     }
729
730     protected PdfProducerFactory getPdfProducerFactory()
731     {
732         String producerFactory = propertiesUtil.getProperty(PDF_PRODUCER_FACTORY_PROPERTY);
733         try
734         {
735             return pdfProducerCache.getCachedInstance(producerFactory);
736         }
737         catch (JRException e)
738         {
739             throw new JRRuntimeException(e);
740         }
741     }
742     
743     protected PdfProducerContext createPdfProducerContext()
744     {
745         return new PdfProducerContext()
746         {            
747             @Override
748             public JasperReportsContext getJasperReportsContext()
749             {
750                 return jasperReportsContext;
751             }
752             
753             @Override
754             public JRPropertiesUtil getProperties()
755             {
756                 return propertiesUtil;
757             }
758             
759             @Override
760             public FontUtil getFontUtil()
761             {
762                 return fontUtil;
763             }
764             
765             @Override
766             public JRStyledTextUtil getStyledTextUtil()
767             {
768                 return styledTextUtil;
769             }
770
771             @Override
772             public boolean isTagged()
773             {
774                 return tagHelper.isTagged;
775             }
776
777             @Override
778             public void setMinimalVersion(PdfVersionEnum version)
779             {
780                 minimalVersion = version;
781             }
782             
783             @Override
784             public JasperPrint getCurrentJasperPrint()
785             {
786                 return JRPdfExporter.this.getCurrentJasperPrint();
787             }
788             
789             @Override
790             public void setFont(Map<Attribute, Object> attributes, Locale locale, boolean setFontLines, FontRecipient recipient)
791             {
792                 JRPdfExporter.this.setFont(attributes, locale, setFontLines, recipient);
793             }
794
795             @Override
796             public JRException handleDocumentException(Exception e) 
797             {
798                 return new JRException(EXCEPTION_MESSAGE_KEY_DOCUMENT_ERROR,
799                     new Object[]{jasperPrint.getName()}, e);
800             }
801         };
802     }
803
804     protected PdfProducer createPdfProducer()
805     {
806         PdfProducerFactory producerFactory = getPdfProducerFactory();
807         PdfProducerContext producerContext = createPdfProducerContext();
808         return producerFactory.createProducer(producerContext);
809     }
810     
811     /**
812      *
813      */

814     protected void exportReportToStream(OutputStream os) throws JRException
815     {
816         //ByteArrayOutputStream baos = new ByteArrayOutputStream();
817         
818         PdfExporterConfiguration configuration = getCurrentConfiguration();
819
820         pageFormat = jasperPrint.getPageFormat(0);
821
822         PdfDocument document = pdfProducer.createDocument(pageFormat);
823
824         boolean closeDocuments = true;
825         try
826         {
827             PdfDocumentWriter pdfWriter = pdfProducer.createWriter(os);
828
829             tagHelper.setPdfProducer(pdfProducer);
830             
831             PdfVersionEnum pdfVersion = configuration.getPdfVersion();
832             if (pdfVersion != null)
833             {
834                 pdfWriter.setPdfVersion(pdfVersion);
835             }
836             
837             if (minimalVersion != null)
838             {
839                 pdfWriter.setMinimalPdfVersion(minimalVersion);
840             }
841             
842             if (configuration.isCompressed())
843             {
844                 pdfWriter.setFullCompression();
845             }
846             if (configuration.isEncrypted())
847             {
848                 int perms = configuration.isOverrideHints() == null || configuration.isOverrideHints()
849                     ? (configuration.getPermissions() != null 
850                         ? (Integer)configuration.getPermissions() 
851                         : permissions) 
852                     : (permissions != 0 
853                         ? permissions 
854                         :(configuration.getPermissions() != null 
855                             ? (Integer)configuration.getPermissions() 
856                             : 0));
857                 
858                         pdfWriter.setEncryption(configuration.getUserPassword(), configuration.getOwnerPassword(), 
859                                 perms, configuration.is128BitKey());
860             }
861             
862
863             PdfPrintScalingEnum printScaling = configuration.getPrintScaling();
864             pdfWriter.setPrintScaling(printScaling);
865             
866             boolean justifiedLetterSpacing = propertiesUtil.getBooleanProperty(jasperPrint, 
867                     PdfExporterConfiguration.PROPERTY_JUSTIFIED_LETTER_SPACING, false);
868             if (!justifiedLetterSpacing)
869             {
870                 pdfWriter.setNoSpaceCharRatio();
871             }
872
873             // Add meta-data parameters to generated PDF document
874             // mtclough@users.sourceforge.net 2005-12-05
875             String title = configuration.getMetadataTitle();
876             if( title != null )
877             {
878                 document.addTitle(title);
879                 if(configuration.isDisplayMetadataTitle()){
880                     pdfWriter.setDisplayMetadataTitle();
881                 }
882             }
883             String author = configuration.getMetadataAuthor();
884             if( author != null )
885             {
886                 document.addAuthor(author);
887             }
888             String subject = configuration.getMetadataSubject();
889             if( subject != null )
890             {
891                 document.addSubject(subject);
892             }
893             String keywords = configuration.getMetadataKeywords();
894             if( keywords != null )
895             {
896                 document.addKeywords(keywords);
897             }
898             String creator = configuration.getMetadataCreator();
899             if( creator == null )
900             {
901                 creator = "JasperReports Library version " + Package.getPackage("net.sf.jasperreports.engine").getImplementationVersion();
902             }
903             document.addCreator(creator);
904             
905             //accessibility check: tab order follows the structure of the document
906             pdfWriter.setTabOrderStructure();
907             
908             //accessibility check: setting the document primary language
909             String language = configuration.getTagLanguage();
910             if(language != null){
911                 pdfWriter.setLanguage(language);
912             }
913
914             // BEGIN: PDF/A support
915             PdfaConformanceEnum pdfaConformance = configuration.getPdfaConformance();
916             boolean gotPdfa = false;
917             if (pdfaConformance != null && pdfaConformance != PdfaConformanceEnum.NONE)
918             {
919                 pdfWriter.setPdfaConformance(pdfaConformance);
920                 gotPdfa = true;
921             }
922
923             if (gotPdfa) 
924             {
925                 pdfWriter.createXmpMetadata(title, subject, keywords);
926             } else 
927             {
928                 pdfWriter.setRgbTransparencyBlending(true);
929             }
930             // END: PDF/A support
931             
932             document.open();
933             // BEGIN: PDF/A support
934             if (gotPdfa) {
935                 String iccProfilePath = configuration.getIccProfilePath();
936                 if (iccProfilePath != null) {
937                     InputStream iccIs = RepositoryUtil.getInstance(jasperReportsContext).getInputStreamFromLocation(iccProfilePath);//FIXME use getRepository?
938                     pdfWriter.setIccProfilePath(iccProfilePath, iccIs);
939                 } else {
940                     throw new JRPdfaIccProfileNotFoundException();
941                 }
942             }
943             // END: PDF/A support
944             
945             String pdfJavaScript = configuration.getPdfJavaScript();
946             if(pdfJavaScript != null)
947             {
948                 pdfWriter.addJavaScript(pdfJavaScript);
949             }
950
951             pdfContent = pdfProducer.createPdfContent();
952
953             tagHelper.init();
954
955             List<ExporterInputItem> items = exporterInput.getItems();
956
957             initBookmarks(items);
958             
959             boolean isCreatingBatchModeBookmarks = configuration.isCreatingBatchModeBookmarks();
960
961             for (reportIndex = 0; reportIndex < items.size(); reportIndex++)
962             {
963                 ExporterInputItem item = items.get(reportIndex);
964
965                 setCurrentExporterInputItem(item);
966                 
967                 pageFormat = jasperPrint.getPageFormat(0);
968
969                 setPageSize(null);
970                 
971                 List<JRPrintPage> pages = jasperPrint.getPages();
972                 if (pages != null && pages.size() > 0)
973                 {
974                     if (items.size() > 1)
975                     {
976                         pdfProducer.newPage();
977
978                         if( isCreatingBatchModeBookmarks )
979                         {
980                             //add a new level to our outline for this report
981                             addBookmark(0, jasperPrint.getName(), 0, 0);
982                         }
983                     }
984                     
985                     PdfReportConfiguration lcItemConfiguration = getCurrentItemConfiguration();
986
987                     boolean sizePageToContent = lcItemConfiguration.isSizePageToContent();
988                     
989                     PrintPageFormat oldPageFormat = null;
990
991                     PageRange pageRange = getPageRange();
992                     int startPageIndex = (pageRange == null || pageRange.getStartPageIndex() == null) ? 0 : pageRange.getStartPageIndex();
993                     int endPageIndex = (pageRange == null || pageRange.getEndPageIndex() == null) ? (pages.size() - 1) : pageRange.getEndPageIndex();
994
995                     for (int pageIndex = startPageIndex; pageIndex <= endPageIndex; pageIndex++)
996                     {
997                         if (Thread.interrupted())
998                         {
999                             throw new ExportInterruptedException();
1000                         }
1001
1002                         JRPrintPage page = pages.get(pageIndex);
1003
1004                         pageFormat = jasperPrint.getPageFormat(pageIndex);
1005                         
1006                         if (sizePageToContent || oldPageFormat != pageFormat)
1007                         {
1008                             setPageSize(sizePageToContent ? page : null);
1009                         }
1010                         
1011                         pdfProducer.newPage();
1012
1013                         pdfProducer.getPdfContent().setLineCap(LineCapStyle.PROJECTING_SQUARE);
1014
1015                         writePageAnchor(pageIndex);
1016                         
1017                         crtDocumentPageNumber++;
1018
1019                         /*   */
1020                         exportPage(page);
1021                         
1022                         oldPageFormat = pageFormat;
1023                     }
1024                 }
1025                 else
1026                 {
1027                     pdfProducer.newPage();
1028                     pdfContent.setLiteral("\n");
1029                 }
1030             }
1031
1032             closeDocuments = false;
1033             pdfProducer.close();
1034         }
1035         catch(IOException e)
1036         {
1037             throw 
1038                 new JRException(
1039                     EXCEPTION_MESSAGE_KEY_REPORT_GENERATION_ERROR,
1040                     new Object[]{jasperPrint.getName()}, 
1041                     e);
1042         }
1043         finally
1044         {
1045             if (closeDocuments) //only on exception
1046             {
1047                 try
1048                 {
1049                     pdfProducer.close();
1050                 }
1051                 catch (Exception e)
1052                 {
1053                     // ignore, let the original exception propagate
1054                 }
1055             }
1056         }
1057
1058         //return os.toByteArray();
1059     }
1060
1061
1062     protected void writePageAnchor(int pageIndex) 
1063     {
1064         Map<Attribute,Object> attributes = new HashMap<Attribute,Object>();
1065         fontUtil.getAttributesWithoutAwtFont(attributes, new JRBasePrintText(jasperPrint.getDefaultStyleProvider()));
1066         PdfTextChunk chunk = pdfProducer.createChunk(" ", attributes, getLocale());
1067         
1068         chunk.setLocalDestination(JR_PAGE_ANCHOR_PREFIX + reportIndex + "_" + (pageIndex + 1));
1069
1070         tagHelper.startPageAnchor();
1071         
1072         PdfPhrase phrase = pdfProducer.createPhrase(chunk);
1073         
1074         phrase.go(
1075             0,
1076             pageFormat.getPageHeight(),
1077             1,
1078             1,
1079             0,
1080             0,
1081             PdfTextAlignment.LEFT,
1082             TextDirection.DEFAULT
1083             );
1084
1085         tagHelper.endPageAnchor();
1086     }
1087
1088     /**
1089      *
1090      */

1091     protected void setPageSize(JRPrintPage page) throws JRException, IOException
1092     {
1093         int pageWidth = 0; 
1094         int pageHeight = 0;
1095
1096         if (page != null)
1097         {
1098             Collection<JRPrintElement> elements = page.getElements();
1099             for (JRPrintElement element : elements)
1100             {
1101                 int elementRight = element.getX() + element.getWidth();
1102                 int elementBottom = element.getY() + element.getHeight();
1103                 pageWidth = pageWidth < elementRight ? elementRight : pageWidth;
1104                 pageHeight = pageHeight < elementBottom ? elementBottom : pageHeight;
1105             }
1106             
1107             pageWidth += pageFormat.getRightMargin();
1108             pageHeight += pageFormat.getBottomMargin();
1109         }
1110         
1111         pageWidth = pageWidth < pageFormat.getPageWidth() ? pageFormat.getPageWidth() : pageWidth; 
1112         pageHeight = pageHeight < pageFormat.getPageHeight() ? pageFormat.getPageHeight() : pageHeight; 
1113         
1114         pdfProducer.setPageSize(pageFormat, pageWidth, pageHeight);
1115     }
1116
1117     /**
1118      *
1119      */

1120     protected void exportPage(JRPrintPage page) throws JRException, IOException
1121     {
1122         tagHelper.startPage();
1123         
1124         Collection<JRPrintElement> elements = page.getElements();
1125         exportElements(elements);
1126
1127         pdfProducer.endPage();
1128         
1129         tagHelper.endPage();
1130
1131         JRExportProgressMonitor progressMonitor = getCurrentItemConfiguration().getProgressMonitor();
1132         if (progressMonitor != null)
1133         {
1134             progressMonitor.afterPageExport();
1135         }
1136     }
1137
1138     protected void exportElements(Collection<JRPrintElement> elements) throws IOException, JRException
1139     {
1140         if (elements != null && elements.size() > 0)
1141         {
1142             for(Iterator<JRPrintElement> it = elements.iterator(); it.hasNext();)
1143             {
1144                 JRPrintElement element = it.next();
1145
1146                 if (filter == null || filter.isToExport(element))
1147                 {
1148                     tagHelper.startElement(element);
1149
1150                     String strFieldType = element.getPropertiesMap().getProperty(PDF_FIELD_TYPE);
1151                     PdfFieldTypeEnum fieldType = PdfFieldTypeEnum.getByName(strFieldType);
1152                     if (fieldType == PdfFieldTypeEnum.CHECK)
1153                     {
1154                         exportFieldCheck(element);
1155                     }
1156                     else if (fieldType == PdfFieldTypeEnum.RADIO)
1157                     {
1158                         exportFieldRadio(element);
1159                     }
1160                     else if (element instanceof JRPrintLine)
1161                     {
1162                         exportLine((JRPrintLine)element);
1163                     }
1164                     else if (element instanceof JRPrintRectangle)
1165                     {
1166                         exportRectangle((JRPrintRectangle)element);
1167                     }
1168                     else if (element instanceof JRPrintEllipse)
1169                     {
1170                         exportEllipse((JRPrintEllipse)element);
1171                     }
1172                     else if (element instanceof JRPrintImage)
1173                     {
1174                         exportImage((JRPrintImage)element);
1175                     }
1176                     else if (element instanceof JRPrintText)
1177                     {
1178                         if (
1179                             fieldType == PdfFieldTypeEnum.TEXT
1180                             || fieldType == PdfFieldTypeEnum.COMBO
1181                             || fieldType == PdfFieldTypeEnum.LIST
1182                             )
1183                         {
1184                             exportFieldText((JRPrintText)element, fieldType);
1185                         }
1186                         else
1187                         {
1188                             exportText((JRPrintText)element);
1189                         }
1190                     }
1191                     else if (element instanceof JRPrintFrame)
1192                     {
1193                         exportFrame((JRPrintFrame)element);
1194                     }
1195                     else if (element instanceof JRGenericPrintElement)
1196                     {
1197                         exportGenericElement((JRGenericPrintElement) element);
1198                     }
1199
1200                     tagHelper.endElement(element);
1201                 }
1202             }
1203         }
1204     }
1205
1206
1207     /**
1208      *
1209      */

1210     protected void exportLine(JRPrintLine line)
1211     {
1212         int lcOffsetX = getOffsetX();
1213         int lcOffsetY = getOffsetY();
1214
1215         float lineWidth = line.getLinePen().getLineWidth(); 
1216         if (lineWidth > 0f)
1217         {
1218             preparePen(line.getLinePen(), LineCapStyle.BUTT);
1219
1220             if (line.getWidth() == 1)
1221             {
1222                 if (line.getHeight() != 1)
1223                 {
1224                     //Vertical line
1225                     if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
1226                     {
1227                         pdfContent.strokeLine(
1228                             line.getX() + lcOffsetX + 0.5f - lineWidth / 3,
1229                             pageFormat.getPageHeight() - line.getY() - lcOffsetY,
1230                             line.getX() + lcOffsetX + 0.5f - lineWidth / 3,
1231                             pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight()
1232                             );
1233                         
1234                         pdfContent.strokeLine(
1235                             line.getX() + lcOffsetX + 0.5f + lineWidth / 3,
1236                             pageFormat.getPageHeight() - line.getY() - lcOffsetY,
1237                             line.getX() + lcOffsetX + 0.5f + lineWidth / 3,
1238                             pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight()
1239                             );
1240                     }
1241                     else
1242                     {
1243                         pdfContent.strokeLine(
1244                             line.getX() + lcOffsetX + 0.5f,
1245                             pageFormat.getPageHeight() - line.getY() - lcOffsetY,
1246                             line.getX() + lcOffsetX + 0.5f,
1247                             pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight()
1248                             );
1249                     }
1250                 }
1251             }
1252             else
1253             {
1254                 if (line.getHeight() == 1)
1255                 {
1256                     //Horizontal line
1257                     if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
1258                     {
1259                         pdfContent.strokeLine(
1260                             line.getX() + lcOffsetX,
1261                             pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f + lineWidth / 3,
1262                             line.getX() + lcOffsetX + line.getWidth(),
1263                             pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f + lineWidth / 3
1264                             );
1265                         
1266                         pdfContent.strokeLine(
1267                             line.getX() + lcOffsetX,
1268                             pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f - lineWidth / 3,
1269                             line.getX() + lcOffsetX + line.getWidth(),
1270                             pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f - lineWidth / 3
1271                             );
1272                     }
1273                     else
1274                     {
1275                         pdfContent.strokeLine(
1276                             line.getX() + lcOffsetX,
1277                             pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f,
1278                             line.getX() + lcOffsetX + line.getWidth(),
1279                             pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f
1280                             );
1281                     }
1282                 }
1283                 else
1284                 {
1285                     //Oblique line
1286                     if (line.getDirectionValue() == LineDirectionEnum.TOP_DOWN)
1287                     {
1288                         if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
1289                         {
1290                             double xtrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getWidth(), 2) / Math.pow(line.getHeight(), 2))); 
1291                             double ytrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getHeight(), 2) / Math.pow(line.getWidth(), 2))); 
1292                             
1293                             pdfContent.strokeLine(
1294                                 line.getX() + lcOffsetX + (float)xtrans,
1295                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY + (float)ytrans,
1296                                 line.getX() + lcOffsetX + line.getWidth() + (float)xtrans,
1297                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight() + (float)ytrans
1298                                 );
1299                             
1300                             pdfContent.strokeLine(
1301                                 line.getX() + lcOffsetX - (float)xtrans,
1302                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY - (float)ytrans,
1303                                 line.getX() + lcOffsetX + line.getWidth() - (float)xtrans,
1304                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight() - (float)ytrans
1305                                 );
1306                         }
1307                         else
1308                         {
1309                             pdfContent.strokeLine(
1310                                 line.getX() + lcOffsetX,
1311                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY,
1312                                 line.getX() + lcOffsetX + line.getWidth(),
1313                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight()
1314                                 );
1315                         }
1316                     }
1317                     else
1318                     {
1319                         if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
1320                         {
1321                             double xtrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getWidth(), 2) / Math.pow(line.getHeight(), 2))); 
1322                             double ytrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getHeight(), 2) / Math.pow(line.getWidth(), 2))); 
1323                             
1324                             pdfContent.strokeLine(
1325                                 line.getX() + lcOffsetX + (float)xtrans,
1326                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight() - (float)ytrans,
1327                                 line.getX() + lcOffsetX + line.getWidth() + (float)xtrans,
1328                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY - (float)ytrans
1329                                 );
1330
1331                             pdfContent.strokeLine(
1332                                 line.getX() + lcOffsetX - (float)xtrans,
1333                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight() + (float)ytrans,
1334                                 line.getX() + lcOffsetX + line.getWidth() - (float)xtrans,
1335                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY + (float)ytrans
1336                                 );
1337                         }
1338                         else
1339                         {
1340                             pdfContent.strokeLine(
1341                                 line.getX() + lcOffsetX,
1342                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight(),
1343                                 line.getX() + lcOffsetX + line.getWidth(),
1344                                 pageFormat.getPageHeight() - line.getY() - lcOffsetY
1345                                 );
1346                         }
1347                     }
1348                 }
1349             }
1350
1351             resetPen();
1352             pdfContent.setLineDash(0f);
1353             pdfContent.setLineCap(LineCapStyle.PROJECTING_SQUARE);
1354         }
1355     }
1356
1357
1358     /**
1359      *
1360      */

1361     protected void exportRectangle(JRPrintRectangle rectangle)
1362     {
1363         pdfContent.setFillColor(rectangle.getBackcolor());
1364         preparePen(rectangle.getLinePen(), LineCapStyle.PROJECTING_SQUARE);
1365
1366         float lineWidth = rectangle.getLinePen().getLineWidth();
1367         int lcOffsetX = getOffsetX();
1368         int lcOffsetY = getOffsetY();
1369         
1370         if (rectangle.getModeValue() == ModeEnum.OPAQUE)
1371         {
1372             pdfContent.fillRoundRectangle(
1373                 rectangle.getX() + lcOffsetX,
1374                 pageFormat.getPageHeight() - rectangle.getY() - lcOffsetY - rectangle.getHeight(),
1375                 rectangle.getWidth(),
1376                 rectangle.getHeight(),
1377                 rectangle.getRadius()
1378                 );
1379         }
1380
1381         if (lineWidth > 0f)
1382         {
1383             if (rectangle.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
1384             {
1385                 pdfContent.strokeRoundRectangle(
1386                     rectangle.getX() + lcOffsetX - lineWidth / 3,
1387                     pageFormat.getPageHeight() - rectangle.getY() - lcOffsetY - rectangle.getHeight() - lineWidth / 3,
1388                     rectangle.getWidth() + 2 * lineWidth / 3,
1389                     rectangle.getHeight() + 2 * lineWidth / 3,
1390                     rectangle.getRadius()
1391                     );
1392                 
1393                 pdfContent.strokeRoundRectangle(
1394                     rectangle.getX() + lcOffsetX + lineWidth / 3,
1395                     pageFormat.getPageHeight() - rectangle.getY() - lcOffsetY - rectangle.getHeight() + lineWidth / 3,
1396                     rectangle.getWidth() - 2 * lineWidth / 3,
1397                     rectangle.getHeight() - 2 * lineWidth / 3,
1398                     rectangle.getRadius()
1399                     );
1400             }
1401             else
1402             {
1403                 pdfContent.strokeRoundRectangle(
1404                     rectangle.getX() + lcOffsetX,
1405                     pageFormat.getPageHeight() - rectangle.getY() - lcOffsetY - rectangle.getHeight(),
1406                     rectangle.getWidth(),
1407                     rectangle.getHeight(),
1408                     rectangle.getRadius()
1409                     );
1410             }
1411         }
1412
1413         resetPen();
1414         pdfContent.resetFillColor();
1415         pdfContent.setLineDash(0f);
1416     }
1417
1418
1419     /**
1420      *
1421      */

1422     protected void exportEllipse(JRPrintEllipse ellipse)
1423     {
1424         pdfContent.setFillColor(ellipse.getBackcolor());
1425         preparePen(ellipse.getLinePen(), LineCapStyle.PROJECTING_SQUARE);
1426
1427         float lineWidth = ellipse.getLinePen().getLineWidth();
1428         int lcOffsetX = getOffsetX();
1429         int lcOffsetY = getOffsetY();
1430         
1431         if (ellipse.getModeValue() == ModeEnum.OPAQUE)
1432         {
1433             pdfContent.fillEllipse(
1434                 ellipse.getX() + lcOffsetX,
1435                 pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY - ellipse.getHeight(),
1436                 ellipse.getX() + lcOffsetX + ellipse.getWidth(),
1437                 pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY
1438                 );
1439         }
1440
1441         if (lineWidth > 0f)
1442         {
1443             if (ellipse.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
1444             {
1445                 pdfContent.strokeEllipse(
1446                     ellipse.getX() + lcOffsetX - lineWidth / 3,
1447                     pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY - ellipse.getHeight() - lineWidth / 3,
1448                     ellipse.getX() + lcOffsetX + ellipse.getWidth() + lineWidth / 3,
1449                     pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY + lineWidth / 3
1450                     );
1451
1452                 pdfContent.strokeEllipse(
1453                     ellipse.getX() + lcOffsetX + lineWidth / 3,
1454                     pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY - ellipse.getHeight() + lineWidth / 3,
1455                     ellipse.getX() + lcOffsetX + ellipse.getWidth() - lineWidth / 3,
1456                     pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY - lineWidth / 3
1457                     );
1458             }
1459             else
1460             {
1461                 pdfContent.strokeEllipse(
1462                     ellipse.getX() + lcOffsetX,
1463                     pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY - ellipse.getHeight(),
1464                     ellipse.getX() + lcOffsetX + ellipse.getWidth(),
1465                     pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY
1466                     );
1467             }
1468         }
1469
1470         resetPen();
1471         pdfContent.resetFillColor();
1472         
1473         pdfContent.setLineDash(0f);
1474     }
1475
1476
1477     /**
1478      *
1479      */

1480     public void exportImage(JRPrintImage printImage) throws IOException,  JRException
1481     {
1482         if (printImage.getModeValue() == ModeEnum.OPAQUE)
1483         {
1484             pdfContent.setFillColor(printImage.getBackcolor());
1485             pdfContent.fillRectangle(
1486                 printImage.getX() + getOffsetX(),
1487                 pageFormat.getPageHeight() - printImage.getY() - getOffsetY(),
1488                 printImage.getWidth(),
1489                 - printImage.getHeight()
1490                 );
1491             pdfContent.resetFillColor();
1492         }
1493
1494         InternalImageProcessor imageProcessor =
1495             new InternalImageProcessor(printImage);
1496         
1497         Renderable renderer = printImage.getRenderer();
1498
1499         if (
1500             renderer != null 
1501             && imageProcessor.availableImageWidth > 0 
1502             && imageProcessor.availableImageHeight > 0
1503             )
1504         {
1505             InternalImageProcessorResult imageProcessorResult = null;
1506             
1507             try
1508             {
1509                 imageProcessorResult = imageProcessor.process(renderer);
1510             }
1511             catch (Exception e)
1512             {
1513                 Renderable onErrorRenderer = getRendererUtil().handleImageError(e, printImage.getOnErrorTypeValue());
1514                 if (onErrorRenderer != null)
1515                 {
1516                     imageProcessorResult = imageProcessor.process(onErrorRenderer);
1517                 }
1518             }
1519
1520             if (imageProcessorResult != null)
1521             {
1522                 setAnchor(imageProcessorResult.chunk, printImage, printImage);
1523                 setHyperlinkInfo(imageProcessorResult.chunk, printImage);
1524
1525                 tagHelper.startImage(printImage);
1526                 
1527                 PdfPhrase phrase = pdfProducer.createPhrase(imageProcessorResult.chunk);
1528                 
1529                 int upperY = pageFormat.getPageHeight() - printImage.getY() - imageProcessor.topPadding - getOffsetY() - imageProcessorResult.yoffset;
1530                 int lowerX = printImage.getX() + imageProcessor.leftPadding + getOffsetX() + imageProcessorResult.xoffset;
1531                 phrase.go(
1532                     lowerX,
1533                     upperY,
1534                     lowerX + imageProcessorResult.scaledWidth,
1535                     upperY - imageProcessorResult.scaledHeight,
1536                     0,
1537                     0,
1538                     PdfTextAlignment.LEFT,
1539                     TextDirection.DEFAULT
1540                     );
1541
1542                 tagHelper.endImage();
1543             }
1544         }
1545
1546
1547         if (
1548             printImage.getLineBox().getTopPen().getLineWidth() <= 0f &&
1549             printImage.getLineBox().getLeftPen().getLineWidth() <= 0f &&
1550             printImage.getLineBox().getBottomPen().getLineWidth() <= 0f &&
1551             printImage.getLineBox().getRightPen().getLineWidth() <= 0f
1552             )
1553         {
1554             if (printImage.getLinePen().getLineWidth() > 0f)
1555             {
1556                 exportPen(printImage.getLinePen(), printImage);
1557             }
1558         }
1559         else
1560         {
1561             /*   */
1562             exportBox(
1563                 printImage.getLineBox(),
1564                 printImage
1565                 );
1566         }
1567     }
1568
1569     private class InternalImageProcessor
1570     {
1571         private final JRPrintImage printImage;
1572         private final RenderersCache imageRenderersCache;
1573         
1574         private final int topPadding;
1575         private final int leftPadding;
1576         private final int bottomPadding;
1577         private final int rightPadding;
1578
1579         private final int availableImageWidth;
1580         private final int availableImageHeight;
1581         
1582         private InternalImageProcessor(JRPrintImage printImage)
1583         {
1584             this.printImage = printImage;
1585             this.imageRenderersCache = printImage.isUsingCache() ? renderersCache : new RenderersCache(getJasperReportsContext());
1586             
1587             topPadding = printImage.getLineBox().getTopPadding();
1588             leftPadding = printImage.getLineBox().getLeftPadding();
1589             bottomPadding = printImage.getLineBox().getBottomPadding();
1590             rightPadding = printImage.getLineBox().getRightPadding();
1591
1592             int tmpAvailableImageWidth = printImage.getWidth() - leftPadding - rightPadding;
1593             availableImageWidth = tmpAvailableImageWidth < 0 ? 0 : tmpAvailableImageWidth;
1594
1595             int tmpAvailableImageHeight = printImage.getHeight() - topPadding - bottomPadding;
1596             availableImageHeight = tmpAvailableImageHeight < 0 ? 0 : tmpAvailableImageHeight;
1597         }
1598         
1599         private InternalImageProcessorResult process(Renderable renderer) throws JRException, IOException
1600         {
1601             InternalImageProcessorResult imageProcessorResult = null;
1602
1603             if (renderer instanceof ResourceRenderer)
1604             {
1605                 renderer = imageRenderersCache.getLoadedRenderer((ResourceRenderer)renderer);
1606             }
1607             
1608             if (renderer instanceof Graphics2DRenderable)
1609             {
1610                 imageProcessorResult = processGraphics2D((Graphics2DRenderable)renderer);
1611             }
1612             else if (renderer instanceof DataRenderable)
1613             {
1614                 boolean isSvgData = getRendererUtil().isSvgData((DataRenderable)renderer);
1615                 
1616                 if (isSvgData)
1617                 {
1618                     imageProcessorResult = 
1619                         processGraphics2D(
1620                             new WrappingSvgDataToGraphics2DRenderer((DataRenderable)renderer)
1621                             );
1622                 }
1623                 else
1624                 {
1625                     switch(printImage.getScaleImageValue())
1626                     {
1627                         case CLIP :
1628                         {
1629                             imageProcessorResult = 
1630                                 processImageClip(
1631                                     new WrappingImageDataToGraphics2DRenderer((DataRenderable)renderer)
1632                                     );
1633                             break;
1634                         }
1635                         case FILL_FRAME :
1636                         {
1637                             imageProcessorResult = processImageFillFrame(renderer.getId(), (DataRenderable)renderer);
1638                             break;
1639                         }
1640                         case RETAIN_SHAPE :
1641                         default :
1642                         {
1643                             imageProcessorResult = processImageRetainShape(renderer.getId(), (DataRenderable)renderer);
1644                         }
1645                     }
1646                 }
1647             }
1648             else
1649             {
1650                 throw 
1651                     new JRException(
1652                         RendererUtil.EXCEPTION_MESSAGE_KEY_RENDERABLE_MUST_IMPLEMENT_INTERFACE,
1653                         new Object[]{
1654                             renderer.getClass().getName(),
1655                             DataRenderable.class.getName() 
1656                                 + " or " + Graphics2DRenderable.class.getName() 
1657                             }
1658                         );
1659             }
1660
1661             return imageProcessorResult;
1662         }
1663         
1664         
1665         private InternalImageProcessorResult processImageClip(Graphics2DRenderable renderer) throws JRException, IOException
1666         {
1667             int normalWidth = availableImageWidth;
1668             int normalHeight = availableImageHeight;
1669
1670             Dimension2D dimension = 
1671                 renderer instanceof DimensionRenderable 
1672                 ? ((DimensionRenderable)renderer).getDimension(jasperReportsContext) 
1673                 : null;
1674             if (dimension != null)
1675             {
1676                 normalWidth = (int)dimension.getWidth();
1677                 normalHeight = (int)dimension.getHeight();
1678             }
1679
1680             int minWidth = Math.min(normalWidth, availableImageWidth);
1681             int minHeight = Math.min(normalHeight, availableImageHeight);
1682             int xoffset = (int)(ImageUtil.getXAlignFactor(printImage) * (availableImageWidth - normalWidth));
1683             int yoffset = (int)(ImageUtil.getYAlignFactor(printImage) * (availableImageHeight - normalHeight));
1684             int translateX = xoffset;
1685             int translateY = yoffset;
1686             int angle = 0;
1687             
1688             switch (printImage.getRotation())
1689             {
1690                 case LEFT :
1691                 {
1692                     minWidth = Math.min(normalWidth, availableImageHeight);
1693                     minHeight = Math.min(normalHeight, availableImageWidth);
1694                     xoffset = (int)(ImageUtil.getYAlignFactor(printImage) * (availableImageWidth - normalHeight));
1695                     yoffset = (int)((1f - ImageUtil.getXAlignFactor(printImage)) * (availableImageHeight - normalWidth));
1696                     translateX = (int)(ImageUtil.getXAlignFactor(printImage) * (availableImageHeight - normalWidth));
1697                     translateY = xoffset;
1698                     angle = 90;
1699                     break;
1700                 }
1701                 case RIGHT :
1702                 {
1703                     minWidth = Math.min(normalWidth, availableImageHeight);
1704                     minHeight = Math.min(normalHeight, availableImageWidth);
1705                     xoffset = (int)((1f - ImageUtil.getYAlignFactor(printImage)) * (availableImageWidth - normalHeight));
1706                     yoffset = (int)(ImageUtil.getXAlignFactor(printImage) * (availableImageHeight - normalWidth));
1707                     translateX = yoffset;
1708                     translateY = (int)(ImageUtil.getYAlignFactor(printImage) * (availableImageWidth - normalHeight));
1709                     angle = -90;
1710                     break;
1711                 }
1712                 case UPSIDE_DOWN :
1713                 {
1714                     minWidth = Math.min(normalWidth, availableImageWidth);
1715                     minHeight = Math.min(normalHeight, availableImageHeight);
1716                     xoffset = (int)((1f - ImageUtil.getXAlignFactor(printImage)) * (availableImageWidth - normalWidth));
1717                     yoffset = (int)((1f - ImageUtil.getYAlignFactor(printImage)) * (availableImageHeight - normalHeight));
1718                     translateX = (int)(ImageUtil.getXAlignFactor(printImage) * (availableImageWidth - normalWidth));
1719                     translateY = (int)(ImageUtil.getYAlignFactor(printImage) * (availableImageHeight - normalHeight));
1720                     angle = 180;
1721                     break;
1722                 }
1723                 case NONE :
1724                 default :
1725                 {
1726                 }
1727             }
1728
1729             BufferedImage bi =
1730                 new BufferedImage(minWidth, minHeight, BufferedImage.TYPE_INT_ARGB);
1731
1732             Graphics2D g = bi.createGraphics();
1733             try
1734             {
1735                 if (printImage.getModeValue() == ModeEnum.OPAQUE)
1736                 {
1737                     g.setColor(printImage.getBackcolor());
1738                     g.fillRect(0, 0, minWidth, minHeight);
1739                 }
1740                 renderer.render(
1741                     jasperReportsContext,
1742                     g,
1743                     new java.awt.Rectangle(
1744                         translateX > 0 ? 0 : translateX,
1745                         translateY > 0 ? 0 : translateY,
1746                         normalWidth,
1747                         normalHeight
1748                         )
1749                     );
1750             }
1751             finally
1752             {
1753                 g.dispose();
1754             }
1755
1756             //awtImage = bi.getSubimage(0, 0, minWidth, minHeight);
1757
1758             //image = com.lowagie.text.Image.getInstance(awtImage, printImage.getBackcolor());
1759             PdfImage image = pdfProducer.createImage(bi, angle);
1760             PdfChunk chunk = pdfProducer.createChunk(image);
1761
1762             return 
1763                 new InternalImageProcessorResult(
1764                     chunk, 
1765                     image.getScaledWidth(), 
1766                     image.getScaledHeight(),
1767                     xoffset < 0 ? 0 : xoffset,
1768                     yoffset < 0 ? 0 : yoffset
1769                     );
1770         }
1771
1772         private InternalImageProcessorResult processImageFillFrame(String rendererId, DataRenderable renderer) throws JRException
1773         {
1774             PdfImage image = null;
1775             
1776             if (printImage.isUsingCache() && loadedImagesMap.containsKey(rendererId))
1777             {
1778                 image = loadedImagesMap.get(rendererId);
1779             }
1780             else
1781             {
1782                 try
1783                 {
1784                     image = pdfProducer.createImage(renderer.getData(jasperReportsContext), true);
1785                 }
1786                 catch (Exception e)
1787                 {
1788                     throw new JRException(e);
1789                 }
1790
1791                 if (printImage.isUsingCache())
1792                 {
1793                     loadedImagesMap.put(rendererId, image);
1794                 }
1795             }
1796
1797             switch (printImage.getRotation())
1798             {
1799                 case LEFT :
1800                 {
1801                     image.scaleAbsolute(availableImageHeight, availableImageWidth);
1802                     image.setRotationDegrees(90);
1803                     break;
1804                 }
1805                 case RIGHT :
1806                 {
1807                     image.scaleAbsolute(availableImageHeight, availableImageWidth);
1808                     image.setRotationDegrees(-90);
1809                     break;
1810                 }
1811                 case UPSIDE_DOWN :
1812                 {
1813                     image.scaleAbsolute(availableImageWidth, availableImageHeight);
1814                     image.setRotationDegrees(180);
1815                     break;
1816                 }
1817                 case NONE :
1818                 default :
1819                 {
1820                     image.scaleAbsolute(availableImageWidth, availableImageHeight);
1821                 }
1822             }
1823             
1824             PdfChunk chunk = pdfProducer.createChunk(image);
1825             return 
1826                 new InternalImageProcessorResult(
1827                     chunk, 
1828                     image.getScaledWidth(), 
1829                     image.getScaledHeight(),
1830                     0,
1831                     0
1832                     );
1833         }
1834
1835         private InternalImageProcessorResult processImageRetainShape(String rendererId, DataRenderable renderer) throws JRException
1836         {
1837             PdfImage image = null;
1838             
1839             if (printImage.isUsingCache() && loadedImagesMap.containsKey(rendererId))
1840             {
1841                 image = loadedImagesMap.get(rendererId);
1842             }
1843             else
1844             {
1845                 try
1846                 {
1847                     image = pdfProducer.createImage(renderer.getData(jasperReportsContext), true);
1848                 }
1849                 catch (Exception e)
1850                 {
1851                     throw new JRException(e);
1852                 }
1853
1854                 if (printImage.isUsingCache())
1855                 {
1856                     loadedImagesMap.put(rendererId, image);
1857                 }
1858             }
1859
1860             int xoffset = 0;
1861             int yoffset = 0;
1862
1863             image.setRotationDegrees(0); // reset in case the image is from cache
1864             
1865             switch (printImage.getRotation())
1866             {
1867                 case LEFT :
1868                 {
1869                     image.scaleToFit(availableImageHeight, availableImageWidth);
1870                     image.setRotationDegrees(90);
1871                     xoffset = (int)(ImageUtil.getYAlignFactor(printImage) * (availableImageWidth - image.getPlainHeight()));
1872                     yoffset = (int)((1f - ImageUtil.getXAlignFactor(printImage)) * (availableImageHeight - image.getPlainWidth()));
1873                     break;
1874                 }
1875                 case RIGHT :
1876                 {
1877                     image.scaleToFit(availableImageHeight, availableImageWidth);
1878                     image.setRotationDegrees(-90);
1879                     xoffset = (int)((1f - ImageUtil.getYAlignFactor(printImage)) * (availableImageWidth - image.getPlainHeight()));
1880                     yoffset = (int)(ImageUtil.getXAlignFactor(printImage) * (availableImageHeight - image.getPlainWidth()));
1881                     break;
1882                 }
1883                 case UPSIDE_DOWN :
1884                 {
1885                     image.scaleToFit(availableImageWidth, availableImageHeight);
1886                     image.setRotationDegrees(180);
1887                     xoffset = (int)((1f - ImageUtil.getXAlignFactor(printImage)) * (availableImageWidth - image.getPlainWidth()));
1888                     yoffset = (int)((1f - ImageUtil.getYAlignFactor(printImage)) * (availableImageHeight - image.getPlainHeight()));
1889                     break;
1890                 }
1891                 case NONE :
1892                 default :
1893                 {
1894                     image.scaleToFit(availableImageWidth, availableImageHeight);
1895                     xoffset = (int)(ImageUtil.getXAlignFactor(printImage) * (availableImageWidth - image.getPlainWidth()));
1896                     yoffset = (int)(ImageUtil.getYAlignFactor(printImage) * (availableImageHeight - image.getPlainHeight()));
1897                 }
1898             }
1899             
1900             xoffset = (xoffset < 0 ? 0 : xoffset);
1901             yoffset = (yoffset < 0 ? 0 : yoffset);
1902             
1903             PdfChunk chunk = pdfProducer.createChunk(image);
1904             return 
1905                 new InternalImageProcessorResult(
1906                     chunk, 
1907                     image.getScaledWidth(), 
1908                     image.getScaledHeight(),
1909                     xoffset,
1910                     yoffset
1911                     );
1912         }
1913         
1914         private InternalImageProcessorResult processGraphics2D(Graphics2DRenderable renderer) throws JRException, IOException
1915         {
1916             int xoffset = 0;
1917             int yoffset = 0;
1918             int translateX = 0;
1919             int translateY = 0;
1920             double templateWidth = 0;
1921             double templateHeight = 0;
1922             double renderWidth = 0;
1923             double renderHeight = 0;
1924             double ratioX = 1f;
1925             double ratioY = 1f;
1926             double angle = 0;
1927
1928             switch (printImage.getScaleImageValue())
1929             {
1930                 case CLIP:
1931                 {
1932                     Dimension2D dimension = 
1933                         renderer instanceof DimensionRenderable 
1934                         ? ((DimensionRenderable)renderer).getDimension(jasperReportsContext) 
1935                         : null;
1936                     if (dimension != null)
1937                     {
1938                         renderWidth = dimension.getWidth();
1939                         renderHeight = dimension.getHeight();
1940                     }
1941                         
1942                     templateWidth = availableImageWidth;
1943                     templateHeight = availableImageHeight;
1944
1945                     switch (printImage.getRotation())
1946                     {
1947                         case LEFT:
1948                             if (dimension == null)
1949                             {
1950                                 renderWidth = availableImageHeight;
1951                                 renderHeight = availableImageWidth;
1952                             }
1953                             translateX = (int)(ImageUtil.getYAlignFactor(printImage) * (availableImageWidth - renderHeight));
1954                             translateY = availableImageHeight - (int)(ImageUtil.getXAlignFactor(printImage) * (availableImageHeight - renderWidth));
1955                             angle = - Math.PI / 2;
1956                             break;
1957                         case RIGHT:
1958                             if (dimension == null)
1959                             {
1960                                 renderWidth = availableImageHeight;
1961                                 renderHeight = availableImageWidth;
1962                             }
1963                             translateX = availableImageWidth - (int)(ImageUtil.getYAlignFactor(printImage) * (availableImageWidth - renderHeight));
1964                             translateY = (int)(ImageUtil.getXAlignFactor(printImage) * (availableImageHeight - renderWidth));
1965                             angle = Math.PI / 2;
1966                             break;
1967                         case UPSIDE_DOWN:
1968                             if (dimension == null)
1969                             {
1970                                 renderWidth = availableImageWidth;
1971                                 renderHeight = availableImageHeight;
1972                             }
1973                             translateX = availableImageWidth - (int)(ImageUtil.getXAlignFactor(printImage) * (availableImageWidth - renderWidth));
1974                             translateY = availableImageHeight - (int)(ImageUtil.getYAlignFactor(printImage) * (availableImageHeight - renderHeight));
1975                             angle = Math.PI;
1976                             break;
1977                         case NONE:
1978                         default:
1979                             if (dimension == null)
1980                             {
1981                                 renderWidth = availableImageWidth;
1982                                 renderHeight = availableImageHeight;
1983                             }
1984                             translateX = (int) (ImageUtil.getXAlignFactor(printImage) * (availableImageWidth - renderWidth));
1985                             translateY = (int) (ImageUtil.getYAlignFactor(printImage) * (availableImageHeight - renderHeight));
1986                             angle = 0;
1987                             break;
1988                     }
1989                     break;
1990                 }
1991                 case FILL_FRAME:
1992                 {
1993                     templateWidth = availableImageWidth;
1994                     templateHeight = availableImageHeight;
1995
1996                     switch (printImage.getRotation())
1997                     {
1998                         case LEFT:
1999                             renderWidth = availableImageHeight;
2000                             renderHeight = availableImageWidth;
2001                             translateX = 0;
2002                             translateY = availableImageHeight;
2003                             angle = - Math.PI / 2;
2004                             break;
2005                         case RIGHT:
2006                             renderWidth = availableImageHeight;
2007                             renderHeight = availableImageWidth;
2008                             translateX = availableImageWidth;
2009                             translateY = 0;
2010                             angle = Math.PI / 2;
2011                             break;
2012                         case UPSIDE_DOWN:
2013                             renderWidth = availableImageWidth;
2014                             renderHeight = availableImageHeight;
2015                             translateX = availableImageWidth;
2016                             translateY = availableImageHeight;
2017                             angle = Math.PI;
2018                             break;
2019                         case NONE:
2020                         default:
2021                             renderWidth = availableImageWidth;
2022                             renderHeight = availableImageHeight;
2023                             translateX = 0;
2024                             translateY = 0;
2025                             angle = 0;
2026                             break;
2027                     }
2028                     break;
2029                 }
2030                 case RETAIN_SHAPE:
2031                 default:
2032                 {
2033                     Dimension2D dimension = 
2034                         renderer instanceof DimensionRenderable 
2035                         ? ((DimensionRenderable)renderer).getDimension(jasperReportsContext) 
2036                         : null;
2037                     if (dimension != null)
2038                     {
2039                         renderWidth = dimension.getWidth();
2040                         renderHeight = dimension.getHeight();
2041                     }
2042                         
2043                     switch (printImage.getRotation())
2044                     {
2045                         case LEFT:
2046                             if (dimension == null)
2047                             {
2048                                 renderWidth = availableImageHeight;
2049                                 renderHeight = availableImageWidth;
2050                             }
2051                             ratioX = availableImageWidth / renderHeight;
2052                             ratioY = availableImageHeight / renderWidth;
2053                             ratioX = ratioX < ratioY ? ratioX : ratioY;
2054                             ratioY = ratioX;
2055                             templateWidth = renderHeight;
2056                             templateHeight = renderWidth;
2057                             translateX = 0;
2058                             translateY = (int)renderWidth;
2059                             xoffset = (int) (ImageUtil.getYAlignFactor(printImage) * (availableImageWidth - renderHeight * ratioX));
2060                             yoffset = (int) (ImageUtil.getXAlignFactor(printImage) * (availableImageHeight - renderWidth * ratioY));
2061                             angle = - Math.PI / 2;
2062                             break;
2063                         case RIGHT:
2064                             if (dimension == null)
2065                             {
2066                                 renderWidth = availableImageHeight;
2067                                 renderHeight = availableImageWidth;
2068                             }
2069                             ratioX = availableImageWidth / renderHeight;
2070                             ratioY = availableImageHeight / renderWidth;
2071                             ratioX = ratioX < ratioY ? ratioX : ratioY;
2072                             ratioY = ratioX;
2073                             templateWidth = renderHeight;
2074                             templateHeight = renderWidth;
2075                             translateX = (int)renderHeight;
2076                             translateY = 0;
2077                             xoffset = (int) ((1f - ImageUtil.getYAlignFactor(printImage)) * (availableImageWidth - renderHeight * ratioX));
2078                             yoffset = (int) ((1f - ImageUtil.getXAlignFactor(printImage)) * (availableImageHeight - renderWidth * ratioY));
2079                             angle = Math.PI / 2;
2080                             break;
2081                         case UPSIDE_DOWN:
2082                             if (dimension == null)
2083                             {
2084                                 renderWidth = availableImageWidth;
2085                                 renderHeight = availableImageHeight;
2086                             }
2087                             ratioX = availableImageWidth / renderWidth;
2088                             ratioY = availableImageHeight / renderHeight;
2089                             ratioX = ratioX < ratioY ? ratioX : ratioY;
2090                             ratioY = ratioX;
2091                             templateWidth = renderWidth;
2092                             templateHeight = renderHeight;
2093                             translateX = (int)renderWidth;
2094                             translateY = (int)renderHeight;
2095                             xoffset = (int) ((1f - ImageUtil.getXAlignFactor(printImage)) * (availableImageWidth - renderWidth * ratioX));
2096                             yoffset = (int) (ImageUtil.getYAlignFactor(printImage) * (availableImageHeight - renderHeight * ratioY));
2097                             angle = Math.PI;
2098                             break;
2099                         case NONE:
2100                         default:
2101                             if (dimension == null)
2102                             {
2103                                 renderWidth = availableImageWidth;
2104                                 renderHeight = availableImageHeight;
2105                             }
2106                             ratioX = availableImageWidth / renderWidth;
2107                             ratioY = availableImageHeight / renderHeight;
2108                             ratioX = ratioX < ratioY ? ratioX : ratioY;
2109                             ratioY = ratioX;
2110                             templateWidth = renderWidth;
2111                             templateHeight = renderHeight;
2112                             translateX = 0;
2113                             translateY = 0;
2114                             xoffset = (int) (ImageUtil.getXAlignFactor(printImage) * (availableImageWidth - renderWidth * ratioX));
2115                             yoffset = (int) ((1f - ImageUtil.getYAlignFactor(printImage)) * (availableImageHeight - renderHeight * ratioY));
2116                             angle = 0;
2117                             break;
2118                     }
2119                     break;
2120                 }
2121             }
2122
2123             pdfProducer.drawImage(printImage, renderer, getCurrentItemConfiguration().isForceSvgShapes(), 
2124                     templateWidth,  templateHeight, 
2125                     translateX, translateY, angle, 
2126                     renderWidth, renderHeight, 
2127                     (float) ratioX, (float) ratioY, 
2128                     printImage.getX() + leftPadding + getOffsetX()
2129                         + xoffset,
2130                     pageFormat.getPageHeight() - printImage.getY() - topPadding - getOffsetY()
2131                         - availableImageHeight
2132                         + yoffset);
2133
2134             PdfImage image = getPxImage();
2135             image.scaleAbsolute(availableImageWidth, availableImageHeight);
2136             
2137             PdfChunk chunk = pdfProducer.createChunk(image);            
2138             InternalImageProcessorResult result =
2139                 new InternalImageProcessorResult(
2140                     chunk,
2141                     availableImageWidth,
2142                     availableImageHeight,
2143                     0,
2144                     0
2145                     );
2146             
2147             return result;
2148         }
2149     }
2150
2151     private class InternalImageProcessorResult
2152     {
2153         private final PdfChunk chunk;
2154         private final float scaledWidth;
2155         private final float scaledHeight;
2156         private final int xoffset;
2157         private final int yoffset;
2158         
2159         private InternalImageProcessorResult(
2160                 PdfChunk chunk,
2161                 float scaledWidth,
2162                 float scaledHeight,
2163                 int xoffset,
2164                 int yoffset
2165             )
2166         {
2167             this.chunk = chunk;
2168             this.scaledWidth = scaledWidth;
2169             this.scaledHeight = scaledHeight;
2170             this.xoffset = xoffset;
2171             this.yoffset = yoffset;
2172         }
2173     }
2174
2175
2176     /**
2177      *
2178      */

2179     protected void setHyperlinkInfo(PdfChunk chunk, JRPrintHyperlink link)
2180     {
2181         if (link != null)
2182         {
2183             Boolean ignoreHyperlink = HyperlinkUtil.getIgnoreHyperlink(PdfReportConfiguration.PROPERTY_IGNORE_HYPERLINK, link);
2184             if (ignoreHyperlink == null)
2185             {
2186                 ignoreHyperlink = getCurrentItemConfiguration().isIgnoreHyperlink();
2187             }
2188             
2189             if (!ignoreHyperlink)
2190             {
2191                 switch(link.getHyperlinkTypeValue())
2192                 {
2193                     case REFERENCE :
2194                     {
2195                         if (link.getHyperlinkReference() != null)
2196                         {
2197                             switch(link.getHyperlinkTargetValue())
2198                             {
2199                                 case BLANK :
2200                                 {
2201                                     chunk.setJavaScriptAction(
2202                                             "if (app.viewerVersion < 7)"
2203                                                 + "{this.getURL(\"" + link.getHyperlinkReference() + "\");}"
2204                                                 + "else {app.launchURL(\"" + link.getHyperlinkReference() + "\", true);};"
2205                                         );
2206                                     break;
2207                                 }
2208                                 case SELF :
2209                                 default :
2210                                 {
2211                                     chunk.setAnchor(link.getHyperlinkReference());
2212                                     break;
2213                                 }
2214                             }
2215                         }
2216                         break;
2217                     }
2218                     case LOCAL_ANCHOR :
2219                     {
2220                         if (link.getHyperlinkAnchor() != null)
2221                         {
2222                             chunk.setLocalGoto(link.getHyperlinkAnchor());
2223                         }
2224                         break;
2225                     }
2226                     case LOCAL_PAGE :
2227                     {
2228                         if (link.getHyperlinkPage() != null)
2229                         {
2230                             chunk.setLocalGoto(JR_PAGE_ANCHOR_PREFIX + reportIndex + "_" + link.getHyperlinkPage().toString());
2231                         }
2232                         break;
2233                     }
2234                     case REMOTE_ANCHOR :
2235                     {
2236                         if (
2237                             link.getHyperlinkReference() != null &&
2238                             link.getHyperlinkAnchor() != null
2239                             )
2240                         {
2241                             chunk.setRemoteGoto(
2242                                 link.getHyperlinkReference(),
2243                                 link.getHyperlinkAnchor()
2244                                 );
2245                         }
2246                         break;
2247                     }
2248                     case REMOTE_PAGE :
2249                     {
2250                         if (
2251                             link.getHyperlinkReference() != null &&
2252                             link.getHyperlinkPage() != null
2253                             )
2254                         {
2255                             chunk.setRemoteGoto(
2256                                 link.getHyperlinkReference(),
2257                                 link.getHyperlinkPage()
2258                                 );
2259                         }
2260                         break;
2261                     }
2262                     case CUSTOM :
2263                     {
2264                         JRHyperlinkProducerFactory hyperlinkProducerFactory = getCurrentItemConfiguration().getHyperlinkProducerFactory();
2265                         if (hyperlinkProducerFactory != null)
2266                         {
2267                             String hyperlink = hyperlinkProducerFactory.produceHyperlink(link);
2268                             if (hyperlink != null)
2269                             {
2270                                 switch(link.getHyperlinkTargetValue())
2271                                 {
2272                                     case BLANK :
2273                                     {
2274                                         chunk.setJavaScriptAction(
2275                                                 "if (app.viewerVersion < 7)"
2276                                                     + "{this.getURL(\"" + hyperlink + "\");}"
2277                                                     + "else {app.launchURL(\"" + hyperlink + "\", true);};"
2278                                             );
2279                                         break;
2280                                     }
2281                                     case SELF :
2282                                     default :
2283                                     {
2284                                         chunk.setAnchor(hyperlink);
2285                                         break;
2286                                     }
2287                                 }
2288                             }
2289                         }
2290                     }
2291                     case NONE :
2292                     default :
2293                     {
2294                         break;
2295                     }
2296                 }
2297             }
2298         }
2299     }
2300     
2301     @Override
2302     protected Locale getTextLocale(JRPrintText text)
2303     {
2304         // only overriding for package access
2305         return super.getTextLocale(text);
2306     }
2307
2308
2309     /**
2310      *
2311      */

2312     protected void getPhrase(AttributedString as, String text, JRPrintText textElement,
2313             PdfPhrase phrase)
2314     {
2315         int runLimit = 0;
2316
2317         AttributedCharacterIterator iterator = as.getIterator();
2318         Locale locale = getTextLocale(textElement);
2319          
2320         boolean firstChunk = true;
2321         while(runLimit < text.length() && (runLimit = iterator.getRunLimit()) <= text.length())
2322         {
2323             Map<Attribute,Object> attributes = iterator.getAttributes();
2324             PdfTextChunk chunk = getChunk(attributes, text.substring(iterator.getIndex(), runLimit), locale);
2325             
2326             if (firstChunk)
2327             {
2328                 // only set anchor + bookmark for the first chunk in the text
2329                 setAnchor(chunk, textElement, textElement);
2330             }
2331             
2332             JRPrintHyperlink hyperlink = textElement;
2333             if (hyperlink.getHyperlinkTypeValue() == HyperlinkTypeEnum.NONE)
2334             {
2335                 hyperlink = (JRPrintHyperlink)attributes.get(JRTextAttribute.HYPERLINK);
2336             }
2337             
2338             setHyperlinkInfo(chunk, hyperlink);
2339             phrase.add(chunk);
2340
2341             iterator.setIndex(runLimit);
2342             firstChunk = false;
2343         }
2344     }
2345
2346
2347     /**
2348      *
2349      */

2350     protected PdfTextChunk getChunk(Map<Attribute,Object> attributes, String text, Locale locale)
2351     {
2352         // underline and strikethrough are set on the chunk below
2353         PdfTextChunk chunk = pdfProducer.createChunk(text, attributes, locale);
2354         
2355         if (hasUnderline(attributes))
2356         {
2357             chunk.setUnderline();
2358         }
2359         
2360         if (hasStrikethrough(attributes))
2361         {
2362             chunk.setStrikethrough();
2363         }
2364
2365         Color backcolor = (Color)attributes.get(TextAttribute.BACKGROUND);
2366         if (backcolor != null)
2367         {
2368             chunk.setBackground(backcolor);
2369         }
2370
2371         Object script = attributes.get(TextAttribute.SUPERSCRIPT);
2372         if (script != null)
2373         {
2374             if (TextAttribute.SUPERSCRIPT_SUPER.equals(script))
2375             {
2376                 chunk.setSuperscript();
2377             }
2378             else if (TextAttribute.SUPERSCRIPT_SUB.equals(script))
2379             {
2380                 chunk.setSubscript();
2381             }
2382         }
2383
2384         return chunk;
2385     }
2386
2387     protected boolean hasUnderline(Map<Attribute,Object> textAttributes)
2388     {
2389         Integer underline = (Integer) textAttributes.get(TextAttribute.UNDERLINE);
2390         return TextAttribute.UNDERLINE_ON.equals(underline);
2391     }
2392
2393     protected boolean hasStrikethrough(Map<Attribute,Object> textAttributes)
2394     {
2395         Boolean strike = (Boolean) textAttributes.get(TextAttribute.STRIKETHROUGH);
2396         return TextAttribute.STRIKETHROUGH_ON.equals(strike);
2397     }
2398
2399
2400     /**
2401      * Creates a PDF font.
2402      * 
2403      * @param attributes the text attributes of the font
2404      * @param locale the locale for which to create the font
2405      * @param setFontLines whether to set underline and strikethrough as font style
2406      * @param recipient the font recipient
2407      */

2408     protected void setFont(Map<Attribute,Object> attributes, Locale locale, boolean setFontLines,
2409             FontRecipient recipient)
2410     {
2411         JRFont jrFont = new JRBaseFont(attributes);
2412
2413         Exception initialException = null;
2414
2415         Color forecolor = (Color)attributes.get(TextAttribute.FOREGROUND);
2416
2417         // use the same font scale ratio as in JRStyledText.getAwtAttributedString
2418         float fontSizeScale = 1f;
2419         Integer scriptStyle = (Integer) attributes.get(TextAttribute.SUPERSCRIPT);
2420         if (scriptStyle != null && (
2421                 TextAttribute.SUPERSCRIPT_SUB.equals(scriptStyle)
2422                 || TextAttribute.SUPERSCRIPT_SUPER.equals(scriptStyle)))
2423         {
2424             fontSizeScale = 2f / 3;
2425         }
2426         
2427         String pdfFontName = null;
2428         String pdfEncoding = null;
2429         boolean isPdfEmbedded = false;
2430         boolean isPdfSimulatedBold = false;
2431         boolean isPdfSimulatedItalic = false;
2432
2433         FontInfo fontInfo = (FontInfo) attributes.get(JRTextAttribute.FONT_INFO);
2434         if (fontInfo == null)
2435         {
2436             fontInfo = fontUtil.getFontInfo(jrFont.getFontName(), locale);
2437         }
2438         
2439         if (fontInfo == null)
2440         {
2441             //fontName NOT found in font extensions
2442             pdfFontName = jrFont.getPdfFontName();
2443             pdfEncoding = jrFont.getPdfEncoding();
2444             isPdfEmbedded = jrFont.isPdfEmbedded();
2445         }
2446         else
2447         {
2448             //fontName found in font extensions
2449             FontFamily family = fontInfo.getFontFamily();
2450             
2451             int pdfFontStyle = java.awt.Font.PLAIN;
2452             
2453             FontFace fontFace = fontInfo.getFontFace();
2454             if (fontFace != null)
2455             {
2456                 pdfFontName = fontFace.getPdf();
2457                 pdfFontName = pdfFontName == null ? fontFace.getTtf() : pdfFontName;
2458                 pdfFontStyle = fontInfo.getStyle();
2459             }
2460             
2461             if (pdfFontName == null && jrFont.isBold() && jrFont.isItalic())
2462             {
2463                 fontFace = family.getBoldItalicFace();
2464                 if (fontFace != null)
2465                 {
2466                     pdfFontName = fontFace.getPdf();
2467                     pdfFontName = pdfFontName == null ? fontFace.getTtf() : pdfFontName;
2468                     pdfFontStyle = java.awt.Font.BOLD | java.awt.Font.ITALIC;
2469                 }
2470             }
2471             
2472             if (pdfFontName == null && jrFont.isBold())
2473             {
2474                 fontFace = family.getBoldFace();
2475                 if (fontFace != null)
2476                 {
2477                     pdfFontName = fontFace.getPdf();
2478                     pdfFontName = pdfFontName == null ? fontFace.getTtf() : pdfFontName;
2479                     pdfFontStyle = java.awt.Font.BOLD;
2480                 }
2481             }
2482             
2483             if (pdfFontName == null && jrFont.isItalic())
2484             {
2485                 fontFace = family.getItalicFace();
2486                 if (fontFace != null)
2487                 {
2488                     pdfFontName = fontFace.getPdf();
2489                     pdfFontName = pdfFontName == null ? fontFace.getTtf() : pdfFontName;
2490                     pdfFontStyle = java.awt.Font.ITALIC;
2491                 }
2492             }
2493             
2494             if (pdfFontName == null)
2495             {
2496                 fontFace = family.getNormalFace();
2497                 if (fontFace != null)
2498                 {
2499                     pdfFontName = fontFace.getPdf();
2500                     pdfFontName = pdfFontName == null ? fontFace.getTtf() : pdfFontName;
2501                     pdfFontStyle = java.awt.Font.PLAIN;
2502                 }
2503             }
2504
2505             if (pdfFontName == null)
2506             {
2507                 pdfFontName = jrFont.getPdfFontName();
2508             }
2509
2510             pdfEncoding = family.getPdfEncoding() == null ? jrFont.getPdfEncoding() : family.getPdfEncoding();
2511             isPdfEmbedded = family.isPdfEmbedded() == null ? jrFont.isPdfEmbedded() : family.isPdfEmbedded(); 
2512             isPdfSimulatedBold = jrFont.isBold() && ((pdfFontStyle & java.awt.Font.BOLD) == 0); 
2513             isPdfSimulatedItalic = jrFont.isItalic() && ((pdfFontStyle & java.awt.Font.ITALIC) == 0); 
2514         }
2515
2516         PdfFontStyle pdfFontStyle = new PdfFontStyle(isPdfSimulatedBold, isPdfSimulatedItalic,
2517                 setFontLines && jrFont.isUnderline(), setFontLines && jrFont.isStrikeThrough());
2518         
2519         try
2520         {
2521             recipient.setFont(
2522                 pdfFontName,
2523                 pdfEncoding,
2524                 isPdfEmbedded,
2525                 jrFont.getFontsize() * fontSizeScale,
2526                 pdfFontStyle,
2527                 forecolor
2528                 );
2529         }
2530         catch(Exception e)
2531         {
2532             initialException = e;
2533         }
2534
2535         if (!recipient.hasFont())
2536         {
2537             byte[] bytes = null;
2538
2539             try
2540             {
2541                 bytes = getRepository().getBytesFromLocation(pdfFontName);
2542             }
2543             catch(JRException e)
2544             {
2545                 throw //NOPMD
2546                     new JRRuntimeException(
2547                         EXCEPTION_MESSAGE_KEY_FONT_LOADING_ERROR,
2548                         new Object[]{pdfFontName, pdfEncoding, isPdfEmbedded},
2549                         initialException);
2550             }
2551
2552             recipient.setFont(pdfFontName, pdfEncoding, isPdfEmbedded, 
2553                     jrFont.getFontsize() * fontSizeScale, pdfFontStyle, forecolor, bytes);
2554         }
2555     }
2556
2557
2558     /**
2559      *
2560      */

2561     public void exportText(JRPrintText text)
2562     {
2563         JRStyledText styledText = styledTextUtil.getProcessedStyledText(text, noBackcolorSelector, null);
2564
2565         if (styledText == null)
2566         {
2567             return;
2568         }
2569         
2570         AbstractPdfTextRenderer textRenderer = getTextRenderer(text, styledText);
2571         textRenderer.initialize(this, pdfProducer, text, styledText, getOffsetX(), getOffsetY());
2572
2573         double angle = 0;
2574
2575         switch (text.getRotationValue())
2576         {
2577             case LEFT :
2578             {
2579                 angle = Math.PI / 2;
2580                 break;
2581             }
2582             case RIGHT :
2583             {
2584                 angle = - Math.PI / 2;
2585                 break;
2586             }
2587             case UPSIDE_DOWN :
2588             {
2589                 angle = Math.PI;
2590                 break;
2591             }
2592             case NONE :
2593             default :
2594             {
2595             }
2596         }
2597
2598         AffineTransform atrans = new AffineTransform();
2599         atrans.rotate(angle, textRenderer.getX(), pageFormat.getPageHeight() - textRenderer.getY());
2600         pdfContent.transform(atrans);
2601
2602         if (text.getModeValue() == ModeEnum.OPAQUE)
2603         {
2604             Color backcolor = text.getBackcolor();
2605             pdfContent.setFillColor(backcolor);
2606             pdfContent.fillRectangle(
2607                 textRenderer.getX(),
2608                 pageFormat.getPageHeight() - textRenderer.getY(),
2609                 textRenderer.getWidth(),
2610                 - textRenderer.getHeight()
2611                 );
2612             pdfContent.resetFillColor();
2613         }
2614         
2615         if (textRenderer.addActualText())
2616         {
2617             tagHelper.startText(styledText.getText(), text.getLinkType() != null);
2618         }
2619         else
2620         {
2621             tagHelper.startText(text.getLinkType() != null);
2622         }
2623
2624         int forecolorAlpha = getSingleForecolorAlpha(styledText);
2625         pdfContent.setFillColorAlpha(forecolorAlpha);
2626         
2627         /* rendering only non empty texts  */
2628         if (styledText.length() > 0)
2629         {
2630             textRenderer.render();
2631         }
2632         tagHelper.endText();
2633         
2634         pdfContent.resetFillColor();
2635
2636         atrans = new AffineTransform();
2637         atrans.rotate(-angle, textRenderer.getX(), pageFormat.getPageHeight() - textRenderer.getY());
2638         pdfContent.transform(atrans);
2639
2640         /*   */
2641         exportBox(
2642             text.getLineBox(),
2643             text
2644             );
2645     }
2646     
2647     protected int getSingleForecolorAlpha(JRStyledText styledText)
2648     {
2649         Color forecolor = (Color) styledText.getGlobalAttributes().get(TextAttribute.FOREGROUND);
2650         if (forecolor == null || forecolor.getAlpha() == 255)
2651         {
2652             return 255;
2653         }
2654         
2655         List<JRStyledText.Run> runs = styledText.getRuns();
2656         if (runs.size() > 1)
2657         {
2658             for (JRStyledText.Run run : runs)
2659             {
2660                 Color runForecolor = (Color) run.attributes.get(TextAttribute.FOREGROUND);
2661                 if (runForecolor != null && runForecolor.getAlpha() != forecolor.getAlpha())
2662                 {
2663                     //per run alpha currently not working because there's no support in Chunk
2664                     //falling back to opaque
2665                     return 255;
2666                 }
2667             }
2668         }
2669         
2670         return forecolor.getAlpha();
2671     }
2672     
2673     /**
2674      *
2675      */

2676     public void exportFieldText(JRPrintText text, PdfFieldTypeEnum fieldType)
2677     {
2678         String fieldName = text.getPropertiesMap().getProperty(PDF_FIELD_NAME);
2679         fieldName = fieldName == null || fieldName.trim().length() == 0 ? "FIELD_" + text.getUUID() : fieldName;
2680         
2681         String value = null;
2682         if (text.getPropertiesMap().containsProperty(PDF_FIELD_VALUE))
2683         {
2684             value = text.getPropertiesMap().getProperty(PDF_FIELD_VALUE);
2685         }
2686         else
2687         {
2688             value = text.getFullText();
2689         }
2690         
2691         int llx = text.getX() + exporterContext.getOffsetX();
2692         int lly = jasperPrint.getPageHeight() - text.getY() - exporterContext.getOffsetY();
2693         int urx = llx + text.getWidth();
2694         int ury = jasperPrint.getPageHeight() - text.getY() - exporterContext.getOffsetY() - text.getHeight();
2695         
2696         PdfTextField pdfTextField;
2697         switch (fieldType)
2698         {
2699         case TEXT:
2700             pdfTextField = pdfProducer.createTextField(llx, lly, urx, ury, fieldName);            
2701             if (value != null)
2702             {
2703                 pdfTextField.setText(value);
2704             }
2705             break;
2706         case COMBO:
2707             String[] comboChoices = getPdfFieldChoices(text);
2708             pdfTextField = pdfProducer.createComboField(llx, lly, urx, ury, fieldName, value, comboChoices);
2709             if (propertiesUtil.getBooleanProperty(PDF_FIELD_COMBO_EDIT, false, text, jasperPrint))
2710             {
2711                 pdfTextField.setEdit();
2712             }
2713             break;
2714         case LIST:
2715             String[] listChoices = getPdfFieldChoices(text);
2716             pdfTextField = pdfProducer.createListField(llx, lly, urx, ury, fieldName, value, listChoices);            
2717             break;
2718         default:
2719             throw new JRRuntimeException("Unknown field type " + fieldType);
2720         }
2721         
2722         if (ModeEnum.OPAQUE == text.getModeValue())
2723         {
2724             pdfTextField.setBackgroundColor(text.getBackcolor());
2725         }
2726         pdfTextField.setTextColor(text.getForecolor());
2727         
2728         switch (text.getHorizontalTextAlign())
2729         {
2730             case RIGHT :
2731                 pdfTextField.setAlignment(PdfTextAlignment.RIGHT);
2732                 break;
2733             case CENTER :
2734                 pdfTextField.setAlignment(PdfTextAlignment.CENTER);
2735                 break;
2736             case JUSTIFIED :
2737                 pdfTextField.setAlignment(PdfTextAlignment.JUSTIFIED);
2738                 break;
2739             case LEFT :
2740             default :
2741                 pdfTextField.setAlignment(PdfTextAlignment.LEFT);
2742         }
2743         
2744         JRPen pen = getFieldPen(text);
2745         if (pen != null)
2746         {
2747             float borderWidth = Math.round(pen.getLineWidth());
2748             if (borderWidth > 0)
2749             {
2750                 pdfTextField.setBorderColor(pen.getLineColor());
2751                 pdfTextField.setBorderWidth(borderWidth);
2752                 String strBorderStyle = propertiesUtil.getProperty(PDF_FIELD_BORDER_STYLE, text, jasperPrint);
2753                 PdfFieldBorderStyleEnum borderStyle = PdfFieldBorderStyleEnum.getByName(strBorderStyle);
2754                 if (borderStyle == null)
2755                 {
2756                     borderStyle = pen.getLineStyleValue() == LineStyleEnum.DASHED ? PdfFieldBorderStyleEnum.DASHED : PdfFieldBorderStyleEnum.SOLID;
2757                 }
2758                 pdfTextField.setBorderStyle(borderStyle);
2759             }
2760         }
2761
2762         String readOnly = text.getPropertiesMap().getProperty(PDF_FIELD_READ_ONLY);
2763         if (readOnly != null)
2764         {
2765             if (Boolean.valueOf(readOnly))
2766             {
2767                 pdfTextField.setReadOnly();
2768             }
2769         }
2770         
2771 //        pdfTextField.setExtraMargin(0, 0);
2772         
2773         Map<Attribute,Object> attributes = new HashMap<Attribute,Object>();
2774         fontUtil.getAttributesWithoutAwtFont(attributes, text);
2775         pdfTextField.setFont(attributes, getLocale());
2776         pdfTextField.setFontSize(text.getFontsize());
2777 //        pdfTextField.setExtensionFont(pdfFont.getBaseFont());
2778         
2779         boolean isMultiLine = JRPropertiesUtil.asBoolean(text.getPropertiesMap().getProperty(PDF_FIELD_TEXT_MULTILINE), false);
2780         if (isMultiLine)
2781         {
2782             pdfTextField.setMultiline();
2783         }
2784         
2785         if (pageFormat.getOrientation() == OrientationEnum.LANDSCAPE)
2786         {
2787             pdfTextField.setRotation(90);
2788         }
2789         pdfTextField.setVisible();
2790
2791         pdfTextField.add();
2792     }
2793
2794     protected String[] getPdfFieldChoices(JRPrintText text)
2795     {
2796         String[] choices = null;
2797         String strChoices = text.getPropertiesMap().getProperty(PDF_FIELD_CHOICES);
2798         if (strChoices != null && strChoices.trim().length() > 0)
2799         {
2800             String choiceSeparators = propertiesUtil.getProperty(PDF_FIELD_CHOICE_SEPARATORS, text, jasperPrint);
2801             StringTokenizer tkzer = new StringTokenizer(strChoices, choiceSeparators);
2802             List<String> choicesList = new ArrayList<String>();
2803             while (tkzer.hasMoreTokens())
2804             {
2805                 choicesList.add(tkzer.nextToken());
2806             }
2807             choices = choicesList.toArray(new String[choicesList.size()]);
2808         }
2809         return choices;
2810     }
2811     
2812     /**
2813      *
2814      */

2815     public void exportFieldCheck(JRPrintElement element)
2816     {
2817         String fieldName = element.getPropertiesMap().getProperty(PDF_FIELD_NAME);
2818         fieldName = fieldName == null || fieldName.trim().length() == 0 ? "FIELD_" + element.getUUID() : fieldName;
2819         
2820         PdfRadioCheck checkField = pdfProducer.createCheckField(
2821                 element.getX() + exporterContext.getOffsetX(),
2822                 jasperPrint.getPageHeight() - element.getY() - exporterContext.getOffsetY(),
2823                 element.getX() + exporterContext.getOffsetX() + element.getWidth(),
2824                 jasperPrint.getPageHeight() - element.getY() - exporterContext.getOffsetY() - element.getHeight(),
2825                 fieldName,
2826                 "checked"
2827         );
2828         
2829         PdfFieldCheckTypeEnum checkType = PdfFieldCheckTypeEnum.getByName(element.getPropertiesMap().getProperty(PDF_FIELD_CHECK_TYPE));
2830         if (checkType != null)
2831         {
2832             checkField.setCheckType(checkType);
2833         }
2834
2835         if (ModeEnum.OPAQUE == element.getModeValue())
2836         {
2837             checkField.setBackgroundColor(element.getBackcolor());
2838         }
2839         checkField.setTextColor(element.getForecolor());
2840
2841         JRPen pen = getFieldPen(element);
2842         if (pen != null)
2843         {
2844             float borderWidth = Math.round(pen.getLineWidth());
2845             if (borderWidth > 0)
2846             {
2847                 checkField.setBorderColor(pen.getLineColor());
2848                 checkField.setBorderWidth(borderWidth);
2849                 String strBorderStyle = propertiesUtil.getProperty(PDF_FIELD_BORDER_STYLE, element, jasperPrint);
2850                 PdfFieldBorderStyleEnum borderStyle = PdfFieldBorderStyleEnum.getByName(strBorderStyle);
2851                 if (borderStyle == null)
2852                 {
2853                     borderStyle = pen.getLineStyleValue() == LineStyleEnum.DASHED ? PdfFieldBorderStyleEnum.DASHED : PdfFieldBorderStyleEnum.SOLID;
2854                 }
2855                 checkField.setBorderStyle(borderStyle);
2856             }
2857         }
2858         
2859         String checked = element.getPropertiesMap().getProperty(PDF_FIELD_CHECKED);
2860         if (checked != null)
2861         {
2862             checkField.setChecked(Boolean.valueOf(checked));
2863         }
2864
2865         String readOnly = element.getPropertiesMap().getProperty(PDF_FIELD_READ_ONLY);
2866         if (readOnly != null)
2867         {
2868             if (Boolean.valueOf(readOnly))
2869             {
2870                 checkField.setReadOnly();
2871             }
2872         }
2873
2874         checkField.add();
2875     }
2876     
2877     /**
2878      *
2879      */

2880     public void exportFieldRadio(JRPrintElement element) throws IOException
2881     {
2882         String fieldName = element.getPropertiesMap().getProperty(PDF_FIELD_NAME);
2883         fieldName = fieldName == null || fieldName.trim().length() == 0 ? "FIELD_" + element.getUUID() : fieldName;
2884         
2885         PdfRadioCheck radioField = pdfProducer.getRadioField(
2886                 element.getX() + exporterContext.getOffsetX(),
2887                 jasperPrint.getPageHeight() - element.getY() - exporterContext.getOffsetY(),
2888                 element.getX() + exporterContext.getOffsetX() + element.getWidth(),
2889                 jasperPrint.getPageHeight() - element.getY() - exporterContext.getOffsetY() - element.getHeight(),
2890                 fieldName,
2891                 "FIELD_" + element.getUUID());
2892
2893         PdfFieldCheckTypeEnum checkType = PdfFieldCheckTypeEnum.getByName(element.getPropertiesMap().getProperty(PDF_FIELD_CHECK_TYPE));
2894         if (checkType != null)
2895         {
2896             radioField.setCheckType(checkType);
2897         }
2898
2899         if (ModeEnum.OPAQUE == element.getModeValue())
2900         {
2901             radioField.setBackgroundColor(element.getBackcolor());
2902         }
2903         radioField.setTextColor(element.getForecolor());
2904
2905         JRPen pen = getFieldPen(element);
2906         if (pen != null)
2907         {
2908             float borderWidth = Math.round(pen.getLineWidth());
2909             if (borderWidth > 0)
2910             {
2911                 radioField.setBorderColor(pen.getLineColor());
2912                 radioField.setBorderWidth(borderWidth);
2913                 String strBorderStyle = propertiesUtil.getProperty(PDF_FIELD_BORDER_STYLE, element, jasperPrint);
2914                 PdfFieldBorderStyleEnum borderStyle = PdfFieldBorderStyleEnum.getByName(strBorderStyle);
2915                 if (borderStyle == null)
2916                 {
2917                     borderStyle = pen.getLineStyleValue() == LineStyleEnum.DASHED ? PdfFieldBorderStyleEnum.DASHED : PdfFieldBorderStyleEnum.SOLID;
2918                 }
2919                 radioField.setBorderStyle(borderStyle);
2920             }
2921         }
2922         
2923         radioField.setOnValue("FIELD_" + element.getUUID());
2924
2925         String checked = element.getPropertiesMap().getProperty(PDF_FIELD_CHECKED);
2926         radioField.setChecked(Boolean.valueOf(checked)); // need to set to false if previous button was checked
2927
2928         // setting the read-only option has to occur before the getRadioGroup() call
2929         String readOnly = element.getPropertiesMap().getProperty(PDF_FIELD_READ_ONLY);
2930         if (readOnly != null)
2931         {
2932             if (Boolean.valueOf(readOnly))
2933             {
2934                 radioField.setReadOnly();
2935             }
2936         }
2937
2938         radioField.addToGroup();
2939     }
2940     
2941     protected JRPen getFieldPen(JRPrintElement element)
2942     {
2943         JRPen pen = null;
2944
2945         JRLineBox box = element instanceof JRBoxContainer ? ((JRBoxContainer)element).getLineBox() : null;
2946         
2947         if (box == null)
2948         {
2949             pen = element instanceof JRCommonGraphicElement ? ((JRCommonGraphicElement)element).getLinePen() : null;
2950         }
2951         else
2952         {
2953             Float lineWidth = box.getPen().getLineWidth();
2954             if (lineWidth == 0)
2955             {
2956                 // PDF fields do not support side borders
2957                 // in case side borders are defined for the report element, ensure that all 4 are declared and all of them come with the same settings
2958                 if(
2959                     ((JRBasePen)box.getTopPen()).isIdentical(box.getLeftPen())
2960                     && ((JRBasePen)box.getTopPen()).isIdentical(box.getBottomPen())
2961                     && ((JRBasePen)box.getTopPen()).isIdentical(box.getRightPen())
2962                     && box.getTopPen().getLineWidth() > 0
2963                     )
2964                 {
2965                     pen = new JRBasePen(box);
2966                     pen.setLineWidth(box.getTopPen().getLineWidth());
2967                     pen.setLineColor(box.getTopPen().getLineColor());
2968                     pen.setLineStyle(box.getTopPen().getLineStyleValue());
2969                 }
2970             }
2971             else
2972             {
2973                 pen = new JRBasePen(box);
2974                 pen.setLineWidth(lineWidth);
2975                 pen.setLineColor(box.getPen().getLineColor());
2976                 pen.setLineStyle(box.getPen().getLineStyleValue());
2977             }
2978         }
2979
2980         return pen;
2981     }
2982     
2983     protected AbstractPdfTextRenderer getTextRenderer(JRPrintText text, JRStyledText styledText)
2984     {
2985         Locale textLocale = getTextLocale(text);
2986         AbstractPdfTextRenderer textRenderer = pdfProducer.getTextRenderer(
2987                 text, styledText, textLocale,
2988                 awtIgnoreMissingFont, defaultIndentFirstLine, defaultJustifyLastLine);
2989         return textRenderer;
2990     }
2991
2992
2993     /**
2994      *
2995      */

2996     protected void exportBox(JRLineBox box, JRPrintElement element)
2997     {
2998         exportTopPen(box.getTopPen(), box.getLeftPen(), box.getRightPen(), element);
2999         exportLeftPen(box.getTopPen(), box.getLeftPen(), box.getBottomPen(), element);
3000         exportBottomPen(box.getLeftPen(), box.getBottomPen(), box.getRightPen(), element);
3001         exportRightPen(box.getTopPen(), box.getBottomPen(), box.getRightPen(), element);
3002
3003         pdfContent.setLineDash(0f);
3004         pdfContent.setLineCap(LineCapStyle.PROJECTING_SQUARE);
3005     }
3006
3007
3008     /**
3009      *
3010      */

3011     protected void exportPen(JRPen pen, JRPrintElement element)
3012     {
3013         exportTopPen(pen, pen, pen, element);
3014         exportLeftPen(pen, pen, pen, element);
3015         exportBottomPen(pen, pen, pen, element);
3016         exportRightPen(pen, pen, pen, element);
3017
3018         pdfContent.setLineDash(0f);
3019         pdfContent.setLineCap(LineCapStyle.PROJECTING_SQUARE);
3020     }
3021
3022
3023     /**
3024      *
3025      */

3026     protected void exportTopPen(
3027         JRPen topPen, 
3028         JRPen leftPen, 
3029         JRPen rightPen, 
3030         JRPrintElement element)
3031     {
3032         if (topPen.getLineWidth() > 0f)
3033         {
3034             float leftOffset = leftPen.getLineWidth() / 2;
3035             float rightOffset = rightPen.getLineWidth() / 2;
3036             int lcOffsetX = getOffsetX();
3037             int lcOffsetY = getOffsetY();
3038             
3039             preparePen(topPen, LineCapStyle.BUTT);
3040             
3041             if (topPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
3042             {
3043                 float topOffset = topPen.getLineWidth();
3044
3045                 pdfContent.strokeLine(
3046                     element.getX() + lcOffsetX - leftOffset,
3047                     pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset / 3,
3048                     element.getX() + lcOffsetX + element.getWidth() + rightOffset,
3049                     pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset / 3
3050                     );
3051
3052                 pdfContent.strokeLine(
3053                     element.getX() + lcOffsetX + leftOffset / 3,
3054                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - topOffset / 3,
3055                     element.getX() + lcOffsetX + element.getWidth() - rightOffset / 3,
3056                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - topOffset / 3
3057                     );
3058             }
3059             else
3060             {
3061                 pdfContent.strokeLine(
3062                     element.getX() + lcOffsetX - leftOffset,
3063                     pageFormat.getPageHeight() - element.getY() - lcOffsetY,
3064                     element.getX() + lcOffsetX + element.getWidth() + rightOffset,
3065                     pageFormat.getPageHeight() - element.getY() - lcOffsetY
3066                     );
3067             }
3068             
3069             resetPen();
3070         }
3071     }
3072
3073
3074     /**
3075      *
3076      */

3077     protected void exportLeftPen(JRPen topPen, JRPen leftPen, JRPen bottomPen, JRPrintElement element)
3078     {
3079         if (leftPen.getLineWidth() > 0f)
3080         {
3081             float topOffset = topPen.getLineWidth() / 2;
3082             float bottomOffset = bottomPen.getLineWidth() / 2;
3083             int lcOffsetX = getOffsetX();
3084             int lcOffsetY = getOffsetY();
3085
3086             preparePen(leftPen, LineCapStyle.BUTT);
3087
3088             if (leftPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
3089             {
3090                 float leftOffset = leftPen.getLineWidth();
3091
3092                 pdfContent.strokeLine(
3093                     element.getX() + lcOffsetX - leftOffset / 3,
3094                     pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset,
3095                     element.getX() + lcOffsetX - leftOffset / 3,
3096                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset
3097                     );
3098
3099                 pdfContent.strokeLine(
3100                     element.getX() + lcOffsetX + leftOffset / 3,
3101                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - topOffset / 3,
3102                     element.getX() + lcOffsetX + leftOffset / 3,
3103                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() + bottomOffset / 3
3104                     );
3105             }
3106             else
3107             {
3108                 pdfContent.strokeLine(
3109                     element.getX() + lcOffsetX,
3110                     pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset,
3111                     element.getX() + lcOffsetX,
3112                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset
3113                     );
3114             }
3115             
3116             resetPen();
3117         }
3118     }
3119
3120
3121     /**
3122      *
3123      */

3124     protected void exportBottomPen(JRPen leftPen, JRPen bottomPen, JRPen rightPen, JRPrintElement element)
3125     {
3126         if (bottomPen.getLineWidth() > 0f)
3127         {
3128             float leftOffset = leftPen.getLineWidth() / 2;
3129             float rightOffset = rightPen.getLineWidth() / 2;
3130             int lcOffsetX = getOffsetX();
3131             int lcOffsetY = getOffsetY();
3132             
3133             preparePen(bottomPen, LineCapStyle.BUTT);
3134             
3135             if (bottomPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
3136             {
3137                 float bottomOffset = bottomPen.getLineWidth();
3138
3139                 pdfContent.strokeLine(
3140                     element.getX() + lcOffsetX - leftOffset,
3141                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset / 3,
3142                     element.getX() + lcOffsetX + element.getWidth() + rightOffset,
3143                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset / 3
3144                     );
3145
3146                 pdfContent.strokeLine(
3147                     element.getX() + lcOffsetX + leftOffset / 3,
3148                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() + bottomOffset / 3,
3149                     element.getX() + lcOffsetX + element.getWidth() - rightOffset / 3,
3150                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() + bottomOffset / 3
3151                     );
3152             }
3153             else
3154             {
3155                 pdfContent.strokeLine(
3156                     element.getX() + lcOffsetX - leftOffset,
3157                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight(),
3158                     element.getX() + lcOffsetX + element.getWidth() + rightOffset,
3159                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight()
3160                     );
3161             }
3162             
3163             resetPen();
3164         }
3165     }
3166
3167
3168     /**
3169      *
3170      */

3171     protected void exportRightPen(JRPen topPen, JRPen bottomPen, JRPen rightPen, JRPrintElement element)
3172     {
3173         if (rightPen.getLineWidth() > 0f)
3174         {
3175             float topOffset = topPen.getLineWidth() / 2;
3176             float bottomOffset = bottomPen.getLineWidth() / 2;
3177             int lcOffsetX = getOffsetX();
3178             int lcOffsetY = getOffsetY();
3179
3180             preparePen(rightPen, LineCapStyle.BUTT);
3181
3182             if (rightPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
3183             {
3184                 float rightOffset = rightPen.getLineWidth();
3185
3186                 pdfContent.strokeLine(
3187                     element.getX() + lcOffsetX + element.getWidth() + rightOffset / 3,
3188                     pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset,
3189                     element.getX() + lcOffsetX + element.getWidth() + rightOffset / 3,
3190                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset
3191                     );
3192
3193                 pdfContent.strokeLine(
3194                     element.getX() + lcOffsetX + element.getWidth() - rightOffset / 3,
3195                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - topOffset / 3,
3196                     element.getX() + lcOffsetX + element.getWidth() - rightOffset / 3,
3197                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() + bottomOffset / 3
3198                     );
3199             }
3200             else
3201             {
3202                 pdfContent.strokeLine(
3203                     element.getX() + lcOffsetX + element.getWidth(),
3204                     pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset,
3205                     element.getX() + lcOffsetX + element.getWidth(),
3206                     pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset
3207                     );
3208             }
3209             
3210             resetPen();
3211         }
3212     }
3213
3214
3215     /**
3216      *
3217      */

3218     private void preparePen(JRPen pen, LineCapStyle lineCap)
3219     {
3220         float lineWidth = pen.getLineWidth();
3221
3222         if (lineWidth <= 0)
3223         {
3224             return;
3225         }
3226         
3227         PdfContent pdfContent = pdfProducer.getPdfContent();
3228         pdfContent.setLineWidth(lineWidth);
3229         pdfContent.setLineCap(lineCap);
3230
3231         Color color = pen.getLineColor();
3232         pdfContent.setStrokeColor(color);
3233
3234         switch (pen.getLineStyleValue())
3235         {
3236             case DOUBLE :
3237             {
3238                 pdfContent.setLineWidth(lineWidth / 3);
3239                 pdfContent.setLineDash(0f);
3240                 break;
3241             }
3242             case DOTTED :
3243             {
3244                 switch (lineCap)
3245                 {
3246                     case BUTT :
3247                     {
3248                         pdfContent.setLineDash(lineWidth, lineWidth, 0f);
3249                         break;
3250                     }
3251                     case PROJECTING_SQUARE :
3252                     {
3253                         pdfContent.setLineDash(0, 2 * lineWidth, 0f);
3254                         break;
3255                     }
3256                 }
3257                 break;
3258             }
3259             case DASHED :
3260             {
3261                 switch (lineCap)
3262                 {
3263                     case BUTT :
3264                     {
3265                         pdfContent.setLineDash(5 * lineWidth, 3 * lineWidth, 0f);
3266                         break;
3267                     }
3268                     case PROJECTING_SQUARE :
3269                     {
3270                         pdfContent.setLineDash(4 * lineWidth, 4 * lineWidth, 0f);
3271                         break;
3272                     }
3273                 }
3274                 break;
3275             }
3276             case SOLID :
3277             default :
3278             {
3279                 pdfContent.setLineDash(0f);
3280                 break;
3281             }
3282         }
3283     }
3284     
3285     private void resetPen()
3286     {
3287         pdfContent.resetStrokeColor();
3288     }
3289
3290     protected static synchronized void registerFonts ()
3291     {
3292         //TODO lucian
3293         if (!fontsRegistered)
3294         {
3295             List<PropertySuffix> fontFiles = JRPropertiesUtil.getInstance(DefaultJasperReportsContext.getInstance()).getProperties(PDF_FONT_FILES_PREFIX);//FIXMECONTEXT no default here and below
3296             if (!fontFiles.isEmpty())
3297             {
3298                 for (Iterator<PropertySuffix> i = fontFiles.iterator(); i.hasNext();)
3299                 {
3300                     JRPropertiesUtil.PropertySuffix font = i.next();
3301                     String file = font.getValue();
3302                     if (file.toLowerCase().endsWith(".ttc"))
3303                     {
3304                         FontFactory.register(file);
3305                     }
3306                     else
3307                     {
3308                         String alias = font.getSuffix();
3309                         FontFactory.register(file, alias);
3310                     }
3311                 }
3312             }
3313
3314             List<PropertySuffix> fontDirs = JRPropertiesUtil.getInstance(DefaultJasperReportsContext.getInstance()).getProperties(PDF_FONT_DIRS_PREFIX);
3315             if (!fontDirs.isEmpty())
3316             {
3317                 for (Iterator<PropertySuffix> i = fontDirs.iterator(); i.hasNext();)
3318                 {
3319                     JRPropertiesUtil.PropertySuffix dir = i.next();
3320                     FontFactory.registerDirectory(dir.getValue());
3321                 }
3322             }
3323
3324             fontsRegistered = true;
3325         }
3326     }
3327
3328
3329     static protected class Bookmark
3330     {
3331         final PdfOutlineEntry pdfOutline;
3332         final int level;
3333
3334         Bookmark(Bookmark parent, int x, int top, String title)
3335         {
3336             this.pdfOutline = parent.pdfOutline.createChild(title, x, top);
3337             this.level = parent.level + 1;
3338         }
3339
3340         Bookmark(Bookmark parent, String title)
3341         {
3342             this.pdfOutline = parent.pdfOutline.createChild(title);
3343             this.level = parent.level + 1;
3344         }
3345
3346         Bookmark(PdfOutlineEntry pdfOutline, int level)
3347         {
3348             this.pdfOutline = pdfOutline;
3349             this.level = level;
3350         }
3351     }
3352
3353     static protected class BookmarkStack
3354     {
3355         LinkedList<Bookmark> stack;
3356
3357         BookmarkStack()
3358         {
3359             stack = new LinkedList<Bookmark>();
3360         }
3361
3362         void push(Bookmark bookmark)
3363         {
3364             stack.add(bookmark);
3365         }
3366
3367         Bookmark pop()
3368         {
3369             return stack.removeLast();
3370         }
3371
3372         Bookmark peek()
3373         {
3374             return stack.getLast();
3375         }
3376     }
3377
3378
3379     protected void initBookmarks(List<ExporterInputItem> items)
3380     {
3381         bookmarkStack = new BookmarkStack();
3382
3383         int rootLevel = items.size() > 1 && getCurrentConfiguration().isCreatingBatchModeBookmarks() ? -1 : 0;
3384         Bookmark bookmark = new Bookmark(pdfProducer.getRootOutline(), rootLevel);
3385         bookmarkStack.push(bookmark);
3386     }
3387
3388
3389     protected void addBookmark(int level, String title, int x, int y)
3390     {
3391         Bookmark parent = bookmarkStack.peek();
3392         // searching for parent
3393         while(parent.level >= level)
3394         {
3395             bookmarkStack.pop();
3396             parent = bookmarkStack.peek();
3397         }
3398
3399         if (!getCurrentItemConfiguration().isCollapseMissingBookmarkLevels())
3400         {
3401             // creating empty bookmarks in order to preserve the bookmark level
3402             for (int i = parent.level + 1; i < level; ++i)
3403             {
3404                 Bookmark emptyBookmark = new Bookmark(parent, EMPTY_BOOKMARK_TITLE);
3405                 bookmarkStack.push(emptyBookmark);
3406                 parent = emptyBookmark;
3407             }
3408         }
3409         int height = OrientationEnum.PORTRAIT.equals(pageFormat.getOrientation()) 
3410                 ? pageFormat.getPageHeight() - y 
3411                 : y;
3412         Bookmark bookmark = new Bookmark(parent, x, height, title);
3413         bookmarkStack.push(bookmark);
3414     }
3415
3416
3417     protected void setAnchor(PdfChunk chunk, JRPrintAnchor anchor, JRPrintElement element)
3418     {
3419         String anchorName = anchor.getAnchorName();
3420         if (anchorName != null)
3421         {
3422             chunk.setLocalDestination(anchorName);
3423
3424             if (anchor.getBookmarkLevel() != JRAnchor.NO_BOOKMARK)
3425             {
3426                 int x = OrientationEnum.PORTRAIT.equals(pageFormat.getOrientation()) 
3427                         ? getOffsetX() + element.getX() 
3428                         : getOffsetY() + element.getY();
3429                 int y = OrientationEnum.PORTRAIT.equals(pageFormat.getOrientation()) 
3430                         ? getOffsetY() + element.getY() 
3431                         : getOffsetX() + element.getX();
3432                 addBookmark(anchor.getBookmarkLevel(), anchor.getAnchorName(), x, y);
3433             }
3434         }
3435     }
3436
3437
3438     public void exportFrame(JRPrintFrame frame) throws IOException, JRException
3439     {
3440         if (frame.getModeValue() == ModeEnum.OPAQUE)
3441         {
3442             int x = frame.getX() + getOffsetX();
3443             int y = frame.getY() + getOffsetY();
3444
3445             Color backcolor = frame.getBackcolor();
3446             
3447             PdfContent pdfContent = pdfProducer.getPdfContent();
3448             pdfContent.setFillColor(backcolor);
3449             pdfContent.fillRectangle(
3450                 x,
3451                 pageFormat.getPageHeight() - y,
3452                 frame.getWidth(),
3453                 - frame.getHeight()
3454                 );
3455             pdfContent.resetFillColor();
3456         }
3457
3458         setFrameElementsOffset(frame, false);
3459         try
3460         {
3461             exportElements(frame.getElements());
3462         }
3463         finally
3464         {
3465             restoreElementOffsets();
3466         }
3467
3468         exportBox(frame.getLineBox(), frame);
3469     }
3470
3471
3472     /**
3473      *
3474      */

3475     protected PrintPageFormat getCurrentPageFormat()
3476     {
3477         return pageFormat;
3478     }
3479
3480
3481     @Override
3482     protected int getOffsetX()
3483     {
3484         return 
3485             super.getOffsetX() 
3486             + (insideFrame() ? 0 : (crtDocumentPageNumber % 2 == 0 
3487                 ? crtEvenPageOffsetX 
3488                 : crtOddPageOffsetX));
3489     }
3490
3491
3492     @Override
3493     protected int getOffsetY()
3494     {
3495         return 
3496             super.getOffsetY() 
3497             + (insideFrame() ? 0 : (crtDocumentPageNumber % 2 == 0 
3498                 ? crtEvenPageOffsetY 
3499                 : crtOddPageOffsetY));
3500     }
3501
3502
3503     /**
3504      *
3505      */

3506     protected void exportGenericElement(JRGenericPrintElement element)
3507     {
3508         GenericElementPdfHandler handler = (GenericElementPdfHandler) 
3509                 GenericElementHandlerEnviroment.getInstance(getJasperReportsContext()).getElementHandler(
3510                         element.getGenericType(), PDF_EXPORTER_KEY);
3511         
3512         if (handler != null)
3513         {
3514             handler.exportElement(exporterContext, element);
3515         }
3516         else
3517         {
3518             if (log.isDebugEnabled())
3519             {
3520                 log.debug("No PDF generic element handler for " 
3521                         + element.getGenericType());
3522             }
3523         }
3524     }
3525
3526     
3527     @Override
3528     public String getExporterKey()
3529     {
3530         return PDF_EXPORTER_KEY;
3531     }
3532
3533     
3534     @Override
3535     public String getExporterPropertiesPrefix()
3536     {
3537         return PDF_EXPORTER_PROPERTIES_PREFIX;
3538     }
3539     
3540     public static int getIntegerPermissions(String permissions) {
3541         int permission = 0;
3542         if(permissions != null && permissions.length() > 0) {
3543             String[] perms = permissions.split("\\|");
3544             for(String perm : perms) {
3545                 if(PdfPermissionsEnum.ALL.equals(PdfPermissionsEnum.getByName(perm))) {
3546                     permission = PdfExporterConfiguration.ALL_PERMISSIONS;
3547                     break;
3548                 }
3549                 if(perm != null && perm.length()>0) {
3550                     permission |= PdfPermissionsEnum.getByName(perm).getPdfPermission();
3551                 }
3552             }
3553         }
3554         return permission;
3555     }
3556 }
3557