001: /*
002: * Copyright (C) 2005, 2006 data2c GmbH (www.data2c.com)
003: *
004: * Author: Wolfgang S. Kechel - wolfgang.kechel@data2c.com
005: *
006: */
007:
008: package org.graphics;
009:
010: import java.io.EOFException;
011: import java.io.InputStream;
012: import java.io.InputStreamReader;
013: import java.io.IOException;
014:
015: import java.util.Hashtable;
016:
017: /*
018: * Set internal define to make module widely independant of build environment
019: */
020: //#ifndef j2se
021: //#define ISMIDLET
022: //#endif
023: //#ifndef ISMIDLET
024: /*
025: * Control usage of HERSHEY font stuff. Comment next line to disable
026: * creation of vector font from hershey font.
027: */
028: //#define USEHERSHEY
029: import java.io.File;
030: import java.io.FileInputStream;
031: import java.io.FileNotFoundException;
032: import java.io.PrintStream;
033:
034: import java.awt.Graphics;
035: import java.awt.Point;
036: import java.awt.Polygon;
037: import java.awt.Rectangle;
038:
039: import java.net.URL;
040:
041: import java.util.Enumeration;
042:
043: //#ifdef USEHERSHEY
044: import org.hershey.HersheyFont; //#endif
045:
046: //#else
047: import javax.microedition.lcdui.Graphics;
048:
049: import org.awt.Point;
050: import org.awt.Rectangle;
051:
052: //#endif
053:
054: public class VFont {
055: public final static double ITALICS = 0.75;
056:
057: //#ifndef ISMIDLET
058: public final static int HCENTER = 1;
059: public final static int VCENTER = 2;
060: public final static int LEFT = 4;
061: public final static int RIGHT = 8;
062: public final static int TOP = 16;
063: public final static int BOTTOM = 32;
064: public final static int BASELINE = 64;
065: //public final static int CAP = 0x80;
066: protected static double dpi = 100;
067: //#else
068: public final static int HCENTER = Graphics.HCENTER;
069: public final static int LEFT = Graphics.LEFT;
070: public final static int RIGHT = Graphics.RIGHT;
071: public final static int TOP = Graphics.TOP;
072: public final static int VCENTER = Graphics.VCENTER;
073: public final static int BOTTOM = Graphics.BOTTOM;
074: public final static int BASELINE = Graphics.BASELINE;
075: //public final static int CAP = 2;
076: protected static double dpi = 125;
077: //#endif
078:
079: // Target size of font in point (default: 12pt)
080: protected static double normsize = 12.0;
081: // Hold precalculated value to scale font to normsize
082: // accoring to display resolution in dpi.
083: protected static double scaling = (normsize * dpi) / 72.0;
084:
085: public static int getDPI() {
086: return (int) dpi;
087: }
088:
089: public static void setDPI(int dotsperinch) {
090: dpi = dotsperinch;
091: scaling = (normsize * dpi) / 72.0;
092: }
093:
094: // Class to hold all parameters needed for drawing a string
095: public static class DrawParams {
096: public DrawParams() {
097: this (BOTTOM | LEFT, 1, 1, 0, 0);
098: }
099:
100: public DrawParams(int anchorspec, double w, double h,
101: double rot, double italics) {
102: anchor = anchorspec;
103: width = w;
104: height = h;
105: theta = rot;
106: slant = italics;
107: }
108:
109: void setup(double fontheight) {
110: scale = scaling / fontheight;
111: xscale = width * scale;
112: yscale = height * scale;
113: if (theta != .0) {
114: double tmp = (double) (-Math.PI / 180.0 * theta);
115: costheta = (double) Math.cos(tmp);
116: sintheta = (double) Math.sin(tmp);
117: } else {
118: costheta = 1.0;
119: sintheta = .0;
120: }
121: finalslant = slant * yscale;
122: /*
123: System.err.println("fh="+fontheight
124: +", w="+width +" / xs="+xscale
125: +", h="+height +" / ys="+yscale
126: +", sl="+slant +" / fsl="+finalslant);
127: */
128: }
129:
130: public int anchor;
131: public double width;
132: public double height;
133: public double theta; // rotation in degrees
134: public double slant; // slant as tan(slantangle)
135: double costheta = 1.0;
136: double sintheta = .0;
137: double finalslant = .0;
138: double scale;
139: double xscale;
140: double yscale;
141: }
142:
143: public static class Glyph
144: //#ifndef ISMIDLET
145: implements Cloneable
146: //#endif
147: {
148: // A glyph describes a character.
149: // The coordinate system has (0,0) in the lower left corner increasing
150: // to the left (x) and upwards (y).
151: public Glyph(char ch, int cwidth, char[] strokes) {
152: this (ch, cwidth, 0, strokes);
153: }
154:
155: public Glyph(char ch, int cwidth, int ml, char[] strokes) {
156: this .ch = ch;
157: this .cwidth = (short) cwidth;
158: this .midline = (short) ml;
159: this .strokes = strokes;
160: }
161:
162: public int draw(int xp, int yp, int rotpx, int rotpy,
163: DrawParams dp, Rectangle r, Graphics g) {
164: int npts = 0;
165: Point[] pts = null;
166: for (int i = 0; i < strokes.length;) {
167: int what = (int) strokes[i++];
168: switch (what) {
169: case 'v':
170: case 'l':
171: npts = 2;
172: //System.out.println("ex v 2");
173: pts = getPoints(npts, i, xp, yp, rotpx, rotpy, dp,
174: r);
175: line(pts[0].x, pts[0].y, pts[1].x, pts[1].y, g);
176: i += 2 * npts;
177: break;
178: case 'V':
179: case 'p':
180: case 'P':
181: npts = (int) strokes[i++];
182: //System.out.println("ex p "+npts);
183: pts = getPoints(npts, i, xp, yp, rotpx, rotpy, dp,
184: r);
185: switch (what) {
186: case 'V':
187: lines(pts, g);
188: break;
189: case 'p':
190: if (g != null)
191: Draw.drawPolygon(g, npts, pts, false);
192: break;
193: case 'P':
194: if (g != null)
195: Draw.drawPolygon(g, npts, pts, true);
196: break;
197: }
198: i += 2 * npts;
199: break;
200: default:
201: i = strokes.length;
202: break;
203: }
204: }
205: return round(dp.xscale/*dp.width*/* cwidth);
206: }
207:
208: protected Point[] getPoints(int npts, int startat, int xp,
209: int yp, int rotpx, int rotpy, DrawParams dp, Rectangle r) {
210: int x;
211: int y;
212: Point[] p = new Point[npts];
213: for (int i = 0; i < npts; ++i, startat += 2) {
214: x = xform(xp, strokes[startat], dp.xscale);
215: if (0.0 != dp.finalslant) {
216: //System.out.println("y="+(int)strokes[startat+1] +"corr="+(strokes[startat+1]*finalslant));
217: // add italics offset to the "normal" point transformation
218: x += strokes[startat + 1] * dp.finalslant;
219: }
220: // calculate the y coordinate
221: y = xform(yp, -strokes[startat + 1], dp.yscale);
222:
223: if (dp.theta != .0) {
224: // apply the rotation matrix ...
225: // transform the coordinate to the rotation center point
226: double xd = x - rotpx;
227: double yd = y - rotpy;
228:
229: // rotate
230: double xd2 = xd * dp.costheta - yd * dp.sintheta;
231: double yd2 = xd * dp.sintheta + yd * dp.costheta;
232:
233: // transform back
234: x = round(xd2) + rotpx;
235: y = round(yd2) + rotpy;
236: }
237: if (r != null) {
238: if (x < r.x) {
239: r.x = x;
240: }
241: if (y < r.y) {
242: r.y = y;
243: }
244: if (x > r.width) {
245: r.width = x;
246: }
247: if (y > r.height) {
248: r.height = y;
249: }
250: }
251: p[i] = new Point(x, y);
252: }
253: return p;
254: }
255:
256: protected void lines(Point[] points, Graphics g) {
257: if (points == null || g == null)
258: return;
259: for (int i = 0; i < points.length - 1; ++i) {
260: line(points[i].x, points[i].y, points[i + 1].x,
261: points[i + 1].y, g);
262: }
263: }
264:
265: protected void line(int x1, int y1, int x2, int y2, Graphics g) {
266: if (g == null)
267: return;
268:
269: //#ifdef notdef
270: if (width > 1) {
271: //#ifndef ISMIDLET
272: // if the width is greater than one
273: Polygon filledPolygon = new Polygon();
274:
275: int offset = width / 2;
276:
277: // this does not generate a true "wide line" but it seems to
278: // look OK for font lines
279: filledPolygon.addPoint(x1 - offset, y1 + offset);
280: filledPolygon.addPoint(x1 + offset, y1 - offset);
281: filledPolygon.addPoint(x2 + offset, y2 - offset);
282: filledPolygon.addPoint(x2 - offset, y2 + offset);
283: // draw a polygon
284: g.fillPolygon(filledPolygon);
285: //#else
286: System.out.println("NO THICK LINES!!!");
287: //#endif
288: }
289: //#endif
290: // draw a line
291: //#ifdef ISMIDLET
292: int oldstroke = g.getStrokeStyle();
293: g.setStrokeStyle(Graphics.SOLID);
294: //#endif
295: g.drawLine(x1, y1, x2, y2);
296: //#ifdef ISMIDLET
297: g.setStrokeStyle(oldstroke);
298: //#endif
299: }
300:
301: //#ifndef ISMIDLET
302: public Glyph(Glyph g) {
303: this (g.ch, g.cwidth, g.midline, copyStrokes(g.strokes));
304: }
305:
306: public Object clone() {
307: return new Glyph(this );
308: }
309:
310: public char[] getStrokes() {
311: return strokes;
312: }
313:
314: public void setStrokes(char[] newstrokes) {
315: strokes = newstrokes;
316: }
317:
318: public PrintStream storeOn(PrintStream s) {
319: s.print("# " + Integer.toString(ch) + " w " + cwidth);
320: if (midline != 0 && midline != cwidth / 2)
321: s.print(" m " + midline);
322: if (strokes != null) {
323: for (int i = 0; i < strokes.length;) {
324: char ch = strokes[i++];
325: s.print(' ');
326: s.print(ch);
327: switch (ch) {
328: case 'v':
329: case 'l':
330: s.print(" " + ((int) strokes[i++]));
331: s.print(" " + ((int) strokes[i++]));
332: s.print(" " + ((int) strokes[i++]));
333: s.print(" " + ((int) strokes[i++]));
334: break;
335: case 'V':
336: case 'p':
337: case 'P':
338: int n = (int) strokes[i++];
339: s.print(" " + String.valueOf(n));
340: while (n-- > 0) {
341: s.print(" " + ((int) strokes[i++]));
342: s.print(" " + ((int) strokes[i++]));
343: }
344: break;
345: default:
346: i = strokes.length;
347: break;
348: }
349: }
350: }
351: s.print(";");
352: return s;
353: }
354:
355: public int getMidLine() {
356: return midline;
357: }
358:
359: public void setWidth(int val) {
360: if (val >= 0)
361: cwidth = (short) val;
362: }
363:
364: public void setMidLine(int val) {
365: if (val >= 0)
366: midline = (short) val;
367: }
368:
369: private static char[] copyStrokes(char[] strokes) {
370: char[] newstrokes = new char[strokes.length];
371: System.arraycopy(strokes, 0, newstrokes, 0, strokes.length);
372: return newstrokes;
373: }
374:
375: //#endif
376:
377: public int getWidth() {
378: return cwidth;
379: }
380:
381: static protected final int xform(int offset, int val, double mag) {
382: return round(offset + val * mag);
383: }
384:
385: char ch;
386: short cwidth;
387: short midline = 0;
388: char[] strokes = null;
389: }
390:
391: public VFont(String name, String resourcename) throws IOException {
392: this (name, new InputStreamReader(checkis(resourcename
393: .getClass().getResourceAsStream(resourcename),
394: resourcename)));
395: }
396:
397: public VFont(String name, InputStreamReader is) throws IOException {
398: fontname = name;
399:
400: // read format
401: int format = getc(is);
402: int unused = getc(is);
403: int ch;
404:
405: while ((ch = getc(is)) != '{' && ch != -1)
406: ;
407: if (ch == -1)
408: // error: premature eof
409: throw new EOFException("Premature EOF in font file.");
410: if (format != 'A' || unused != 'X')
411: // error: unknown format
412: throw new IOException("Font file format error.");
413:
414: while ((ch = getc(is)) != '#' && ch != -1) {
415: switch (ch) {
416: case 'n':
417: // ignore
418: readInt(is);
419: //h.numchars = readInt(is);
420: /* note that number of characters is one more than max characters */
421: break;
422: case 'h':
423: fontheight = readInt(is);
424: break;
425: case 'b':
426: fontbaseline = readInt(is);
427: break;
428: case 'x':
429: fontxline = readInt(is);
430: break;
431: case 'c':
432: fontcapline = readInt(is);
433: break;
434: case 'w':
435: fontcwidth = readInt(is);
436: break;
437: case 'T':
438: fontthickness = readInt(is);
439: break;
440: case ' ':
441: default:
442: break;
443: }
444: }
445: if (ch == -1) {
446: // error: premature eof
447: throw new EOFException("Premature EOF in font file header.");
448: }
449: ungetc(is, ch);
450:
451: boolean done = false;
452: while (!done) {
453: while ((ch = getc(is)) != '#' && ch != '}' && ch != -1) {
454: //skip to #
455: ;
456: }
457:
458: if (ch == '#') {
459: readStrokes((char) readInt(is), is);
460: } else {
461: done = true;
462: }
463: }
464: //if(fontcwidth
465: makeDefaultGlyph();
466: }
467:
468: //#ifndef ISMIDLET
469: public VFont(File file) throws IOException {
470: this (file.getName(), new InputStreamReader(makeis(file)));
471: }
472:
473: public VFont(String name, File filename) throws IOException {
474: this (name, new InputStreamReader(makeis(filename)));
475: }
476:
477: //#ifdef USEHERSHEY
478: public VFont(String name, HersheyFont hf) {
479: fontname = name;
480:
481: int miny = hf.characterSetMinY;
482: int maxy = hf.characterSetMaxY;
483:
484: fontheight = maxy - miny;
485: fontcwidth = 0;
486: fontcapline = fontxline = fontthickness = 0;
487:
488: for (int i = 0; i < hf.charactersInSet; ++i) {
489: char thechar = (char) (' ' + i);
490: StringBuffer all = new StringBuffer();
491: StringBuffer sb = new StringBuffer();
492: int minx = hf.characterMinX[i];
493: int maxx = hf.characterMaxX[i];
494: int cw = maxx - minx;
495:
496: // Remind widest character
497: if (cw > fontcwidth)
498: fontcwidth = cw;
499:
500: char n = 0;
501: int hfnpts = hf.numberOfPoints[i];
502: for (int k = 1; k < hfnpts; ++k) {
503: char[] xvals = hf.characterVectors[i][HersheyFont.X];
504: char[] yvals = hf.characterVectors[i][HersheyFont.Y];
505:
506: if (xvals[k] == (int) ' ') {
507: if (n > 0) {
508: if (n > 2)
509: all.append('V').append(n).append(
510: sb.toString());
511: else
512: all.append('v').append(sb.toString());
513: n = 0;
514: sb = new StringBuffer();
515: }
516: } else {
517: sb.append((char) (((int) xvals[k]) - minx));
518: sb
519: .append((char) (fontheight - (((int) yvals[k]) - miny)));
520: ++n;
521: }
522: }
523: if (n > 0) {
524: if (n > 2)
525: all.append('V').append((char) n).append(
526: sb.toString());
527: else
528: all.append('v').append(sb.toString());
529: }
530: setGlyph(thechar, new Glyph(thechar, cw, all.toString()
531: .toCharArray()));
532: }
533: makeDefaultGlyph();
534: }
535:
536: //#endif
537:
538: public int getFontBaseLine() {
539: return fontbaseline;
540: }
541:
542: public int getFontCapLine() {
543: return fontcapline;
544: }
545:
546: public int getFontCharWidth() {
547: return fontcwidth;
548: }
549:
550: public int getFontHeight() {
551: return fontheight;
552: }
553:
554: public int getFontXLine() {
555: return fontxline;
556: }
557:
558: public void setFontBaseLine(int v) {
559: fontbaseline = v;
560: }
561:
562: public void setFontCapLine(int v) {
563: fontcapline = v;
564: }
565:
566: public void setFontCharWidth(int v) {
567: fontcwidth = v;
568: }
569:
570: public void setFontHeight(int v) {
571: fontheight = v;
572: }
573:
574: public void setFontXLine(int v) {
575: fontxline = v;
576: }
577:
578: public void setDefaultGlyph(Glyph glyph) {
579: // Silently ignore null default glyph!
580: if (glyph != null)
581: defaultglyph = glyph;
582: }
583:
584: //#endif
585:
586: public String getName() {
587: return fontname;
588: }
589:
590: public int getHeight() {
591: return round(scaling);
592: }
593:
594: public void setGlyph(char thechar, Glyph glyph) {
595: // Tricky: make sure the Glyph really describes the character for
596: // which it is stored!
597: glyph.ch = thechar;
598: if (thechar >= 0 && thechar < 256)
599: isoglyphs[thechar] = glyph;
600: else {
601: glyphtab.put(new Character(thechar), glyph);
602: }
603: }
604:
605: public Glyph getDefaultGlyph() {
606: return defaultglyph;
607: }
608:
609: public Glyph getGlyph(char thechar) {
610: Glyph glyph = null;
611:
612: if (thechar >= 0 && thechar < 256)
613: glyph = isoglyphs[thechar];
614: else {
615: glyph = (Glyph) glyphtab.get(new Character(thechar));
616: }
617: if (glyph == null)
618: glyph = defaultglyph;
619: //System.out.println("thechar="+thechar+", glyph="+glyph);
620: //glyph.printOn(System.out);
621: return glyph;
622: }
623:
624: public int glyphWidth(char thechar) {
625: return getGlyph(thechar).getWidth();
626: }
627:
628: public void drawString(String text, int xc, int yc, Rectangle r,
629: Graphics g) {
630: drawString(text, xc, yc, new DrawParams(BOTTOM | LEFT, 1.0,
631: 1.0, .0, .0), r, g);
632: }
633:
634: public void drawString(String text, int xc, int yc, int anchor,
635: Rectangle r, Graphics g) {
636: drawString(text, xc, yc, new DrawParams(anchor, 1.0, 1.0, .0,
637: .0), r, g);
638: }
639:
640: public void drawString(String text, int xc, int yc, int anchor,
641: double width, double height, Rectangle r, Graphics g) {
642: drawString(text, xc, yc, new DrawParams(anchor, width, height,
643: .0, .0), r, g);
644: }
645:
646: public void drawString(String text, int xc, int yc, int anchor,
647: double width, double height, double theta, double slant,
648: Rectangle r, Graphics g) {
649: drawString(text, xc, yc, new DrawParams(anchor, width, height,
650: theta, slant), r, g);
651: }
652:
653: public void drawString(String text, int xc, int yc, DrawParams dp,
654: Rectangle r, Graphics g) {
655:
656: // starting position
657: int xp = xc, yp = yc;
658: // set the position to do all rotations about
659: int rotpx = xc, rotpy = yc;
660: // set the flag to true if the angle is not 0.0
661: double verticalOffsetFactor = .0;
662:
663: // if we are to do a rotation
664: dp.setup(fontheight);
665:
666: // if we are not going to actually draw the string
667: if (r != null) {
668: // set up to initialize the bounding rectangle
669: r.x = r.width = xp;
670: r.y = r.height = yp;
671: }
672:
673: if ((dp.anchor & BASELINE) == BASELINE) {
674: verticalOffsetFactor = .0f;
675: yp -= round(dp.yscale/*dp.height*/* fontbaseline);
676: } else {
677: if ((dp.anchor & TOP) == TOP) {
678: verticalOffsetFactor = 1.0;
679: }
680: if ((dp.anchor & VCENTER) == VCENTER) {
681: verticalOffsetFactor = 0.5;
682: }
683: if ((dp.anchor & BOTTOM) == BOTTOM) {
684: verticalOffsetFactor = .0;
685: }
686: // move the y position based on the vertical alignment
687: yp -= round(verticalOffsetFactor * dp.yscale/*dp.height*/
688: * fontheight);
689: }
690:
691: char[] chars = text.toCharArray();
692: if (chars.length > 0) {
693: // if we have a non-standard horizontal alignment
694: if ((dp.anchor & LEFT) != LEFT) {
695: // find the length of the string in pixels ...
696: int len = 0;
697:
698: for (int j = 0; j < chars.length; j++) {
699: // the character's number in the array ...
700: len += glyphWidth(chars[j]);
701: }
702: len = round(dp.xscale/*dp.width*/* len);
703:
704: // if we are center aligned
705: if ((dp.anchor & HCENTER) == HCENTER) {
706: // move the starting point half to the left
707: xp -= len / 2;
708: } else {
709: // alignment is right, move the start all the way to the left
710: xp -= len;
711: }
712: }
713:
714: // loop through each character in the string ...
715: for (int j = 0; j < chars.length; j++) {
716: // advance the starting coordinate
717: xp += getGlyph(chars[j]).draw(xp, yp, rotpx, rotpy, dp,
718: r, g);
719: }
720: }
721:
722: // Correct rectangle dimension
723: if (r != null) {
724: r.width = r.width - r.x + 1;
725: r.height = r.height - r.y + 1;
726: }
727: }
728:
729: public Rectangle extent(String text, int xc, int yc, int anchor,
730: Rectangle r) {
731: return extent(text, xc, yc, new DrawParams(anchor, 1.0, 1.0,
732: .0, .0), r);
733: }
734:
735: public Rectangle extent(String text, int xc, int yc, int anchor,
736: double width, double height, Rectangle r) {
737: return extent(text, xc, yc, new DrawParams(anchor, width,
738: height, .0, .0), r);
739: }
740:
741: public Rectangle extent(String text, int xc, int yc, int anchor,
742: double width, double height, double theta, double slant,
743: Rectangle r) {
744: return extent(text, xc, yc, new DrawParams(anchor, width,
745: height, theta, slant), r);
746: }
747:
748: public Rectangle extent(String text, int xc, int yc, DrawParams dp,
749: Rectangle r) {
750: if (r == null)
751: r = new Rectangle();
752: drawString(text, xc, yc, dp, r, null);
753: return r;
754: }
755:
756: //#ifndef ISMIDLET
757: public int getNumberOfGlyphs() {
758: int numglyphs = 0;
759:
760: for (int i = 0; i < isoglyphs.length; ++i) {
761: if (isoglyphs[i] != null)
762: ++numglyphs;
763: }
764: Enumeration e = glyphtab.elements();
765: while (e.hasMoreElements()) {
766: e.nextElement();
767: ++numglyphs;
768: }
769: return numglyphs;
770: }
771:
772: public boolean hasGlyph(char thechar) {
773: if (thechar >= 0 && thechar < 256)
774: return isoglyphs[thechar] != null;
775: return null != glyphtab.get(new Character(thechar));
776: }
777:
778: public PrintStream storeOn(PrintStream s) {
779: StringBuffer sb = new StringBuffer();
780:
781: sb.append("AX\n{\n");
782: // number of glyphs
783: sb.append("\tn ").append(getNumberOfGlyphs()).append('\n');
784: // font height
785: sb.append("\th ").append(fontheight).append('\n');
786: // font baseline position
787: sb.append("\tb ").append(fontbaseline).append('\n');
788: // font Xline
789: sb.append("\tx ").append(fontxline).append('\n');
790: // font capline
791: sb.append("\tc ").append(fontcapline).append('\n');
792: // font character width (average??)
793: sb.append("\tw ").append(fontcwidth).append('\n');
794: s.print(sb.toString());
795: for (int i = 0; i < isoglyphs.length; ++i) {
796: if (isoglyphs[i] != null) {
797: s.print('\t');
798: isoglyphs[i].storeOn(s).println();
799: }
800: }
801: Enumeration e = glyphtab.elements();
802: while (e.hasMoreElements()) {
803: s.print('\t');
804: ((Glyph) e.nextElement()).storeOn(s).println();
805: }
806: s.println("}");
807: return s;
808: }
809:
810: //#endif
811:
812: protected void makeDefaultGlyph() {
813: StringBuffer sb = new StringBuffer();
814: int w = fontcwidth / 2;
815:
816: // make sure character has some extent...
817: if (w < 3)
818: w = 3;
819:
820: // generate a glyph that is a box
821: sb.append('V').append((char) 5);
822: sb.append((char) 1).append((char) 1);
823: sb.append((char) (w - 2)).append((char) 1);
824: sb.append((char) (w - 2)).append((char) (fontheight - 2));
825: sb.append((char) (1)).append((char) (fontheight - 2));
826: sb.append((char) 1).append((char) 1);
827: defaultglyph = new Glyph((char) 0, w, sb.toString()
828: .toCharArray());
829: }
830:
831: protected int getc(InputStreamReader is) throws IOException {
832: int ch;
833:
834: if (is.markSupported()) {
835: is.mark(1);
836: ch = lastchar = is.read();
837: } else {
838: if (pushback) {
839: pushback = false;
840: ch = lastchar;
841: } else {
842: ch = lastchar = is.read();
843: }
844: }
845: return ch;
846: }
847:
848: protected void ungetc(InputStreamReader is, int ch)
849: throws IOException {
850: if (is.markSupported())
851: is.reset();
852: else {
853: pushback = true;
854: lastchar = ch;
855: }
856: }
857:
858: protected void skipWS(InputStreamReader is) throws IOException {
859: // skip whitespace
860: int ch;
861: do {
862: ch = getc(is);
863: } while (ch == ' ' || ch == '\t' || ch == '\n');
864: if (ch != ' ' && ch != '\t' && ch != '\n' && ch != -1)
865: ungetc(is, ch);
866: }
867:
868: protected int readInt(InputStreamReader is) throws IOException {
869: int ch;
870: int val = 0;
871: boolean neg = false;
872: int numdigits = 0;
873:
874: skipWS(is);
875: ch = getc(is);
876: if (ch == '+')
877: ch = getc(is);
878: else if (ch == '-') {
879: neg = true;
880: ch = getc(is);
881: }
882: while (ch != -1 && Character.isDigit((char) ch)) {
883: ++numdigits;
884: val = 10 * val + Character.digit((char) ch, 10);
885: ch = getc(is);
886: }
887: if (neg)
888: val = -val;
889: if (ch != -1 && numdigits > 0)
890: ungetc(is, ch);
891: return val;
892: }
893:
894: protected void readStrokes(char thechar, InputStreamReader is)
895: throws IOException {
896: StringBuffer sb = new StringBuffer();
897: int i = 0;
898: int c;
899: int cwidth = fontcwidth;
900: int midline = 0;
901:
902: while ((c = getc(is)) != ';' && c != '}' && c != -1) {
903: switch (c) {
904: case 'w':
905: // character width
906: cwidth = readInt(is);
907: break;
908: case 'm':
909: // font character centerline/midline
910: midline = readInt(is);
911: break;
912: case 'l':
913: case 'v':
914: // draw lline (with thickness)
915: sb.append((char) c); /* insert the opcode */
916: /* read first argument */
917: sb.append((char) readInt(is));
918: sb.append((char) readInt(is));
919: /* read second argument */
920: sb.append((char) readInt(is));
921: sb.append((char) readInt(is));
922: break;
923: case 'V':
924: case 'p':
925: case 'P':
926: case 'B':
927: sb.append((char) c); /* insert the opcode */
928: i = readInt(is);
929: sb.append((char) i);
930: for (int j = 0; j < i; j++) {
931: sb.append((char) readInt(is));
932: sb.append((char) readInt(is));
933: }
934: break;
935: case ' ': /* space between input chars, ignore */
936: case '\t':
937: default:
938: break;
939: }
940: }
941: if (c == -1 || c == '}')
942: throw new IOException("EOF or format error in font file.");
943: setGlyph(thechar, new Glyph(thechar, cwidth, midline, sb
944: .toString().toCharArray()));
945: }
946:
947: private static InputStream checkis(InputStream is, String name)
948: throws IOException {
949: if (is == null)
950: throw new IOException("Font '" + name + "' not found.");
951: return is;
952: }
953:
954: //#ifndef ISMIDLET
955: private static InputStream makeis(File file) throws IOException {
956: return checkis(new FileInputStream(file), file
957: .getCanonicalPath());
958: }
959:
960: //#endif
961:
962: private static int round(double x) {
963: return (int) Math.floor(.5 + x);
964: }
965:
966: protected String fontname;
967: protected int fontheight = 1;
968: protected int fontbaseline = 0;
969: protected int fontcwidth = 0;
970: protected int fontcapline = 0; // unused
971: protected int fontxline = 0; // unused
972: protected int fontthickness = 0; // unused
973:
974: protected Hashtable glyphtab = new Hashtable();
975: protected Glyph[] isoglyphs = new Glyph[256];
976: protected Glyph defaultglyph;
977: protected boolean pushback = false;
978: protected int lastchar = 0;
979: }
|