001    /*
002     * Copyright 2002 - 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: Mfrac.java,v bc1d5fde7b73 2009/06/01 14:40:54 maxberger $ */
018    
019    package net.sourceforge.jeuclid.elements.presentation.general;
020    
021    import java.awt.Color;
022    import java.awt.Graphics2D;
023    import java.awt.geom.Dimension2D;
024    
025    import net.sourceforge.jeuclid.LayoutContext;
026    import net.sourceforge.jeuclid.context.InlineLayoutContext;
027    import net.sourceforge.jeuclid.context.Parameter;
028    import net.sourceforge.jeuclid.elements.AbstractJEuclidElement;
029    import net.sourceforge.jeuclid.elements.support.Dimension2DImpl;
030    import net.sourceforge.jeuclid.elements.support.ElementListSupport;
031    import net.sourceforge.jeuclid.elements.support.GraphicsSupport;
032    import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
033    import net.sourceforge.jeuclid.elements.support.attributes.HAlign;
034    import net.sourceforge.jeuclid.layout.GraphicsObject;
035    import net.sourceforge.jeuclid.layout.LayoutInfo;
036    import net.sourceforge.jeuclid.layout.LayoutStage;
037    import net.sourceforge.jeuclid.layout.LayoutView;
038    import net.sourceforge.jeuclid.layout.LayoutableNode;
039    import net.sourceforge.jeuclid.layout.LineObject;
040    
041    import org.apache.batik.dom.AbstractDocument;
042    import org.w3c.dom.Node;
043    import org.w3c.dom.mathml.MathMLElement;
044    import org.w3c.dom.mathml.MathMLFractionElement;
045    
046    /**
047     * This math element presents a mathematical fraction.
048     * 
049     * @version $Revision: bc1d5fde7b73 $
050     */
051    public final class Mfrac extends AbstractJEuclidElement implements
052            MathMLFractionElement {
053    
054        /**
055         * The XML element from this class.
056         */
057        public static final String ELEMENT = "mfrac";
058    
059        /**
060         * Tilt angle for frac.
061         */
062        public static final float FRAC_TILT_ANGLE = 0.577f;
063    
064        /**
065         * Attribute name of the linethickness property.
066         */
067        public static final String ATTR_LINETHICKNESS = "linethickness";
068    
069        /** The wrong beveled attribute. */
070        public static final String ATTR_BEVELED_WRONG = "beveled";
071    
072        /** The real beveled attribute. */
073        public static final String ATTR_BEVELLED = "bevelled";
074    
075        /** The numalign attribute. */
076        public static final String ATTR_NUMALIGN = "numalign";
077    
078        /** The denomalign attribute. */
079        public static final String ATTR_DENOMALIGN = "denomalign";
080    
081        private static final String EXTRA_SPACE_AROUND = "0.1em";
082    
083        private static final long serialVersionUID = 1L;
084    
085        /**
086         * Lines thinner than this are ignored.
087         */
088        private static final float NOLINE_THRESHHOLD = 0.001f;
089    
090        /**
091         * Default constructor. Sets MathML Namespace.
092         * 
093         * @param qname
094         *            Qualified name.
095         * @param odoc
096         *            Owner Document.
097         */
098        public Mfrac(final String qname, final AbstractDocument odoc) {
099            super(qname, odoc);
100    
101            this.setDefaultMathAttribute(Mfrac.ATTR_LINETHICKNESS, "1");
102            this.setDefaultMathAttribute(Mfrac.ATTR_BEVELLED, Boolean.FALSE
103                    .toString());
104            this.setDefaultMathAttribute(Mfrac.ATTR_NUMALIGN, HAlign.ALIGN_CENTER);
105            this
106                    .setDefaultMathAttribute(Mfrac.ATTR_DENOMALIGN,
107                            HAlign.ALIGN_CENTER);
108        }
109    
110        /** {@inheritDoc} */
111        @Override
112        protected Node newNode() {
113            return new Mfrac(this.nodeName, this.ownerDocument);
114        }
115    
116        /** {@inheritDoc} */
117        @Override
118        public LayoutContext getChildLayoutContext(final int childNum,
119                final LayoutContext context) {
120            return new InlineLayoutContext(this
121                    .applyLocalAttributesToContext(context), !((Boolean) context
122                    .getParameter(Parameter.MFRAC_KEEP_SCRIPTLEVEL)).booleanValue());
123        }
124    
125        /**
126         * Sets the thickness of the fraction line.
127         * 
128         * @param newLinethickness
129         *            Thickness
130         */
131        public void setLinethickness(final String newLinethickness) {
132            this.setAttribute(Mfrac.ATTR_LINETHICKNESS, newLinethickness);
133        }
134    
135        /**
136         * @return thickness of the fraction line
137         * @param context
138         *            LayoutContext to use
139         * @param g
140         *            Graphics2D context to use.
141         */
142        public float getLinethickness(final Graphics2D g,
143                final LayoutContext context) {
144            final String sThickness = this.getLinethickness();
145            float thickness;
146            try {
147                thickness = Float.parseFloat(sThickness);
148                thickness *= GraphicsSupport.lineWidth(context);
149            } catch (final NumberFormatException nfe) {
150                thickness = AttributesHelper.convertSizeToPt(sThickness, this
151                        .applyLocalAttributesToContext(context),
152                        AttributesHelper.PT);
153                if ((thickness < GraphicsSupport.MIN_LINEWIDTH)
154                        && (thickness >= Mfrac.NOLINE_THRESHHOLD)) {
155                    thickness = GraphicsSupport.MIN_LINEWIDTH;
156                }
157            }
158            return thickness;
159        }
160    
161        /**
162         * Set value of the beveled attribute.
163         * 
164         * @param bevelled
165         *            Value
166         */
167        public void setBevelled(final String bevelled) {
168            this.setAttribute(Mfrac.ATTR_BEVELLED, bevelled);
169        }
170    
171        /**
172         * @return Value of beveled attribute
173         */
174        public String getBevelled() {
175            final String wrongAttr = this.getMathAttribute(
176                    Mfrac.ATTR_BEVELED_WRONG, false);
177            if (wrongAttr == null) {
178                return this.getMathAttribute(Mfrac.ATTR_BEVELLED);
179            } else {
180                return wrongAttr;
181            }
182        }
183    
184        /** {@inheritDoc} */
185        public MathMLElement getDenominator() {
186            return this.getMathElement(1);
187        }
188    
189        /** {@inheritDoc} */
190        public String getLinethickness() {
191            return this.getMathAttribute(Mfrac.ATTR_LINETHICKNESS);
192        }
193    
194        /** {@inheritDoc} */
195        public MathMLElement getNumerator() {
196            return this.getMathElement(0);
197        }
198    
199        /** {@inheritDoc} */
200        public void setDenominator(final MathMLElement denominator) {
201            this.setMathElement(1, denominator);
202        }
203    
204        /** {@inheritDoc} */
205        public void setNumerator(final MathMLElement numerator) {
206            this.setMathElement(0, numerator);
207        }
208    
209        /** {@inheritDoc} */
210        public String getDenomalign() {
211            return this.getMathAttribute(Mfrac.ATTR_DENOMALIGN);
212        }
213    
214        /** {@inheritDoc} */
215        public String getNumalign() {
216            return this.getMathAttribute(Mfrac.ATTR_NUMALIGN);
217        }
218    
219        /** {@inheritDoc} */
220        public void setDenomalign(final String denomalign) {
221            this.setAttribute(Mfrac.ATTR_DENOMALIGN, denomalign);
222        }
223    
224        /** {@inheritDoc} */
225        public void setNumalign(final String numalign) {
226            this.setAttribute(Mfrac.ATTR_NUMALIGN, numalign);
227        }
228    
229        /** {@inheritDoc} */
230        @Override
231        protected void layoutStageInvariant(final LayoutView view,
232                final LayoutInfo info, final LayoutStage stage,
233                final LayoutContext context) {
234            final Graphics2D g = view.getGraphics();
235    
236            final float middleShift = this.getMiddleShift(g, context);
237            final boolean beveled = Boolean.parseBoolean(this.getBevelled());
238            final float linethickness = this.getLinethickness(g, context);
239            final float extraSpace = AttributesHelper.convertSizeToPt(
240                    Mfrac.EXTRA_SPACE_AROUND, this
241                            .applyLocalAttributesToContext(context), "");
242    
243            final LayoutInfo numerator = view.getInfo((LayoutableNode) this
244                    .getNumerator());
245            final LayoutInfo denominator = view.getInfo((LayoutableNode) this
246                    .getDenominator());
247    
248            if (beveled) {
249                this.layoutBeveled(info, stage, middleShift, linethickness,
250                        extraSpace, numerator, denominator, context);
251            } else {
252                this.layoutStacked(info, stage, middleShift, linethickness,
253                        extraSpace, numerator, denominator, context);
254            }
255    
256            final Dimension2D borderLeftTop = new Dimension2DImpl(extraSpace
257                    + linethickness, 0.0f);
258            final Dimension2D borderRightBottom = new Dimension2DImpl(extraSpace
259                    + linethickness, 0.0f);
260            ElementListSupport.fillInfoFromChildren(view, info, this, stage,
261                    borderLeftTop, borderRightBottom);
262        }
263    
264        // CHECKSTYLE:OFF
265        // More than 7 parameters - but only used here, so this is ok.
266        private void layoutStacked(final LayoutInfo info, final LayoutStage stage,
267                final float middleShift, final float linethickness,
268                final float extraSpace, final LayoutInfo numerator,
269                final LayoutInfo denominator, final LayoutContext context) {
270            // CHECKSTLYE:ON
271            final float numWidth = numerator.getWidth(stage);
272            final float denumWidth = denominator.getWidth(stage);
273            final float width = Math.max(denumWidth, numWidth);
274    
275            final float numOffset = HAlign.parseString(this.getNumalign(),
276                    HAlign.CENTER).getHAlignOffset(stage, numerator, width);
277            final float denumOffset = HAlign.parseString(this.getDenomalign(),
278                    HAlign.CENTER).getHAlignOffset(stage, denominator, width);
279    
280            numerator
281                    .moveTo(
282                            numOffset + extraSpace,
283                            -(middleShift + linethickness / 2.0f + extraSpace + numerator
284                                    .getDescentHeight(stage)), stage);
285    
286            denominator.moveTo(denumOffset + extraSpace, -middleShift
287                    + linethickness / 2.0f + extraSpace
288                    + denominator.getAscentHeight(stage), stage);
289    
290            if (linethickness > Mfrac.NOLINE_THRESHHOLD) {
291                final GraphicsObject line = new LineObject(extraSpace,
292                        -middleShift, extraSpace + width, -middleShift,
293                        linethickness, (Color) this.applyLocalAttributesToContext(
294                                context).getParameter(Parameter.MATHCOLOR));
295                info.setGraphicsObject(line);
296            }
297        }
298    
299        // CHECKSTYLE:OFF
300        // More than 7 parameters - but only used here, so this is ok.
301        private void layoutBeveled(final LayoutInfo info, final LayoutStage stage,
302                final float middleShift, final float linethickness,
303                final float extraSpace, final LayoutInfo numerator,
304                final LayoutInfo denominator, final LayoutContext context) {
305            // CHECKSTYLE:ON
306            final float numPosY = -middleShift / 2.0f
307                    + numerator.getDescentHeight(stage);
308            final float denPosY = middleShift / 2.0f
309                    + denominator.getDescentHeight(stage);
310    
311            final float totalAscent = Math.max(-numPosY
312                    + numerator.getAscentHeight(stage), -denPosY
313                    + denominator.getAscentHeight(stage));
314            final float totalDescent = Math.max(numPosY
315                    + numerator.getDescentHeight(stage), denPosY
316                    + denominator.getDescentHeight(stage));
317    
318            final float totalHeight = totalAscent + totalDescent;
319            final float lineWidth = totalHeight * Mfrac.FRAC_TILT_ANGLE;
320    
321            numerator.moveTo(extraSpace, numPosY, stage);
322            float posX = numerator.getWidth(stage) + extraSpace;
323            if (linethickness > Mfrac.NOLINE_THRESHHOLD) {
324                final GraphicsObject line = new LineObject(posX, totalDescent,
325                        lineWidth + posX, totalDescent - totalHeight,
326                        linethickness, (Color) this.applyLocalAttributesToContext(
327                                context).getParameter(Parameter.MATHCOLOR));
328                info.setGraphicsObject(line);
329            }
330            posX += lineWidth;
331            denominator.moveTo(posX, denPosY, stage);
332        }
333    }