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 }