Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* JasperReports - Free Java Reporting Library.
* Copyright (C) 2001 - 2018 TIBCO Software Inc. All rights reserved.
* http://www.jaspersoft.com
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is part of JasperReports.
*
* JasperReports is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JasperReports is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JasperReports. If not, see .
*/
/*
* Contributors:
* Adrian Jackson - [email protected]
* David Taylor - [email protected]
* Lars Kristensen - [email protected]
* Ling Li - [email protected]
* Martin Clough - [email protected]
*/
package net.sf.jasperreports.engine.export;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.color.ICC_Profile;
import java.awt.font.TextAttribute;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.Character.UnicodeBlock;
import java.text.AttributedCharacterIterator;
import java.text.AttributedCharacterIterator.Attribute;
import java.text.AttributedString;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.lowagie.text.BadElementException;
import com.lowagie.text.Chunk;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.FontFactory;
import com.lowagie.text.Image;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;
import com.lowagie.text.SplitCharacter;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.ColumnText;
import com.lowagie.text.pdf.FontMapper;
import com.lowagie.text.pdf.PdfAction;
import com.lowagie.text.pdf.PdfArray;
import com.lowagie.text.pdf.PdfBoolean;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfDestination;
import com.lowagie.text.pdf.PdfDictionary;
import com.lowagie.text.pdf.PdfICCBased;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfOutline;
import com.lowagie.text.pdf.PdfString;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;
import net.sf.jasperreports.annotations.properties.Property;
import net.sf.jasperreports.annotations.properties.PropertyScope;
import net.sf.jasperreports.engine.DefaultJasperReportsContext;
import net.sf.jasperreports.engine.JRAbstractExporter;
import net.sf.jasperreports.engine.JRAnchor;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRFont;
import net.sf.jasperreports.engine.JRGenericElementType;
import net.sf.jasperreports.engine.JRGenericPrintElement;
import net.sf.jasperreports.engine.JRLineBox;
import net.sf.jasperreports.engine.JRPen;
import net.sf.jasperreports.engine.JRPrintAnchor;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRPrintEllipse;
import net.sf.jasperreports.engine.JRPrintFrame;
import net.sf.jasperreports.engine.JRPrintHyperlink;
import net.sf.jasperreports.engine.JRPrintImage;
import net.sf.jasperreports.engine.JRPrintLine;
import net.sf.jasperreports.engine.JRPrintPage;
import net.sf.jasperreports.engine.JRPrintRectangle;
import net.sf.jasperreports.engine.JRPrintText;
import net.sf.jasperreports.engine.JRPropertiesUtil;
import net.sf.jasperreports.engine.JRPropertiesUtil.PropertySuffix;
import net.sf.jasperreports.engine.JRRuntimeException;
import net.sf.jasperreports.engine.JasperReportsContext;
import net.sf.jasperreports.engine.PrintPageFormat;
import net.sf.jasperreports.engine.base.JRBaseFont;
import net.sf.jasperreports.engine.base.JRBasePrintText;
import net.sf.jasperreports.engine.fonts.AwtFontAttribute;
import net.sf.jasperreports.engine.fonts.FontFace;
import net.sf.jasperreports.engine.fonts.FontFamily;
import net.sf.jasperreports.engine.fonts.FontInfo;
import net.sf.jasperreports.engine.type.HyperlinkTypeEnum;
import net.sf.jasperreports.engine.type.LineDirectionEnum;
import net.sf.jasperreports.engine.type.LineStyleEnum;
import net.sf.jasperreports.engine.type.ModeEnum;
import net.sf.jasperreports.engine.type.OrientationEnum;
import net.sf.jasperreports.engine.util.BreakIteratorSplitCharacter;
import net.sf.jasperreports.engine.util.ImageUtil;
import net.sf.jasperreports.engine.util.JRImageLoader;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.engine.util.JRPdfaIccProfileNotFoundException;
import net.sf.jasperreports.engine.util.JRStyledText;
import net.sf.jasperreports.engine.util.JRTextAttribute;
import net.sf.jasperreports.engine.util.NullOutputStream;
import net.sf.jasperreports.export.ExportInterruptedException;
import net.sf.jasperreports.export.ExporterInputItem;
import net.sf.jasperreports.export.OutputStreamExporterOutput;
import net.sf.jasperreports.export.PdfExporterConfiguration;
import net.sf.jasperreports.export.PdfReportConfiguration;
import net.sf.jasperreports.export.type.PdfPermissionsEnum;
import net.sf.jasperreports.export.type.PdfPrintScalingEnum;
import net.sf.jasperreports.export.type.PdfVersionEnum;
import net.sf.jasperreports.export.type.PdfaConformanceEnum;
import net.sf.jasperreports.properties.PropertyConstants;
import net.sf.jasperreports.renderers.DataRenderable;
import net.sf.jasperreports.renderers.DimensionRenderable;
import net.sf.jasperreports.renderers.Graphics2DRenderable;
import net.sf.jasperreports.renderers.Renderable;
import net.sf.jasperreports.renderers.RenderersCache;
import net.sf.jasperreports.renderers.ResourceRenderer;
import net.sf.jasperreports.renderers.WrappingImageDataToGraphics2DRenderer;
import net.sf.jasperreports.renderers.WrappingSvgDataToGraphics2DRenderer;
import net.sf.jasperreports.renderers.util.RendererUtil;
import net.sf.jasperreports.repo.RepositoryUtil;
/**
* Exports a JasperReports document to PDF format. It has binary output type and exports the document to
* a free-form layout.
*
* As its name indicates, PDF is a very precise and complex document format that ensures
* documents will look and print the same on all platforms.
* This is why the PDF exporter implemented by the
* {@link net.sf.jasperreports.engine.export.JRPdfExporter} class in JasperReports is
* one of the best exporters. The output it produces is almost of the same quality as that
* produced by the {@link net.sf.jasperreports.engine.export.JRGraphics2DExporter},
* which is always the reference.
*
* The {@link net.sf.jasperreports.engine.export.JRPdfExporter} implementation uses iText,
* which is a specialized PDF-generating library. PDF is a binary document format that allows
* absolute positioning of the elements inside a page, so the existing PDF exporter does not
* have the limitations of a grid exporter.
*
* It also works very well in batch mode because it allows concatenation of multiple
* documents within the same PDF file, even if the files have different page sizes.
*
Font Mappings
* Exporting to PDF requires mapping the fonts using three attributes:
* pdfFontName, pdfEncoding and isPdfEmbedded.
* Even though these three attributes are still supported in JRXML and
* the API, we recommend making the PDF font mappings at export time using font
* extensions.
*
* When exporting documents to PDF, for each combination of the three fontName,
* isBold, and isItalic font attributes, there must be an equivalent
* combination of the PDF-related font attributes pdfFontName, pdfEncoding
* and isPdfEmbedded.
*
* Equivalent combination means one that causes the text elements to be rendered exactly
* the same (or at least as closely as possible) in PDF and the built-in Graphics2D
* exporter, which is the reference.
*
* In some cases, there is no font file available to use with the pdfFontName attribute in
* order to render bold and italic text exactly like the Graphics2D exporter renders it in
* AWT. Those fonts might only have a normal style variant and no variants for bold and
* italic. In such cases, the PDF exporter (the iText library, to be more precise) is able to
* simulate those styles by applying transformations to the normal font glyphs. The
* {@link net.sf.jasperreports.engine.export.JRPdfExporter} internally acquires the needed PDF
* font based on the font extension mechanism (see the getFont(Map, Locale, boolean)
* method.
*
Batch Mode Bookmarks
* When several JasperPrint documents must be concatenated in the same PDF file by
* batch export, one can introduce PDF bookmarks in the resulting PDF document to mark
* the beginning of each individual document that was part of the initial document list.
*
* These bookmarks have the same name as the original JasperPrint document as
* specified by the jasperPrint.getName() property. However, users can turn on and off
* the creation of those bookmarks by turning on or off the
* {@link net.sf.jasperreports.export.PdfExporterConfiguration#isCreatingBatchModeBookmarks() isCreatingBatchModeBookmarks()}
* exporter configuration setting. The exporter does not create such bookmarks by default.
*
Encrypted PDF
* In some cases, users might want to encrypt the PDF documents generated by
* JasperReports so that only authorized viewers can have access to those documents.
* There are five exporter configuration settings for this (see {@link net.sf.jasperreports.export.PdfExporterConfiguration}):
*
* Some applications require marking the generated files with a particular PDF
* specifications version. Related export configuration settings are the following
* (see {@link net.sf.jasperreports.export.PdfExporterConfiguration}):
*
* Since version 1.5, the PDF format supports compression. By default, the PDF exporter in
* JasperReports does not create compressed PDF documents, but this feature can be turned
* on using the {@link net.sf.jasperreports.export.PdfExporterConfiguration#isCompressed() isCompressed()}
* exporter configuration setting. Note that because compressed PDFs
* are available only since PDF version 1.5, the PDF version of the resulting document is
* set to 1.5 automatically if compression is turned on.
*
Word Wrap and Line Break Policy
* By default, the PDF exporter does not guarantee that text with the same style properties
* will be rendered exactly as it is using AWT. The word wrap and line break policy is
* slightly different, and in some cases it might cause portions of text to disappear at the
* end of longer text paragraphs.
*
* To make sure this does not happen, one can configure the PDF exporter to use the AWT
* word wrap and line break policy by setting the
* {@link net.sf.jasperreports.export.PdfReportConfiguration#isForceLineBreakPolicy() isForceLineBreakPolicy()}
* exporter configuration setting to true. Note that this feature is not turned on by default, because it affects the
* exporter performance. This default behavior that applies in the absence of the mentioned
* export parameter can be controlled using the
* {@link net.sf.jasperreports.export.PdfReportConfiguration#PROPERTY_FORCE_LINEBREAK_POLICY net.sf.jasperreports.export.pdf.force.linebreak.policy} configuration
* property
*
JavaScript Actions
* The PDF specifications provide a means for the automation of various processes, such as
* the automatic printing of the document when it is opened. PDF viewer applications are
* able to execute Acrobat JavaScript code that is embedded in the PDF and associated with
* different events.
*
* JasperReports only allows inserting Acrobat JavaScript code. This code gets executed
* when the PDF document is opened in the viewer. This can be achieved using the
* {@link net.sf.jasperreports.export.PdfExporterConfiguration#getPdfJavaScript() getPdfJavaScript()}
* configuration setting, which retrieve the Acrobat JavaScript source code.
* Note that Acrobat JavaScript is a programming language based on JavaScript. More
* details about this can be found in the iText documentation.
*
Metadata Information
* PDF documents can store metadata information such as the author of the document, its
* title, and keywords. JasperReports exposes this feature of PDF through special exporter
* configuration settings available in the {@link net.sf.jasperreports.export.PdfExporterConfiguration}
* class. They are all listed following:
*
* The {@link net.sf.jasperreports.export.PdfReportConfiguration#isForceSvgShapes() isForceSvgShapes()}
* flag is used to force the rendering of SVG images using shapes on the PDF Graphics2D
* context. This allows fonts to be rendered as shapes, thus avoiding any font mapping issues that
* might cause Unicode text to not show up properly; however, it has the disadvantage of producing
* larger PDF files.
*
* By default, the flag is set to true, mainly due to backward-compatibility reasons. To
* reduce PDF file size for documents containing SVG images such as charts, this flag
* should be set to false. However, in such a case, the accuracy of the text content
* rendered by the SVG element in PDF depends on the correct PDF font information being
* available in the SVG implementation itself.
*
* In JasperReports, SVG elements are rendered using
* {@link net.sf.jasperreports.renderers.Renderable} implementations,
* which are most likely subclasses of the {@link net.sf.jasperreports.renderers.AbstractRenderToImageDataRenderer}
* class. SVG renderer implementations should be concerned only with
* implementing the
*
* public void render(JasperReportsContext jasperReportsContext, Graphics2D grx, Rectangle2D rectangle)
*
* method, which should contain all the code
* required for rendering the SVG on a Graphics2D context. Correct PDF font information
* means that the java.awt.Font objects used to draw text on the Graphics2D
* context should have PDF-related text attributes embedded so that when rendered on a PDF
* Graphics2D context, the exporter can make use of them. Embedding PDF-related text
* attributes into the SVG means using the following text attributes when creating
* java.awt.Font to render text in the SVG renderer implementation:
*
*
* The built-in chart component in JasperReports hides this complexity of dealing with
* fonts in a SVG renderer by exposing to the end user the usual three PDF-specific font
* attributes (pdfFontName, pdfEncoding, and isPdfEmbedded)
* to be set along with the normal font attributes every time a font setting is made for the chart
* title, subtitle, chart legend, or axis. This feature can be controlled system-wide using the
* {@link net.sf.jasperreports.export.PdfReportConfiguration#PROPERTY_FORCE_SVG_SHAPES net.sf.jasperreports.export.pdf.force.svg.shapes} configuration property.
* The {@link net.sf.jasperreports.export.PdfReportConfiguration#isForceSvgShapes() isForceSvgShapes()}
* export configuration setting overrides the configuration property value, if present.
*
Section 508 Compliance
* PDF files can contain hidden tags that describe the structure of the document. Some of
* the tags are used by the automated reader tool that reads PDF documents aloud to people
* with disabilities.
*
* The PDF tags feature of JasperReports allows adding hidden PDF tags to the files
* generated by the JasperReports PDF exporter. The resulting files comply with the
* requirements of the Section 508 of the U.S. Rehabilitation Act
* (http://www.section508.gov/).
*
Producing Tagged PDF Files
* By default, the JasperReports exporter does not put any hidden structural tags inside its
* generated PDF files. In order to turn on the creation of hidden structural tags, any of the
* following can be used:
*
*
setting to true the {@link net.sf.jasperreports.export.PdfExporterConfiguration#isTagged() isTagged()}
* configuration flag
*
setting to true the {@link net.sf.jasperreports.export.PdfExporterConfiguration#PROPERTY_TAGGED net.sf.jasperreports.export.pdf.tagged} configuration property.
*
*
Setting the PDF File Language
* When a full accessibility check is requested from Acrobat Professional, among the things
* it determines is whether the PDF file or the various pieces of content inside it have a
* language set. JasperReports allows setting the language for the entire content by doing
* any one of the following:
*
*
using the {@link net.sf.jasperreports.export.PdfExporterConfiguration#getTagLanguage() getTagLanguage()}
* configuration setting to retrieve the language as a java.lang.String value;
*
using the {@link net.sf.jasperreports.export.PdfExporterConfiguration#PROPERTY_TAG_LANGUAGE net.sf.jasperreports.export.pdf.tag.language} configuration property
* globally or at report level
*
*
Alternate Text for Images
* In tagged PDF files, image elements can be described in alternate text that is read by the
* automated reader. The text is specified using the hyperlinkTooltipExpression
* property of the image element in JRXML.
*
* For more information about tagged PDF documents in JasperReports,
* please consult the {@link net.sf.jasperreports.engine.export.JRPdfExporterTagHelper} class.
*
* @see net.sf.jasperreports.export.PdfExporterConfiguration
* @see net.sf.jasperreports.export.PdfReportConfiguration
* @see net.sf.jasperreports.engine.util.JRTextAttribute#IS_PDF_EMBEDDED
* @see net.sf.jasperreports.engine.util.JRTextAttribute#PDF_ENCODING
* @see net.sf.jasperreports.engine.util.JRTextAttribute#PDF_FONT_NAME
* @author Teodor Danciu ([email protected])
*/
public class JRPdfExporter extends JRAbstractExporter
{
private static final Log log = LogFactory.getLog(JRPdfExporter.class);
public static final String PDF_EXPORTER_PROPERTIES_PREFIX = JRPropertiesUtil.PROPERTY_PREFIX + "export.pdf.";
public static final String EXCEPTION_MESSAGE_KEY_DOCUMENT_ERROR = "export.pdf.document.error";
public static final String EXCEPTION_MESSAGE_KEY_FONT_LOADING_ERROR = "export.pdf.font.loading.error";
public static final String EXCEPTION_MESSAGE_KEY_REPORT_GENERATION_ERROR = "export.pdf.report.generation.error";
/**
* @deprecated Replaced by {@link PdfReportConfiguration#PROPERTY_IGNORE_HYPERLINK}.
*/
public static final String PROPERTY_IGNORE_HYPERLINK = PdfReportConfiguration.PROPERTY_IGNORE_HYPERLINK;
/**
* Prefix of properties that specify font files for the PDF exporter.
*/
@Property(
name = "net.sf.jasperreports.export.pdf.font.{arbitrary_name}",
category = PropertyConstants.CATEGORY_EXPORT,
scopes = {PropertyScope.GLOBAL},
sinceVersion = PropertyConstants.VERSION_1_0_0
)
public static final String PDF_FONT_FILES_PREFIX = PDF_EXPORTER_PROPERTIES_PREFIX + "font.";
/**
* Prefix of properties that specify font directories for the PDF exporter.
*/
@Property(
name = "net.sf.jasperreports.export.pdf.fontdir.{arbitrary_name}",
category = PropertyConstants.CATEGORY_EXPORT,
scopes = {PropertyScope.GLOBAL},
sinceVersion = PropertyConstants.VERSION_1_0_0
)
public static final String PDF_FONT_DIRS_PREFIX = PDF_EXPORTER_PROPERTIES_PREFIX + "fontdir.";
/**
* The exporter key, as used in
* {@link GenericElementHandlerEnviroment#getElementHandler(JRGenericElementType, String)}.
*/
public static final String PDF_EXPORTER_KEY = JRPropertiesUtil.PROPERTY_PREFIX + "pdf";
private static final String EMPTY_BOOKMARK_TITLE = "";
/**
*
*/
protected static final String JR_PAGE_ANCHOR_PREFIX = "JR_PAGE_ANCHOR_";
protected static boolean fontsRegistered;
protected class ExporterContext extends BaseExporterContext implements JRPdfExporterContext
{
@Override
public PdfWriter getPdfWriter()
{
return pdfWriter;
}
}
/**
*
*/
protected Document document;
protected PdfContentByte pdfContentByte;
protected PdfWriter pdfWriter;
protected Document imageTesterDocument;
protected PdfContentByte imageTesterPdfContentByte;
protected JRPdfExporterTagHelper tagHelper = new JRPdfExporterTagHelper(this);
protected int reportIndex;
protected PrintPageFormat pageFormat;
protected int crtDocumentPageNumber;
protected int permissions;
/**
*
*/
protected RenderersCache renderersCache;
protected Map loadedImagesMap;
protected Image pxImage;
private BookmarkStack bookmarkStack;
private SplitCharacter splitCharacter;
private int crtOddPageOffsetX;
private int crtOddPageOffsetY;
private int crtEvenPageOffsetX;
private int crtEvenPageOffsetY;
private boolean awtIgnoreMissingFont;
private Set glyphRendererBlocks;
private boolean glyphRendererAddActualText;
private PdfVersionEnum minimalVersion;
private Map glyphRendererFonts;
/**
* @see #JRPdfExporter(JasperReportsContext)
*/
public JRPdfExporter()
{
this(DefaultJasperReportsContext.getInstance());
}
/**
*
*/
public JRPdfExporter(JasperReportsContext jasperReportsContext)
{
super(jasperReportsContext);
exporterContext = new ExporterContext();
glyphRendererFonts = new HashMap();
}
@Override
protected Class getConfigurationInterface()
{
return PdfExporterConfiguration.class;
}
@Override
protected Class getItemConfigurationInterface()
{
return PdfReportConfiguration.class;
}
@Override
@SuppressWarnings("deprecation")
protected void ensureOutput()
{
if (exporterOutput == null)
{
exporterOutput =
new net.sf.jasperreports.export.parameters.ParametersOutputStreamExporterOutput(
getJasperReportsContext(),
getParameters(),
getCurrentJasperPrint()
);
}
}
/**
*
*/
protected Image getPxImage()
{
if (pxImage == null)
{
try
{
pxImage =
Image.getInstance(
JRLoader.loadBytesFromResource(JRImageLoader.PIXEL_IMAGE_RESOURCE)
);
}
catch(Exception e)
{
throw new JRRuntimeException(e);
}
}
return pxImage;
}
@Override
public void exportReport() throws JRException
{
registerFonts();
/* */
ensureJasperReportsContext();
ensureInput();
initExport();
ensureOutput();
OutputStream outputStream = getExporterOutput().getOutputStream();
try
{
exportReportToStream(outputStream);
}
finally
{
getExporterOutput().close();
resetExportContext();
}
}
@Override
protected void initExport()
{
super.initExport();
PdfExporterConfiguration configuration = getCurrentConfiguration();
Boolean isTagged = configuration.isTagged();
if (isTagged != null)
{
tagHelper.setTagged(isTagged);
}
tagHelper.setLanguage(configuration.getTagLanguage());
this.permissions = getIntegerPermissions(configuration.getAllowedPermissions()) & (~getIntegerPermissions(configuration.getDeniedPermissions()));
crtDocumentPageNumber = 0;
awtIgnoreMissingFont = getPropertiesUtil().getBooleanProperty(
JRStyledText.PROPERTY_AWT_IGNORE_MISSING_FONT);//FIXMECONTEXT replace with getPropertiesUtil in all exporters
glyphRendererAddActualText = propertiesUtil.getBooleanProperty(
PdfReportConfiguration.PROPERTY_GLYPH_RENDERER_ADD_ACTUAL_TEXT, false);
if (glyphRendererAddActualText && !tagHelper.isTagged && PdfGlyphRenderer.supported())
{
minimalVersion = PdfVersionEnum.VERSION_1_5;
}
}
@Override
protected void initReport()
{
super.initReport();
PdfReportConfiguration configuration = getCurrentItemConfiguration();
if (configuration.isForceLineBreakPolicy())
{
splitCharacter = new BreakIteratorSplitCharacter();
}
crtOddPageOffsetX = configuration.getOddPageOffsetX();
crtOddPageOffsetY = configuration.getOddPageOffsetY();
crtEvenPageOffsetX = configuration.getEvenPageOffsetX();
crtEvenPageOffsetY = configuration.getEvenPageOffsetY();
initGlyphRenderer();
renderersCache = new RenderersCache(getJasperReportsContext());
loadedImagesMap = new HashMap();
}
protected void initGlyphRenderer()
{
glyphRendererBlocks = new HashSet();
List props = propertiesUtil.getAllProperties(getCurrentJasperPrint(),
PdfReportConfiguration.PROPERTY_PREFIX_GLYPH_RENDERER_BLOCKS);
for (PropertySuffix prop : props)
{
String blocks = prop.getValue();
for (String blockToken : blocks.split(","))
{
UnicodeBlock block = resolveUnicodeBlock(blockToken);
if (block != null)
{
if (log.isDebugEnabled())
{
log.debug("glyph renderer block " + block);
}
glyphRendererBlocks.add(block);
}
}
}
}
protected UnicodeBlock resolveUnicodeBlock(String name)
{
if (name.trim().isEmpty())
{
return null;
}
try
{
return UnicodeBlock.forName(name.trim());
}
catch (IllegalArgumentException e)
{
log.warn("Could not resolve \"" + name + "\" to a Unicode block");
return null;
}
}
/**
*
*/
protected void exportReportToStream(OutputStream os) throws JRException
{
//ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfExporterConfiguration configuration = getCurrentConfiguration();
pageFormat = jasperPrint.getPageFormat(0);
document =
new Document(
new Rectangle(
pageFormat.getPageWidth(),
pageFormat.getPageHeight()
)
);
imageTesterDocument =
new Document(
new Rectangle(
10, //jasperPrint.getPageWidth(),
10 //jasperPrint.getPageHeight()
)
);
boolean closeDocuments = true;
try
{
pdfWriter = PdfWriter.getInstance(document, os);
pdfWriter.setCloseStream(false);
tagHelper.setPdfWriter(pdfWriter);
PdfVersionEnum pdfVersion = configuration.getPdfVersion();
if (pdfVersion != null)
{
pdfWriter.setPdfVersion(pdfVersion.getName().charAt(0));
}
if (minimalVersion != null)
{
pdfWriter.setAtLeastPdfVersion(minimalVersion.getName().charAt(0));
}
if (configuration.isCompressed())
{
pdfWriter.setFullCompression();
}
if (configuration.isEncrypted())
{
int perms = configuration.isOverrideHints() == null || configuration.isOverrideHints()
? (configuration.getPermissions() != null
? (Integer)configuration.getPermissions()
: permissions)
: (permissions != 0
? permissions
:(configuration.getPermissions() != null
? (Integer)configuration.getPermissions()
: 0));
pdfWriter.setEncryption(
PdfWriter.getISOBytes(configuration.getUserPassword()),
PdfWriter.getISOBytes(configuration.getOwnerPassword()),
perms,
configuration.is128BitKey() ? PdfWriter.STANDARD_ENCRYPTION_128 : PdfWriter.STANDARD_ENCRYPTION_40
);
}
PdfPrintScalingEnum printScaling = configuration.getPrintScaling();
if (PdfPrintScalingEnum.DEFAULT == printScaling)
{
pdfWriter.addViewerPreference(PdfName.PRINTSCALING, PdfName.APPDEFAULT);
}
else if (PdfPrintScalingEnum.NONE == printScaling)
{
pdfWriter.addViewerPreference(PdfName.PRINTSCALING, PdfName.NONE);
}
boolean justifiedLetterSpacing = propertiesUtil.getBooleanProperty(jasperPrint,
PdfExporterConfiguration.PROPERTY_JUSTIFIED_LETTER_SPACING, false);
if (!justifiedLetterSpacing)
{
pdfWriter.setSpaceCharRatio(PdfWriter.NO_SPACE_CHAR_RATIO);
}
// Add meta-data parameters to generated PDF document
// [email protected] 2005-12-05
String title = configuration.getMetadataTitle();
if( title != null )
{
document.addTitle(title);
if(configuration.isDisplayMetadataTitle()){
pdfWriter.addViewerPreference(PdfName.DISPLAYDOCTITLE, new PdfBoolean(true));
}
}
String author = configuration.getMetadataAuthor();
if( author != null )
{
document.addAuthor(author);
}
String subject = configuration.getMetadataSubject();
if( subject != null )
{
document.addSubject(subject);
}
String keywords = configuration.getMetadataKeywords();
if( keywords != null )
{
document.addKeywords(keywords);
}
String creator = configuration.getMetadataCreator();
if( creator == null )
{
creator = "JasperReports Library version " + Package.getPackage("net.sf.jasperreports.engine").getImplementationVersion();
}
document.addCreator(creator);
//accessibility check: tab order follows the structure of the document
pdfWriter.setTabs(PdfName.S);
//accessibility check: setting the document primary language
String language = configuration.getTagLanguage();
if(language != null){
pdfWriter.getExtraCatalog().put(PdfName.LANG, new PdfString(language));
}
// BEGIN: PDF/A support
PdfaConformanceEnum pdfaConformance = configuration.getPdfaConformance();
boolean gotPdfa = false;
if (PdfaConformanceEnum.PDFA_1A == pdfaConformance)
{
pdfWriter.setPDFXConformance(PdfWriter.PDFA1A);
gotPdfa = true;
}
else if (PdfaConformanceEnum.PDFA_1B == pdfaConformance)
{
pdfWriter.setPDFXConformance(PdfWriter.PDFA1B);
gotPdfa = true;
}
if (gotPdfa)
{
if (PdfXmpCreator.supported())
{
byte[] metadata = PdfXmpCreator.createXmpMetadata(pdfWriter);
pdfWriter.setXmpMetadata(metadata);
}
else
{
if ((title != null || subject != null || keywords != null) && log.isWarnEnabled())
{
// iText 2.1.7 does not properly write localized properties and keywords
log.warn("XMP metadata might be non conforming, include the Adobe XMP library to correct");
}
pdfWriter.createXmpMetadata();
}
} else
{
pdfWriter.setRgbTransparencyBlending(true);
}
// END: PDF/A support
document.open();
// BEGIN: PDF/A support
if (gotPdfa) {
String iccProfilePath = configuration.getIccProfilePath();
if (iccProfilePath != null) {
PdfDictionary pdfDictionary = new PdfDictionary(PdfName.OUTPUTINTENT);
pdfDictionary.put(PdfName.OUTPUTCONDITIONIDENTIFIER, new PdfString("sRGB IEC61966-2.1"));
pdfDictionary.put(PdfName.INFO, new PdfString("sRGB IEC61966-2.1"));
pdfDictionary.put(PdfName.S, PdfName.GTS_PDFA1);
InputStream iccIs = RepositoryUtil.getInstance(jasperReportsContext).getInputStreamFromLocation(iccProfilePath);//FIXME use getRepository?
PdfICCBased pdfICCBased = new PdfICCBased(ICC_Profile.getInstance(iccIs));
pdfICCBased.remove(PdfName.ALTERNATE);
pdfDictionary.put(PdfName.DESTOUTPUTPROFILE, pdfWriter.addToBody(pdfICCBased).getIndirectReference());
pdfWriter.getExtraCatalog().put(PdfName.OUTPUTINTENTS, new PdfArray(pdfDictionary));
} else {
throw new JRPdfaIccProfileNotFoundException();
}
}
// END: PDF/A support
String pdfJavaScript = configuration.getPdfJavaScript();
if(pdfJavaScript != null)
{
pdfWriter.addJavaScript(pdfJavaScript);
}
pdfContentByte = pdfWriter.getDirectContent();
tagHelper.init(pdfContentByte);
PdfWriter imageTesterPdfWriter =
PdfWriter.getInstance(
imageTesterDocument,
new NullOutputStream() // discard the output
);
imageTesterDocument.open();
imageTesterDocument.newPage();
imageTesterPdfContentByte = imageTesterPdfWriter.getDirectContent();
imageTesterPdfContentByte.setLiteral("\n");
List items = exporterInput.getItems();
initBookmarks(items);
boolean isCreatingBatchModeBookmarks = configuration.isCreatingBatchModeBookmarks();
for (reportIndex = 0; reportIndex < items.size(); reportIndex++)
{
ExporterInputItem item = items.get(reportIndex);
setCurrentExporterInputItem(item);
pageFormat = jasperPrint.getPageFormat(0);
setPageSize(null);
List pages = jasperPrint.getPages();
if (pages != null && pages.size() > 0)
{
if (items.size() > 1)
{
document.newPage();
if( isCreatingBatchModeBookmarks )
{
//add a new level to our outline for this report
addBookmark(0, jasperPrint.getName(), 0, 0);
}
}
PdfReportConfiguration lcItemConfiguration = getCurrentItemConfiguration();
boolean sizePageToContent = lcItemConfiguration.isSizePageToContent();
PrintPageFormat oldPageFormat = null;
PageRange pageRange = getPageRange();
int startPageIndex = (pageRange == null || pageRange.getStartPageIndex() == null) ? 0 : pageRange.getStartPageIndex();
int endPageIndex = (pageRange == null || pageRange.getEndPageIndex() == null) ? (pages.size() - 1) : pageRange.getEndPageIndex();
for (int pageIndex = startPageIndex; pageIndex <= endPageIndex; pageIndex++)
{
if (Thread.interrupted())
{
throw new ExportInterruptedException();
}
JRPrintPage page = pages.get(pageIndex);
pageFormat = jasperPrint.getPageFormat(pageIndex);
if (sizePageToContent || oldPageFormat != pageFormat)
{
setPageSize(sizePageToContent ? page : null);
}
document.newPage();
pdfContentByte = pdfWriter.getDirectContent();
pdfContentByte.setLineCap(2);//PdfContentByte.LINE_CAP_PROJECTING_SQUARE since iText 1.02b
writePageAnchor(pageIndex);
crtDocumentPageNumber++;
/* */
exportPage(page);
oldPageFormat = pageFormat;
}
}
else
{
document.newPage();
pdfContentByte = pdfWriter.getDirectContent();
pdfContentByte.setLiteral("\n");
}
}
closeDocuments = false;
document.close();
imageTesterDocument.close();
}
catch(DocumentException e)
{
throw
new JRException(
EXCEPTION_MESSAGE_KEY_DOCUMENT_ERROR,
new Object[]{jasperPrint.getName()},
e);
}
catch(IOException e)
{
throw
new JRException(
EXCEPTION_MESSAGE_KEY_REPORT_GENERATION_ERROR,
new Object[]{jasperPrint.getName()},
e);
}
finally
{
if (closeDocuments) //only on exception
{
try
{
document.close();
}
catch (Exception e)
{
// ignore, let the original exception propagate
}
try
{
imageTesterDocument.close();
}
catch (Exception e)
{
// ignore, let the original exception propagate
}
}
}
//return os.toByteArray();
}
protected void writePageAnchor(int pageIndex) throws DocumentException
{
Map attributes = new HashMap();
fontUtil.getAttributesWithoutAwtFont(attributes, new JRBasePrintText(jasperPrint.getDefaultStyleProvider()));
Font pdfFont = getFont(attributes, getLocale(), false);
Chunk chunk = new Chunk(" ", pdfFont);
chunk.setLocalDestination(JR_PAGE_ANCHOR_PREFIX + reportIndex + "_" + (pageIndex + 1));
tagHelper.startPageAnchor();
ColumnText colText = new ColumnText(pdfContentByte);
colText.setSimpleColumn(
new Phrase(chunk),
0,
pageFormat.getPageHeight(),
1,
1,
0,
Element.ALIGN_LEFT
);
colText.go();
tagHelper.endPageAnchor();
}
/**
*
*/
protected void setPageSize(JRPrintPage page) throws JRException, DocumentException, IOException
{
int pageWidth = 0;
int pageHeight = 0;
if (page != null)
{
Collection elements = page.getElements();
for (JRPrintElement element : elements)
{
int elementRight = element.getX() + element.getWidth();
int elementBottom = element.getY() + element.getHeight();
pageWidth = pageWidth < elementRight ? elementRight : pageWidth;
pageHeight = pageHeight < elementBottom ? elementBottom : pageHeight;
}
pageWidth += pageFormat.getRightMargin();
pageHeight += pageFormat.getBottomMargin();
}
pageWidth = pageWidth < pageFormat.getPageWidth() ? pageFormat.getPageWidth() : pageWidth;
pageHeight = pageHeight < pageFormat.getPageHeight() ? pageFormat.getPageHeight() : pageHeight;
Rectangle pageSize;
switch (pageFormat.getOrientation())
{
case LANDSCAPE:
// using rotate to indicate landscape page
pageSize = new Rectangle(pageHeight, pageWidth).rotate();
break;
default:
pageSize = new Rectangle(pageWidth, pageHeight);
break;
}
document.setPageSize(pageSize);
}
/**
*
*/
protected void exportPage(JRPrintPage page) throws JRException, DocumentException, IOException
{
tagHelper.startPage();
Collection elements = page.getElements();
exportElements(elements);
tagHelper.endPage();
JRExportProgressMonitor progressMonitor = getCurrentItemConfiguration().getProgressMonitor();
if (progressMonitor != null)
{
progressMonitor.afterPageExport();
}
}
protected void exportElements(Collection elements) throws DocumentException, IOException, JRException
{
if (elements != null && elements.size() > 0)
{
for(Iterator it = elements.iterator(); it.hasNext();)
{
JRPrintElement element = it.next();
if (filter == null || filter.isToExport(element))
{
tagHelper.startElement(element);
if (element instanceof JRPrintLine)
{
exportLine((JRPrintLine)element);
}
else if (element instanceof JRPrintRectangle)
{
exportRectangle((JRPrintRectangle)element);
}
else if (element instanceof JRPrintEllipse)
{
exportEllipse((JRPrintEllipse)element);
}
else if (element instanceof JRPrintImage)
{
exportImage((JRPrintImage)element);
}
else if (element instanceof JRPrintText)
{
exportText((JRPrintText)element);
}
else if (element instanceof JRPrintFrame)
{
exportFrame((JRPrintFrame)element);
}
else if (element instanceof JRGenericPrintElement)
{
exportGenericElement((JRGenericPrintElement) element);
}
tagHelper.endElement(element);
}
}
}
}
/**
*
*/
protected void exportLine(JRPrintLine line)
{
int lcOffsetX = getOffsetX();
int lcOffsetY = getOffsetY();
float lineWidth = line.getLinePen().getLineWidth().floatValue();
if (lineWidth > 0f)
{
preparePen(pdfContentByte, line.getLinePen(), PdfContentByte.LINE_CAP_BUTT);
if (line.getWidth() == 1)
{
if (line.getHeight() != 1)
{
//Vertical line
if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
{
pdfContentByte.moveTo(
line.getX() + lcOffsetX + 0.5f - lineWidth / 3,
pageFormat.getPageHeight() - line.getY() - lcOffsetY
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + 0.5f - lineWidth / 3,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight()
);
pdfContentByte.stroke();
pdfContentByte.moveTo(
line.getX() + lcOffsetX + 0.5f + lineWidth / 3,
pageFormat.getPageHeight() - line.getY() - lcOffsetY
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + 0.5f + lineWidth / 3,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight()
);
}
else
{
pdfContentByte.moveTo(
line.getX() + lcOffsetX + 0.5f,
pageFormat.getPageHeight() - line.getY() - lcOffsetY
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + 0.5f,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight()
);
}
}
}
else
{
if (line.getHeight() == 1)
{
//Horizontal line
if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
{
pdfContentByte.moveTo(
line.getX() + lcOffsetX,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f + lineWidth / 3
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + line.getWidth(),
pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f + lineWidth / 3
);
pdfContentByte.stroke();
pdfContentByte.moveTo(
line.getX() + lcOffsetX,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f - lineWidth / 3
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + line.getWidth(),
pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f - lineWidth / 3
);
}
else
{
pdfContentByte.moveTo(
line.getX() + lcOffsetX,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + line.getWidth(),
pageFormat.getPageHeight() - line.getY() - lcOffsetY - 0.5f
);
}
}
else
{
//Oblique line
if (line.getDirectionValue() == LineDirectionEnum.TOP_DOWN)
{
if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
{
double xtrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getWidth(), 2) / Math.pow(line.getHeight(), 2)));
double ytrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getHeight(), 2) / Math.pow(line.getWidth(), 2)));
pdfContentByte.moveTo(
line.getX() + lcOffsetX + (float)xtrans,
pageFormat.getPageHeight() - line.getY() - lcOffsetY + (float)ytrans
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + line.getWidth() + (float)xtrans,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight() + (float)ytrans
);
pdfContentByte.stroke();
pdfContentByte.moveTo(
line.getX() + lcOffsetX - (float)xtrans,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - (float)ytrans
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + line.getWidth() - (float)xtrans,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight() - (float)ytrans
);
}
else
{
pdfContentByte.moveTo(
line.getX() + lcOffsetX,
pageFormat.getPageHeight() - line.getY() - lcOffsetY
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + line.getWidth(),
pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight()
);
}
}
else
{
if (line.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
{
double xtrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getWidth(), 2) / Math.pow(line.getHeight(), 2)));
double ytrans = lineWidth / (3 * Math.sqrt(1 + Math.pow(line.getHeight(), 2) / Math.pow(line.getWidth(), 2)));
pdfContentByte.moveTo(
line.getX() + lcOffsetX + (float)xtrans,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight() - (float)ytrans
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + line.getWidth() + (float)xtrans,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - (float)ytrans
);
pdfContentByte.stroke();
pdfContentByte.moveTo(
line.getX() + lcOffsetX - (float)xtrans,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight() + (float)ytrans
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + line.getWidth() - (float)xtrans,
pageFormat.getPageHeight() - line.getY() - lcOffsetY + (float)ytrans
);
}
else
{
pdfContentByte.moveTo(
line.getX() + lcOffsetX,
pageFormat.getPageHeight() - line.getY() - lcOffsetY - line.getHeight()
);
pdfContentByte.lineTo(
line.getX() + lcOffsetX + line.getWidth(),
pageFormat.getPageHeight() - line.getY() - lcOffsetY
);
}
}
}
}
pdfContentByte.stroke();
pdfContentByte.setLineDash(0f);
pdfContentByte.setLineCap(PdfContentByte.LINE_CAP_PROJECTING_SQUARE);
}
}
/**
*
*/
protected void exportRectangle(JRPrintRectangle rectangle)
{
pdfContentByte.setRGBColorFill(
rectangle.getBackcolor().getRed(),
rectangle.getBackcolor().getGreen(),
rectangle.getBackcolor().getBlue()
);
preparePen(pdfContentByte, rectangle.getLinePen(), PdfContentByte.LINE_CAP_PROJECTING_SQUARE);
float lineWidth = rectangle.getLinePen().getLineWidth().floatValue();
int lcOffsetX = getOffsetX();
int lcOffsetY = getOffsetY();
if (rectangle.getModeValue() == ModeEnum.OPAQUE)
{
pdfContentByte.roundRectangle(
rectangle.getX() + lcOffsetX,
pageFormat.getPageHeight() - rectangle.getY() - lcOffsetY - rectangle.getHeight(),
rectangle.getWidth(),
rectangle.getHeight(),
rectangle.getRadius()
);
pdfContentByte.fill();
}
if (lineWidth > 0f)
{
if (rectangle.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
{
pdfContentByte.roundRectangle(
rectangle.getX() + lcOffsetX - lineWidth / 3,
pageFormat.getPageHeight() - rectangle.getY() - lcOffsetY - rectangle.getHeight() - lineWidth / 3,
rectangle.getWidth() + 2 * lineWidth / 3,
rectangle.getHeight() + 2 * lineWidth / 3,
rectangle.getRadius()
);
pdfContentByte.stroke();
pdfContentByte.roundRectangle(
rectangle.getX() + lcOffsetX + lineWidth / 3,
pageFormat.getPageHeight() - rectangle.getY() - lcOffsetY - rectangle.getHeight() + lineWidth / 3,
rectangle.getWidth() - 2 * lineWidth / 3,
rectangle.getHeight() - 2 * lineWidth / 3,
rectangle.getRadius()
);
pdfContentByte.stroke();
}
else
{
pdfContentByte.roundRectangle(
rectangle.getX() + lcOffsetX,
pageFormat.getPageHeight() - rectangle.getY() - lcOffsetY - rectangle.getHeight(),
rectangle.getWidth(),
rectangle.getHeight(),
rectangle.getRadius()
);
pdfContentByte.stroke();
}
}
pdfContentByte.setLineDash(0f);
}
/**
*
*/
protected void exportEllipse(JRPrintEllipse ellipse)
{
pdfContentByte.setRGBColorFill(
ellipse.getBackcolor().getRed(),
ellipse.getBackcolor().getGreen(),
ellipse.getBackcolor().getBlue()
);
preparePen(pdfContentByte, ellipse.getLinePen(), PdfContentByte.LINE_CAP_PROJECTING_SQUARE);
float lineWidth = ellipse.getLinePen().getLineWidth().floatValue();
int lcOffsetX = getOffsetX();
int lcOffsetY = getOffsetY();
if (ellipse.getModeValue() == ModeEnum.OPAQUE)
{
pdfContentByte.ellipse(
ellipse.getX() + lcOffsetX,
pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY - ellipse.getHeight(),
ellipse.getX() + lcOffsetX + ellipse.getWidth(),
pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY
);
pdfContentByte.fill();
}
if (lineWidth > 0f)
{
if (ellipse.getLinePen().getLineStyleValue() == LineStyleEnum.DOUBLE)
{
pdfContentByte.ellipse(
ellipse.getX() + lcOffsetX - lineWidth / 3,
pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY - ellipse.getHeight() - lineWidth / 3,
ellipse.getX() + lcOffsetX + ellipse.getWidth() + lineWidth / 3,
pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY + lineWidth / 3
);
pdfContentByte.stroke();
pdfContentByte.ellipse(
ellipse.getX() + lcOffsetX + lineWidth / 3,
pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY - ellipse.getHeight() + lineWidth / 3,
ellipse.getX() + lcOffsetX + ellipse.getWidth() - lineWidth / 3,
pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY - lineWidth / 3
);
pdfContentByte.stroke();
}
else
{
pdfContentByte.ellipse(
ellipse.getX() + lcOffsetX,
pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY - ellipse.getHeight(),
ellipse.getX() + lcOffsetX + ellipse.getWidth(),
pageFormat.getPageHeight() - ellipse.getY() - lcOffsetY
);
pdfContentByte.stroke();
}
}
pdfContentByte.setLineDash(0f);
}
/**
*
*/
public void exportImage(JRPrintImage printImage) throws DocumentException, IOException, JRException
{
if (printImage.getModeValue() == ModeEnum.OPAQUE)
{
pdfContentByte.setRGBColorFill(
printImage.getBackcolor().getRed(),
printImage.getBackcolor().getGreen(),
printImage.getBackcolor().getBlue()
);
pdfContentByte.rectangle(
printImage.getX() + getOffsetX(),
pageFormat.getPageHeight() - printImage.getY() - getOffsetY(),
printImage.getWidth(),
- printImage.getHeight()
);
pdfContentByte.fill();
}
InternalImageProcessor imageProcessor =
new InternalImageProcessor(printImage);
Renderable renderer = printImage.getRenderer();
if (
renderer != null
&& imageProcessor.availableImageWidth > 0
&& imageProcessor.availableImageHeight > 0
)
{
InternalImageProcessorResult imageProcessorResult = null;
try
{
imageProcessorResult = imageProcessor.process(renderer);
}
catch (Exception e)
{
Renderable onErrorRenderer = getRendererUtil().handleImageError(e, printImage.getOnErrorTypeValue());
if (onErrorRenderer != null)
{
imageProcessorResult = imageProcessor.process(onErrorRenderer);
}
}
if (imageProcessorResult != null)
{
setAnchor(imageProcessorResult.chunk, printImage, printImage);
setHyperlinkInfo(imageProcessorResult.chunk, printImage);
tagHelper.startImage(printImage);
ColumnText colText = new ColumnText(pdfContentByte);
int upperY = pageFormat.getPageHeight() - printImage.getY() - imageProcessor.topPadding - getOffsetY() - imageProcessorResult.yoffset;
int lowerX = printImage.getX() + imageProcessor.leftPadding + getOffsetX() + imageProcessorResult.xoffset;
colText.setSimpleColumn(
new Phrase(imageProcessorResult.chunk),
lowerX,
upperY - imageProcessorResult.scaledHeight,
lowerX + imageProcessorResult.scaledWidth,
upperY,
imageProcessorResult.scaledHeight,
Element.ALIGN_LEFT
);
colText.go();
tagHelper.endImage();
}
}
if (
printImage.getLineBox().getTopPen().getLineWidth().floatValue() <= 0f &&
printImage.getLineBox().getLeftPen().getLineWidth().floatValue() <= 0f &&
printImage.getLineBox().getBottomPen().getLineWidth().floatValue() <= 0f &&
printImage.getLineBox().getRightPen().getLineWidth().floatValue() <= 0f
)
{
if (printImage.getLinePen().getLineWidth().floatValue() > 0f)
{
exportPen(printImage.getLinePen(), printImage);
}
}
else
{
/* */
exportBox(
printImage.getLineBox(),
printImage
);
}
}
private class InternalImageProcessor
{
private final JRPrintImage printImage;
private final RenderersCache imageRenderersCache;
private final int topPadding;
private final int leftPadding;
private final int bottomPadding;
private final int rightPadding;
private final int availableImageWidth;
private final int availableImageHeight;
private InternalImageProcessor(JRPrintImage printImage)
{
this.printImage = printImage;
this.imageRenderersCache = printImage.isUsingCache() ? renderersCache : new RenderersCache(getJasperReportsContext());
topPadding = printImage.getLineBox().getTopPadding().intValue();
leftPadding = printImage.getLineBox().getLeftPadding().intValue();
bottomPadding = printImage.getLineBox().getBottomPadding().intValue();
rightPadding = printImage.getLineBox().getRightPadding().intValue();
int tmpAvailableImageWidth = printImage.getWidth() - leftPadding - rightPadding;
availableImageWidth = tmpAvailableImageWidth < 0 ? 0 : tmpAvailableImageWidth;
int tmpAvailableImageHeight = printImage.getHeight() - topPadding - bottomPadding;
availableImageHeight = tmpAvailableImageHeight < 0 ? 0 : tmpAvailableImageHeight;
}
private InternalImageProcessorResult process(Renderable renderer) throws JRException, IOException, BadElementException
{
InternalImageProcessorResult imageProcessorResult = null;
if (renderer instanceof ResourceRenderer)
{
renderer = imageRenderersCache.getLoadedRenderer((ResourceRenderer)renderer);
}
if (renderer instanceof Graphics2DRenderable)
{
imageProcessorResult = processGraphics2D((Graphics2DRenderable)renderer);
}
else if (renderer instanceof DataRenderable)
{
boolean isSvgData = getRendererUtil().isSvgData((DataRenderable)renderer);
if (isSvgData)
{
imageProcessorResult =
processGraphics2D(
new WrappingSvgDataToGraphics2DRenderer((DataRenderable)renderer)
);
}
else
{
switch(printImage.getScaleImageValue())
{
case CLIP :
{
imageProcessorResult =
processImageClip(
new WrappingImageDataToGraphics2DRenderer((DataRenderable)renderer)
);
break;
}
case FILL_FRAME :
{
imageProcessorResult = processImageFillFrame(renderer.getId(), (DataRenderable)renderer);
break;
}
case RETAIN_SHAPE :
default :
{
imageProcessorResult = processImageRetainShape(renderer.getId(), (DataRenderable)renderer);
}
}
}
}
else
{
throw
new JRException(
RendererUtil.EXCEPTION_MESSAGE_KEY_RENDERABLE_MUST_IMPLEMENT_INTERFACE,
new Object[]{
renderer.getClass().getName(),
DataRenderable.class.getName()
+ " or " + Graphics2DRenderable.class.getName()
}
);
}
return imageProcessorResult;
}
private InternalImageProcessorResult processImageClip(Graphics2DRenderable renderer) throws JRException, IOException, BadElementException
{
int normalWidth = availableImageWidth;
int normalHeight = availableImageHeight;
Dimension2D dimension =
renderer instanceof DimensionRenderable
? ((DimensionRenderable)renderer).getDimension(jasperReportsContext)
: null;
if (dimension != null)
{
normalWidth = (int)dimension.getWidth();
normalHeight = (int)dimension.getHeight();
}
int xoffset = (int)(ImageUtil.getXAlignFactor(printImage) * (availableImageWidth - normalWidth));
int yoffset = (int)(ImageUtil.getYAlignFactor(printImage) * (availableImageHeight - normalHeight));
int minWidth = Math.min(normalWidth, availableImageWidth);
int minHeight = Math.min(normalHeight, availableImageHeight);
BufferedImage bi =
new BufferedImage(minWidth, minHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
try
{
if (printImage.getModeValue() == ModeEnum.OPAQUE)
{
g.setColor(printImage.getBackcolor());
g.fillRect(0, 0, minWidth, minHeight);
}
renderer.render(
jasperReportsContext,
g,
new java.awt.Rectangle(
(xoffset > 0 ? 0 : xoffset),
(yoffset > 0 ? 0 : yoffset),
normalWidth,
normalHeight
)
);
}
finally
{
g.dispose();
}
xoffset = (xoffset < 0 ? 0 : xoffset);
yoffset = (yoffset < 0 ? 0 : yoffset);
//awtImage = bi.getSubimage(0, 0, minWidth, minHeight);
//image = com.lowagie.text.Image.getInstance(awtImage, printImage.getBackcolor());
Image image = Image.getInstance(bi, null);
return
new InternalImageProcessorResult(
new Chunk(image, 0, 0),
image.getScaledWidth(),
image.getScaledHeight(),
xoffset,
yoffset
);
}
private InternalImageProcessorResult processImageFillFrame(String rendererId, DataRenderable renderer) throws JRException
{
Image image = null;
if (printImage.isUsingCache() && loadedImagesMap.containsKey(rendererId))
{
image = loadedImagesMap.get(rendererId);
}
else
{
try
{
image = Image.getInstance(renderer.getData(jasperReportsContext));
imageTesterPdfContentByte.addImage(image, 10, 0, 0, 10, 0, 0);
}
catch (Exception e)
{
throw new JRException(e);
}
if (printImage.isUsingCache())
{
loadedImagesMap.put(rendererId, image);
}
}
image.scaleAbsolute(availableImageWidth, availableImageHeight);
return
new InternalImageProcessorResult(
new Chunk(image, 0, 0),
image.getScaledWidth(),
image.getScaledHeight(),
0,
0
);
}
private InternalImageProcessorResult processImageRetainShape(String rendererId, DataRenderable renderer) throws JRException
{
Image image = null;
if (printImage.isUsingCache() && loadedImagesMap.containsKey(rendererId))
{
image = loadedImagesMap.get(rendererId);
}
else
{
try
{
image = Image.getInstance(renderer.getData(jasperReportsContext));
imageTesterPdfContentByte.addImage(image, 10, 0, 0, 10, 0, 0);
}
catch (Exception e)
{
throw new JRException(e);
}
if (printImage.isUsingCache())
{
loadedImagesMap.put(rendererId, image);
}
}
image.scaleToFit(availableImageWidth, availableImageHeight);
int xoffset = (int)(ImageUtil.getXAlignFactor(printImage) * (availableImageWidth - image.getPlainWidth()));
int yoffset = (int)(ImageUtil.getYAlignFactor(printImage) * (availableImageHeight - image.getPlainHeight()));
xoffset = (xoffset < 0 ? 0 : xoffset);
yoffset = (yoffset < 0 ? 0 : yoffset);
return
new InternalImageProcessorResult(
new Chunk(image, 0, 0),
image.getScaledWidth(),
image.getScaledHeight(),
xoffset,
yoffset
);
}
private InternalImageProcessorResult processGraphics2D(Graphics2DRenderable renderer) throws JRException, IOException
{
int xoffset = 0;
int yoffset = 0;
double normalWidth = availableImageWidth;
double normalHeight = availableImageHeight;
double displayWidth = availableImageWidth;
double displayHeight = availableImageHeight;
double ratioX = 1f;
double ratioY = 1f;
Rectangle2D clip = null;
Dimension2D dimension = renderer instanceof DimensionRenderable ? ((DimensionRenderable)renderer).getDimension(jasperReportsContext) : null;
if (dimension != null)
{
normalWidth = dimension.getWidth();
normalHeight = dimension.getHeight();
displayWidth = normalWidth;
displayHeight = normalHeight;
switch (printImage.getScaleImageValue())
{
case CLIP:
{
xoffset = (int) (ImageUtil.getXAlignFactor(printImage) * (availableImageWidth - normalWidth));
yoffset = (int) (ImageUtil.getYAlignFactor(printImage) * (availableImageHeight - normalHeight));
clip =
new Rectangle2D.Double(
- xoffset,
- yoffset,
availableImageWidth,
availableImageHeight
);
break;
}
case FILL_FRAME:
{
ratioX = availableImageWidth / normalWidth;
ratioY = availableImageHeight / normalHeight;
normalWidth *= ratioX;
normalHeight *= ratioY;
xoffset = 0;
yoffset = 0;
break;
}
case RETAIN_SHAPE:
default:
{
ratioX = availableImageWidth / normalWidth;
ratioY = availableImageHeight / normalHeight;
ratioX = ratioX < ratioY ? ratioX : ratioY;
ratioY = ratioX;
normalWidth *= ratioX;
normalHeight *= ratioY;
xoffset = (int) (ImageUtil.getXAlignFactor(printImage) * (availableImageWidth - normalWidth));
yoffset = (int) (ImageUtil.getYAlignFactor(printImage) * (availableImageHeight - normalHeight));
break;
}
}
}
PdfTemplate template = pdfContentByte.createTemplate((float)displayWidth, (float)displayHeight);
Graphics2D g = getCurrentItemConfiguration().isForceSvgShapes()
? template.createGraphicsShapes((float)displayWidth, (float)displayHeight)
: template.createGraphics(availableImageWidth, availableImageHeight, new LocalFontMapper());
try
{
if (clip != null)
{
g.setClip(clip);
}
if (printImage.getModeValue() == ModeEnum.OPAQUE)
{
g.setColor(printImage.getBackcolor());
g.fillRect(0, 0, (int)displayWidth, (int)displayHeight);
}
renderer.render(jasperReportsContext, g, new Rectangle2D.Double(0, 0, displayWidth, displayHeight));
}
finally
{
g.dispose();
}
pdfContentByte.saveState();
pdfContentByte.addTemplate(
template,
(float)ratioX, 0f, 0f, (float)ratioY,
printImage.getX()
+ leftPadding + getOffsetX() + xoffset,
pageFormat.getPageHeight()
- printImage.getY() - topPadding - getOffsetY()
- (int)normalHeight
- yoffset
);
pdfContentByte.restoreState();
Image image = getPxImage();
image.scaleAbsolute(availableImageWidth, availableImageHeight);
InternalImageProcessorResult result =
new InternalImageProcessorResult(
new Chunk(image, 0, 0),
availableImageWidth,
availableImageHeight,
xoffset,
yoffset
);
pdfWriter.releaseTemplate(template);
return result;
}
}
private class InternalImageProcessorResult
{
private final Chunk chunk;
private final float scaledWidth;
private final float scaledHeight;
private final int xoffset;
private final int yoffset;
private InternalImageProcessorResult(
Chunk chunk,
float scaledWidth,
float scaledHeight,
int xoffset,
int yoffset
)
{
this.chunk = chunk;
this.scaledWidth = scaledWidth;
this.scaledHeight = scaledHeight;
this.xoffset = xoffset;
this.yoffset = yoffset;
}
}
/**
*
*/
protected void setHyperlinkInfo(Chunk chunk, JRPrintHyperlink link)
{
if (link != null)
{
Boolean ignoreHyperlink = HyperlinkUtil.getIgnoreHyperlink(PdfReportConfiguration.PROPERTY_IGNORE_HYPERLINK, link);
if (ignoreHyperlink == null)
{
ignoreHyperlink = getCurrentItemConfiguration().isIgnoreHyperlink();
}
if (!ignoreHyperlink)
{
switch(link.getHyperlinkTypeValue())
{
case REFERENCE :
{
if (link.getHyperlinkReference() != null)
{
switch(link.getHyperlinkTargetValue())
{
case BLANK :
{
chunk.setAction(
PdfAction.javaScript(
"if (app.viewerVersion < 7)"
+ "{this.getURL(\"" + link.getHyperlinkReference() + "\");}"
+ "else {app.launchURL(\"" + link.getHyperlinkReference() + "\", true);};",
pdfWriter
)
);
break;
}
case SELF :
default :
{
chunk.setAnchor(link.getHyperlinkReference());
break;
}
}
}
break;
}
case LOCAL_ANCHOR :
{
if (link.getHyperlinkAnchor() != null)
{
chunk.setLocalGoto(link.getHyperlinkAnchor());
}
break;
}
case LOCAL_PAGE :
{
if (link.getHyperlinkPage() != null)
{
chunk.setLocalGoto(JR_PAGE_ANCHOR_PREFIX + reportIndex + "_" + link.getHyperlinkPage().toString());
}
break;
}
case REMOTE_ANCHOR :
{
if (
link.getHyperlinkReference() != null &&
link.getHyperlinkAnchor() != null
)
{
chunk.setRemoteGoto(
link.getHyperlinkReference(),
link.getHyperlinkAnchor()
);
}
break;
}
case REMOTE_PAGE :
{
if (
link.getHyperlinkReference() != null &&
link.getHyperlinkPage() != null
)
{
chunk.setRemoteGoto(
link.getHyperlinkReference(),
link.getHyperlinkPage().intValue()
);
}
break;
}
case CUSTOM :
{
JRHyperlinkProducerFactory hyperlinkProducerFactory = getCurrentItemConfiguration().getHyperlinkProducerFactory();
if (hyperlinkProducerFactory != null)
{
String hyperlink = hyperlinkProducerFactory.produceHyperlink(link);
if (hyperlink != null)
{
switch(link.getHyperlinkTargetValue())
{
case BLANK :
{
chunk.setAction(
PdfAction.javaScript(
"if (app.viewerVersion < 7)"
+ "{this.getURL(\"" + hyperlink + "\");}"
+ "else {app.launchURL(\"" + hyperlink + "\", true);};",
pdfWriter
)
);
break;
}
case SELF :
default :
{
chunk.setAnchor(hyperlink);
break;
}
}
}
}
}
case NONE :
default :
{
break;
}
}
}
}
}
@Override
protected Locale getTextLocale(JRPrintText text)
{
// only overriding for package access
return super.getTextLocale(text);
}
/**
*
*/
protected Phrase getPhrase(AttributedString as, String text, JRPrintText textElement)
{
Phrase phrase = new Phrase();
int runLimit = 0;
AttributedCharacterIterator iterator = as.getIterator();
Locale locale = getTextLocale(textElement);
boolean firstChunk = true;
while(runLimit < text.length() && (runLimit = iterator.getRunLimit()) <= text.length())
{
Map attributes = iterator.getAttributes();
Chunk chunk = getChunk(attributes, text.substring(iterator.getIndex(), runLimit), locale);
if (firstChunk)
{
// only set anchor + bookmark for the first chunk in the text
setAnchor(chunk, textElement, textElement);
}
JRPrintHyperlink hyperlink = textElement;
if (hyperlink.getHyperlinkTypeValue() == HyperlinkTypeEnum.NONE)
{
hyperlink = (JRPrintHyperlink)attributes.get(JRTextAttribute.HYPERLINK);
}
setHyperlinkInfo(chunk, hyperlink);
phrase.add(chunk);
iterator.setIndex(runLimit);
firstChunk = false;
}
return phrase;
}
/**
*
*/
protected Chunk getChunk(Map attributes, String text, Locale locale)
{
// underline and strikethrough are set on the chunk below
Font font = getFont(attributes, locale, false);
Chunk chunk = new Chunk(text, font);
if (hasUnderline(attributes))
{
// using the same values as sun.font.Fond2D
chunk.setUnderline(null, 0, 1f / 18, 0, -1f / 12, 0);
}
if (hasStrikethrough(attributes))
{
// using the same thickness as sun.font.Fond2D.
// the position is calculated in Fond2D based on the ascent, defaulting
// to iText default position which depends on the font size
chunk.setUnderline(null, 0, 1f / 18, 0, 1f / 3, 0);
}
Color backcolor = (Color)attributes.get(TextAttribute.BACKGROUND);
if (backcolor != null)
{
chunk.setBackground(backcolor);
}
Object script = attributes.get(TextAttribute.SUPERSCRIPT);
if (script != null)
{
if (TextAttribute.SUPERSCRIPT_SUPER.equals(script))
{
chunk.setTextRise(font.getCalculatedLeading(1f)/2);
}
else if (TextAttribute.SUPERSCRIPT_SUB.equals(script))
{
chunk.setTextRise(-font.getCalculatedLeading(1f)/2);
}
}
if (splitCharacter != null)
{
//TODO use line break offsets if available?
chunk.setSplitCharacter(splitCharacter);
}
return chunk;
}
protected boolean hasUnderline(Map textAttributes)
{
Integer underline = (Integer) textAttributes.get(TextAttribute.UNDERLINE);
return TextAttribute.UNDERLINE_ON.equals(underline);
}
protected boolean hasStrikethrough(Map textAttributes)
{
Boolean strike = (Boolean) textAttributes.get(TextAttribute.STRIKETHROUGH);
return TextAttribute.STRIKETHROUGH_ON.equals(strike);
}
/**
* Creates a PDF font.
*
* @param attributes the text attributes of the font
* @param locale the locale for which to create the font
* @param setFontLines whether to set underline and strikethrough as font style
* @return the PDF font for the specified attributes
*/
protected Font getFont(Map attributes, Locale locale, boolean setFontLines)
{
JRFont jrFont = new JRBaseFont(attributes);
Exception initialException = null;
Color forecolor = (Color)attributes.get(TextAttribute.FOREGROUND);
// use the same font scale ratio as in JRStyledText.getAwtAttributedString
float fontSizeScale = 1f;
Integer scriptStyle = (Integer) attributes.get(TextAttribute.SUPERSCRIPT);
if (scriptStyle != null && (
TextAttribute.SUPERSCRIPT_SUB.equals(scriptStyle)
|| TextAttribute.SUPERSCRIPT_SUPER.equals(scriptStyle)))
{
fontSizeScale = 2f / 3;
}
Font font = null;
String pdfFontName = null;
String pdfEncoding = null;
boolean isPdfEmbedded = false;
boolean isPdfSimulatedBold = false;
boolean isPdfSimulatedItalic = false;
FontInfo fontInfo = (FontInfo) attributes.get(JRTextAttribute.FONT_INFO);
if (fontInfo == null)
{
fontInfo = fontUtil.getFontInfo(jrFont.getFontName(), locale);
}
if (fontInfo == null)
{
//fontName NOT found in font extensions
pdfFontName = jrFont.getPdfFontName();
pdfEncoding = jrFont.getPdfEncoding();
isPdfEmbedded = jrFont.isPdfEmbedded();
}
else
{
//fontName found in font extensions
FontFamily family = fontInfo.getFontFamily();
int pdfFontStyle = java.awt.Font.PLAIN;
FontFace fontFace = fontInfo.getFontFace();
if (fontFace != null)
{
pdfFontName = fontFace.getPdf();
pdfFontName = pdfFontName == null ? fontFace.getTtf() : pdfFontName;
pdfFontStyle = fontInfo.getStyle();
}
if (pdfFontName == null && jrFont.isBold() && jrFont.isItalic())
{
fontFace = family.getBoldItalicFace();
if (fontFace != null)
{
pdfFontName = fontFace.getPdf();
pdfFontName = pdfFontName == null ? fontFace.getTtf() : pdfFontName;
pdfFontStyle = java.awt.Font.BOLD | java.awt.Font.ITALIC;
}
}
if (pdfFontName == null && jrFont.isBold())
{
fontFace = family.getBoldFace();
if (fontFace != null)
{
pdfFontName = fontFace.getPdf();
pdfFontName = pdfFontName == null ? fontFace.getTtf() : pdfFontName;
pdfFontStyle = java.awt.Font.BOLD;
}
}
if (pdfFontName == null && jrFont.isItalic())
{
fontFace = family.getItalicFace();
if (fontFace != null)
{
pdfFontName = fontFace.getPdf();
pdfFontName = pdfFontName == null ? fontFace.getTtf() : pdfFontName;
pdfFontStyle = java.awt.Font.ITALIC;
}
}
if (pdfFontName == null)
{
fontFace = family.getNormalFace();
if (fontFace != null)
{
pdfFontName = fontFace.getPdf();
pdfFontName = pdfFontName == null ? fontFace.getTtf() : pdfFontName;
pdfFontStyle = java.awt.Font.PLAIN;
}
}
if (pdfFontName == null)
{
pdfFontName = jrFont.getPdfFontName();
}
pdfEncoding = family.getPdfEncoding() == null ? jrFont.getPdfEncoding() : family.getPdfEncoding();
isPdfEmbedded = family.isPdfEmbedded() == null ? jrFont.isPdfEmbedded() : family.isPdfEmbedded().booleanValue();
isPdfSimulatedBold = jrFont.isBold() && ((pdfFontStyle & java.awt.Font.BOLD) == 0);
isPdfSimulatedItalic = jrFont.isItalic() && ((pdfFontStyle & java.awt.Font.ITALIC) == 0);
}
int pdfFontStyle = (isPdfSimulatedBold ? Font.BOLD : 0)
| (isPdfSimulatedItalic ? Font.ITALIC : 0);
if (setFontLines)
{
pdfFontStyle |= (jrFont.isUnderline() ? Font.UNDERLINE : 0)
| (jrFont.isStrikeThrough() ? Font.STRIKETHRU : 0);
}
try
{
font = FontFactory.getFont(
pdfFontName,
pdfEncoding,
isPdfEmbedded,
jrFont.getFontsize() * fontSizeScale,
pdfFontStyle,
forecolor
);
// check if FontFactory didn't find the font
if (font != null && font.getBaseFont() == null && font.getFamily() == Font.UNDEFINED)
{
font = null;
}
}
catch(Exception e)
{
initialException = e;
}
if (font == null)
{
byte[] bytes = null;
try
{
bytes = getRepository().getBytesFromLocation(pdfFontName);
}
catch(JRException e)
{
throw //NOPMD
new JRRuntimeException(
EXCEPTION_MESSAGE_KEY_FONT_LOADING_ERROR,
new Object[]{pdfFontName, pdfEncoding, isPdfEmbedded},
initialException);
}
BaseFont baseFont = null;
try
{
baseFont =
BaseFont.createFont(
pdfFontName,
pdfEncoding,
isPdfEmbedded,
true,
bytes,
null
);
}
catch(DocumentException e)
{
throw new JRRuntimeException(e);
}
catch(IOException e)
{
throw new JRRuntimeException(e);
}
font =
new Font(
baseFont,
jrFont.getFontsize() * fontSizeScale,
pdfFontStyle,
forecolor
);
}
return font;
}
/**
*
*/
public void exportText(JRPrintText text) throws DocumentException
{
JRStyledText styledText = styledTextUtil.getProcessedStyledText(text, noBackcolorSelector, null);
if (styledText == null)
{
return;
}
AbstractPdfTextRenderer textRenderer = getTextRenderer(text, styledText);
textRenderer.initialize(this, pdfContentByte, text, styledText, getOffsetX(), getOffsetY());
double angle = 0;
switch (text.getRotationValue())
{
case LEFT :
{
angle = Math.PI / 2;
break;
}
case RIGHT :
{
angle = - Math.PI / 2;
break;
}
case UPSIDE_DOWN :
{
angle = Math.PI;
break;
}
case NONE :
default :
{
}
}
AffineTransform atrans = new AffineTransform();
atrans.rotate(angle, textRenderer.getX(), pageFormat.getPageHeight() - textRenderer.getY());
pdfContentByte.transform(atrans);
if (text.getModeValue() == ModeEnum.OPAQUE)
{
Color backcolor = text.getBackcolor();
pdfContentByte.setRGBColorFill(
backcolor.getRed(),
backcolor.getGreen(),
backcolor.getBlue()
);
pdfContentByte.rectangle(
textRenderer.getX(),
pageFormat.getPageHeight() - textRenderer.getY(),
textRenderer.getWidth(),
- textRenderer.getHeight()
);
pdfContentByte.fill();
}
if (glyphRendererAddActualText && textRenderer instanceof PdfGlyphRenderer)
{
tagHelper.startText(styledText.getText(), text.getLinkType() != null);
}
else
{
tagHelper.startText(text.getLinkType() != null);
}
/* rendering only non empty texts */
if (styledText.length() > 0)
{
textRenderer.render();
}
tagHelper.endText();
atrans = new AffineTransform();
atrans.rotate(-angle, textRenderer.getX(), pageFormat.getPageHeight() - textRenderer.getY());
pdfContentByte.transform(atrans);
/* */
exportBox(
text.getLineBox(),
text
);
}
protected AbstractPdfTextRenderer getTextRenderer(JRPrintText text, JRStyledText styledText)
{
AbstractPdfTextRenderer textRenderer;
if (toUseGlyphRenderer(text)
&& PdfGlyphRenderer.supported()
&& canUseGlyphRendering(text, styledText))
{
textRenderer = new PdfGlyphRenderer(jasperReportsContext, awtIgnoreMissingFont,
glyphRendererAddActualText && !tagHelper.isTagged);
}
else if (text.getLeadingOffset() == 0)
{
textRenderer = new PdfTextRenderer(jasperReportsContext, awtIgnoreMissingFont);
}
else
{
textRenderer = new SimplePdfTextRenderer(jasperReportsContext, awtIgnoreMissingFont);//FIXMETAB optimize this
}
return textRenderer;
}
protected boolean canUseGlyphRendering(JRPrintText text, JRStyledText styledText)
{
Locale locale = getTextLocale(text);
AttributedCharacterIterator attributesIterator = styledText.getAttributedString().getIterator();
int index = 0;
while (index < styledText.length())
{
FontKey fontKey = extractFontKey(attributesIterator.getAttributes(), locale);
if (!fontKey.fontAttribute.hasAttribute())
{
return false;
}
Boolean canUse = glyphRendererFonts.get(fontKey);
if (canUse == null)
{
canUse = canUseGlyphRendering(fontKey);
glyphRendererFonts.put(fontKey, canUse);
}
if (!canUse)
{
return false;
}
index = attributesIterator.getRunLimit();
attributesIterator.setIndex(index);
}
return true;
}
protected FontKey extractFontKey(Map attributes, Locale locale)
{
AwtFontAttribute fontAttribute = AwtFontAttribute.fromAttributes(attributes);
Number posture = (Number) attributes.get(TextAttribute.POSTURE);
boolean italic = TextAttribute.POSTURE_OBLIQUE.equals(posture);//FIXME check for non standard posture
Number weight = (Number) attributes.get(TextAttribute.WEIGHT);
boolean bold = TextAttribute.WEIGHT_BOLD.equals(weight);
return new FontKey(fontAttribute, italic, bold, locale);
}
protected boolean canUseGlyphRendering(FontKey fontKey)
{
Map fontAttributes = new HashMap();
fontKey.fontAttribute.putAttributes(fontAttributes);
fontAttributes.put(TextAttribute.SIZE, 10f);
int style = 0;
if (fontKey.italic)
{
style |= java.awt.Font.ITALIC;
fontAttributes.put(TextAttribute.POSTURE, TextAttribute.POSTURE_OBLIQUE);
}
if (fontKey.bold)
{
style |= java.awt.Font.BOLD;
fontAttributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_BOLD);
}
Font pdfFont = getFont(fontAttributes, fontKey.locale, false);
BaseFont baseFont = pdfFont.getBaseFont();
if (baseFont.getFontType() != BaseFont.FONT_TYPE_TTUNI
|| baseFont.isFontSpecific())
{
if (log.isDebugEnabled())
{
log.debug("pdf font for " + fontKey + " has type " + baseFont.getFontType()
+ ", symbol " + baseFont.isFontSpecific()
+ ", cannot use glyph rendering");
}
return false;
}
java.awt.Font awtFont = fontUtil.getAwtFontFromBundles(fontKey.fontAttribute, style,
10f, fontKey.locale, awtIgnoreMissingFont);
if (awtFont == null)
{
awtFont = new java.awt.Font(fontAttributes);
}
String awtFontName = awtFont.getFontName();
if (log.isDebugEnabled())
{
log.debug(fontKey + " resolved to awt font " + awtFontName);
}
// we need the fonts to be identical.
// it would be safer to only allow fonts from extensions,
// but for now we are just checking the font names.
// we need to compare full names because we can't get the base name from awt.
String[][] pdfFontNames = baseFont.getFullFontName();
boolean nameMatch = false;
for (String[] nameArray : pdfFontNames)
{
if (nameArray.length >= 4)
{
if (log.isDebugEnabled())
{
log.debug(fontKey + " resolved to pdf font " + nameArray[3]);
}
if (awtFontName.equals(nameArray[3]))
{
nameMatch = true;
break;
}
}
}
return nameMatch;
}
protected boolean toUseGlyphRenderer(JRPrintText text)
{
String value = styledTextUtil.getTruncatedText(text);
if (value == null)
{
return false;
}
if (glyphRendererBlocks.isEmpty())
{
return false;
}
int charCount = value.length();
char[] chars = new char[charCount];
value.getChars(0, charCount, chars, 0);
for (char c : chars)
{
UnicodeBlock block = UnicodeBlock.of(c);
if (glyphRendererBlocks.contains(block))
{
if (log.isTraceEnabled())
{
log.trace("found character in block " + block + ", using the glyph renderer");
}
return true;
}
}
return false;
}
/**
*
*/
protected void exportBox(JRLineBox box, JRPrintElement element)
{
exportTopPen(box.getTopPen(), box.getLeftPen(), box.getRightPen(), element);
exportLeftPen(box.getTopPen(), box.getLeftPen(), box.getBottomPen(), element);
exportBottomPen(box.getLeftPen(), box.getBottomPen(), box.getRightPen(), element);
exportRightPen(box.getTopPen(), box.getBottomPen(), box.getRightPen(), element);
pdfContentByte.setLineDash(0f);
pdfContentByte.setLineCap(PdfContentByte.LINE_CAP_PROJECTING_SQUARE);
}
/**
*
*/
protected void exportPen(JRPen pen, JRPrintElement element)
{
exportTopPen(pen, pen, pen, element);
exportLeftPen(pen, pen, pen, element);
exportBottomPen(pen, pen, pen, element);
exportRightPen(pen, pen, pen, element);
pdfContentByte.setLineDash(0f);
pdfContentByte.setLineCap(PdfContentByte.LINE_CAP_PROJECTING_SQUARE);
}
/**
*
*/
protected void exportTopPen(
JRPen topPen,
JRPen leftPen,
JRPen rightPen,
JRPrintElement element)
{
if (topPen.getLineWidth().floatValue() > 0f)
{
float leftOffset = leftPen.getLineWidth().floatValue() / 2;
float rightOffset = rightPen.getLineWidth().floatValue() / 2;
int lcOffsetX = getOffsetX();
int lcOffsetY = getOffsetY();
preparePen(pdfContentByte, topPen, PdfContentByte.LINE_CAP_BUTT);
if (topPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
{
float topOffset = topPen.getLineWidth().floatValue();
pdfContentByte.moveTo(
element.getX() + lcOffsetX - leftOffset,
pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset / 3
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX + element.getWidth() + rightOffset,
pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset / 3
);
pdfContentByte.stroke();
pdfContentByte.moveTo(
element.getX() + lcOffsetX + leftOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - topOffset / 3
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX + element.getWidth() - rightOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - topOffset / 3
);
pdfContentByte.stroke();
}
else
{
pdfContentByte.moveTo(
element.getX() + lcOffsetX - leftOffset,
pageFormat.getPageHeight() - element.getY() - lcOffsetY
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX + element.getWidth() + rightOffset,
pageFormat.getPageHeight() - element.getY() - lcOffsetY
);
pdfContentByte.stroke();
}
}
}
/**
*
*/
protected void exportLeftPen(JRPen topPen, JRPen leftPen, JRPen bottomPen, JRPrintElement element)
{
if (leftPen.getLineWidth().floatValue() > 0f)
{
float topOffset = topPen.getLineWidth().floatValue() / 2;
float bottomOffset = bottomPen.getLineWidth().floatValue() / 2;
int lcOffsetX = getOffsetX();
int lcOffsetY = getOffsetY();
preparePen(pdfContentByte, leftPen, PdfContentByte.LINE_CAP_BUTT);
if (leftPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
{
float leftOffset = leftPen.getLineWidth().floatValue();
pdfContentByte.moveTo(
element.getX() + lcOffsetX - leftOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX - leftOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset
);
pdfContentByte.stroke();
pdfContentByte.moveTo(
element.getX() + lcOffsetX + leftOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - topOffset / 3
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX + leftOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() + bottomOffset / 3
);
pdfContentByte.stroke();
}
else
{
pdfContentByte.moveTo(
element.getX() + lcOffsetX,
pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset
);
pdfContentByte.stroke();
}
}
}
/**
*
*/
protected void exportBottomPen(JRPen leftPen, JRPen bottomPen, JRPen rightPen, JRPrintElement element)
{
if (bottomPen.getLineWidth().floatValue() > 0f)
{
float leftOffset = leftPen.getLineWidth().floatValue() / 2;
float rightOffset = rightPen.getLineWidth().floatValue() / 2;
int lcOffsetX = getOffsetX();
int lcOffsetY = getOffsetY();
preparePen(pdfContentByte, bottomPen, PdfContentByte.LINE_CAP_BUTT);
if (bottomPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
{
float bottomOffset = bottomPen.getLineWidth().floatValue();
pdfContentByte.moveTo(
element.getX() + lcOffsetX - leftOffset,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset / 3
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX + element.getWidth() + rightOffset,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset / 3
);
pdfContentByte.stroke();
pdfContentByte.moveTo(
element.getX() + lcOffsetX + leftOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() + bottomOffset / 3
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX + element.getWidth() - rightOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() + bottomOffset / 3
);
pdfContentByte.stroke();
}
else
{
pdfContentByte.moveTo(
element.getX() + lcOffsetX - leftOffset,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight()
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX + element.getWidth() + rightOffset,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight()
);
pdfContentByte.stroke();
}
}
}
/**
*
*/
protected void exportRightPen(JRPen topPen, JRPen bottomPen, JRPen rightPen, JRPrintElement element)
{
if (rightPen.getLineWidth().floatValue() > 0f)
{
float topOffset = topPen.getLineWidth().floatValue() / 2;
float bottomOffset = bottomPen.getLineWidth().floatValue() / 2;
int lcOffsetX = getOffsetX();
int lcOffsetY = getOffsetY();
preparePen(pdfContentByte, rightPen, PdfContentByte.LINE_CAP_BUTT);
if (rightPen.getLineStyleValue() == LineStyleEnum.DOUBLE)
{
float rightOffset = rightPen.getLineWidth().floatValue();
pdfContentByte.moveTo(
element.getX() + lcOffsetX + element.getWidth() + rightOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX + element.getWidth() + rightOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset
);
pdfContentByte.stroke();
pdfContentByte.moveTo(
element.getX() + lcOffsetX + element.getWidth() - rightOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - topOffset / 3
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX + element.getWidth() - rightOffset / 3,
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() + bottomOffset / 3
);
pdfContentByte.stroke();
}
else
{
pdfContentByte.moveTo(
element.getX() + lcOffsetX + element.getWidth(),
pageFormat.getPageHeight() - element.getY() - lcOffsetY + topOffset
);
pdfContentByte.lineTo(
element.getX() + lcOffsetX + element.getWidth(),
pageFormat.getPageHeight() - element.getY() - lcOffsetY - element.getHeight() - bottomOffset
);
pdfContentByte.stroke();
}
}
}
/**
*
*/
private static void preparePen(PdfContentByte pdfContentByte, JRPen pen, int lineCap)
{
float lineWidth = pen.getLineWidth().floatValue();
if (lineWidth <= 0)
{
return;
}
pdfContentByte.setLineWidth(lineWidth);
pdfContentByte.setLineCap(lineCap);
Color color = pen.getLineColor();
pdfContentByte.setRGBColorStroke(
color.getRed(),
color.getGreen(),
color.getBlue()
);
switch (pen.getLineStyleValue())
{
case DOUBLE :
{
pdfContentByte.setLineWidth(lineWidth / 3);
pdfContentByte.setLineDash(0f);
break;
}
case DOTTED :
{
switch (lineCap)
{
case PdfContentByte.LINE_CAP_BUTT :
{
pdfContentByte.setLineDash(lineWidth, lineWidth, 0f);
break;
}
case PdfContentByte.LINE_CAP_PROJECTING_SQUARE :
{
pdfContentByte.setLineDash(0, 2 * lineWidth, 0f);
break;
}
}
break;
}
case DASHED :
{
switch (lineCap)
{
case PdfContentByte.LINE_CAP_BUTT :
{
pdfContentByte.setLineDash(5 * lineWidth, 3 * lineWidth, 0f);
break;
}
case PdfContentByte.LINE_CAP_PROJECTING_SQUARE :
{
pdfContentByte.setLineDash(4 * lineWidth, 4 * lineWidth, 0f);
break;
}
}
break;
}
case SOLID :
default :
{
pdfContentByte.setLineDash(0f);
break;
}
}
}
protected static synchronized void registerFonts ()
{
if (!fontsRegistered)
{
List fontFiles = JRPropertiesUtil.getInstance(DefaultJasperReportsContext.getInstance()).getProperties(PDF_FONT_FILES_PREFIX);//FIXMECONTEXT no default here and below
if (!fontFiles.isEmpty())
{
for (Iterator i = fontFiles.iterator(); i.hasNext();)
{
JRPropertiesUtil.PropertySuffix font = i.next();
String file = font.getValue();
if (file.toLowerCase().endsWith(".ttc"))
{
FontFactory.register(file);
}
else
{
String alias = font.getSuffix();
FontFactory.register(file, alias);
}
}
}
List fontDirs = JRPropertiesUtil.getInstance(DefaultJasperReportsContext.getInstance()).getProperties(PDF_FONT_DIRS_PREFIX);
if (!fontDirs.isEmpty())
{
for (Iterator i = fontDirs.iterator(); i.hasNext();)
{
JRPropertiesUtil.PropertySuffix dir = i.next();
FontFactory.registerDirectory(dir.getValue());
}
}
fontsRegistered = true;
}
}
static protected class Bookmark
{
final PdfOutline pdfOutline;
final int level;
Bookmark(Bookmark parent, int x, int top, String title)
{
this(parent, new PdfDestination(PdfDestination.XYZ, x, top, 0), title);
}
Bookmark(Bookmark parent, PdfDestination destination, String title)
{
this.pdfOutline = new PdfOutline(parent.pdfOutline, destination, title, false);
this.level = parent.level + 1;
}
Bookmark(PdfOutline pdfOutline, int level)
{
this.pdfOutline = pdfOutline;
this.level = level;
}
}
static protected class BookmarkStack
{
LinkedList stack;
BookmarkStack()
{
stack = new LinkedList();
}
void push(Bookmark bookmark)
{
stack.add(bookmark);
}
Bookmark pop()
{
return stack.removeLast();
}
Bookmark peek()
{
return stack.getLast();
}
}
protected void initBookmarks(List items)
{
bookmarkStack = new BookmarkStack();
int rootLevel = items.size() > 1 && getCurrentConfiguration().isCreatingBatchModeBookmarks() ? -1 : 0;
Bookmark bookmark = new Bookmark(pdfContentByte.getRootOutline(), rootLevel);
bookmarkStack.push(bookmark);
}
protected void addBookmark(int level, String title, int x, int y)
{
Bookmark parent = bookmarkStack.peek();
// searching for parent
while(parent.level >= level)
{
bookmarkStack.pop();
parent = bookmarkStack.peek();
}
if (!getCurrentItemConfiguration().isCollapseMissingBookmarkLevels())
{
// creating empty bookmarks in order to preserve the bookmark level
for (int i = parent.level + 1; i < level; ++i)
{
Bookmark emptyBookmark = new Bookmark(parent, parent.pdfOutline.getPdfDestination(), EMPTY_BOOKMARK_TITLE);
bookmarkStack.push(emptyBookmark);
parent = emptyBookmark;
}
}
int height = OrientationEnum.PORTRAIT.equals(pageFormat.getOrientation())
? pageFormat.getPageHeight() - y
: y;
Bookmark bookmark = new Bookmark(parent, x, height, title);
bookmarkStack.push(bookmark);
}
protected void setAnchor(Chunk chunk, JRPrintAnchor anchor, JRPrintElement element)
{
String anchorName = anchor.getAnchorName();
if (anchorName != null)
{
chunk.setLocalDestination(anchorName);
if (anchor.getBookmarkLevel() != JRAnchor.NO_BOOKMARK)
{
int x = OrientationEnum.PORTRAIT.equals(pageFormat.getOrientation())
? getOffsetX() + element.getX()
: getOffsetY() + element.getY();
int y = OrientationEnum.PORTRAIT.equals(pageFormat.getOrientation())
? getOffsetY() + element.getY()
: getOffsetX() + element.getX();
addBookmark(anchor.getBookmarkLevel(), anchor.getAnchorName(), x, y);
}
}
}
public void exportFrame(JRPrintFrame frame) throws DocumentException, IOException, JRException
{
if (frame.getModeValue() == ModeEnum.OPAQUE)
{
int x = frame.getX() + getOffsetX();
int y = frame.getY() + getOffsetY();
Color backcolor = frame.getBackcolor();
pdfContentByte.setRGBColorFill(
backcolor.getRed(),
backcolor.getGreen(),
backcolor.getBlue()
);
pdfContentByte.rectangle(
x,
pageFormat.getPageHeight() - y,
frame.getWidth(),
- frame.getHeight()
);
pdfContentByte.fill();
}
setFrameElementsOffset(frame, false);
try
{
exportElements(frame.getElements());
}
finally
{
restoreElementOffsets();
}
exportBox(frame.getLineBox(), frame);
}
/**
*
*/
protected PrintPageFormat getCurrentPageFormat()
{
return pageFormat;
}
@Override
protected int getOffsetX()
{
return
super.getOffsetX()
+ (insideFrame() ? 0 : (crtDocumentPageNumber % 2 == 0
? crtEvenPageOffsetX
: crtOddPageOffsetX));
}
@Override
protected int getOffsetY()
{
return
super.getOffsetY()
+ (insideFrame() ? 0 : (crtDocumentPageNumber % 2 == 0
? crtEvenPageOffsetY
: crtOddPageOffsetY));
}
/**
*
*/
class LocalFontMapper implements FontMapper
{
public LocalFontMapper()
{
}
@Override
public BaseFont awtToPdf(java.awt.Font font)
{
// not setting underline and strikethrough as we only need the base font.
// underline and strikethrough will not work here because PdfGraphics2D
// doesn't check the font attributes.
Map atts = new HashMap();
atts.putAll(font.getAttributes());
return getFont(atts, null, false).getBaseFont();
}
@Override
public java.awt.Font pdfToAwt(BaseFont font, int size)
{
return null;
}
}
/**
*
*/
protected void exportGenericElement(JRGenericPrintElement element)
{
GenericElementPdfHandler handler = (GenericElementPdfHandler)
GenericElementHandlerEnviroment.getInstance(getJasperReportsContext()).getElementHandler(
element.getGenericType(), PDF_EXPORTER_KEY);
if (handler != null)
{
handler.exportElement(exporterContext, element);
}
else
{
if (log.isDebugEnabled())
{
log.debug("No PDF generic element handler for "
+ element.getGenericType());
}
}
}
@Override
public String getExporterKey()
{
return PDF_EXPORTER_KEY;
}
@Override
public String getExporterPropertiesPrefix()
{
return PDF_EXPORTER_PROPERTIES_PREFIX;
}
public static int getIntegerPermissions(String permissions) {
int permission = 0;
if(permissions != null && permissions.length() > 0) {
String[] perms = permissions.split("\\|");
for(String perm : perms) {
if(PdfPermissionsEnum.ALL.equals(PdfPermissionsEnum.getByName(perm))) {
permission = PdfExporterConfiguration.ALL_PERMISSIONS;
break;
}
if(perm != null && perm.length()>0) {
permission |= PdfPermissionsEnum.getByName(perm).getPdfPermission();
}
}
}
return permission;
}
protected static class FontKey
{
AwtFontAttribute fontAttribute;
boolean italic;
boolean bold;
Locale locale;
public FontKey(AwtFontAttribute fontAttribute, boolean italic, boolean bold, Locale locale)
{
this.fontAttribute = fontAttribute;
this.italic = italic;
this.bold = bold;
this.locale = locale;
}
@Override
public int hashCode()
{
int hash = 43;
hash = hash*29 + fontAttribute.hashCode();
hash = hash*29 + (italic ? 1231 : 1237);
hash = hash*29 + (bold ? 1231 : 1237);
hash = hash*29 + (locale == null ? 0 : locale.hashCode());
return hash;
}
@Override
public boolean equals(Object obj)
{
FontKey key = (FontKey) obj;
return fontAttribute.equals(key.fontAttribute) && italic == key.italic && bold == key.bold
&& ((locale == null) ? (key.locale == null) : (key.locale != null && locale.equals(key.locale)));
}
@Override
public String toString()
{
return "{font: " + fontAttribute
+ ", italic: " + italic
+ ", bold: " + bold
+ "}";
}
}
}