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: * VerticalAlignmentProcessor.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.layout.process.valign;
030:
031: import org.jfree.report.ElementAlignment;
032: import org.jfree.report.layout.model.RenderBox;
033: import org.jfree.report.layout.model.RenderNode;
034: import org.jfree.report.layout.process.CacheBoxShifter;
035: import org.jfree.report.layout.process.InfiniteMajorAxisLayoutStep;
036: import org.jfree.report.layout.text.ExtendedBaselineInfo;
037: import org.jfree.report.style.VerticalTextAlign;
038:
039: /**
040: * There's only one alignment processor for the vertical layouting. The
041: * processor is non-iterative, it receives a single primary sequence (which
042: * represents a line) and processes that fully.
043: * <p/>
044: * As result, this processor generates a list of offsets and heights; the offset
045: * of the outermost element is always zero and the height is equal to the height
046: * of the whole line.
047: *
048: * @author Thomas Morgner
049: */
050: public class VerticalAlignmentProcessor {
051: // private long lineHeight;
052: private long minTopPos;
053: private long maxBottomPos;
054: private BoxAlignContext rootContext;
055: private long sourcePosition;
056: private InfiniteMajorAxisLayoutStep majorAxisLayoutStep;
057:
058: public VerticalAlignmentProcessor() {
059: }
060:
061: private InfiniteMajorAxisLayoutStep getMajorAxisLayoutStep() {
062: if (majorAxisLayoutStep == null) {
063: majorAxisLayoutStep = new InfiniteMajorAxisLayoutStep();
064: }
065: return majorAxisLayoutStep;
066: }
067:
068: // 7% of our time is spent here ..
069: public void align(final BoxAlignContext alignStructure,
070: final long y1, final long lineHeight) {
071: this .minTopPos = Long.MAX_VALUE;
072: this .maxBottomPos = Long.MIN_VALUE;
073: //this.lineHeight = lineHeight;
074: this .rootContext = alignStructure;
075: this .sourcePosition = y1;
076:
077: performAlignment(alignStructure);
078: performExtendedAlignment(alignStructure, alignStructure);
079: normalizeAlignment(alignStructure);
080:
081: alignStructure.setAfterEdge(Math.max(maxBottomPos, lineHeight));
082: alignStructure.shift(-minTopPos + y1);
083: apply(alignStructure);
084:
085: this .rootContext = null;
086: }
087:
088: private void performAlignment(final BoxAlignContext box) {
089: // We have a valid align structure here.
090: AlignContext child = box.getFirstChild();
091: while (child != null) {
092: if (child instanceof InlineBlockAlignContext) {
093: final InlineBlockAlignContext context = (InlineBlockAlignContext) child;
094: final InfiniteMajorAxisLayoutStep majorAxisLayoutStep = getMajorAxisLayoutStep();
095: majorAxisLayoutStep
096: .continueComputation((RenderBox) context
097: .getNode());
098:
099: // todo: Allow to select other than the first baseline ..
100: }
101:
102: BoxAlignContext parent = box;
103: final VerticalTextAlign verticalAlignment = child.getNode()
104: .getVerticalTextAlignment();
105: if (VerticalTextAlign.TOP.equals(verticalAlignment)
106: || VerticalTextAlign.BOTTOM
107: .equals(verticalAlignment)) {
108: // Those alignments ignore the normal alignment rules and all boxes
109: // align themself on the extended linebox.
110: // I'm quite sure that the definition itself is unclean ..
111: //continue;
112: parent = rootContext;
113: }
114:
115: // Now lets assume we have a valid structure...
116: // All childs have been aligned. Now check how this box is positioned
117: // in relation to its parent.
118: final long shiftDistance = computeShift(child, parent);
119: // The alignment baseline defines to which baseline of the parent we
120: // will align this element
121: final int alignmentBaseline = child.getDominantBaseline();
122:
123: // The alignment adjust defines, where the alignment point of this
124: // child will be. The alignment adjust is relative to the child's
125: // line-height. In the normal case, this will be zero to indicate, that
126: // the alignment point is equal to the child's dominant baseline.
127: final long childAlignmentPoint = computeAlignmentAdjust(
128: child, alignmentBaseline);
129: final long childAscent = child
130: .getBaselineDistance(ExtendedBaselineInfo.BEFORE_EDGE);
131: final long childPosition = (-childAscent + childAlignmentPoint)
132: + child.getBeforeEdge();
133:
134: // If zero, the parent's alignment point is on the parent's dominant
135: // baseline.
136: final long parentAlignmentPoint = parent
137: .getBaselineDistance(alignmentBaseline);
138: final long parentAscent = parent
139: .getBaselineDistance(ExtendedBaselineInfo.BEFORE_EDGE);
140:
141: final long parentPosition = (-parentAscent + parentAlignmentPoint)
142: + parent.getBeforeEdge();
143:
144: final long alignment = parentPosition - childPosition;
145: final long offset = shiftDistance + alignment;
146: child.shift(offset);
147:
148: if (rootContext.getBeforeEdge() > child.getBeforeEdge()) {
149: rootContext.setBeforeEdge(child.getBeforeEdge());
150: }
151:
152: if (rootContext.getAfterEdge() < child.getAfterEdge()) {
153: rootContext.setAfterEdge(child.getAfterEdge());
154: }
155:
156: if (child instanceof BoxAlignContext) {
157: performAlignment((BoxAlignContext) child);
158: }
159:
160: child = child.getNext();
161: }
162: }
163:
164: /**
165: * This simply searches the maximum shift that we have to do to normalize
166: * the element.
167: *
168: * @param box
169: * @return
170: */
171: private void normalizeAlignment(final BoxAlignContext box) {
172: minTopPos = Math.min(minTopPos, box.getBeforeEdge());
173: maxBottomPos = Math.max(maxBottomPos, box.getAfterEdge());
174:
175: AlignContext child = box.getFirstChild();
176: while (child != null) {
177: if (child instanceof BoxAlignContext) {
178: normalizeAlignment((BoxAlignContext) child);
179: }
180: child = child.getNext();
181: }
182: }
183:
184: private long computeShift(final AlignContext child,
185: final BoxAlignContext box) {
186: // for now, we do not perform any advanced layouting. Maybe later ..
187: return 0;
188: }
189:
190: private long computeAlignmentAdjust(final AlignContext context,
191: final int defaultBaseLine) {
192: // for now, we do not perform any advanced layouting. Maybe later ..
193: return context.getBaselineDistance(defaultBaseLine);
194: }
195:
196: private void apply(final BoxAlignContext box) {
197: final RenderNode node = box.getNode();
198: node.setCachedY(box.getBeforeEdge());
199: node.setCachedHeight(box.getAfterEdge() - box.getBeforeEdge());
200:
201: AlignContext child = box.getFirstChild();
202: while (child != null) {
203: if (child instanceof BoxAlignContext) {
204: apply((BoxAlignContext) child);
205: } else if (child instanceof InlineBlockAlignContext) {
206: // Luckily the layoutmodel does not yet specify inline-boxes. Need to be fixed in the flow-engine.
207: // also shift all the childs.
208: final long shift = child.getBeforeEdge()
209: - sourcePosition;
210: CacheBoxShifter.shiftBox(child.getNode(), shift);
211: } else {
212: final RenderNode childNode = child.getNode();
213: childNode.setCachedY(child.getBeforeEdge());
214: childNode.setCachedHeight(child.getAfterEdge()
215: - child.getBeforeEdge());
216: }
217:
218: child = child.getNext();
219: }
220: }
221:
222: // protected static void print (final BoxAlignContext alignContext, final int level)
223: // {
224: // Log.debug ("Box: L:" + level + " Y1:" + alignContext.getBeforeEdge() +
225: // " Y2:" + alignContext.getAfterEdge() +
226: // " H:" + (alignContext.getAfterEdge() - alignContext.getBeforeEdge())
227: // );
228: // // We have a valid align structure here.
229: // AlignContext child = alignContext.getFirstChild();
230: // while (child != null)
231: // {
232: // if (child instanceof BoxAlignContext)
233: // {
234: // print((BoxAlignContext) child, level + 1);
235: // }
236: // else
237: // {
238: // Log.debug ("...: L:" + level + " Y1:" + child.getBeforeEdge() +
239: // " Y2:" + (child.getAfterEdge()) +
240: // " H:" + (child.getAfterEdge() - child.getBeforeEdge()));
241: // }
242: // child = child.getNext();
243: // }
244: // }
245: //
246:
247: /**
248: * Verify all elements with alignment top or bottom. This step is required,
249: * as the extended linebox is allowed to change its height during the
250: * ordinary alignment. Argh, I hate that specificiation.
251: *
252: * @param box
253: */
254: private void performExtendedAlignment(final BoxAlignContext box,
255: final BoxAlignContext lineBox) {
256: // Aligns elements with vertical-align TOP and vertical-align BOTTOM
257: AlignContext child = box.getFirstChild();
258: while (child != null) {
259: final ElementAlignment verticalAlignment = child.getNode()
260: .getNodeLayoutProperties().getVerticalAlignment();
261: if (ElementAlignment.TOP.equals(verticalAlignment)) {
262: final long childTopEdge = child.getBeforeEdge();
263: final long parentTopEdge = lineBox.getBeforeEdge();
264: child.shift(parentTopEdge - childTopEdge);
265: } else if (ElementAlignment.BOTTOM
266: .equals(verticalAlignment)) {
267: // Align the childs after-edge with the parent's after-edge
268: final long childBottomEdge = child.getAfterEdge();
269: final long parentBottomEdge = lineBox.getAfterEdge();
270: child.shift(parentBottomEdge - childBottomEdge);
271: }
272:
273: if (child instanceof BoxAlignContext) {
274: performExtendedAlignment((BoxAlignContext) child,
275: lineBox);
276: }
277:
278: child = child.getNext();
279: }
280: }
281: }
|