/* * Copyright © 2012 Philipp Brüschweiler * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * 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 <assert.h> #include <fcntl.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #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); } } } } /* * Originally from * http://cgit.freedesktop.org/xorg/lib/libXfont/tree/src/builtins/fonts.c * Changed to the MIT "Expat" style license for Wayland.. */ static const char cursor_licence[] = "/*\n" "* Copyright 1999 SuSE, Inc.\n" "*\n" "* Permission is hereby granted, free of charge, to any person obtaining\n" "* a copy of this software and associated documentation files (the\n" "* \"Software\"), to deal in the Software without restriction, including\n" "* without limitation the rights to use, copy, modify, merge, publish,\n" "* distribute, sublicense, and/or sell copies of the Software, and to\n" "* permit persons to whom the Software is furnished to do so, subject to\n" "* the following conditions:\n" "*\n" "* The above copyright notice and this permission notice (including the\n" "* next paragraph) shall be included in all copies or substantial\n" "* portions of the Software.\n" "*\n" "* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n" "* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n" "* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n" "* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n" "* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n" "* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n" "* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" "* 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.h", "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(); }