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: * RenderableText.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.layout.model;
030:
031: import org.jfree.fonts.text.Spacing;
032: import org.jfree.fonts.text.breaks.BreakOpportunityProducer;
033: import org.jfree.report.layout.text.ExtendedBaselineInfo;
034: import org.jfree.report.layout.text.Glyph;
035: import org.jfree.report.style.StyleSheet;
036:
037: /**
038: * The renderable text is a text chunk, enriched with layouting information,
039: * such as break opportunities, character sizes, kerning information and spacing
040: * information.
041: * <p/>
042: * Text is given as codepoints. Break opportunities are given as integer values,
043: * where zero forbids breaking, and higher values denote better breaks. Spacing
044: * and glyph sizes and kerning is given in micro-points; Spacing is the 'added'
045: * space between codepoints if text-justification is enabled.
046: * <p/>
047: * The text is computed as grapheme clusters; this means that several unicode
048: * codepoints may result in a single /virtual/ glyph/codepoint/character.
049: * (Example: 'A' + accent symbols). If the font supports Lithurges, these
050: * lithurges may also be represented as a single grapheme cluster (and thus
051: * behave unbreakable).
052: * <p/>
053: * Grapheme clusters with more than one unicode char have the size of that char
054: * added to the first codepoint, all subsequence codepoints of the same cluster
055: * have a size/kerning/etc of zero and are unbreakable.
056: * <p/>
057: * This text chunk is perfectly suitable for horizontal text, going either from
058: * left-to-right or right-to-left. (Breaking mixed text is up to the
059: * textfactory).
060: *
061: * @author Thomas Morgner
062: */
063: public class RenderableText extends RenderNode {
064: private Glyph[] glyphs;
065: private int offset;
066: private int length;
067: private boolean ltr;
068: private int script;
069:
070: private long minimumWidth;
071: private long preferredWidth;
072: private boolean forceLinebreak;
073: private ExtendedBaselineInfo baselineInfo;
074: private boolean normalTextSpacing;
075:
076: public RenderableText(final StyleSheet layoutContext,
077: final ExtendedBaselineInfo baselineInfo,
078: final Glyph[] glyphs, final int offset, final int length,
079: final int script, final boolean forceLinebreak) {
080: super (layoutContext);
081: initialize(glyphs, offset, length, baselineInfo, script,
082: forceLinebreak);
083: }
084:
085: protected void initialize(final Glyph[] glyphs, final int offset,
086: final int length, final ExtendedBaselineInfo baselineInfo,
087: final int script, final boolean forceLinebreak) {
088: if (glyphs == null) {
089: throw new NullPointerException();
090: }
091: if (glyphs.length < offset + length) {
092: throw new IllegalArgumentException();
093: }
094:
095: this .baselineInfo = baselineInfo;
096: this .ltr = true; // this depends on the script value
097: this .script = script;
098:
099: this .glyphs = glyphs;
100: this .offset = offset;
101: this .length = length;
102: this .forceLinebreak = forceLinebreak;
103:
104: // Major axis: All child boxes are placed from left-to-right
105: setMajorAxis(HORIZONTAL_AXIS);
106: // Minor: The childs might be aligned on their position (shifted up or down)
107: setMinorAxis(VERTICAL_AXIS);
108:
109: normalTextSpacing = true;
110: long wordMinChunkWidth = 0;
111:
112: // long heightAbove = 0;
113: // long heightBelow = 0;
114: long minimumChunkWidth = 0;
115:
116: long realCharTotal = 0;
117: long spacerMin = 0;
118: long spacerMax = 0;
119: long spacerOpt = 0;
120:
121: final int lastPos = Math.min(glyphs.length, offset + length);
122: for (int i = offset; i < lastPos; i++) {
123: final Glyph glyph = glyphs[i];
124: // heightAbove = Math.max(glyph.getBaseLine(), heightAbove);
125: // heightBelow = Math.max(glyph.getHeight() - glyph.getBaseLine(), heightBelow);
126: final int kerning = glyph.getKerning();
127: final int width = glyph.getWidth();
128: final int realCharSpace = width - kerning;
129: realCharTotal += realCharSpace;
130: if (i == (lastPos - 1)) {
131: wordMinChunkWidth += realCharSpace;
132: } else {
133: final Spacing spacing = glyph.getSpacing();
134: spacerMax += spacing.getMaximum();
135: spacerMin += spacing.getMinimum();
136: spacerOpt += spacing.getOptimum();
137: if (normalTextSpacing == true
138: && Spacing.EMPTY_SPACING.equals(glyph
139: .getSpacing()) == false) {
140: normalTextSpacing = false;
141: }
142:
143: wordMinChunkWidth += spacing.getMinimum()
144: + realCharSpace;
145: }
146:
147: if (glyph.getBreakWeight() > BreakOpportunityProducer.BREAK_CHAR) {
148: minimumChunkWidth = Math.max(minimumChunkWidth,
149: wordMinChunkWidth);
150: wordMinChunkWidth = 0;
151:
152: // Paranoid sanity checks: The word- and linebreaks should have been
153: // replaced by other definitions in the text factory.
154: if (glyph.getBreakWeight() == BreakOpportunityProducer.BREAK_LINE) {
155: throw new IllegalStateException(
156: "A renderable text cannot and must "
157: + "not contain linebreaks.");
158: }
159: }
160: }
161:
162: final long wordMinWidth = spacerMin + realCharTotal;
163: final long wordPrefWidth = spacerOpt + realCharTotal;
164: final long wordMaxWidth = spacerMax + realCharTotal;
165:
166: minimumChunkWidth = Math.max(minimumChunkWidth,
167: wordMinChunkWidth);
168: minimumWidth = wordMinWidth;
169: preferredWidth = wordPrefWidth;
170:
171: setMaximumBoxWidth(wordMaxWidth);
172: setMinimumChunkWidth(minimumChunkWidth);
173: }
174:
175: public boolean isNormalTextSpacing() {
176: return normalTextSpacing;
177: }
178:
179: public boolean isForceLinebreak() {
180: return forceLinebreak;
181: }
182:
183: public boolean isLtr() {
184: return ltr;
185: }
186:
187: public Glyph[] getGlyphs() {
188: return glyphs;
189: }
190:
191: public int getOffset() {
192: return offset;
193: }
194:
195: public int getLength() {
196: return length;
197: }
198:
199: public String getRawText() {
200: final Glyph[] gs = getGlyphs();
201: final int length = getLength();
202: final StringBuffer b = new StringBuffer(length);
203: for (int i = getOffset(); i < length; i++) {
204: final Glyph g = gs[i];
205: b.append((char) (0xffff & g.getCodepoint()));
206: final int[] extraCPs = g.getExtraChars();
207: final int extraCharCount = extraCPs.length;
208: for (int j = 0; j < extraCharCount; j++) {
209: final int extraCP = extraCPs[j];
210: b.append(extraCP);
211: }
212: }
213: return b.toString();
214: }
215:
216: public boolean isEmpty() {
217: return length == 0 && forceLinebreak == false;
218: }
219:
220: public boolean isDiscardable() {
221: if (forceLinebreak) {
222: return false;
223: }
224:
225: return glyphs.length == 0;
226: }
227:
228: /**
229: * Returns the baseline info for the given node. This can be null, if the node
230: * does not have any baseline info.
231: *
232: * @return
233: */
234: public ExtendedBaselineInfo getBaselineInfo() {
235: return baselineInfo;
236: }
237:
238: public int getScript() {
239: return script;
240: }
241:
242: public long getMinimumWidth() {
243: return minimumWidth;
244: }
245:
246: public long getPreferredWidth() {
247: return preferredWidth;
248: }
249: }
|