1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
48
49
50
51 public final class Mfrac extends AbstractJEuclidElement implements
52 MathMLFractionElement {
53
54
55
56
57 public static final String ELEMENT = "mfrac";
58
59
60
61
62 public static final float FRAC_TILT_ANGLE = 0.577f;
63
64
65
66
67 public static final String ATTR_LINETHICKNESS = "linethickness";
68
69
70 public static final String ATTR_BEVELED_WRONG = "beveled";
71
72
73 public static final String ATTR_BEVELLED = "bevelled";
74
75
76 public static final String ATTR_NUMALIGN = "numalign";
77
78
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
87
88 private static final float NOLINE_THRESHHOLD = 0.001f;
89
90
91
92
93
94
95
96
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
111 @Override
112 protected Node newNode() {
113 return new Mfrac(this.nodeName, this.ownerDocument);
114 }
115
116
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
127
128
129
130
131 public void setLinethickness(final String newLinethickness) {
132 this.setAttribute(Mfrac.ATTR_LINETHICKNESS, newLinethickness);
133 }
134
135
136
137
138
139
140
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
163
164
165
166
167 public void setBevelled(final String bevelled) {
168 this.setAttribute(Mfrac.ATTR_BEVELLED, bevelled);
169 }
170
171
172
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
185 public MathMLElement getDenominator() {
186 return this.getMathElement(1);
187 }
188
189
190 public String getLinethickness() {
191 return this.getMathAttribute(Mfrac.ATTR_LINETHICKNESS);
192 }
193
194
195 public MathMLElement getNumerator() {
196 return this.getMathElement(0);
197 }
198
199
200 public void setDenominator(final MathMLElement denominator) {
201 this.setMathElement(1, denominator);
202 }
203
204
205 public void setNumerator(final MathMLElement numerator) {
206 this.setMathElement(0, numerator);
207 }
208
209
210 public String getDenomalign() {
211 return this.getMathAttribute(Mfrac.ATTR_DENOMALIGN);
212 }
213
214
215 public String getNumalign() {
216 return this.getMathAttribute(Mfrac.ATTR_NUMALIGN);
217 }
218
219
220 public void setDenomalign(final String denomalign) {
221 this.setAttribute(Mfrac.ATTR_DENOMALIGN, denomalign);
222 }
223
224
225 public void setNumalign(final String numalign) {
226 this.setAttribute(Mfrac.ATTR_NUMALIGN, numalign);
227 }
228
229
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
265
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
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
300
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
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 }