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 package net.sf.jasperreports.engine.util;
25
26 import java.util.Locale;
27
28 import net.sf.jasperreports.annotations.properties.Property;
29 import net.sf.jasperreports.annotations.properties.PropertyScope;
30 import net.sf.jasperreports.engine.JRCommonText;
31 import net.sf.jasperreports.engine.JRException;
32 import net.sf.jasperreports.engine.JRPrintText;
33 import net.sf.jasperreports.engine.JRPropertiesHolder;
34 import net.sf.jasperreports.engine.JRPropertiesUtil;
35 import net.sf.jasperreports.engine.JRRuntimeException;
36 import net.sf.jasperreports.engine.JRStyledTextAttributeSelector;
37 import net.sf.jasperreports.engine.JasperReportsContext;
38 import net.sf.jasperreports.engine.fill.JRMeasuredText;
39 import net.sf.jasperreports.engine.fill.JRTextMeasurer;
40 import net.sf.jasperreports.properties.PropertyConstants;
41
42 /**
43  * Text measurer utility class.
44  * 
45  * @author Lucian Chirita (lucianc@users.sourceforge.net)
46  * @see JRTextMeasurer
47  * @see JRTextMeasurerFactory
48  */

49 public final class JRTextMeasurerUtil
50 {
51     private final JasperReportsContext jasperReportsContext;
52     private final JRStyledTextAttributeSelector noBackcolorSelector;//FIXMECONTEXT make this a context object everywhere and retrieve using a constant key
53     private final JRStyledTextUtil styledTextUtil;
54
55
56     /**
57      *
58      */

59     private JRTextMeasurerUtil(JasperReportsContext jasperReportsContext)
60     {
61         this.jasperReportsContext = jasperReportsContext;
62         this.noBackcolorSelector = JRStyledTextAttributeSelector.getNoBackcolorSelector(jasperReportsContext);
63         this.styledTextUtil = JRStyledTextUtil.getInstance(jasperReportsContext);
64     }
65     
66     
67     /**
68      *
69      */

70     public static JRTextMeasurerUtil getInstance(JasperReportsContext jasperReportsContext)
71     {
72         return new JRTextMeasurerUtil(jasperReportsContext);
73     }
74     
75     
76     /**
77      * Property that specifies a text measurer factory.
78      * 
79      * <p>
80      * This property can either hold the name of a text measurer factory class, e.g.
81      * <code>
82      * <pre>
83      * net.sf.jasperreports.text.measurer.factory=org.me.MyTextMeasurerFactory
84      * </pre>
85      * </code>
86      * or hold an alias of a text measurer factory class property, e.g.
87      * <code>
88      * <pre>
89      * net.sf.jasperreports.text.measurer.factory=myTextMeasurer
90      * ...
91      * net.sf.jasperreports.text.measurer.factory.myTextMeasurer=org.me.MyTextMeasurerFactory
92      * </pre>
93      * </code>
94      * </p>
95      * 
96      * @see JRTextMeasurerFactory
97      */

98     @Property(
99             category = PropertyConstants.CATEGORY_FILL,
100             defaultValue = "net.sf.jasperreports.engine.fill.TextMeasurerFactory",
101             scopes = {PropertyScope.CONTEXT, PropertyScope.REPORT, PropertyScope.TEXT_ELEMENT},
102             sinceVersion = PropertyConstants.VERSION_2_0_3
103             )
104     public static final String PROPERTY_TEXT_MEASURER_FACTORY = 
105         JRPropertiesUtil.PROPERTY_PREFIX + "text.measurer.factory";
106     
107     private static final JRSingletonCache<JRTextMeasurerFactory> cache = 
108             new JRSingletonCache<JRTextMeasurerFactory>(JRTextMeasurerFactory.class);
109     
110     /**
111      * Creates a text measurer for a text object.
112      * 
113      * <p>
114      * If the text object is an instance of {@link JRPropertiesHolder}, its properties
115      * are used when determining the text measurer factory.
116      * </p>
117      * 
118      * @param text the text object
119      * @return a text measurer for the text object
120      */

121     public JRTextMeasurer createTextMeasurer(JRCommonText text)
122     {
123         JRPropertiesHolder propertiesHolder =
124             text instanceof JRPropertiesHolder ? (JRPropertiesHolder) text : null;
125         return createTextMeasurer(text, propertiesHolder);
126     }
127     
128     /**
129      * Creates a text measurer for a text object.
130      * 
131      * @param text the text object
132      * @param propertiesHolder the properties to use for determining the text measurer factory;
133      * can be <code>null</code>
134      * @return a text measurer for the text object
135      */

136     public JRTextMeasurer createTextMeasurer(JRCommonText text, JRPropertiesHolder propertiesHolder)
137     {
138         JRTextMeasurerFactory factory = getFactory(propertiesHolder);
139         return factory.createMeasurer(jasperReportsContext, text);
140     }
141     
142     /**
143      * Returns the text measurer factory given a set of properties.
144      * 
145      * @param propertiesHolder the properties holder
146      * @return the text measurer factory
147      */

148     public JRTextMeasurerFactory getFactory(JRPropertiesHolder propertiesHolder)
149     {
150         String factoryClass = getTextMeasurerFactoryClass(propertiesHolder);
151         try
152         {
153             return cache.getCachedInstance(factoryClass);
154         }
155         catch (JRException e)
156         {
157             throw new JRRuntimeException(e);
158         }
159     }
160
161     protected String getTextMeasurerFactoryClass(JRPropertiesHolder propertiesHolder)
162     {
163         String factory = JRPropertiesUtil.getInstance(jasperReportsContext).getProperty(propertiesHolder, PROPERTY_TEXT_MEASURER_FACTORY);
164         String factoryClassProperty = PROPERTY_TEXT_MEASURER_FACTORY + '.' + factory;
165         String factoryClass = JRPropertiesUtil.getInstance(jasperReportsContext).getProperty(propertiesHolder, factoryClassProperty);
166         if (factoryClass == null)
167         {
168             factoryClass = factory;
169         }
170         return factoryClass;
171     }
172
173     
174     /**
175      * 
176      */

177     public void measureTextElement(JRPrintText printText)
178     {
179         String text = styledTextUtil.getTruncatedText(printText);
180         
181         JRTextMeasurer textMeasurer = createTextMeasurer(printText);//FIXME use element properties?
182         
183         if (text == null)
184         {
185             text = "";
186         }
187         Locale textLocale = JRStyledTextAttributeSelector.getTextLocale(printText);
188         JRStyledText styledText = 
189             JRStyledTextParser.getInstance().getStyledText(
190                 noBackcolorSelector.getStyledTextAttributes(printText), 
191                 text, 
192                 JRCommonText.MARKUP_STYLED_TEXT.equals(printText.getMarkup()),//FIXMEMARKUP only static styled text appears on preview. no other markup
193                 textLocale
194                 );
195
196         JRStyledText processedStyledText = styledTextUtil.resolveFonts(styledText, textLocale);
197         JRMeasuredText measuredText = textMeasurer.measure(
198                 processedStyledText, 
199                 0,
200                 0,
201                 true,
202                 false
203                 );
204         printText.setTextHeight(measuredText.getTextHeight() < printText.getHeight() ? measuredText.getTextHeight() : printText.getHeight());
205         printText.setLeadingOffset(measuredText.getLeadingOffset());
206         printText.setLineSpacingFactor(measuredText.getLineSpacingFactor());
207         
208         int textEnd = measuredText.getTextOffset();
209         String printedText;
210         if (JRCommonText.MARKUP_STYLED_TEXT.equals(printText.getMarkup()))
211         {
212             printedText = JRStyledTextParser.getInstance().write(styledText, 0, textEnd);
213         }
214         else
215         {
216             printedText = text.substring(0, textEnd);
217         }
218         printText.setText(printedText);
219     }
220 }
221