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: * AbstractOutputProcessorMetaData.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.layout.output;
030:
031: import java.util.HashMap;
032: import java.util.HashSet;
033:
034: import org.jfree.fonts.awt.AWTFontRegistry;
035: import org.jfree.fonts.registry.DefaultFontStorage;
036: import org.jfree.fonts.registry.FontContext;
037: import org.jfree.fonts.registry.FontFamily;
038: import org.jfree.fonts.registry.FontMetrics;
039: import org.jfree.fonts.registry.FontRecord;
040: import org.jfree.fonts.registry.FontRegistry;
041: import org.jfree.fonts.registry.FontStorage;
042: import org.jfree.report.layout.text.LegacyFontMetrics;
043: import org.jfree.report.style.StyleSheet;
044: import org.jfree.report.style.TextStyleKeys;
045: import org.jfree.util.Configuration;
046: import org.jfree.util.ExtendedConfiguration;
047: import org.jfree.util.ExtendedConfigurationWrapper;
048: import org.jfree.util.Log;
049:
050: /**
051: * Creation-Date: 07.04.2007, 19:21:44
052: *
053: * @author Thomas Morgner
054: */
055: public abstract class AbstractOutputProcessorMetaData implements
056: OutputProcessorMetaData {
057: protected static class ReusableFontContext implements FontContext {
058: private boolean antiAliased;
059: private double fontSize;
060: private boolean embedded;
061: private String encoding;
062:
063: protected ReusableFontContext() {
064: }
065:
066: public boolean isEmbedded() {
067: return embedded;
068: }
069:
070: public void setEmbedded(final boolean embedded) {
071: this .embedded = embedded;
072: }
073:
074: public String getEncoding() {
075: return encoding;
076: }
077:
078: public void setEncoding(final String encoding) {
079: this .encoding = encoding;
080: }
081:
082: public void setAntiAliased(final boolean antiAliased) {
083: this .antiAliased = antiAliased;
084: }
085:
086: public void setFontSize(final double fontSize) {
087: this .fontSize = fontSize;
088: }
089:
090: /**
091: * This is controlled by the output target and the stylesheet. If the output target does not support aliasing, it
092: * makes no sense to enable it and all such requests are ignored.
093: *
094: * @return
095: */
096: public boolean isAntiAliased() {
097: return antiAliased;
098: }
099:
100: /**
101: * This is defined by the output target. This is not controlled by the stylesheet.
102: *
103: * @return
104: */
105: public boolean isFractionalMetrics() {
106: return true;
107: }
108:
109: /**
110: * The requested font size. A font may have a fractional font size (ie. 8.5 point). The font size may be influenced
111: * by the output target.
112: *
113: * @return the font size.
114: */
115: public double getFontSize() {
116: return fontSize;
117: }
118: }
119:
120: private FontStorage fontStorage;
121: private FontRegistry fontRegistry;
122: private HashMap numericFeatures;
123: private HashMap fontFamilies;
124: private HashSet features;
125: private Configuration configuration;
126: private ReusableFontContext reusableFontContext;
127: private HashMap fontMetricsCache;
128: private FontMetricsKey lookupKey;
129:
130: protected AbstractOutputProcessorMetaData(
131: final Configuration configuration) {
132: this (configuration, new DefaultFontStorage(
133: new AWTFontRegistry()));
134: }
135:
136: protected AbstractOutputProcessorMetaData(
137: final Configuration configuration,
138: final FontStorage fontStorage) {
139: this .configuration = configuration;
140: this .fontRegistry = fontStorage.getFontRegistry();
141: this .fontStorage = fontStorage;
142: this .features = new HashSet();
143: this .numericFeatures = new HashMap();
144: this .reusableFontContext = new ReusableFontContext();
145: this .fontMetricsCache = new HashMap();
146: this .lookupKey = new FontMetricsKey();
147:
148: final ExtendedConfiguration extendedConfig = new ExtendedConfigurationWrapper(
149: configuration);
150:
151: final double defaultFontSize = extendedConfig.getIntProperty(
152: "org.jfree.report.layout.defaults.FontSize", 12);
153:
154: fontFamilies = new HashMap();
155:
156: setNumericFeatureValue(
157: OutputProcessorFeature.DEFAULT_FONT_SIZE,
158: defaultFontSize);
159:
160: final double fontSmoothThreshold = extendedConfig
161: .getIntProperty(
162: "org.jfree.report.layout.defaults.FontSmoothThreshold",
163: 8);
164: setNumericFeatureValue(
165: OutputProcessorFeature.FONT_SMOOTH_THRESHOLD,
166: fontSmoothThreshold);
167:
168: if (extendedConfig
169: .getBoolProperty(
170: "org.jfree.report.layout.fontrenderer.UseMaxCharBounds",
171: true) == false) {
172: addFeature(OutputProcessorFeature.LEGACY_LINEHEIGHT_CALC);
173: }
174: if (extendedConfig.getBoolProperty(
175: "org.jfree.report.FixImageResolutionMapping", true)) {
176: addFeature(OutputProcessorFeature.IMAGE_RESOLUTION_MAPPING);
177: }
178: if (extendedConfig.getBoolProperty(
179: "org.jfree.report.UseNativeScaling", true)) {
180: addFeature(OutputProcessorFeature.PREFER_NATIVE_SCALING);
181: }
182:
183: final double deviceResolution = extendedConfig.getIntProperty(
184: "org.jfree.report.layout.DeviceResolution", 72);
185: if (deviceResolution > 0) {
186: setNumericFeatureValue(
187: OutputProcessorFeature.DEVICE_RESOLUTION,
188: deviceResolution);
189: } else {
190: setNumericFeatureValue(
191: OutputProcessorFeature.DEVICE_RESOLUTION, 72);
192: }
193:
194: setFamilyMapping(null, "SansSerif");
195: }
196:
197: public Configuration getConfiguration() {
198: return configuration;
199: }
200:
201: protected void setFamilyMapping(final String family,
202: final String name) {
203: if (name == null) {
204: throw new NullPointerException();
205: }
206: fontFamilies.put(family, name);
207: }
208:
209: protected void addFeature(
210: final OutputProcessorFeature.BooleanOutputProcessorFeature feature) {
211: if (feature == null) {
212: throw new NullPointerException();
213: }
214: this .features.add(feature);
215: }
216:
217: protected void removeFeature(
218: final OutputProcessorFeature.BooleanOutputProcessorFeature feature) {
219: if (feature == null) {
220: throw new NullPointerException();
221: }
222: this .features.remove(feature);
223: }
224:
225: public boolean isFeatureSupported(
226: final OutputProcessorFeature.BooleanOutputProcessorFeature feature) {
227: if (feature == null) {
228: throw new NullPointerException();
229: }
230: return this .features.contains(feature);
231: }
232:
233: protected void setNumericFeatureValue(
234: final OutputProcessorFeature.NumericOutputProcessorFeature feature,
235: final double value) {
236: if (feature == null) {
237: throw new NullPointerException();
238: }
239: numericFeatures.put(feature, new Double(value));
240: }
241:
242: public double getNumericFeatureValue(
243: final OutputProcessorFeature.NumericOutputProcessorFeature feature) {
244: if (feature == null) {
245: throw new NullPointerException();
246: }
247: final Double d = (Double) numericFeatures.get(feature);
248: if (d == null) {
249: return 0;
250: }
251: return d.doubleValue();
252: }
253:
254: public boolean isContentSupported(final Object content) {
255: return true;
256: }
257:
258: protected FontRegistry getFontRegistry() {
259: return fontRegistry;
260: }
261:
262: protected FontStorage getFontStorage() {
263: return fontStorage;
264: }
265:
266: public String getNormalizedFontFamilyName(final String name) {
267: final String normalizedFontFamily = (String) fontFamilies
268: .get(name);
269: if (normalizedFontFamily == null) {
270: return name;
271: }
272: return normalizedFontFamily;
273: }
274:
275: private static class FontMetricsKey {
276: private String fontFamily;
277: private double fontSize;
278: private boolean antiAliased;
279: private boolean embedded;
280: private String encoding;
281: private boolean italics;
282: private boolean bold;
283:
284: public FontMetricsKey() {
285: }
286:
287: public FontMetricsKey(final FontMetricsKey derived) {
288: this .fontFamily = derived.fontFamily;
289: this .fontSize = derived.fontSize;
290: this .antiAliased = derived.antiAliased;
291: this .embedded = derived.embedded;
292: this .encoding = derived.encoding;
293: this .italics = derived.italics;
294: this .bold = derived.bold;
295: }
296:
297: public String getFontFamily() {
298: return fontFamily;
299: }
300:
301: public void setFontFamily(final String fontFamily) {
302: this .fontFamily = fontFamily;
303: }
304:
305: public double getFontSize() {
306: return fontSize;
307: }
308:
309: public void setFontSize(final double fontSize) {
310: this .fontSize = fontSize;
311: }
312:
313: public boolean isAntiAliased() {
314: return antiAliased;
315: }
316:
317: public void setAntiAliased(final boolean antiAliased) {
318: this .antiAliased = antiAliased;
319: }
320:
321: public boolean isEmbedded() {
322: return embedded;
323: }
324:
325: public void setEmbedded(final boolean embedded) {
326: this .embedded = embedded;
327: }
328:
329: public String getEncoding() {
330: return encoding;
331: }
332:
333: public void setEncoding(final String encoding) {
334: this .encoding = encoding;
335: }
336:
337: public boolean isItalics() {
338: return italics;
339: }
340:
341: public void setItalics(final boolean italics) {
342: this .italics = italics;
343: }
344:
345: public boolean isBold() {
346: return bold;
347: }
348:
349: public void setBold(final boolean bold) {
350: this .bold = bold;
351: }
352:
353: public boolean equals(final Object o) {
354: if (this == o) {
355: return true;
356: }
357: if (o == null || getClass() != o.getClass()) {
358: return false;
359: }
360:
361: final FontMetricsKey that = (FontMetricsKey) o;
362:
363: if (antiAliased != that.antiAliased) {
364: return false;
365: }
366: if (embedded != that.embedded) {
367: return false;
368: }
369: if (bold != that.bold) {
370: return false;
371: }
372: if (italics != that.italics) {
373: return false;
374: }
375: if (Double.doubleToLongBits(that.fontSize) != Double
376: .doubleToLongBits(fontSize)) {
377: return false;
378: }
379: if (encoding != null ? !encoding.equals(that.encoding)
380: : that.encoding != null) {
381: return false;
382: }
383: if (!fontFamily.equals(that.fontFamily)) {
384: return false;
385: }
386:
387: return true;
388: }
389:
390: public int hashCode() {
391: int result = fontFamily.hashCode();
392: final long temp = fontSize != +0.0d ? Double
393: .doubleToLongBits(fontSize) : 0L;
394: result = 29 * result + (int) (temp ^ (temp >>> 32));
395: result = 29 * result + (antiAliased ? 1 : 0);
396: result = 29 * result + (embedded ? 1 : 0);
397: result = 29 * result + (italics ? 1 : 0);
398: result = 29 * result + (bold ? 1 : 0);
399: result = 29 * result
400: + (encoding != null ? encoding.hashCode() : 0);
401: return result;
402: }
403: }
404:
405: /**
406: * Returns the font metrics for the font specified in the style sheet.
407: * <p/>
408: * <B>NOTE: This method will throw an <code>IllegalArgumentException</code> if the specified font family can not be
409: * found and the default font family can not be found</B>
410: *
411: * @param styleSheet ths style sheet from which the font information will be extracted
412: * @return FontMetrics for the specified font. If the font family can not be found, the FontMetrics for the default
413: * font family will be returned
414: * @throws IllegalArgumentException indicated the font metrics could not be determined (this is thrown since methods
415: * depending upon this method can not handle a <code>null</code> return).
416: */
417: public FontMetrics getFontMetrics(final StyleSheet styleSheet)
418: throws IllegalArgumentException {
419: final String fontFamily = getNormalizedFontFamilyName((String) styleSheet
420: .getStyleProperty(TextStyleKeys.FONT));
421: if (fontFamily == null) {
422: // If this case happens, the stylesheet is not implemented correctly. At that point,
423: // we have to assume that the whole engine is no longer behaving valid and therefore we
424: // abort early.
425: final String errorMessage = "No font family specified.";
426: Log.error(errorMessage);
427: throw new IllegalArgumentException(errorMessage);
428: }
429:
430: final double fontSize = styleSheet
431: .getDoubleStyleProperty(
432: TextStyleKeys.FONTSIZE,
433: getNumericFeatureValue(OutputProcessorFeature.DEFAULT_FONT_SIZE));
434:
435: final boolean antiAliasing = RenderUtility.isFontSmooth(
436: styleSheet, this );
437: final String encoding = (String) styleSheet
438: .getStyleProperty(TextStyleKeys.FONTENCODING);
439: final boolean embedded = isFeatureSupported(OutputProcessorFeature.EMBED_ALL_FONTS)
440: || styleSheet
441: .getBooleanStyleProperty(TextStyleKeys.EMBEDDED_FONT);
442: final boolean bold = styleSheet.getBooleanStyleProperty(
443: TextStyleKeys.BOLD, false);
444: final boolean italics = styleSheet.getBooleanStyleProperty(
445: TextStyleKeys.ITALIC, false);
446:
447: lookupKey.setAntiAliased(antiAliasing);
448: lookupKey.setEncoding(encoding);
449: lookupKey.setEmbedded(embedded);
450: lookupKey.setFontFamily(fontFamily);
451: lookupKey.setFontSize(fontSize);
452: lookupKey.setBold(bold);
453: lookupKey.setItalics(italics);
454:
455: final FontMetrics cached = (FontMetrics) fontMetricsCache
456: .get(lookupKey);
457: if (cached != null) {
458: return cached;
459: }
460:
461: final FontRegistry registry = getFontRegistry();
462: FontFamily family = registry.getFontFamily(fontFamily);
463: if (family == null) {
464: Log.warn("Unable to lookup the font family: " + fontFamily);
465:
466: // Get the default font name
467: final String fallBack = getNormalizedFontFamilyName(null);
468: if (fallBack == null) {
469: // If this case happens, the output-processor meta-data does not provide a sensible
470: // fall-back value. As we cannot continue without a font, we fail here instead of
471: // waiting for a NullPointer or other weird error later.
472: final String errorMessage = "No default family defined, aborting.";
473: Log.error(errorMessage);
474: throw new IllegalArgumentException(errorMessage);
475: }
476:
477: family = registry.getFontFamily(fallBack);
478: if (family == null) {
479: // If this case happens, the output-processor meta-data does not provide a sensible
480: // fall-back value. As we cannot continue without a font, we fail here instead of
481: // waiting for a NullPointer or other weird error later.
482: final String errorMessage = "Default family is invalid. Aborting.";
483: Log.error(errorMessage);
484: throw new IllegalArgumentException(errorMessage);
485: }
486: }
487:
488: reusableFontContext.setAntiAliased(antiAliasing);
489: reusableFontContext.setFontSize(fontSize);
490: reusableFontContext.setEncoding(encoding);
491: reusableFontContext.setEmbedded(embedded);
492:
493: final FontRecord record = family.getFontRecord(bold, italics);
494: final FontMetrics fm = getFontStorage().getFontMetrics(
495: record.getIdentifier(), reusableFontContext);
496: if (fm == null) {
497: // If this case happens, then the previous steps of mapping the font name into sensible
498: // defaults failed. The font-system's font-registry is not in sync with the actual font-metrics
499: // provider (which indicates that the LibFonts font-system implementation is invalid).
500: throw new NullPointerException(
501: "FontMetrics returned from factory is null.");
502: }
503:
504: if (isFeatureSupported(OutputProcessorFeature.LEGACY_LINEHEIGHT_CALC)) {
505: // Wrap the font metrics into the legacy-metrics ..
506: final LegacyFontMetrics legacyFontMetrics = new LegacyFontMetrics(
507: fm, fontSize);
508: fontMetricsCache.put(new FontMetricsKey(lookupKey),
509: legacyFontMetrics);
510: return legacyFontMetrics;
511: }
512:
513: fontMetricsCache.put(new FontMetricsKey(lookupKey), fm);
514: return fm;
515: }
516: }
|