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;
023: import java.awt.font.FontRenderContext;
024: import java.awt.font.LineMetrics;
025: import java.awt.geom.AffineTransform;
026: import java.awt.geom.Rectangle2D;
027: import java.awt.image.BufferedImage;
028: import java.awt.image.DataBuffer;
029: import java.awt.image.IndexColorModel;
030: import java.awt.image.Raster;
031: import java.awt.image.WritableRaster;
032: import java.io.IOException;
033: import java.util.Hashtable;
034: import java.util.Locale;
035:
036: import org.apache.harmony.awt.gl.font.FontPeerImpl;
037: import org.apache.harmony.awt.gl.font.Glyph;
038: import org.apache.harmony.awt.gl.font.LineMetricsImpl;
039: import org.apache.harmony.awt.internal.nls.Messages;
040:
041: /**
042: * Windows platform font peer implementation based on GDI font object.
043: */
044: public class WindowsFont extends FontPeerImpl {
045:
046: // table with loaded cached Glyphs
047: private final Hashtable<Integer, WinGlyph> glyphs = new Hashtable<Integer, WinGlyph>();
048: // table with loaded glyph codes
049: private final Hashtable<Integer, Integer> glyphCodes = new Hashtable<Integer, Integer>();
050:
051: // Pairs of [begin, end],[..].. unicode ranges values
052: private int[] fontUnicodeRanges;
053:
054: public WindowsFont(String fontName, int fontStyle, int fontSize) {
055: this .size = fontSize;
056: this .style = fontStyle;
057: this .name = fontName;
058: this .fontFamilyName = name;
059:
060: pFont = NativeFont.initializeFont(this , fontName, fontStyle,
061: fontSize);
062:
063: initWindowsFont();
064:
065: }
066:
067: public void initWindowsFont() {
068:
069: this .fontType = NativeFont.getFontType(name, style);
070:
071: // Font Metrics init
072: getDefaultLineMetrics();
073:
074: this .ascent = nlm.getLogicalAscent();
075: this .descent = nlm.getLogicalDescent();
076: this .height = (int) nlm.getHeight();
077: this .leading = nlm.getLogicalLeading();
078: this .maxAdvance = nlm.getLogicalMaxCharWidth();
079:
080: this .maxCharBounds = new Rectangle2D.Float(0, -nlm.getAscent(),
081: nlm.getMaxCharWidth(), nlm.getHeight());
082: this .italicAngle = NativeFont.getItalicAngleNative(pFont);
083:
084: this .numGlyphs = 0;
085: //TODO: implement NativeFont.getNumGlyphs(pFont, fontType);
086:
087: if (this .fontType == FontManager.FONT_TYPE_T1) {
088: this .defaultChar = 1;
089: }
090: // this.defaultChar = NativeFont.getDefaultCharNative(pFont);
091:
092: // addGlyphs((char) 0x20, (char) 0x7E);
093: }
094:
095: /**
096: * Returns true if the specified character can be displayed by this
097: * WindowsFont.
098: * @param chr the specified character
099: */
100: @Override
101: public boolean canDisplay(char chr) {
102: return isGlyphExists(chr);
103: }
104:
105: /**
106: * Adds range of existing glyphs to this WindowsFont object
107: * @param uFirst the lowest range's bound, inclusive
108: * @param uLast the highest range's bound, exclusive
109: */
110:
111: public void addGlyphs(char uFirst, char uLast) {
112: char index = uFirst;
113: if (uLast < uFirst) {
114: // awt.09=min range bound value is grater than max range bound
115: throw new IllegalArgumentException(Messages
116: .getString("awt.09")); //$NON-NLS-1$
117: }
118: while (index < uLast) {
119: addGlyph(index);
120: index++;
121: }
122: }
123:
124: /**
125: * Add existing glyph to this WindowsFont object.
126: * @param uChar the specified character
127: * @return true if glyph of the specified character exists in this
128: * WindowsFont or this character is escape sequence character.
129: */
130: public boolean addGlyph(char uChar) {
131: boolean result = false;
132: boolean isEscape = false;
133:
134: isEscape = ((uChar == '\t') || (uChar == '\n') || (uChar == '\r'));
135:
136: if (isEscape || this .isGlyphExists(uChar)) {
137: glyphs.put(Integer.valueOf(uChar), new WinGlyph(this .pFont,
138: this .getSize(), uChar, NativeFont
139: .getGlyphCodeNative(this .pFont, uChar)));
140: result = true;
141: }
142:
143: return result;
144: }
145:
146: /**
147: * Checks whether given Glyph belongs to any font supported unicode range.
148: * @param uIndex specified character
149: * @return true if specified character in unicode ranges, false otherwise
150: */
151: public boolean isGlyphExists(char uIndex) {
152: for (int i = 0; i < fontUnicodeRanges.length - 1; i += 2) {
153: if (uIndex <= fontUnicodeRanges[i + 1]) {
154: if (uIndex >= fontUnicodeRanges[i]) {
155: return true;
156: }
157: return false;
158: }
159: }
160:
161: return false;
162: }
163:
164: /**
165: * Returns an array of unicode ranges that are supported by this WindowsFont.
166: */
167: public int[] getUnicodeRanges() {
168: int[] ranges = new int[fontUnicodeRanges.length];
169: System.arraycopy(fontUnicodeRanges, 0, ranges, 0,
170: fontUnicodeRanges.length);
171: return ranges;
172: }
173:
174: /**
175: * Initializes the array of unicode ranges that are supported by this
176: * WindowsFont with the values from the specified array.
177: * @param ranges the array of unicode ranges values
178: */
179: public void setUnicodeRanges(int[] ranges) {
180: if (ranges != null) {
181: fontUnicodeRanges = new int[ranges.length];
182: System.arraycopy(ranges, 0, fontUnicodeRanges, 0,
183: ranges.length);
184: }
185: }
186:
187: /**
188: * Returns Glyph object for the specified character in this WindowsFont.
189: * Default Glyph object returned if the specified character
190: * doesn't exist in the WindowsFont
191: * @param index the specified character
192: */
193: @Override
194: public Glyph getGlyph(char index) {
195: Glyph result = null;
196: Integer key = Integer.valueOf(index);
197: if (glyphs.containsKey(key)) {
198: result = glyphs.get(key);
199: } else {
200: if (this .addGlyph(index)) {
201: result = glyphs.get(key);
202: } else {
203: result = this .getDefaultGlyph();
204: }
205: }
206: return result;
207: }
208:
209: /**
210: * Returns default Glyph object of this WindowsFont.
211: */
212: @Override
213: public Glyph getDefaultGlyph() {
214: Glyph result;
215: Integer defaultKey = Integer.valueOf(defaultChar);
216:
217: if (glyphs.containsKey(defaultKey)) {
218: result = glyphs.get(defaultKey);
219: } else {
220: if (this .fontType == FontManager.FONT_TYPE_T1) {
221: // XXX: !! Type1 has no default glyphs
222: glyphs.put(defaultKey, new WinGlyph(defaultChar,
223: defaultChar));
224: result = glyphs.get(defaultKey);
225: } else {
226: glyphs.put(defaultKey, new WinGlyph(this .pFont, this
227: .getSize(), defaultChar, NativeFont
228: .getGlyphCodeNative(this .pFont, defaultChar)));
229: result = glyphs.get(defaultKey);
230: }
231: }
232:
233: return result;
234:
235: }
236:
237: /**
238: * Returns string image, that can be blitted onto a BufferedImageGraphics2D.
239: * @param str the specified string
240: * @return a BufferedImage object that is the representation of the rendered
241: * string
242: */
243: public BufferedImage getStringImage(String str) {
244: // !! this method isn't used now,
245: // need to be modified with proper transparency parameters
246: int height = this .ascent + this .descent;
247: int width;
248: int baseXOffset = 0; // X Offset of the Glyph cell along the base line
249: int drawXOffset = 0; // X Offset of the Glyph image along the base line
250: int drawYOffset = 0;
251: Glyph[] gls = this .getGlyphs(str);
252:
253: // total width of the glyph vector is equal to the sum of their advances -
254: // LSB of the first glyph - RSB of the last glyph
255: width = Math.max(-(int) gls[0].getGlyphPointMetrics().getLSB(),
256: 0);
257: baseXOffset = Math.max(-(int) gls[0].getGlyphPointMetrics()
258: .getLSB(), 0);
259:
260: for (Glyph element : gls) {
261: width += element.getGlyphPointMetrics().getAdvanceX();
262: }
263:
264: width += Math.max(-(int) gls[gls.length - 1]
265: .getGlyphPointMetrics().getRSB(), 0);
266:
267: WritableRaster wr = Raster.createPackedRaster(
268: DataBuffer.TYPE_BYTE, width, height, 1, 1, null);
269:
270: for (Glyph curGlyph : gls) {
271: drawYOffset = this .ascent
272: + (int) Math.ceil(curGlyph.getGlyphPointMetrics()
273: .getBounds2D().getY()) - 1;
274: drawXOffset = baseXOffset
275: + (int) Math.ceil(curGlyph.getGlyphPointMetrics()
276: .getLSB());
277:
278: wr.setDataElements(drawXOffset, drawYOffset,
279: ((WinGlyph) curGlyph).getImage().getRaster());
280:
281: baseXOffset += curGlyph.getGlyphPointMetrics()
282: .getAdvanceX();
283: }
284:
285: byte[] blackWhite = new byte[] { 0, (byte) 0xff };
286: IndexColorModel colorModel = new IndexColorModel(1, 2,
287: blackWhite, blackWhite, blackWhite);
288:
289: BufferedImage stringImage = new BufferedImage(colorModel, wr,
290: false, null);
291: return stringImage;
292: }
293:
294: // Font Dependent methods
295:
296: /**
297: * Returns locale dependent family name of this WindowsFont.
298: */
299: @Override
300: public String getFamily(Locale l) {
301: if (this .fontType == FontManager.FONT_TYPE_T1) {
302: return this .name;
303: }
304:
305: //TODO: implement
306: return this .getFamily();
307: }
308:
309: /**
310: * Returns locale dependent face name of this WindowsFont.
311: */
312: @Override
313: public String getFontName(Locale l) {
314: if (this .fontType == FontManager.FONT_TYPE_T1) {
315: return this .name;
316: }
317:
318: return this .getFontName();
319: }
320:
321: /**
322: * Returns a clone of LineMetrics object that contains metrics of this
323: * WindowsFont.
324: */
325: private LineMetricsImpl getDefaultLineMetrics() {
326: // TODO: implement baseline offsets for TrueType fonts
327: if (nlm != null) {
328: return (LineMetricsImpl) nlm.clone();
329: }
330: float[] metrics = NativeFont.getNativeLineMetrics(this
331: .getFontHandle(), this .getSize(), false, false, this
332: .getFontType());
333: int _numChars = 0;
334:
335: nlm = new LineMetricsImpl(_numChars, metrics, null);
336: return (LineMetricsImpl) nlm.clone();
337: }
338:
339: /**
340: * Returns a LineMetrics object that contains text metrics of this
341: * WindowsFont.
342: */
343: @Override
344: public LineMetrics getLineMetrics(String str,
345: FontRenderContext frc, AffineTransform at) {
346: AffineTransform frcAt = null;
347: LineMetricsImpl lm = getDefaultLineMetrics();
348: lm.setNumChars(str.length());
349:
350: if (frc != null)
351: frcAt = frc.getTransform();
352:
353: if ((at != null) && (!at.isIdentity())) {
354: if (frcAt != null)
355: at.concatenate(frcAt);
356: lm.scale((float) at.getScaleX(), (float) at.getScaleY());
357: } else if ((frcAt != null) && (!frcAt.isIdentity())) {
358: lm.scale((float) frcAt.getScaleX(), (float) frcAt
359: .getScaleY());
360: }
361:
362: return lm;
363: }
364:
365: /**
366: * Return Font object if it was successfully embedded in the system.
367: */
368: public static Font embedFont(String absolutePath)
369: throws IOException {
370: return NativeFont.embedFont(absolutePath);
371: }
372:
373: /**
374: * Dispose all native resources and deleting temporary font file
375: * if this WindowsFont object was created from stream.
376: */
377: @Override
378: public void dispose() {
379: if (pFont != 0) {
380: NativeFont.pFontFree(pFont);
381: pFont = 0;
382:
383: if (isCreatedFromStream()) {
384: NativeFont.RemoveFontResource(getTempFontFileName());
385: }
386:
387: }
388: }
389:
390: /**
391: * Returns postscript name of this WindowsFont.
392: */
393: @Override
394: public String getPSName() {
395: if (psName == null) {
396: // TODO: implement method
397: psName = getFontName();
398: }
399: return psName;
400: }
401:
402: @Override
403: public int getMissingGlyphCode() {
404: return getDefaultGlyph().getGlyphCode();
405: }
406:
407: /**
408: * Returns the advance width of the specified char of this WindowsFont.
409: * @param ind the char which width is to be returned
410: * @return the advance width of the specified char of this WindowsFont
411: */
412: @Override
413: public int charWidth(int ind) {
414: return charWidth((char) ind);
415: }
416:
417: /**
418: * Returns face name of this WindowsFont.
419: */
420: @Override
421: public String getFontName() {
422: if (faceName == null) {
423: if (this .fontType == FontManager.FONT_TYPE_T1) {
424: faceName = getFamily();
425: } else {
426: faceName = NativeFont.getFontNameNative(this .pFont);
427: }
428: }
429:
430: return faceName;
431: }
432:
433: /**
434: * Returns unicode by index from the 'cmap' table of this font.
435: */
436: @Override
437: public char getUnicodeByIndex(int glyphCode) {
438: char result;
439:
440: if (glyphCodes.isEmpty()) {
441: for (int i = 0; i < fontUnicodeRanges.length - 1; i += 2) {
442: for (int j = fontUnicodeRanges[i]; j <= fontUnicodeRanges[i + 1]; j++) {
443: int code = NativeFont.getGlyphCodeNative(pFont,
444: (char) j);
445: glyphCodes.put(code, j);
446: }
447: }
448: }
449:
450: if (glyphCodes.containsKey(glyphCode)) {
451: result = (char) glyphCodes.get(glyphCode).intValue();
452: } else {
453: result = defaultChar;
454: }
455:
456: return result;
457: }
458:
459: /**
460: * Returns initiated FontExtraMetrics instance of this WindowsFont.
461: */
462: @Override
463: public FontExtraMetrics getExtraMetrics() {
464: if (extraMetrix == null) {
465:
466: //!! for Type1 fonts 'x' char width used as average char width
467: float[] metrics = NativeFont.getExtraMetricsNative(this
468: .getFontHandle(), this .size, this .fontType);
469: if (fontType == FontManager.FONT_TYPE_T1) {
470: metrics[0] = charWidth('x');
471: }
472: extraMetrix = new FontExtraMetrics(metrics);
473: }
474: return extraMetrix;
475: }
476:
477: }
|