/*****************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved. *
* ------------------------------------------------------------------------- *
* This software is published under the terms of the Apache Software License *
* version 1.1, a copy of which has been included with this distribution in *
* the LICENSE file. *
*****************************************************************************/
package org.apache.batik.bridge;
import java.awt.Font;
import java.awt.Paint;
import java.awt.Stroke;
import java.awt.font.TextAttribute;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.font.GlyphVector;
import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.text.AttributedCharacterIterator;
import java.util.Vector;
import java.util.StringTokenizer;
import org.w3c.dom.Element;
import org.apache.batik.gvt.text.ArabicTextHandler;
import org.apache.batik.gvt.text.AttributedCharacterSpanIterator;
import org.apache.batik.gvt.text.GVTAttributedCharacterIterator;
import org.apache.batik.gvt.font.GVTFont;
import org.apache.batik.gvt.font.GVTGlyphVector;
import org.apache.batik.gvt.font.SVGGVTGlyphVector;
import org.apache.batik.gvt.font.GVTLineMetrics;
import org.apache.batik.gvt.font.Glyph;
import org.apache.batik.gvt.font.Kern;
import org.apache.batik.gvt.font.KerningTable;
import org.apache.batik.util.SVGConstants;
import org.apache.batik.dom.util.XMLSupport;
import org.apache.batik.css.CSSOMReadOnlyStyleDeclaration;
import org.w3c.dom.css.CSSPrimitiveValue;
import org.w3c.dom.css.CSSStyleDeclaration;
/**
* Represents an SVG font.
*
* @author <a href="mailto:bella.robinson@cmis.csiro.au">Bella Robinson</a>
* @version $Id: SVGGVTFont.java,v 1.6 2001/08/02 07:49:35 bella Exp $
*/
public final class SVGGVTFont implements GVTFont, SVGConstants {
private float fontSize;
private SVGFontFace fontFace;
private String[] glyphUnicodes;
private String[] glyphNames;
private String[] glyphLangs;
private String[] glyphOrientations;
private String[] glyphForms;
private Element[] glyphElements;
private Element[] hkernElements;
private Element[] vkernElements;
private BridgeContext ctx;
private Element textElement;
private Element missingGlyphElement;
private KerningTable hKerningTable;
private KerningTable vKerningTable;
private String language;
private String orientation;
/**
* Constructs a new SVGGVTFont of the specified size.
*
* @param fontSize The size of the font to create.
* @param fontFace The font face that describes the font.
* @param glyphUnicodes An array containing the unicode values for all
* the glyphs this font can display.
* @param glyphNames An array containing the names of all the glyphs this
* font can display.
* @param ctx The bridge context.
* @param glyphElements An array containing the children glyph elements
* of the SVG font.
* @param missingGlyphElement The missing glyph element for this font.
* @param hkernElements An array containing all hkern elements for this font.
* @param vkernElements An array containing all vkern elements for this font.
* @param textElement The text element that contains the text to be rendered
* using this font.
*/
public SVGGVTFont(float fontSize, SVGFontFace fontFace, String[] glyphUnicodes,
String[] glyphNames, String[] glyphLangs,
String[] glyphOrientations, String[] glyphForms,
BridgeContext ctx,
Element[] glyphElements,
Element missingGlyphElement,
Element[] hkernElements,
Element[] vkernElements,
Element textElement) {
this.fontFace = fontFace;
this.fontSize = fontSize;
this.glyphUnicodes = glyphUnicodes;
this.glyphNames = glyphNames;
this.glyphLangs = glyphLangs;
this.glyphOrientations = glyphOrientations;
this.glyphForms = glyphForms;
this.ctx = ctx;
this.glyphElements = glyphElements;
this.missingGlyphElement = missingGlyphElement;
this.hkernElements = hkernElements;
this.vkernElements = vkernElements;
this.textElement = textElement;
this.language = XMLSupport.getXMLLang(textElement);
CSSOMReadOnlyStyleDeclaration cssDecl = CSSUtilities.getComputedStyle(textElement);
CSSPrimitiveValue v = (CSSPrimitiveValue)cssDecl.getPropertyCSSValueInternal(CSS_WRITING_MODE_PROPERTY);
if (v.getStringValue().startsWith(CSS_TB_VALUE)) {
// top to bottom, so set orientation to "v"
this.orientation = SVG_V_VALUE;
} else {
this.orientation = SVG_H_VALUE;
}
createKerningTables();
}
/**
* Creates the kerning tables for this font. Two tables are created,
* horizontal and vertical. If there are not children vkern or hkern
* elements these tables will be empty.
*/
private void createKerningTables() {
Kern[] hEntries = new Kern[hkernElements.length];
for (int i = 0; i < hkernElements.length; i++) {
Element hkernElement = hkernElements[i];
SVGHKernElementBridge hkernBridge = (SVGHKernElementBridge)ctx.getBridge(hkernElement);
Kern hkern = hkernBridge.createKern(ctx, hkernElement, this);
hEntries[i] = hkern;
}
hKerningTable = new KerningTable(hEntries);
Kern[] vEntries = new Kern[vkernElements.length];
for (int i = 0; i < vkernElements.length; i++) {
Element vkernElement = vkernElements[i];
SVGVKernElementBridge vkernBridge = (SVGVKernElementBridge)ctx.getBridge(vkernElement);
Kern vkern = vkernBridge.createKern(ctx, vkernElement, this);
vEntries[i] = vkern;
}
vKerningTable = new KerningTable(vEntries);
}
/**
* Returns the horizontal kerning value for the specified glyph pair.
* This will be zero if there is no explicit horizontal kerning value
* for this particular glyph pair.
*
* @param glyphCode1 The id of the first glyph.
* @param glyphCode2 The id of the second glyph.
*
* @return The horizontal kerning value.
*/
public float getHKern(int glyphCode1, int glyphCode2) {
if (glyphCode1 < 0 || glyphCode1 >= glyphUnicodes.length
|| glyphCode2 < 0 || glyphCode2 >= glyphUnicodes.length) {
return 0f;
}
return hKerningTable.getKerningValue(glyphCode1, glyphCode2,
glyphUnicodes[glyphCode1], glyphUnicodes[glyphCode2]);
}
/**
* Returns the vertical kerning value for the specified glyph pair.
* This will be zero if there is no explicit vertical kerning value for
* for this particular glyph pair.
*
* @param glyphCode1 The id of the first glyph.
* @param glyphCode2 The id of the second glyph.
*
* @return The vertical kerning value.
*/
public float getVKern(int glyphCode1, int glyphCode2) {
if (glyphCode1 < 0 || glyphCode1 >= glyphUnicodes.length
|| glyphCode2 < 0 || glyphCode2 >= glyphUnicodes.length) {
return 0f;
}
return vKerningTable.getKerningValue(glyphCode1, glyphCode2,
glyphUnicodes[glyphCode1], glyphUnicodes[glyphCode2]);
}
/**
* Returns an array of glyph codes (unique ids) of the glyphs with the
* specified name (there may be more than one).
*
* @param name The name of the glyph.
*
* @return An array of matching glyph codes. This may be empty.
*/
public int[] getGlyphCodesForName(String name) {
Vector glyphCodes = new Vector();
for (int i = 0; i < glyphNames.length; i++) {
if (glyphNames[i] != null && glyphNames[i].equals(name)) {
glyphCodes.add(new Integer(i));
}
}
int[] glyphCodeArray = new int[glyphCodes.size()];
for (int i = 0; i < glyphCodes.size(); i++) {
glyphCodeArray[i] = ((Integer)glyphCodes.elementAt(i)).intValue();
}
return glyphCodeArray;
}
/**
* Returns an array of glyph codes (unique ids) of the glyphs with the
* specified unicode value (there may be more than one).
*
* @param unicode The unicode value of the glyph.
*
* @return An array of matching glyph codes. This may be empty.
*/
public int[] getGlyphCodesForUnicode(String unicode) {
Vector glyphCodes = new Vector();
for (int i = 0; i < glyphUnicodes.length; i++) {
if (glyphUnicodes[i] != null && glyphUnicodes[i].equals(unicode)) {
glyphCodes.add(new Integer(i));
}
}
int[] glyphCodeArray = new int[glyphCodes.size()];
for (int i = 0; i < glyphCodes.size(); i++) {
glyphCodeArray[i] = ((Integer)glyphCodes.elementAt(i)).intValue();
}
return glyphCodeArray;
}
/**
* Returns true if the glyph language matches the language of the text node
* to be rendered by this font. This will be the case if one of the
* languages in glyphLang matches exactly with the xml:lang attibute of
* the text node, or if the xml:lang attribute exactly equals a prefix of one
* glyph languages.
*
* @param glyphLang A comma separated list of languages that are associated
* with a glyph.
*
* @return Whether or not the glyph language matches the language of the
* text node.
*/
private boolean languageMatches(String glyphLang) {
if (glyphLang == null || glyphLang.length() == 0) {
return true; // will match all languages
}
StringTokenizer st = new StringTokenizer(glyphLang, ",");
while (st.hasMoreTokens()) {
String s = st.nextToken();
if (s.equals(language)
|| (s.startsWith(language) && s.length() > language.length()
&& s.charAt(language.length()) == '-')) {
return true;
}
}
return false;
}
/**
* Returns true if the glyph orientation matches the orientation of the
* text node to be rendered by this font.
*
* @param glyphOrientation The glyph orientation attribute value. Will be
* "h", "v" or empty.
*
* @return Whether or not the glyph orientation matches the text to be
* rendered by this font object.
*/
private boolean orientationMatches(String glyphOrientation) {
if (glyphOrientation == null || glyphOrientation.length() == 0) {
return true;
}
return glyphOrientation.equals(orientation);
}
/**
* Returns true if the glyph form matches that of the current character in
* the aci.
*
* @param gyphUnicode The unicode value of the glyph.
* @param glyphForm The arabic-form glyph attribute.
* @param aci The aci containing the character to check.
* @param currentIndex The index of the character to check.
*/
private boolean formMatches(String glyphUnicode, String glyphForm,
AttributedCharacterIterator aci, int currentIndex) {
if (aci == null || glyphForm == null || glyphForm.length() == 0) {
// there aren't any attributes attached to the text
// or the glyph doesn't have an arabic form
return true;
}
char c = aci.setIndex(currentIndex);
Integer form = (Integer)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.ARABIC_FORM);
if (form == null || form.equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_NONE)) {
// the glyph has an arabic form and the current character form is "none" so don't match
return false;
}
// see if c is the start of an arabic ligature
if (glyphUnicode.length() > 1) {
boolean matched = true;
for (int j = 1; j < glyphUnicode.length(); j++) {
c = aci.next();
if (glyphUnicode.charAt(j) != c) {
matched = false;
break;
}
}
// reset the aci
aci.setIndex(currentIndex);
if (matched) {
// ligature matches, now check that the arabic forms are ok
char lastChar = aci.setIndex(currentIndex + glyphUnicode.length() - 1);
Integer lastForm = (Integer)aci.getAttribute(
GVTAttributedCharacterIterator.TextAttribute.ARABIC_FORM);
// reset the aci again
aci.setIndex(currentIndex);
if (form != null && lastForm != null) {
if (form.equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_TERMINAL)
&& lastForm.equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_INITIAL)) {
// return true if the glyph form is isolated
return glyphForm.equals(SVGConstants.SVG_ISOLATED_VALUE);
} else if (form.equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_TERMINAL)) {
// return true if the glyph form is terminal
return glyphForm.equals(SVGConstants.SVG_TERMINAL_VALUE);
} else if (form.equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_MEDIAL)
&& lastForm.equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_MEDIAL)) {
// return true if the glyph form is medial
return glyphForm.equals(SVGConstants.SVG_MEDIAL_VALUE);
}
// should test for other combos as well here
}
}
}
if (form.equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_ISOLATED)) {
return glyphForm.equals(SVGConstants.SVG_ISOLATED_VALUE);
}
if (form.equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_TERMINAL)) {
return glyphForm.equals(SVGConstants.SVG_TERMINAL_VALUE);
}
if (form.equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_INITIAL)) {
return glyphForm.equals(SVGConstants.SVG_INITIAL_VALUE);
}
if (form.equals(GVTAttributedCharacterIterator.TextAttribute.ARABIC_MEDIAL)) {
return glyphForm.equals(SVGConstants.SVG_MEDIAL_VALUE);
}
return false;
}
/**
* Indicates whether or not the specified glyph can be displayed by this
* font.
*
* @param name The name of the glyph to check.
*
* @return True if the glyph can be displayed.
*/
public boolean canDisplayGivenName(String name) {
for (int i = 0; i < glyphNames.length; i++) {
if (glyphNames[i] != null && glyphNames[i].equals(name)
&& languageMatches(glyphLangs[i])
&& orientationMatches(glyphOrientations[i])) {
return true;
}
}
return false;
}
/**
* Indicates whether or not the specified character can be displayed by this
* font.
*
* @param c The character to check.
*
* @param True if the character can be displayed.
*/
public boolean canDisplay(char c) {
for (int i = 0; i < glyphUnicodes.length; i++) {
if (glyphUnicodes[i].indexOf(c) != -1
&& languageMatches(glyphLangs[i])
&& orientationMatches(glyphOrientations[i])) {
return true;
}
}
return false;
}
/**
* Checks whether this Font can display the characters in the
* specified character array starting at start and ending at limit.
*
* @param text An array containing the characters to check.
* @param start The index of the first character to check.
* @param limit The index of the last character to check.
*
* @return The index of the first character it can't display or -1 if
* it can display the whole string.
*/
public int canDisplayUpTo(char[] text, int start, int limit) {
StringCharacterIterator sci = new StringCharacterIterator(new String(text));
return canDisplayUpTo(sci, start, limit);
}
/**
* Checks whether this Font can display the characters in the
* specified character iterator starting at start and ending at limit.
*
* @param iter The iterator containing the characters to check.
* @param start The index of the first character to check.
* @param limit The index of the last character to check.
*
* @return The index of the first character it can't display or -1 if
* it can display the whole string.
*/
public int canDisplayUpTo(CharacterIterator iter, int start, int limit) {
AttributedCharacterIterator aci = null;
if (iter instanceof AttributedCharacterIterator) {
aci = (AttributedCharacterIterator)iter;
}
char c = iter.setIndex(start);
int currentIndex = start;
while (c != iter.DONE && currentIndex < limit) {
boolean foundMatchingGlyph = false;
for (int i = 0; i < glyphUnicodes.length; i++) {
if (glyphUnicodes[i].indexOf(c) == 0
&& languageMatches(glyphLangs[i])
&& orientationMatches(glyphOrientations[i])
&& formMatches(glyphUnicodes[i], glyphForms[i], aci, currentIndex)) { // found a possible match
if (glyphUnicodes[i].length() == 1) { // not a ligature
foundMatchingGlyph = true;
break;
} else {
// glyphCodes[i] is a ligature so try and
// match the rest of the glyphCode chars
boolean matched = true;
for (int j = 1; j < glyphUnicodes[i].length(); j++) {
c = iter.next();
if (glyphUnicodes[i].charAt(j) != c) {
matched = false;
break;
}
}
if (matched) { // found a matching ligature!
foundMatchingGlyph = true;
break;
} else {
// did not match ligature, keep looking for another glyph
c = iter.setIndex(currentIndex);
}
}
}
}
if (!foundMatchingGlyph) {
return currentIndex;
}
c = iter.next();
currentIndex = iter.getIndex();
}
return -1;
}
/**
* Checks whether or not this font can display the characters in the
* specified String.
*
* @param str The string containing the characters to check.
*
* @return The index of the first character it can't display or -1 if
* it can display the whole string.
*/
public int canDisplayUpTo(String str) {
StringCharacterIterator sci = new StringCharacterIterator(str);
return canDisplayUpTo(sci, 0, str.length());
}
/**
* Returns a new GVTGlyphVector object for the specified array of
* characters.
*
* @param frc The current font render context.
* @param chars The array of chars that the glyph vector will represent.
*
* @return The new glyph vector.
*/
public GVTGlyphVector createGlyphVector(FontRenderContext frc,
char[] chars) {
StringCharacterIterator sci = new StringCharacterIterator(new String(chars));
return createGlyphVector(frc, sci);
}
/**
* Returns a new GVTGlyphVector object for the characters in the
* specified character iterator.
*
* @param frc The current font render context.
* @param ci The character iterator that the glyph vector will represent.
*
* @return The new glyph vector.
*/
public GVTGlyphVector createGlyphVector(FontRenderContext frc,
CharacterIterator ci) {
AttributedCharacterIterator aci = null;
if (ci instanceof AttributedCharacterIterator) {
aci = (AttributedCharacterIterator)ci;
}
Vector glyphs = new Vector();
char c = ci.first();
while (c != ci.DONE) {
boolean foundMatchingGlyph = false;
for (int i = 0; i < glyphUnicodes.length; i++) {
if (glyphUnicodes[i].indexOf(c) == 0
&& languageMatches(glyphLangs[i])
&& orientationMatches(glyphOrientations[i])
&& formMatches(glyphUnicodes[i], glyphForms[i], aci, ci.getIndex())) { // found a possible match
if (glyphUnicodes[i].length() == 1) { // not a ligature
Element glyphElement = glyphElements[i];
SVGGlyphElementBridge glyphBridge
= (SVGGlyphElementBridge)ctx.getBridge(glyphElement);
Glyph glyph;
if (aci != null) {
aci.setIndex(ci.getIndex());
Paint fillPaint = (Paint)aci.getAttribute(TextAttribute.FOREGROUND);
Paint strokePaint = (Paint)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.STROKE_PAINT);
Stroke stroke = (Stroke)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.STROKE);
glyph = glyphBridge.createGlyph(
ctx, glyphElement, textElement, i, fontSize, fontFace, fillPaint, strokePaint, stroke);
} else {
glyph = glyphBridge.createGlyph(
ctx, glyphElement, textElement, i, fontSize, fontFace, null, null, null);
}
glyphs.add(glyph);
foundMatchingGlyph = true;
break;
} else {
// glyphCodes[i] is a ligature so try and
// match the rest of the glyphCode chars
int current = ci.getIndex();
boolean matched = true;
for (int j = 1; j < glyphUnicodes[i].length(); j++) {
c = ci.next();
if (glyphUnicodes[i].charAt(j) != c) {
matched = false;
break;
}
}
if (matched) { // found a matching ligature!
Element glyphElement = glyphElements[i];
SVGGlyphElementBridge glyphBridge
= (SVGGlyphElementBridge)ctx.getBridge(glyphElement);
Glyph glyph;
if (aci != null) {
aci.setIndex(ci.getIndex());
Paint fillPaint = (Paint)aci.getAttribute(TextAttribute.FOREGROUND);
Paint strokePaint = (Paint)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.STROKE_PAINT);
Stroke stroke = (Stroke)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.STROKE);
glyph = glyphBridge.createGlyph(
ctx, glyphElement, textElement, i, fontSize, fontFace, fillPaint, strokePaint, stroke);
} else {
glyph = glyphBridge.createGlyph(
ctx, glyphElement, textElement, i, fontSize, fontFace, null, null, null);
}
glyphs.add(glyph);
foundMatchingGlyph = true;
break;
} else {
// did not match ligature, keep looking for another glyph
c = ci.setIndex(current);
}
}
}
}
if (!foundMatchingGlyph) {
// add the missing glyph
SVGGlyphElementBridge glyphBridge
= (SVGGlyphElementBridge)ctx.getBridge(missingGlyphElement);
Glyph glyph;
if (aci != null) {
aci.setIndex(ci.getIndex());
Paint fillPaint = (Paint)aci.getAttribute(TextAttribute.FOREGROUND);
Paint strokePaint = (Paint)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.STROKE_PAINT);
Stroke stroke = (Stroke)aci.getAttribute(GVTAttributedCharacterIterator.TextAttribute.STROKE);
glyph = glyphBridge.createGlyph(
ctx, missingGlyphElement, textElement, -1, fontSize,
fontFace, fillPaint, strokePaint, stroke);
} else {
glyph = glyphBridge.createGlyph(
ctx, missingGlyphElement, textElement, -1, fontSize,
fontFace, null, null, null);
}
glyphs.add(glyph);
}
c = ci.next();
}
// turn the vector of glyphs into an array;
int numGlyphs = glyphs.size();
Glyph[] glyphArray = new Glyph[numGlyphs];
for (int i =0; i < numGlyphs; i++) {
glyphArray[i] = (Glyph)glyphs.get(i);
}
// return a new SVGGVTGlyphVector
return new SVGGVTGlyphVector(this, glyphArray, frc);
}
/**
* Returns a new GVTGlyphVector object for the glyphs in the
* the glyph code array.
*
* @param frc The current font render context.
* @param glyphCodes An array containin the ids of the glyphs that
* the glyph vector will represent.
*
* @return The new glyph vector.
*/
public GVTGlyphVector createGlyphVector(FontRenderContext frc,
int[] glyphCodes,
CharacterIterator ci) {
// costruct a string from the glyphCodes
String str = "";
for (int i = 0; i < glyphCodes.length; i++) {
str += glyphUnicodes[glyphCodes[i]];
}
StringCharacterIterator sci = new StringCharacterIterator(str);
return createGlyphVector(frc, sci);
}
/**
* Returns a new GVTGlyphVector object for the specified String.
*
* @param frc The current font render context.
* @param str The string that the glyph vector will represent.
*
* @return The new glyph vector.
*/
public GVTGlyphVector createGlyphVector(FontRenderContext frc, String str) {
StringCharacterIterator sci = new StringCharacterIterator(str);
return createGlyphVector(frc, sci);
}
/**
* Creates a new GVTFont object by replicating this font object and
* applying a new size to it.
*
* @param size The size of the new font.
*
* @param return The new font object.
*/
public GVTFont deriveFont(float size) {
return new SVGGVTFont(size, fontFace, glyphUnicodes, glyphNames,
glyphLangs, glyphOrientations, glyphForms, ctx,
glyphElements, missingGlyphElement, hkernElements,
vkernElements, textElement);
}
/**
* Returns the line metrics for the specified text.
*
* @param chars The character array containing the text.
* @param beginIndex The index of the first character.
* @param limit The limit of characters.
* @param frc The current font render context.
*
* @return The new GVTLineMetrics object.
*/
public GVTLineMetrics getLineMetrics(char[] chars, int beginIndex, int limit,
FontRenderContext frc) {
StringCharacterIterator sci = new StringCharacterIterator(new String(chars));
return getLineMetrics(sci, beginIndex, limit, frc);
}
/**
* Returns the line metrics for the specified text.
*
* @param ci The character iterator containing the text.
* @param beginIndex The index of the first character.
* @param limit The limit of characters.
* @param frc The current font render context.
*
* @return The new GVTLineMetrics object.
*/
public GVTLineMetrics getLineMetrics(CharacterIterator ci, int beginIndex,
int limit, FontRenderContext frc) {
// first create the character iterator that represents the subset
// from beginIndex to limit
String s = "";
char c = ci.setIndex(beginIndex);
int currentIndex = beginIndex;
while (c != ci.DONE && currentIndex < limit) {
s += c;
currentIndex++;
c = ci.next();
}
StringCharacterIterator sci = new StringCharacterIterator(s);
GVTGlyphVector gv = createGlyphVector(frc, sci);
float fontHeight = fontFace.getUnitsPerEm();
float scale = fontSize/fontHeight;
float ascent = fontFace.getAscent() * scale;
float descent = fontFace.getDescent() * scale;
int numGlyphs = gv.getNumGlyphs();
float[] baselineOffsets = new float[numGlyphs];
for (int i = 0; i < numGlyphs; i++) {
baselineOffsets[i] = (float)( gv.getGlyphMetrics(i).getBounds2D().getMaxY()
- gv.getGlyphPosition(i).getY());
}
float strikethroughOffset = fontFace.getStrikethroughPosition() * -scale;
float strikethroughThickness = fontFace.getStrikethroughThickness() * scale;
float underlineOffset = fontFace.getUnderlinePosition() * scale;
float underlineThickness = fontFace.getUnderlineThickness() * scale;
float overlineOffset = fontFace.getOverlinePosition() * -scale;
float overlineThickness = fontFace.getOverlineThickness() * scale;
return new GVTLineMetrics(ascent, Font.ROMAN_BASELINE, baselineOffsets, descent,
fontHeight, fontHeight, numGlyphs, strikethroughOffset,
strikethroughThickness, underlineOffset,
underlineThickness, overlineOffset, overlineThickness);
}
/**
* Returns the line metrics for the specified text.
*
* @param str The string containing the text.
* @param frc The current font render context.
*
* @return The new GVTLineMetrics object.
*/
public GVTLineMetrics getLineMetrics(String str, FontRenderContext frc) {
StringCharacterIterator sci = new StringCharacterIterator(str);
return getLineMetrics(sci, 0, str.length(), frc);
}
/**
* Returns the line metrics for the specified text.
*
* @param str The string containing the text.
* @param beginIndex The index of the first character.
* @param limit The limit of characters.
* @param frc The current font render context.
*
* @return The new GVTLineMetrics object.
*/
public GVTLineMetrics getLineMetrics(String str, int beginIndex, int limit,
FontRenderContext frc) {
StringCharacterIterator sci = new StringCharacterIterator(str);
return getLineMetrics(sci, beginIndex, limit, frc);
}
/**
* Returns the size of this font.
*
* @return The font size.
*/
public float getSize() {
return fontSize;
}
/**
* Returns a string representation of this font.
* This is for debugging purposes only.
*
* @return A string representation of this font.
*/
public String toString() {
return fontFace.getFamilyName() + " " + fontFace.getFontWeight() + " "
+ fontFace.getFontStyle();
}
}