001: /**
002: * =========================================================
003: * Pentaho-Reporting-Classic : 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: * CenterAlignmentProcessor.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.layout.process.alignment;
030:
031: import org.jfree.report.layout.model.RenderBox;
032: import org.jfree.report.layout.model.RenderNode;
033: import org.jfree.report.layout.process.layoutrules.EndSequenceElement;
034: import org.jfree.report.layout.process.layoutrules.InlineBoxSequenceElement;
035: import org.jfree.report.layout.process.layoutrules.InlineSequenceElement;
036: import org.jfree.report.layout.process.layoutrules.StartSequenceElement;
037: import org.jfree.report.layout.process.layoutrules.TextSequenceElement;
038:
039: /**
040: * Right alignment strategy. Not working yet, as this is unimplemented right now.
041: *
042: * @author Thomas Morgner
043: */
044: public class CenterAlignmentProcessor extends
045: AbstractAlignmentProcessor {
046: public CenterAlignmentProcessor() {
047: }
048:
049: protected int handleElement(final int start, final int count) {
050: final InlineSequenceElement[] sequenceElements = getSequenceElements();
051: final RenderNode[] nodes = getNodes();
052: final long[] elementDimensions = getElementDimensions();
053: final long[] elementPositions = getElementPositions();
054:
055: // if we reached that method, then this means, that the elements may fit
056: // into the available space. (Assuming that there is no inner pagebreak;
057: // a thing we do not handle yet)
058: final int endIndex = start + count;
059: long usedWidth = 0;
060: long usedWidthToStart = 0;
061: int contentIndex = start;
062: InlineSequenceElement contentElement = null;
063: for (int i = 0; i < endIndex; i++) {
064: final InlineSequenceElement element = sequenceElements[i];
065: final RenderNode node = nodes[i];
066: usedWidth += element.getMaximumWidth(node);
067: if (i < start) {
068: usedWidthToStart += element.getMaximumWidth(node);
069: }
070: if (element instanceof StartSequenceElement
071: || element instanceof EndSequenceElement) {
072: continue;
073: }
074: contentElement = element;
075: contentIndex = i;
076: }
077:
078: final long nextPosition = (getStartOfLine() + usedWidth);
079: final long lastPageBreak = getPageBreak(getPagebreakCount() - 1);
080: if (nextPosition > lastPageBreak) {
081: // The contents we processed so far will not fit on the current line. That's dangerous.
082: // We have to center align the content up to the start position.
083: performCenterAlignment(start, usedWidthToStart,
084: sequenceElements, nodes, elementDimensions,
085: elementPositions);
086:
087: // we cross a pagebreak. Stop working on it - we bail out here.
088:
089: if (contentElement instanceof TextSequenceElement) {
090: // the element may be splittable. Test, and if so, give a hint to the
091: // outside world ..
092: setSkipIndex(endIndex);
093: setBreakableIndex(contentIndex);
094: return (start);
095: }
096:
097: // This is the first element and it still does not fit. How evil.
098: if (start == 0) {
099: if (contentElement instanceof InlineBoxSequenceElement) {
100: final RenderNode node = nodes[contentIndex];
101: if (node instanceof RenderBox) {
102: // OK, limit the size of the box to the maximum line width and
103: // revalidate it.
104: final long contentPosition = elementPositions[contentIndex];
105: final RenderBox box = (RenderBox) node;
106: final long maxWidth = (getEndOfLine() - contentPosition);
107: computeInlineBlock(box, contentPosition,
108: maxWidth);
109:
110: elementDimensions[endIndex - 1] = node
111: .getCachedWidth();
112: }
113: }
114: setSkipIndex(endIndex);
115: }
116: return (start);
117: }
118:
119: // if we reached that method, then this means, that the elements may fit
120: // into the available space. (Assuming that there is no inner pagebreak;
121: // a thing we do not handle yet)
122:
123: if (performCenterAlignment(endIndex, usedWidth,
124: sequenceElements, nodes, elementDimensions,
125: elementPositions)) {
126: return endIndex;
127: }
128: return start;
129: }
130:
131: private boolean performCenterAlignment(final int endIndex,
132: final long usedWidth,
133: final InlineSequenceElement[] sequenceElements,
134: final RenderNode[] nodes, final long[] elementDimensions,
135: final long[] elementPositions) {
136: final long startOfLine = getStartOfLine();
137: final long totalWidth = getEndOfLine() - startOfLine;
138: final long emptySpace = Math.max(0, (totalWidth - usedWidth));
139: long position = startOfLine + emptySpace / 2;
140: // first, make a very simple distribution of the text over all the space, and ignore the pagebreaks
141: for (int i = 0; i < endIndex; i++) {
142: final RenderNode node = nodes[i];
143: final long elementWidth = sequenceElements[i]
144: .getMaximumWidth(node);
145: elementDimensions[i] = elementWidth;
146: elementPositions[i] = position;
147:
148: position += elementWidth;
149: }
150:
151: // If this does not span over multiple pages, we are finished now.
152: // in case the centered text is larger than the available space, we fall back to left-alignment later
153: if (getPagebreakCount() == 1) {
154: return true;
155: }
156:
157: // Now search the element at the center-point.
158: // Find the center-point of the element and the center point (and center element) of the elements.
159: final long centerPoint = startOfLine + totalWidth / 2;
160: final int centerPageSegment = findStartOfPageSegmentForPosition(centerPoint);
161: final int centerPageSegmentNext = Math.min(
162: getPagebreakCount() - 1, centerPageSegment + 1);
163: final long centerPageSegmentStart = getPageBreak(centerPageSegment);
164:
165: int leftShiftEndIndex;
166: int rightShiftStartIndex;
167: if (centerPageSegmentStart == centerPoint) {
168: // case 1: The center point sits directly on a pagebreak. This means, we shift the element touching the center
169: // point to the left; and everything else is shifted to the right.
170: final int centerElement = findElementForPosition(
171: centerPoint, endIndex);
172: final long centerElementPosition = elementPositions[centerElement];
173: final long centerElementEnd = centerElementPosition
174: + elementDimensions[centerElement];
175: if ((centerPoint - centerElementPosition) > (centerElementEnd - centerPoint)) {
176: leftShiftEndIndex = centerElement + 1;
177: rightShiftStartIndex = centerElement + 1;
178: } else {
179: leftShiftEndIndex = centerElement;
180: rightShiftStartIndex = centerElement;
181: }
182: } else {
183: // the end-of-line is always included in the page-break-pos array.
184: final int endOfLineSegment = getPagebreakCount() - 1;
185: final int startOfLineSegment = 0;
186: if (centerPageSegment > startOfLineSegment) {
187: final int leftElement = findElementForPosition(
188: centerPageSegmentStart, endIndex);
189:
190: // We have some elements that need to be shifted to the left.
191: leftShiftEndIndex = leftElement;
192: } else {
193: leftShiftEndIndex = 0;
194: }
195: if (centerPageSegment < endOfLineSegment) {
196: // we also have some elements that need to be shifted to the right.
197: final long centerPageSegmentEnd = getPageBreak(centerPageSegmentNext);
198: final int rightElement = findElementForPosition(
199: centerPageSegmentEnd, endIndex);
200: rightShiftStartIndex = rightElement;
201: } else {
202: rightShiftStartIndex = endIndex;
203: }
204: }
205:
206: // The distance between start of the element and the center point is greater
207: // than the distance between the center point and the end of the element, then shift the center element
208: // to the left. Also shift it to the left, if the element is the only element that should be centered.
209: final long[] savedElementPos = (long[]) elementPositions
210: .clone();
211:
212: // The center-element will be shifted to the right.
213: if (performShiftLeft(leftShiftEndIndex, centerPageSegment,
214: savedElementPos)
215: && performShiftRight(rightShiftStartIndex, endIndex,
216: centerPageSegmentNext, savedElementPos)) {
217: System.arraycopy(savedElementPos, 0, getElementPositions(),
218: 0, savedElementPos.length);
219: return true;
220: }
221: return false;
222: }
223:
224: private boolean performShiftRight(final int firstElementIndex,
225: final int lastElementIndex, int segment,
226: final long[] elementPositions) {
227: if (firstElementIndex >= lastElementIndex) {
228: // nothing to do here ..
229: return true;
230: }
231:
232: final long[] elementDimensions = getElementDimensions();
233: final long endOfLine = getEndOfLine();
234:
235: // We dont need the start of the center-segment, we need the end of it.
236: //int segment = findStartOfPageSegmentForPosition(centerPoint) + 1;
237: final int pagebreakCount = getPagebreakCount();
238: if (segment >= pagebreakCount) {
239: // Indicate that the element will not fit. More correct: the findStart.. method returned the
240: // last segment of the page. There is no space to shift anything to the right ..
241: return false;
242: }
243: long segmentEnd = getPageBreak(segment);
244: long segmentStart = getStartOfSegment(segment);
245:
246: for (int i = firstElementIndex; i < lastElementIndex; i++) {
247: final long elementWidth = elementDimensions[i];
248: long elementEnd = segmentStart + elementWidth;
249: if (elementEnd > endOfLine) {
250: // this element will not fit ..
251: return false;
252: }
253:
254: // make a while a if so that we shift the element only once. This results in a slightly better laoyout
255: if (((segment + 1) < pagebreakCount)
256: && (elementEnd > segmentEnd)) {
257: // as long as there are more segments where we could shift the element to and as long as the
258: // element does not fit into the current segment
259: // try the next segment ..
260: segment += 1;
261: segmentStart = segmentEnd;
262: segmentEnd = getPageBreak(segment);
263: elementEnd = segmentStart + elementWidth;
264: }
265:
266: if (elementEnd > endOfLine) {
267: // the element will not fit into any of the remaining segments. So skip it.
268: return false;
269: }
270:
271: elementPositions[i] = segmentStart;
272: segmentStart = elementEnd;
273: }
274:
275: return true;
276: }
277:
278: private boolean performShiftLeft(final int lastElementIndex,
279: int segment, final long[] elementPositions) {
280: if (lastElementIndex == 0) {
281: // there is nothing to shift here ..
282: return true;
283: }
284:
285: // This code only fires if we distribute an element over more than a single page segment.
286: // The text that is left of the center segment will be shifted to the left and right-aligned there.
287: // The "centerPoint specifies the center of the element and therefore defines which segment is
288: // considered the center-segment.
289:
290: // we will work on a clone, so that the undo is easier ..
291: final long[] elementDimensions = getElementDimensions();
292:
293: final int elementIdx = lastElementIndex - 1;
294:
295: // iterate backwards; start from the center element and right align all previous elements ..
296: final long startOfLine = getStartOfLine();
297: // the current segment.
298:
299: //
300:
301: //int segment = findStartOfPageSegmentForPosition(centerPoint);
302: long segmentEnd = getPageBreak(segment);
303: long segmentStart = getStartOfSegment(segment);
304:
305: for (int i = elementIdx; i >= 0; i--) {
306: final long elementWidth = elementDimensions[i];
307: long elementStart = segmentEnd - elementWidth;
308: if (elementStart < startOfLine) {
309: // this element will not fit. Skip it.
310: return false;
311: }
312:
313: while (segment > 0 && elementStart < segmentStart) {
314: // the element will not fit into the current segment. Move it to the next segment.
315: elementStart = segmentStart - elementWidth;
316: segment -= 1;
317: segmentStart = getStartOfSegment(segment);
318: }
319:
320: if (elementStart < segmentStart) {
321: // the element will not fit into any of the remaining segments. So skip it.
322: return false;
323: }
324:
325: elementPositions[i] = elementStart;
326: segmentEnd = elementStart;
327: }
328:
329: // Commit the changes ..
330: return true;
331: }
332:
333: private long getStartOfSegment(final int segment) {
334: if (segment <= 0) {
335: return getStartOfLine();
336: }
337:
338: return getPageBreak(segment - 1);
339: }
340:
341: /**
342: * Returns the index of the previous pagebreak (the page-boundary that is left to the given position) for the
343: * specified position. This specifies the page-segment in which the position sits.
344: *
345: * @param position the position in micro-points.
346: * @return the number of the page segment.
347: */
348: private int findStartOfPageSegmentForPosition(final long position) {
349: final long[] breaks = getPageBreaks();
350: final int elementSize = getPagebreakCount();
351: final int i = binarySearch(breaks, position, elementSize);
352: if (i > -1) {
353: return i;
354: }
355: if (i == -1) {
356: return 0;
357: }
358:
359: return Math.min(-(i + 2), elementSize - 1);
360: }
361:
362: private int findElementForPosition(final long position,
363: final int endIndex) {
364: final long[] elementPositions = getElementPositions();
365: final int i = binarySearch(elementPositions, position, endIndex);
366: if (i > -1) {
367: return i;
368: }
369: if (i == -1) {
370: return 0;
371: }
372:
373: // if greater than last break, return the last break ..
374: return Math.min(-(i + 2), endIndex - 1);
375: }
376:
377: private static int binarySearch(final long[] array, final long key,
378: final int end) {
379: int low = 0;
380: int high = end - 1;
381:
382: while (low <= high) {
383: final int mid = (low + high) >>> 1;
384: final long midVal = array[mid];
385:
386: if (midVal < key) {
387: low = mid + 1;
388: } else if (midVal > key) {
389: high = mid - 1;
390: } else {
391: return mid; // key found
392: }
393: }
394: return -(low + 1); // key not found.
395: }
396:
397: }
|