/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2 -*- font-bdf.c --- simple, self-contained code for manipulating BDF fonts. Copyright © 2001, 2002 Jamie Zawinski Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. No representations are made about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. */ #include "config.h" #include "ppm-lite.h" #include "font-bdf.h" #include #include #include #undef countof #define countof(x) (sizeof((x))/sizeof(*(x))) /* for parsing hex numbers fast */ static const char hex[128] = {16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,16,16,16,16,16,16, 16,10,11,12,13,14,15,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, 16,10,11,12,13,14,15,16,16,16,16,16,16,16,16,16, 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16}; struct font * read_bdf (const char *file) { int stdin_p = !strcmp(file, "-"); FILE *in; char buf [1024]; struct font *font = (struct font *) calloc (1, sizeof(*font)); int line = 1; int current_char = -1; int current_char_height = -1; int current_char_descent = -1; int overall_bitmap_height = -1; int overall_bitmap_descent = -1; if (stdin_p) in = stdin; else { in = fopen (file, "r"); if (!in) { sprintf(buf, "%.255s: %.255s", progname, file); perror(buf); exit (1); } } if (!fgets (buf, sizeof(buf)-1, in)) { fprintf (stderr, "%s: %s: %d: premature EOF\n", progname, file, line); exit (1); } if (!!strncmp (buf, "STARTFONT 2.", 12)) { fprintf (stderr, "%s: %s: %d: not a BDF 2 file\n", progname, file, line); exit (1); } line++; while (fgets (buf, sizeof(buf)-1, in)) { char dummy; int dummyn; if (!strncmp (buf, "FACE_NAME ", 10) && !font->name) { char *ss; font->name = strdup (buf + 10); for (ss = font->name; *ss; ss++) if (*ss == '\"' || *ss == '\r' || *ss == '\n') { strcpy (ss, ss+1); if (ss != font->name) ss--; } } else if (!strncmp (buf, "FONT_ASCENT ", 12)) { if (1 != sscanf (buf+12, "%d %c", &font->ascent, &dummy)) goto FAIL; } else if (!strncmp (buf, "FONT_DESCENT ", 12)) { if (1 != sscanf (buf+12, "%d %c", &font->descent, &dummy)) goto FAIL; } else if (!strncmp (buf, "FONTBOUNDINGBOX ", 16)) { int w, h, x, y; if (4 != sscanf (buf+16, "%d %d %d %d %c", &w, &h, &x, &y, &dummy)) goto FAIL; overall_bitmap_height = h; overall_bitmap_descent = y; } else if (!strncmp (buf, "ENCODING ", 9)) { if (1 != sscanf (buf+9, "%d %c", ¤t_char, &dummy) && current_char <= 255 && current_char >= -1) goto FAIL; } else if (!strncmp (buf, "STARTCHAR", 9) || !strncmp (buf, "ENDCHAR", 7)) { current_char = -1; } else if (!strncmp (buf, "DWIDTH ", 7)) { int w; if (2 != sscanf (buf+7, "%d %d %c", &w, &dummyn, &dummy)) goto FAIL; if (current_char != -1) font->chars[current_char].width = w; } else if (!strncmp (buf, "BBX ", 4)) { int w, h, x, y; struct ppm *ppm; if (4 != sscanf (buf+4, "%d %d %d %d %c", &w, &h, &x, &y, &dummy)) goto FAIL; /* Make all ppm data be the same height, so that when scaling, all characters experience proportional roundoff artifacts. */ current_char_height = h; current_char_descent = y; h = overall_bitmap_height; y = overall_bitmap_descent; if (current_char != -1) { font->chars[current_char].lbearing = x; font->chars[current_char].descent = y; ppm = (struct ppm *) calloc (1, sizeof(struct ppm)); ppm->type = 4; ppm->width = ((w + 7) / 8) * 8; ppm->height = h; ppm->rgba = (unsigned char *) calloc (1, (ppm->width * ppm->height * 4)); if (!ppm->rgba) { fprintf (stderr, "%s: out of memory (%d x %d)\n", progname, ppm->width, ppm->height); exit (1); } font->chars[current_char].ppm = ppm; } } else if (!strncmp (buf, "BITMAP ", 4)) { int y, yoff; if (font->chars[current_char].width == 0) { fprintf (stderr, "%s: %s: %d: zero-width char ('%c') with bits?\n", progname, file, line, current_char); exit (1); } yoff = ((overall_bitmap_height - current_char_height) + + (overall_bitmap_descent - current_char_descent)); if (yoff < 0 || yoff + current_char_height - 1 >= overall_bitmap_height) { fprintf (stderr, "%s: %s: %d: char %d bbox is not contained in font bbox\n", progname, file, line, current_char); exit (1); } for (y = 0; y < current_char_height; y++) { if (!fgets (buf, sizeof(buf)-1, in)) { fprintf (stderr, "%s: %s: %d: premature EOF\n", progname, file, line); exit (1); } if (current_char > 0 && current_char <= 255) { int yy = (y + yoff); struct ppm *ppm = font->chars[current_char].ppm; unsigned char *o = (ppm->rgba + (yy * ppm->width * 4)); char *s; if (yy < 0 || yy >= ppm->height) abort(); for (s = buf; *s; s++) { int h = hex[(int) *s]; int i; if (h != 16) for (i = 3; i >= 0; i--) { /* ink bits are 0 (black) and opaque (255). non-ink bits are 0 (black) and clear (0). */ o[3] = ((h & (1<name) font->name = strdup (""); if (!stdin_p) fclose (in); font->monochrome_p = 1; return font; } struct font * copy_font (struct font *font) { int i; struct font *f2 = (struct font *) malloc (sizeof(*font)); memcpy (f2, font, sizeof(*font)); font = f2; font->name = strdup (font->name); for (i = 0; i < countof(font->chars); i++) if (font->chars[i].ppm) font->chars[i].ppm = copy_ppm (font->chars[i].ppm); return font; } void free_font (struct font *font) { int i; free (font->name); for (i = 0; i < countof(font->chars); i++) if (font->chars[i].ppm) free_ppm (font->chars[i].ppm); memset (font, 0xDE, sizeof(*font)); free (font); } static int draw_char (struct font *font, const unsigned char c, struct ppm *into, int x, int y, unsigned long fg, unsigned long bg, int alpha) { int w = font->chars[(int) c].width; struct ppm *from = font->chars[(int) c].ppm; if (from && c != ' ') { x += font->chars[(int) c].lbearing; y -= from->height + font->chars[(int) c].descent; y += font->ascent; paste_ppm (into, x, y, from, 0, 0, from->width, from->height, fg, bg, alpha); } return w; } /* draw a string with origin XY. Alignment 0 means center on the Y axis; -1 means flushright; 1 means flushleft. Alpha ranges from 0-255. Newlines are allowed; tabs are not handled specially. */ void draw_string (struct font *font, const unsigned char *string, struct ppm *into, int x, int y, int alignment, unsigned long fg, unsigned long bg, int alpha) { int ox = x; int w; const unsigned char *s2; LINE: x = ox; w = 0; if (alignment <= 0) { for (s2 = string; *s2 && *s2 != '\n'; s2++) w += font->chars[(int) *s2].width; if (alignment < 0) x -= w; else if (alignment == 0) x -= w/2; x--; } /* back up over the lbearing of the first character on the line */ x -= 2 * font->chars[(int) *string].lbearing; while (*string) { if (*string == '\n') { y += font->ascent + font->descent; string++; goto LINE; } else { int w = draw_char (font, *string, into, x, y, fg, bg, alpha); x += w; } string++; } } void scale_font (struct font *font, double scale) { int i; # define SCALE(x) ((x) = (scale * ((x) > 0 ? ((x) + 0.5) : ((x) - 0.5)))) SCALE (font->ascent); SCALE (font->descent); for (i = 0; i < countof(font->chars); i++) { SCALE (font->chars[i].lbearing); SCALE (font->chars[i].width); SCALE (font->chars[i].descent); if (font->chars[i].ppm) { struct ppm *ppm2 = scale_ppm (font->chars[i].ppm, scale); free_ppm (font->chars[i].ppm); font->chars[i].ppm = ppm2; } } # undef SCALE } void halo_font (struct font *font, int radius) { int i; for (i = 0; i < countof(font->chars); i++) if (font->chars[i].ppm) { struct ppm *ppm = font->chars[i].ppm; struct ppm *halo = blur_ppm (ppm, radius); unsigned char *p = halo->rgba; unsigned char *end = p + (halo->width * halo->height * 4); /* Set the halo pixels to be the background color (white). */ while (p < end) { *p++ = 255; *p++ = 255; *p++ = 255; p++; } /* Overlay the character bits (black) on the halo bits (white). */ paste_ppm (halo, radius, radius, ppm, 0, 0, ppm->width, ppm->height, -1, -1, 255); /* Now install the result in the font. */ font->chars[i].ppm = halo; free_ppm (ppm); /* The size of the PPM changed, so adjust the offsets into it. */ font->chars[i].lbearing += radius; font->chars[i].descent -= radius; } font->monochrome_p = 0; } void dump_font (struct font *font, int which) { int i; int alpha = 128; for (i = 0; i < countof(font->chars); i++) { int x, y; int x1, x2, y1, y2; struct ppm *ppm = font->chars[i].ppm; if (!ppm) continue; if (which > 0 && which != i) continue; x1 = font->chars[i].lbearing; x2 = font->chars[i].width + font->chars[i].lbearing; y1 = ppm->height + font->chars[i].descent - font->ascent; y2 = ppm->height + font->chars[i].descent; fprintf (stderr, "%3d: %c ", i, i); for (x = 0; x < ppm->width; x++) fprintf (stderr, "%c", (x >= 10 ? (x / 10) + '0' : ' ')); fputs ("\n", stderr); fprintf (stderr, " "); for (x = 0; x < ppm->width; x++) fprintf (stderr, "%d", (x % 10)); fputs ("\n", stderr); fputs (" +", stderr); for (x = 0; x < ppm->width; x++) fputs ((x == x1 ? "/" : x == x2 ? "\\" : "-"), stderr); fprintf (stderr, "+ %d,%d - %d,%d\n", x1, y1, x2, y2); for (y = 0; y < ppm->height; y++) { fprintf (stderr, " %2d: %c", y, (y == y1 ? '/' : y == y2 ? '\\' : '|')); for (x = 0; x < ppm->width; x++) { unsigned char r, g, b, a; get_pixel (ppm, x, y, &r, &g, &b, &a); if (a >= alpha) fputs ("#", stderr); else if (x == x1 || x == x2 || y == y1 || y == y2) fputs (":", stderr); else if (a > 0) fputs ("-", stderr); else fputs (" ", stderr); } fprintf (stderr, "%c\n", (y == y1 ? '\\' : y == y2 ? '/' : '|')); } fputs (" +", stderr); for (x = 0; x < ppm->width; x++) fputs ((x == x1 ? "\\" : x == x2 ? "/" : "-"), stderr); fputs ("+\n", stderr); } }