| /* |
| * 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 |
| } |
| } |