1/*
2 * Copyright © 2012 Philipp Brüschweiler
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23/*
24 * This is a small, hacky tool to extract cursors from a .pcf file.
25 * The information about the file format has been gathered from
26 * http://fontforge.org/pcf-format.html
27 */
28
29#include <assert.h>
30#include <fcntl.h>
31#include <stdint.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <sys/mman.h>
36#include <sys/stat.h>
37#include <sys/types.h>
38
39#define min(a, b) ((a) < (b) ? (a) : (b))
40#define max(a, b) ((a) > (b) ? (a) : (b))
41
42struct glyph {
43 char *name;
44 int16_t left_bearing, right_bearing, ascent, descent;
45
46 int16_t width, height;
47 int16_t hotx, hoty;
48
49 int32_t data_format;
50 char *data;
51};
52
53static struct {
54 int count;
55 struct glyph *glyphs;
56} extracted_font = {0, NULL};
57
58#define PCF_PROPERTIES (1 << 0)
59#define PCF_ACCELERATORS (1 << 1)
60#define PCF_METRICS (1 << 2)
61#define PCF_BITMAPS (1 << 3)
62#define PCF_INK_METRICS (1 << 4)
63#define PCF_BDF_ENCODINGS (1 << 5)
64#define PCF_SWIDTHS (1 << 6)
65#define PCF_GLYPH_NAMES (1 << 7)
66#define PCF_BDF_ACCELERATORS (1 << 8)
67
68#define PCF_DEFAULT_FORMAT 0x00000000
69#define PCF_INKBOUNDS 0x00000200
70#define PCF_ACCEL_W_INKBOUNDS 0x00000100
71#define PCF_COMPRESSED_METRICS 0x00000100
72
73#define PCF_FORMAT_MASK 0xffffff00
74
75struct pcf_header {
76 char header[4];
77 int32_t table_count;
78 struct toc_entry {
79 int32_t type;
80 int32_t format;
81 int32_t size;
82 int32_t offset;
83 } tables[];
84};
85
86struct compressed_metrics {
87 uint8_t left_sided_bearing;
88 uint8_t right_side_bearing;
89 uint8_t character_width;
90 uint8_t character_ascent;
91 uint8_t character_descent;
92};
93
94struct uncompressed_metrics {
95 int16_t left_sided_bearing;
96 int16_t right_side_bearing;
97 int16_t character_width;
98 int16_t character_ascent;
99 int16_t character_descent;
100 uint16_t character_attributes;
101};
102
103struct metrics {
104 int32_t format;
105 union {
106 struct {
107 int16_t count;
108 struct compressed_metrics compressed_metrics[];
109 } compressed;
110 struct {
111 int32_t count;
112 struct uncompressed_metrics uncompressed_metrics[];
113 } uncompressed;
114 };
115};
116
117struct glyph_names {
118 int32_t format;
119 int32_t glyph_count;
120 int32_t offsets[];
121};
122
123struct bitmaps {
124 int32_t format;
125 int32_t glyph_count;
126 int32_t offsets[];
127};
128
129static void
130handle_compressed_metrics(int32_t count, struct compressed_metrics *m)
131{
132#if ENABLE_DEBUG
133 fprintf(stderr, "metrics count: %d\n", count);
134#endif
135 extracted_font.count = count;
136 extracted_font.glyphs = calloc(count, sizeof(struct glyph));
137
138 int i;
139 for (i = 0; i < count; ++i) {
140 struct glyph *glyph = &extracted_font.glyphs[i];
141 glyph->left_bearing = ((int16_t)m[i].left_sided_bearing) - 0x80;
142 glyph->right_bearing = ((int16_t)m[i].right_side_bearing) - 0x80;
143 glyph->width = ((int16_t)m[i].character_width) - 0x80;
144 glyph->ascent = ((int16_t)m[i].character_ascent) - 0x80;
145 glyph->descent = ((int16_t)m[i].character_descent) - 0x80;
146
147 /* computed stuff */
148 glyph->height = glyph->ascent + glyph->descent;
149
150 glyph->hotx = -glyph->left_bearing;
151 glyph->hoty = glyph->ascent;
152 }
153}
154
155static void
156handle_metrics(void *metricbuf)
157{
158 struct metrics *metrics = metricbuf;
159#if ENABLE_DEBUG
160 fprintf(stderr, "metric format: %x\n", metrics->format);
161#endif
162
163 if ((metrics->format & PCF_FORMAT_MASK) == PCF_DEFAULT_FORMAT) {
164#if ENABLE_DEBUG
165 fprintf(stderr, "todo...\n");
166#endif
167 } else if ((metrics->format & PCF_FORMAT_MASK) == PCF_COMPRESSED_METRICS) {
168 handle_compressed_metrics(metrics->compressed.count,
169 &metrics->compressed.compressed_metrics[0]);
170 } else {
171#if ENABLE_DEBUG
172 fprintf(stderr, "incompatible format\n");
173#endif
174 abort();
175 }
176}
177
178static void
179handle_glyph_names(struct glyph_names *names)
180{
181#if ENABLE_DEBUG
182 fprintf(stderr, "glyph count %d\n", names->glyph_count);
183#endif
184
185 if (names->glyph_count != extracted_font.count) {
186 abort();
187 }
188
189#if ENABLE_DEBUG
190 fprintf(stderr, "glyph names format %x\n", names->format);
191#endif
192
193 char *names_start = ((char *)names) + sizeof(struct glyph_names) +
194 (names->glyph_count + 1) * sizeof(int32_t);
195
196 int i;
197 for (i = 0; i < names->glyph_count; ++i) {
198 int32_t start = names->offsets[i];
199 int32_t end = names->offsets[i + 1];
200 char *name = names_start + start;
201 extracted_font.glyphs[i].name = calloc(1, end - start + 1);
202 memcpy(extracted_font.glyphs[i].name, name, end - start);
203 }
204}
205
206static void
207handle_bitmaps(struct bitmaps *bitmaps)
208{
209#if ENABLE_DEBUG
210 fprintf(stderr, "bitmaps count %d\n", bitmaps->glyph_count);
211#endif
212
213 if (bitmaps->glyph_count != extracted_font.count) {
214 abort();
215 }
216#if ENABLE_DEBUG
217 fprintf(stderr, "format %x\n", bitmaps->format);
218#endif
219
220 if (bitmaps->format != 2) {
221#if ENABLE_DEBUG
222 fprintf(stderr, "format not yet supported\n");
223#endif
224 abort();
225 }
226
227 char *bitmaps_start = ((char *)bitmaps) + sizeof(struct bitmaps) +
228 (bitmaps->glyph_count + 4) * sizeof(int32_t);
229
230 for (unsigned i = 0; i < bitmaps->glyph_count; ++i) {
231 int32_t offset = bitmaps->offsets[i];
232 struct glyph *glyph = &extracted_font.glyphs[i];
233 glyph->data_format = bitmaps->format;
234
235 glyph->data = bitmaps_start + offset;
236 }
237}
238
239static void
240handle_pcf(void *fontbuf)
241{
242 struct pcf_header *header = fontbuf;
243#if ENABLE_DEBUG
244 fprintf(stderr, "tablecount %d\n", header->table_count);
245#endif
246
247 for (unsigned i = 0; i < header->table_count; ++i) {
248 struct toc_entry *entry = &header->tables[i];
249#if ENABLE_DEBUG
250 fprintf(stderr, "type: %d\n", entry->type);
251#endif
252 if (entry->type == PCF_METRICS) {
253 handle_metrics((void *)((uintptr_t)fontbuf + entry->offset));
254 } else if (entry->type == PCF_GLYPH_NAMES) {
255 handle_glyph_names((void *)((uintptr_t)fontbuf + entry->offset));
256 } else if (entry->type == PCF_BITMAPS) {
257 handle_bitmaps((void *)((uintptr_t)fontbuf + entry->offset));
258 }
259 }
260}
261
262static char
263get_glyph_pixel(struct glyph *glyph, int x, int y)
264{
265 int absx = glyph->hotx + x;
266 int absy = glyph->hoty + y;
267
268 if (absx < 0 || absx >= glyph->width || absy < 0 || absy >= glyph->height) {
269 return 0;
270 }
271
272 int stride = (glyph->width + 31) / 32 * 4;
273 unsigned char block = glyph->data[absy * stride + (absx / 8)];
274 int idx = absx % 8;
275 return (block >> idx) & 1;
276}
277
278static struct {
279 uint32_t *data;
280 size_t capacity, size;
281} data_buffer;
282
283static void
284init_data_buffer(void)
285{
286 data_buffer.data = malloc(sizeof(uint32_t) * 10);
287 data_buffer.capacity = 10;
288 data_buffer.size = 0;
289}
290
291static void
292add_pixel(uint32_t pixel)
293{
294 if (data_buffer.size == data_buffer.capacity) {
295 data_buffer.capacity *= 2;
296 data_buffer.data =
297 realloc(data_buffer.data, sizeof(uint32_t) * data_buffer.capacity);
298 }
299 data_buffer.data[data_buffer.size++] = pixel;
300}
301
302struct reconstructed_glyph {
303 int32_t width, height;
304 int32_t hotspot_x, hotspot_y;
305 size_t offset;
306 char *name;
307};
308
309static void
310reconstruct_glyph(struct glyph *cursor, struct glyph *mask, char *name,
311 struct reconstructed_glyph *glyph)
312{
313 int minx = min(-cursor->hotx, -mask->hotx);
314 int maxx = max(cursor->right_bearing, mask->right_bearing);
315
316 int miny = min(-cursor->hoty, -mask->hoty);
317 int maxy = max(cursor->height - cursor->hoty, mask->height - mask->hoty);
318
319 int width = maxx - minx;
320 int height = maxy - miny;
321
322 glyph->name = strdup(name);
323 glyph->width = width;
324 glyph->height = height;
325 glyph->hotspot_x = -minx;
326 glyph->hotspot_y = -miny;
327 glyph->offset = data_buffer.size;
328
329 int x, y;
330 for (y = miny; y < maxy; ++y) {
331 for (x = minx; x < maxx; ++x) {
332 char alpha = get_glyph_pixel(mask, x, y);
333 if (alpha) {
334 char color = get_glyph_pixel(cursor, x, y);
335 if (color) {
336 add_pixel(0xff000000);
337 } else {
338 add_pixel(0xffffffff);
339 }
340 } else {
341 add_pixel(0);
342 }
343 }
344 }
345}
346
347/* From http://cgit.freedesktop.org/xorg/lib/libXfont/tree/src/builtins/fonts.c
348 */
349static const char cursor_licence[] =
350 "/*\n"
351 "* Copyright 1999 SuSE, Inc.\n"
352 "*\n"
353 "* Permission to use, copy, modify, distribute, and sell this software and "
354 "its\n"
355 "* documentation for any purpose is hereby granted without fee, provided "
356 "that\n"
357 "* the above copyright notice appear in all copies and that both that\n"
358 "* copyright notice and this permission notice appear in supporting\n"
359 "* documentation, and that the name of SuSE not be used in advertising or\n"
360 "* publicity pertaining to distribution of the software without specific,\n"
361 "* written prior permission. SuSE makes no representations about the\n"
362 "* suitability of this software for any purpose. It is provided \"as "
363 "is\"\n"
364 "* without express or implied warranty.\n"
365 "*\n"
366 "* SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING "
367 "ALL\n"
368 "* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL "
369 "SuSE\n"
370 "* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY "
371 "DAMAGES\n"
372 "* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN "
373 "ACTION\n"
374 "* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN\n"
375 "* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n"
376 "*\n"
377 "* Author: Keith Packard, SuSE, Inc.\n"
378 "*/\n";
379
380static void
381write_output_file(FILE *file, struct reconstructed_glyph *glyphs, int n)
382{
383 int i, j, counter, size;
384 uint32_t *data;
385
386 fprintf(file, "%s\n", cursor_licence);
387
388 fprintf(file, "static uint32_t cursor_data[] = {\n\t");
389
390 counter = 0;
391 for (i = 0; i < n; ++i) {
392 data = data_buffer.data + glyphs[i].offset;
393 size = glyphs[i].width * glyphs[i].height;
394
395 for (j = 0; j < size; ++j) {
396 fprintf(file, "0x%08x, ", data[j]);
397 if (++counter % 6 == 0) {
398 fprintf(file, "\n\t");
399 }
400 }
401 }
402 fprintf(file, "\n};\n\n");
403
404 fputs("enum cursor_type {\n", file);
405
406 for (i = 0; i < n; ++i) {
407 fprintf(file, "\tcursor_%s,\n", glyphs[i].name);
408 }
409
410 fputs("};\n\n", file);
411
412 fprintf(file,
413 "static struct cursor {\n"
414 "\tint width, height;\n"
415 "\tint hotspot_x, hotspot_y;\n"
416 "\tsize_t offset;\n"
417 "} cursor_metadata[] = {\n");
418
419 for (i = 0; i < n; ++i) {
420 fprintf(file, "\t{ %d, %d, %d, %d, %zu }, /* %s */\n", glyphs[i].width,
421 glyphs[i].height, glyphs[i].hotspot_x, glyphs[i].hotspot_y,
422 glyphs[i].offset, glyphs[i].name);
423 }
424
425 fputs("};\n", file);
426}
427
428struct glyph *
429find_mask_glyph(char *name)
430{
431 const char mask[] = "_mask";
432 const int masklen = strlen(mask);
433
434 int len = strlen(name);
435 int i;
436 for (i = 0; i < extracted_font.count; ++i) {
437 struct glyph *g = &extracted_font.glyphs[i];
438 int l2 = strlen(g->name);
439 if ((l2 == len + masklen) && (memcmp(g->name, name, len) == 0) &&
440 (memcmp(g->name + len, mask, masklen) == 0)) {
441 return g;
442 }
443 }
444 return NULL;
445}
446
447static void
448find_cursor_and_mask(const char *name,
449 struct glyph **cursor,
450 struct glyph **mask)
451{
452 int i;
453 char mask_name[100];
454 sprintf(mask_name, "%s_mask", name);
455
456 *cursor = *mask = NULL;
457
458 for (i = 0; i < extracted_font.count && (!*mask || !*cursor); ++i) {
459 struct glyph *g = &extracted_font.glyphs[i];
460 if (!strcmp(name, g->name)) {
461 *cursor = g;
462 } else if (!strcmp(mask_name, g->name)) {
463 *mask = g;
464 }
465 }
466}
467
468static struct {
469 char *target_name, *source_name;
470} interesting_cursors[] = {{"bottom_left_corner", "bottom_left_corner"},
471 {"bottom_right_corner", "bottom_right_corner"},
472 {"bottom_side", "bottom_side"},
473 {"grabbing", "fleur"},
474 {"left_ptr", "left_ptr"},
475 {"left_side", "left_side"},
476 {"right_side", "right_side"},
477 {"top_left_corner", "top_left_corner"},
478 {"top_right_corner", "top_right_corner"},
479 {"top_side", "top_side"},
480 {"xterm", "xterm"},
481 {"hand1", "hand1"},
482 {"watch", "watch"}};
483
484static void
485output_interesting_cursors(FILE *file)
486{
487 int i;
488 int n = sizeof(interesting_cursors) / sizeof(interesting_cursors[0]);
489 struct reconstructed_glyph *glyphs = malloc(n * sizeof(*glyphs));
490
491 for (i = 0; i < n; ++i) {
492 struct glyph *cursor, *mask;
493 find_cursor_and_mask(interesting_cursors[i].source_name, &cursor,
494 &mask);
495 if (!cursor) {
496#if ENABLE_DEBUG
497 fprintf(stderr, "no cursor for %s\n",
498 interesting_cursors[i].source_name);
499#endif
500 abort();
501 }
502 if (!mask) {
503#if ENABLE_DEBUG
504 fprintf(stderr, "no mask for %s\n",
505 interesting_cursors[i].source_name);
506#endif
507 abort();
508 }
509 reconstruct_glyph(cursor, mask, interesting_cursors[i].target_name,
510 &glyphs[i]);
511 }
512
513 write_output_file(file, glyphs, n);
514}
515
516int
517main(int argc, char *argv[])
518{
519 if (argc != 3) {
520#if ENABLE_DEBUG
521 fprintf(stderr, "Usage: %s input.pcf output.h\n", argv[0]);
522#endif
523 return EXIT_FAILURE;
524 }
525
526 int fd = open(argv[1], O_RDONLY);
527 struct stat filestat;
528
529 fstat(fd, &filestat);
530
531 void *fontbuf = mmap(NULL, filestat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
532
533 handle_pcf(fontbuf);
534
535 init_data_buffer();
536
537 FILE *file = fopen(argv[2], "w");
538 output_interesting_cursors(file);
539 fclose(file);
540
541 return EXIT_SUCCESS;
542}