001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * DefaultSizeCalculator.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.layout;
030:
031: import java.awt.Font;
032: import java.awt.font.FontRenderContext;
033: import java.awt.geom.Rectangle2D;
034:
035: import org.jfree.report.JFreeReportBoot;
036: import org.jfree.report.JFreeReportCoreModule;
037: import org.jfree.report.style.FontDefinition;
038: import org.jfree.util.ExtendedConfiguration;
039: import org.jfree.util.Log;
040:
041: /**
042: * An AWT-Based default implementation of an SizeCalculator. This implementation tries to
043: * detect the currently used FontRendererContext; some JDKs are unable to return
044: * reasonable sizes for the given text.
045: *
046: * @author Thomas Morgner
047: * @see org.jfree.report.layout.DefaultSizeCalculator
048: */
049: public strictfp class DefaultSizeCalculator implements SizeCalculator {
050: private static final boolean VERBOSE_LOGGING = false;
051:
052: /**
053: * A helper class that is able to detect whether the implementation is considered buggy.
054: * A non-buggy implementation should show no differences between aliased-versions of
055: * Graphics2D and non-aliased versions.
056: * <p/>
057: * On JDK 1.4 the font renderer changed. In previous versions, the font renderer was
058: * sensitive to fractional metrics, so that fonts were always rendered without
059: * FractionalMetrics enabled. Since 1.4, fonts are always rendered with
060: * FractionalMetrics disabled.
061: * <p/>
062: * Obviously this is annoying if you try to write a layouter for all JDKs :(
063: */
064: public static class BuggyFontRendererDetector {
065: /**
066: * a flag that indicates whether the FontRenderContext implementation is buggy.
067: */
068: private boolean isBuggyVersion;
069:
070: /**
071: * a flag that checks whether aliasing is used to draw the contents on Graphics
072: * objects.
073: */
074: private final boolean isAliased;
075:
076: /**
077: * Cache the created FontRenderContext. FRC is read only.
078: */
079: private FontRenderContext fontRenderContext;
080:
081: /**
082: * creates a new BuggyFontRendererDetector.
083: */
084: protected BuggyFontRendererDetector() {
085: final ExtendedConfiguration extConfiguration = JFreeReportBoot
086: .getInstance().getExtendedConfig();
087: isAliased = extConfiguration
088: .getBoolProperty(JFreeReportCoreModule.FONTRENDERER_USEALIASING_KEY);
089:
090: // Another funny thing for the docs: On JDK 1.4 the font renderer changed.
091: // in previous versions, the font renderer was sensitive to fractional metrics,
092: // so that fonts were always rendered with FractionalMetrics enabled.
093: // Since 1.4, fonts are always rendered with FractionalMetrics disabled.
094:
095: // On a 1.4 version, the aliasing has no influence on non-fractional metrics
096: // aliasing has no influence on any version if fractional metrics are enabled.
097: final FontRenderContext frcAlias = new FontRenderContext(
098: null, true, false);
099: final FontRenderContext frcNoAlias = new FontRenderContext(
100: null, false, false);
101: final Font font = new Font("Serif", Font.PLAIN, 10);
102: final String myText = "A simple text with some characters to calculate the length.";
103:
104: final double wAlias = font.getStringBounds(myText, 0,
105: myText.length(), frcAlias).getWidth();
106: final double wNoAlias = font.getStringBounds(myText, 0,
107: myText.length(), frcNoAlias).getWidth();
108: isBuggyVersion = (wAlias != wNoAlias);
109: final boolean buggyOverride = extConfiguration
110: .getBoolProperty(JFreeReportCoreModule.FONTRENDERER_ISBUGGY_FRC_KEY);
111:
112: if (VERBOSE_LOGGING) {
113: Log
114: .debug("This is a buggy version of the font-renderer context: "
115: + isBuggyVersion);
116: Log
117: .debug("The buggy-value is defined in the configuration : "
118: + buggyOverride);
119: if (isBuggyVersion) {
120: if (isAliased()) {
121: Log
122: .debug("The G2OutputTarget uses Antialiasing. \n"
123: + "The FontRendererBugs should not be visible in TextAntiAliasing-Mode.\n"
124: + "If there are problems with the string-placement, please report your \n"
125: + "Operating System version and your JDK Version to "
126: + "www.object-refinery.com/jfreereport.\n");
127: } else {
128: Log
129: .debug("The G2OutputTarget does not use Antialiasing. \n"
130: + "Your FontRenderer is buggy (text is not displayed correctly by "
131: + "default).\n"
132: + "The system was able to detect this and tries to correct that bug. \n"
133: + "If your strings are not displayed correctly, report your Operating System "
134: + "version and your \n"
135: + "JDK Version to www.object-refinery.com/jfreereport\n");
136: }
137: } else {
138: Log
139: .debug("Your FontRenderer seems to be ok, our tests didn't produce buggy results. \n"
140: + "If your strings are not displayed correctly, try to enable the "
141: + "configuration key \n"
142: + "\"org.jfree.report.targets.G2OutputTarget.isBuggyFRC=true\"\n"
143: + "in the file 'jfreereport.properties' or set this property as "
144: + "System-property. \n"
145: + "If the bug still remains alive, please report your Operating System version "
146: + "and your \nJDK Version to www.object-refinery.com/jfreereport.\n");
147: }
148: Log
149: .debug("If text layouting is working as expected, no further action is required.");
150: }
151:
152: if (buggyOverride == true) {
153: isBuggyVersion = true;
154: }
155: }
156:
157: /**
158: * creates a new FontRenderContext suitable to calculate a string size, independend
159: * from the AWT-bug.
160: *
161: * @return a font render context that is valid and not affected by the bugs.
162: */
163: protected FontRenderContext createFontRenderContext() {
164: if (fontRenderContext == null) {
165: if (isAliased()) {
166: fontRenderContext = new FontRenderContext(null,
167: isAliased(), true);
168: } else {
169: // buggy is only important on non-aliased environments ...
170: // dont use fractional metrics on buggy versions
171:
172: // use int_metrics wenn buggy ...
173: fontRenderContext = new FontRenderContext(null,
174: isAliased(), isBuggyVersion() == false);
175: }
176: }
177: return fontRenderContext;
178: }
179:
180: /**
181: * Gets the defined aliasing state for the FontRenderContext and the target
182: * Graphics2D.
183: *
184: * @return the aliasing state.
185: */
186: public boolean isAliased() {
187: return isAliased;
188: }
189:
190: /**
191: * Gets the buggy state of the AWT implementation.
192: *
193: * @return true, if the AWT implementation is buggy and not able to perform accurate
194: * font rendering.
195: */
196: public boolean isBuggyVersion() {
197: return isBuggyVersion;
198: }
199: }
200:
201: /**
202: * the FontRenderContext bug detector instance.
203: */
204: private static BuggyFontRendererDetector frcDetector;
205: private float lineHeight;
206:
207: /**
208: * Returns a singleon instance of the FontRenderContext bug detector.
209: *
210: * @return the FontRenderContext-detector
211: */
212: public static BuggyFontRendererDetector getFrcDetector() {
213: if (frcDetector == null) {
214: frcDetector = new BuggyFontRendererDetector();
215: }
216: return frcDetector;
217: }
218:
219: /**
220: * The font.
221: */
222: private Font font;
223:
224: private char[] chars;
225:
226: /**
227: * Creates a new size calculator.
228: *
229: * @param font The font definition.
230: * @param maxLineHeightUsed a flag indicating whether the maximum bounding box is used.
231: * @return A default size calculator.
232: * @deprecated Do not use the FontDefinition, use the Font-constructor instead and instantiate the
233: * size-calculator directly.
234: */
235: public static synchronized DefaultSizeCalculator getDefaultSizeCalculator(
236: final FontDefinition font, final boolean maxLineHeightUsed) {
237: return new DefaultSizeCalculator(font.getFont(),
238: maxLineHeightUsed);
239: }
240:
241: /**
242: * Creates a new size calculator.
243: *
244: * @param font the font
245: * @param maxLineHeightUsed a flag indicating whether the maximum bounding box is used.
246: * @deprecated Do not use the FontDefinition, use the Font-constructor instead.
247: */
248: public DefaultSizeCalculator(final FontDefinition font,
249: final boolean maxLineHeightUsed) {
250: this (font.getFont(), maxLineHeightUsed);
251: }
252:
253: /**
254: * Creates a new size calculator.
255: *
256: * @param font the font.
257: * @param maxLineHeightUsed a flag indicating whether the maximum bounding box is used.
258: */
259: public DefaultSizeCalculator(final Font font,
260: final boolean maxLineHeightUsed) {
261: if (font == null) {
262: throw new NullPointerException(
263: "Given FontDefinition is null");
264: }
265: if (font.getSize2D() <= 0) {
266: throw new IllegalArgumentException(
267: "The given FontSize is <= 0");
268: }
269:
270: if (maxLineHeightUsed) {
271: final Rectangle2D rect = font
272: .getMaxCharBounds(getFrcDetector()
273: .createFontRenderContext());
274: this .lineHeight = (float) rect.getHeight();
275: } else {
276: this .lineHeight = font.getSize2D();
277: }
278: // Log.debug ("FontSize: " + rect + " -> " + font.getFont().getSize2D() + " vs " + lineHeight + " -> " + font.getFontName());
279: this .font = font;
280: this .chars = new char[100];
281: }
282:
283: /**
284: * Returns the height of the current font. The font height specifies the distance
285: * between 2 base lines.
286: *
287: * @return the font height.
288: */
289: public float getLineHeight() {
290: return lineHeight;
291: }
292:
293: /**
294: * Calculates the width of the specified String in the current Graphics context.
295: *
296: * @param text the text to be weighted.
297: * @param lineStartPos the start position of the substring to be weighted.
298: * @param endPos the position of the last characterto be included in the
299: * weightening process.
300: * @return the width of the given string in 1/72" dpi.
301: */
302: public float getStringWidth(final String text,
303: final int lineStartPos, final int endPos) {
304: if (lineStartPos < 0) {
305: throw new IllegalArgumentException();
306: }
307: if (lineStartPos > endPos) {
308: throw new IllegalArgumentException("LineStart on: "
309: + lineStartPos + " End on " + endPos);
310: }
311:
312: if (lineStartPos == endPos) {
313: return 0;
314: }
315:
316: final FontRenderContext frc = getFrcDetector()
317: .createFontRenderContext();
318:
319: if (chars.length < text.length()) {
320: chars = new char[Math
321: .max(chars.length + 100, text.length())];
322: }
323:
324: text.getChars(lineStartPos, endPos, chars, 0);
325: final Rectangle2D textBounds2 = font.getStringBounds(chars, 0,
326: endPos - lineStartPos, frc);
327: return (float) textBounds2.getWidth();
328: }
329:
330: /**
331: * Converts this object to a string.
332: *
333: * @return a string.
334: */
335: public String toString() {
336: return "DefaultSizeCalculator={font=" + font + '}';
337: }
338: }
|