Navigation C API Pages Python bindings Applications

Font and Text rendering

Fonttest

A simple program to show all font characters with different styles.

// SPDX-License-Identifier: GPL-2.1-or-later
/*
 * Copyright (C) 2009-2010 Jiri "BlueBear" Dluhos
 *                         <jiri.bluebear.dluhos@gmail.com>
 *
 * Copyright (C) 2009-2013 Cyril Hrubis <metan@ucw.cz>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <gfxprim.h>

static gp_backend *win;

static const char *font_path = NULL;
static unsigned int font_h = 16;

static gp_pixel white_pixel, gray_pixel, dark_gray_pixel, black_pixel,
                red_pixel, blue_pixel;

static const char *test_strings[] = {
        " !\"#$%&\047()*+,-./0123456789:;<=>?@",
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`",
        "abcdefghijklmnopqrstuvwxyz{|}~",
        "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor..."
};

static int font_flag = 0;
static int tracking = 0;
static gp_font_face *font = NULL;

static const char *glyph_bitmap_format_name(const gp_font_bitmap_format format)
{
        switch (format) {
        case GP_FONT_BITMAP_1BPP:
                return "1BPP";
        break;
        case GP_FONT_BITMAP_8BPP:
                return "8BPP";
        break;
        default:
                return "Unknown";
        }
}

static void print_character_metadata(const gp_font_face *font, int c)
{
        const gp_glyph *glyph = gp_get_glyph(font, c);
        fprintf(stderr, "Properties of the character '%c':\n", c);

        if (glyph) {
                fprintf(stderr, "   bitmap width: %d, height: %d\n",
                                glyph->width, glyph->height);
                fprintf(stderr, "          bearing_x: %d bearing_y %d\n",
                                glyph->bearing_x, glyph->bearing_y);
                fprintf(stderr, "          advance_x: %d\n",
                                glyph->advance_x);
        } else {
                fprintf(stderr, "(null)\n");
        }
}

static void print_font_properties(const gp_font_face *font)
{
        fprintf(stderr, "Font '%s %s' properties:\n",
                        gp_font_family(font), gp_font_style(font));
        fprintf(stderr, "    Height: ascend: %d, descend: %d\n",
                        gp_font_ascend(font), gp_font_descend(font));
        fprintf(stderr, "    Max advance_x: %u\n",
                        gp_font_max_advance_x(font));
        fprintf(stderr, "    Glyph bitmap format: %s\n",
                        glyph_bitmap_format_name(font->glyph_bitmap_format));
        fprintf(stderr, "    Bounding box width: %d, heigth: %d\n",
                        gp_font_max_width(font), gp_font_height(font));

        print_character_metadata(font, 'a');
        print_character_metadata(font, 'm');
        print_character_metadata(font, '0');
}

#define SPACING 120

void redraw_screen(void)
{
        gp_fill(win->pixmap, black_pixel);

        gp_text_style style = GP_DEFAULT_TEXT_STYLE;

        switch (font_flag) {
        case 0:
                style.font = gp_font_gfxprim;
        break;
        case 1:
                style.font = gp_font_gfxprim_mono;
        break;
        case 2:
                style.font = gp_font_tiny;
        break;
        case 3:
                style.font = gp_font_tiny_mono;
        break;
        case 4:
                style.font = gp_font_c64;
        break;
        case 5:
                style.font = gp_font_haxor_narrow_15;
        break;
        case 6:
                style.font = gp_font_haxor_narrow_16;
        break;
        case 7:
                style.font = gp_font_haxor_narrow_17;
        break;
        case 8:
                style.font = font;
        break;
        }

        print_font_properties(style.font);

        /* Text alignment (we are always drawing to the right
         * and below the starting point).
         */
        int align = GP_ALIGN_RIGHT|GP_VALIGN_BELOW;

        const size_t TEST_STRING_COUNT = sizeof(test_strings)/sizeof(const char *);
        size_t i;
        for (i = 0; i < TEST_STRING_COUNT; i++) {
                const char *test_string = test_strings[i];

                style.pixel_xmul = 1;
                style.pixel_ymul = 1;
                style.pixel_xspace = 0;
                style.pixel_yspace = 0;
                style.char_xspace = tracking;

                gp_fill_rect_xywh(win->pixmap,
                        16, SPACING*i + 16,
                        gp_text_width(&style, test_string),
                        gp_font_height(style.font),
                        dark_gray_pixel);

                gp_rect_xywh(win->pixmap,
                        15, SPACING*i + 15,
                        gp_text_max_width(&style, strlen(test_string)) + 1,
                        gp_font_height(style.font) + 1,
                        blue_pixel);

                gp_hline_xyw(win->pixmap, 15,
                             SPACING*i + 15 + gp_font_ascend(style.font),
                             gp_text_max_width(&style, strlen(test_string)) + 1,
                             blue_pixel);

                gp_text(win->pixmap, &style, 16, SPACING*i + 16, align,
                        white_pixel, dark_gray_pixel, test_string);


                style.pixel_xmul = 2;
                style.pixel_ymul = 2;
                style.pixel_yspace = 1;

                gp_text(win->pixmap, &style, 34, SPACING * i + 44, align,
                        white_pixel, black_pixel, test_string);

                gp_rect_xywh(win->pixmap, 33, SPACING * i + 43,
                             gp_text_width(&style, test_string) + 1,
                             gp_text_height(&style) + 1, dark_gray_pixel);

                style.pixel_xmul = 4;
                style.pixel_ymul = 2;

                style.pixel_xspace = 1;
                style.pixel_yspace = 1;

                if (font_flag == 2 || font_flag == 3) {
                        style.pixel_xmul = 2;
                        style.pixel_ymul = 5;

                        style.pixel_xspace = 2;
                        style.pixel_yspace = 2;
                }

                gp_text(win->pixmap, &style, 64, SPACING*i + 88, align,
                        dark_gray_pixel, black_pixel, test_string);
        }
}

void event_loop(void)
{
        gp_event ev;

        for (;;) {
                gp_backend_wait_event(win, &ev);

                switch (ev.type) {
                case GP_EV_KEY:
                        if (ev.code != GP_EV_KEY_DOWN)
                                continue;

                        switch (ev.key.key) {
                        case GP_KEY_SPACE:
                                if (font)
                                        font_flag = (font_flag + 1) % 9;
                                else
                                        font_flag = (font_flag + 1) % 8;

                                redraw_screen();
                                gp_backend_flip(win);
                        break;
                        case GP_KEY_UP:
                                tracking++;
                                redraw_screen();
                                gp_backend_flip(win);
                        break;
                        case GP_KEY_DOWN:
                                tracking--;
                                redraw_screen();
                                gp_backend_flip(win);
                        break;
                        case GP_KEY_B:
                                font_h++;
                                if (font_path) {
                                        gp_font_face_free(font);
                                        font = gp_font_face_load(font_path, 0, font_h);
                                        redraw_screen();
                                        gp_backend_flip(win);
                                }
                        break;
                        case GP_KEY_S:
                                font_h--;
                                if (font_path) {
                                        gp_font_face_free(font);
                                        font = gp_font_face_load(font_path, 0, font_h);
                                        redraw_screen();
                                        gp_backend_flip(win);
                                }
                        break;
                        case GP_KEY_ESC:
                                gp_backend_exit(win);
                                exit(0);
                        break;
                        }
                break;
                case GP_EV_SYS:
                        switch(ev.code) {
                        case GP_EV_SYS_QUIT:
                                gp_backend_exit(win);
                                exit(0);
                        break;
                        case GP_EV_SYS_RESIZE:
                                gp_backend_resize_ack(win);
                                redraw_screen();
                                gp_backend_flip(win);
                        break;
                        }
                break;
                }
        }
}

void print_instructions(void)
{
        printf("Use the following keys to control the test:\n");
        printf("    Esc ................. exit\n");
        printf("    Space ............... change font\n");
        printf("    up/down ............. increase/decrease tracking\n");
        printf("    b/s ................. change font size (freetype only)\n");
}

int main(int argc, char *argv[])
{
        const char *backend_opts = "X11";
        const char *font_face = NULL;
        int opt;

        print_instructions();

        while ((opt = getopt(argc, argv, "b:f:h")) != -1) {
                switch (opt) {
                case 'b':
                        backend_opts = optarg;
                break;
                case 'f':
                        font_face = optarg;
                break;
                case 'h':
                        printf("\nUsage: %s [-b backend] [-f font_face]\n\n", argv[0]);
                        gp_backend_init(NULL, NULL);
                        return 0;
                default:
                        fprintf(stderr, "Invalid paramter '%c'\n", opt);
                        return 1;
                }
        }

        if (font_face) {
                fprintf(stderr, "\nLoading font '%s'\n", font_face);
                font = gp_font_face_load(font_face, 0, font_h);
        }

        win = gp_backend_init(backend_opts, "Font Test");

        if (win == NULL) {
                fprintf(stderr, "Failed to initalize backend '%s'\n",
                        backend_opts);
                return 1;
        }

        white_pixel     = gp_rgb_to_pixmap_pixel(0xff, 0xff, 0xff, win->pixmap);
        gray_pixel      = gp_rgb_to_pixmap_pixel(0xbe, 0xbe, 0xbe, win->pixmap);
        dark_gray_pixel = gp_rgb_to_pixmap_pixel(0x7f, 0x7f, 0x7f, win->pixmap);
        black_pixel     = gp_rgb_to_pixmap_pixel(0x00, 0x00, 0x00, win->pixmap);
        red_pixel       = gp_rgb_to_pixmap_pixel(0xff, 0x00, 0x00, win->pixmap);
        blue_pixel      = gp_rgb_to_pixmap_pixel(0x00, 0x00, 0xff, win->pixmap);

        redraw_screen();
        gp_backend_flip(win);

        event_loop();

        return 0;
}

Fileview

A simple program to show contents of a file.

// SPDX-License-Identifier: GPL-2.1-or-later
/*
 * Copyright (C) 2009-2010 Jiri "BlueBear" Dluhos
 *                         <jiri.bluebear.dluhos@gmail.com>
 *
 * Copyright (C) 2009-2013 Cyril Hrubis <metan@ucw.cz>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <gfxprim.h>

static gp_backend *backend;

static gp_pixel white_pixel, gray_pixel, dark_gray_pixel, black_pixel,
                red_pixel, blue_pixel;

static int font_flag = 0;
static int tracking = 0;

static int mul = 1;
static int space = 0;

static gp_font_face *font;

struct file_line {
        char *text;
        struct file_line *next;
        struct file_line *prev;
};

struct file_line *first_line = NULL;
struct file_line *last_line = NULL;

void redraw_screen(void)
{
        gp_pixmap *win = backend->pixmap;

        gp_fill(win, gray_pixel);

        gp_text_style style = GP_DEFAULT_TEXT_STYLE;

        switch (font_flag) {
        case 0:
                style.font = gp_font_gfxprim_mono;
        break;
        case 1:
                style.font = gp_font_gfxprim;
        break;
        case 2:
                style.font = gp_font_tiny_mono;
        break;
        case 3:
                style.font = gp_font_tiny;
        break;
        case 4:
                style.font = gp_font_c64;
        break;
        case 5:
                style.font = font;
        break;
        }

        style.pixel_xmul = mul;
        style.pixel_ymul = mul;
        style.pixel_xspace = space;
        style.pixel_yspace = space;
        style.char_xspace = tracking;

        /* Text alignment (we are always drawing to the right
         * and below the starting point).
         */
        int align = GP_ALIGN_RIGHT|GP_VALIGN_BELOW;

        struct file_line *line = first_line;
        unsigned int i;
        for (i = 0; i < win->h/gp_text_height(&style); i++) {
                if (line == NULL)
                        break;
                gp_text(win, &style, 16, 16 + (1.0 * gp_text_height(&style))*i,
                        align, black_pixel, gray_pixel, line->text);
                line = line->next;
        }
}

static void warp_up(int lines)
{
        while (lines-- > 0) {
                if (first_line->prev != NULL)
                        first_line = first_line->prev;
        }

        redraw_screen();
        gp_backend_flip(backend);
}

static void warp_down(int lines)
{
        while (lines-- > 0) {
                if (first_line->next != NULL)
                        first_line = first_line->next;
        }

        redraw_screen();
        gp_backend_flip(backend);
}

void event_loop(void)
{
        gp_event ev;

        for (;;) {
                gp_backend_wait_event(backend, &ev);

                switch (ev.type) {
                case GP_EV_KEY:
                        if (ev.code != GP_EV_KEY_DOWN)
                                continue;

                        switch (ev.key.key) {
                        case GP_KEY_SPACE:
                                if (font)
                                        font_flag = (font_flag + 1) % 6;
                                else
                                        font_flag = (font_flag + 1) % 5;

                                redraw_screen();
                                gp_backend_flip(backend);
                        break;
                        case GP_KEY_RIGHT:
                                tracking++;
                                redraw_screen();
                                gp_backend_flip(backend);
                        break;
                        case GP_KEY_LEFT:
                                tracking--;
                                redraw_screen();
                                gp_backend_flip(backend);
                        break;
                        case GP_KEY_UP:
                                warp_up(1);
                        break;
                        case GP_KEY_DOWN:
                                warp_down(1);
                        break;
                        case GP_KEY_DOT:
                                space++;
                                redraw_screen();
                                gp_backend_flip(backend);
                        break;
                        case GP_KEY_COMMA:
                                space--;
                                redraw_screen();
                                gp_backend_flip(backend);
                        break;
                        case GP_KEY_RIGHT_BRACE:
                                mul++;
                                redraw_screen();
                                gp_backend_flip(backend);
                        break;
                        case GP_KEY_LEFT_BRACE:
                                if (mul > 0)
                                        mul--;
                                redraw_screen();
                                gp_backend_flip(backend);
                        break;
                        case GP_KEY_PAGE_UP:
                                warp_up(30);
                        break;
                        case GP_KEY_PAGE_DOWN:
                                warp_down(30);
                        break;
                        case GP_KEY_ESC:
                                gp_backend_exit(backend);
                                exit(0);
                        break;
                        }
                break;
                case GP_EV_SYS:
                        switch(ev.code) {
                        case GP_EV_SYS_QUIT:
                                gp_backend_exit(backend);
                                exit(0);
                        break;
                        case GP_EV_SYS_RESIZE:
                                gp_backend_resize_ack(backend);
                                redraw_screen();
                                gp_backend_flip(backend);
                        break;
                        }
                break;
                }
        }
}

static int read_file_head(const char *filename)
{
        FILE *f = fopen(filename, "r");
        char buf[512];

        if (f == NULL) {
                fprintf(stderr, "Could not open file: %s\n", filename);
                return 0;
        }

        for (;;) {

                if (fgets(buf, 511, f) == NULL)
                        break;

                struct file_line *line = malloc(sizeof(*line));
                line->text = strdup(buf);
                line->next = NULL;
                line->prev = NULL;

                if (first_line == NULL) {
                        first_line = line;
                        last_line = line;
                } else {
                        line->prev = last_line;
                        last_line->next = line;
                        last_line = line;
                }
        }

        fclose(f);
        return 1;
}

int main(int argc, char *argv[])
{
        const char *backend_opts = "X11";
        const char *font_face = NULL;
        int opt;

        while ((opt = getopt(argc, argv, "b:f:h")) != -1) {
                switch (opt) {
                case 'b':
                        backend_opts = optarg;
                break;
                case 'f':
                        font_face = optarg;
                break;
                case 'h':
                        printf("Usage: %s [-b backend] [-f font_face] filename\n\n", argv[0]);
                        gp_backend_init(NULL, NULL);
                        return 0;
                default:
                        fprintf(stderr, "Invalid paramter '%c'\n", opt);
                        return 1;
                }
        }

        if (font_face)
                font = gp_font_face_load(font_face, 0, 16);

        if (optind >= argc)
                fprintf(stderr, "Expected filename\n");

        if (!read_file_head(argv[optind]))
                return 1;

        backend = gp_backend_init(backend_opts, "File View");

        if (backend == NULL) {
                fprintf(stderr, "Failed to initalize backend '%s'\n",
                        backend_opts);
                return 1;
        }

        gp_pixmap *win = backend->pixmap;

        white_pixel     = gp_rgb_to_pixmap_pixel(0xff, 0xff, 0xff, win);
        gray_pixel      = gp_rgb_to_pixmap_pixel(0xbe, 0xbe, 0xbe, win);
        dark_gray_pixel = gp_rgb_to_pixmap_pixel(0x7f, 0x7f, 0x7f, win);
        black_pixel     = gp_rgb_to_pixmap_pixel(0x00, 0x00, 0x00, win);
        red_pixel       = gp_rgb_to_pixmap_pixel(0xff, 0x00, 0x00, win);
        blue_pixel      = gp_rgb_to_pixmap_pixel(0x00, 0x00, 0xff, win);

        redraw_screen();
        gp_backend_flip(backend);

        event_loop();

        return 0;
}