import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Panel;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import static java.lang.Math.*;
public class FontOutlines
extends Panel
{
private static final long serialVersionUID = 3084369841425890033L;
static final int DM = 8;
final char[] text;
final float prec;
Font bigFont = GraphicsEnvironment.getLocalGraphicsEnvironment().createGraphics(new BufferedImage(1,1,2)).getFont().deriveFont(600.f);
private FontOutlines(String text, float prec)
{
this.text = text.toCharArray();
this.prec = (prec % 1);
setFont(bigFont);
AffineTransform at = bigFont.getTransform();
FontRenderContext frc = new FontRenderContext(at, true, false);
Rectangle2D b = bigFont.getStringBounds(text, frc);
Dimension d = new Dimension((int) b.getWidth(), (int) b.getHeight());
setBounds((int) b.getX(), (int) b.getY(), d.width, d.height);
setPreferredSize(d);
setMaximumSize(d);
setMinimumSize(d);
}
/**
* @param args
*/
public static void main(String[] args)
{
if(args == null || args.length == 0 || args[0].equals("")) {
args = new String[] {String.valueOf('@')};
}
final Frame f = new Frame("FontOutlines");
FontOutlines fo = new FontOutlines(args[0], .1f);
f.addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent e)
{
f.dispose();
System.exit(0);
}
});
f.setBackground(Color.GRAY);
f.add(fo);
f.pack();
f.setVisible(true);
}
@Override
public void paint(Graphics g)
{
AffineTransform at = bigFont.getTransform();
FontMetrics fm = g.getFontMetrics(bigFont);
g.drawChars(text, 0, text.length, 0, fm.getAscent());
FontRenderContext frc = new FontRenderContext(at, true, false);
float h = fm.getAscent();
float w = 0;
float[] l = new float[2], m = new float[2];
for(int n = 0; n < text.length; n++) {
GlyphVector gv = bigFont.createGlyphVector(frc, String.valueOf(text[n]));
GeneralPath gp = new GeneralPath(gv.getOutline());
PathIterator pi = gp.getPathIterator(at);
float cp[] = new float[2];
float dp[] = new float[6];
int seg;
float p;
// DAS WESENTLICHE
do {
seg = pi.currentSegment(dp);
switch(seg) {
case PathIterator.SEG_MOVETO:
cp[0] = dp[0];
cp[1] = dp[1];
drawPoint((int) (cp[0] + w), (int) (cp[1] + h), Color.BLUE, g);
break;
case PathIterator.SEG_LINETO:
drawLine((int) (cp[0] + w), (int) (cp[1] + h), (int) (dp[0] + w), (int) (dp[1] + h), Color.YELLOW, g);
cp[0] = dp[0];
cp[1] = dp[1];
drawPoint((int) (cp[0] + w), (int) (cp[1] + h), Color.BLUE, g);
break;
case PathIterator.SEG_CUBICTO:
g.setColor(Color.YELLOW);
drawLine((int) (cp[0] + w), (int) (cp[1] + h), (int) (dp[0] + w), (int) (dp[1] + h), Color.YELLOW, g);
drawPoint((int) (dp[0] + w), (int) (dp[1] + h), Color.RED, g);
drawLine((int) (dp[0] + w), (int) (dp[1] + h), (int) (dp[2] + w), (int) (dp[3] + h), Color.YELLOW, g);
drawPoint((int) (dp[2] + w), (int) (dp[3] + h), Color.RED, g);
drawLine((int) (dp[2] + w), (int) (dp[3] + h), (int) (dp[4] + w), (int) (dp[5] + h), Color.YELLOW, g);
for(p = prec; prec < 1.0f; p += prec) {
l = cubicTo(cp, dp, w, h, pi.getWindingRule(), 1 - p);
if((int) l[0] != (int) m[0] || (int) l[1] != (int) m[1]) {
drawLine((int) m[0], (int) m[1], (int) l[0], (int) l[1], Color.GREEN, g);
}
}
cp[0] = dp[4];
cp[1] = dp[5];
drawPoint((int) (cp[0] + w), (int) (cp[1] + h), Color.BLUE, g);
break;
case PathIterator.SEG_QUADTO:
drawLine((int) (cp[0] + w), (int) (cp[1] + h), (int) (dp[0] + w), (int) (dp[1] + h), Color.YELLOW, g);
drawPoint((int) (dp[0] + w), (int) (dp[1] + h), Color.RED, g);
drawLine((int) (dp[0] + w), (int) (dp[1] + h), (int) (dp[2] + w), (int) (dp[3] + h), Color.YELLOW, g);
m[0] = cp[0] + w;
m[1] = cp[1] + h;
for(p = prec; p <= 1.0f; p += prec) {
l = quadTo(cp, dp, w, h, pi.getWindingRule(), 1 - p);
if((int) l[0] != (int) m[0] || (int) l[1] != (int) m[1]) {
drawLine((int) m[0], (int) m[1], (int) l[0], (int) l[1], Color.GREEN, g);
}
m = l;
}
cp[0] = dp[2];
cp[1] = dp[3];
drawPoint((int) (cp[0] + w), (int) (cp[1] + h), Color.BLUE, g);
break;
case PathIterator.SEG_CLOSE:
break;
}
pi.next();
} while(!pi.isDone());
// ENDE VON DAS WESENTLICHE
w += fm.getStringBounds(String.valueOf(text[n]), g).getWidth();
}
}
private float[] quadTo(float[] st, float[] pts, float w, float h, int wr, float pr)
{
float[] a = new float[] {st[0] + w, st[1] + h};
float[] b = new float[] {pts[0] + w, pts[1] + h};
float[] c = new float[] {pts[2] + w, pts[3] + h};
pointTimes((float) pow(pr,2), a);
pointTimes((float) (2*pr - 2*pow(pr,2)), b);
pointTimes((float) (1 - 2*pr + pow(pr,2)), c);
return pointAdd(pointAdd(a,b),c);
}
private float[] cubicTo(float[] st, float[] pts, float w, float h, int wr, float pr)
{
float[] a = new float[] {st[0] + w, st[1] + h};
float[] b = new float[] {pts[0] + w, pts[1] + h};
float[] c = new float[] {pts[2] + w, pts[3] + h};
float[] d = new float[] {pts[4] + w, pts[5] + h};
pointTimes((float) pow(pr, 3), a);
pointTimes((float) (3*pow(pr, 2) * (1 - pr)), b);
pointTimes((float) (3 * pr * pow((1 - pr),2)), c);
pointTimes((float) (pow((1 - pr), 3)), d);
return pointAdd(pointAdd(a, b), pointAdd(c, d));
}
private void pointTimes(float pr, float[] p)
{
p[0] *= pr;
p[1] *= pr;
}
private float[] pointAdd(float[] p1, float[] p2)
{
p1[0] += p2[0];
p1[1] += p2[1];
return p1;
}
private void drawPoint(int x, int y, Color col, Graphics g)
{
// Color tmp = g.getColor();
g.setColor(col);
g.fillOval(x - (DM / 2), y - (DM / 2), DM, DM);
// g.setColor(tmp);
}
private void drawLine(int x, int y, int width, int height, Color col, Graphics g)
{
// Color tmp = g.getColor();
g.setColor(col);
g.drawLine(x, y, width, height);
// g.setColor(tmp);
}
}