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: AbstractJEuclidElement.java 446 2007-08-22 21:40:13Z maxberger $ */
18  
19  package net.sourceforge.jeuclid.elements;
20  
21  import java.awt.Color;
22  import java.awt.Font;
23  import java.awt.FontMetrics;
24  import java.awt.Graphics2D;
25  import java.awt.geom.Line2D;
26  import java.awt.geom.Rectangle2D;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import net.sourceforge.jeuclid.MathBase;
33  import net.sourceforge.jeuclid.ParameterKey;
34  import net.sourceforge.jeuclid.dom.AbstractChangeTrackingElement;
35  import net.sourceforge.jeuclid.dom.PartialTextImpl;
36  import net.sourceforge.jeuclid.elements.generic.MathImpl;
37  import net.sourceforge.jeuclid.elements.presentation.general.Mrow;
38  import net.sourceforge.jeuclid.elements.presentation.table.Mtable;
39  import net.sourceforge.jeuclid.elements.presentation.table.Mtr;
40  import net.sourceforge.jeuclid.elements.presentation.token.Mo;
41  import net.sourceforge.jeuclid.elements.presentation.token.Mtext;
42  import net.sourceforge.jeuclid.elements.support.attributes.AttributeMap;
43  import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
44  import net.sourceforge.jeuclid.elements.support.attributes.MathVariant;
45  import net.sourceforge.jeuclid.elements.support.text.CharConverter;
46  
47  import org.apache.commons.logging.Log;
48  import org.apache.commons.logging.LogFactory;
49  import org.w3c.dom.Node;
50  import org.w3c.dom.Text;
51  import org.w3c.dom.mathml.MathMLElement;
52  import org.w3c.dom.mathml.MathMLMathElement;
53  import org.w3c.dom.mathml.MathMLNodeList;
54  
55  /**
56   * The basic class for all math elements. Every element class inherits from
57   * this class. It provides basic functionality for drawing.
58   * 
59   * @author Unknown
60   * @author Max Berger
61   * @version $Revision: 446 $
62   */
63  // CHECKSTYLE:OFF
64  public abstract class AbstractJEuclidElement extends
65  // CHECKSTYLE:ON
66          AbstractChangeTrackingElement implements JEuclidElement {
67  
68      /** Constant for mathvariant attribute. */
69      public static final String ATTR_MATHVARIANT = "mathvariant";
70  
71      /** Constant for mathcolor attribute. */
72      public static final String ATTR_MATHCOLOR = "mathcolor";
73  
74      /** Constant for mathsize attribute. */
75      public static final String ATTR_MATHSIZE = "mathsize";
76  
77      /** Constant for fontfamily attribute. */
78      public static final String ATTR_DEPRECATED_FONTFAMILY = "fontfamily";
79  
80      /** Constant for fontstyle attribute. */
81      public static final String ATTR_DEPRECATED_FONTSTYLE = "fontstyle";
82  
83      /** Constant for fontweight attribute. */
84      public static final String ATTR_DEPRECATED_FONTWEIGHT = "fontweight";
85  
86      /** Constant for fontsize attribute. */
87      public static final String ATTR_DEPRECATED_FONTSIZE = "fontsize";
88  
89      /** Constant for color attribute. */
90      public static final String ATTR_DEPRECATED_COLOR = "color";
91  
92      /** Constant for background attribute. */
93      public static final String ATTR_DEPRECATED_BACKGROUND = "background";
94  
95      /** Constant for class attribute. */
96      public static final String ATTR_CLASS = "class";
97  
98      /** Constant for style attribute. */
99      public static final String ATTR_STYLE = "style";
100 
101     /** Constant for id attribute. */
102     public static final String ATTR_ID = "id";
103 
104     /** Constant for href attribute. */
105     public static final String ATTR_HREF = "xlink:href";
106 
107     /** Constant for xref attribute. */
108     public static final String ATTR_XREF = "xref";
109 
110     /** The mathbackground attribute. */
111     public static final String ATTR_MATHBACKGROUND = "mathbackground";
112 
113     /** value for top alignment. */
114     public static final String ALIGN_TOP = "top";
115 
116     /** value for bottom alignment. */
117     public static final String ALIGN_BOTTOM = "bottom";
118 
119     /** value for center alignment. */
120     public static final String ALIGN_CENTER = "center";
121 
122     /** value for baseline alignment. */
123     public static final String ALIGN_BASELINE = "baseline";
124 
125     /** value for axis alignment. */
126     public static final String ALIGN_AXIS = "axis";
127 
128     /** value for left alignment. */
129     public static final String ALIGN_LEFT = "left";
130 
131     /** value for right alignment. */
132     public static final String ALIGN_RIGHT = "right";
133 
134     /**
135      * largest value for all trivial spaces (= spaces that can be ignored /
136      * shortened).
137      */
138     public static final int TRIVIAL_SPACE_MAX = 0x20;
139 
140     /**
141      * The URI from MathML.
142      */
143     public static final String URI = "http://www.w3.org/1998/Math/MathML";
144 
145     private static final float MIDDLE_SHIFT = 0.38f;
146 
147     private static final float DEFAULT_SCIPTSIZEMULTIPLIER = 0.71f;
148 
149     /**
150      * Logger for this class
151      */
152     private static final Log LOGGER = LogFactory
153             .getLog(AbstractJEuclidElement.class);
154 
155     private static final Set<String> DEPRECATED_ATTRIBUTES = new HashSet<String>();
156 
157     /**
158      * Reference to the MathBase object, which controls all font and metrics
159      * computing.
160      */
161     private MathBase mbase;
162 
163     /**
164      * flag - true when runing calculationg of the element.
165      */
166     private boolean calculatingSize;
167 
168     /**
169      * Value of calculated height of the element .
170      */
171     private float calculatedHeight = -1;
172 
173     /**
174      * Value of calculated width of the element .
175      */
176     private float calculatedWidth = -1;
177 
178     /**
179      * Value of calculated ascent height of the element
180      */
181     private float calculatedAscentHeight = -1;
182 
183     /**
184      * Value of calculated descent height of the element
185      */
186     private float calculatedDescentHeight = -1;
187 
188     /**
189      * Value of calculated height of the element
190      */
191     private float calculatedStretchHeight = -1;
192 
193     /**
194      * Value of calculated ascent height of the element
195      */
196     private float calculatedStretchAscentHeight = -1;
197 
198     /**
199      * Value of calculated descent height of the element .
200      */
201     private float calculatedStretchDescentHeight = -1;
202 
203     private float lastPaintedX = -1;
204 
205     private float lastPaintedY = -1;
206 
207     /**
208      * Reference to the element acting as parent if there is no parent.
209      */
210     private JEuclidElement fakeParent;
211 
212     private final Map<String, String> defaultMathAttributes = new HashMap<String, String>();
213 
214     /**
215      * Variable of "scriptsize" attribute, default value is 0.71.
216      */
217     private float mscriptsizemultiplier = AbstractJEuclidElement.DEFAULT_SCIPTSIZEMULTIPLIER;
218 
219     /**
220      * This variable is intended to keep the value of vertical shift of the
221      * line. Actually this value is stored in the top-level element of the
222      * line. This value affects only elements with enlarged parts (such as
223      * "msubsup", "munderover", etc.)
224      */
225     private float globalLineCorrecter;
226 
227     /**
228      * Creates a math element.
229      * 
230      * @param base
231      *            The base for the math element tree.
232      */
233 
234     public AbstractJEuclidElement(final MathBase base) {
235         this.setMathBase(base);
236     }
237 
238     /**
239      * Reset element sizes.
240      */
241     protected void recalculateSize() {
242         this.calculatedHeight = -1;
243         this.calculatedAscentHeight = -1;
244         this.calculatedDescentHeight = -1;
245         this.calculatedStretchHeight = -1;
246         this.calculatedStretchAscentHeight = -1;
247         this.calculatedStretchDescentHeight = -1;
248         this.calculatedWidth = -1;
249     }
250 
251     /** {@inheritDoc} */
252     @Override
253     protected void changeHook() {
254         super.changeHook();
255         this.recalculateSize();
256     }
257 
258     /**
259      * Gets the size of the actual font used (including scriptsizemultiplier).
260      * 
261      * @return size of the current font.
262      */
263     public float getFontsizeInPoint() {
264         final float scriptMultiplier = (float) Math.pow(this
265                 .getScriptSizeMultiplier(), this.getAbsoluteScriptLevel());
266         float size = this.getMathsizeInPoint() * scriptMultiplier;
267 
268         // This results in a size 8 for a default size of 12.
269         // TODO: This should use scriptminsize (3.3.4.2)
270         final float minSize = this.mbase.getFontSize() * 0.66f;
271         if (size < minSize) {
272             size = minSize;
273         }
274         return size;
275     }
276 
277     /**
278      * Gets the used font. Everything regardes font, processed by MathBase
279      * object.
280      * 
281      * @return Font Font object.
282      */
283     public Font getFont() {
284         final String content = this.getText();
285         final char aChar;
286         if (content.length() > 0) {
287             aChar = content.charAt(0);
288         } else {
289             aChar = 'A';
290         }
291         return this.getMathvariantAsVariant().createFont(
292                 this.getFontsizeInPoint(), aChar, this.mbase);
293 
294     }
295 
296     /** {@inheritDoc} */
297     public MathVariant getMathvariantAsVariant() {
298         final String mv = this.getMathvariant();
299         MathVariant variant = null;
300         if (mv != null) {
301             variant = MathVariant.stringToMathVariant(mv);
302         }
303         if (variant == null) {
304             // TODO: Not all elements inherit MathVariant!
305             final JEuclidElement parent = this.getParent();
306             if (parent != null) {
307                 variant = parent.getMathvariantAsVariant();
308             } else {
309                 // TODO: This is NOT ALWAYS the default variant
310                 variant = MathVariant.NORMAL;
311             }
312         }
313         return variant;
314     }
315 
316     /**
317      * Retrieve the absolute script level. For most items this will ask the
318      * parent item.
319      * 
320      * @return the absolute script level.
321      * @see #getInheritedScriptlevel()
322      */
323     protected int getAbsoluteScriptLevel() {
324         return this.getInheritedScriptlevel();
325     }
326 
327     /**
328      * Retrieves the scriptlevel from the parent node.
329      * 
330      * @return the scriptlevel of the parent node
331      */
332     protected int getInheritedScriptlevel() {
333         final JEuclidElement parent = this.getParent();
334         if (parent == null) {
335             return 0;
336         } else {
337             return parent.getScriptlevelForChild(this);
338         }
339     }
340 
341     /** {@inheritDoc} */
342     public int getScriptlevelForChild(final JEuclidElement child) {
343         return this.getAbsoluteScriptLevel();
344     }
345 
346     // /**
347     // * Setting size of child of the element.
348     // *
349     // * @param child
350     // * Child element
351     // * @param childpos
352     // * Position of the child element
353     // */
354     //
355     // private void setChildSize(AbstractMathElement child, int childpos) {
356     //
357     // float childSize = (float) Math.pow(getScriptSizeMultiplier(), child
358     // .getAbsoluteScriptLevel());
359     //
360     // child.multipleFont(m_font, (float) Math.pow(getScriptSizeMultiplier(),
361     // child.getAbsoluteScriptLevel()));
362     // System.out.println(child.toString() + " "
363     // + child.getAbsoluteScriptLevel() + " "
364     // + getScriptSizeMultiplier() + " " + childSize);
365     //
366     // child.setScriptSizeMultiplier(getScriptSizeMultiplier());
367     //
368     // if (this instanceof MathMultiScripts) {
369     // if (childpos > 0) {
370     // child.multipleFont(m_font, getScriptSizeMultiplier());
371     // child.setDisplayStyle(false);
372     // child.setInheritSisplayStyle(false);
373     // }
374     // } else if (this instanceof MathOver) {
375     // if (childpos == 1) {
376     // if (((getMathElement(0) instanceof MathOperator) && ((MathOperator)
377     // getMathElement(0))
378     // .getMoveableLimits())
379     // || (!((MathOver) this).getAccent())) {
380     // child.multipleFont(m_font, getScriptSizeMultiplier());
381     // child.setDisplayStyle(false);
382     // child.setInheritSisplayStyle(false);
383     // }
384     // }
385     // } else if (this instanceof MathUnder) {
386     // if (childpos == 1) {
387     // if (((getMathElement(0) instanceof MathOperator) && ((MathOperator)
388     // getMathElement(0))
389     // .getMoveableLimits())
390     // || (!((MathUnder) this).getAccentUnder())) {
391     // child.multipleFont(m_font, getScriptSizeMultiplier());
392     // child.setDisplayStyle(false);
393     // child.setInheritSisplayStyle(false);
394     // }
395     // }
396     // } else if (this instanceof MathUnderOver) {
397     // if (childpos > 0) {
398     // if (((getMathElement(0) instanceof MathOperator) && ((MathOperator)
399     // getMathElement(0))
400     // .getMoveableLimits())
401     // || ((childpos == 1) && (!((MathUnderOver) this)
402     // .getAccentUnder()))
403     // || ((childpos == 2) && (!((MathUnderOver) this)
404     // .getAccent()))) {
405     // child.multipleFont(m_font, getScriptSizeMultiplier());
406     // child.setDisplayStyle(false);
407     // child.setInheritSisplayStyle(false);
408     // }
409     // }
410     // } else if (this instanceof MathRoot) {
411     // if (childpos == 1) {
412     // child.multipleFont(m_font, (float) Math.pow(
413     // getScriptSizeMultiplier(), 2));
414     // child.setDisplayStyle(false);
415     // child.setInheritSisplayStyle(false);
416     // }
417     // } else if (this instanceof MathSub) {
418     // if (childpos == 1) {
419     // child.multipleFont(m_font, getScriptSizeMultiplier());
420     // child.setDisplayStyle(false);
421     // child.setInheritSisplayStyle(false);
422     // }
423     // } else if (this instanceof MathSup) {
424     // if (childpos == 1) {
425     // child.multipleFont(m_font, getScriptSizeMultiplier());
426     // child.setDisplayStyle(false);
427     // child.setInheritSisplayStyle(false);
428     // }
429     // } else if (this instanceof MathSubSup) {
430     // if (childpos > 0) {
431     // child.multipleFont(m_font, getScriptSizeMultiplier());
432     // child.setDisplayStyle(false);
433     // child.setInheritSisplayStyle(false);
434     // }
435     // } else if (this instanceof MathStyle) {
436     // // child.multipleFont(m_font, (float) Math.pow(
437     // // getScriptSizeMultiplier(), ((MathStyle) this)
438     // // .getScriptlevel()));
439     // } else {
440     // child.setFont(m_font);
441     // }
442     // }
443 
444     /**
445      * Add a math element as a child.
446      * 
447      * @param child
448      *            Math element object.
449      */
450     public final void addMathElement(final MathMLElement child) {
451         if (child != null) {
452             this.appendChild(child);
453             if (child instanceof AbstractJEuclidElement) {
454                 ((AbstractJEuclidElement) child).setMathBase(this
455                         .getMathBase());
456             }
457         }
458     }
459 
460     /** {@inheritDoc} */
461     public JEuclidElement getMathElement(final int index) {
462         if (index >= 0 && index < this.getMathElementCount()) {
463             final org.w3c.dom.NodeList childList = this.getChildNodes();
464             for (int i = 0, elementIndex = 0; i < childList.getLength(); i++) {
465                 final Node child = childList.item(i);
466                 if (child instanceof JEuclidElement) {
467                     if (elementIndex == index) {
468                         return (JEuclidElement) child;
469                     }
470                     elementIndex++;
471                 }
472             }
473         }
474         return null;
475     }
476 
477     /** {@inheritDoc} */
478     public void setMathElement(final int index, final MathMLElement newElement) {
479         final org.w3c.dom.NodeList childList = this.getChildNodes();
480         while (childList.getLength() < index) {
481             this.addMathElement(new Mtext(this.mbase));
482         }
483         if (childList.getLength() == index) {
484             this.addMathElement(newElement);
485         } else {
486             this.replaceChild(newElement, childList.item(index));
487         }
488     }
489 
490     /** {@inheritDoc} */
491     public int getIndexOfMathElement(final JEuclidElement element) {
492         final org.w3c.dom.NodeList childList = this.getChildNodes();
493         for (int i = 0; i < childList.getLength(); i++) {
494             if (childList.item(i).equals(element)) {
495                 return i;
496             }
497         }
498         return -1;
499     }
500 
501     /** {@inheritDoc} */
502     public int getMathElementCount() {
503         final org.w3c.dom.NodeList childList = this.getChildNodes();
504         int elementIndex = 0;
505         for (int i = 0; i < childList.getLength(); i++) {
506             final Node child = childList.item(i);
507             if (child instanceof JEuclidElement) {
508                 elementIndex++;
509             }
510         }
511         return elementIndex;
512     }
513 
514     /**
515      * Add the content of a String to this element.
516      * 
517      * @param text
518      *            String with text of this object.
519      */
520     public void addText(final String text) {
521         Node textNode = this.getLastChild();
522         if (!(textNode instanceof Text)) {
523             textNode = new PartialTextImpl("");
524             this.appendChild(textNode);
525         }
526 
527         final StringBuilder newText = new StringBuilder();
528         if (this.getTextContent() != null) {
529             newText.append(textNode.getTextContent());
530         }
531 
532         // As seen in 2.4.6
533         if (text != null) {
534             newText.append(text.trim());
535         }
536 
537         for (int i = 0; i < newText.length() - 1; i++) {
538             if (newText.charAt(i) <= AbstractJEuclidElement.TRIVIAL_SPACE_MAX
539                     && newText.charAt(i + 1) <= AbstractJEuclidElement.TRIVIAL_SPACE_MAX) {
540                 newText.deleteCharAt(i);
541                 // CHECKSTYLE:OFF
542                 // This is intentional
543                 i--;
544                 // CHECKSTYLE:ON
545             }
546         }
547 
548         final String toSet = CharConverter.convertEarly(newText.toString());
549         if (toSet.length() > 0) {
550             textNode.setTextContent(toSet);
551         } else {
552             this.removeChild(textNode);
553         }
554 
555     }
556 
557     /**
558      * Returns the text content of this element.
559      * 
560      * @return Text content.
561      */
562     public String getText() {
563         final String theText = this.getTextContent();
564         if (theText == null) {
565             return "";
566         } else {
567             return theText;
568         }
569     }
570 
571     /**
572      * Sets the base for this element.
573      * 
574      * @param base
575      *            Math base object.
576      */
577 
578     public void setMathBase(final MathBase base) {
579         this.mbase = base;
580         final org.w3c.dom.NodeList childList = this.getChildNodes();
581         for (int i = 0; i < childList.getLength(); i++) {
582             final Node node = childList.item(i);
583             if (node instanceof AbstractJEuclidElement) {
584                 ((AbstractJEuclidElement) node).setMathBase(base);
585             }
586         }
587     }
588 
589     /** {@inheritDoc} */
590     public MathBase getMathBase() {
591         return this.mbase;
592     }
593 
594     /** {@inheritDoc} */
595     public void setFakeParent(final JEuclidElement parent) {
596         this.fakeParent = parent;
597     }
598 
599     /** {@inheritDoc} */
600     public JEuclidElement getParent() {
601         final Node parentNode = this.getParentNode();
602         JEuclidElement theParent = null;
603         if (parentNode instanceof JEuclidElement) {
604             theParent = (JEuclidElement) this.getParentNode();
605         }
606         if (theParent == null) {
607             return this.fakeParent;
608         } else {
609             return theParent;
610         }
611     }
612 
613     /**
614      * Sets value of mathvariant attribute (style of the element).
615      * 
616      * @param mathvariant
617      *            Value of mathvariant.
618      */
619     public void setMathvariant(final String mathvariant) {
620         this.setAttribute(AbstractJEuclidElement.ATTR_MATHVARIANT,
621                 mathvariant);
622     }
623 
624     /**
625      * Returns value of mathvariant attribute (style of the element).
626      * 
627      * @return Value of mathvariant.
628      */
629     public String getMathvariant() {
630         // TODO: Support deprecated name
631         return this.getMathAttribute(AbstractJEuclidElement.ATTR_MATHVARIANT);
632     }
633 
634     /**
635      * Gets value of scriptsize attribute.
636      * 
637      * @return Value of scriptsize attribute.
638      */
639     public float getScriptSizeMultiplier() {
640         return this.mscriptsizemultiplier;
641     }
642 
643     /**
644      * Sets value of scriptsize attribute.
645      * 
646      * @param scriptsizemultiplier
647      *            Value of scriptsize attribute.
648      */
649     public void setScriptSizeMultiplier(final float scriptsizemultiplier) {
650         this.mscriptsizemultiplier = scriptsizemultiplier;
651     }
652 
653     /**
654      * Gets the font metrics of the used font.
655      * 
656      * @return Font metrics.
657      * @param g
658      *            Graphics2D context to use.
659      */
660     public FontMetrics getFontMetrics(final Graphics2D g) {
661         return g.getFontMetrics(this.getFont());
662     }
663 
664     /**
665      * Sets value of math color attribute.
666      * 
667      * @param mathcolor
668      *            Color object.
669      */
670     public void setMathcolor(final String mathcolor) {
671         this.setAttribute(AbstractJEuclidElement.ATTR_MATHCOLOR, mathcolor);
672     }
673 
674     /**
675      * Returns value of mathcolor attribute.
676      * 
677      * @return Color as string.
678      */
679     public String getMathcolor() {
680         String color;
681         color = this.getMathAttribute(AbstractJEuclidElement.ATTR_MATHCOLOR);
682         if (color == null) {
683             color = this
684                     .getMathAttribute(AbstractJEuclidElement.ATTR_DEPRECATED_COLOR);
685         }
686         return color;
687     }
688 
689     /**
690      * Retrieve the mathsize attribute.
691      * 
692      * @return the mathsize attribute.
693      */
694     public String getMathsize() {
695         String size;
696         size = this.getMathAttribute(AbstractJEuclidElement.ATTR_MATHSIZE);
697         if (size == null) {
698             size = this
699                     .getMathAttribute(AbstractJEuclidElement.ATTR_DEPRECATED_FONTSIZE);
700         }
701         return size;
702 
703     }
704 
705     /**
706      * Sets mathsize to a new value.
707      * 
708      * @param mathsize
709      *            value of mathsize.
710      */
711     public void setMathsize(final String mathsize) {
712         this.setAttribute(AbstractJEuclidElement.ATTR_MATHSIZE, mathsize);
713     }
714 
715     /** {@inheritDoc} */
716     public float getMathsizeInPoint() {
717 
718         final String msize = this.getMathsize();
719 
720         JEuclidNode relativeToElement = null;
721         if (this.getParent() != null) {
722             relativeToElement = this.getParent();
723         } else {
724             relativeToElement = this.mbase.getRootElement();
725         }
726         if (msize == null) {
727             return relativeToElement.getMathsizeInPoint();
728         }
729         return AttributesHelper.convertSizeToPt(msize, relativeToElement,
730                 AttributesHelper.PT);
731     }
732 
733     /**
734      * Sets default values for math attributes. Default values are returned
735      * through getMathAttribute, but not stored in the actual DOM tree. This
736      * is necessary to support proper serialization.
737      * 
738      * @param key
739      *            the attribute to set.
740      * @param value
741      *            value of the attribute.
742      */
743     protected void setDefaultMathAttribute(final String key,
744             final String value) {
745         this.defaultMathAttributes.put(key, value);
746     }
747 
748     /**
749      * retrieve an attribute from the MathML or default namespace.
750      * 
751      * @param attrName
752      *            the name of the attribute
753      * @return attribtue value
754      */
755     protected String getMathAttribute(final String attrName) {
756         String attrValue;
757         attrValue = this.getAttributeNS(AbstractJEuclidElement.URI, attrName);
758         if (attrValue == null) {
759             attrValue = this.getAttribute(attrName);
760         }
761         if (attrValue == null) {
762             attrValue = this.defaultMathAttributes.get(attrName);
763         }
764         return attrValue;
765     }
766 
767     /**
768      * Returns value of mathbackground attribute.
769      * 
770      * @return Color as string.
771      */
772     public String getMathbackground() {
773         String color;
774         color = this
775                 .getMathAttribute(AbstractJEuclidElement.ATTR_MATHBACKGROUND);
776         if (color == null) {
777             color = this
778                     .getMathAttribute(AbstractJEuclidElement.ATTR_DEPRECATED_BACKGROUND);
779         }
780         return color;
781     }
782 
783     /**
784      * Sets the value of the machbackground attribute.
785      * 
786      * @param mathbackground
787      *            a string to be used as background color.
788      */
789     public void setMathbackground(final String mathbackground) {
790         this.setAttribute(AbstractJEuclidElement.ATTR_MATHBACKGROUND,
791                 mathbackground);
792     }
793 
794     /** {@inheritDoc} */
795     public Color getForegroundColor() {
796         final String colorString = this.getMathcolor();
797         Color theColor;
798         if (colorString == null) {
799             if (this.getParent() != null) {
800                 theColor = this.getParent().getForegroundColor();
801             } else {
802                 theColor = AttributesHelper.stringToColor(this.mbase
803                         .getParams().get(ParameterKey.ForegroundColor),
804                         Color.BLACK);
805             }
806         } else {
807             theColor = AttributesHelper.stringToColor(colorString,
808                     Color.BLACK);
809         }
810         return theColor;
811     }
812 
813     /** {@inheritDoc} */
814     public Color getBackgroundColor() {
815         final String colorString = this.getMathbackground();
816         final Color theColor;
817         if (colorString == null) {
818             if (this.getParent() != null) {
819                 // For height debugging purposes, this is left here.
820                 // theColor = this.getParent().getBackgroundColor();
821                 theColor = null;
822             } else {
823                 theColor = AttributesHelper.stringToColor(this.mbase
824                         .getParams().get(ParameterKey.BackgroundColor), null);
825             }
826         } else {
827             theColor = AttributesHelper.stringToColor(colorString, null);
828         }
829         return theColor;
830     }
831 
832     /**
833      * Paints a border around this element as debug information.
834      * 
835      * @param g
836      *            The graphics context to use for painting
837      * @param posX
838      *            The first left position for painting
839      * @param posY
840      *            The position of the baseline
841      */
842     public void debug(final Graphics2D g, final float posX, final float posY) {
843         g.setColor(Color.blue);
844         g.draw(new Line2D.Float(posX, posY - this.getAscentHeight(g), posX
845                 + this.getWidth(g), posY - this.getAscentHeight(g)));
846         g.draw(new Line2D.Float(posX + this.getWidth(g), posY
847                 - this.getAscentHeight(g), posX + this.getWidth(g), posY
848                 + this.getDescentHeight(g)));
849         g.draw(new Line2D.Float(posX, posY + this.getDescentHeight(g), posX
850                 + this.getWidth(g), posY + this.getDescentHeight(g)));
851         g.draw(new Line2D.Float(posX, posY - this.getAscentHeight(g), posX,
852                 posY + this.getDescentHeight(g)));
853         g.setColor(Color.red);
854         g.draw(new Line2D.Float(posX, posY, posX + this.getWidth(g), posY));
855         g.setColor(Color.black);
856     }
857 
858     /** {@inheritDoc} */
859     public void setGlobalLineCorrector(final float corrector) {
860         if (this.getParent() == null) {
861             return;
862         }
863 
864         // if this is a top-element of the row
865         if (this instanceof Mtr || this instanceof Mrow
866                 && this.getParent() instanceof Mtable
867                 || this.getParent() instanceof MathImpl) {
868             if (this.globalLineCorrecter < corrector) {
869                 this.globalLineCorrecter = corrector;
870             }
871         } else {
872             this.getParent().setGlobalLineCorrector(corrector);
873         }
874     }
875 
876     /** {@inheritDoc} */
877     public float getGlobalLineCorrector() {
878         final float retVal;
879         if (this.getParent() == null) {
880             retVal = 0;
881         } else
882 
883         // if this is a top-element of the line, it contains the correct
884         // number
885         if (this instanceof Mtr || this instanceof Mrow
886                 && this.getParent() instanceof Mtable
887                 || this.getParent() instanceof MathImpl) {
888             retVal = this.globalLineCorrecter;
889         } else {
890             retVal = this.getParent().getGlobalLineCorrector();
891         }
892         return retVal;
893     }
894 
895     /** {@inheritDoc} */
896     public float getWidth(final Graphics2D g) {
897         if (this.calculatedWidth < 0) {
898             this.calculatedWidth = this.calculateWidth(g);
899         }
900         return this.calculatedWidth;
901     }
902 
903     /**
904      * Caculates width of the element.
905      * 
906      * @return Width of the element.
907      * @param g
908      *            Graphics2D context to use.
909      */
910     public abstract float calculateWidth(Graphics2D g);
911 
912     /** {@inheritDoc} */
913     public float getHeight(final Graphics2D g) {
914         if (this.calculatingSize || this.getParent() != null
915                 && this.getParent().isCalculatingSize()) {
916             if (this.calculatedStretchHeight < 0) {
917                 this.calculatedStretchHeight = this.calculateHeight(g);
918             }
919             return this.calculatedStretchHeight;
920         } else {
921             if (this.calculatedHeight < 0) {
922                 this.calculatedHeight = this.calculateHeight(g);
923             }
924             return this.calculatedHeight;
925         }
926     }
927 
928     /**
929      * Calculates the current height of the element.
930      * 
931      * @return Height of the element.
932      * @param g
933      *            Graphics2D context to use.
934      */
935     public float calculateHeight(final Graphics2D g) {
936         return this.getAscentHeight(g) + this.getDescentHeight(g);
937     }
938 
939     /** {@inheritDoc} */
940     public float getAscentHeight(final Graphics2D g) {
941         if (this.calculatingSize || this.getParent() != null
942                 && this.getParent().isCalculatingSize()) {
943             if (this.calculatedStretchAscentHeight < 0) {
944                 this.calculatedStretchAscentHeight = this
945                         .calculateAscentHeight(g);
946             }
947             return this.calculatedStretchAscentHeight;
948         } else {
949             if (this.calculatedAscentHeight < 0) {
950                 this.calculatedAscentHeight = this.calculateAscentHeight(g);
951             }
952             return this.calculatedAscentHeight;
953         }
954     }
955 
956     /** {@inheritDoc} */
957     public abstract float calculateAscentHeight(Graphics2D g);
958 
959     /** {@inheritDoc} */
960     public float getDescentHeight(final Graphics2D g) {
961         if (this.calculatingSize || this.getParent() != null
962                 && this.getParent().isCalculatingSize()) {
963             if (this.calculatedStretchDescentHeight < 0) {
964                 this.calculatedStretchDescentHeight = this
965                         .calculateDescentHeight(g);
966             }
967             return this.calculatedStretchDescentHeight;
968         } else {
969             if (this.calculatedDescentHeight < 0) {
970                 this.calculatedDescentHeight = this.calculateDescentHeight(g);
971             }
972             return this.calculatedDescentHeight;
973         }
974     }
975 
976     /** {@inheritDoc} */
977     public abstract float calculateDescentHeight(Graphics2D g);
978 
979     /**
980      * Returns the distance of the baseline and the middleline.
981      * 
982      * @return Distance baseline - middleline.
983      * @param g
984      *            Graphics2D context to use.
985      */
986     public float getMiddleShift(final Graphics2D g) {
987         return this.getFontMetrics(g).getAscent()
988                 * AbstractJEuclidElement.MIDDLE_SHIFT;
989     }
990 
991     /** {@inheritDoc} */
992     public void setMathAttributes(final AttributeMap attributes) {
993         final Map<String, String> attrsAsMap = attributes.getAsMap();
994         for (final Map.Entry<String, String> e : attrsAsMap.entrySet()) {
995             final String attrName = e.getKey();
996             if (AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
997                     .contains(attrName)) {
998                 AbstractJEuclidElement.LOGGER
999                         .warn("Deprecated attribute for " + this.getTagName()
1000                                 + ": " + attrName);
1001             }
1002             this.setAttribute(attrName, e.getValue());
1003         }
1004         this.recalculateSize();
1005     }
1006 
1007     /** {@inheritDoc} */
1008     public boolean isCalculatingSize() {
1009         return this.calculatingSize;
1010     }
1011 
1012     /** {@inheritDoc} */
1013     public void setCalculatingSize(final boolean ncalculatingSize) {
1014         this.calculatingSize = ncalculatingSize;
1015     }
1016 
1017     /** {@inheritDoc} */
1018     public String getClassName() {
1019         return this.getAttribute(AbstractJEuclidElement.ATTR_CLASS);
1020     }
1021 
1022     /** {@inheritDoc} */
1023     public void setClassName(final String className) {
1024         this.setAttribute(AbstractJEuclidElement.ATTR_CLASS, className);
1025     }
1026 
1027     /** {@inheritDoc} */
1028     public String getMathElementStyle() {
1029         return this.getAttribute(AbstractJEuclidElement.ATTR_STYLE);
1030     }
1031 
1032     /** {@inheritDoc} */
1033     public void setMathElementStyle(final String mathElementStyle) {
1034         this
1035                 .setAttribute(AbstractJEuclidElement.ATTR_STYLE,
1036                         mathElementStyle);
1037     }
1038 
1039     /** {@inheritDoc} */
1040     public String getId() {
1041         return this.getAttribute(AbstractJEuclidElement.ATTR_ID);
1042     }
1043 
1044     /** {@inheritDoc} */
1045     public void setId(final String id) {
1046         this.setAttribute(AbstractJEuclidElement.ATTR_ID, id);
1047     }
1048 
1049     /** {@inheritDoc} */
1050     public String getXref() {
1051         return this.getAttribute(AbstractJEuclidElement.ATTR_XREF);
1052     }
1053 
1054     /** {@inheritDoc} */
1055     public void setXref(final String xref) {
1056         this.setAttribute(AbstractJEuclidElement.ATTR_XREF, xref);
1057     }
1058 
1059     /** {@inheritDoc} */
1060     public String getHref() {
1061         return this.getAttribute(AbstractJEuclidElement.ATTR_HREF);
1062     }
1063 
1064     /** {@inheritDoc} */
1065     public void setHref(final String href) {
1066         this.setAttribute(AbstractJEuclidElement.ATTR_HREF, href);
1067     }
1068 
1069     /** {@inheritDoc} */
1070     public MathMLMathElement getOwnerMathElement() {
1071         JEuclidElement node = this.getParent();
1072         while (node != null) {
1073             if (node instanceof MathMLMathElement) {
1074                 return (MathMLMathElement) node;
1075             }
1076             node = node.getParent();
1077         }
1078         return null;
1079     }
1080 
1081     /** {@inheritDoc} */
1082     public boolean isChildBlock(final JEuclidElement child) {
1083         final JEuclidElement parent = this.getParent();
1084         if (parent != null) {
1085             return parent.isChildBlock(this);
1086         } else {
1087             return true;
1088         }
1089     }
1090 
1091     /** {@inheritDoc} */
1092     public boolean hasChildPrescripts(final JEuclidElement child) {
1093         return false;
1094     }
1095 
1096     /** {@inheritDoc} */
1097     public boolean hasChildPostscripts(final JEuclidElement child) {
1098         return false;
1099     }
1100 
1101     /** {@inheritDoc} */
1102     public void paint(final Graphics2D g, final float posX, final float posY) {
1103         this.lastPaintedX = posX;
1104         this.lastPaintedY = posY;
1105         if (this.getBackgroundColor() != null) {
1106             g.setColor(this.getBackgroundColor());
1107             final float ascent = (float) Math.ceil(this.getAscentHeight(g));
1108             final float descent = (float) Math.ceil(this.getDescentHeight(g));
1109             g.fill(new Rectangle2D.Float(posX, posY - ascent, (float) Math
1110                     .ceil(this.getWidth(g)), ascent + descent));
1111         }
1112 
1113         if (this.getMathBase().isDebug()) {
1114             this.debug(g, posX, posY);
1115         }
1116         g.setColor(this.getForegroundColor());
1117         g.setFont(this.getFont());
1118 
1119     }
1120 
1121     /** {@inheritDoc} */
1122     public float getPaintedPosX() {
1123         return this.lastPaintedX;
1124     }
1125 
1126     /** {@inheritDoc} */
1127     public float getPaintedPosY() {
1128         return this.lastPaintedY;
1129     }
1130 
1131     /**
1132      * Returns the children as a MathML NodeList.
1133      * 
1134      * @return a list of children
1135      */
1136     public MathMLNodeList getContents() {
1137         return (MathMLNodeList) this.getChildNodes();
1138     }
1139 
1140     /** {@inheritDoc} */
1141     public float getXCenter(final Graphics2D g) {
1142         return this.getWidth(g) / 2.0f;
1143     }
1144 
1145     {
1146         AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
1147                 .add(AbstractJEuclidElement.ATTR_DEPRECATED_COLOR);
1148         AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
1149                 .add(AbstractJEuclidElement.ATTR_DEPRECATED_BACKGROUND);
1150         AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
1151                 .add(AbstractJEuclidElement.ATTR_DEPRECATED_FONTSIZE);
1152         AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
1153                 .add(AbstractJEuclidElement.ATTR_DEPRECATED_FONTWEIGHT);
1154         AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
1155                 .add(AbstractJEuclidElement.ATTR_DEPRECATED_FONTSTYLE);
1156         AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
1157                 .add(AbstractJEuclidElement.ATTR_DEPRECATED_FONTFAMILY);
1158 
1159         AbstractJEuclidElement.DEPRECATED_ATTRIBUTES
1160                 .add(Mo.ATTR_MOVEABLEWRONG);
1161     }
1162 }