blob: 70cb3341b2245804fb1a1460fe2a1dc3966e40a6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Ilya S. Okomin
* @version $Revision$
*/
package org.apache.harmony.awt.gl.font;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import org.apache.harmony.awt.gl.font.FontPeerImpl;
import org.apache.harmony.awt.gl.font.FontProperty;
/**
* CompositeFont class is the implementation of logical font classes.
* Every logical font consists of several physical fonts that described
* in font.properties file according to the face name of this logical font.
*/
public class CompositeFont extends FontPeerImpl{
// a number of physical fonts that CompositeFont consist of
int numFonts;
// font family name
String family;
// font face name
String face;
String[] fontNames;
// an array of font properties applicable to this CompositeFont
FontProperty[] fontProperties;
// an array of font peers applicable to this CompositeFont
public FontPeerImpl[] fPhysicalFonts;
// missing glyph code field
int missingGlyphCode = -1;
// line metrics of this font
LineMetricsImpl nlm = null;
// cached num glyphs parameter of this font that is the sum of num glyphs of
// font peers composing this font
int cachedNumGlyphs = -1;
/**
* Creates CompositeFont object that is corresponding to the specified logical
* family name.
*
* @param familyName logical family name CompositeFont is to be created from
* @param faceName logical face name CompositeFont is to be created from
* @param _style style of the CompositeFont to be created
* @param _size size of the CompositeFont to be created
* @param fProperties an array of FontProperties describing physical fonts -
* parts of logical font
* @param physFonts an array of physical font peers related to the CompositeFont
* to be created
*/
public CompositeFont(String familyName, String faceName, int _style, int _size, FontProperty[] fProperties, FontPeerImpl[] physFonts){
this.size = _size;
this.name = faceName;
this.family = familyName;
this.style = _style;
this.face = faceName;
this.psName = faceName;
this.fontProperties = fProperties;// !! Supposed that fProperties parameter != null
fPhysicalFonts = physFonts;
numFonts = fPhysicalFonts.length;
setDefaultLineMetrics("", null); //$NON-NLS-1$
this.uniformLM = false;
}
/**
* Returns the index of the FontPeer in array of physical fonts that is applicable
* for the given character. This font has to have the highest priority among fonts
* that can display this character and don't have exclusion range covering
* specified character. If there is no desired fonts -1 is returned.
*
* @param chr specified character
* @return index of the font from the array of physical fonts that will be used
* during processing of the specified character.
*/
public int getCharFontIndex(char chr){
for (int i = 0; i < numFonts; i++){
if (fontProperties[i].isCharExcluded(chr)){
continue;
}
if (fPhysicalFonts[i].canDisplay(chr)){
return i;
}
}
return -1;
}
/**
* Returns the index of the FontPeer in array of physical fonts that is applicable
* for the given character. This font has to have the highest priority among fonts
* that can display this character and don't have exclusion range covering
* specified character. If there is no desired fonts default value is returned.
*
* @param chr specified character
* @param defaultValue default index that is returned if the necessary font couldn't be found.
* @return index of the font from the array of physical fonts that will be used
* during processing of the specified character.
*/
public int getCharFontIndex(char chr, int defaultValue){
for (int i = 0; i < numFonts; i++){
if (fontProperties[i].isCharExcluded(chr)){
continue;
}
if (fPhysicalFonts[i].canDisplay(chr)){
return i;
}
}
return defaultValue;
}
/**
* Returns true if one of the physical fonts composing this font CompositeFont
* can display specified character.
*
* @param chr specified character
*/
@Override
public boolean canDisplay(char chr){
return (getCharFontIndex(chr) != -1);
}
/**
* Returns logical ascent (in pixels)
*/
@Override
public int getAscent(){
return nlm.getLogicalAscent();
}
/**
* Returns LineMetrics instance scaled according to the specified transform.
*
* @param str specified String
* @param frc specified FontRenderContext
* @param at specified AffineTransform
*/
@Override
public LineMetrics getLineMetrics(String str, FontRenderContext frc , AffineTransform at){
LineMetricsImpl lm = (LineMetricsImpl)(this.nlm.clone());
lm.setNumChars(str.length());
if ((at != null) && (!at.isIdentity())){
lm.scale((float)at.getScaleX(), (float)at.getScaleY());
}
return lm;
}
/**
* Returns cached LineMetrics instance for the null string or creates it if
* it wasn't cached yet.
*/
@Override
public LineMetrics getLineMetrics(){
if (nlm == null){
setDefaultLineMetrics("", null); //$NON-NLS-1$
}
return this.nlm;
}
/**
* Creates LineMetrics instance and set cached LineMetrics field to it.
* Created LineMetrics has maximum values of the idividual metrics of all
* composing physical fonts. If there is only one physical font - it's
* LineMetrics object is returned.
*
* @param str specified String
* @param frc specified FontRenderContext
*/
private void setDefaultLineMetrics(String str, FontRenderContext frc){
LineMetrics lm = fPhysicalFonts[0].getLineMetrics(str, frc, null);
float maxCharWidth = (float)fPhysicalFonts[0].getMaxCharBounds(frc).getWidth();
if (numFonts == 1) {
this.nlm = (LineMetricsImpl)lm;
return;
}
float[] baselineOffsets = lm.getBaselineOffsets();
int numChars = str.length();
// XXX: default value - common for all Fonts
int baseLineIndex = lm.getBaselineIndex();
float maxUnderlineThickness = lm.getUnderlineThickness();
float maxUnderlineOffset = lm.getUnderlineOffset();
float maxStrikethroughThickness = lm.getStrikethroughThickness();
float minStrikethroughOffset = lm.getStrikethroughOffset();
float maxLeading = lm.getLeading(); // External leading
float maxHeight = lm.getHeight(); // Height of the font ( == (ascent + descent + leading))
float maxAscent = lm.getAscent(); // Ascent of the font
float maxDescent = lm.getDescent(); // Descent of the font
for (int i = 1; i < numFonts; i++){
lm = fPhysicalFonts[i].getLineMetrics(str, frc, null);
if (maxUnderlineThickness < lm.getUnderlineThickness()){
maxUnderlineThickness = lm.getUnderlineThickness();
}
if (maxUnderlineOffset < lm.getUnderlineOffset()){
maxUnderlineOffset = lm.getUnderlineOffset();
}
if (maxStrikethroughThickness < lm.getStrikethroughThickness()){
maxStrikethroughThickness = lm.getStrikethroughThickness();
}
if (minStrikethroughOffset > lm.getStrikethroughOffset()){
minStrikethroughOffset = lm.getStrikethroughOffset();
}
if (maxLeading < lm.getLeading()){
maxLeading = lm.getLeading();
}
if (maxAscent < lm.getAscent()){
maxAscent = lm.getAscent();
}
if (maxDescent < lm.getDescent()){
maxDescent = lm.getDescent();
}
float width = (float)fPhysicalFonts[i].getMaxCharBounds(frc).getWidth();
if(maxCharWidth < width){
maxCharWidth = width;
}
for (int j =0; j < baselineOffsets.length; j++){
float[] offsets = lm.getBaselineOffsets();
if (baselineOffsets[j] > offsets[j]){
baselineOffsets[j] = offsets[j];
}
}
}
maxHeight = maxAscent + maxDescent + maxLeading;
this.nlm = new LineMetricsImpl(
numChars,
baseLineIndex,
baselineOffsets,
maxUnderlineThickness,
maxUnderlineOffset,
maxStrikethroughThickness,
minStrikethroughOffset,
maxLeading,
maxHeight,
maxAscent,
maxDescent,
maxCharWidth);
}
/**
* Returns the number of glyphs in this CompositeFont object.
*/
@Override
public int getNumGlyphs(){
if (this.cachedNumGlyphs == -1){
this.cachedNumGlyphs = 0;
for (int i = 0; i < numFonts; i++){
this.cachedNumGlyphs += fPhysicalFonts[i].getNumGlyphs();
}
}
return this.cachedNumGlyphs;
}
/**
* Returns the italic angle of this object.
*/
@Override
public float getItalicAngle(){
// !! only first physical font used to get this value
return fPhysicalFonts[0].getItalicAngle();
}
/**
* Returns rectangle that bounds the specified string in terms of composite line metrics.
*
* @param chars an array of chars
* @param start the initial offset in array of chars
* @param end the end offset in array of chars
* @param frc specified FontRenderContext
*/
public Rectangle2D getStringBounds(char[] chars, int start, int end, FontRenderContext frc){
LineMetrics lm = getLineMetrics();
float minY = -lm.getAscent();
float minX = 0;
float height = lm.getHeight();
float width = 0;
for (int i = start; i < end; i++){
width += charWidth(chars[i]);
}
Rectangle2D rect2D = new Rectangle2D.Float(minX, minY, width, height);
return rect2D;
}
/**
* Returns maximum rectangle that encloses all maximum char bounds of
* physical fonts composing this CompositeFont.
*
* @param frc specified FontRenderContext
*/
@Override
public Rectangle2D getMaxCharBounds(FontRenderContext frc){
Rectangle2D rect2D = fPhysicalFonts[0].getMaxCharBounds(frc);
float minY = (float)rect2D.getY();
float maxWidth = (float)rect2D.getWidth();
float maxHeight = (float)rect2D.getHeight();
if (numFonts == 1){
return rect2D;
}
for (int i = 1; i < numFonts; i++){
if (fPhysicalFonts[i] != null){
rect2D = fPhysicalFonts[i].getMaxCharBounds(frc);
float y = (float)rect2D.getY();
float mWidth = (float)rect2D.getWidth();
float mHeight = (float)rect2D.getHeight();
if (y < minY){
minY = y;
}
if (mWidth > maxWidth){
maxHeight = mWidth;
}
if (mHeight > maxHeight){
maxHeight = mHeight;
}
}
}
rect2D = new Rectangle2D.Float(0, minY, maxWidth, maxHeight);
return rect2D;
}
/**
* Returns font name.
*/
@Override
public String getFontName(){
return face;
}
/**
* Returns font postscript name.
*/
@Override
public String getPSName(){
return psName;
}
/**
* Returns font family name.
*/
@Override
public String getFamily(){
return family;
}
/**
* Returns the code of the missing glyph.
*/
@Override
public int getMissingGlyphCode(){
// !! only first physical font used to get this value
return fPhysicalFonts[0].getMissingGlyphCode();
}
/**
* Returns Glyph object corresponding to the specified character.
*
* @param ch specified char
*/
@Override
public Glyph getGlyph(char ch){
for (int i = 0; i < numFonts; i++){
if (fontProperties[i].isCharExcluded(ch)){
continue;
}
/* Control symbols considered to be supported by the font peer */
if ((ch < 0x20) || fPhysicalFonts[i].canDisplay(ch)){
return fPhysicalFonts[i].getGlyph(ch);
}
}
return getDefaultGlyph();
}
/**
* Returns width of the char with specified index.
*
* @param ind specified index of the character
*/
@Override
public int charWidth(int ind){
return charWidth((char)ind);
}
/**
* Returns width of the specified char.
*
* @param c specified character
*/
@Override
public int charWidth(char c){
Glyph gl = this.getGlyph(c);
return (int)gl.getGlyphPointMetrics().getAdvanceX();
}
/**
* Returns debug information about this class.
*/
@Override
public String toString(){
return new String(this.getClass().getName() +
"[name=" + this.name + //$NON-NLS-1$
",style="+ this.style + //$NON-NLS-1$
",fps=" + this.fontProperties + "]"); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Returns Glyph object corresponding to the default glyph.
*/
@Override
public Glyph getDefaultGlyph(){
// !! only first physical font used to get this value
return fPhysicalFonts[0].getDefaultGlyph();
}
/**
* Returns FontExtraMetrics object with extra metrics
* related to this CompositeFont.
*/
@Override
public FontExtraMetrics getExtraMetrics(){
// Returns FontExtraMetrics instanse of the first physical
// Font from the array of fonts.
return fPhysicalFonts[0].getExtraMetrics();
}
/**
* Disposes CompositeFont object's resources.
*/
@Override
public void dispose() {
// Nothing to dispose
}
}