diff --git a/cursor/convert_font.c b/cursor/convert_font.c new file mode 100644 index 0000000..f297125 --- /dev/null +++ b/cursor/convert_font.c @@ -0,0 +1,531 @@ +/* + * Copyright © 2012 Philipp Brüschweiler + * + * 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, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +/* + * This is a small, hacky tool to extract cursors from a .pcf file. + * The information about the file format has been gathered from + * http://fontforge.org/pcf-format.html + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define min(a, b) ((a) < (b) ? (a) : (b)) +#define max(a, b) ((a) > (b) ? (a) : (b)) + +struct glyph { + char *name; + int16_t left_bearing, right_bearing, ascent, descent; + + int16_t width, height; + int16_t hotx, hoty; + + int32_t data_format; + char *data; +}; + +static struct { + int count; + struct glyph *glyphs; +} extracted_font = {0, NULL}; + +#define PCF_PROPERTIES (1<<0) +#define PCF_ACCELERATORS (1<<1) +#define PCF_METRICS (1<<2) +#define PCF_BITMAPS (1<<3) +#define PCF_INK_METRICS (1<<4) +#define PCF_BDF_ENCODINGS (1<<5) +#define PCF_SWIDTHS (1<<6) +#define PCF_GLYPH_NAMES (1<<7) +#define PCF_BDF_ACCELERATORS (1<<8) + +#define PCF_DEFAULT_FORMAT 0x00000000 +#define PCF_INKBOUNDS 0x00000200 +#define PCF_ACCEL_W_INKBOUNDS 0x00000100 +#define PCF_COMPRESSED_METRICS 0x00000100 + +#define PCF_FORMAT_MASK 0xffffff00 + +struct pcf_header { + char header[4]; + int32_t table_count; + struct toc_entry { + int32_t type; + int32_t format; + int32_t size; + int32_t offset; + } tables[0]; +}; + +struct compressed_metrics { + uint8_t left_sided_bearing; + uint8_t right_side_bearing; + uint8_t character_width; + uint8_t character_ascent; + uint8_t character_descent; +}; + +struct uncompressed_metrics { + int16_t left_sided_bearing; + int16_t right_side_bearing; + int16_t character_width; + int16_t character_ascent; + int16_t character_descent; + uint16_t character_attributes; +}; + +struct metrics { + int32_t format; + union { + struct { + int16_t count; + struct compressed_metrics compressed_metrics[0]; + } compressed; + struct { + int32_t count; + struct uncompressed_metrics uncompressed_metrics[0]; + } uncompressed; + }; +}; + +struct glyph_names { + int32_t format; + int32_t glyph_count; + int32_t offsets[0]; +}; + +struct bitmaps { + int32_t format; + int32_t glyph_count; + int32_t offsets[0]; +}; + +static void +handle_compressed_metrics(int32_t count, struct compressed_metrics *m) +{ + printf("metrics count: %d\n", count); + extracted_font.count = count; + extracted_font.glyphs = calloc(count, sizeof(struct glyph)); + + int i; + for (i = 0; i < count; ++i) { + struct glyph *glyph = &extracted_font.glyphs[i]; + glyph->left_bearing = + ((int16_t) m[i].left_sided_bearing) - 0x80; + glyph->right_bearing = + ((int16_t) m[i].right_side_bearing) - 0x80; + glyph->width = ((int16_t) m[i].character_width) - 0x80; + glyph->ascent = ((int16_t) m[i].character_ascent) - 0x80; + glyph->descent = ((int16_t) m[i].character_descent) - 0x80; + + /* computed stuff */ + glyph->height = glyph->ascent + glyph->descent; + + glyph->hotx = -glyph->left_bearing; + glyph->hoty = glyph->ascent; + } +} + +static void +handle_metrics(void *metricbuf) +{ + struct metrics *metrics = metricbuf; + printf("metric format: %x\n", metrics->format); + + if ((metrics->format & PCF_FORMAT_MASK) == PCF_DEFAULT_FORMAT) { + printf("todo...\n"); + } else if ((metrics->format & PCF_FORMAT_MASK) == + PCF_COMPRESSED_METRICS) { + handle_compressed_metrics( + metrics->compressed.count, + &metrics->compressed.compressed_metrics[0]); + } else { + printf("incompatible format\n"); + abort(); + } +} + +static void +handle_glyph_names(struct glyph_names *names) +{ + printf("glyph count %d\n", names->glyph_count); + + if (names->glyph_count != extracted_font.count) { + abort(); + } + + printf("glyph names format %x\n", names->format); + + void *names_start = ((void*) names) + sizeof(struct glyph_names) + + (names->glyph_count + 1) * sizeof(int32_t); + + int i; + for (i = 0; i < names->glyph_count; ++i) { + int32_t start = names->offsets[i]; + int32_t end = names->offsets[i+1]; + char *name = names_start + start; + extracted_font.glyphs[i].name = calloc(1, end - start + 1); + memcpy(extracted_font.glyphs[i].name, name, end - start); + } +} + +static void +handle_bitmaps(struct bitmaps *bitmaps) +{ + printf("bitmaps count %d\n", bitmaps->glyph_count); + + if (bitmaps->glyph_count != extracted_font.count) { + abort(); + } + + printf("format %x\n", bitmaps->format); + + if (bitmaps->format != 2) { + printf("format not yet supported\n"); + abort(); + } + + void *bitmaps_start = ((void*) bitmaps) + sizeof(struct bitmaps) + + (bitmaps->glyph_count + 4) * sizeof(int32_t); + + int i; + for (i = 0; i < bitmaps->glyph_count; ++i) { + int32_t offset = bitmaps->offsets[i]; + struct glyph *glyph = &extracted_font.glyphs[i]; + glyph->data_format = bitmaps->format; + + glyph->data = bitmaps_start + offset; + } +} + +static void +handle_pcf(void *fontbuf) +{ + struct pcf_header *header = fontbuf; + printf("tablecount %d\n", header->table_count); + + int i; + for (i = 0; i < header->table_count; ++i) { + struct toc_entry *entry = &header->tables[i]; + printf("type: %d\n", entry->type); + if (entry->type == PCF_METRICS) { + handle_metrics(fontbuf + entry->offset); + } else if (entry->type == PCF_GLYPH_NAMES) { + handle_glyph_names(fontbuf + entry->offset); + } else if (entry->type == PCF_BITMAPS) { + handle_bitmaps(fontbuf + entry->offset); + } + } +} + +static char +get_glyph_pixel(struct glyph *glyph, int x, int y) +{ + int absx = glyph->hotx + x; + int absy = glyph->hoty + y; + + if (absx < 0 || absx >= glyph->width || + absy < 0 || absy >= glyph->height) + return 0; + + int stride = (glyph->width + 31) / 32 * 4; + unsigned char block = glyph->data[absy * stride + (absx/8)]; + int idx = absx % 8; + return (block >> idx) & 1; +} + +static struct { + uint32_t *data; + size_t capacity, size; +} data_buffer; + +static void +init_data_buffer() +{ + data_buffer.data = malloc(sizeof(uint32_t) * 10); + data_buffer.capacity = 10; + data_buffer.size = 0; +} + +static void +add_pixel(uint32_t pixel) +{ + if (data_buffer.size == data_buffer.capacity) { + data_buffer.capacity *= 2; + data_buffer.data = + realloc(data_buffer.data, + sizeof(uint32_t) * data_buffer.capacity); + } + data_buffer.data[data_buffer.size++] = pixel; +} + +struct reconstructed_glyph { + int32_t width, height; + int32_t hotspot_x, hotspot_y; + size_t offset; + char *name; +}; + +static void +reconstruct_glyph(struct glyph *cursor, struct glyph *mask, char *name, + struct reconstructed_glyph *glyph) +{ + int minx = min(-cursor->hotx, -mask->hotx); + int maxx = max(cursor->right_bearing, mask->right_bearing); + + int miny = min(-cursor->hoty, -mask->hoty); + int maxy = max(cursor->height - cursor->hoty, + mask->height - mask->hoty); + + int width = maxx - minx; + int height = maxy - miny; + + glyph->name = strdup(name); + glyph->width = width; + glyph->height = height; + glyph->hotspot_x = -minx; + glyph->hotspot_y = -miny; + glyph->offset = data_buffer.size; + + int x, y; + for (y = miny; y < maxy; ++y) { + for (x = minx; x < maxx; ++x) { + char alpha = get_glyph_pixel(mask, x, y); + if (alpha) { + char color = get_glyph_pixel(cursor, x, y); + if (color) + add_pixel(0xff000000); + else + add_pixel(0xffffffff); + } else { + add_pixel(0); + } + } + } +} + +/* From http://cgit.freedesktop.org/xorg/lib/libXfont/tree/src/builtins/fonts.c */ +static const char cursor_licence[] = + "/*\n" + "* Copyright 1999 SuSE, Inc.\n" + "*\n" + "* Permission to use, copy, modify, distribute, and sell this software and its\n" + "* documentation for any purpose is hereby granted without fee, provided that\n" + "* the above copyright notice appear in all copies and that both that\n" + "* copyright notice and this permission notice appear in supporting\n" + "* documentation, and that the name of SuSE not be used in advertising or\n" + "* publicity pertaining to distribution of the software without specific,\n" + "* written prior permission. SuSE makes no representations about the\n" + "* suitability of this software for any purpose. It is provided \"as is\"\n" + "* without express or implied warranty.\n" + "*\n" + "* SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL\n" + "* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE\n" + "* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n" + "* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION\n" + "* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n" + "* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n" + "*\n" + "* Author: Keith Packard, SuSE, Inc.\n" + "*/\n"; + +static void +write_output_file(struct reconstructed_glyph *glyphs, int n) +{ + int i, j, counter, size; + FILE *file = fopen("cursor_data.c", "w"); + uint32_t *data; + + fprintf(file, "%s\n", cursor_licence); + + fprintf(file, "static uint32_t cursor_data[] = {\n\t"); + + counter = 0; + for (i = 0; i < n; ++i) { + data = data_buffer.data + glyphs[i].offset; + size = glyphs[i].width * glyphs[i].height; + + for (j = 0; j < size; ++j) { + fprintf(file, "0x%08x, ", data[j]); + if (++counter % 6 == 0) + fprintf(file, "\n\t"); + } + } + fprintf(file, "\n};\n\n"); + + fprintf(file, + "static struct {\n" + "\tchar *name;\n" + "\tint width, height;\n" + "\tint hotspot_x, hotspot_y;\n" + "\tsize_t offset;\n" + "} cursor_metadata[] = {\n"); + + for (i = 0; i < n; ++i) + fprintf(file, "\t{ \"%s\", %d, %d, %d, %d, %zu },\n", + glyphs[i].name, + glyphs[i].width, glyphs[i].height, + glyphs[i].hotspot_x, glyphs[i].hotspot_y, + glyphs[i].offset); + + fprintf(file, "};"); + + fclose(file); +} + +struct glyph * +find_mask_glyph(char *name) +{ + const char mask[] = "_mask"; + const int masklen = strlen(mask); + + int len = strlen(name); + int i; + for (i = 0; i < extracted_font.count; ++i) { + struct glyph *g = &extracted_font.glyphs[i]; + int l2 = strlen(g->name); + if ((l2 == len + masklen) && + (memcmp(g->name, name, len) == 0) && + (memcmp(g->name + len, mask, masklen) == 0)) { + return g; + } + } + return NULL; +} + +static void +output_all_cursors() +{ + int i, j; + struct reconstructed_glyph *glyphs = + malloc(sizeof(struct reconstructed_glyph) * + extracted_font.count/2); + j = 0; + + for (i = 0; i < extracted_font.count; ++i) { + struct glyph *g = &extracted_font.glyphs[i]; + if (strstr(g->name, "_mask")) + continue; + + struct glyph *mask = find_mask_glyph(g->name); + + reconstruct_glyph(g, mask, g->name, &glyphs[j]); + j++; + } + + write_output_file(glyphs, extracted_font.count/2); +} + +static void +find_cursor_and_mask(const char *name, + struct glyph **cursor, + struct glyph **mask) +{ + int i; + char mask_name[100]; + sprintf(mask_name, "%s_mask", name); + + *cursor = *mask = NULL; + + for (i = 0; i < extracted_font.count && (!*mask || !*cursor); ++i) { + struct glyph *g = &extracted_font.glyphs[i]; + if (!strcmp(name, g->name)) + *cursor = g; + else if (!strcmp(mask_name, g->name)) + *mask = g; + } +} + +static struct { + char *target_name, *source_name; +} interesting_cursors[] = { + { "bottom_left_corner", "bottom_left_corner" }, + { "bottom_right_corner", "bottom_right_corner" }, + { "bottom_side", "bottom_side" }, + { "grabbing", "fleur" }, + { "left_ptr", "left_ptr" }, + { "left_side", "left_side" }, + { "right_side", "right_side" }, + { "top_left_corner", "top_left_corner" }, + { "top_right_corner", "top_right_corner" }, + { "top_side", "top_side" }, + { "xterm", "xterm" }, + { "hand1", "hand1" }, + { "watch", "watch" } +}; + +static void +output_interesting_cursors() +{ + int i; + int n = sizeof(interesting_cursors) / sizeof(interesting_cursors[0]); + struct reconstructed_glyph *glyphs = + malloc(n * sizeof(*glyphs)); + + for (i = 0; i < n; ++i) { + struct glyph *cursor, *mask; + find_cursor_and_mask(interesting_cursors[i].source_name, + &cursor, &mask); + if (!cursor) { + printf("no cursor for %s\n", + interesting_cursors[i].source_name); + abort(); + } + if (!mask) { + printf("no mask for %s\n", + interesting_cursors[i].source_name); + abort(); + } + reconstruct_glyph(cursor, mask, + interesting_cursors[i].target_name, + &glyphs[i]); + } + + write_output_file(glyphs, n); +} + +int main() +{ + const char filename[] = "cursor.pcf"; + + int fd = open(filename, O_RDONLY); + struct stat filestat; + + fstat(fd, &filestat); + + void *fontbuf = mmap(NULL, filestat.st_size, PROT_READ, + MAP_PRIVATE, fd, 0); + + handle_pcf(fontbuf); + + init_data_buffer(); + + //output_all_cursors(); + output_interesting_cursors(); +} diff --git a/cursor/cursor.pcf b/cursor/cursor.pcf new file mode 100644 index 0000000..812fcc5 Binary files /dev/null and b/cursor/cursor.pcf differ