main neuswc / cursor / convert_font.c
  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}