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: Mo.java 810 2008-06-26 06:54:16Z maxberger $ */
18  
19  package net.sourceforge.jeuclid.elements.presentation.token;
20  
21  import java.awt.Color;
22  import java.awt.Graphics2D;
23  import java.awt.font.TextLayout;
24  import java.awt.geom.AffineTransform;
25  import java.text.AttributedString;
26  
27  import net.sourceforge.jeuclid.Constants;
28  import net.sourceforge.jeuclid.LayoutContext;
29  import net.sourceforge.jeuclid.context.Display;
30  import net.sourceforge.jeuclid.context.Parameter;
31  import net.sourceforge.jeuclid.elements.AbstractJEuclidElement;
32  import net.sourceforge.jeuclid.elements.JEuclidElement;
33  import net.sourceforge.jeuclid.elements.presentation.general.Mrow;
34  import net.sourceforge.jeuclid.elements.support.GraphicsSupport;
35  import net.sourceforge.jeuclid.elements.support.attributes.AttributesHelper;
36  import net.sourceforge.jeuclid.elements.support.operatordict.OperatorDictionary;
37  import net.sourceforge.jeuclid.elements.support.operatordict.UnknownAttributeException;
38  import net.sourceforge.jeuclid.elements.support.text.StringUtil;
39  import net.sourceforge.jeuclid.elements.support.text.StringUtil.TextLayoutInfo;
40  import net.sourceforge.jeuclid.layout.LayoutInfo;
41  import net.sourceforge.jeuclid.layout.LayoutStage;
42  import net.sourceforge.jeuclid.layout.LayoutView;
43  import net.sourceforge.jeuclid.layout.TextObject;
44  
45  import org.apache.batik.dom.events.DOMCustomEvent;
46  import org.w3c.dom.Node;
47  import org.w3c.dom.events.CustomEvent;
48  import org.w3c.dom.events.Event;
49  import org.w3c.dom.events.EventListener;
50  import org.w3c.dom.events.EventTarget;
51  import org.w3c.dom.mathml.MathMLOperatorElement;
52  import org.w3c.dom.mathml.MathMLUnderOverElement;
53  
54  /**
55   * This class presents a math operator, like "(" or "*".
56   * 
57   * @version $Revision: 810 $
58   */
59  
60  // CHECKSTYLE:OFF
61  // Class Fan-out is to high. However, this is required due to complexity of
62  // mo.
63  public final class Mo extends AbstractJEuclidElement implements
64          MathMLOperatorElement, EventListener {
65      // CHECKSTYLE:ON
66  
67      /** Attribute for form. */
68      public static final String ATTR_FORM = "form";
69  
70      /** Attribute for separator. */
71      public static final String ATTR_SEPARATOR = "separator";
72  
73      /** Attribute for lspace. */
74      public static final String ATTR_LSPACE = "lspace";
75  
76      /** Attribute for rspace. */
77      public static final String ATTR_RSPACE = "rspace";
78  
79      /** Attribute for min size. */
80      public static final String ATTR_MINSIZE = "minsize";
81  
82      /** Attribute for max size. */
83      public static final String ATTR_MAXSIZE = "maxsize";
84  
85      /** Wrong attribute name for movable limits. */
86      public static final String ATTR_MOVEABLEWRONG = "moveablelimits";
87  
88      /** Attribute for movable limits. */
89      public static final String ATTR_MOVABLELIMITS = "movablelimits";
90  
91      /** Attribute for accent. */
92      public static final String ATTR_ACCENT = "accent";
93  
94      /**
95       * The XML element from this class.
96       */
97      public static final String ELEMENT = "mo";
98  
99      /**
100      * Multiplier for increasing size of mo with attribute largop = true.
101      */
102     public static final float LARGEOP_CORRECTOR_INLINE = (float) 1.2;
103 
104     /**
105      * Multiplier for increasing size of mo with attribute largop = true.
106      */
107     public static final float LARGEOP_CORRECTOR_BLOCK = (float) 1.5;
108 
109     /**
110      * Attribute name of the stretchy property.
111      */
112     public static final String ATTR_STRETCHY = "stretchy";
113 
114     /**
115      * Attribute name of the largeop property.
116      */
117     public static final String ATTR_LARGEOP = "largeop";
118 
119     /**
120      * Attribute name of the symmetric property.
121      */
122     public static final String ATTR_SYMMETRIC = "symmetric";
123 
124     /**
125      * Attribute name of the fence property.
126      */
127     public static final String ATTR_FENCE = "fence";
128 
129     /**
130      * Event name for operator events.
131      */
132     public static final String MOEVENT = "MOEvent";
133 
134     /**
135      * Horizontal delimiters.
136      * 
137      * @todo Check the uncommented ones, possibly add more / remove some?
138      */
139     public static final String HOR_DELIMITERS = /* _ */"\u005F"
140             + /* OverBar */"\u00AF" + /* UnderBar */"\u0332" + "\u0333"
141             + "\u033F" + "\u2190" + "\u2192" + "\u2194"
142             + /* OverBracket */"\u23B4" + /* UnderBracket */"\u23B5"
143             + /* OverParenthesis */"\uFE35"
144             + /* UnderParenthesis */"\uFE36" + /* OverBrace */"\uFE37"
145             + /* UnderBrace */"\uFE38" + /* Frown */"\u2322";
146 
147     /**
148      * Vertical delimiters.
149      * 
150      * @todo Add others (?)
151      */
152     public static final String VER_DELIMITERS = "[{()}]|"
153             + /* Up Arrow */"\u2191" + /* Down Arrow */"\u2193" + /*
154                                                                    * Up Arrow
155                                                                    * Down
156                                                                    * Arrow
157                                                                    */"\u21C5"
158             + /* Up Arrow Up Arrow */"\u21C8"
159             + /* Down Down Arrows */"\u21CA"
160             + /* Down Arrow Up Arrow */"\u21F5" + "\u2223\u2225\u2329\u232A";
161 
162     private static final long serialVersionUID = 1L;
163 
164     private final OperatorDictionary opDict;
165 
166     private boolean inChangeHook;
167 
168     /**
169      * Logger for this class
170      */
171     // unused
172     // private static final Log LOGGER =
173     // LogFactory.getLog(MathOperator.class);
174     /**
175      * Creates a mathoperator element.
176      */
177     public Mo() {
178         super();
179         this.setDefaultMathAttribute(Mo.ATTR_FORM,
180                 OperatorDictionary.FORM_INFIX);
181         this.setDefaultMathAttribute(Mo.ATTR_FENCE, Constants.FALSE);
182         this.setDefaultMathAttribute(Mo.ATTR_SEPARATOR, Constants.FALSE);
183         this.setDefaultMathAttribute(Mo.ATTR_LSPACE,
184                 AttributesHelper.THICKMATHSPACE);
185         this.setDefaultMathAttribute(Mo.ATTR_RSPACE,
186                 AttributesHelper.THICKMATHSPACE);
187         this.setDefaultMathAttribute(Mo.ATTR_STRETCHY, Constants.FALSE);
188         this.setDefaultMathAttribute(Mo.ATTR_SYMMETRIC, Constants.TRUE);
189         this.setDefaultMathAttribute(Mo.ATTR_MAXSIZE,
190                 AttributesHelper.INFINITY);
191         this.setDefaultMathAttribute(Mo.ATTR_MINSIZE, "1");
192         this.setDefaultMathAttribute(Mo.ATTR_LARGEOP, Constants.FALSE);
193         this.setDefaultMathAttribute(Mo.ATTR_MOVABLELIMITS, Constants.FALSE);
194         this.setDefaultMathAttribute(Mo.ATTR_ACCENT, Constants.FALSE);
195         this.opDict = OperatorDictionary.getInstance();
196     }
197 
198     /** {@inheritDoc} */
199     @Override
200     protected Node newNode() {
201         return new Mo();
202     }
203 
204     /**
205      * Gets value of lspace property of the operator.
206      * 
207      * @return Flag of lspace property.
208      */
209     private float getLspaceAsFloat(final LayoutContext now) {
210         if (((Integer) now.getParameter(Parameter.SCRIPTLEVEL)) > 0) {
211             return 0.0f;
212         } else {
213             return AttributesHelper.convertSizeToPt(this.getLspace(), now,
214                     AttributesHelper.PT);
215         }
216     }
217 
218     /**
219      * @param now
220      *            applied layout context.
221      * @return Multiplier for increasing size of mo whith attribute largop =
222      *         true
223      */
224     public float getLargeOpCorrector(final LayoutContext now) {
225         if (Display.BLOCK.equals(now.getParameter(Parameter.DISPLAY))) {
226             return Mo.LARGEOP_CORRECTOR_BLOCK;
227         } else {
228             return Mo.LARGEOP_CORRECTOR_INLINE;
229         }
230     }
231 
232     /**
233      * Gets value of rspace property of the operator.
234      * 
235      * @return Flag of rspace property.
236      */
237     private float getRspaceAsFloat(final LayoutContext now) {
238         if (((Integer) now.getParameter(Parameter.SCRIPTLEVEL)) > 0) {
239             return 0.0f;
240         } else {
241             return AttributesHelper.convertSizeToPt(this.getRspace(), now,
242                     AttributesHelper.PT);
243         }
244     }
245 
246     private boolean isFence() {
247         return Boolean.parseBoolean(this.getFence());
248     }
249 
250     /**
251      * Sets value of maxsize property.
252      * 
253      * @param maxsize
254      *            Maxsize value.
255      */
256     public void setMaxsize(final String maxsize) {
257         this.setAttribute(Mo.ATTR_MAXSIZE, maxsize);
258     }
259 
260     /**
261      * Gets value of maxsize property.
262      * 
263      * @return Maxsize value.
264      */
265     public String getMaxsize() {
266         return this.getMathAttribute(Mo.ATTR_MAXSIZE);
267     }
268 
269     /**
270      * Sets value of minsize property.
271      * 
272      * @param minsize
273      *            Minsize value.
274      */
275     public void setMinsize(final String minsize) {
276         this.setAttribute(Mo.ATTR_MINSIZE, minsize);
277     }
278 
279     /**
280      * Gets value of minsize property.
281      * 
282      * @return Minsize value.
283      */
284     public String getMinsize() {
285         return this.getMathAttribute(Mo.ATTR_MINSIZE);
286     }
287 
288     private boolean isVerticalDelimeter() {
289         return this.getText().length() == 1
290                 && (Mo.VER_DELIMITERS.indexOf(this.getText().charAt(0)) >= 0 || this
291                         .isFence());
292     }
293 
294     private boolean isHorizontalDelimeter() {
295         return this.getText().length() == 1
296                 && (Mo.HOR_DELIMITERS.indexOf(this.getText().charAt(0)) >= 0);
297     }
298 
299     private TextLayout produceUnstrechtedLayout(final Graphics2D g,
300             final LayoutContext now) {
301         assert g != null : "Graphics2d is null in produceUnstrechtedLayout";
302         float fontSizeInPoint = GraphicsSupport.getFontsizeInPoint(now);
303         if (Boolean.parseBoolean(this.getLargeop())) {
304             fontSizeInPoint *= this.getLargeOpCorrector(now);
305         }
306 
307         final String theText = this.getText();
308         final AttributedString aString = StringUtil
309                 .convertStringtoAttributedString(theText, this
310                         .getMathvariantAsVariant(), fontSizeInPoint, now);
311         final TextLayout theLayout = StringUtil
312                 .createTextLayoutFromAttributedString(g, aString, now);
313         return theLayout;
314     }
315 
316     /** {@inheritDoc} */
317     @Override
318     public void changeHook() {
319         super.changeHook();
320         if (!this.inChangeHook) {
321             this.inChangeHook = true;
322             this.detectFormParameter();
323             this
324                     .loadAttributeFromDictionary(Mo.ATTR_LARGEOP,
325                             Constants.FALSE);
326             this.loadAttributeFromDictionary(Mo.ATTR_SYMMETRIC,
327                     Constants.TRUE);
328             this.loadAttributeFromDictionary(Mo.ATTR_STRETCHY,
329                     Constants.FALSE);
330             this.loadAttributeFromDictionary(Mo.ATTR_FENCE, Constants.FALSE);
331             this.loadAttributeFromDictionary(Mo.ATTR_LSPACE,
332                     AttributesHelper.THICKMATHSPACE);
333             this.loadAttributeFromDictionary(Mo.ATTR_RSPACE,
334                     AttributesHelper.THICKMATHSPACE);
335             this.loadAttributeFromDictionary(Mo.ATTR_MOVABLELIMITS,
336                     Constants.FALSE);
337             // TODO: Load all.
338 
339             JEuclidElement parent = this.getParent();
340             while (parent != null) {
341                 if (parent instanceof EventTarget) {
342                     ((EventTarget) parent).addEventListener(
343                             "DOMSubtreeModified", this, false);
344                 }
345                 if (parent instanceof Mrow) {
346                     parent = null;
347                 } else {
348                     parent = parent.getParent();
349                 }
350             }
351 
352             if (this.isFence()) {
353                 this
354                         .setDefaultMathAttribute(Mo.ATTR_STRETCHY,
355                                 Constants.TRUE);
356             }
357             final CustomEvent evt = new DOMCustomEvent();
358             evt.initCustomEventNS(null, Mo.MOEVENT, true, false, null);
359             this.dispatchEvent(evt);
360             this.inChangeHook = false;
361         }
362     }
363 
364     private void loadAttributeFromDictionary(final String attrname,
365             final String defvalue) {
366         String attr;
367         try {
368             attr = this.opDict.getDefaultAttributeValue(this.getText(), this
369                     .getForm(), attrname);
370         } catch (final UnknownAttributeException e) {
371             attr = defvalue;
372         }
373         if (attr.equals(OperatorDictionary.VALUE_UNKNOWN)) {
374             attr = defvalue;
375         }
376         this.setDefaultMathAttribute(attrname, attr);
377 
378     }
379 
380     private void detectFormParameter() {
381         final String form;
382         final JEuclidElement parent = this.getParent();
383         if (parent != null && (parent instanceof Mrow)) {
384             final int index = parent.getIndexOfMathElement(this);
385             if (index == 0 && parent.getMathElementCount() > 0) {
386                 form = OperatorDictionary.FORM_PREFIX;
387             } else {
388                 if (index == (parent.getMathElementCount() - 1)
389                         && parent.getMathElementCount() > 0) {
390                     form = OperatorDictionary.FORM_POSTFIX;
391                 } else {
392                     form = OperatorDictionary.FORM_INFIX;
393                 }
394             }
395         } else {
396             form = OperatorDictionary.FORM_INFIX;
397         }
398         this.setDefaultMathAttribute(Mo.ATTR_FORM, form);
399         // TODO: Exception for embellished operators
400     }
401 
402     /** {@inheritDoc} */
403     public String getLargeop() {
404         return this.getMathAttribute(Mo.ATTR_LARGEOP);
405     }
406 
407     /** {@inheritDoc} */
408     public String getLspace() {
409         return this.getMathAttribute(Mo.ATTR_LSPACE);
410     }
411 
412     /** {@inheritDoc} */
413     public String getMovablelimits() {
414         final String wrongAttr = this.getMathAttribute(Mo.ATTR_MOVEABLEWRONG,
415                 false);
416         if (wrongAttr == null) {
417             return this.getMathAttribute(Mo.ATTR_MOVABLELIMITS);
418         } else {
419             return wrongAttr;
420         }
421     }
422 
423     /** {@inheritDoc} */
424     public String getRspace() {
425         return this.getMathAttribute(Mo.ATTR_RSPACE);
426     }
427 
428     /** {@inheritDoc} */
429     public void setAccent(final String accent) {
430         this.setAttribute(Mo.ATTR_ACCENT, accent);
431     }
432 
433     /** {@inheritDoc} */
434     public void setFence(final String fence) {
435         this.setAttribute(Mo.ATTR_FENCE, fence);
436     }
437 
438     /** {@inheritDoc} */
439     public void setForm(final String form) {
440         this.setAttribute(Mo.ATTR_FORM, form);
441     }
442 
443     /** {@inheritDoc} */
444     public void setLargeop(final String largeop) {
445         this.setAttribute(Mo.ATTR_LARGEOP, largeop);
446     }
447 
448     /** {@inheritDoc} */
449     public void setLspace(final String lspace) {
450         this.setAttribute(Mo.ATTR_LSPACE, lspace);
451     }
452 
453     /** {@inheritDoc} */
454     public void setMovablelimits(final String movablelimits) {
455         this.setAttribute(Mo.ATTR_MOVABLELIMITS, movablelimits);
456     }
457 
458     /** {@inheritDoc} */
459     public void setRspace(final String rspace) {
460         this.setAttribute(Mo.ATTR_RSPACE, rspace);
461     }
462 
463     /** {@inheritDoc} */
464     public void setSeparator(final String separator) {
465         this.setAttribute(Mo.ATTR_SEPARATOR, separator);
466     }
467 
468     /** {@inheritDoc} */
469     public void setStretchy(final String stretchy) {
470         this.setAttribute(Mo.ATTR_STRETCHY, stretchy);
471     }
472 
473     /** {@inheritDoc} */
474     public void setSymmetric(final String symmetric) {
475         this.setAttribute(Mo.ATTR_SYMMETRIC, symmetric);
476     }
477 
478     /** {@inheritDoc} */
479     public String getFence() {
480         return this.getMathAttribute(Mo.ATTR_FENCE);
481     }
482 
483     /** {@inheritDoc} */
484     public String getForm() {
485         return this.getMathAttribute(Mo.ATTR_FORM);
486     }
487 
488     /** {@inheritDoc} */
489     public String getSeparator() {
490         return this.getMathAttribute(Mo.ATTR_SEPARATOR);
491     }
492 
493     /** {@inheritDoc} */
494     public String getStretchy() {
495         return this.getMathAttribute(Mo.ATTR_STRETCHY);
496     }
497 
498     /** {@inheritDoc} */
499     public String getAccent() {
500         return this.getMathAttribute(Mo.ATTR_ACCENT);
501     }
502 
503     /** {@inheritDoc} */
504     public String getSymmetric() {
505         return this.getMathAttribute(Mo.ATTR_SYMMETRIC);
506     }
507 
508     /** {@inheritDoc} */
509     @Override
510     public void layoutStage1(final LayoutView view, final LayoutInfo info,
511             final LayoutStage childMinStage, final LayoutContext context) {
512         final LayoutContext now = this.applyLocalAttributesToContext(context);
513         final Graphics2D g = view.getGraphics();
514         final TextLayout t = this.produceUnstrechtedLayout(g, now);
515 
516         final StringUtil.TextLayoutInfo tli = StringUtil.getTextLayoutInfo(t,
517                 true);
518         final float ascent = tli.getAscent();
519         final float descent = tli.getDescent();
520         final float xOffset = tli.getOffset();
521         final float contentWidth = tli.getWidth() + xOffset;
522         final JEuclidElement parent = this.getParent();
523         float lspace = this.getLspaceAsFloat(now);
524         float rspace = this.getRspaceAsFloat(now);
525         if ((parent != null) && (parent.hasChildPostscripts(this, context))) {
526             rspace = 0.0f;
527         } else {
528             rspace = this.getRspaceAsFloat(now);
529         }
530         if ((parent != null) && (parent.hasChildPrescripts(this))) {
531             lspace = 0.0f;
532         } else {
533             lspace = this.getLspaceAsFloat(now);
534         }
535 
536         info.setAscentHeight(ascent, LayoutStage.STAGE1);
537         info.setDescentHeight(descent, LayoutStage.STAGE1);
538         info.setHorizontalCenterOffset(lspace + contentWidth / 2.0f,
539                 LayoutStage.STAGE1);
540         info.setWidth(lspace + contentWidth + rspace, LayoutStage.STAGE1);
541         if (Boolean.parseBoolean(this.getStretchy())
542                 || this.isVerticalDelimeter() || this.isHorizontalDelimeter()) {
543             info.setLayoutStage(LayoutStage.STAGE1);
544         } else {
545             info.setGraphicsObject(new TextObject(t,
546                     lspace + tli.getOffset(), 0, null, (Color) now
547                             .getParameter(Parameter.MATHCOLOR)));
548             info.setLayoutStage(LayoutStage.STAGE2);
549         }
550     }
551 
552     /** {@inheritDoc} */
553     @Override
554     public void layoutStage2(final LayoutView view, final LayoutInfo info,
555             final LayoutContext context) {
556         final LayoutContext now = this.applyLocalAttributesToContext(context);
557 
558         final Graphics2D g = view.getGraphics();
559         final TextLayout t = this.produceUnstrechtedLayout(g, now);
560 
561         final float calcScaleY;
562         final float calcScaleX;
563         final float calcBaselineShift;
564         final boolean stretchVertically = this.isVerticalDelimeter();
565         JEuclidElement parent = this.getParent();
566         while (((parent instanceof MathMLUnderOverElement) && stretchVertically)
567                 || ((parent instanceof Mrow) && (parent.getMathElementCount() == 1))) {
568             parent = parent.getParent();
569         }
570         final LayoutInfo parentInfo = view.getInfo(parent);
571         final TextLayoutInfo textLayoutInfo = StringUtil.getTextLayoutInfo(t,
572                 true);
573         if (stretchVertically) {
574             final float[] yf = this.calcYScaleFactorAndBaselineShift(info,
575                     parentInfo, textLayoutInfo, now);
576             calcScaleY = yf[0];
577             calcBaselineShift = yf[1];
578         } else {
579             calcScaleY = 1.0f;
580             calcBaselineShift = 0.0f;
581         }
582         calcScaleX = this.calcXScaleFactor(info, parentInfo, textLayoutInfo);
583 
584         info.setGraphicsObject(new TextObject(t, this.getLspaceAsFloat(now)
585                 + textLayoutInfo.getOffset() * calcScaleX, calcBaselineShift,
586                 AffineTransform.getScaleInstance(calcScaleX, calcScaleY),
587                 (Color) now.getParameter(Parameter.MATHCOLOR)));
588         info.setLayoutStage(LayoutStage.STAGE2);
589     }
590 
591     private float calcXScaleFactor(final LayoutInfo info,
592             final LayoutInfo parentInfo, final TextLayoutInfo textLayoutInfo) {
593         final float calcScaleX;
594         final float stretchWidth = parentInfo.getStretchWidth();
595         if ((this.isHorizontalDelimeter()) && (stretchWidth > 0.0f)) {
596             final float realwidth = textLayoutInfo.getWidth();
597             if (realwidth > 0) {
598                 calcScaleX = stretchWidth / realwidth;
599                 info.setWidth(stretchWidth, LayoutStage.STAGE2);
600                 info.setHorizontalCenterOffset(stretchWidth / 2.0f,
601                         LayoutStage.STAGE2);
602             } else {
603                 calcScaleX = 1.0f;
604             }
605         } else {
606             calcScaleX = 1.0f;
607         }
608         return calcScaleX;
609     }
610 
611     private float[] calcYScaleFactorAndBaselineShift(final LayoutInfo info,
612             final LayoutInfo parentInfo, final TextLayoutInfo textLayoutInfo,
613             final LayoutContext now) {
614         final float calcScaleY;
615         final float calcBaselineShift;
616         final float targetNAscent = parentInfo.getStretchAscent();
617         final float targetNDescent = parentInfo.getStretchDescent();
618 
619         final float targetNHeight = targetNAscent + targetNDescent;
620 
621         final float realDescent = textLayoutInfo.getDescent();
622         final float realHeight = textLayoutInfo.getAscent() + realDescent;
623 
624         // TODO: MaxSize / MinSize could also be inherited from MStyle.
625         final float maxSize = AttributesHelper.parseRelativeSize(this
626                 .getMaxsize(), now, realHeight);
627         final float minSize = AttributesHelper.parseRelativeSize(this
628                 .getMinsize(), now, realHeight);
629         final float targetHeight = Math.max(Math.min(targetNHeight, maxSize),
630                 minSize);
631         final float targetDescent = targetHeight / targetNHeight
632                 * (targetNHeight / 2.0f)
633                 - (targetNHeight / 2.0f - targetNDescent);
634 
635         if (realHeight > 0.0f) {
636             calcScaleY = targetHeight / realHeight;
637         } else {
638             calcScaleY = 1.0f;
639         }
640         final float realDescentScaled = realDescent * calcScaleY;
641         calcBaselineShift = targetDescent - realDescentScaled;
642 
643         info.setDescentHeight(targetDescent, LayoutStage.STAGE2);
644         info
645                 .setAscentHeight(targetHeight - targetDescent,
646                         LayoutStage.STAGE2);
647         return new float[] { calcScaleY, calcBaselineShift };
648     }
649 
650     /** {@inheritDoc} */
651     public void handleEvent(final Event evt) {
652         this.changeHook();
653     }
654 }