/* * string - an applet that displays a string. * * Copyright (c) 1999-2001 by Vladimir Vysotskii, trivee@noir.crocodile.org. * * This code is released under Artistic License. */ import java.applet.*; import java.awt.*; import java.io.*; import java.net.*; import java.util.*; public class string extends Applet { // style data private Color fgcolor; private Color bkcolor; private int penShape = DOT; private int penX = 1; private int penY = 1; private int hsize = 16; private int vsize = 16; private static final int DOT = 0; private static final int CIRCLE = 1; private static final int SQUARE = 2; private static final int LEFT = 3; private static final int RIGHT = 4; private static final int SCALE = 4; private static final int BEZ_THRESHOLD = 4; // font data private int coordsys = 0; private int em = 16; private Point ascender = new Point(0,0); private Point distance = new Point(0,0); private Hashtable glyphs = new Hashtable(); private static final int NEG_X = 1; private static final int POS_Y = 2; private static final int SWAP = 4; // text to be drawn private String text; private Vector lines; // offscreen bitmap private boolean needRepaint = true; private Image osImage; private int osWidth; private int osHeight; public string() { } public void init() { super.init(); try { getStyle(); } catch ( Exception e ) { System.out.println("init: " + e); e.printStackTrace(); } getText(); needRepaint = true; repaint(); } private void getStyle() throws IOException { BufferedReader style = getReader(getParameter("style"), null); String line; while ( (line = style.readLine()) != null ) { StringTokenizer st = new StringTokenizer(line); int numtok = st.countTokens(); if ( numtok >= 2 ) { String name = st.nextToken(); String value = st.nextToken(); if ( name.equalsIgnoreCase("fgcolor") ) fgcolor = getColor(value); else if ( name.equalsIgnoreCase("bkcolor") ) bkcolor = getColor(value); else if ( name.equalsIgnoreCase("font") ) { String encoding = null; if ( numtok >= 3 ) encoding = st.nextToken(); getFont(value, encoding); } else if ( name.equalsIgnoreCase("size") ) hsize = vsize = getInteger(value); else if ( name.equalsIgnoreCase("hsize") ) hsize = getInteger(value); else if ( name.equalsIgnoreCase("vsize") ) vsize = getInteger(value); else if ( name.equalsIgnoreCase("penshape") ) { if ( value.equalsIgnoreCase("dot") ) penShape = DOT; else if ( value.equalsIgnoreCase("circle") ) penShape = CIRCLE; else if ( value.equalsIgnoreCase("square") ) penShape = SQUARE; else if ( value.equalsIgnoreCase("left") ) penShape = LEFT; else if ( value.equalsIgnoreCase("right") ) penShape = RIGHT; } else if ( name.equalsIgnoreCase("penwidth") ) penX = getInteger(value); else if ( name.equalsIgnoreCase("penheight") ) penY = getInteger(value); } } } private Color getColor(String value) { try { return new Color(Integer.parseInt(value.trim(), 16)); } catch ( Exception e ) { return Color.white; } } private int getInteger(String value) { try { return Integer.parseInt(value.trim()); } catch ( Exception e ) { return 0; } } private void getFont(String fontname, String encoding) throws IOException { BufferedReader font = getReader(fontname, encoding); if ( font == null ) { return; } String line; while ( (line = font.readLine()) != null ) { if ( line.length() < 1 ) continue; char code = line.charAt(0); switch ( code ) { case 'c': coordsys = getInteger(line.substring(1)); break; case 'm': em = getInteger(line.substring(1)); break; case 'a': getPoint(line.substring(1), ascender); break; case 'd': getPoint(line.substring(1), distance); break; case '"': char ch = line.charAt(1); //System.out.println("Reading char " + (int)ch); glyphs.put(new Character(ch), line.substring(2)); break; } } } private void getPoint(String value, Point pt) { pt.x = 0; pt.y = 0; try { int pos = value.indexOf(','); pt.x = getInteger(value.substring(0,pos)); pt.y = getInteger(value.substring(pos+1)); } catch ( Exception e ) { } } private BufferedReader getReader(String name, String encoding) { name = name.trim(); try { URL url = new URL(getCodeBase(), name); InputStream is = url.openConnection().getInputStream(); if ( encoding == null ) { // use default return new BufferedReader(new InputStreamReader(is)); } else { System.out.println("Font encoding <" + encoding.trim() + ">"); return new BufferedReader(new InputStreamReader(is, encoding.trim())); } } catch ( Exception e ) { System.out.println("getInput: exception " + e); e.printStackTrace(); return null; } } private void getText() { text = getParameter("text"); if ( text == null || text.length() == 0 ) { text = null; lines = new Vector(); for ( int i = 1; ; i++ ) { String line = getParameter("line" + i); if ( line == null || line.length() == 0 ) break; lines.addElement(line); } } } public void start() { } public void stop() { } public void paint(Graphics g) { Dimension s = size(); if ( osImage == null || osWidth < s.width || osHeight < s.height ) { if ( osImage != null ) { osImage.flush(); } osImage = createImage(s.width, s.height); osWidth = s.width; osHeight = s.height; needRepaint = true; } if ( needRepaint ) { Graphics osg = osImage.getGraphics(); osg.setColor(bkcolor); osg.fillRect(0, 0, osWidth, osHeight); osg.setColor(fgcolor); Point pt = new Point(0, 0); if ( text != null ) { setBaseline(pt, 0, s); for ( int i = 0; i < text.length(); ++i ) drawChar(osg, pt, text.charAt(i)); } else { for ( int i = 0; i < lines.size(); ++i ) { setBaseline(pt, i, s); String line = (String)lines.elementAt(i); for ( int j = 0; j < line.length(); ++j ) drawChar(osg, pt, line.charAt(j)); } } osg.dispose(); osg = null; needRepaint = false; } g.drawImage(osImage, 0, 0, this); } private void setBaseline(Point pt, int line, Dimension s) { pt.x = - (ascender.x + distance.x * line); pt.y = - (ascender.y + distance.y * line); scalePoint(pt); if ( pt.x < 0 ) pt.x += s.width * SCALE; if ( pt.y < 0 ) pt.y += s.height * SCALE; } private void scalePoint(Point pt) { pt.x = (pt.x * hsize * SCALE) / em; pt.y = (pt.y * vsize * SCALE) / em; if ( (coordsys & SWAP ) != 0 ) { int temp = pt.x; pt.x = pt.y; pt.y = temp; } if ( (coordsys & NEG_X) != 0 ) pt.x = - pt.x; if ( (coordsys & POS_Y) == 0 ) pt.y = - pt.y; } private String getGlyph(char c) { String glyph = (String)glyphs.get(new Character(c)); if ( glyph == null ) glyph = ""; return glyph; } private Point cur = new Point(0, 0); private Point one = new Point(0, 0); private Point two = new Point(0, 0); private void drawChar(Graphics g, Point pt, char c) { //System.out.println("Drawing char " + (int)c); String glyph = getGlyph(c); int pos = 0; char lastcode = 'm'; cur.x = pt.x; cur.y = pt.y; one.x = 0; one.y = 0; two.x = 0; two.y = 0; while ( pos < glyph.length() ) { // get code char code = glyph.charAt(pos); if ( code == '(' ) code = lastcode; else pos++; switch(code) { case 's': pos += getPoint(glyph, pos, one); pt.x += one.x; pt.y += one.y; lastcode = code; break; case 'm': pos += getPoint(glyph, pos, one); cur.x = pt.x + one.x; cur.y = pt.y + one.y; lastcode = code; break; case 'l': pos += getPoint(glyph, pos, one); one.x += pt.x; one.y += pt.y; drawLine(g, cur.x, cur.y, one.x, one.y); cur.x = one.x; cur.y = one.y; lastcode = code; break; case 'b': pos += getPoint(glyph, pos, one); pos += getPoint(glyph, pos, two); two.x += pt.x; two.y += pt.y; drawBezier(g, cur.x, cur.y, pt.x+one.x, pt.y+one.y, two.x, two.y); cur.x = two.x; cur.y = two.y; lastcode = code; break; case 'c': pos += drawChar(g, pt, glyph, pos); lastcode = code; break; case 'd': pos += getPoint(glyph, pos, one); drawPoint(g, pt.x+one.x, pt.y+one.y); lastcode = code; break; } } } private int getPoint(String glyph, int pos, Point pt) { int left = glyph.indexOf('(', pos); int right = glyph.indexOf(')', pos); if ( left < 0 || right < left+4 ) { pt.x = 0; pt.y = 0; return 0; } getPoint(glyph.substring(left+1, right), pt); scalePoint(pt); return right+1-pos; } private int drawChar(Graphics g, Point pt, String glyph, int pos) { int left = glyph.indexOf('(', pos); int right = glyph.indexOf(')', pos); if ( left < 0 || right != left+2 ) { return 0; } drawChar(g, pt, glyph.charAt(left+1)); return right+1-pos; } private void drawBezier(Graphics g, int x1, int y1, int x2, int y2, int x3, int y3) { if ( Math.abs(x2-x1) <= BEZ_THRESHOLD && Math.abs(y2-y1) <= BEZ_THRESHOLD && Math.abs(x3-x2) <= BEZ_THRESHOLD && Math.abs(y3-y2) <= BEZ_THRESHOLD ) { drawLine(g,x1,y1,x3,y3); //drawLine(g,x2,y2,x3,y3); } else { int x12 = (x1+x2+1)/2; int y12 = (y1+y2+1)/2; int x23 = (x2+x3+1)/2; int y23 = (y2+y3+1)/2; int x123 = (x1+2*x2+x3+2)/4; int y123 = (y1+2*y2+y3+2)/4; drawBezier(g, x1, y1, x12, y12, x123, y123); drawBezier(g, x123, y123, x23, y23, x3, y3); } } private void drawLine(Graphics g, int x1, int y1, int x2, int y2) { int dx = x2-x1; int dy = y2-y1; if (dx == 0 && dy == 0) { return; } else if (Math.abs(dx) > Math.abs(dy)) { int sx = dx > 0 ? 1 : -1; dx = Math.abs(dx); int oy = dy > 0 ? dx/2 : - dx/2; for ( int i = 0; i < dx; i += SCALE ) drawPoint(g, x1 + i*sx, y1 + (i * dy + oy) / dx); } else { int sy = dy > 0 ? 1 : -1; dy = Math.abs(dy); int ox = dx > 0 ? dy/2 : - dy/2; for ( int i = 0; i < dy; i += SCALE ) drawPoint(g, x1+(i*dx+ox)/dy, y1 + i*sy); } } private void drawPoint(Graphics g, int x, int y) { x = (x + SCALE/2) / SCALE; y = (y + SCALE/2) / SCALE; if (penShape == DOT) { g.fillRect(x,y,1,1); } x -= penX / 2; y -= penY / 2; switch(penShape) { case CIRCLE: g.fillOval(x,y,penX,penY); break; case SQUARE: g.fillRect(x,y,penX,penY); break; case LEFT: g.drawLine(x,y,x+penX-1,y+penY-1); g.drawLine(x+1,y,x+penX,y+penY-1); break; case RIGHT: g.drawLine(x,y+penY-1,x+penX-1,y); g.drawLine(x+1,y+penY-1,x+penX,y); break; } } }