case "indexof": return METHOD;
case "distanceto": return METHOD;
case "text": return text;
- case "path": throw new JSExn("cannot read from the path property");
+ case "path":
+ if (path != null) return path.toString();
+ if (text == null) return null;
+ if (font == null) return null;
+ String ret = "";
+ for(int i=0; i<text.length(); i++) ret += font.glyphs[text.charAt(i)].path;
+ return ret;
case "fill": return Color.colorToString(fillcolor);
case "strokecolor": return Color.colorToString(strokecolor);
case "textcolor": return Color.colorToString(strokecolor);
case "shrink": CHECKSET_FLAG(HSHRINK | VSHRINK); RECONSTRAIN();
case "hshrink": CHECKSET_FLAG(HSHRINK); RECONSTRAIN();
case "vshrink": CHECKSET_FLAG(VSHRINK); RECONSTRAIN();
- case "path": path = Path.parse(toString(value)); dirty();
+ case "path": path = Path.parse(toString(value)); RECONSTRAIN(); dirty();
case "width": setWidth(toInt(value), toInt(value));
case "height": setHeight(toInt(value), toInt(value));
case "maxwidth": setWidth(minwidth, toInt(value));
public int max_ascent; ///< the maximum ascent, in pixels
public int max_descent; ///< the maximum descent, in pixels
boolean latinCharsPreloaded = false; ///< true if a request to preload ASCII 32-127 has begun
- Glyph[] glyphs = new Glyph[65535]; ///< the glyphs that comprise this font
+ public Glyph[] glyphs = new Glyph[65535]; ///< the glyphs that comprise this font
public abstract static class Glyph {
protected Glyph(Font font, char c) { this.font = font; this.c = c; }
public int width = -1; ///< the width of the glyph
public int height = -1; ///< the height of the glyph
public byte[] data = null; ///< the alpha channel samples for this font
+ public String path = null; // FIXME this should be shared across point sizes
void render() {
if (!isLoaded) try { renderGlyph(this); } catch (IOException e) { Log.info(Font.class, e); }
isLoaded = true;
glyph.width = rt.getUserInfo(1);
glyph.height = rt.getUserInfo(2);
-
+
glyph.data = new byte[glyph.width * glyph.height];
int addr = rt.getUserInfo(0);
rt.copyin(addr, glyph.data, glyph.width * glyph.height);
+ byte[] path = new byte[rt.getUserInfo(8)];
+ rt.copyin(rt.getUserInfo(7), path, rt.getUserInfo(8));
+ glyph.path = new String(path);
+ glyph.path += " m " + (64 * glyph.advance) + " 0 ";
+
glyph.isLoaded = true;
} catch (Exception e) {
// FEATURE: Better error reporting (thow an exception?)
return FT_New_Memory_Face(library, buf, length, 0, &face);
}
+// if the path is more than 16k chars long, we're screwed anyways...
+static char path[1024 * 16];
+static int pathlen = 0;
+static int current_x;
+static int current_y;
+
+static void append(FT_Pos x, FT_Pos y) {
+ sprintf(path + pathlen, "%d,%d ", x - current_x, y - current_y);
+ pathlen += strlen(path + pathlen);
+}
+static int moveto(FT_Vector* to, void* user) {
+ if (pathlen > 0) {
+ path[pathlen++] = 'z';
+ path[pathlen++] = ' ';
+ }
+ path[pathlen++] = 'm';
+ path[pathlen++] = ' ';
+ append(to->x, to->y);
+ current_x = to->x; current_y = to->y;
+ return 0;
+}
+static int lineto(FT_Vector* to, void* user) {
+ path[pathlen++] = 'l';
+ path[pathlen++] = ' ';
+ append(to->x, to->y);
+ current_x = to->x; current_y = to->y;
+ return 0;
+}
+static int conicto(FT_Vector* control, FT_Vector* to, void* user) {
+ path[pathlen++] = 'q';
+ path[pathlen++] = ' ';
+ append(control->x, control->y);
+ append(to->x, to->y);
+ current_x = to->x; current_y = to->y;
+ return 0;
+}
+static int cubicto(FT_Vector* control1, FT_Vector* control2, FT_Vector* to, void* user) {
+ path[pathlen++] = 'c';
+ path[pathlen++] = ' ';
+ append(control1->x, control1->y);
+ append(control2->x, control2->y);
+ append(to->x, to->y);
+ current_x = to->x; current_y = to->y;
+ return 0;
+}
+static FT_Outline_Funcs buildPath = { &moveto, &lineto, &conicto, &cubicto, 0, 0 };
+
int render(int charcode, int size) __attribute__((section(".text")));
int render(int charcode, int size) {
int glyph_index;
user_info[4] = (-1 * face->size->metrics.descender) >> 6;
user_info[5] = face->glyph->metrics.horiBearingY >> 6;
user_info[6] = face->glyph->advance.x >> 6;
+
+ pathlen = 0; current_x = 0; current_y = 0;
+ FT_Outline_Decompose(&face->glyph->outline, &buildPath, NULL);
+ path[pathlen++] = 'z';
+ path[pathlen++] = ' ';
+ path[pathlen++] = 'm';
+ path[pathlen++] = ' ';
+ append(0, 0);
+ path[pathlen++] = ' ';
+ user_info[7] = (int)&path;
+ user_info[8] = pathlen;
}
// Kerning code; add back in later
static final byte TYPE_QUADRADIC = 4;
public static Path parse(String s) { return Tokenizer.parse(s); }
+
+ // FIXME: hack
+ private String toString;
+ private Path(String s) { this.toString = s; }
+ public String toString() { return toString; }
+
public static class Tokenizer {
// FIXME: check array bounds exception for improperly terminated string
String s;
public static Path parse(String s) {
if (s == null) return null;
Tokenizer t = new Tokenizer(s);
- Path ret = new Path();
+ Path ret = new Path(s);
char last_command = 'M';
boolean first = true;
while(t.hasMoreTokens()) {