001: /*
002: *
003: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License version
008: * 2 only, as published by the Free Software Foundation.
009: *
010: * This program is distributed in the hope that it will be useful, but
011: * WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * General Public License version 2 for more details (a copy is
014: * included at /legal/license.txt).
015: *
016: * You should have received a copy of the GNU General Public License
017: * version 2 along with this work; if not, write to the Free Software
018: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
019: * 02110-1301 USA
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
022: * Clara, CA 95054 or visit www.sun.com if you need additional
023: * information or have any questions.
024: */
025:
026: package com.sun.pisces;
027:
028: import java.io.DataInputStream;
029: import java.io.EOFException;
030: import java.io.IOException;
031: import java.io.InputStream;
032: import java.util.Hashtable;
033:
034: class Face {
035:
036: PathStore[] paths = new PathStore[256];
037: int[] minX = new int[256];
038: int[] minY = new int[256];
039: int[] width = new int[256];
040: int[] height = new int[256];
041: double scale;
042:
043: public Face(InputStream in) throws IOException {
044: GZIPInputStream gin = new GZIPInputStream(in);
045: DataInputStream dis = new DataInputStream(gin);
046: String name = dis.readUTF();
047: String style = dis.readUTF();
048:
049: this .scale = 65536.0 / dis.readDouble();
050:
051: while (true) {
052: char glyph;
053: try {
054: glyph = dis.readChar();
055: } catch (EOFException eof) {
056: return;
057: }
058: int gx = dis.readInt();
059: int gy = dis.readInt();
060: int gwidth = dis.readInt();
061: int gheight = dis.readInt();
062: int numEntries = dis.readInt();
063:
064: PathStore ps = new PathStore(numEntries);
065:
066: int[] x = new int[4];
067: int[] y = new int[4];
068: int sx0 = 0, sy0 = 0, xp = 0, yp = 0;
069:
070: boolean prevIsQuad = false;
071: boolean prevIsCubic = false;
072:
073: while (true) {
074: char tok = dis.readChar();
075: if (tok == 'Z') {
076: ps.close();
077: ps.end();
078: break;
079: } else if (tok == 'E') {
080: ps.end();
081: break;
082: }
083:
084: int x0 = x[0];
085: int y0 = y[0];
086:
087: switch (tok) {
088: case 'M':
089: // readInts(x, 1);
090: // readInts(y, 1);
091: x[0] = dis.readInt();
092: y[0] = dis.readInt();
093: break;
094:
095: case 'm':
096: // readShorts(x, 1);
097: // readShorts(y, 1);
098: x[0] += dis.readShort();
099: y[0] += dis.readShort();
100: break;
101:
102: case 'n':
103: // readBytes(x, 1);
104: // readBytes(y, 1);
105: x[0] += dis.readByte();
106: y[0] += dis.readByte();
107: break;
108:
109: case 'H':
110: // readInts(x, 1);
111: x[0] = dis.readInt();
112: break;
113:
114: case 'h':
115: // readShorts(x, 1);
116: x[0] += dis.readShort();
117: break;
118:
119: case 'i':
120: // readBytes(x, 1);
121: x[0] += dis.readByte();
122: break;
123:
124: case 'V':
125: // readInts(y, 1);
126: y[0] = dis.readInt();
127: break;
128:
129: case 'v':
130: // readShorts(y, 1);
131: y[0] += dis.readShort();
132: break;
133:
134: case 'w':
135: // readBytes(y, 1);
136: y[0] += dis.readByte();
137: break;
138:
139: case 'L':
140: // readInts(x, 1);
141: // readInts(y, 1);
142: x[0] = dis.readInt();
143: y[0] = dis.readInt();
144: break;
145:
146: case 'l':
147: // readShorts(x, 1);
148: // readShorts(y, 1);
149: x[0] += dis.readShort();
150: y[0] += dis.readShort();
151: break;
152:
153: case 'k':
154: // readBytes(x, 1);
155: // readBytes(y, 1);
156: x[0] += dis.readByte();
157: y[0] += dis.readByte();
158: break;
159:
160: case 'Q':
161: // readInts(x, 2);
162: // readInts(y, 2);
163: x[0] = dis.readInt();
164: y[0] = dis.readInt();
165: x[1] = dis.readInt();
166: y[1] = dis.readInt();
167: break;
168:
169: case 'q':
170: // readShorts(x, 2);
171: // readShorts(y, 2);
172: x[0] = x0 + dis.readShort();
173: y[0] = y0 + dis.readShort();
174: x[1] = x0 + dis.readShort();
175: y[1] = y0 + dis.readShort();
176: break;
177:
178: case 'r':
179: // readBytes(x, 2);
180: // readBytes(y, 2);
181: x[0] = x0 + dis.readByte();
182: y[0] = y0 + dis.readByte();
183: x[1] = x0 + dis.readByte();
184: y[1] = y0 + dis.readByte();
185: break;
186:
187: case 'T':
188: x[0] = x0 + (prevIsQuad ? (x0 - xp) : 0);
189: y[0] = y0 + (prevIsQuad ? (y0 - yp) : 0);
190: x[1] = dis.readInt();
191: y[1] = dis.readInt();
192: break;
193:
194: case 't':
195: x[0] = x0 + (prevIsQuad ? (x0 - xp) : 0);
196: y[0] = y0 + (prevIsQuad ? (y0 - yp) : 0);
197: x[1] = x0 + dis.readShort();
198: y[1] = y0 + dis.readShort();
199: break;
200:
201: case 'u':
202: x[0] = x0 + (prevIsQuad ? (x0 - xp) : 0);
203: y[0] = y0 + (prevIsQuad ? (y0 - yp) : 0);
204: x[1] = x0 + dis.readByte();
205: y[1] = y0 + dis.readByte();
206: break;
207:
208: case 'C':
209: // readInts(x, 3);
210: // readInts(y, 3);
211: x[0] = dis.readInt();
212: y[0] = dis.readInt();
213: x[1] = dis.readInt();
214: y[1] = dis.readInt();
215: x[2] = dis.readInt();
216: y[2] = dis.readInt();
217: break;
218:
219: case 'c':
220: x[0] = x0 + dis.readShort();
221: y[0] = y0 + dis.readShort();
222: x[1] = x0 + dis.readShort();
223: y[1] = y0 + dis.readShort();
224: x[2] = x0 + dis.readShort();
225: y[2] = y0 + dis.readShort();
226: break;
227:
228: case 'd':
229: x[0] = x0 + dis.readByte();
230: y[0] = y0 + dis.readByte();
231: x[1] = x0 + dis.readByte();
232: y[1] = y0 + dis.readByte();
233: x[2] = x0 + dis.readByte();
234: y[2] = y0 + dis.readByte();
235: break;
236:
237: case 'S':
238: x[0] = x0 + (prevIsCubic ? (x0 - xp) : 0);
239: y[0] = y0 + (prevIsCubic ? (y0 - yp) : 0);
240: x[1] = dis.readInt();
241: y[1] = dis.readInt();
242: x[2] = dis.readInt();
243: y[2] = dis.readInt();
244: break;
245:
246: case 's':
247: x[0] = x0 + (prevIsCubic ? (x0 - xp) : 0);
248: y[0] = y0 + (prevIsCubic ? (y0 - yp) : 0);
249: x[1] = x0 + dis.readShort();
250: y[1] = y0 + dis.readShort();
251: x[2] = x0 + dis.readShort();
252: y[2] = y0 + dis.readShort();
253: break;
254:
255: case 'p':
256: x[0] = x0 + (prevIsCubic ? (x0 - xp) : 0);
257: y[0] = y0 + (prevIsCubic ? (y0 - yp) : 0);
258: x[1] = x0 + dis.readByte();
259: y[1] = y0 + dis.readByte();
260: x[2] = x0 + dis.readByte();
261: y[2] = y0 + dis.readByte();
262: break;
263: }
264:
265: switch (tok) {
266: case 'M':
267: case 'm':
268: case 'n':
269: ps.moveTo(x[0], y[0]);
270: sx0 = x[0];
271: sy0 = y[0];
272: prevIsQuad = prevIsCubic = false;
273: break;
274:
275: case 'H':
276: case 'h':
277: case 'i':
278: case 'V':
279: case 'v':
280: case 'w':
281: case 'L':
282: case 'l':
283: case 'k':
284: ps.lineTo(x[0], y[0]);
285: prevIsQuad = prevIsCubic = false;
286: break;
287:
288: case 'Q':
289: case 'q':
290: case 'r':
291: case 'T':
292: case 't':
293: case 'u':
294: ps.quadTo(x[0], y[0], x[1], y[1]);
295: xp = x[0];
296: yp = y[0];
297: x[0] = x[1];
298: y[0] = y[1];
299: prevIsQuad = true;
300: prevIsCubic = false;
301: break;
302:
303: case 'C':
304: case 'c':
305: case 'd':
306: case 'S':
307: case 's':
308: case 'p':
309: ps.cubicTo(x[0], y[0], x[1], y[1], x[2], y[2]);
310: xp = x[1];
311: yp = y[1];
312: x[0] = x[2];
313: y[0] = y[2];
314: prevIsQuad = false;
315: prevIsCubic = true;
316: break;
317:
318: case 'z':
319: ps.close();
320: x[0] = sx0;
321: y[0] = sy0;
322: prevIsQuad = prevIsCubic = false;
323: break;
324: }
325: }
326:
327: int idx = glyph;
328: paths[idx] = ps;
329: minX[idx] = gx;
330: minY[idx] = gy;
331: width[idx] = gwidth;
332: height[idx] = gheight;
333: }
334: }
335: }
336:
337: public class PiscesFont {
338:
339: public static final int PLAIN = 0;
340: public static final int BOLD = 1;
341: public static final int ITALIC = 2;
342:
343: private static final String[] styles = { "PLAIN", "BOLD", "ITALIC",
344: "BOLD+ITALIC" };
345:
346: private static Hashtable faces = new Hashtable();
347:
348: String name;
349: int style;
350: int size;
351:
352: Face face;
353:
354: // size is S15.16
355: public PiscesFont(String name, int style, int size)
356: throws IOException {
357: this .face = getFace(name, style);
358: this .name = name;
359: this .style = style;
360: this .size = size;
361: }
362:
363: private static Face getFace(String name, int style)
364: throws IOException {
365: String fname = "/" + name + "_" + styles[style] + ".fnt.gz";
366: Face face = (Face) faces.get(fname);
367: if (face == null) {
368: InputStream in = (PiscesFont.class)
369: .getResourceAsStream(fname);
370: if (in == null && style != PLAIN) {
371: return getFace(name, PLAIN);
372: }
373: face = new Face(in);
374: faces.put(fname, face);
375: }
376: return face;
377: }
378:
379: public String getName() {
380: return name;
381: }
382:
383: public int getStyle() {
384: return style;
385: }
386:
387: public int getSize() {
388: return size;
389: }
390:
391: public void getBounds(String s, int[] bounds) {
392: // int size = (int)(this.size*face.scale);
393:
394: int c = (int) s.charAt(0);
395: int minX = face.minX[c];
396: int minY = face.minY[c];
397: int width = 0;
398: int height = 0;
399: for (int i = 0; i < s.length(); i++) {
400: c = (int) s.charAt(i);
401: width += (int) ((long) face.width[c] * size >> 16);
402: if (height < face.height[c]) {
403: height = face.height[c];
404: }
405: }
406:
407: bounds[0] = minX;
408: bounds[1] = minY;
409: bounds[2] = width;
410: bounds[3] = height;
411: }
412:
413: public void produce(PathSink consumer, String s, int x, int y) {
414: int size2 = (int) (this .size * face.scale);
415:
416: for (int i = 0; i < s.length(); i++) {
417: int c = (int) s.charAt(i);
418: PathStore glyph = face.paths[c];
419: int width = (int) ((long) face.width[c] * size >> 16);
420:
421: Transform6 transform = new Transform6(size2, 0, 0, size2,
422: x, y);
423: Transformer pt = new Transformer(consumer, transform);
424:
425: glyph.produce(pt);
426: x += width;
427: }
428: }
429: }
|