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 }