/* AccentedAtom.java
* =========================================================================
* This file is originally part of the JMathTeX Library - http://jmathtex.sourceforge.net
*
* Copyright (C) 2004-2007 Universiteit Gent
* Copyright (C) 2009 DENIZET Calixte
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* A copy of the GNU General Public License can be found in the file
* LICENSE.txt provided with the source distribution of this program (see
* the META-INF directory in the source jar). This license can also be
* found on the GNU website at http://www.gnu.org/licenses/gpl.html.
*
* If you did not receive a copy of the GNU General Public License along
* with this program, contact the lead developer, or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* Linking this library statically or dynamically with other modules
* is making a combined work based on this library. Thus, the terms
* and conditions of the GNU General Public License cover the whole
* combination.
*
* As a special exception, the copyright holders of this library give you
* permission to link this library with independent modules to produce
* an executable, regardless of the license terms of these independent
* modules, and to copy and distribute the resulting executable under terms
* of your choice, provided that you also meet, for each linked independent
* module, the terms and conditions of the license of that module.
* An independent module is a module which is not derived from or based
* on this library. If you modify this library, you may extend this exception
* to your version of the library, but you are not obliged to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
*/
package com.himamis.retex.renderer.share;
import com.himamis.retex.renderer.share.exception.InvalidSymbolTypeException;
import com.himamis.retex.renderer.share.exception.InvalidTeXFormulaException;
import com.himamis.retex.renderer.share.exception.SymbolNotFoundException;
/**
* An atom representing another atom with an accent symbol above it.
*/
public class AccentedAtom extends Atom {
// accent symbol
private final SymbolAtom accent;
private boolean acc = false;
private boolean changeSize = true;
// base atom
protected Atom base = null;
protected Atom underbase = null;
@Override
final public Atom duplicate() {
return setFields(new AccentedAtom(base, accent, changeSize, acc, underbase));
}
private AccentedAtom(Atom base, SymbolAtom accent, boolean changeSize, boolean acc, Atom underbase) {
this.base = base;
this.accent = accent;
this.changeSize = changeSize;
this.acc = acc;
this.underbase = underbase;
}
public AccentedAtom(Atom base, Atom accent) throws InvalidSymbolTypeException {
this.base = base;
if (base instanceof AccentedAtom) {
underbase = ((AccentedAtom) base).underbase;
} else {
underbase = base;
}
if (!(accent instanceof SymbolAtom)) {
throw new InvalidSymbolTypeException("Invalid accent");
}
this.accent = (SymbolAtom) accent;
this.acc = true;
}
public AccentedAtom(Atom base, Atom accent, boolean changeSize) throws InvalidSymbolTypeException {
this(base, accent);
this.changeSize = changeSize;
}
/**
* Creates an AccentedAtom from a base atom and an accent symbol defined by its name
*
* @param base base atom
* @param accentName name of the accent symbol to be put over the base atom
* @throws InvalidSymbolTypeException if the symbol is not defined as an accent ('acc')
* @throws SymbolNotFoundException if there's no symbol defined with the given name
*/
public AccentedAtom(Atom base, String accentName) throws InvalidSymbolTypeException,
SymbolNotFoundException {
accent = SymbolAtom.get(accentName);
if (accent.type == TeXConstants.TYPE_ACCENT) {
this.base = base;
if (base instanceof AccentedAtom) {
underbase = ((AccentedAtom) base).underbase;
} else {
underbase = base;
}
} else {
throw new InvalidSymbolTypeException("The symbol with the name '" + accentName
+ "' is not defined as an accent (" + TeXSymbolParser.TYPE_ATTR + "='acc') in '"
+ TeXSymbolParser.RESOURCE_NAME + "'!");
}
}
/**
* Creates an AccentedAtom from a base atom and an accent symbol defined as a TeXFormula. This
* is used for parsing MathML.
*
* @param base base atom
* @param acc TeXFormula representing an accent (SymbolAtom)
* @throws InvalidTeXFormulaException if the given TeXFormula does not represent a single
* SymbolAtom (type "TeXConstants.TYPE_ACCENT")
* @throws InvalidSymbolTypeException if the symbol is not defined as an accent ('acc')
*/
public AccentedAtom(Atom base, TeXFormula acc) throws InvalidTeXFormulaException,
InvalidSymbolTypeException {
if (acc == null) {
throw new InvalidTeXFormulaException("The accent TeXFormula can't be null!");
}
Atom root = acc.root;
if (root instanceof SymbolAtom) {
accent = (SymbolAtom) root;
if (accent.type == TeXConstants.TYPE_ACCENT) {
this.base = base;
} else {
throw new InvalidSymbolTypeException(
"The accent TeXFormula represents a single symbol with the name '"
+ accent.getName()
+ "', but this symbol is not defined as an accent ("
+ TeXSymbolParser.TYPE_ATTR + "='acc') in '"
+ TeXSymbolParser.RESOURCE_NAME + "'!");
}
} else {
throw new InvalidTeXFormulaException(
"The accent TeXFormula does not represent a single symbol!");
}
}
@Override
public Box createBox(TeXEnvironment env) {
TeXFont tf = env.getTeXFont();
int style = env.getStyle();
// set base in cramped style
Box b = (base == null ? new StrutBox(0, 0, 0, 0) : base.createBox(env.crampStyle()));
double u = b.getWidth();
double s = 0;
if (underbase instanceof CharSymbol) {
s = tf.getSkew(((CharSymbol) underbase).getCharFont(tf), style);
}
// retrieve best Char from the accent symbol
Char ch = tf.getChar(accent.getName(), style);
while (tf.hasNextLarger(ch)) {
Char larger = tf.getNextLarger(ch, style);
if (larger.getWidth() <= u) {
ch = larger;
} else {
break;
}
}
// calculate delta
double ec = -SpaceAtom.getFactor(TeXConstants.UNIT_MU, env);
double delta = acc ? ec : Math.min(b.getHeight(), tf.getXHeight(style, ch.getFontCode()));
// create vertical box
VerticalBox vBox = new VerticalBox();
// accent
Box y;
double italic = ch.getItalic();
Box cb = new CharBox(ch);
if (acc) {
cb = accent.createBox(changeSize ? env.subStyle() : env);
}
if (Math.abs(italic) > TeXFormula.PREC) {
y = new HorizontalBox(new StrutBox(-italic, 0, 0, 0));
y.add(cb);
} else {
y = cb;
}
// if diff > 0, center accent, otherwise center base
double diff = (u - y.getWidth()) / 2;
y.setShift(s + (diff > 0 ? diff : 0));
if (diff < 0) {
b = new HorizontalBox(b, y.getWidth(), TeXConstants.ALIGN_CENTER);
}
vBox.add(y);
// kern
vBox.add(new StrutBox(0, changeSize ? -delta : -b.getHeight(), 0, 0));
// base
vBox.add(b);
// set height and depth vertical box
double total = vBox.getHeight() + vBox.getDepth(), d = b.getDepth();
vBox.setDepth(d);
vBox.setHeight(total - d);
if (diff < 0) {
HorizontalBox hb = new HorizontalBox(new StrutBox(diff, 0, 0, 0));
hb.add(vBox);
hb.setWidth(u);
return hb;
}
return vBox;
}
}