001: /*
002: * ============================================================================
003: * GNU Lesser General Public License
004: * ============================================================================
005: *
006: * JasperReports - Free Java report-generating library.
007: * Copyright (C) 2001-2006 JasperSoft Corporation http://www.jaspersoft.com
008: *
009: * This library is free software; you can redistribute it and/or
010: * modify it under the terms of the GNU Lesser General Public
011: * License as published by the Free Software Foundation; either
012: * 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,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017: * Lesser General Public License for more details.
018: *
019: * You should have received a copy of the GNU Lesser General Public
020: * License along with this library; if not, write to the Free Software
021: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
022: *
023: * JasperSoft Corporation
024: * 303 Second Street, Suite 450 North
025: * San Francisco, CA 94107
026: * http://www.jaspersoft.com
027: */
028: package net.sf.jasperreports.engine.fill;
029:
030: import java.awt.font.FontRenderContext;
031: import java.awt.font.LineBreakMeasurer;
032: import java.awt.font.TextLayout;
033: import java.text.AttributedCharacterIterator;
034: import java.text.AttributedString;
035: import java.util.StringTokenizer;
036:
037: import net.sf.jasperreports.engine.JRBox;
038: import net.sf.jasperreports.engine.JRTextElement;
039: import net.sf.jasperreports.engine.export.TextRenderer;
040: import net.sf.jasperreports.engine.util.JRStyledText;
041: import net.sf.jasperreports.engine.util.MaxFontSizeFinder;
042:
043: /**
044: * @author Teodor Danciu (teodord@users.sourceforge.net)
045: * @version $Id: TextMeasurer.java 1797 2007-07-30 09:38:35Z teodord $
046: */
047: public class TextMeasurer {
048:
049: /**
050: *
051: */
052: private static final FontRenderContext FONT_RENDER_CONTEXT = TextRenderer.LINE_BREAK_FONT_RENDER_CONTEXT;
053:
054: private int elementWidth;
055: private int elementHeight;
056: private JRBox elementBox;
057: private byte elementRotation;
058: private byte elementLineSpacing;
059: private boolean elementStyledText;
060: private int elementFontSize;
061:
062: /**
063: *
064: */
065: private MaxFontSizeFinder maxFontSizeFinder = null;
066:
067: private int width = 0;
068: private int height = 0;
069: private int topPadding = 0;
070: private int leftPadding = 0;
071: private int bottomPadding = 0;
072: private int rightPadding = 0;
073: private float lineSpacing = 0;
074:
075: private float formatWidth = 0;
076: private int maxHeight = 0;
077: private int textOffset = 0;
078: private int lines = 0;
079: private int fontSizeSum = 0;
080: private int firstLineMaxFontSize = 0;
081: private float textHeight = 0;
082: private float firstLineLeading = 0;
083: private boolean isLeftToRight = true;
084: private boolean isMaxHeightReached = false;
085:
086: /**
087: *
088: */
089: public TextMeasurer(JRTextElement textElement) {
090: this (textElement.getWidth(), textElement.getHeight(),
091: textElement, textElement.getRotation(), textElement
092: .getLineSpacing(), textElement.isStyledText(),
093: textElement.getFontSize());
094: }
095:
096: //FIXME find a better way to do this
097: public TextMeasurer(int width, int height, JRBox box,
098: byte rotation, byte lineSpacing, boolean styledText,
099: int fontSize) {
100: this .elementWidth = width;
101: this .elementHeight = height;
102: this .elementBox = box;
103: this .elementRotation = rotation;
104: this .elementLineSpacing = lineSpacing;
105: this .elementStyledText = styledText;
106: this .elementFontSize = fontSize;
107: }
108:
109: /**
110: *
111: */
112: private void initialize(int availableStretchHeight) {
113: width = elementWidth;
114: height = elementHeight;
115:
116: topPadding = elementBox.getTopPadding();
117: leftPadding = elementBox.getLeftPadding();
118: bottomPadding = elementBox.getBottomPadding();
119: rightPadding = elementBox.getRightPadding();
120:
121: switch (elementRotation) {
122: case JRTextElement.ROTATION_LEFT: {
123: width = elementHeight;
124: height = elementWidth;
125: int tmpPadding = topPadding;
126: topPadding = leftPadding;
127: leftPadding = bottomPadding;
128: bottomPadding = rightPadding;
129: rightPadding = tmpPadding;
130: break;
131: }
132: case JRTextElement.ROTATION_RIGHT: {
133: width = elementHeight;
134: height = elementWidth;
135: int tmpPadding = topPadding;
136: topPadding = rightPadding;
137: rightPadding = bottomPadding;
138: bottomPadding = leftPadding;
139: leftPadding = tmpPadding;
140: break;
141: }
142: case JRTextElement.ROTATION_UPSIDE_DOWN: {
143: int tmpPadding = topPadding;
144: topPadding = bottomPadding;
145: bottomPadding = tmpPadding;
146: tmpPadding = leftPadding;
147: leftPadding = rightPadding;
148: rightPadding = tmpPadding;
149: break;
150: }
151: case JRTextElement.ROTATION_NONE:
152: default: {
153: }
154: }
155:
156: /* */
157: switch (elementLineSpacing) {
158: case JRTextElement.LINE_SPACING_SINGLE: {
159: lineSpacing = 1f;
160: break;
161: }
162: case JRTextElement.LINE_SPACING_1_1_2: {
163: lineSpacing = 1.5f;
164: break;
165: }
166: case JRTextElement.LINE_SPACING_DOUBLE: {
167: lineSpacing = 2f;
168: break;
169: }
170: default: {
171: lineSpacing = 1f;
172: }
173: }
174:
175: maxFontSizeFinder = MaxFontSizeFinder
176: .getInstance(elementStyledText);
177:
178: formatWidth = width - leftPadding - rightPadding;
179: formatWidth = formatWidth < 0 ? 0 : formatWidth;
180: maxHeight = height + availableStretchHeight - topPadding
181: - bottomPadding;
182: maxHeight = maxHeight < 0 ? 0 : maxHeight;
183: textOffset = 0;
184: lines = 0;
185: fontSizeSum = 0;
186: firstLineMaxFontSize = 0;
187: textHeight = 0;
188: firstLineLeading = 0;
189: isLeftToRight = true;
190: isMaxHeightReached = false;
191: }
192:
193: /**
194: *
195: */
196: public void measure(JRStyledText styledText, String remainingText,
197: int remainingTextStart, int availableStretchHeight) {
198: /* */
199: initialize(availableStretchHeight);
200:
201: AttributedCharacterIterator allParagraphs = styledText
202: .getAttributedString().getIterator();
203:
204: int tokenPosition = remainingTextStart;
205: int lastParagraphStart = remainingTextStart;
206: String lastParagraphText = null;
207:
208: StringTokenizer tkzer = new StringTokenizer(remainingText,
209: "\n", true);
210:
211: while (tkzer.hasMoreTokens() && !isMaxHeightReached) {
212: String token = tkzer.nextToken();
213:
214: if ("\n".equals(token)) {
215: renderParagraph(allParagraphs, lastParagraphStart,
216: lastParagraphText);
217:
218: lastParagraphStart = tokenPosition
219: + (tkzer.hasMoreTokens() || tokenPosition == 0 ? 1
220: : 0);
221: lastParagraphText = null;
222: } else {
223: lastParagraphStart = tokenPosition;
224: lastParagraphText = token;
225: }
226:
227: tokenPosition += token.length();
228: }
229:
230: if (!isMaxHeightReached
231: && lastParagraphStart < remainingTextStart
232: + remainingText.length()) {
233: renderParagraph(allParagraphs, lastParagraphStart,
234: lastParagraphText);
235: }
236: }
237:
238: /**
239: *
240: */
241: private void renderParagraph(
242: AttributedCharacterIterator allParagraphs,
243: int lastParagraphStart, String lastParagraphText) {
244: AttributedCharacterIterator paragraph = null;
245:
246: if (lastParagraphText == null) {
247: paragraph = new AttributedString(" ", new AttributedString(
248: allParagraphs, lastParagraphStart,
249: lastParagraphStart + 1).getIterator()
250: .getAttributes()).getIterator();
251: } else {
252: paragraph = new AttributedString(allParagraphs,
253: lastParagraphStart, lastParagraphStart
254: + lastParagraphText.length()).getIterator();
255: }
256:
257: int positionWithinParagraph = 0;
258:
259: LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(
260: paragraph, FONT_RENDER_CONTEXT);
261:
262: while (lineMeasurer.getPosition() < paragraph.getEndIndex()
263: && !isMaxHeightReached) {
264: int lineStartPosition = lineMeasurer.getPosition();
265:
266: TextLayout layout = lineMeasurer.nextLayout(formatWidth);
267:
268: isLeftToRight = isLeftToRight && layout.isLeftToRight();
269:
270: textHeight += layout.getLeading() + lineSpacing
271: * layout.getAscent();
272:
273: if (textHeight + layout.getDescent() <= maxHeight) {
274: lines++;
275:
276: fontSizeSum += maxFontSizeFinder.findMaxFontSize(
277: new AttributedString(paragraph,
278: lineStartPosition, lineStartPosition
279: + layout.getCharacterCount())
280: .getIterator(), elementFontSize);
281:
282: if (lines == 1) {
283: firstLineLeading = textHeight;
284: firstLineMaxFontSize = fontSizeSum;
285: }
286:
287: positionWithinParagraph = lineMeasurer.getPosition();
288: // here is the Y offset where we would draw the line
289: //lastDrawPosY = drawPosY;
290: //
291: textHeight += layout.getDescent();
292: } else {
293: textHeight -= layout.getLeading() + lineSpacing
294: * layout.getAscent();
295: isMaxHeightReached = true;
296: }
297: }
298:
299: textOffset = lastParagraphStart + positionWithinParagraph;//(lastParagraphText == null ? 0 : positionWithinParagraph);
300: }
301:
302: /**
303: *
304: */
305: protected boolean isLeftToRight() {
306: return isLeftToRight;
307: }
308:
309: /**
310: *
311: */
312: protected int getTextOffset() {
313: return textOffset;
314: }
315:
316: /**
317: *
318: */
319: public float getTextHeight() {
320: return textHeight;
321: }
322:
323: /**
324: *
325: */
326: public float getLineSpacingFactor() {
327: if (lines > 0) {
328: return textHeight / fontSizeSum;
329: }
330: return 0;
331: }
332:
333: /**
334: *
335: */
336: public float getLeadingOffset() {
337: return firstLineLeading - firstLineMaxFontSize
338: * getLineSpacingFactor();
339: }
340:
341: }
|