001    /*
002     * Copyright 2007 - 2007 JEuclid, http://jeuclid.sf.net
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    /* $Id: ScriptSupport.java,v d02878fc1d01 2009/09/08 12:35:02 max $ */
018    
019    package net.sourceforge.jeuclid.elements.presentation.script;
020    
021    import java.awt.geom.Dimension2D;
022    
023    import net.sourceforge.jeuclid.LayoutContext;
024    import net.sourceforge.jeuclid.elements.JEuclidElement;
025    import net.sourceforge.jeuclid.elements.support.Dimension2DImpl;
026    import net.sourceforge.jeuclid.elements.support.ElementListSupport;
027    import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
028    import net.sourceforge.jeuclid.layout.LayoutInfo;
029    import net.sourceforge.jeuclid.layout.LayoutStage;
030    import net.sourceforge.jeuclid.layout.LayoutView;
031    
032    /**
033     * Support class for script elements.
034     * 
035     * @see AbstractSubSuper
036     * @see AbstractUnderOver
037     * @version $Revision: d02878fc1d01 $
038     */
039    public final class ScriptSupport {
040        /** Default Constructor. */
041        private ScriptSupport() {
042            // Empty on purpose.
043        }
044    
045        /**
046         * Info for baseline shifts.
047         * 
048         * @version $Revision: d02878fc1d01 $
049         */
050        static class ShiftInfo {
051            private float superShift;
052    
053            private float subShift;
054    
055            /**
056             * Creates a new ShiftInfo object.
057             * 
058             * @param sub
059             *            subShift.
060             * @param sup
061             *            superShift.
062             */
063            protected ShiftInfo(final float sub, final float sup) {
064                this.superShift = sup;
065                this.subShift = sub;
066            }
067    
068            /**
069             * Getter method for superShift.
070             * 
071             * @return the superShift
072             */
073            public float getSuperShift() {
074                return this.superShift;
075            }
076    
077            /**
078             * Getter method for subShift.
079             * 
080             * @return the subShift
081             */
082            public float getSubShift() {
083                return this.subShift;
084            }
085    
086            /**
087             * Adjust this shift to contain the max shift from current shit and
088             * other info.
089             * 
090             * @param otherInfo
091             *            other info to use.
092             */
093            public void max(final ShiftInfo otherInfo) {
094                this.subShift = Math.max(this.subShift, otherInfo.subShift);
095                this.superShift = Math.max(this.superShift, otherInfo.superShift);
096            }
097    
098        }
099    
100        // CHECKSTYLE:OFF
101        // More than 7 parameters. But only used internally, so that's ok.
102        static void layout(final LayoutView view, final LayoutInfo info,
103                final LayoutStage stage, final LayoutContext now,
104                final JEuclidElement parent, final JEuclidElement base,
105                final JEuclidElement sub, final JEuclidElement sup,
106                final String subScriptShift, final String superScriptShift) {
107            // CHECKSTYLE:ON
108            final LayoutInfo baseInfo = view.getInfo(base);
109            final float width = baseInfo.getWidth(stage);
110    
111            final LayoutInfo subInfo = view.getInfo(sub);
112            final LayoutInfo superInfo = view.getInfo(sup);
113    
114            final ShiftInfo shiftInfo = ScriptSupport.calculateScriptShfits(stage,
115                    now, subScriptShift, superScriptShift, baseInfo, subInfo,
116                    superInfo);
117    
118            if (subInfo != null) {
119                subInfo.moveTo(width, shiftInfo.getSubShift(), stage);
120            }
121            if (superInfo != null) {
122                superInfo.moveTo(width, -shiftInfo.getSuperShift(), stage);
123            }
124    
125            final Dimension2D borderLeftTop = new Dimension2DImpl(0.0f, 0.0f);
126            final Dimension2D borderRightBottom = new Dimension2DImpl(0.0f, 0.0f);
127            ElementListSupport.fillInfoFromChildren(view, info, parent, stage,
128                    borderLeftTop, borderRightBottom);
129            info.setStretchAscent(baseInfo.getStretchAscent());
130            info.setStretchDescent(baseInfo.getStretchDescent());
131        }
132    
133        static ShiftInfo calculateScriptShfits(final LayoutStage stage,
134                final LayoutContext now, final String subScriptShift,
135                final String superScriptShift, final LayoutInfo baseInfo,
136                final LayoutInfo subInfo, final LayoutInfo superInfo) {
137            float subShift = 0.0f;
138            float superShift = 0.0f;
139            if (subInfo != null) {
140                subShift = Math.max(baseInfo.getDescentHeight(stage)
141                        + (subInfo.getAscentHeight(stage) - subInfo
142                                .getDescentHeight(stage)) / 2.0f, AttributesHelper
143                        .convertSizeToPt(subScriptShift, now, AttributesHelper.PT));
144            }
145            if (superInfo != null) {
146                superShift = Math.max(baseInfo.getAscentHeight(stage)
147                        - (superInfo.getAscentHeight(stage) - superInfo
148                                .getDescentHeight(stage)) / 2.0f,
149                        AttributesHelper.convertSizeToPt(superScriptShift, now,
150                                AttributesHelper.PT));
151            }
152            if ((subInfo != null) && (superInfo != null)) {
153                final float topSub = -subShift + subInfo.getAscentHeight(stage)
154                        + 1.0f;
155                final float bottomSuper = superShift
156                        - superInfo.getDescentHeight(stage) - 1.0f;
157    
158                final float overlap = Math.max(0.0f, topSub - bottomSuper);
159                final float overlapShift = overlap / 2.0f;
160    
161                superShift += overlapShift;
162                subShift += overlapShift;
163            }
164            return new ShiftInfo(subShift, superShift);
165        }
166    
167    }