View Javadoc

1   /*
2    * Copyright 2002 - 2007 JEuclid, http://jeuclid.sf.net
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  /* $Id: Mfrac.java,v bc1d5fde7b73 2009/06/01 14:40:54 maxberger $ */
18  
19  package net.sourceforge.jeuclid.elements.presentation.general;
20  
21  import java.awt.Color;
22  import java.awt.Graphics2D;
23  import java.awt.geom.Dimension2D;
24  
25  import net.sourceforge.jeuclid.LayoutContext;
26  import net.sourceforge.jeuclid.context.InlineLayoutContext;
27  import net.sourceforge.jeuclid.context.Parameter;
28  import net.sourceforge.jeuclid.elements.AbstractJEuclidElement;
29  import net.sourceforge.jeuclid.elements.support.Dimension2DImpl;
30  import net.sourceforge.jeuclid.elements.support.ElementListSupport;
31  import net.sourceforge.jeuclid.elements.support.GraphicsSupport;
32  import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
33  import net.sourceforge.jeuclid.elements.support.attributes.HAlign;
34  import net.sourceforge.jeuclid.layout.GraphicsObject;
35  import net.sourceforge.jeuclid.layout.LayoutInfo;
36  import net.sourceforge.jeuclid.layout.LayoutStage;
37  import net.sourceforge.jeuclid.layout.LayoutView;
38  import net.sourceforge.jeuclid.layout.LayoutableNode;
39  import net.sourceforge.jeuclid.layout.LineObject;
40  
41  import org.apache.batik.dom.AbstractDocument;
42  import org.w3c.dom.Node;
43  import org.w3c.dom.mathml.MathMLElement;
44  import org.w3c.dom.mathml.MathMLFractionElement;
45  
46  /**
47   * This math element presents a mathematical fraction.
48   * 
49   * @version $Revision: bc1d5fde7b73 $
50   */
51  public final class Mfrac extends AbstractJEuclidElement implements
52          MathMLFractionElement {
53  
54      /**
55       * The XML element from this class.
56       */
57      public static final String ELEMENT = "mfrac";
58  
59      /**
60       * Tilt angle for frac.
61       */
62      public static final float FRAC_TILT_ANGLE = 0.577f;
63  
64      /**
65       * Attribute name of the linethickness property.
66       */
67      public static final String ATTR_LINETHICKNESS = "linethickness";
68  
69      /** The wrong beveled attribute. */
70      public static final String ATTR_BEVELED_WRONG = "beveled";
71  
72      /** The real beveled attribute. */
73      public static final String ATTR_BEVELLED = "bevelled";
74  
75      /** The numalign attribute. */
76      public static final String ATTR_NUMALIGN = "numalign";
77  
78      /** The denomalign attribute. */
79      public static final String ATTR_DENOMALIGN = "denomalign";
80  
81      private static final String EXTRA_SPACE_AROUND = "0.1em";
82  
83      private static final long serialVersionUID = 1L;
84  
85      /**
86       * Lines thinner than this are ignored.
87       */
88      private static final float NOLINE_THRESHHOLD = 0.001f;
89  
90      /**
91       * Default constructor. Sets MathML Namespace.
92       * 
93       * @param qname
94       *            Qualified name.
95       * @param odoc
96       *            Owner Document.
97       */
98      public Mfrac(final String qname, final AbstractDocument odoc) {
99          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 }