001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Ilya S. Okomin
019: * @version $Revision$
020: */package org.apache.harmony.awt.gl.font;
021:
022: import java.awt.font.FontRenderContext;
023: import java.awt.font.LineMetrics;
024: import java.awt.geom.AffineTransform;
025: import java.awt.geom.Rectangle2D;
026:
027: import org.apache.harmony.awt.gl.font.FontPeerImpl;
028: import org.apache.harmony.awt.gl.font.FontProperty;
029:
030: /**
031: * CompositeFont class is the implementation of logical font classes.
032: * Every logical font consists of several physical fonts that described
033: * in font.properties file according to the face name of this logical font.
034: */
035: public class CompositeFont extends FontPeerImpl {
036:
037: // a number of physical fonts that CompositeFont consist of
038: int numFonts;
039:
040: // font family name
041: String family;
042:
043: // font face name
044: String face;
045:
046: String[] fontNames;
047:
048: // an array of font properties applicable to this CompositeFont
049: FontProperty[] fontProperties;
050:
051: // an array of font peers applicable to this CompositeFont
052: public FontPeerImpl[] fPhysicalFonts;
053:
054: // missing glyph code field
055: int missingGlyphCode = -1;
056:
057: // line metrics of this font
058: LineMetricsImpl nlm = null;
059:
060: // cached num glyphs parameter of this font that is the sum of num glyphs of
061: // font peers composing this font
062: int cachedNumGlyphs = -1;
063:
064: /**
065: * Creates CompositeFont object that is corresponding to the specified logical
066: * family name.
067: *
068: * @param familyName logical family name CompositeFont is to be created from
069: * @param faceName logical face name CompositeFont is to be created from
070: * @param _style style of the CompositeFont to be created
071: * @param _size size of the CompositeFont to be created
072: * @param fProperties an array of FontProperties describing physical fonts -
073: * parts of logical font
074: * @param physFonts an array of physical font peers related to the CompositeFont
075: * to be created
076: */
077: public CompositeFont(String familyName, String faceName,
078: int _style, int _size, FontProperty[] fProperties,
079: FontPeerImpl[] physFonts) {
080: this .size = _size;
081: this .name = faceName;
082: this .family = familyName;
083: this .style = _style;
084: this .face = faceName;
085: this .psName = faceName;
086: this .fontProperties = fProperties;// !! Supposed that fProperties parameter != null
087: fPhysicalFonts = physFonts;
088: numFonts = fPhysicalFonts.length;
089: setDefaultLineMetrics("", null); //$NON-NLS-1$
090: this .uniformLM = false;
091: }
092:
093: /**
094: * Returns the index of the FontPeer in array of physical fonts that is applicable
095: * for the given character. This font has to have the highest priority among fonts
096: * that can display this character and don't have exclusion range covering
097: * specified character. If there is no desired fonts -1 is returned.
098: *
099: * @param chr specified character
100: * @return index of the font from the array of physical fonts that will be used
101: * during processing of the specified character.
102: */
103: public int getCharFontIndex(char chr) {
104: for (int i = 0; i < numFonts; i++) {
105: if (fontProperties[i].isCharExcluded(chr)) {
106: continue;
107: }
108: if (fPhysicalFonts[i].canDisplay(chr)) {
109: return i;
110: }
111: }
112:
113: return -1;
114: }
115:
116: /**
117: * Returns the index of the FontPeer in array of physical fonts that is applicable
118: * for the given character. This font has to have the highest priority among fonts
119: * that can display this character and don't have exclusion range covering
120: * specified character. If there is no desired fonts default value is returned.
121: *
122: * @param chr specified character
123: * @param defaultValue default index that is returned if the necessary font couldn't be found.
124: * @return index of the font from the array of physical fonts that will be used
125: * during processing of the specified character.
126: */
127: public int getCharFontIndex(char chr, int defaultValue) {
128: for (int i = 0; i < numFonts; i++) {
129: if (fontProperties[i].isCharExcluded(chr)) {
130: continue;
131: }
132: if (fPhysicalFonts[i].canDisplay(chr)) {
133: return i;
134: }
135: }
136:
137: return defaultValue;
138: }
139:
140: /**
141: * Returns true if one of the physical fonts composing this font CompositeFont
142: * can display specified character.
143: *
144: * @param chr specified character
145: */
146: @Override
147: public boolean canDisplay(char chr) {
148: return (getCharFontIndex(chr) != -1);
149: }
150:
151: /**
152: * Returns logical ascent (in pixels)
153: */
154: @Override
155: public int getAscent() {
156: return nlm.getLogicalAscent();
157: }
158:
159: /**
160: * Returns LineMetrics instance scaled according to the specified transform.
161: *
162: * @param str specified String
163: * @param frc specified FontRenderContext
164: * @param at specified AffineTransform
165: */
166: @Override
167: public LineMetrics getLineMetrics(String str,
168: FontRenderContext frc, AffineTransform at) {
169: AffineTransform frcAt = null;
170: LineMetricsImpl lm = (LineMetricsImpl) (this .nlm.clone());
171: lm.setNumChars(str.length());
172: if (frc != null)
173: frcAt = frc.getTransform();
174:
175: if ((at != null) && (!at.isIdentity())) {
176: if (frcAt != null)
177: at.concatenate(frcAt);
178: lm.scale((float) at.getScaleX(), (float) at.getScaleY());
179: } else if ((frcAt != null) && (!frcAt.isIdentity())) {
180: lm.scale((float) frcAt.getScaleX(), (float) frcAt
181: .getScaleY());
182: }
183:
184: return lm;
185: }
186:
187: /**
188: * Returns cached LineMetrics instance for the null string or creates it if
189: * it wasn't cached yet.
190: */
191: @Override
192: public LineMetrics getLineMetrics() {
193: if (nlm == null) {
194: setDefaultLineMetrics("", null); //$NON-NLS-1$
195: }
196:
197: return this .nlm;
198: }
199:
200: /**
201: * Creates LineMetrics instance and set cached LineMetrics field to it.
202: * Created LineMetrics has maximum values of the idividual metrics of all
203: * composing physical fonts. If there is only one physical font - it's
204: * LineMetrics object is returned.
205: *
206: * @param str specified String
207: * @param frc specified FontRenderContext
208: */
209: private void setDefaultLineMetrics(String str, FontRenderContext frc) {
210: LineMetrics lm = fPhysicalFonts[0].getLineMetrics(str, frc,
211: null);
212: float maxCharWidth = (float) fPhysicalFonts[0]
213: .getMaxCharBounds(frc).getWidth();
214:
215: if (numFonts == 1) {
216: this .nlm = (LineMetricsImpl) lm;
217: return;
218: }
219:
220: float[] baselineOffsets = lm.getBaselineOffsets();
221: int numChars = str.length();
222:
223: // XXX: default value - common for all Fonts
224: int baseLineIndex = lm.getBaselineIndex();
225:
226: float maxUnderlineThickness = lm.getUnderlineThickness();
227: float maxUnderlineOffset = lm.getUnderlineOffset();
228: float maxStrikethroughThickness = lm
229: .getStrikethroughThickness();
230: float minStrikethroughOffset = lm.getStrikethroughOffset();
231: float maxLeading = lm.getLeading(); // External leading
232: float maxHeight = lm.getHeight(); // Height of the font ( == (ascent + descent + leading))
233: float maxAscent = lm.getAscent(); // Ascent of the font
234: float maxDescent = lm.getDescent(); // Descent of the font
235:
236: for (int i = 1; i < numFonts; i++) {
237: lm = fPhysicalFonts[i].getLineMetrics(str, frc, null);
238: if (maxUnderlineThickness < lm.getUnderlineThickness()) {
239: maxUnderlineThickness = lm.getUnderlineThickness();
240: }
241:
242: if (maxUnderlineOffset < lm.getUnderlineOffset()) {
243: maxUnderlineOffset = lm.getUnderlineOffset();
244: }
245:
246: if (maxStrikethroughThickness < lm
247: .getStrikethroughThickness()) {
248: maxStrikethroughThickness = lm
249: .getStrikethroughThickness();
250: }
251:
252: if (minStrikethroughOffset > lm.getStrikethroughOffset()) {
253: minStrikethroughOffset = lm.getStrikethroughOffset();
254: }
255:
256: if (maxLeading < lm.getLeading()) {
257: maxLeading = lm.getLeading();
258: }
259:
260: if (maxAscent < lm.getAscent()) {
261: maxAscent = lm.getAscent();
262: }
263:
264: if (maxDescent < lm.getDescent()) {
265: maxDescent = lm.getDescent();
266: }
267:
268: float width = (float) fPhysicalFonts[i].getMaxCharBounds(
269: frc).getWidth();
270: if (maxCharWidth < width) {
271: maxCharWidth = width;
272: }
273: for (int j = 0; j < baselineOffsets.length; j++) {
274: float[] offsets = lm.getBaselineOffsets();
275: if (baselineOffsets[j] > offsets[j]) {
276: baselineOffsets[j] = offsets[j];
277: }
278: }
279:
280: }
281: maxHeight = maxAscent + maxDescent + maxLeading;
282:
283: this .nlm = new LineMetricsImpl(numChars, baseLineIndex,
284: baselineOffsets, maxUnderlineThickness,
285: maxUnderlineOffset, maxStrikethroughThickness,
286: minStrikethroughOffset, maxLeading, maxHeight,
287: maxAscent, maxDescent, maxCharWidth);
288:
289: }
290:
291: /**
292: * Returns the number of glyphs in this CompositeFont object.
293: */
294: @Override
295: public int getNumGlyphs() {
296: if (this .cachedNumGlyphs == -1) {
297:
298: this .cachedNumGlyphs = 0;
299:
300: for (int i = 0; i < numFonts; i++) {
301: this .cachedNumGlyphs += fPhysicalFonts[i]
302: .getNumGlyphs();
303: }
304: }
305:
306: return this .cachedNumGlyphs;
307: }
308:
309: /**
310: * Returns the italic angle of this object.
311: */
312: @Override
313: public float getItalicAngle() {
314: // !! only first physical font used to get this value
315: return fPhysicalFonts[0].getItalicAngle();
316: }
317:
318: /**
319: * Returns rectangle that bounds the specified string in terms of composite line metrics.
320: *
321: * @param chars an array of chars
322: * @param start the initial offset in array of chars
323: * @param end the end offset in array of chars
324: * @param frc specified FontRenderContext
325: */
326: public Rectangle2D getStringBounds(char[] chars, int start,
327: int end, FontRenderContext frc) {
328:
329: if (nlm == null) {
330: setDefaultLineMetrics("", frc); //$NON-NLS-1$
331: }
332:
333: LineMetrics lm = nlm;
334: float minY = -lm.getAscent();
335: float minX = 0;
336: float height = lm.getHeight();
337: float width = 0;
338:
339: for (int i = start; i < end; i++) {
340: width += charWidth(chars[i]);
341: }
342:
343: Rectangle2D rect2D = new Rectangle2D.Float(minX, minY, width,
344: height);
345: return rect2D;
346:
347: }
348:
349: /**
350: * Returns maximum rectangle that encloses all maximum char bounds of
351: * physical fonts composing this CompositeFont.
352: *
353: * @param frc specified FontRenderContext
354: */
355: @Override
356: public Rectangle2D getMaxCharBounds(FontRenderContext frc) {
357:
358: Rectangle2D rect2D = fPhysicalFonts[0].getMaxCharBounds(frc);
359: float minY = (float) rect2D.getY();
360: float maxWidth = (float) rect2D.getWidth();
361: float maxHeight = (float) rect2D.getHeight();
362: if (numFonts == 1) {
363: return rect2D;
364: }
365:
366: for (int i = 1; i < numFonts; i++) {
367: if (fPhysicalFonts[i] != null) {
368: rect2D = fPhysicalFonts[i].getMaxCharBounds(frc);
369: float y = (float) rect2D.getY();
370: float mWidth = (float) rect2D.getWidth();
371: float mHeight = (float) rect2D.getHeight();
372: if (y < minY) {
373: minY = y;
374: }
375: if (mWidth > maxWidth) {
376: maxHeight = mWidth;
377: }
378:
379: if (mHeight > maxHeight) {
380: maxHeight = mHeight;
381: }
382: }
383: }
384:
385: rect2D = new Rectangle2D.Float(0, minY, maxWidth, maxHeight);
386:
387: return rect2D;
388: }
389:
390: /**
391: * Returns font name.
392: */
393: @Override
394: public String getFontName() {
395: return face;
396: }
397:
398: /**
399: * Returns font postscript name.
400: */
401: @Override
402: public String getPSName() {
403: return psName;
404: }
405:
406: /**
407: * Returns font family name.
408: */
409: @Override
410: public String getFamily() {
411: return family;
412: }
413:
414: /**
415: * Returns the code of the missing glyph.
416: */
417: @Override
418: public int getMissingGlyphCode() {
419: // !! only first physical font used to get this value
420: return fPhysicalFonts[0].getMissingGlyphCode();
421: }
422:
423: /**
424: * Returns Glyph object corresponding to the specified character.
425: *
426: * @param ch specified char
427: */
428: @Override
429: public Glyph getGlyph(char ch) {
430: for (int i = 0; i < numFonts; i++) {
431: if (fontProperties[i].isCharExcluded(ch)) {
432: continue;
433: }
434:
435: /* Control symbols considered to be supported by the font peer */
436: if ((ch < 0x20) || fPhysicalFonts[i].canDisplay(ch)) {
437: return fPhysicalFonts[i].getGlyph(ch);
438: }
439: }
440: return getDefaultGlyph();
441: }
442:
443: /**
444: * Returns width of the char with specified index.
445: *
446: * @param ind specified index of the character
447: */
448: @Override
449: public int charWidth(int ind) {
450: return charWidth((char) ind);
451: }
452:
453: /**
454: * Returns width of the specified char.
455: *
456: * @param c specified character
457: */
458: @Override
459: public int charWidth(char c) {
460: Glyph gl = this .getGlyph(c);
461: return (int) gl.getGlyphPointMetrics().getAdvanceX();
462: }
463:
464: /**
465: * Returns debug information about this class.
466: */
467: @Override
468: public String toString() {
469: return new String(this .getClass().getName()
470: + "[name=" + this .name + //$NON-NLS-1$
471: ",style=" + this .style + //$NON-NLS-1$
472: ",fps=" + this .fontProperties + "]"); //$NON-NLS-1$ //$NON-NLS-2$
473: }
474:
475: /**
476: * Returns Glyph object corresponding to the default glyph.
477: */
478: @Override
479: public Glyph getDefaultGlyph() {
480: // !! only first physical font used to get this value
481: return fPhysicalFonts[0].getDefaultGlyph();
482: }
483:
484: /**
485: * Returns FontExtraMetrics object with extra metrics
486: * related to this CompositeFont.
487: */
488: @Override
489: public FontExtraMetrics getExtraMetrics() {
490: // Returns FontExtraMetrics instance of the first physical
491: // Font from the array of fonts.
492: return fPhysicalFonts[0].getExtraMetrics();
493: }
494:
495: /**
496: * Disposes CompositeFont object's resources.
497: */
498: @Override
499: public void dispose() {
500: // Nothing to dispose
501: }
502: }
|