cursor: add cursor.pcf and extraction program
authorPhilipp Brüschweiler <blei42@gmail.com>
Thu, 6 Sep 2012 16:54:02 +0000 (18:54 +0200)
committerKristian Høgsberg <krh@bitplanet.net>
Tue, 11 Sep 2012 01:05:14 +0000 (21:05 -0400)
cursor/convert_font.c [new file with mode: 0644]
cursor/cursor.pcf [new file with mode: 0644]

diff --git a/cursor/convert_font.c b/cursor/convert_font.c
new file mode 100644 (file)
index 0000000..f297125
--- /dev/null
@@ -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 <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);
+                       }
+               }
+       }
+}
+
+/* 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 (file)
index 0000000..812fcc5
Binary files /dev/null and b/cursor/cursor.pcf differ