URI: 
       tAdd current WIP on complex text rendering; misc. changes - ltkx - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltkx.git
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit 1f9c27cd41ea59996f06c0ac265ef8f7ac6a7856
   DIR parent a8d438e533db40481c75ddd952a14f14b6f68c94
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Fri, 20 Jul 2018 20:11:34 +0200
       
       Add current WIP on complex text rendering; misc. changes
       
       Note: the text rendering is in a very early stage - I didn't even bother to add delete functions for anything yet.
       The current up-to-date file for the text rendering engine is text-hb.ubernew.c.
       
       /me shall now go on hiatus for a while in order to learn a lot about Xlib and other things so he can come back and actually have a clue what he's doing.
       
       Diffstat:
         M LICENSE                             |       2 +-
         A Lumidify_Casual.ttf                 |       0 
         M Makefile                            |       8 ++++----
         M README.md                           |      14 +++++++++-----
         M button.c                            |     280 ++++++++++++++-----------------
         M button.h                            |      98 ++++++++++---------------------
         D cJSON.c                             |    1492 -------------------------------
         D cJSON.h                             |     160 -------------------------------
         M grid.c                              |     521 ++++++++++++++++---------------
         M grid.h                              |      34 ++++++++++++++++----------------
         A ini.c                               |     201 ++++++++++++++++++++++++++++++
         A ini.h                               |     104 +++++++++++++++++++++++++++++++
         M ltk.c                               |     650 ++++++++++++++++---------------
         M ltk.h                               |     303 ++++++++-----------------------
         M main.c                              |      83 ++++++++++++++++---------------
         A stb_truetype.h                      |    4021 +++++++++++++++++++++++++++++++
         A temp                                |       1 +
         M test1.c                             |      63 ++++++++++++++++++-------------
         A text.c                              |     131 +++++++++++++++++++++++++++++++
         A text.c.bak                          |     161 +++++++++++++++++++++++++++++++
         A text.h                              |      50 +++++++++++++++++++++++++++++++
         A text/:                              |     249 +++++++++++++++++++++++++++++++
         A text/Awami_beta3.ttf                |       0 
         A text/FONTLOG.txt                    |     284 +++++++++++++++++++++++++++++++
         A text/GENTIUM-FAQ.txt                |     205 ++++++++++++++++++++++++++++++
         A text/GentiumPlus-I.ttf              |       0 
         A text/GentiumPlus-R.ttf              |       0 
         A text/LICENSE_OFL.txt                |      92 +++++++++++++++++++++++++++++++
         A text/Makefile                       |       2 ++
         A text/NotoNastaliqUrdu-Regular.ttf   |       0 
         A text/OFL-FAQ.txt                    |     427 +++++++++++++++++++++++++++++++
         A text/OFL.txt                        |      94 +++++++++++++++++++++++++++++++
         A text/README                         |       5 +++++
         A text/README.txt                     |      88 +++++++++++++++++++++++++++++++
         A text/bob.c                          |      28 ++++++++++++++++++++++++++++
         A text/documentation/AwamiNastaliq-F… |       0 
         A text/documentation/AwamiNastaliq-F… |       0 
         A text/documentation/AwamiNastaliq-T… |       0 
         A text/documentation/AwamiNastaliq-T… |       0 
         A text/documentation/DOCUMENTATION.t… |      11 +++++++++++
         A text/documentation/GentiumPlus-fea… |       0 
         A text/documentation/GentiumPlus-fea… |       0 
         A text/font.ttf                       |       0 
         A text/font1.ttf                      |       0 
         A text/font2.ttf                      |       0 
         A text/hashtest.c                     |      27 +++++++++++++++++++++++++++
         A text/khash.h                        |     627 +++++++++++++++++++++++++++++++
         A text/main.c                         |     200 +++++++++++++++++++++++++++++++
         A text/main1.c                        |      94 +++++++++++++++++++++++++++++++
         A text/new.c                          |     126 +++++++++++++++++++++++++++++++
         A text/stb_image_write.h              |    1048 +++++++++++++++++++++++++++++++
         A text/stb_truetype.h                 |    4018 +++++++++++++++++++++++++++++++
         A text/test data/RandomWords.odt      |       0 
         A text/test data/RandomWords.pdf      |       0 
         A text/test data/RandomWords.xml      |      30 ++++++++++++++++++++++++++++++
         A text/test data/ftml_wf.xsl          |     253 +++++++++++++++++++++++++++++++
         A text/test data/language data/Kalam… |       0 
         A text/test data/language data/Kalam… |       0 
         A text/test data/language data/Khowa… |       0 
         A text/test data/language data/Khowa… |       0 
         A text/test data/language data/Palul… |       0 
         A text/test data/language data/Palul… |       0 
         A text/test data/language data/Sarai… |       0 
         A text/test data/language data/Sarai… |       0 
         A text/test data/language data/Sarai… |       0 
         A text/test data/language data/Sarai… |       0 
         A text/test data/language data/Shina… |       0 
         A text/test data/language data/Shina… |       0 
         A text/test data/language data/Urdu-… |       0 
         A text/test data/language data/Urdu-… |       0 
         A text/test data/language data/UrduW… |       0 
         A text/test data/language data/UrduW… |       0 
         A text/test data/letter combinations… |     251 +++++++++++++++++++++++++++++++
         A text/test data/letter combinations… |       0 
         A text/test data/letter combinations… |    2324 +++++++++++++++++++++++++++++++
         A text/test data/letter combinations… |       0 
         A text/test data/letter combinations… |    6792 +++++++++++++++++++++++++++++++
         A text/test data/letter combinations… |       0 
         A text/test data/letter combinations… |    2626 +++++++++++++++++++++++++++++++
         A text/test.c                         |      61 +++++++++++++++++++++++++++++++
         A text/test23.c                       |      55 +++++++++++++++++++++++++++++++
         A text/test3.c                        |     131 +++++++++++++++++++++++++++++++
         A text/text-hb.c                      |     362 +++++++++++++++++++++++++++++++
         A text/text-hb.new.c                  |     386 +++++++++++++++++++++++++++++++
         A text/text-hb.ubernew.c              |     452 +++++++++++++++++++++++++++++++
         A text/text.c                         |     247 +++++++++++++++++++++++++++++++
         A text/text1.c                        |     312 +++++++++++++++++++++++++++++++
         A text/text2.c                        |     282 +++++++++++++++++++++++++++++++
         A text/utf8.c                         |     730 +++++++++++++++++++++++++++++++
         A text/utf8.h                         |     115 +++++++++++++++++++++++++++++++
         A text/utf8_new.c                     |      22 ++++++++++++++++++++++
         A theme.c                             |      82 +++++++++++++++++++++++++++++++
         A theme.h                             |      23 +++++++++++++++++++++++
         M themes/default.ini                  |      35 +++++++++++++++++--------------
       
       94 files changed, 28797 insertions(+), 2776 deletions(-)
       ---
   DIR diff --git a/LICENSE b/LICENSE
       t@@ -1,7 +1,7 @@
        MIT/X Consortium License
        
        The Lumidify ToolKit (LTK)
       -Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       +Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org>
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
   DIR diff --git a/Lumidify_Casual.ttf b/Lumidify_Casual.ttf
       Binary files differ.
   DIR diff --git a/Makefile b/Makefile
       t@@ -1,7 +1,7 @@
       -LIBS = -lX11 -lm -ldl
       -STD = -std=c89
       -FLAGS = -g -w -Wall -Werror -Wextra -pedantic
       -CFILES = ltk.c cJSON.c grid.c button.c test1.c
       +LIBS = -lX11 -lm -L/usr/X11R6/lib
       +STD = -std=c99
       +FLAGS = -g -w -fcommon -Wall -Werror -Wextra -I/usr/X11R6/include #-pedantic
       +CFILES = text.c ltk.c ini.c grid.c button.c test1.c
        
        all: test1.c
                gcc $(STD) $(FLAGS) $(LIBS) $(CFILES) -o test
   DIR diff --git a/README.md b/README.md
       t@@ -1,11 +1,15 @@
       -# LTK - Lumidify ToolKit
       +#LTK - Lumidify ToolKit
        
       -This is work in progress.
       -
       -Please do not attempt to actually use any of the code.
       +This is work in progress. Please do not attempt to actually use any of the code.
        
        ## Licenses of Other Libraries Used
        
        [uthash](https://troydhanson.github.io/uthash/) by Troy D. Hanson: [BSD Revised](https://troydhanson.github.io/uthash/license.html)
        
       -[cJSON](https://github.com/DaveGamble/cJSON) by Dave Gamble: [MIT/X](https://github.com/DaveGamble/cJSON/blob/master/LICENSE)
       +[inih](https://github.com/benhoyt/inih) by Ben Hoyt: [New BSD](https://github.com/benhoyt/inih/blob/master/LICENSE.txt)
       +
       +[stb_truetype](https://github.com/nothings/stb/blob/master/stb_truetype.h) by Sean T. Barrett: [MIT/Public Domain](https://github.com/nothings/stb/blob/e6afb9cbae4064da8c3e69af3ff5c4629579c1d2/stb_truetype.h#L4815)
       +
       +[cutef8](https://github.com/JeffBezanson/cutef8/) by Jeff Bezanson: [Public Domain](https://github.com/JeffBezanson/cutef8/blob/ce8607864ef59ceef39fc20c9653265f6b91d4bc/utf8.c#L4)
       +
       +Note: LTK is in no way affiliated with any of the projects listed above.
   DIR diff --git a/button.c b/button.c
       t@@ -23,178 +23,154 @@
        
        #include "ltk.h"
        
       -LtkButtonTheme *ltk_parse_button_theme(cJSON *button_json)
       +void ltk_button_ini_handler(LtkTheme *theme, const char *prop, const char *value)
        {
       -    LtkButtonTheme *button_theme = malloc(sizeof(LtkButtonTheme));
       -    cJSON *normal_json = cJSON_GetObjectItem(button_json, "normal");
       -    if (!normal_json)
       -    {
       -        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       -    }
       -    cJSON *border_width = cJSON_GetObjectItem(normal_json, "border-width");
       -    cJSON *font_size = cJSON_GetObjectItem(normal_json, "font-size");
       -    cJSON *border_color = cJSON_GetObjectItem(normal_json, "border-color");
       -    cJSON *fill_color = cJSON_GetObjectItem(normal_json, "fill-color");
       -    cJSON *padding_left = cJSON_GetObjectItem(normal_json, "padding-left");
       -    cJSON *padding_right = cJSON_GetObjectItem(normal_json, "padding-right");
       -    cJSON *padding_top = cJSON_GetObjectItem(normal_json, "padding-top");
       -    cJSON *padding_bottom = cJSON_GetObjectItem(normal_json, "padding-bottom");
       -
       -    button_theme->border_width = border_width != NULL ? border_width->valueint : 0;
       -    button_theme->font_size = font_size != NULL ? font_size->valueint : 20;
       -    button_theme->border_color = ltk_create_xcolor(border_color->valuestring);
       -    button_theme->fill_color = ltk_create_xcolor(fill_color->valuestring);
       -    button_theme->padding_left = padding_left != NULL ? padding_left->valueint : 0;
       -    button_theme->padding_right = padding_right != NULL ? padding_right->valueint : 0;
       -    button_theme->padding_top = padding_top != NULL ? padding_top->valueint : 0;
       -    button_theme->padding_bottom = padding_bottom != NULL ? padding_bottom->valueint : 0;
       -
       -    cJSON *hover_json = cJSON_GetObjectItem(button_json, "hover");
       -    if (!hover_json)
       -        {
       -        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       -    }
       -    cJSON *border_width_hover = cJSON_GetObjectItem(hover_json, "border-width");
       -    cJSON *font_size_hover = cJSON_GetObjectItem(hover_json, "font-size");
       -    cJSON *border_color_hover = cJSON_GetObjectItem(hover_json, "border-color");
       -    cJSON *fill_color_hover = cJSON_GetObjectItem(hover_json, "fill-color");
       -
       -    button_theme->border_width_hover = border_width_hover != NULL ? border_width_hover->valueint : button_theme->border_width;
       -    button_theme->font_size_hover = font_size_hover != NULL ? font_size_hover->valueint : button_theme->font_size;
       -    button_theme->border_color_hover = border_color_hover != NULL ? ltk_create_xcolor(border_color_hover->valuestring) : button_theme->border_color;
       -    button_theme->fill_color_hover = fill_color_hover != NULL ? ltk_create_xcolor(fill_color_hover->valuestring) : button_theme->fill_color;
       -
       -    cJSON *pressed_json = cJSON_GetObjectItem(button_json, "pressed");
       -    if (!pressed_json)
       -    {
       -        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       -    }
       -    cJSON *border_width_pressed = cJSON_GetObjectItem(pressed_json, "border-width");
       -    cJSON *font_size_pressed = cJSON_GetObjectItem(pressed_json, "font-size");
       -    cJSON *border_color_pressed = cJSON_GetObjectItem(pressed_json, "border-color");
       -    cJSON *fill_color_pressed = cJSON_GetObjectItem(pressed_json, "fill-color");
       -
       -    button_theme->border_width_pressed = border_width_pressed != NULL ? border_width_pressed->valueint : button_theme->border_width;
       -    button_theme->font_size_pressed = font_size_hover != NULL ? font_size_pressed->valueint : button_theme->font_size;
       -    button_theme->border_color_pressed = border_color_pressed != NULL ? ltk_create_xcolor(border_color_pressed->valuestring) : button_theme->border_color;
       -    button_theme->fill_color_pressed = fill_color_pressed != NULL ? ltk_create_xcolor(fill_color_pressed->valuestring) : button_theme->fill_color;
       -
       -    cJSON *active_json = cJSON_GetObjectItem(button_json, "active");
       -    if (!active_json)
       -    {
       -        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       -    }
       -    cJSON *border_width_active = cJSON_GetObjectItem(active_json, "border-width");
       -    cJSON *font_size_active = cJSON_GetObjectItem(active_json, "font-size");
       -    cJSON *border_color_active = cJSON_GetObjectItem(active_json, "border-color");
       -    cJSON *fill_color_active = cJSON_GetObjectItem(active_json, "fill-color");
       -
       -    button_theme->border_width_active = border_width_active != NULL ? border_width_active->valueint : button_theme->border_width;
       -    button_theme->font_size_active = font_size_active != NULL ? font_size_active->valueint : button_theme->font_size;
       -    button_theme->border_color_active = border_color_active != NULL ? ltk_create_xcolor(border_color_active->valuestring) : button_theme->border_color;
       -    button_theme->fill_color_active = fill_color_active != NULL ? ltk_create_xcolor(fill_color_active->valuestring) : button_theme->fill_color;
       -
       -    cJSON *disabled_json = cJSON_GetObjectItem(button_json, "disabled");
       -    if (!disabled_json)
       -    {
       -        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       -    }
       -    cJSON *border_width_disabled = cJSON_GetObjectItem(disabled_json, "border-width");
       -    cJSON *font_size_disabled = cJSON_GetObjectItem(disabled_json, "font-size");
       -    cJSON *border_color_disabled = cJSON_GetObjectItem(disabled_json, "border-color");
       -    cJSON *fill_color_disabled = cJSON_GetObjectItem(disabled_json, "fill-color");
       -
       -    button_theme->border_width_disabled = border_width_disabled != NULL ? border_width_disabled->valueint : button_theme->border_width;
       -    button_theme->font_size_disabled = font_size_disabled != NULL ? font_size_disabled->valueint : button_theme->font_size;
       -    button_theme->border_color_disabled = border_color_disabled != NULL ? ltk_create_xcolor(border_color_disabled->valuestring) : button_theme->border_color;
       -    button_theme->fill_color_disabled = fill_color_disabled != NULL ? ltk_create_xcolor(fill_color_disabled->valuestring) : button_theme->fill_color;
       +        if (strcmp(prop, "border_width") == 0) {
       +                theme->button->border_width = atoi(value);
       +        } else if (strcmp(prop, "font_size") == 0) {
       +                theme->button->font_size = atoi(value);
       +        } else if (strcmp(prop, "padl") == 0) {
       +                theme->button->padl = atoi(value);
       +        } else if (strcmp(prop, "padr") == 0) {
       +                theme->button->padr = atoi(value);
       +        } else if (strcmp(prop, "padt") == 0) {
       +                theme->button->padt = atoi(value);
       +        } else if (strcmp(prop, "padb") == 0) {
       +                theme->button->padb = atoi(value);
       +        } else if (strcmp(prop, "border") == 0) {
       +                theme->button->border = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "fill") == 0) {
       +                theme->button->fill = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "border_hover") == 0) {
       +                theme->button->border_hover = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "fill_hover") == 0) {
       +                theme->button->fill_hover = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "border_pressed") == 0) {
       +                theme->button->border_pressed = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "fill_pressed") == 0) {
       +                theme->button->fill_pressed = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "border_active") == 0) {
       +                theme->button->border_active = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "fill_active") == 0) {
       +                theme->button->fill_active = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "border_disabled") == 0) {
       +                theme->button->border_disabled = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "fill_disabled") == 0) {
       +                theme->button->fill_disabled = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "text_color") == 0) {
       +                theme->button->text_color = ltk_create_xcolor(value);
       +        } else {
       +                printf("WARNING: Unknown property \"%s\" for button style.\n");
       +        }
       +}
        
       -    return button_theme;
       +/* FIXME: this is probably really slow and there is probably a really simple alternative */
       +unsigned long ltk_blend_pixel(XColor fg, XColor bg, double a)
       +{
       +        XColor blended;
       +        if (a == 1.0)
       +                return fg.pixel;
       +        else if (a == 0.0)
       +                return bg.pixel;
       +        blended.red = (int)((fg.red - bg.red) * a + bg.red);
       +        blended.green = (int)((fg.green - bg.green) * a + bg.green);
       +        blended.blue = (int)((fg.blue - bg.blue) * a + bg.blue);
       +        XAllocColor(ltk_global->display, ltk_global->colormap, &blended);
       +
       +        return blended.pixel;
        }
        
        void ltk_draw_button(LtkButton *button)
        {
       -    LtkButtonTheme *theme = ltk_global->theme->button;
       -    LtkWindow *window = button->widget.window;
       -    LtkRect rect = button->widget.rect;
       -    XColor border_color;
       -    XColor fill_color;
       -    int border_width;
       -    switch (button->widget.state)
       -    {
       -    case LTK_NORMAL:
       -        border_color = theme->border_color;
       -        fill_color = theme->fill_color;
       -        border_width = theme->border_width;
       -        break;
       -    case LTK_HOVERACTIVE:
       -    case LTK_HOVER:
       -        border_color = theme->border_color_hover;
       -        fill_color = theme->fill_color_hover;
       -        border_width = theme->border_width_hover;
       -        break;
       -    case LTK_PRESSED:
       -        border_color = theme->border_color_pressed;
       -        fill_color = theme->fill_color_pressed;
       -        border_width = theme->border_width_pressed;
       -        break;
       -    case LTK_ACTIVE:
       -        border_color = theme->border_color_active;
       -        fill_color = theme->fill_color_active;
       -        border_width = theme->border_width_active;
       -        break;
       -    case LTK_DISABLED:
       -        border_color = theme->border_color_disabled;
       -        fill_color = theme->fill_color_disabled;
       -        border_width = theme->border_width_disabled;
       -        break;
       -    default:
       -        ltk_fatal("No style found for button!\n");
       -    }
       -    XSetForeground(ltk_global->display, window->gc, fill_color.pixel);
       -    XFillRectangle(ltk_global->display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
       -    if (border_width < 1) return;
       -    XSetForeground(ltk_global->display, window->gc, border_color.pixel);
       -    XSetLineAttributes(ltk_global->display, window->gc, border_width, LineSolid, CapButt, JoinMiter);
       -    XDrawRectangle(ltk_global->display, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
       +        LtkButtonTheme *theme = ltk_global->theme->button;
       +        LtkWindow *window = button->widget.window;
       +        LtkRect rect = button->widget.rect;
       +        XColor border;
       +        XColor fill;
       +        switch (button->widget.state) {
       +        case LTK_NORMAL:
       +                border = theme->border;
       +                fill = theme->fill;
       +                break;
       +        case LTK_HOVERACTIVE:
       +        case LTK_HOVER:
       +                border = theme->border_hover;
       +                fill = theme->fill_hover;
       +                break;
       +        case LTK_PRESSED:
       +                border = theme->border_pressed;
       +                fill = theme->fill_pressed;
       +                break;
       +        case LTK_ACTIVE:
       +                border = theme->border_active;
       +                fill = theme->fill_active;
       +                break;
       +        case LTK_DISABLED:
       +                border = theme->border_disabled;
       +                fill = theme->fill_disabled;
       +                break;
       +        default:
       +                ltk_fatal("No style found for button!\n");
       +        }
       +        XSetForeground(ltk_global->display, window->gc, fill.pixel);
       +        XFillRectangle(ltk_global->display, window->xwindow, window->gc,
       +                       rect.x, rect.y, rect.w, rect.h);
       +        if (theme->border_width < 1) return;
       +        XSetForeground(ltk_global->display, window->gc, border.pixel);
       +        XSetLineAttributes(ltk_global->display, window->gc, theme->border_width,
       +                           LineSolid, CapButt, JoinMiter);
       +        XDrawRectangle(ltk_global->display, window->xwindow, window->gc,
       +                       rect.x, rect.y, rect.w, rect.h);
       +        int height = theme->font_size;
       +        int width = button->text_width;
       +        int i, j;
       +        for (i = 0; i < height; i++)
       +        {
       +                for (j = 0; j < width; j++)
       +                {
       +//                        XSetForeground(ltk_global->display, window->gc, (button->text_bitmap[i * width + j] / 255.0) * theme->text_color.pixel + ((255 - button->text_bitmap[i * width + j]) / 255.0) * fill.pixel);
       +                        XSetForeground(ltk_global->display, window->gc, ltk_blend_pixel(theme->text_color, fill, (button->text_bitmap[i * width + j] / 255.0)));
       +                        XDrawPoint(ltk_global->display, window->xwindow, window->gc, rect.x + j, rect.y + i);
       +                }
       +        }
        }
        
       -LtkButton *ltk_create_button(LtkWindow *window, const char *text, void (*callback)(void))
       +LtkButton *ltk_create_button(LtkWindow *window, const char *text,
       +                             void (*callback) (void))
        {
       -    LtkButton *button = malloc(sizeof(LtkButton));
       +        LtkButton *button = malloc(sizeof(LtkButton));
        
       -    if (button == NULL)
       -    {
       -        printf("Button could not be created.\n");
       -            exit(1);
       -    }
       +        if (button == NULL) {
       +                ltk_fatal("ERROR: Unable to allocate memory for LtkButton.\n");
       +        }
        
       -    button->widget = ltk_create_widget(window, &ltk_draw_button, &ltk_destroy_button, 1);
       -    button->widget.mouse_release = &ltk_button_mouse_release;
       +        button->widget = ltk_create_widget(window, &ltk_draw_button, &ltk_destroy_button, 1);
       +        button->widget.mouse_release = &ltk_button_mouse_release;
        
       -    button->callback = callback;
       -    button->text = strdup(text);
       +        button->callback = callback;
       +        LtkTheme *theme = ltk_global->theme;
       +        button->text_width = ltk_text_width(text, theme->window->font, theme->button->font_size);
       +        button->text_bitmap = ltk_render_text(text, theme->window->font, theme->button->font_size, button->text_width);
        
       -    return button;
       +        return button;
        }
        
        void ltk_destroy_button(void *widget)
        {
       -    LtkButton *button = (LtkButton *)widget;
       -    if (!button)
       -    {
       -        printf("Tried to destroy NULL button.\n");
       -    }
       -    free(button->text);
       -    free(button);
       +        LtkButton *button = (LtkButton *) widget;
       +        if (!button) {
       +                printf("WARNING: Tried to destroy NULL button.\n");
       +        }
       +        free(button->text_bitmap);
       +        free(button);
        }
        
       +/* FIXME: is the fixme below supposed to be for the function above? */
        /* FIXME: ungrid button if gridded */
        void ltk_button_mouse_release(void *widget, XEvent event)
        {
       -    LtkButton *button = widget;
       -    if (button->widget.state == LTK_HOVERACTIVE && button->callback)
       -    {
       -        button->callback();
       -    }
       +        LtkButton *button = widget;
       +        if (button->widget.state == LTK_HOVERACTIVE && button->callback) {
       +                button->callback();
       +        }
        }
   DIR diff --git a/button.h b/button.h
       t@@ -24,85 +24,51 @@
        #ifndef _LTK_BUTTON_H_
        #define _LTK_BUTTON_H_
        
       -/*
       - * Struct to represent a button widget.
       - */
       -typedef struct
       -{
       -    LtkWidget widget;
       -    void (*callback)(void);
       -    char *text;
       +typedef struct {
       +        LtkWidget widget;
       +        void (*callback) (void);
       +
       +        int text_width;
       +        char *text_raw;
       +        unsigned char *text_bitmap;
       +        Pixmap text;
       +        Pixmap text_hover;
       +        Pixmap text_pressed;
       +        Pixmap text_active;
       +        Pixmap text_disabled;
        } LtkButton;
        
       -/*
       - * Struct to contain all style information for buttons.
       - */
       -typedef struct LtkButtonTheme
       -{
       -    int border_width;
       -    int font_size;
       -    XColor border_color;
       -    XColor fill_color;
       -    int padding_left;
       -    int padding_right;
       -    int padding_top;
       -    int padding_bottom;
       +typedef struct LtkButtonTheme {
       +        int border_width;
       +        int font_size;
       +        XColor text_color;
       +        int padl;
       +        int padr;
       +        int padt;
       +        int padb;
        
       -    int border_width_hover;
       -    int font_size_hover;
       -    XColor border_color_hover;
       -    XColor fill_color_hover;
       +        XColor border;
       +        XColor fill;
        
       -    int border_width_pressed;
       -    int font_size_pressed;
       -    XColor border_color_pressed;
       -    XColor fill_color_pressed;
       +        XColor border_hover;
       +        XColor fill_hover;
        
       -    int border_width_active;
       -    int font_size_active;
       -    XColor border_color_active;
       -    XColor fill_color_active;
       +        XColor border_pressed;
       +        XColor fill_pressed;
        
       -    int border_width_disabled;
       -    int font_size_disabled;
       -    XColor border_color_disabled;
       -    XColor fill_color_disabled;
       +        XColor border_active;
       +        XColor fill_active;
        
       +        XColor border_disabled;
       +        XColor fill_disabled;
        } LtkButtonTheme;
        
       -/*
       - * Extract style information for buttons.
       - * button_json: A cJSON struct containing the JSON for the button style.
       - * Returns: An LtkButtonTheme struct containing the style for a button.
       - */
       -LtkButtonTheme *ltk_parse_button_theme(cJSON *button_json);
       -
       -/*
       - * Draw a button in its current state.
       - * button: Pointer to the button to draw.
       - */
       -void ltk_draw_button(LtkButton *button);
       +void ltk_draw_button(LtkButton * button);
        
       -/*
       - * Create a button widget.
       - * window: The window the button will be shown on.
       - * text: The text to be shown on the button.
       - * callback: The function to be called when the button is clicked.
       - * Returns: A pointer to the newly created button.
       - */
       -LtkButton *ltk_create_button(LtkWindow *window, const char *text, void (*callback)(void));
       +LtkButton *ltk_create_button(LtkWindow * window, const char *text, void (*callback) (void));
        
       -/*
       - * Destroy a button.
       - * widget: Pointer to the button.
       - */
        void ltk_destroy_button(void *widget);
        
       -/*
       - * Default mouse button release handler for buttons.
       - * widget: Pointer to the button.
       - * event: The event to be handled.
       - */
        void ltk_button_mouse_release(void *widget, XEvent event);
        
        #endif
   DIR diff --git a/cJSON.c b/cJSON.c
       t@@ -1,1492 +0,0 @@
       -/*
       -  Copyright (c) 2009 Dave Gamble
       -
       -  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 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.
       -*/
       -
       -/* cJSON */
       -/* JSON parser in C. */
       -
       -#include <string.h>
       -#include <stdio.h>
       -#include <math.h>
       -#include <stdlib.h>
       -#include <float.h>
       -#include <limits.h>
       -#include <ctype.h>
       -#include "cJSON.h"
       -
       -static const char *global_ep;
       -
       -const char *cJSON_GetErrorPtr(void)
       -{
       -        return global_ep;
       -}
       -
       -static int cJSON_strcasecmp(const char *s1, const char *s2)
       -{
       -        if (!s1)
       -                return (s1 == s2) ? 0 : 1;
       -        if (!s2)
       -                return 1;
       -        for (; tolower(*s1) == tolower(*s2); ++s1, ++s2)
       -                if (*s1 == 0)
       -                        return 0;
       -        return tolower(*(const unsigned char *)s1) -
       -            tolower(*(const unsigned char *)s2);
       -}
       -
       -static void *(*cJSON_malloc) (size_t sz) = malloc;
       -static void (*cJSON_free) (void *ptr) = free;
       -
       -static char *cJSON_strdup(const char *str)
       -{
       -        size_t len;
       -        char *copy;
       -
       -        len = strlen(str) + 1;
       -        if (!(copy = (char *)cJSON_malloc(len)))
       -                return 0;
       -        memcpy(copy, str, len);
       -        return copy;
       -}
       -
       -void cJSON_InitHooks(cJSON_Hooks * hooks)
       -{
       -        if (!hooks) {                /* Reset hooks */
       -                cJSON_malloc = malloc;
       -                cJSON_free = free;
       -                return;
       -        }
       -
       -        cJSON_malloc = (hooks->malloc_fn) ? hooks->malloc_fn : malloc;
       -        cJSON_free = (hooks->free_fn) ? hooks->free_fn : free;
       -}
       -
       -/* Internal constructor. */
       -static cJSON *cJSON_New_Item(void)
       -{
       -        cJSON *node = (cJSON *) cJSON_malloc(sizeof(cJSON));
       -        if (node)
       -                memset(node, 0, sizeof(cJSON));
       -        return node;
       -}
       -
       -/* Delete a cJSON structure. */
       -void cJSON_Delete(cJSON * c)
       -{
       -        cJSON *next;
       -        while (c) {
       -                next = c->next;
       -                if (!(c->type & cJSON_IsReference) && c->child)
       -                        cJSON_Delete(c->child);
       -                if (!(c->type & cJSON_IsReference) && c->valuestring)
       -                        cJSON_free(c->valuestring);
       -                if (!(c->type & cJSON_StringIsConst) && c->string)
       -                        cJSON_free(c->string);
       -                cJSON_free(c);
       -                c = next;
       -        }
       -}
       -
       -/* Parse the input text to generate a number, and populate the result into item. */
       -static const char *parse_number(cJSON * item, const char *num)
       -{
       -        double n = 0, sign = 1, scale = 0;
       -        int subscale = 0, signsubscale = 1;
       -
       -        if (*num == '-')
       -                sign = -1, num++;        /* Has sign? */
       -        if (*num == '0')
       -                num++;                /* is zero */
       -        if (*num >= '1' && *num <= '9')
       -                do
       -                        n = (n * 10.0) + (*num++ - '0');
       -                while (*num >= '0' && *num <= '9');        /* Number? */
       -        if (*num == '.' && num[1] >= '0' && num[1] <= '9') {
       -                num++;
       -                do
       -                        n = (n * 10.0) + (*num++ - '0'), scale--;
       -                while (*num >= '0' && *num <= '9');
       -        }                        /* Fractional part? */
       -        if (*num == 'e' || *num == 'E') {        /* Exponent? */
       -                num++;
       -                if (*num == '+')
       -                        num++;
       -                else if (*num == '-')
       -                        signsubscale = -1, num++;        /* With sign? */
       -                while (*num >= '0' && *num <= '9')
       -                        subscale = (subscale * 10) + (*num++ - '0');        /* Number? */
       -        }
       -
       -        n = sign * n * pow(10.0, (scale + subscale * signsubscale));        /* number = +/- number.fraction * 10^+/- exponent */
       -
       -        item->valuedouble = n;
       -        item->valueint = (int)n;
       -        item->type = cJSON_Number;
       -        return num;
       -}
       -
       -static int pow2gt(int x)
       -{
       -        --x;
       -        x |= x >> 1;
       -        x |= x >> 2;
       -        x |= x >> 4;
       -        x |= x >> 8;
       -        x |= x >> 16;
       -        return x + 1;
       -}
       -
       -typedef struct {
       -        char *buffer;
       -        int length;
       -        int offset;
       -} printbuffer;
       -
       -static char *ensure(printbuffer * p, int needed)
       -{
       -        char *newbuffer;
       -        int newsize;
       -        if (!p || !p->buffer)
       -                return 0;
       -        needed += p->offset;
       -        if (needed <= p->length)
       -                return p->buffer + p->offset;
       -
       -        newsize = pow2gt(needed);
       -        newbuffer = (char *)cJSON_malloc(newsize);
       -        if (!newbuffer) {
       -                cJSON_free(p->buffer);
       -                p->length = 0, p->buffer = 0;
       -                return 0;
       -        }
       -        if (newbuffer)
       -                memcpy(newbuffer, p->buffer, p->length);
       -        cJSON_free(p->buffer);
       -        p->length = newsize;
       -        p->buffer = newbuffer;
       -        return newbuffer + p->offset;
       -}
       -
       -static int update(printbuffer * p)
       -{
       -        char *str;
       -        if (!p || !p->buffer)
       -                return 0;
       -        str = p->buffer + p->offset;
       -        return p->offset + strlen(str);
       -}
       -
       -/* Render the number nicely from the given item into a string. */
       -static char *print_number(cJSON * item, printbuffer * p)
       -{
       -        char *str = 0;
       -        double d = item->valuedouble;
       -        if (d == 0) {
       -                if (p)
       -                        str = ensure(p, 2);
       -                else
       -                        str = (char *)cJSON_malloc(2);        /* special case for 0. */
       -                if (str)
       -                        strcpy(str, "0");
       -        } else if (fabs(((double)item->valueint) - d) <= DBL_EPSILON
       -                   && d <= INT_MAX && d >= INT_MIN) {
       -                if (p)
       -                        str = ensure(p, 21);
       -                else
       -                        str = (char *)cJSON_malloc(21);        /* 2^64+1 can be represented in 21 chars. */
       -                if (str)
       -                        sprintf(str, "%d", item->valueint);
       -        } else {
       -                if (p)
       -                        str = ensure(p, 64);
       -                else
       -                        str = (char *)cJSON_malloc(64);        /* This is a nice tradeoff. */
       -                if (str) {
       -                        if (d * 0 != 0)
       -                                sprintf(str, "null");        /* This checks for NaN and Infinity */
       -                        else if (fabs(floor(d) - d) <= DBL_EPSILON
       -                                 && fabs(d) < 1.0e60)
       -                                sprintf(str, "%.0f", d);
       -                        else if (fabs(d) < 1.0e-6 || fabs(d) > 1.0e9)
       -                                sprintf(str, "%e", d);
       -                        else
       -                                sprintf(str, "%f", d);
       -                }
       -        }
       -        return str;
       -}
       -
       -static unsigned parse_hex4(const char *str)
       -{
       -        unsigned h = 0;
       -        if (*str >= '0' && *str <= '9')
       -                h += (*str) - '0';
       -        else if (*str >= 'A' && *str <= 'F')
       -                h += 10 + (*str) - 'A';
       -        else if (*str >= 'a' && *str <= 'f')
       -                h += 10 + (*str) - 'a';
       -        else
       -                return 0;
       -        h = h << 4;
       -        str++;
       -        if (*str >= '0' && *str <= '9')
       -                h += (*str) - '0';
       -        else if (*str >= 'A' && *str <= 'F')
       -                h += 10 + (*str) - 'A';
       -        else if (*str >= 'a' && *str <= 'f')
       -                h += 10 + (*str) - 'a';
       -        else
       -                return 0;
       -        h = h << 4;
       -        str++;
       -        if (*str >= '0' && *str <= '9')
       -                h += (*str) - '0';
       -        else if (*str >= 'A' && *str <= 'F')
       -                h += 10 + (*str) - 'A';
       -        else if (*str >= 'a' && *str <= 'f')
       -                h += 10 + (*str) - 'a';
       -        else
       -                return 0;
       -        h = h << 4;
       -        str++;
       -        if (*str >= '0' && *str <= '9')
       -                h += (*str) - '0';
       -        else if (*str >= 'A' && *str <= 'F')
       -                h += 10 + (*str) - 'A';
       -        else if (*str >= 'a' && *str <= 'f')
       -                h += 10 + (*str) - 'a';
       -        else
       -                return 0;
       -        return h;
       -}
       -
       -/* Parse the input text into an unescaped cstring, and populate item. */
       -static const unsigned char firstByteMark[7] =
       -    { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
       -static const char *parse_string(cJSON * item, const char *str, const char **ep)
       -{
       -        const char *ptr = str + 1, *end_ptr = str + 1;
       -        char *ptr2;
       -        char *out;
       -        int len = 0;
       -        unsigned uc, uc2;
       -        if (*str != '\"') {
       -                *ep = str;
       -                return 0;
       -        }
       -        /* not a string! */
       -        while (*end_ptr != '\"' && *end_ptr && ++len)
       -                if (*end_ptr++ == '\\')
       -                        end_ptr++;        /* Skip escaped quotes. */
       -
       -        out = (char *)cJSON_malloc(len + 1);        /* This is how long we need for the string, roughly. */
       -        if (!out)
       -                return 0;
       -        item->valuestring = out;        /* assign here so out will be deleted during cJSON_Delete() later */
       -        item->type = cJSON_String;
       -
       -        ptr = str + 1;
       -        ptr2 = out;
       -        while (ptr < end_ptr) {
       -                if (*ptr != '\\')
       -                        *ptr2++ = *ptr++;
       -                else {
       -                        ptr++;
       -                        switch (*ptr) {
       -                        case 'b':
       -                                *ptr2++ = '\b';
       -                                break;
       -                        case 'f':
       -                                *ptr2++ = '\f';
       -                                break;
       -                        case 'n':
       -                                *ptr2++ = '\n';
       -                                break;
       -                        case 'r':
       -                                *ptr2++ = '\r';
       -                                break;
       -                        case 't':
       -                                *ptr2++ = '\t';
       -                                break;
       -                        case 'u':        /* transcode utf16 to utf8. */
       -                                uc = parse_hex4(ptr + 1);
       -                                ptr += 4;        /* get the unicode char. */
       -                                if (ptr >= end_ptr) {
       -                                        *ep = str;
       -                                        return 0;
       -                                }
       -                                /* invalid */
       -                                if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0) {
       -                                        *ep = str;
       -                                        return 0;
       -                                }
       -                                /* check for invalid.   */
       -                                if (uc >= 0xD800 && uc <= 0xDBFF) {        /* UTF16 surrogate pairs.       */
       -                                        if (ptr + 6 > end_ptr) {
       -                                                *ep = str;
       -                                                return 0;
       -                                        }        /* invalid */
       -                                        if (ptr[1] != '\\' || ptr[2] != 'u') {
       -                                                *ep = str;
       -                                                return 0;
       -                                        }        /* missing second-half of surrogate.    */
       -                                        uc2 = parse_hex4(ptr + 3);
       -                                        ptr += 6;
       -                                        if (uc2 < 0xDC00 || uc2 > 0xDFFF) {
       -                                                *ep = str;
       -                                                return 0;
       -                                        }        /* invalid second-half of surrogate.    */
       -                                        uc = 0x10000 +
       -                                            (((uc & 0x3FF) << 10) |
       -                                             (uc2 & 0x3FF));
       -                                }
       -
       -                                len = 4;
       -                                if (uc < 0x80)
       -                                        len = 1;
       -                                else if (uc < 0x800)
       -                                        len = 2;
       -                                else if (uc < 0x10000)
       -                                        len = 3;
       -                                ptr2 += len;
       -
       -                                switch (len) {
       -                                case 4:
       -                                        *--ptr2 = ((uc | 0x80) & 0xBF);
       -                                        uc >>= 6;
       -                                case 3:
       -                                        *--ptr2 = ((uc | 0x80) & 0xBF);
       -                                        uc >>= 6;
       -                                case 2:
       -                                        *--ptr2 = ((uc | 0x80) & 0xBF);
       -                                        uc >>= 6;
       -                                case 1:
       -                                        *--ptr2 = (uc | firstByteMark[len]);
       -                                }
       -                                ptr2 += len;
       -                                break;
       -                        default:
       -                                *ptr2++ = *ptr;
       -                                break;
       -                        }
       -                        ptr++;
       -                }
       -        }
       -        *ptr2 = 0;
       -        if (*ptr == '\"')
       -                ptr++;
       -        return ptr;
       -}
       -
       -/* Render the cstring provided to an escaped version that can be printed. */
       -static char *print_string_ptr(const char *str, printbuffer * p)
       -{
       -        const char *ptr;
       -        char *ptr2, *out;
       -        int len = 0, flag = 0;
       -        unsigned char token;
       -
       -        if (!str) {
       -                if (p)
       -                        out = ensure(p, 3);
       -                else
       -                        out = (char *)cJSON_malloc(3);
       -                if (!out)
       -                        return 0;
       -                strcpy(out, "\"\"");
       -                return out;
       -        }
       -
       -        for (ptr = str; *ptr; ptr++)
       -                flag |= ((*ptr > 0 && *ptr < 32) || (*ptr == '\"')
       -                         || (*ptr == '\\')) ? 1 : 0;
       -        if (!flag) {
       -                len = ptr - str;
       -                if (p)
       -                        out = ensure(p, len + 3);
       -                else
       -                        out = (char *)cJSON_malloc(len + 3);
       -                if (!out)
       -                        return 0;
       -                ptr2 = out;
       -                *ptr2++ = '\"';
       -                strcpy(ptr2, str);
       -                ptr2[len] = '\"';
       -                ptr2[len + 1] = 0;
       -                return out;
       -        }
       -
       -        ptr = str;
       -        while ((token = *ptr) && ++len) {
       -                if (strchr("\"\\\b\f\n\r\t", token))
       -                        len++;
       -                else if (token < 32)
       -                        len += 5;
       -                ptr++;
       -        }
       -
       -        if (p)
       -                out = ensure(p, len + 3);
       -        else
       -                out = (char *)cJSON_malloc(len + 3);
       -        if (!out)
       -                return 0;
       -
       -        ptr2 = out;
       -        ptr = str;
       -        *ptr2++ = '\"';
       -        while (*ptr) {
       -                if ((unsigned char)*ptr > 31 && *ptr != '\"' && *ptr != '\\')
       -                        *ptr2++ = *ptr++;
       -                else {
       -                        *ptr2++ = '\\';
       -                        switch (token = *ptr++) {
       -                        case '\\':
       -                                *ptr2++ = '\\';
       -                                break;
       -                        case '\"':
       -                                *ptr2++ = '\"';
       -                                break;
       -                        case '\b':
       -                                *ptr2++ = 'b';
       -                                break;
       -                        case '\f':
       -                                *ptr2++ = 'f';
       -                                break;
       -                        case '\n':
       -                                *ptr2++ = 'n';
       -                                break;
       -                        case '\r':
       -                                *ptr2++ = 'r';
       -                                break;
       -                        case '\t':
       -                                *ptr2++ = 't';
       -                                break;
       -                        default:
       -                                sprintf(ptr2, "u%04x", token);
       -                                ptr2 += 5;
       -                                break;        /* escape and print */
       -                        }
       -                }
       -        }
       -        *ptr2++ = '\"';
       -        *ptr2++ = 0;
       -        return out;
       -}
       -
       -/* Invote print_string_ptr (which is useful) on an item. */
       -static char *print_string(cJSON * item, printbuffer * p)
       -{
       -        return print_string_ptr(item->valuestring, p);
       -}
       -
       -/* Predeclare these prototypes. */
       -static const char *parse_value(cJSON * item, const char *value,
       -                               const char **ep);
       -static char *print_value(cJSON * item, int depth, int fmt, printbuffer * p);
       -static const char *parse_array(cJSON * item, const char *value,
       -                               const char **ep);
       -static char *print_array(cJSON * item, int depth, int fmt, printbuffer * p);
       -static const char *parse_object(cJSON * item, const char *value,
       -                                const char **ep);
       -static char *print_object(cJSON * item, int depth, int fmt, printbuffer * p);
       -
       -/* Utility to jump whitespace and cr/lf */
       -static const char *skip(const char *in)
       -{
       -        while (in && *in && (unsigned char)*in <= 32)
       -                in++;
       -        return in;
       -}
       -
       -/* Parse an object - create a new root, and populate. */
       -cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end,
       -                           int require_null_terminated)
       -{
       -        const char *end = 0, **ep =
       -            return_parse_end ? return_parse_end : &global_ep;
       -        cJSON *c = cJSON_New_Item();
       -        *ep = 0;
       -        if (!c)
       -                return 0;        /* memory fail */
       -
       -        end = parse_value(c, skip(value), ep);
       -        if (!end) {
       -                cJSON_Delete(c);
       -                return 0;
       -        }
       -
       -        /* parse failure. ep is set. */
       -        /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
       -        if (require_null_terminated) {
       -                end = skip(end);
       -                if (*end) {
       -                        cJSON_Delete(c);
       -                        *ep = end;
       -                        return 0;
       -                }
       -        }
       -        if (return_parse_end)
       -                *return_parse_end = end;
       -        return c;
       -}
       -
       -/* Default options for cJSON_Parse */
       -cJSON *cJSON_Parse(const char *value)
       -{
       -        return cJSON_ParseWithOpts(value, 0, 0);
       -}
       -
       -/* Render a cJSON item/entity/structure to text. */
       -char *cJSON_Print(cJSON * item)
       -{
       -        return print_value(item, 0, 1, 0);
       -}
       -
       -char *cJSON_PrintUnformatted(cJSON * item)
       -{
       -        return print_value(item, 0, 0, 0);
       -}
       -
       -char *cJSON_PrintBuffered(cJSON * item, int prebuffer, int fmt)
       -{
       -        printbuffer p;
       -        p.buffer = (char *)cJSON_malloc(prebuffer);
       -        p.length = prebuffer;
       -        p.offset = 0;
       -        return print_value(item, 0, fmt, &p);
       -}
       -
       -/* Parser core - when encountering text, process appropriately. */
       -static const char *parse_value(cJSON * item, const char *value, const char **ep)
       -{
       -        if (!value)
       -                return 0;        /* Fail on null. */
       -        if (!strncmp(value, "null", 4)) {
       -                item->type = cJSON_NULL;
       -                return value + 4;
       -        }
       -        if (!strncmp(value, "false", 5)) {
       -                item->type = cJSON_False;
       -                return value + 5;
       -        }
       -        if (!strncmp(value, "true", 4)) {
       -                item->type = cJSON_True;
       -                item->valueint = 1;
       -                return value + 4;
       -        }
       -        if (*value == '\"') {
       -                return parse_string(item, value, ep);
       -        }
       -        if (*value == '-' || (*value >= '0' && *value <= '9')) {
       -                return parse_number(item, value);
       -        }
       -        if (*value == '[') {
       -                return parse_array(item, value, ep);
       -        }
       -        if (*value == '{') {
       -                return parse_object(item, value, ep);
       -        }
       -
       -        *ep = value;
       -        return 0;                /* failure. */
       -}
       -
       -/* Render a value to text. */
       -static char *print_value(cJSON * item, int depth, int fmt, printbuffer * p)
       -{
       -        char *out = 0;
       -        if (!item)
       -                return 0;
       -        if (p) {
       -                switch ((item->type) & 255) {
       -                case cJSON_NULL:
       -                        {
       -                                out = ensure(p, 5);
       -                                if (out)
       -                                        strcpy(out, "null");
       -                                break;
       -                        }
       -                case cJSON_False:
       -                        {
       -                                out = ensure(p, 6);
       -                                if (out)
       -                                        strcpy(out, "false");
       -                                break;
       -                        }
       -                case cJSON_True:
       -                        {
       -                                out = ensure(p, 5);
       -                                if (out)
       -                                        strcpy(out, "true");
       -                                break;
       -                        }
       -                case cJSON_Number:
       -                        out = print_number(item, p);
       -                        break;
       -                case cJSON_String:
       -                        out = print_string(item, p);
       -                        break;
       -                case cJSON_Array:
       -                        out = print_array(item, depth, fmt, p);
       -                        break;
       -                case cJSON_Object:
       -                        out = print_object(item, depth, fmt, p);
       -                        break;
       -                }
       -        } else {
       -                switch ((item->type) & 255) {
       -                case cJSON_NULL:
       -                        out = cJSON_strdup("null");
       -                        break;
       -                case cJSON_False:
       -                        out = cJSON_strdup("false");
       -                        break;
       -                case cJSON_True:
       -                        out = cJSON_strdup("true");
       -                        break;
       -                case cJSON_Number:
       -                        out = print_number(item, 0);
       -                        break;
       -                case cJSON_String:
       -                        out = print_string(item, 0);
       -                        break;
       -                case cJSON_Array:
       -                        out = print_array(item, depth, fmt, 0);
       -                        break;
       -                case cJSON_Object:
       -                        out = print_object(item, depth, fmt, 0);
       -                        break;
       -                }
       -        }
       -        return out;
       -}
       -
       -/* Build an array from input text. */
       -static const char *parse_array(cJSON * item, const char *value, const char **ep)
       -{
       -        cJSON *child;
       -        if (*value != '[') {
       -                *ep = value;
       -                return 0;
       -        }
       -        /* not an array! */
       -        item->type = cJSON_Array;
       -        value = skip(value + 1);
       -        if (*value == ']')
       -                return value + 1;        /* empty array. */
       -
       -        item->child = child = cJSON_New_Item();
       -        if (!item->child)
       -                return 0;        /* memory fail */
       -        value = skip(parse_value(child, skip(value), ep));        /* skip any spacing, get the value. */
       -        if (!value)
       -                return 0;
       -
       -        while (*value == ',') {
       -                cJSON *new_item;
       -                if (!(new_item = cJSON_New_Item()))
       -                        return 0;        /* memory fail */
       -                child->next = new_item;
       -                new_item->prev = child;
       -                child = new_item;
       -                value = skip(parse_value(child, skip(value + 1), ep));
       -                if (!value)
       -                        return 0;        /* memory fail */
       -        }
       -
       -        if (*value == ']')
       -                return value + 1;        /* end of array */
       -        *ep = value;
       -        return 0;                /* malformed. */
       -}
       -
       -/* Render an array to text */
       -static char *print_array(cJSON * item, int depth, int fmt, printbuffer * p)
       -{
       -        char **entries;
       -        char *out = 0, *ptr, *ret;
       -        int len = 5;
       -        cJSON *child = item->child;
       -        int numentries = 0, i = 0, fail = 0;
       -        size_t tmplen = 0;
       -
       -        /* How many entries in the array? */
       -        while (child)
       -                numentries++, child = child->next;
       -        /* Explicitly handle numentries==0 */
       -        if (!numentries) {
       -                if (p)
       -                        out = ensure(p, 3);
       -                else
       -                        out = (char *)cJSON_malloc(3);
       -                if (out)
       -                        strcpy(out, "[]");
       -                return out;
       -        }
       -
       -        if (p) {
       -                /* Compose the output array. */
       -                i = p->offset;
       -                ptr = ensure(p, 1);
       -                if (!ptr)
       -                        return 0;
       -                *ptr = '[';
       -                p->offset++;
       -                child = item->child;
       -                while (child && !fail) {
       -                        print_value(child, depth + 1, fmt, p);
       -                        p->offset = update(p);
       -                        if (child->next) {
       -                                len = fmt ? 2 : 1;
       -                                ptr = ensure(p, len + 1);
       -                                if (!ptr)
       -                                        return 0;
       -                                *ptr++ = ',';
       -                                if (fmt)
       -                                        *ptr++ = ' ';
       -                                *ptr = 0;
       -                                p->offset += len;
       -                        }
       -                        child = child->next;
       -                }
       -                ptr = ensure(p, 2);
       -                if (!ptr)
       -                        return 0;
       -                *ptr++ = ']';
       -                *ptr = 0;
       -                out = (p->buffer) + i;
       -        } else {
       -                /* Allocate an array to hold the values for each */
       -                entries = (char **)cJSON_malloc(numentries * sizeof(char *));
       -                if (!entries)
       -                        return 0;
       -                memset(entries, 0, numentries * sizeof(char *));
       -                /* Retrieve all the results: */
       -                child = item->child;
       -                while (child && !fail) {
       -                        ret = print_value(child, depth + 1, fmt, 0);
       -                        entries[i++] = ret;
       -                        if (ret)
       -                                len += strlen(ret) + 2 + (fmt ? 1 : 0);
       -                        else
       -                                fail = 1;
       -                        child = child->next;
       -                }
       -
       -                /* If we didn't fail, try to malloc the output string */
       -                if (!fail)
       -                        out = (char *)cJSON_malloc(len);
       -                /* If that fails, we fail. */
       -                if (!out)
       -                        fail = 1;
       -
       -                /* Handle failure. */
       -                if (fail) {
       -                        for (i = 0; i < numentries; i++)
       -                                if (entries[i])
       -                                        cJSON_free(entries[i]);
       -                        cJSON_free(entries);
       -                        return 0;
       -                }
       -
       -                /* Compose the output array. */
       -                *out = '[';
       -                ptr = out + 1;
       -                *ptr = 0;
       -                for (i = 0; i < numentries; i++) {
       -                        tmplen = strlen(entries[i]);
       -                        memcpy(ptr, entries[i], tmplen);
       -                        ptr += tmplen;
       -                        if (i != numentries - 1) {
       -                                *ptr++ = ',';
       -                                if (fmt)
       -                                        *ptr++ = ' ';
       -                                *ptr = 0;
       -                        }
       -                        cJSON_free(entries[i]);
       -                }
       -                cJSON_free(entries);
       -                *ptr++ = ']';
       -                *ptr++ = 0;
       -        }
       -        return out;
       -}
       -
       -/* Build an object from the text. */
       -static const char *parse_object(cJSON * item, const char *value,
       -                                const char **ep)
       -{
       -        cJSON *child;
       -        if (*value != '{') {
       -                *ep = value;
       -                return 0;
       -        }
       -        /* not an object! */
       -        item->type = cJSON_Object;
       -        value = skip(value + 1);
       -        if (*value == '}')
       -                return value + 1;        /* empty array. */
       -
       -        item->child = child = cJSON_New_Item();
       -        if (!item->child)
       -                return 0;
       -        value = skip(parse_string(child, skip(value), ep));
       -        if (!value)
       -                return 0;
       -        child->string = child->valuestring;
       -        child->valuestring = 0;
       -        if (*value != ':') {
       -                *ep = value;
       -                return 0;
       -        }                        /* fail! */
       -        value = skip(parse_value(child, skip(value + 1), ep));        /* skip any spacing, get the value. */
       -        if (!value)
       -                return 0;
       -
       -        while (*value == ',') {
       -                cJSON *new_item;
       -                if (!(new_item = cJSON_New_Item()))
       -                        return 0;        /* memory fail */
       -                child->next = new_item;
       -                new_item->prev = child;
       -                child = new_item;
       -                value = skip(parse_string(child, skip(value + 1), ep));
       -                if (!value)
       -                        return 0;
       -                child->string = child->valuestring;
       -                child->valuestring = 0;
       -                if (*value != ':') {
       -                        *ep = value;
       -                        return 0;
       -                }                /* fail! */
       -                value = skip(parse_value(child, skip(value + 1), ep));        /* skip any spacing, get the value. */
       -                if (!value)
       -                        return 0;
       -        }
       -
       -        if (*value == '}')
       -                return value + 1;        /* end of array */
       -        *ep = value;
       -        return 0;                /* malformed. */
       -}
       -
       -/* Render an object to text. */
       -static char *print_object(cJSON * item, int depth, int fmt, printbuffer * p)
       -{
       -        char **entries = 0, **names = 0;
       -        char *out = 0, *ptr, *ret, *str;
       -        int len = 7, i = 0, j;
       -        cJSON *child = item->child;
       -        int numentries = 0, fail = 0;
       -        size_t tmplen = 0;
       -        /* Count the number of entries. */
       -        while (child)
       -                numentries++, child = child->next;
       -        /* Explicitly handle empty object case */
       -        if (!numentries) {
       -                if (p)
       -                        out = ensure(p, fmt ? depth + 4 : 3);
       -                else
       -                        out = (char *)cJSON_malloc(fmt ? depth + 4 : 3);
       -                if (!out)
       -                        return 0;
       -                ptr = out;
       -                *ptr++ = '{';
       -                if (fmt) {
       -                        *ptr++ = '\n';
       -                        for (i = 0; i < depth; i++)
       -                                *ptr++ = '\t';
       -                }
       -                *ptr++ = '}';
       -                *ptr++ = 0;
       -                return out;
       -        }
       -        if (p) {
       -                /* Compose the output: */
       -                i = p->offset;
       -                len = fmt ? 2 : 1;
       -                ptr = ensure(p, len + 1);
       -                if (!ptr)
       -                        return 0;
       -                *ptr++ = '{';
       -                if (fmt)
       -                        *ptr++ = '\n';
       -                *ptr = 0;
       -                p->offset += len;
       -                child = item->child;
       -                depth++;
       -                while (child) {
       -                        if (fmt) {
       -                                ptr = ensure(p, depth);
       -                                if (!ptr)
       -                                        return 0;
       -                                for (j = 0; j < depth; j++)
       -                                        *ptr++ = '\t';
       -                                p->offset += depth;
       -                        }
       -                        print_string_ptr(child->string, p);
       -                        p->offset = update(p);
       -
       -                        len = fmt ? 2 : 1;
       -                        ptr = ensure(p, len);
       -                        if (!ptr)
       -                                return 0;
       -                        *ptr++ = ':';
       -                        if (fmt)
       -                                *ptr++ = '\t';
       -                        p->offset += len;
       -
       -                        print_value(child, depth, fmt, p);
       -                        p->offset = update(p);
       -
       -                        len = (fmt ? 1 : 0) + (child->next ? 1 : 0);
       -                        ptr = ensure(p, len + 1);
       -                        if (!ptr)
       -                                return 0;
       -                        if (child->next)
       -                                *ptr++ = ',';
       -                        if (fmt)
       -                                *ptr++ = '\n';
       -                        *ptr = 0;
       -                        p->offset += len;
       -                        child = child->next;
       -                }
       -                ptr = ensure(p, fmt ? (depth + 1) : 2);
       -                if (!ptr)
       -                        return 0;
       -                if (fmt)
       -                        for (i = 0; i < depth - 1; i++)
       -                                *ptr++ = '\t';
       -                *ptr++ = '}';
       -                *ptr = 0;
       -                out = (p->buffer) + i;
       -        } else {
       -                /* Allocate space for the names and the objects */
       -                entries = (char **)cJSON_malloc(numentries * sizeof(char *));
       -                if (!entries)
       -                        return 0;
       -                names = (char **)cJSON_malloc(numentries * sizeof(char *));
       -                if (!names) {
       -                        cJSON_free(entries);
       -                        return 0;
       -                }
       -                memset(entries, 0, sizeof(char *) * numentries);
       -                memset(names, 0, sizeof(char *) * numentries);
       -
       -                /* Collect all the results into our arrays: */
       -                child = item->child;
       -                depth++;
       -                if (fmt)
       -                        len += depth;
       -                while (child && !fail) {
       -                        names[i] = str = print_string_ptr(child->string, 0);
       -                        entries[i++] = ret = print_value(child, depth, fmt, 0);
       -                        if (str && ret)
       -                                len +=
       -                                    strlen(ret) + strlen(str) + 2 + (fmt ? 2 +
       -                                                                     depth : 0);
       -                        else
       -                                fail = 1;
       -                        child = child->next;
       -                }
       -
       -                /* Try to allocate the output string */
       -                if (!fail)
       -                        out = (char *)cJSON_malloc(len);
       -                if (!out)
       -                        fail = 1;
       -
       -                /* Handle failure */
       -                if (fail) {
       -                        for (i = 0; i < numentries; i++) {
       -                                if (names[i])
       -                                        cJSON_free(names[i]);
       -                                if (entries[i])
       -                                        cJSON_free(entries[i]);
       -                        }
       -                        cJSON_free(names);
       -                        cJSON_free(entries);
       -                        return 0;
       -                }
       -
       -                /* Compose the output: */
       -                *out = '{';
       -                ptr = out + 1;
       -                if (fmt)
       -                        *ptr++ = '\n';
       -                *ptr = 0;
       -                for (i = 0; i < numentries; i++) {
       -                        if (fmt)
       -                                for (j = 0; j < depth; j++)
       -                                        *ptr++ = '\t';
       -                        tmplen = strlen(names[i]);
       -                        memcpy(ptr, names[i], tmplen);
       -                        ptr += tmplen;
       -                        *ptr++ = ':';
       -                        if (fmt)
       -                                *ptr++ = '\t';
       -                        strcpy(ptr, entries[i]);
       -                        ptr += strlen(entries[i]);
       -                        if (i != numentries - 1)
       -                                *ptr++ = ',';
       -                        if (fmt)
       -                                *ptr++ = '\n';
       -                        *ptr = 0;
       -                        cJSON_free(names[i]);
       -                        cJSON_free(entries[i]);
       -                }
       -
       -                cJSON_free(names);
       -                cJSON_free(entries);
       -                if (fmt)
       -                        for (i = 0; i < depth - 1; i++)
       -                                *ptr++ = '\t';
       -                *ptr++ = '}';
       -                *ptr++ = 0;
       -        }
       -        return out;
       -}
       -
       -/* Get Array size/item / object item. */
       -int cJSON_GetArraySize(cJSON * array)
       -{
       -        cJSON *c = array->child;
       -        int i = 0;
       -        while (c)
       -                i++, c = c->next;
       -        return i;
       -}
       -
       -cJSON *cJSON_GetArrayItem(cJSON * array, int item)
       -{
       -        cJSON *c = array ? array->child : 0;
       -        while (c && item > 0)
       -                item--, c = c->next;
       -        return c;
       -}
       -
       -cJSON *cJSON_GetObjectItem(cJSON * object, const char *string)
       -{
       -        cJSON *c = object ? object->child : 0;
       -        while (c && cJSON_strcasecmp(c->string, string))
       -                c = c->next;
       -        return c;
       -}
       -
       -int cJSON_HasObjectItem(cJSON * object, const char *string)
       -{
       -        return cJSON_GetObjectItem(object, string) ? 1 : 0;
       -}
       -
       -/* Utility for array list handling. */
       -static void suffix_object(cJSON * prev, cJSON * item)
       -{
       -        prev->next = item;
       -        item->prev = prev;
       -}
       -
       -/* Utility for handling references. */
       -static cJSON *create_reference(cJSON * item)
       -{
       -        cJSON *ref = cJSON_New_Item();
       -        if (!ref)
       -                return 0;
       -        memcpy(ref, item, sizeof(cJSON));
       -        ref->string = 0;
       -        ref->type |= cJSON_IsReference;
       -        ref->next = ref->prev = 0;
       -        return ref;
       -}
       -
       -/* Add item to array/object. */
       -void cJSON_AddItemToArray(cJSON * array, cJSON * item)
       -{
       -        cJSON *c = array->child;
       -        if (!item)
       -                return;
       -        if (!c) {
       -                array->child = item;
       -        } else {
       -                while (c && c->next)
       -                        c = c->next;
       -                suffix_object(c, item);
       -        }
       -}
       -
       -void cJSON_AddItemToObject(cJSON * object, const char *string, cJSON * item)
       -{
       -        if (!item)
       -                return;
       -        if (item->string)
       -                cJSON_free(item->string);
       -        item->string = cJSON_strdup(string);
       -        cJSON_AddItemToArray(object, item);
       -}
       -
       -void cJSON_AddItemToObjectCS(cJSON * object, const char *string, cJSON * item)
       -{
       -        if (!item)
       -                return;
       -        if (!(item->type & cJSON_StringIsConst) && item->string)
       -                cJSON_free(item->string);
       -        item->string = (char *)string;
       -        item->type |= cJSON_StringIsConst;
       -        cJSON_AddItemToArray(object, item);
       -}
       -
       -void cJSON_AddItemReferenceToArray(cJSON * array, cJSON * item)
       -{
       -        cJSON_AddItemToArray(array, create_reference(item));
       -}
       -
       -void
       -cJSON_AddItemReferenceToObject(cJSON * object, const char *string, cJSON * item)
       -{
       -        cJSON_AddItemToObject(object, string, create_reference(item));
       -}
       -
       -cJSON *cJSON_DetachItemFromArray(cJSON * array, int which)
       -{
       -        cJSON *c = array->child;
       -        while (c && which > 0)
       -                c = c->next, which--;
       -        if (!c)
       -                return 0;
       -        if (c->prev)
       -                c->prev->next = c->next;
       -        if (c->next)
       -                c->next->prev = c->prev;
       -        if (c == array->child)
       -                array->child = c->next;
       -        c->prev = c->next = 0;
       -        return c;
       -}
       -
       -void cJSON_DeleteItemFromArray(cJSON * array, int which)
       -{
       -        cJSON_Delete(cJSON_DetachItemFromArray(array, which));
       -}
       -
       -cJSON *cJSON_DetachItemFromObject(cJSON * object, const char *string)
       -{
       -        int i = 0;
       -        cJSON *c = object->child;
       -        while (c && cJSON_strcasecmp(c->string, string))
       -                i++, c = c->next;
       -        if (c)
       -                return cJSON_DetachItemFromArray(object, i);
       -        return 0;
       -}
       -
       -void cJSON_DeleteItemFromObject(cJSON * object, const char *string)
       -{
       -        cJSON_Delete(cJSON_DetachItemFromObject(object, string));
       -}
       -
       -/* Replace array/object items with new ones. */
       -void cJSON_InsertItemInArray(cJSON * array, int which, cJSON * newitem)
       -{
       -        cJSON *c = array->child;
       -        while (c && which > 0)
       -                c = c->next, which--;
       -        if (!c) {
       -                cJSON_AddItemToArray(array, newitem);
       -                return;
       -        }
       -        newitem->next = c;
       -        newitem->prev = c->prev;
       -        c->prev = newitem;
       -        if (c == array->child)
       -                array->child = newitem;
       -        else
       -                newitem->prev->next = newitem;
       -}
       -
       -void cJSON_ReplaceItemInArray(cJSON * array, int which, cJSON * newitem)
       -{
       -        cJSON *c = array->child;
       -        while (c && which > 0)
       -                c = c->next, which--;
       -        if (!c)
       -                return;
       -        newitem->next = c->next;
       -        newitem->prev = c->prev;
       -        if (newitem->next)
       -                newitem->next->prev = newitem;
       -        if (c == array->child)
       -                array->child = newitem;
       -        else
       -                newitem->prev->next = newitem;
       -        c->next = c->prev = 0;
       -        cJSON_Delete(c);
       -}
       -
       -void
       -cJSON_ReplaceItemInObject(cJSON * object, const char *string, cJSON * newitem)
       -{
       -        int i = 0;
       -        cJSON *c = object->child;
       -        while (c && cJSON_strcasecmp(c->string, string))
       -                i++, c = c->next;
       -        if (c) {
       -                newitem->string = cJSON_strdup(string);
       -                cJSON_ReplaceItemInArray(object, i, newitem);
       -        }
       -}
       -
       -/* Create basic types: */
       -cJSON *cJSON_CreateNull(void)
       -{
       -        cJSON *item = cJSON_New_Item();
       -        if (item)
       -                item->type = cJSON_NULL;
       -        return item;
       -}
       -
       -cJSON *cJSON_CreateTrue(void)
       -{
       -        cJSON *item = cJSON_New_Item();
       -        if (item)
       -                item->type = cJSON_True;
       -        return item;
       -}
       -
       -cJSON *cJSON_CreateFalse(void)
       -{
       -        cJSON *item = cJSON_New_Item();
       -        if (item)
       -                item->type = cJSON_False;
       -        return item;
       -}
       -
       -cJSON *cJSON_CreateBool(int b)
       -{
       -        cJSON *item = cJSON_New_Item();
       -        if (item)
       -                item->type = b ? cJSON_True : cJSON_False;
       -        return item;
       -}
       -
       -cJSON *cJSON_CreateNumber(double num)
       -{
       -        cJSON *item = cJSON_New_Item();
       -        if (item) {
       -                item->type = cJSON_Number;
       -                item->valuedouble = num;
       -                item->valueint = (int)num;
       -        }
       -        return item;
       -}
       -
       -cJSON *cJSON_CreateString(const char *string)
       -{
       -        cJSON *item = cJSON_New_Item();
       -        if (item) {
       -                item->type = cJSON_String;
       -                item->valuestring = cJSON_strdup(string);
       -                if (!item->valuestring) {
       -                        cJSON_Delete(item);
       -                        return 0;
       -                }
       -        }
       -        return item;
       -}
       -
       -cJSON *cJSON_CreateArray(void)
       -{
       -        cJSON *item = cJSON_New_Item();
       -        if (item)
       -                item->type = cJSON_Array;
       -        return item;
       -}
       -
       -cJSON *cJSON_CreateObject(void)
       -{
       -        cJSON *item = cJSON_New_Item();
       -        if (item)
       -                item->type = cJSON_Object;
       -        return item;
       -}
       -
       -/* Create Arrays: */
       -cJSON *cJSON_CreateIntArray(const int *numbers, int count)
       -{
       -        int i;
       -        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
       -        for (i = 0; a && i < count; i++) {
       -                n = cJSON_CreateNumber(numbers[i]);
       -                if (!n) {
       -                        cJSON_Delete(a);
       -                        return 0;
       -                }
       -                if (!i)
       -                        a->child = n;
       -                else
       -                        suffix_object(p, n);
       -                p = n;
       -        }
       -        return a;
       -}
       -
       -cJSON *cJSON_CreateFloatArray(const float *numbers, int count)
       -{
       -        int i;
       -        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
       -        for (i = 0; a && i < count; i++) {
       -                n = cJSON_CreateNumber(numbers[i]);
       -                if (!n) {
       -                        cJSON_Delete(a);
       -                        return 0;
       -                }
       -                if (!i)
       -                        a->child = n;
       -                else
       -                        suffix_object(p, n);
       -                p = n;
       -        }
       -        return a;
       -}
       -
       -cJSON *cJSON_CreateDoubleArray(const double *numbers, int count)
       -{
       -        int i;
       -        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
       -        for (i = 0; a && i < count; i++) {
       -                n = cJSON_CreateNumber(numbers[i]);
       -                if (!n) {
       -                        cJSON_Delete(a);
       -                        return 0;
       -                }
       -                if (!i)
       -                        a->child = n;
       -                else
       -                        suffix_object(p, n);
       -                p = n;
       -        }
       -        return a;
       -}
       -
       -cJSON *cJSON_CreateStringArray(const char **strings, int count)
       -{
       -        int i;
       -        cJSON *n = 0, *p = 0, *a = cJSON_CreateArray();
       -        for (i = 0; a && i < count; i++) {
       -                n = cJSON_CreateString(strings[i]);
       -                if (!n) {
       -                        cJSON_Delete(a);
       -                        return 0;
       -                }
       -                if (!i)
       -                        a->child = n;
       -                else
       -                        suffix_object(p, n);
       -                p = n;
       -        }
       -        return a;
       -}
       -
       -/* Duplication */
       -cJSON *cJSON_Duplicate(cJSON * item, int recurse)
       -{
       -        cJSON *newitem, *cptr, *nptr = 0, *newchild;
       -        /* Bail on bad ptr */
       -        if (!item)
       -                return 0;
       -        /* Create new item */
       -        newitem = cJSON_New_Item();
       -        if (!newitem)
       -                return 0;
       -        /* Copy over all vars */
       -        newitem->type = item->type & (~cJSON_IsReference), newitem->valueint =
       -            item->valueint, newitem->valuedouble = item->valuedouble;
       -        if (item->valuestring) {
       -                newitem->valuestring = cJSON_strdup(item->valuestring);
       -                if (!newitem->valuestring) {
       -                        cJSON_Delete(newitem);
       -                        return 0;
       -                }
       -        }
       -        if (item->string) {
       -                newitem->string = cJSON_strdup(item->string);
       -                if (!newitem->string) {
       -                        cJSON_Delete(newitem);
       -                        return 0;
       -                }
       -        }
       -        /* If non-recursive, then we're done! */
       -        if (!recurse)
       -                return newitem;
       -        /* Walk the ->next chain for the child. */
       -        cptr = item->child;
       -        while (cptr) {
       -                newchild = cJSON_Duplicate(cptr, 1);        /* Duplicate (with recurse) each item in the ->next chain */
       -                if (!newchild) {
       -                        cJSON_Delete(newitem);
       -                        return 0;
       -                }
       -                if (nptr) {
       -                        nptr->next = newchild, newchild->prev = nptr;
       -                        nptr = newchild;
       -                } /* If newitem->child already set, then crosswire ->prev and ->next and move on */
       -                else {
       -                        newitem->child = newchild;
       -                        nptr = newchild;
       -                }                /* Set newitem->child and move to it */
       -                cptr = cptr->next;
       -        }
       -        return newitem;
       -}
       -
       -void cJSON_Minify(char *json)
       -{
       -        char *into = json;
       -        while (*json) {
       -                if (*json == ' ')
       -                        json++;
       -                else if (*json == '\t')
       -                        json++;        /* Whitespace characters. */
       -                else if (*json == '\r')
       -                        json++;
       -                else if (*json == '\n')
       -                        json++;
       -                else if (*json == '/' && json[1] == '/')
       -                        while (*json && *json != '\n')
       -                                json++;        /* double-slash comments, to end of line. */
       -                else if (*json == '/' && json[1] == '*') {
       -                        while (*json && !(*json == '*' && json[1] == '/'))
       -                                json++;
       -                        json += 2;
       -                } /* multiline comments. */
       -                else if (*json == '\"') {
       -                        *into++ = *json++;
       -                        while (*json && *json != '\"') {
       -                                if (*json == '\\')
       -                                        *into++ = *json++;
       -                                *into++ = *json++;
       -                        }
       -                        *into++ = *json++;
       -                } /* string literals, which are \" sensitive. */
       -                else
       -                        *into++ = *json++;        /* All other characters. */
       -        }
       -        *into = 0;                /* and null-terminate. */
       -}
   DIR diff --git a/cJSON.h b/cJSON.h
       t@@ -1,160 +0,0 @@
       -/*
       -  Copyright (c) 2009 Dave Gamble
       - 
       -  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 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.
       -*/
       -
       -#ifndef cJSON__h
       -#define cJSON__h
       -
       -#ifdef __cplusplus
       -extern "C" {
       -#endif
       -
       -/* cJSON Types: */
       -#define cJSON_False  (1 << 0)
       -#define cJSON_True   (1 << 1)
       -#define cJSON_NULL   (1 << 2)
       -#define cJSON_Number (1 << 3)
       -#define cJSON_String (1 << 4)
       -#define cJSON_Array  (1 << 5)
       -#define cJSON_Object (1 << 6)
       -
       -#define cJSON_IsReference 256
       -#define cJSON_StringIsConst 512
       -
       -/* The cJSON structure: */
       -        typedef struct cJSON {
       -                struct cJSON *next, *prev;        /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
       -                struct cJSON *child;        /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
       -
       -                int type;        /* The type of the item, as above. */
       -
       -                char *valuestring;        /* The item's string, if type==cJSON_String */
       -                int valueint;        /* The item's number, if type==cJSON_Number */
       -                double valuedouble;        /* The item's number, if type==cJSON_Number */
       -
       -                char *string;        /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
       -        } cJSON;
       -
       -        typedef struct cJSON_Hooks {
       -                void *(*malloc_fn) (size_t sz);
       -                void (*free_fn) (void *ptr);
       -        } cJSON_Hooks;
       -
       -/* Supply malloc, realloc and free functions to cJSON */
       -        extern void cJSON_InitHooks(cJSON_Hooks * hooks);
       -
       -/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
       -        extern cJSON *cJSON_Parse(const char *value);
       -/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
       -        extern char *cJSON_Print(cJSON * item);
       -/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
       -        extern char *cJSON_PrintUnformatted(cJSON * item);
       -/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
       -        extern char *cJSON_PrintBuffered(cJSON * item, int prebuffer, int fmt);
       -/* Delete a cJSON entity and all subentities. */
       -        extern void cJSON_Delete(cJSON * c);
       -
       -/* Returns the number of items in an array (or object). */
       -        extern int cJSON_GetArraySize(cJSON * array);
       -/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
       -        extern cJSON *cJSON_GetArrayItem(cJSON * array, int item);
       -/* Get item "string" from object. Case insensitive. */
       -        extern cJSON *cJSON_GetObjectItem(cJSON * object, const char *string);
       -        extern int cJSON_HasObjectItem(cJSON * object, const char *string);
       -/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
       -        extern const char *cJSON_GetErrorPtr(void);
       -
       -/* These calls create a cJSON item of the appropriate type. */
       -        extern cJSON *cJSON_CreateNull(void);
       -        extern cJSON *cJSON_CreateTrue(void);
       -        extern cJSON *cJSON_CreateFalse(void);
       -        extern cJSON *cJSON_CreateBool(int b);
       -        extern cJSON *cJSON_CreateNumber(double num);
       -        extern cJSON *cJSON_CreateString(const char *string);
       -        extern cJSON *cJSON_CreateArray(void);
       -        extern cJSON *cJSON_CreateObject(void);
       -
       -/* These utilities create an Array of count items. */
       -        extern cJSON *cJSON_CreateIntArray(const int *numbers, int count);
       -        extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count);
       -        extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count);
       -        extern cJSON *cJSON_CreateStringArray(const char **strings, int count);
       -
       -/* Append item to the specified array/object. */
       -        extern void cJSON_AddItemToArray(cJSON * array, cJSON * item);
       -        extern void cJSON_AddItemToObject(cJSON * object, const char *string,
       -                                          cJSON * item);
       -        extern void cJSON_AddItemToObjectCS(cJSON * object, const char *string, cJSON * item);        /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object */
       -/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
       -        extern void cJSON_AddItemReferenceToArray(cJSON * array, cJSON * item);
       -        extern void cJSON_AddItemReferenceToObject(cJSON * object,
       -                                                   const char *string,
       -                                                   cJSON * item);
       -
       -/* Remove/Detatch items from Arrays/Objects. */
       -        extern cJSON *cJSON_DetachItemFromArray(cJSON * array, int which);
       -        extern void cJSON_DeleteItemFromArray(cJSON * array, int which);
       -        extern cJSON *cJSON_DetachItemFromObject(cJSON * object,
       -                                                 const char *string);
       -        extern void cJSON_DeleteItemFromObject(cJSON * object,
       -                                               const char *string);
       -
       -/* Update array items. */
       -        extern void cJSON_InsertItemInArray(cJSON * array, int which, cJSON * newitem);        /* Shifts pre-existing items to the right. */
       -        extern void cJSON_ReplaceItemInArray(cJSON * array, int which,
       -                                             cJSON * newitem);
       -        extern void cJSON_ReplaceItemInObject(cJSON * object,
       -                                              const char *string,
       -                                              cJSON * newitem);
       -
       -/* Duplicate a cJSON item */
       -        extern cJSON *cJSON_Duplicate(cJSON * item, int recurse);
       -/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
       -need to be released. With recurse!=0, it will duplicate any children connected to the item.
       -The item->next and ->prev pointers are always zero on return from Duplicate. */
       -
       -/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
       -/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */
       -        extern cJSON *cJSON_ParseWithOpts(const char *value,
       -                                          const char **return_parse_end,
       -                                          int require_null_terminated);
       -
       -        extern void cJSON_Minify(char *json);
       -
       -/* Macros for creating things quickly. */
       -#define cJSON_AddNullToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateNull())
       -#define cJSON_AddTrueToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
       -#define cJSON_AddFalseToObject(object,name)                cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
       -#define cJSON_AddBoolToObject(object,name,b)        cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
       -#define cJSON_AddNumberToObject(object,name,n)        cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
       -#define cJSON_AddStringToObject(object,name,s)        cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
       -
       -/* When assigning an integer value, it needs to be propagated to valuedouble too. */
       -#define cJSON_SetIntValue(object,val)                        ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
       -#define cJSON_SetNumberValue(object,val)                ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
       -
       -/* Macro for iterating over an array */
       -#define cJSON_ArrayForEach(pos, head)                        for(pos = (head)->child; pos != NULL; pos = pos->next)
       -
       -#ifdef __cplusplus
       -}
       -#endif
       -#endif
   DIR diff --git a/grid.c b/grid.c
       t@@ -25,304 +25,307 @@
        
        #include "ltk.h"
        
       -void ltk_set_row_weight(LtkGrid *grid, int row, int weight)
       +void ltk_set_row_weight(LtkGrid * grid, int row, int weight)
        {
       -    grid->row_weights[row] = weight;
       -    ltk_recalculate_grid(grid);
       +        grid->row_weights[row] = weight;
       +        ltk_recalculate_grid(grid);
        }
        
       -void ltk_set_column_weight(LtkGrid *grid, int column, int weight)
       +void ltk_set_column_weight(LtkGrid * grid, int column, int weight)
        {
       -    grid->column_weights[column] = weight;
       -    ltk_recalculate_grid(grid);
       +        grid->column_weights[column] = weight;
       +        ltk_recalculate_grid(grid);
        }
        
       -void ltk_draw_grid(LtkGrid *grid)
       +void ltk_draw_grid(LtkGrid * grid)
        {
       -    int i;
       -    for (i = 0; i < grid->rows * grid->columns; i++)
       -    {
       -        if (!grid->widget_grid[i]) continue;
       -        LtkWidget *ptr = grid->widget_grid[i];
       -        ptr->draw(ptr);
       -    }
       +        int i;
       +        for (i = 0; i < grid->rows * grid->columns; i++) {
       +                if (!grid->widget_grid[i])
       +                        continue;
       +                LtkWidget *ptr = grid->widget_grid[i];
       +                ptr->draw(ptr);
       +        }
        }
        
       -LtkGrid *ltk_create_grid(LtkWindow *window, int rows, int columns)
       +LtkGrid *ltk_create_grid(LtkWindow * window, int rows, int columns)
        {
       -    LtkGrid *grid = malloc(sizeof(LtkGrid));
       +        LtkGrid *grid = malloc(sizeof(LtkGrid));
        
       -    grid->widget = ltk_create_widget(window, &ltk_draw_grid, &ltk_destroy_grid, 0);
       -    grid->widget.mouse_press = &ltk_grid_mouse_press;
       -    grid->widget.mouse_release = &ltk_grid_mouse_release;
       -    grid->widget.motion_notify = &ltk_grid_motion_notify;
       -    grid->widget.resize = &ltk_recalculate_grid;
       +        grid->widget =
       +            ltk_create_widget(window, &ltk_draw_grid, &ltk_destroy_grid,
       +                              0);
       +        grid->widget.mouse_press = &ltk_grid_mouse_press;
       +        grid->widget.mouse_release = &ltk_grid_mouse_release;
       +        grid->widget.motion_notify = &ltk_grid_motion_notify;
       +        grid->widget.resize = &ltk_recalculate_grid;
        
       -    grid->rows = rows;
       -    grid->columns = columns;
       -    grid->widget_grid = malloc(rows * columns * sizeof(LtkWidget));
       -    grid->row_heights = malloc(rows * sizeof(int));
       -    grid->column_widths = malloc(rows * sizeof(int));
       -    grid->row_weights = malloc(rows * sizeof(int));
       -    grid->column_weights = malloc(columns * sizeof(int));
       -    /* Positions have one extra for the end */
       -    grid->row_pos = malloc((rows + 1) * sizeof(int));
       -    grid->column_pos = malloc((columns + 1) * sizeof(int));
       -    int i;
       -    for (i = 0; i < rows; i++)
       -    {
       -        grid->row_heights[i] = 0;
       -        grid->row_weights[i] = 0;
       -        grid->row_pos[i] = 0;
       -    }
       -    grid->row_pos[rows] = 0;
       -    for (i = 0; i < columns; i++)
       -    {
       -        grid->column_widths[i] = 0;
       -        grid->column_weights[i] = 0;
       -        grid->column_pos[i] = 0;
       -    }
       -    grid->column_pos[columns] = 0;
       -    for (i = 0; i < rows * columns; i++)
       -    {
       -        grid->widget_grid[i] = NULL;
       -    }
       +        grid->rows = rows;
       +        grid->columns = columns;
       +        grid->widget_grid = malloc(rows * columns * sizeof(LtkWidget));
       +        grid->row_heights = malloc(rows * sizeof(int));
       +        grid->column_widths = malloc(rows * sizeof(int));
       +        grid->row_weights = malloc(rows * sizeof(int));
       +        grid->column_weights = malloc(columns * sizeof(int));
       +        /* Positions have one extra for the end */
       +        grid->row_pos = malloc((rows + 1) * sizeof(int));
       +        grid->column_pos = malloc((columns + 1) * sizeof(int));
       +        int i;
       +        for (i = 0; i < rows; i++) {
       +                grid->row_heights[i] = 0;
       +                grid->row_weights[i] = 0;
       +                grid->row_pos[i] = 0;
       +        }
       +        grid->row_pos[rows] = 0;
       +        for (i = 0; i < columns; i++) {
       +                grid->column_widths[i] = 0;
       +                grid->column_weights[i] = 0;
       +                grid->column_pos[i] = 0;
       +        }
       +        grid->column_pos[columns] = 0;
       +        for (i = 0; i < rows * columns; i++) {
       +                grid->widget_grid[i] = NULL;
       +        }
        
       -    ltk_recalculate_grid(grid);
       -    return grid;
       +        ltk_recalculate_grid(grid);
       +        return grid;
        }
        
        void ltk_destroy_grid(void *widget)
        {
       -    LtkGrid *grid = widget;
       -    LtkWidget *ptr;
       -    int i;
       -    for (i = 0; i < grid->rows * grid->columns; i++)
       -    {
       -        if (grid->widget_grid[i])
       -        {
       -            ptr = grid->widget_grid[i];
       -            ptr->destroy(ptr);
       -        }
       -    }
       -    free(grid->widget_grid);
       -    free(grid->row_heights);
       -    free(grid->column_widths);
       -    free(grid->row_weights);
       -    free(grid->column_weights);
       -    free(grid->row_pos);
       -    free(grid->column_pos);
       -    free(grid);
       +        LtkGrid *grid = widget;
       +        LtkWidget *ptr;
       +        int i;
       +        for (i = 0; i < grid->rows * grid->columns; i++) {
       +                if (grid->widget_grid[i]) {
       +                        ptr = grid->widget_grid[i];
       +                        ptr->destroy(ptr);
       +                }
       +        }
       +        free(grid->widget_grid);
       +        free(grid->row_heights);
       +        free(grid->column_widths);
       +        free(grid->row_weights);
       +        free(grid->column_weights);
       +        free(grid->row_pos);
       +        free(grid->column_pos);
       +        free(grid);
        }
        
        void ltk_recalculate_grid(void *widget)
        {
       -    LtkGrid *grid = widget;
       -    unsigned int height_static = 0, width_static = 0;
       -    unsigned int total_row_weight = 0, total_column_weight = 0;
       -    float height_unit = 0, width_unit = 0;
       -    unsigned int currentx = 0, currenty = 0;
       -    int i, j;
       -    for (i = 0; i < grid->rows; i++)
       -    {
       -        total_row_weight += grid->row_weights[i];
       -        if (grid->row_weights[i] == 0)
       -        {
       -            height_static += grid->row_heights[i];
       -        }
       -    }
       -    for (i = 0; i < grid->columns; i++)
       -    {
       -        total_column_weight += grid->column_weights[i];
       -        if (grid->column_weights[i] == 0)
       -        {
       -            width_static += grid->column_widths[i];
       -        }
       -    }
       -    if (total_row_weight > 0)
       -    {
       -        height_unit = (float)(grid->widget.rect.h - height_static) / (float)total_row_weight;
       -    }
       -    if (total_column_weight > 0)
       -    {
       -        width_unit = (float)(grid->widget.rect.w - width_static) / (float)total_column_weight;
       -    }
       -    for (i = 0; i < grid->rows; i++)
       -    {
       -        grid->row_pos[i] = currenty;
       -        if (grid->row_weights[i] > 0)
       -        {
       -            grid->row_heights[i] = grid->row_weights[i] * height_unit;
       -        }
       -        currenty += grid->row_heights[i];
       -    }
       -    grid->row_pos[grid->rows] = currenty;
       -    for (i = 0; i < grid->columns; i++)
       -    {
       -        grid->column_pos[i] = currentx;
       -        if (grid->column_weights[i] > 0)
       -        {
       -            grid->column_widths[i] = grid->column_weights[i] * width_unit;
       -        }
       -        currentx += grid->column_widths[i];
       -    }
       -    grid->column_pos[grid->columns] = currentx;
       -    int orig_width, orig_height;
       -    int end_column, end_row;
       -    for (i = 0; i < grid->rows; i++)
       -    {
       -        for (j = 0; j < grid->columns; j++)
       -        {
       -            if (!grid->widget_grid[i * grid->columns + j])
       -            {
       -                continue;
       -            }
       -            LtkWidget *ptr = grid->widget_grid[i * grid->columns + j];
       -            orig_width = ptr->rect.w;
       -            orig_height = ptr->rect.h;
       -            end_row = i + ptr->row_span;
       -            end_column = j + ptr->column_span;
       -            if ((ptr->sticky & (LTK_STICKY_LEFT | LTK_STICKY_RIGHT)) ==
       -                (LTK_STICKY_LEFT | LTK_STICKY_RIGHT))
       -            {
       -                ptr->rect.w = grid->column_pos[end_column] - grid->column_pos[j];
       -            }
       -            if ((ptr->sticky & (LTK_STICKY_TOP | LTK_STICKY_BOTTOM)) ==
       -                (LTK_STICKY_TOP | LTK_STICKY_BOTTOM))
       -            {
       -                ptr->rect.h = grid->row_pos[end_row] - grid->row_pos[i];
       -            }
       -            if (orig_width != ptr->rect.w || orig_height != ptr->rect.h)
       -            {
       -                if (ptr->resize)
       -                {
       -                    ptr->resize(ptr);
       -                }
       -            }
       +        LtkGrid *grid = widget;
       +        unsigned int height_static = 0, width_static = 0;
       +        unsigned int total_row_weight = 0, total_column_weight = 0;
       +        float height_unit = 0, width_unit = 0;
       +        unsigned int currentx = 0, currenty = 0;
       +        int i, j;
       +        for (i = 0; i < grid->rows; i++) {
       +                total_row_weight += grid->row_weights[i];
       +                if (grid->row_weights[i] == 0) {
       +                        height_static += grid->row_heights[i];
       +                }
       +        }
       +        for (i = 0; i < grid->columns; i++) {
       +                total_column_weight += grid->column_weights[i];
       +                if (grid->column_weights[i] == 0) {
       +                        width_static += grid->column_widths[i];
       +                }
       +        }
       +        if (total_row_weight > 0) {
       +                height_unit =
       +                    (float) (grid->widget.rect.h -
       +                             height_static) / (float) total_row_weight;
       +        }
       +        if (total_column_weight > 0) {
       +                width_unit =
       +                    (float) (grid->widget.rect.w -
       +                             width_static) / (float) total_column_weight;
       +        }
       +        for (i = 0; i < grid->rows; i++) {
       +                grid->row_pos[i] = currenty;
       +                if (grid->row_weights[i] > 0) {
       +                        grid->row_heights[i] =
       +                            grid->row_weights[i] * height_unit;
       +                }
       +                currenty += grid->row_heights[i];
       +        }
       +        grid->row_pos[grid->rows] = currenty;
       +        for (i = 0; i < grid->columns; i++) {
       +                grid->column_pos[i] = currentx;
       +                if (grid->column_weights[i] > 0) {
       +                        grid->column_widths[i] =
       +                            grid->column_weights[i] * width_unit;
       +                }
       +                currentx += grid->column_widths[i];
       +        }
       +        grid->column_pos[grid->columns] = currentx;
       +        int orig_width, orig_height;
       +        int end_column, end_row;
       +        for (i = 0; i < grid->rows; i++) {
       +                for (j = 0; j < grid->columns; j++) {
       +                        if (!grid->widget_grid[i * grid->columns + j]) {
       +                                continue;
       +                        }
       +                        LtkWidget *ptr =
       +                            grid->widget_grid[i * grid->columns + j];
       +                        orig_width = ptr->rect.w;
       +                        orig_height = ptr->rect.h;
       +                        end_row = i + ptr->row_span;
       +                        end_column = j + ptr->column_span;
       +                        if ((ptr->
       +                             sticky & (LTK_STICKY_LEFT | LTK_STICKY_RIGHT))
       +                            == (LTK_STICKY_LEFT | LTK_STICKY_RIGHT)) {
       +                                ptr->rect.w =
       +                                    grid->column_pos[end_column] -
       +                                    grid->column_pos[j];
       +                        }
       +                        if ((ptr->
       +                             sticky & (LTK_STICKY_TOP | LTK_STICKY_BOTTOM))
       +                            == (LTK_STICKY_TOP | LTK_STICKY_BOTTOM)) {
       +                                ptr->rect.h =
       +                                    grid->row_pos[end_row] -
       +                                    grid->row_pos[i];
       +                        }
       +                        if (orig_width != ptr->rect.w
       +                            || orig_height != ptr->rect.h) {
       +                                if (ptr->resize) {
       +                                        ptr->resize(ptr);
       +                                }
       +                        }
        
       -            if ((ptr->sticky & LTK_STICKY_RIGHT) == LTK_STICKY_RIGHT)
       -            {
       -                ptr->rect.x = grid->column_pos[end_column] - ptr->rect.w;
       -            }
       -            else if ((ptr->sticky & LTK_STICKY_LEFT) == LTK_STICKY_LEFT)
       -            {
       -                ptr->rect.x = grid->column_pos[j];
       -            }
       -            else
       -            {
       -                ptr->rect.x = grid->column_pos[j] + ((grid->column_pos[end_column] - grid->column_pos[j]) / 2 - ptr->rect.w / 2);
       -            }
       +                        if ((ptr->sticky & LTK_STICKY_RIGHT) ==
       +                            LTK_STICKY_RIGHT) {
       +                                ptr->rect.x =
       +                                    grid->column_pos[end_column] -
       +                                    ptr->rect.w;
       +                        } else if ((ptr->sticky & LTK_STICKY_LEFT) ==
       +                                   LTK_STICKY_LEFT) {
       +                                ptr->rect.x = grid->column_pos[j];
       +                        } else {
       +                                ptr->rect.x =
       +                                    grid->column_pos[j] +
       +                                    ((grid->column_pos[end_column] -
       +                                      grid->column_pos[j]) / 2 -
       +                                     ptr->rect.w / 2);
       +                        }
        
       -            if ((ptr->sticky & LTK_STICKY_BOTTOM) == LTK_STICKY_BOTTOM)
       -            {
       -                ptr->rect.y = grid->row_pos[end_row] - ptr->rect.h;
       -            }
       -            else if ((ptr->sticky & LTK_STICKY_TOP) == LTK_STICKY_TOP)
       -            {
       -                ptr->rect.y = grid->row_pos[i];
       -            }
       -            else
       -            {
       -                ptr->rect.y = grid->row_pos[i] + ((grid->row_pos[end_row] - grid->row_pos[i]) / 2 - ptr->rect.h / 2);
       -            }
       -        }
       -    }
       +                        if ((ptr->sticky & LTK_STICKY_BOTTOM) ==
       +                            LTK_STICKY_BOTTOM) {
       +                                ptr->rect.y =
       +                                    grid->row_pos[end_row] - ptr->rect.h;
       +                        } else if ((ptr->sticky & LTK_STICKY_TOP) ==
       +                                   LTK_STICKY_TOP) {
       +                                ptr->rect.y = grid->row_pos[i];
       +                        } else {
       +                                ptr->rect.y =
       +                                    grid->row_pos[i] +
       +                                    ((grid->row_pos[end_row] -
       +                                      grid->row_pos[i]) / 2 -
       +                                     ptr->rect.h / 2);
       +                        }
       +                }
       +        }
        }
        
       -void ltk_grid_widget(void *ptr, LtkGrid *grid, int row, int column, int row_span, int column_span, unsigned short sticky)
       +void ltk_grid_widget(void *ptr, LtkGrid * grid, int row, int column,
       +                     int row_span, int column_span, unsigned short sticky)
        {
       -    LtkWidget *widget = ptr;
       -    widget->sticky = sticky;
       -    widget->row = row;
       -    widget->column = column;
       -    widget->row_span = row_span;
       -    widget->column_span = column_span;
       -    if (grid->column_weights[column] == 0 && widget->rect.w > grid->column_widths[column])
       -    {
       -        grid->column_widths[column] = widget->rect.w;
       -    }
       -    if (grid->row_weights[row] == 0 && widget->rect.h > grid->row_heights[row])
       -    {
       -        grid->row_heights[row] = widget->rect.h;
       -    }
       -    grid->widget_grid[widget->row * grid->columns + widget->column] = widget;
       -    widget->parent = grid;
       -    ltk_recalculate_grid(grid);
       +        LtkWidget *widget = ptr;
       +        widget->sticky = sticky;
       +        widget->row = row;
       +        widget->column = column;
       +        widget->row_span = row_span;
       +        widget->column_span = column_span;
       +        if (grid->column_weights[column] == 0
       +            && widget->rect.w > grid->column_widths[column]) {
       +                grid->column_widths[column] = widget->rect.w;
       +        }
       +        if (grid->row_weights[row] == 0
       +            && widget->rect.h > grid->row_heights[row]) {
       +                grid->row_heights[row] = widget->rect.h;
       +        }
       +        grid->widget_grid[widget->row * grid->columns + widget->column] =
       +            widget;
       +        widget->parent = grid;
       +        ltk_recalculate_grid(grid);
        }
        
       -int ltk_grid_find_nearest_column(LtkGrid *grid, int x)
       +int ltk_grid_find_nearest_column(LtkGrid * grid, int x)
        {
       -    int i;
       -    for (i = 0; i < grid->columns; i++)
       -    {
       -        if (grid->column_pos[i] <= x && grid->column_pos[i + 1] >= x)
       -        {
       -            return i;
       -        }
       -    }
       +        int i;
       +        for (i = 0; i < grid->columns; i++) {
       +                if (grid->column_pos[i] <= x
       +                    && grid->column_pos[i + 1] >= x) {
       +                        return i;
       +                }
       +        }
       +        return -1;
        }
        
       -int ltk_grid_find_nearest_row(LtkGrid *grid, int y)
       +int ltk_grid_find_nearest_row(LtkGrid * grid, int y)
        {
       -    int i;
       -    for (i = 0; i < grid->rows; i++)
       -    {
       -        if (grid->row_pos[i] <= y && grid->row_pos[i + 1] >= y)
       -        {
       -            return i;
       -        }
       -    }
       +        int i;
       +        for (i = 0; i < grid->rows; i++) {
       +                if (grid->row_pos[i] <= y && grid->row_pos[i + 1] >= y) {
       +                        return i;
       +                }
       +        }
       +        return -1;
        }
        
       -ltk_grid_mouse_press(void *widget, XEvent event)
       +void ltk_grid_mouse_press(void *widget, XEvent event)
        {
       -    LtkGrid *grid = widget;
       -    int x = event.xbutton.x;
       -    int y = event.xbutton.y;
       -    int row = ltk_grid_find_nearest_row(grid, y);
       -    int column = ltk_grid_find_nearest_column(grid, x);
       -    LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
       -    if (ptr && ltk_collide_rect(ptr->rect, x, y))
       -    {
       -        ltk_mouse_press_event(ptr, event);
       -    }
       +        LtkGrid *grid = widget;
       +        int x = event.xbutton.x;
       +        int y = event.xbutton.y;
       +        int row = ltk_grid_find_nearest_row(grid, y);
       +        int column = ltk_grid_find_nearest_column(grid, x);
       +        if (row == -1 || column == -1)
       +                return;
       +        LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
       +        if (ptr && ltk_collide_rect(ptr->rect, x, y)) {
       +                ltk_mouse_press_event(ptr, event);
       +        }
        }
        
       -ltk_grid_mouse_release(void *widget, XEvent event)
       +void ltk_grid_mouse_release(void *widget, XEvent event)
        {
       -    LtkGrid *grid = widget;
       -    int x = event.xbutton.x;
       -    int y = event.xbutton.y;
       -    int row = ltk_grid_find_nearest_row(grid, y);
       -    int column = ltk_grid_find_nearest_column(grid, x);
       -    LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
       -    if (ptr)
       -    {
       -        if (ltk_collide_rect(ptr->rect, x, y))
       -        {
       -            ltk_mouse_release_event(ptr, event);
       -        }
       -        else
       -        {
       -            ltk_remove_hover_widget(grid);
       -            ltk_change_active_widget_state(grid, LTK_ACTIVE);
       -        }
       -    }
       +        LtkGrid *grid = widget;
       +        int x = event.xbutton.x;
       +        int y = event.xbutton.y;
       +        int row = ltk_grid_find_nearest_row(grid, y);
       +        int column = ltk_grid_find_nearest_column(grid, x);
       +        if (row == -1 || column == -1)
       +                return;
       +        LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
       +        if (ptr) {
       +                if (ltk_collide_rect(ptr->rect, x, y)) {
       +                        ltk_mouse_release_event(ptr, event);
       +                } else {
       +                        ltk_remove_hover_widget(grid);
       +                        ltk_change_active_widget_state(grid, LTK_ACTIVE);
       +                }
       +        }
        }
        
       -ltk_grid_motion_notify(void *widget, XEvent event)
       +void ltk_grid_motion_notify(void *widget, XEvent event)
        {
       -    LtkGrid *grid = widget;
       -    int x = event.xbutton.x;
       -    int y = event.xbutton.y;
       -    int row = ltk_grid_find_nearest_row(grid, y);
       -    int column = ltk_grid_find_nearest_column(grid, x);
       -    LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
       -    if (ptr)
       -    {
       -        if (ltk_collide_rect(ptr->rect, x, y))
       -            ltk_motion_notify_event(ptr, event);
       -        else if ((event.xmotion.state & Button1Mask) != Button1Mask)
       -            ltk_remove_hover_widget(grid);
       -    }
       +        LtkGrid *grid = widget;
       +        short pressed = (event.xmotion.state & Button1Mask) == Button1Mask;
       +        if (pressed)
       +                return;
       +        int x = event.xbutton.x;
       +        int y = event.xbutton.y;
       +        int row = ltk_grid_find_nearest_row(grid, y);
       +        int column = ltk_grid_find_nearest_column(grid, x);
       +        if (row == -1 || column == -1)
       +                return;
       +        LtkWidget *ptr = grid->widget_grid[row * grid->columns + column];
       +        if (ptr) {
       +                if (ltk_collide_rect(ptr->rect, x, y))
       +                        ltk_motion_notify_event(ptr, event);
       +                else if (!pressed)
       +                        ltk_remove_hover_widget(grid);
       +        }
        }
   DIR diff --git a/grid.h b/grid.h
       t@@ -29,18 +29,17 @@
        /*
         * Struct to represent a grid widget.
         */
       -typedef struct LtkGrid
       -{
       -    LtkWidget widget;
       -    unsigned int rows;
       -    unsigned int columns;
       -    void **widget_grid;
       -    unsigned int *row_heights;
       -    unsigned int *column_widths;
       -    unsigned int *row_weights;
       -    unsigned int *column_weights;
       -    unsigned int *row_pos;
       -    unsigned int *column_pos;
       +typedef struct LtkGrid {
       +        LtkWidget widget;
       +        unsigned int rows;
       +        unsigned int columns;
       +        void **widget_grid;
       +        unsigned int *row_heights;
       +        unsigned int *column_widths;
       +        unsigned int *row_weights;
       +        unsigned int *column_weights;
       +        unsigned int *row_pos;
       +        unsigned int *column_pos;
        } LtkGrid;
        
        /*
       t@@ -49,7 +48,7 @@ typedef struct LtkGrid
         * row: The row.
         * weight: The weight to set the row to.
         */
       -void ltk_set_row_weight(LtkGrid *grid, int row, int weight);
       +void ltk_set_row_weight(LtkGrid * grid, int row, int weight);
        
        /*
         * Set the weight of a column in a grid.
       t@@ -57,13 +56,13 @@ void ltk_set_row_weight(LtkGrid *grid, int row, int weight);
         * column: The column.
         * weight: The weight to set the row to.
         */
       -void ltk_set_column_weight(LtkGrid *grid, int column, int weight);
       +void ltk_set_column_weight(LtkGrid * grid, int column, int weight);
        
        /*
         * Draw all the widgets in a grid.
         * grid: The grid to draw the widgets of.
         */
       -void ltk_draw_grid(LtkGrid *grid);
       +void ltk_draw_grid(LtkGrid * grid);
        
        /*
         * Create a grid.
       t@@ -71,7 +70,7 @@ void ltk_draw_grid(LtkGrid *grid);
         * rows: The number of rows in the grid.
         * columns: The number of columns in the grid.
         */
       -LtkGrid *ltk_create_grid(LtkWindow *window, int rows, int columns);
       +LtkGrid *ltk_create_grid(LtkWindow * window, int rows, int columns);
        
        /*
         * Destroy a grid.
       t@@ -96,7 +95,8 @@ void ltk_recalculate_grid(void *widget);
         * columnspan: The amount of columns the widget should span.
         * sticky: Mask of the sticky values (LTK_STICKY_*).
         */
       -void ltk_grid_widget(void *ptr, LtkGrid *grid, int row, int column, int rowspan, int columnspan, unsigned short sticky);
       +void ltk_grid_widget(void *ptr, LtkGrid * grid, int row, int column,
       +                     int rowspan, int columnspan, unsigned short sticky);
        
        /*
         * Delegate a mouse press event on the grid to the proper widget.
   DIR diff --git a/ini.c b/ini.c
       t@@ -0,0 +1,201 @@
       +/* inih -- simple .INI file parser
       +
       +inih is released under the New BSD license (see LICENSE.txt). Go to the project
       +home page for more info:
       +
       +https://github.com/benhoyt/inih
       +
       +*/
       +
       +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
       +#define _CRT_SECURE_NO_WARNINGS
       +#endif
       +
       +#include <stdio.h>
       +#include <ctype.h>
       +#include <string.h>
       +
       +#include "ini.h"
       +
       +#if !INI_USE_STACK
       +#include <stdlib.h>
       +#endif
       +
       +#define MAX_SECTION 50
       +#define MAX_NAME 50
       +
       +/* Strip whitespace chars off end of given string, in place. Return s. */
       +static char* rstrip(char* s)
       +{
       +    char* p = s + strlen(s);
       +    while (p > s && isspace((unsigned char)(*--p)))
       +        *p = '\0';
       +    return s;
       +}
       +
       +/* Return pointer to first non-whitespace char in given string. */
       +static char* lskip(const char* s)
       +{
       +    while (*s && isspace((unsigned char)(*s)))
       +        s++;
       +    return (char*)s;
       +}
       +
       +/* Return pointer to first char (of chars) or inline comment in given string,
       +   or pointer to null at end of string if neither found. Inline comment must
       +   be prefixed by a whitespace character to register as a comment. */
       +static char* find_chars_or_comment(const char* s, const char* chars)
       +{
       +#if INI_ALLOW_INLINE_COMMENTS
       +    int was_space = 0;
       +    while (*s && (!chars || !strchr(chars, *s)) &&
       +           !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
       +        was_space = isspace((unsigned char)(*s));
       +        s++;
       +    }
       +#else
       +    while (*s && (!chars || !strchr(chars, *s))) {
       +        s++;
       +    }
       +#endif
       +    return (char*)s;
       +}
       +
       +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
       +static char* strncpy0(char* dest, const char* src, size_t size)
       +{
       +    strncpy(dest, src, size);
       +    dest[size - 1] = '\0';
       +    return dest;
       +}
       +
       +/* See documentation in header file. */
       +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
       +                     void* user)
       +{
       +    /* Uses a fair bit of stack (use heap instead if you need to) */
       +#if INI_USE_STACK
       +    char line[INI_MAX_LINE];
       +#else
       +    char* line;
       +#endif
       +    char section[MAX_SECTION] = "";
       +    char prev_name[MAX_NAME] = "";
       +
       +    char* start;
       +    char* end;
       +    char* name;
       +    char* value;
       +    int lineno = 0;
       +    int error = 0;
       +
       +#if !INI_USE_STACK
       +    line = (char*)malloc(INI_MAX_LINE);
       +    if (!line) {
       +        return -2;
       +    }
       +#endif
       +
       +#if INI_HANDLER_LINENO
       +#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
       +#else
       +#define HANDLER(u, s, n, v) handler(u, s, n, v)
       +#endif
       +
       +    /* Scan through stream line by line */
       +    while (reader(line, INI_MAX_LINE, stream) != NULL) {
       +        lineno++;
       +
       +        start = line;
       +#if INI_ALLOW_BOM
       +        if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
       +                           (unsigned char)start[1] == 0xBB &&
       +                           (unsigned char)start[2] == 0xBF) {
       +            start += 3;
       +        }
       +#endif
       +        start = lskip(rstrip(start));
       +
       +        if (*start == ';' || *start == '#') {
       +            /* Per Python configparser, allow both ; and # comments at the
       +               start of a line */
       +        }
       +#if INI_ALLOW_MULTILINE
       +        else if (*prev_name && *start && start > line) {
       +            /* Non-blank line with leading whitespace, treat as continuation
       +               of previous name's value (as per Python configparser). */
       +            if (!HANDLER(user, section, prev_name, start) && !error)
       +                error = lineno;
       +        }
       +#endif
       +        else if (*start == '[') {
       +            /* A "[section]" line */
       +            end = find_chars_or_comment(start + 1, "]");
       +            if (*end == ']') {
       +                *end = '\0';
       +                strncpy0(section, start + 1, sizeof(section));
       +                *prev_name = '\0';
       +            }
       +            else if (!error) {
       +                /* No ']' found on section line */
       +                error = lineno;
       +            }
       +        }
       +        else if (*start) {
       +            /* Not a comment, must be a name[=:]value pair */
       +            end = find_chars_or_comment(start, "=:");
       +            if (*end == '=' || *end == ':') {
       +                *end = '\0';
       +                name = rstrip(start);
       +                value = end + 1;
       +#if INI_ALLOW_INLINE_COMMENTS
       +                end = find_chars_or_comment(value, NULL);
       +                if (*end)
       +                    *end = '\0';
       +#endif
       +                value = lskip(value);
       +                rstrip(value);
       +
       +                /* Valid name[=:]value pair found, call handler */
       +                strncpy0(prev_name, name, sizeof(prev_name));
       +                if (!HANDLER(user, section, name, value) && !error)
       +                    error = lineno;
       +            }
       +            else if (!error) {
       +                /* No '=' or ':' found on name[=:]value line */
       +                error = lineno;
       +            }
       +        }
       +
       +#if INI_STOP_ON_FIRST_ERROR
       +        if (error)
       +            break;
       +#endif
       +    }
       +
       +#if !INI_USE_STACK
       +    free(line);
       +#endif
       +
       +    return error;
       +}
       +
       +/* See documentation in header file. */
       +int ini_parse_file(FILE* file, ini_handler handler, void* user)
       +{
       +    return ini_parse_stream((ini_reader)fgets, file, handler, user);
       +}
       +
       +/* See documentation in header file. */
       +int ini_parse(const char* filename, ini_handler handler, void* user)
       +{
       +    FILE* file;
       +    int error;
       +
       +    file = fopen(filename, "r");
       +    if (!file)
       +        return -1;
       +    error = ini_parse_file(file, handler, user);
       +    fclose(file);
       +    return error;
       +}
   DIR diff --git a/ini.h b/ini.h
       t@@ -0,0 +1,104 @@
       +/* inih -- simple .INI file parser
       +
       +inih is released under the New BSD license (see LICENSE.txt). Go to the project
       +home page for more info:
       +
       +https://github.com/benhoyt/inih
       +
       +*/
       +
       +#ifndef __INI_H__
       +#define __INI_H__
       +
       +/* Make this header file easier to include in C++ code */
       +#ifdef __cplusplus
       +extern "C" {
       +#endif
       +
       +#include <stdio.h>
       +
       +/* Nonzero if ini_handler callback should accept lineno parameter. */
       +#ifndef INI_HANDLER_LINENO
       +#define INI_HANDLER_LINENO 0
       +#endif
       +
       +/* Typedef for prototype of handler function. */
       +#if INI_HANDLER_LINENO
       +typedef int (*ini_handler)(void* user, const char* section,
       +                           const char* name, const char* value,
       +                           int lineno);
       +#else
       +typedef int (*ini_handler)(void* user, const char* section,
       +                           const char* name, const char* value);
       +#endif
       +
       +/* Typedef for prototype of fgets-style reader function. */
       +typedef char* (*ini_reader)(char* str, int num, void* stream);
       +
       +/* Parse given INI-style file. May have [section]s, name=value pairs
       +   (whitespace stripped), and comments starting with ';' (semicolon). Section
       +   is "" if name=value pair parsed before any section heading. name:value
       +   pairs are also supported as a concession to Python's configparser.
       +
       +   For each name=value pair parsed, call handler function with given user
       +   pointer as well as section, name, and value (data only valid for duration
       +   of handler call). Handler should return nonzero on success, zero on error.
       +
       +   Returns 0 on success, line number of first error on parse error (doesn't
       +   stop on first error), -1 on file open error, or -2 on memory allocation
       +   error (only when INI_USE_STACK is zero).
       +*/
       +int ini_parse(const char* filename, ini_handler handler, void* user);
       +
       +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
       +   close the file when it's finished -- the caller must do that. */
       +int ini_parse_file(FILE* file, ini_handler handler, void* user);
       +
       +/* Same as ini_parse(), but takes an ini_reader function pointer instead of
       +   filename. Used for implementing custom or string-based I/O. */
       +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
       +                     void* user);
       +
       +/* Nonzero to allow multi-line value parsing, in the style of Python's
       +   configparser. If allowed, ini_parse() will call the handler with the same
       +   name for each subsequent line parsed. */
       +#ifndef INI_ALLOW_MULTILINE
       +#define INI_ALLOW_MULTILINE 1
       +#endif
       +
       +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
       +   the file. See http://code.google.com/p/inih/issues/detail?id=21 */
       +#ifndef INI_ALLOW_BOM
       +#define INI_ALLOW_BOM 1
       +#endif
       +
       +/* Nonzero to allow inline comments (with valid inline comment characters
       +   specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
       +   Python 3.2+ configparser behaviour. */
       +#ifndef INI_ALLOW_INLINE_COMMENTS
       +#define INI_ALLOW_INLINE_COMMENTS 1
       +#endif
       +#ifndef INI_INLINE_COMMENT_PREFIXES
       +#define INI_INLINE_COMMENT_PREFIXES ";"
       +#endif
       +
       +/* Nonzero to use stack, zero to use heap (malloc/free). */
       +#ifndef INI_USE_STACK
       +#define INI_USE_STACK 1
       +#endif
       +
       +/* Stop parsing on first error (default is to keep parsing). */
       +#ifndef INI_STOP_ON_FIRST_ERROR
       +#define INI_STOP_ON_FIRST_ERROR 0
       +#endif
       +
       +/* Maximum line length for any line in INI file. */
       +#ifndef INI_MAX_LINE
       +#define INI_MAX_LINE 200
       +#endif
       +
       +#ifdef __cplusplus
       +}
       +#endif
       +
       +#endif /* __INI_H__ */
   DIR diff --git a/ltk.c b/ltk.c
       t@@ -22,399 +22,432 @@
         */
        
        #include "ltk.h"
       +#include "text.h"
        
        void ltk_init(const char *theme_path)
        {
       -    ltk_global = malloc(sizeof(Ltk));
       -    Ltk *ltk = ltk_global; /* For convenience */
       -    ltk->display = XOpenDisplay(NULL);
       -    ltk->screen = DefaultScreen(ltk->display);
       -    ltk->colormap = DefaultColormap(ltk->display, ltk->screen);
       -    ltk->theme = ltk_load_theme(theme_path);
       -    ltk->window_hash = NULL;
       -    ltk->wm_delete_msg = XInternAtom(ltk->display, "WM_DELETE_WINDOW", False);
       +        ltk_global = malloc(sizeof(Ltk));
       +        Ltk *ltk = ltk_global;        /* For convenience */
       +        ltk->display = XOpenDisplay(NULL);
       +        ltk->screen = DefaultScreen(ltk->display);
       +        ltk->colormap = DefaultColormap(ltk->display, ltk->screen);
       +        ltk->theme = ltk_load_theme(theme_path);
       +        ltk->window_hash = NULL;
       +        ltk->wm_delete_msg = XInternAtom(ltk->display, "WM_DELETE_WINDOW", False);
        }
        
        void ltk_clean_up(void)
        {
       -    LtkWindow *window;
       -    for (window = ltk_global->window_hash; window != NULL; window = window->hh.next)
       -    {
       -        ltk_destroy_window(window);
       -    }
       -    XCloseDisplay(ltk_global->display);
       -    ltk_destroy_theme(ltk_global->theme);
       -    free(ltk_global);
       +        LtkWindow *window;
       +        for (window = ltk_global->window_hash; window != NULL;
       +             window = window->hh.next) {
       +                ltk_destroy_window(window);
       +        }
       +        XCloseDisplay(ltk_global->display);
       +        ltk_destroy_theme(ltk_global->theme);
       +        free(ltk_global);
        }
        
        void ltk_quit(void)
        {
       -    ltk_clean_up();
       -    exit(0);
       +        ltk_clean_up();
       +        exit(0);
        }
        
        void ltk_fatal(const char *msg)
        {
       -    fprintf(stderr, msg);
       -    ltk_clean_up();
       -    exit(1);
       +        fprintf(stderr, msg);
       +        ltk_clean_up();
       +        exit(1);
        };
        
        XColor ltk_create_xcolor(const char *hex)
        {
       -    XColor color;
       -    XParseColor(ltk_global->display, ltk_global->colormap, hex, &color);
       -    XAllocColor(ltk_global->display, ltk_global->colormap, &color);
       +        XColor color;
       +        XParseColor(ltk_global->display, ltk_global->colormap, hex,
       +                    &color);
       +        XAllocColor(ltk_global->display, ltk_global->colormap, &color);
        
       -    return color;
       +        return color;
        }
        
        void ltk_mainloop(void)
        {
       -    XEvent event;
       -    KeySym key;
       -    char text[255];
       -
       -    while(1)
       -    {
       -        XNextEvent(ltk_global->display, &event);
       -        ltk_handle_event(event);
       -        /*
       -        if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1)
       -        {
       -            if (text[0] == 'q')
       -            {
       -                XCloseDisplay(ltk_global->display);
       -                exit(0);
       -            }
       -        }
       -        */
       -    }
       +        XEvent event;
       +        KeySym key;
       +        char text[255];
       +
       +        while (1) {
       +                XNextEvent(ltk_global->display, &event);
       +                ltk_handle_event(event);
       +                /*
       +                   if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1)
       +                   {
       +                   if (text[0] == 'q')
       +                   {
       +                   XCloseDisplay(ltk_global->display);
       +                   exit(0);
       +                   }
       +                   }
       +                 */
       +        }
        }
        
       -LtkWindowTheme *ltk_parse_window_theme(cJSON *window_json)
       +void ltk_redraw_window(LtkWindow * window)
        {
       -    LtkWindowTheme *window_theme = malloc(sizeof(LtkWindowTheme));
       -    if (!window_theme) ltk_fatal("No memory for new LtkWindowTheme\n");
       -    cJSON *border_width = cJSON_GetObjectItem(window_json, "border-width");
       -    cJSON *fg = cJSON_GetObjectItem(window_json, "foreground");
       -    cJSON *bg = cJSON_GetObjectItem(window_json, "background");
       -    window_theme->border_width = border_width ? border_width->valueint : 0;
       -    window_theme->fg = ltk_create_xcolor(fg->valuestring);
       -    window_theme->bg = ltk_create_xcolor(bg->valuestring);
       -
       -    return window_theme;
       +        LtkWidget *ptr;
       +        if (!window) {
       +                return;
       +        }
       +        if (!window->root_widget) {
       +                return;
       +        }
       +        ptr = window->root_widget;
       +        ptr->draw(ptr);
        }
        
       -void ltk_redraw_window(LtkWindow *window)
       +LtkWindow *ltk_create_window(const char *title, int x, int y,
       +                             unsigned int w, unsigned int h)
        {
       -    LtkWidget *ptr;
       -    if (!window)
       -    {
       -        return;
       -    }
       -    if (!window->root_widget)
       -    {
       -        return;
       -    }
       -    ptr = window->root_widget;
       -    ptr->draw(ptr);
       +        LtkWindow *window = malloc(sizeof(LtkWindow));
       +        if (!window)
       +                ltk_fatal("Not enough memory left for window!\n");
       +        LtkWindowTheme *wtheme = ltk_global->theme->window;        /* For convenience */
       +        Display *display = ltk_global->display;        /* For convenience */
       +        window->xwindow =
       +            XCreateSimpleWindow(display, DefaultRootWindow(display), x, y,
       +                                w, h, wtheme->border_width,
       +                                wtheme->fg.pixel, wtheme->bg.pixel);
       +        window->gc = XCreateGC(display, window->xwindow, 0, 0);
       +        XSetForeground(display, window->gc, wtheme->fg.pixel);
       +        XSetBackground(display, window->gc, wtheme->bg.pixel);
       +        XSetStandardProperties(display, window->xwindow, title, NULL, None,
       +                               NULL, 0, NULL);
       +        XSetWMProtocols(display, window->xwindow,
       +                        &ltk_global->wm_delete_msg, 1);
       +        window->root_widget = NULL;
       +
       +        window->other_event = &ltk_window_other_event;
       +
       +        window->rect.w = 0;
       +        window->rect.h = 0;
       +        window->rect.x = 0;
       +        window->rect.y = 0;
       +
       +        XClearWindow(display, window->xwindow);
       +        XMapRaised(display, window->xwindow);
       +        XSelectInput(display, window->xwindow,
       +                     ExposureMask | KeyPressMask | KeyReleaseMask |
       +                     ButtonPressMask | ButtonReleaseMask |
       +                     StructureNotifyMask | PointerMotionMask);
       +
       +        HASH_ADD_INT(ltk_global->window_hash, xwindow, window);
       +
       +        return window;
        }
        
       -LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h)
       +void ltk_remove_window(LtkWindow * window)
        {
       -    LtkWindow *window = malloc(sizeof(LtkWindow));
       -    if (!window) ltk_fatal("Not enough memory left for window!\n");
       -    LtkWindowTheme *wtheme = ltk_global->theme->window; /* For convenience */
       -    Display *display = ltk_global->display; /* For convenience */
       -    window->xwindow = XCreateSimpleWindow(display, DefaultRootWindow(display), x, y, w, h, wtheme->border_width, wtheme->fg.pixel, wtheme->bg.pixel);
       -    window->gc = XCreateGC(display, window->xwindow, 0, 0);
       -    XSetForeground(display, window->gc, wtheme->fg.pixel);
       -    XSetBackground(display, window->gc, wtheme->bg.pixel);
       -    XSetStandardProperties(display, window->xwindow, title, NULL, None, NULL, 0, NULL);
       -    XSetWMProtocols(display, window->xwindow, &ltk_global->wm_delete_msg, 1);
       -    window->root_widget = NULL;
       -
       -    window->other_event = &ltk_window_other_event;
       -
       -    window->rect.w = 0;
       -    window->rect.h = 0;
       -    window->rect.x = 0;
       -    window->rect.y = 0;
       -
       -    XClearWindow(display, window->xwindow);
       -    XMapRaised(display, window->xwindow);
       -    XSelectInput(display, window->xwindow, ExposureMask|KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|StructureNotifyMask|PointerMotionMask);
       -
       -    HASH_ADD_INT(ltk_global->window_hash, xwindow, window);
       -
       -    return window;
       +        ltk_destroy_window(window);
       +        if (!ltk_global->window_hash)
       +                ltk_quit();
        }
        
       -void ltk_remove_window(LtkWindow *window)
       +void ltk_destroy_window(LtkWindow * window)
        {
       -    ltk_destroy_window(window);
       -    if (!ltk_global->window_hash) ltk_quit();
       +        HASH_DEL(ltk_global->window_hash, window);
       +        LtkWidget *ptr = window->root_widget;
       +        if (ptr)
       +                ptr->destroy(ptr);
       +        XDestroyWindow(ltk_global->display, window->xwindow);
       +        free(window);
        }
        
       -void ltk_destroy_window(LtkWindow *window)
       +void ltk_window_other_event(void *widget, XEvent event)
        {
       -    HASH_DEL(ltk_global->window_hash, window);
       -    LtkWidget *ptr = window->root_widget;
       -    if (ptr) ptr->destroy(ptr);
       -    XDestroyWindow(ltk_global->display, window->xwindow);
       -    free(window);
       +        LtkWindow *window = widget;
       +        LtkWidget *ptr = window->root_widget;
       +        if (event.type == ConfigureNotify) {
       +                unsigned int w, h;
       +                w = event.xconfigure.width;
       +                h = event.xconfigure.height;
       +                if (ptr && ptr->resize
       +                    && (window->rect.w != w || window->rect.h != h)) {
       +                        window->rect.w = w;
       +                        window->rect.h = h;
       +                        ptr->rect.w = w;
       +                        ptr->rect.h = h;
       +                        ptr->resize(ptr);
       +                        ltk_redraw_window(window);
       +                }
       +        }
       +        if (event.type == Expose && event.xexpose.count == 0) {
       +                ltk_redraw_window(window);
       +        }
       +        if (event.type == ClientMessage
       +            && event.xclient.data.l[0] == ltk_global->wm_delete_msg) {
       +                ltk_remove_window(window);
       +        }
        }
        
       -void ltk_window_other_event(void *widget, XEvent event)
       +void ltk_window_ini_handler(LtkTheme *theme, const char *prop, const char *value)
       +{
       +        if (strcmp(prop, "border_width") == 0) {
       +                theme->window->border_width = atoi(value);
       +        } else if (strcmp(prop, "bg") == 0) {
       +                theme->window->bg = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "fg") == 0) {
       +                theme->window->fg = ltk_create_xcolor(value);
       +        } else if (strcmp(prop, "font") == 0) {
       +                theme->window->font = ltk_load_font(value);
       +        }
       +}
       +
       +int ltk_ini_handler(void *theme, const char *widget, const char *prop, const char *value)
        {
       -    LtkWindow *window = widget;
       -    LtkWidget *ptr = window->root_widget;
       -    if (event.type == ConfigureNotify)
       -    {
       -        unsigned int w, h;
       -        w = event.xconfigure.width;
       -        h = event.xconfigure.height;
       -        if (ptr && ptr->resize && (window->rect.w != w || window->rect.h != h))
       -        {
       -            window->rect.w = w;
       -            window->rect.h = h;
       -            ptr->rect.w = w;
       -            ptr->rect.h = h;
       -            ptr->resize(ptr);
       -            ltk_redraw_window(window);
       -        }
       -    }
       -    if (event.type == Expose && event.xexpose.count == 0)
       -    {
       -        ltk_redraw_window(window);
       -    }
       -    if (event.type == ClientMessage && event.xclient.data.l[0] == ltk_global->wm_delete_msg)
       -    {
       -        ltk_remove_window(window);
       -    }
       +        if (strcmp(widget, "window") == 0) {
       +                ltk_window_ini_handler(theme, prop, value);
       +        } else if (strcmp(widget, "button") == 0) {
       +                ltk_button_ini_handler(theme, prop, value);
       +        }
        }
        
        LtkTheme *ltk_load_theme(const char *path)
        {
       -    char *file_contents = ltk_read_file(path);
       -
       -    cJSON *json = cJSON_Parse(file_contents);
       -    if (!json)
       -    {
       -        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       -        return NULL;
       -    }
       -    cJSON *button_json = cJSON_GetObjectItem(json, "button");
       -    if (!button_json)
       -    {
       -        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       -        return NULL;
       -    }
       -    cJSON *window_json = cJSON_GetObjectItem(json, "window");
       -    if (!window_json)
       -    {
       -        printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       -        return NULL;
       -    }
       -
       -    LtkTheme *theme = malloc(sizeof(LtkTheme));
       -    theme->button = ltk_parse_button_theme(button_json);
       -    theme->window = ltk_parse_window_theme(window_json);
       -
       -    free(file_contents);
       -    cJSON_Delete(json);
       -
       -    return theme;
       +        LtkTheme *theme = malloc(sizeof(LtkTheme));
       +        theme->window = malloc(sizeof(LtkWindowTheme));
       +        theme->button = malloc(sizeof(LtkButtonTheme));
       +        if (ini_parse(path, ltk_ini_handler, theme) < 0) {
       +                fprintf(stderr, "ERROR: Can't load theme %s\n.", path);
       +                exit(1);
       +        }
       +        /*
       +        char *file_contents = ltk_read_file(path);
       +
       +        cJSON *json = cJSON_Parse(file_contents);
       +        if (!json) {
       +                printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       +                return NULL;
       +        }
       +        cJSON *button_json = cJSON_GetObjectItem(json, "button");
       +        if (!button_json) {
       +                printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       +                return NULL;
       +        }
       +        cJSON *window_json = cJSON_GetObjectItem(json, "window");
       +        if (!window_json) {
       +                printf("Theme error before: [%s]\n", cJSON_GetErrorPtr());
       +                return NULL;
       +        }
       +
       +        LtkTheme *theme = malloc(sizeof(LtkTheme));
       +        theme->button = ltk_parse_button_theme(button_json);
       +        theme->window = ltk_parse_window_theme(window_json);
       +
       +        free(file_contents);
       +        cJSON_Delete(json);
       +        */
       +
       +        return theme;
        }
        
       -void ltk_destroy_theme(LtkTheme *theme)
       +void ltk_destroy_theme(LtkTheme * theme)
        {
       -    free(theme->button);
       -    free(theme->window);
       -    free(theme);
       +        free(theme->button);
       +        free(theme->window);
       +        free(theme);
        }
        
        char *ltk_read_file(const char *path)
        {
       -    FILE *f;
       -    long len;
       -    char *file_contents;
       -    f = fopen(path, "rb");
       -    fseek(f, 0, SEEK_END);
       -    len = ftell(f);
       -    fseek(f, 0, SEEK_SET);
       -    file_contents = malloc(len + 1);
       -    fread(file_contents, 1, len, f);
       -    file_contents[len] = '\0';
       -    fclose(f);
       -
       -    return file_contents;
       +        FILE *f;
       +        long len;
       +        char *file_contents;
       +        f = fopen(path, "rb");
       +        fseek(f, 0, SEEK_END);
       +        len = ftell(f);
       +        fseek(f, 0, SEEK_SET);
       +        file_contents = malloc(len + 1);
       +        fread(file_contents, 1, len, f);
       +        file_contents[len] = '\0';
       +        fclose(f);
       +
       +        return file_contents;
        }
        
        int ltk_collide_rect(LtkRect rect, int x, int y)
        {
       -    return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y && (rect.y + rect.h) >= y);
       +        return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y
       +                && (rect.y + rect.h) >= y);
        }
        
        void ltk_remove_active_widget(void *widget)
        {
       -    if (!widget) return;
       -    LtkWidget *parent = widget;
       -    LtkWidget *child;
       -    while (parent->active_widget)
       -    {
       -        child = parent->active_widget;
       -        child->state = LTK_NORMAL;
       -        child->draw(child);
       -        parent->active_widget = NULL;
       -        parent = child;
       -    }
       +        if (!widget)
       +                return;
       +        LtkWidget *parent = widget;
       +        LtkWidget *child;
       +        while (parent->active_widget) {
       +                child = parent->active_widget;
       +                child->state = LTK_NORMAL;
       +                if (child->needs_redraw)
       +                        child->draw(child);
       +                parent->active_widget = NULL;
       +                parent = child;
       +        }
        }
        
        void ltk_change_active_widget_state(void *widget, LtkWidgetState state)
        {
       -    if (!widget) return;
       -    LtkWidget *ptr = widget;
       -    while (ptr = ptr->active_widget)
       -    {
       -        ptr->state = state;
       -        ptr->draw(ptr);
       -    }
       +        if (!widget)
       +                return;
       +        LtkWidget *ptr = widget;
       +        while (ptr = ptr->active_widget) {
       +                ptr->state = state;
       +                ptr->draw(ptr);
       +        }
        }
        
        void ltk_remove_hover_widget(void *widget)
        {
       -    if (!widget) return;
       -    LtkWidget *parent = widget;
       -    LtkWidget *child;
       -    while (parent->hover_widget)
       -    {
       -        child = parent->hover_widget;
       -        child->state = child->state == LTK_HOVERACTIVE ? LTK_ACTIVE : LTK_NORMAL;
       -        child->draw(child);
       -        parent->hover_widget = NULL;
       -        parent = child;
       -    }
       +        if (!widget)
       +                return;
       +        LtkWidget *parent = widget;
       +        LtkWidget *child;
       +        while (parent->hover_widget) {
       +                child = parent->hover_widget;
       +                child->state =
       +                    child->state ==
       +                    LTK_HOVERACTIVE ? LTK_ACTIVE : LTK_NORMAL;
       +                child->draw(child);
       +                parent->hover_widget = NULL;
       +                parent = child;
       +        }
        }
        
       -LtkWidget ltk_create_widget(LtkWindow *window, void (*draw)(void *), void (*destroy)(void *), int needs_redraw)
       +LtkWidget ltk_create_widget(LtkWindow *window, void (*draw) (void *),
       +                            void (*destroy) (void *),
       +                            unsigned int needs_redraw)
        {
       -    LtkWidget widget;
       -    widget.window = window;
       -    widget.active_widget = NULL;
       -    widget.hover_widget = NULL;
       -    widget.parent = NULL;
       -
       -    widget.key_press = NULL;
       -    widget.key_release = NULL;
       -    widget.mouse_press = NULL;
       -    widget.mouse_release = NULL;
       -    widget.motion_notify = NULL;
       -
       -    widget.resize = NULL;
       -    widget.draw = draw;
       -    widget.destroy = destroy;
       -
       -    widget.needs_redraw = needs_redraw;
       -    widget.state = LTK_NORMAL;
       -    widget.row = 0;
       -    widget.rect.x = 0;
       -    widget.rect.y = 0;
       -    widget.rect.w = 100;
       -    widget.rect.h = 100;
       -
       -    widget.row = NULL;
       -    widget.column = NULL;
       -    widget.row_span = NULL;
       -    widget.column_span = NULL;
       -    widget.sticky = NULL;
       -
       -    return widget;
       +        LtkWidget widget;
       +        widget.window = window;
       +        widget.active_widget = NULL;
       +        widget.hover_widget = NULL;
       +        widget.parent = NULL;
       +
       +        widget.key_press = NULL;
       +        widget.key_release = NULL;
       +        widget.mouse_press = NULL;
       +        widget.mouse_release = NULL;
       +        widget.motion_notify = NULL;
       +
       +        widget.resize = NULL;
       +        widget.draw = draw;
       +        widget.destroy = destroy;
       +
       +        widget.needs_redraw = needs_redraw;
       +        widget.state = LTK_NORMAL;
       +        widget.row = 0;
       +        widget.rect.x = 0;
       +        widget.rect.y = 0;
       +        widget.rect.w = 100;
       +        widget.rect.h = 100;
       +
       +        widget.row = NULL;
       +        widget.column = NULL;
       +        widget.row_span = NULL;
       +        widget.column_span = NULL;
       +        widget.sticky = 0;
       +
       +        return widget;
        }
        
        void ltk_mouse_press_event(void *widget, XEvent event)
        {
       -    LtkWidget *ptr = widget;
       -    if (!ptr || ptr->state == LTK_DISABLED) return;
       -    if (event.xbutton.button == 1)
       -    {
       -        LtkWidget *parent = ptr->parent;
       -        if (parent)
       -        {
       -            ltk_remove_active_widget(parent);
       -            parent->active_widget = ptr;
       -        }
       -        ptr->state = LTK_PRESSED;
       -        if (ptr->needs_redraw) ptr->draw(ptr);
       -    }
       -    if (ptr->mouse_press)
       -    {
       -        ptr->mouse_press(ptr, event);
       -    }
       +        LtkWidget *ptr = widget;
       +        if (!ptr || ptr->state == LTK_DISABLED)
       +                return;
       +        if (event.xbutton.button == 1) {
       +                LtkWidget *parent = ptr->parent;
       +                if (parent) {
       +                        ltk_remove_active_widget(parent);
       +                        parent->active_widget = ptr;
       +                }
       +                ptr->state = LTK_PRESSED;
       +                if (ptr->needs_redraw)
       +                        ptr->draw(ptr);
       +        }
       +        if (ptr->mouse_press) {
       +                ptr->mouse_press(ptr, event);
       +        }
        }
        
        void ltk_mouse_release_event(void *widget, XEvent event)
        {
       -    LtkWidget *ptr = widget;
       -    if (!ptr || ptr->state == LTK_DISABLED) return;
       -    if (ptr->state == LTK_PRESSED)
       -    {
       -        ptr->state = LTK_HOVERACTIVE;
       -        if (ptr->needs_redraw) ptr->draw(ptr);
       -    }
       -    if (ptr->mouse_release)
       -    {
       -        ptr->mouse_release(ptr, event);
       -    }
       +        LtkWidget *ptr = widget;
       +        if (!ptr || ptr->state == LTK_DISABLED)
       +                return;
       +        if (ptr->state == LTK_PRESSED) {
       +                ptr->state = LTK_HOVERACTIVE;
       +                if (ptr->needs_redraw)
       +                        ptr->draw(ptr);
       +        }
       +        if (ptr->mouse_release) {
       +                ptr->mouse_release(ptr, event);
       +        }
        }
        
        void ltk_motion_notify_event(void *widget, XEvent event)
        {
       -    LtkWidget *ptr = widget;
       -    if (ptr && (ptr->state == LTK_NORMAL || ptr->state == LTK_ACTIVE) &&
       -        (event.xmotion.state & Button1Mask) != Button1Mask)
       -    {
       -        ptr->state = ptr->state == LTK_ACTIVE ? LTK_HOVERACTIVE : LTK_HOVER;
       -        LtkWidget *parent = ptr->parent;
       -        if (parent)
       -        {
       -            ltk_remove_hover_widget(parent);
       -            parent->hover_widget = ptr;
       -        }
       -        if (ptr->needs_redraw) ptr->draw(ptr);
       -    }
       -    if (ptr->motion_notify)
       -    {
       -        ptr->motion_notify(ptr, event);
       -    }
       +        LtkWidget *ptr = widget;
       +        LtkWidget *parent;
       +        if (!ptr)
       +                return;
       +        short pressed = (event.xmotion.state & Button1Mask) == Button1Mask;
       +        if ((ptr->state == LTK_NORMAL || ptr->state == LTK_ACTIVE)
       +            && !pressed) {
       +                ptr->state =
       +                    ptr->state == LTK_ACTIVE ? LTK_HOVERACTIVE : LTK_HOVER;
       +                parent = ptr->parent;
       +                if (parent) {
       +                        ltk_remove_hover_widget(parent);
       +                        parent->hover_widget = ptr;
       +                }
       +                if (ptr->needs_redraw)
       +                        ptr->draw(ptr);
       +        }
       +        if (ptr->motion_notify)
       +                ptr->motion_notify(ptr, event);
        }
        
        void ltk_handle_event(XEvent event)
        {
       -    LtkWindow *window;
       -    LtkWidget *root_widget;
       -    HASH_FIND_INT(ltk_global->window_hash, &event.xany.window, window);
       -    if (!window) return;
       -    root_widget = window->root_widget;
       -    switch (event.type)
       -    {
       -    case KeyPress:
       -        break;
       -    case KeyRelease:
       -        break;
       -    case ButtonPress:
       -        if (root_widget) ltk_mouse_press_event(root_widget, event);
       -        break;
       -    case ButtonRelease:
       -        if (root_widget) ltk_mouse_release_event(root_widget, event);
       -        break;
       -    case MotionNotify:
       -        if (root_widget) ltk_motion_notify_event(root_widget, event);
       -        break;
       -    default:
       -        /* FIXME: users should be able to register other events like closing the window */
       -        if (window->other_event)
       -            window->other_event(window, event);
       -    }
       -}
       -\ No newline at end of file
       +        LtkWindow *window;
       +        LtkWidget *root_widget;
       +        HASH_FIND_INT(ltk_global->window_hash, &event.xany.window, window);
       +        if (!window)
       +                return;
       +        root_widget = window->root_widget;
       +        switch (event.type) {
       +        case KeyPress:
       +                break;
       +        case KeyRelease:
       +                break;
       +        case ButtonPress:
       +                if (root_widget)
       +                        ltk_mouse_press_event(root_widget, event);
       +                break;
       +        case ButtonRelease:
       +                if (root_widget)
       +                        ltk_mouse_release_event(root_widget, event);
       +                break;
       +        case MotionNotify:
       +                if (root_widget)
       +                        ltk_motion_notify_event(root_widget, event);
       +                break;
       +        default:
       +                /* FIXME: users should be able to register other events like closing the window */
       +                if (window->other_event)
       +                        window->other_event(window, event);
       +        }
       +}
   DIR diff --git a/ltk.h b/ltk.h
       t@@ -28,294 +28,141 @@
        #include <stdlib.h>
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
       -#include "cJSON.h"
       +#include "ini.h"
        #include "uthash.h"
       +#include "stb_truetype.h"
        
       -/*
       - * Struct to represent a rectangle.
       - */
       -typedef struct
       -{
       -    int x;
       -    int y;
       -    int w;
       -    int h;
       +typedef struct {
       +        int x;
       +        int y;
       +        int w;
       +        int h;
        } LtkRect;
        
        typedef enum {
       -    LTK_STICKY_LEFT = 1 << 0,
       -    LTK_STICKY_RIGHT = 1 << 1,
       -    LTK_STICKY_TOP = 1 << 2,
       -    LTK_STICKY_BOTTOM = 1 << 3
       +        LTK_STICKY_LEFT = 1 << 0,
       +        LTK_STICKY_RIGHT = 1 << 1,
       +        LTK_STICKY_TOP = 1 << 2,
       +        LTK_STICKY_BOTTOM = 1 << 3
        } LtkStickyMask;
        
       -/*
       - * An enumeration of all widget states.
       - */
       -typedef enum
       -{
       -    LTK_NORMAL = 0,
       -    LTK_HOVER = 1,
       -    LTK_PRESSED = 2,
       -    LTK_ACTIVE = 3,
       -    LTK_HOVERACTIVE = 4,
       -    LTK_DISABLED = 5
       +typedef enum {
       +        LTK_NORMAL = 0,
       +        LTK_HOVER = 1,
       +        LTK_PRESSED = 2,
       +        LTK_ACTIVE = 3,
       +        LTK_HOVERACTIVE = 4,
       +        LTK_DISABLED = 5
        } LtkWidgetState;
        
        typedef struct LtkWindow LtkWindow;
        
       -/*
       - * A struct to contain all basic widget information.
       - * First element of every widget so the widget can
       - * be cast to LtkWidget.
       - */
       -typedef struct LtkWidget
       -{
       -    /* The window the widget will be displayed on */
       -    LtkWindow *window;
       -    /* For container widgets; the widget that is currently active */
       -    struct LtkWidget *active_widget;
       -    /* For container widgets; the widget that is currently highlighted */
       -    struct LtkWidget *hover_widget;
       -    /* Parent widget */
       -    struct LtkWidget *parent;
       -
       -    /* Called on KeyPress events */
       -    void (*key_press)(void *, XEvent event);
       -    /* Called on KeyRelease events */
       -    void (*key_release)(void *, XEvent event);
       -    /* Called on ButtonPress events */
       -    void (*mouse_press)(void *, XEvent event);
       -    /* Called on ButtonRelease event */
       -    void (*mouse_release)(void *, XEvent event);
       -    /* Called on MotionNotify events */
       -    void (*motion_notify)(void *, XEvent event);
       -
       -    /* Function to update the widget after its LtkRect has been modified */
       -    void (*resize)(void *);
       -    /* Function to draw the widget */
       -    void (*draw)(void *);
       -    /* Function to destroy the widget */
       -    void (*destroy)(void *);
       -
       -    /* Position and size of the widget */
       -    LtkRect rect;
       -    /* Row of widget if gridded */
       -    unsigned int row;
       -    /* Column of widget if gridded */
       -    unsigned int column;
       -    /* Row span of widget if gridded */
       -    unsigned int row_span;
       -    /* Column span of widget if gridded */
       -    unsigned int column_span;
       -    /* Specifies if the widget needs to be redrawn after a state change */
       -    int needs_redraw : 1;
       -    /* State of the widget */
       -    LtkWidgetState state : 3;
       -    /* Similar to sticky in tk */
       -    unsigned short sticky : 4;
       +/* FIXME: change row, column, etc. to void* struct so
       +   other layout systems can be implemented */
       +typedef struct LtkWidget {
       +        LtkWindow *window;
       +        struct LtkWidget *active_widget;
       +        struct LtkWidget *hover_widget;
       +        struct LtkWidget *parent;
       +
       +        void (*key_press) (void *, XEvent event);
       +        void (*key_release) (void *, XEvent event);
       +        void (*mouse_press) (void *, XEvent event);
       +        void (*mouse_release) (void *, XEvent event);
       +        void (*motion_notify) (void *, XEvent event);
       +
       +        void (*resize) (void *);
       +        void (*draw) (void *);
       +        void (*destroy) (void *);
       +
       +        LtkRect rect;
       +        unsigned int row;
       +        unsigned int column;
       +        unsigned int row_span;
       +        unsigned int column_span;
       +        unsigned int needs_redraw;
       +        LtkWidgetState state;
       +        unsigned short sticky;
        } LtkWidget;
        
       -/*
       - * Struct to represent a window.
       - */
       -typedef struct LtkWindow
       -{
       -    Window xwindow;
       -    GC gc;
       -    void *root_widget;
       -    void (*other_event)(void *, XEvent event);
       -    LtkRect rect;
       -    UT_hash_handle hh;
       +typedef struct LtkWindow {
       +        Window xwindow;
       +        GC gc;
       +        void *root_widget;
       +        void (*other_event) (void *, XEvent event);
       +        LtkRect rect;
       +        UT_hash_handle hh;
        } LtkWindow;
        
       -/*
       - * Struct to represent the border width,
       - * foreground color, and background color
       - * of a window.
       - */
       -typedef struct LtkWindowTheme
       -{
       -    int border_width;
       -    XColor fg;
       -    XColor bg;
       +typedef struct LtkWindowTheme {
       +        int border_width;
       +        stbtt_fontinfo font;
       +        XColor fg;
       +        XColor bg;
        } LtkWindowTheme;
        
        #include "button.h"
        #include "grid.h"
        
       -/*
       - * Struct to contain all styles needed by LTK.
       - */
       -typedef struct
       -{
       -    LtkWindowTheme *window;
       -    LtkButtonTheme *button;
       +typedef struct {
       +        LtkWindowTheme *window;
       +        LtkButtonTheme *button;
        } LtkTheme;
        
       -/*
       - * Load a theme from a JSON file.
       - * path: The path to the file.
       - */
        LtkTheme *ltk_load_theme(const char *path);
        
       -/*
       - * Struct to contain all global information.
       - */
       -typedef struct
       -{
       -    /* The theme used by LTK */
       -    LtkTheme *theme;
       -    /* The connection to the X server */
       -    Display *display;
       -    /* The screen LTK is working on */
       -    int screen;
       -    /* The colormap used by the colors in the theme */
       -    Colormap colormap;
       -    /* A hash table of all windows */
       -    LtkWindow *window_hash;
       -    /* Needed for handling the WM_DELETE_WINDOW signal */
       -    Atom wm_delete_msg;
       +typedef struct {
       +        LtkTheme *theme;
       +        Display *display;
       +        int screen;
       +        Colormap colormap;
       +        LtkWindow *window_hash;
       +        Atom wm_delete_msg;
        } Ltk;
        
        Ltk *ltk_global;
        
       -/*
       - * Initialize LTK.
       - * theme_path: The path to the theme.
       - */
        void ltk_init(const char *theme_path);
        
       -/*
       - * Print a message, clean up, and quit.
       - * msg: The message to print.
       - */
        void ltk_fatal(const char *msg);
        
       -/*
       - * Create an XColor struct from the hex code of a color.
       - * hex: The hex code.
       - */
        XColor ltk_create_xcolor(const char *hex);
        
       -/*
       - * Main event loop.
       - */
        void ltk_mainloop(void);
        
       -/*
       - * Create a window.
       - * title: The title of the window.
       - * x: The x position of the window.
       - * y: The y position of the window.
       - * w: The width of the window.
       - * h: The height of the window.
       - */
       -LtkWindow *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h);
       +LtkWindow *ltk_create_window(const char *title, int x, int y,
       +                             unsigned int w, unsigned int h);
        
       -/*
       - * Redraw the widgets in a window.
       - * window: The window to redraw.
       - */
       -void ltk_redraw_window(LtkWindow *window);
       +void ltk_redraw_window(LtkWindow * window);
        
       -/*
       - * Destroy a window and quit the application
       - * if no windows are left.
       - * window: The window to remove.
       - */
       -void ltk_remove_window(LtkWindow *window);
       +void ltk_remove_window(LtkWindow * window);
        
       -/*
       - * Destroy a window, freeing its memory.
       - * window: The window to destroy.
       - */
       -void ltk_destroy_window(LtkWindow *window);
       +void ltk_destroy_window(LtkWindow * window);
        
       -/*
       - * Handles any event other than mouse or keyboard events.
       - * widget: Pointer to the window.
       - * event: The XEvent to be handled.
       - */
        void ltk_window_other_event(void *widget, XEvent event);
        
       -/*
       - * Destroy an LtkTheme struct.
       - * theme: Pointer to the struct.
       - */
       -void ltk_destroy_theme(LtkTheme *theme);
       +void ltk_destroy_theme(LtkTheme * theme);
        
       -/*
       - * Check if a rectangle collides with a point.
       - * rect: The rectangle.
       - * x: The x coordinate of the point.
       - * y: The y coordinate of the point.
       - */
        int ltk_collide_rect(LtkRect rect, int x, int y);
        
       -/*
       - * Read a file and return a null-terminated string with the contents.
       - * path: The path to the file.
       - */
        char *ltk_read_file(const char *path);
        
       -/*
       - * Recursively set the state of all active_widgets in 'widget' to 'state'.
       - */
        void ltk_change_active_widget_state(void *widget, LtkWidgetState state);
        
       -/*
       - * Recursively set the state of all active_widgets in 'widget' to LTK_NORMAL,
       - * redraw the widgets, and remove the references to them from their parents.
       - */
        void ltk_remove_active_widget(void *widget);
        
       -/*
       - * Recursively set the state of all hover_widgets in 'widget' to LTK_NORMAL or
       - * LTK_ACTIVE, redraw the widgets, and remove the references to them from their parents.
       - */
        void ltk_remove_hover_widget(void *widget);
        
       -/*
       - * Create a widget.
       - * window: The window the widget is to be shown on.
       - * draw: The function used to draw the widget.
       - * destroy: The function used to destroy the widget.
       - * needs_redraw: Flag to indicate if the widget needs to be
       - *               be redrawn when it is resized.
       - * Returns: The new LtkWidget.
       - */
       -LtkWidget ltk_create_widget(LtkWindow *window, void (*draw)(void *), void (*destroy)(void *), int needs_redraw);
       +LtkWidget ltk_create_widget(LtkWindow * window, void (*draw) (void *),
       +                            void (*destroy) (void *),
       +                            unsigned int needs_redraw);
        
       -/*
       - * Handles mouse press events for all widgets and calls
       - * specific widget handlers if set.
       - * widget: Pointer to the widget the mouse is currrently on.
       - * event: The event to be handled.
       - */
        void ltk_mouse_press_event(void *widget, XEvent event);
        
       -/*
       - * Handles mouse release events for all widgets and calls
       - * specific widget handlers if set.
       - * widget: Pointer to the widget the mouse is currrently on.
       - * event: The event to be handled.
       - */
        void ltk_mouse_release_event(void *widget, XEvent event);
        
       -/*
       - * Handles mouse motion events for all widgets and calls
       - * specific widget handlers if set.
       - * widget: Pointer to the widget the mouse is currrently on.
       - * event: The event to be handled.
       - */
        void ltk_motion_notify_event(void *widget, XEvent event);
        
       -/*
       - * Handles all events and dispatches them to their
       - * respective handlers.
       - * event: The event to be handled.
       - */
        void ltk_handle_event(XEvent event);
        
        #endif
   DIR diff --git a/main.c b/main.c
       t@@ -6,47 +6,50 @@
        
        int main(int argc, char *argv[])
        {
       -    Display *display;
       -    int screen;
       -    Window window;
       -    GC gc;
       +        Display *display;
       +        int screen;
       +        Window window;
       +        GC gc;
        
       -    unsigned long black, white;
       -    XColor green;
       -    Colormap colormap;
       -    display = XOpenDisplay((char *)0);
       -    screen = DefaultScreen(display);
       -    colormap = DefaultColormap(display, screen);
       -    black = BlackPixel(display, screen);
       -    white = WhitePixel(display, screen);
       -    XParseColor(display, colormap, "#00FF00", &green);
       -    XAllocColor(display, colormap, &green);
       -    window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, 200, 300, 0, white, green.pixel);
       -    XSetStandardProperties(display, window, "Random Window", NULL, None, NULL, 0, NULL);
       -    XSelectInput(display, window, ExposureMask|ButtonPressMask|KeyPressMask);
       -    gc = XCreateGC(display, window, 0, 0);
       -    XSetBackground(display, gc, white);
       -    XSetForeground(display, gc, black);
       -    XClearWindow(display, window);
       -    XMapRaised(display, window);
       +        unsigned long black, white;
       +        XColor green;
       +        Colormap colormap;
       +        display = XOpenDisplay((char *) 0);
       +        screen = DefaultScreen(display);
       +        colormap = DefaultColormap(display, screen);
       +        black = BlackPixel(display, screen);
       +        white = WhitePixel(display, screen);
       +        XParseColor(display, colormap, "#00FF00", &green);
       +        XAllocColor(display, colormap, &green);
       +        window =
       +            XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0,
       +                                200, 300, 0, white, green.pixel);
       +        XSetStandardProperties(display, window, "Random Window", NULL,
       +                               None, NULL, 0, NULL);
       +        XSelectInput(display, window,
       +                     ExposureMask | ButtonPressMask | KeyPressMask);
       +        gc = XCreateGC(display, window, 0, 0);
       +        XSetBackground(display, gc, white);
       +        XSetForeground(display, gc, black);
       +        XClearWindow(display, window);
       +        XMapRaised(display, window);
        
       -    XEvent event;
       -    KeySym key;
       -    char text[255];
       +        XEvent event;
       +        KeySym key;
       +        char text[255];
        
       -    while(1)
       -    {
       -        XNextEvent(display, &event);
       -        if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1)
       -        {
       -            if (text[0] == 'q')
       -            {
       -                XFreeGC(display, gc);
       -                XFreeColormap(display, colormap);
       -                XDestroyWindow(display, window);
       -                XCloseDisplay(display);
       -                exit(0);
       -            }
       -        }
       -    }
       +        while (1) {
       +                XNextEvent(display, &event);
       +                if (event.type == KeyPress
       +                    && XLookupString(&event.xkey, text, 255, &key,
       +                                     0) == 1) {
       +                        if (text[0] == 'q') {
       +                                XFreeGC(display, gc);
       +                                XFreeColormap(display, colormap);
       +                                XDestroyWindow(display, window);
       +                                XCloseDisplay(display);
       +                                exit(0);
       +                        }
       +                }
       +        }
        }
   DIR diff --git a/stb_truetype.h b/stb_truetype.h
       t@@ -0,0 +1,4021 @@
       +#ifndef BOB
       +#define BOB
       +// stb_truetype.h - v1.13 - public domain
       +// authored from 2009-2016 by Sean Barrett / RAD Game Tools
       +//
       +//   This library processes TrueType files:
       +//        parse files
       +//        extract glyph metrics
       +//        extract glyph shapes
       +//        render glyphs to one-channel bitmaps with antialiasing (box filter)
       +//
       +//   Todo:
       +//        non-MS cmaps
       +//        crashproof on bad data
       +//        hinting? (no longer patented)
       +//        cleartype-style AA?
       +//        optimize: use simple memory allocator for intermediates
       +//        optimize: build edge-list directly from curves
       +//        optimize: rasterize directly from curves?
       +//
       +// ADDITIONAL CONTRIBUTORS
       +//
       +//   Mikko Mononen: compound shape support, more cmap formats
       +//   Tor Andersson: kerning, subpixel rendering
       +//   Dougall Johnson: OpenType / Type 2 font handling
       +//
       +//   Misc other:
       +//       Ryan Gordon
       +//       Simon Glass
       +//       github:IntellectualKitty
       +//
       +//   Bug/warning reports/fixes:
       +//       "Zer" on mollyrocket (with fix)
       +//       Cass Everitt
       +//       stoiko (Haemimont Games)
       +//       Brian Hook 
       +//       Walter van Niftrik
       +//       David Gow
       +//       David Given
       +//       Ivan-Assen Ivanov
       +//       Anthony Pesch
       +//       Johan Duparc
       +//       Hou Qiming
       +//       Fabian "ryg" Giesen
       +//       Martins Mozeiko
       +//       Cap Petschulat
       +//       Omar Cornut
       +//       github:aloucks
       +//       Peter LaValle
       +//       Sergey Popov
       +//       Giumo X. Clanjor
       +//       Higor Euripedes
       +//       Thomas Fields
       +//       Derek Vinyard
       +//
       +// VERSION HISTORY
       +//
       +//   1.13 (2017-01-02) support OpenType fonts, certain Apple fonts, num-fonts-in-TTC function
       +//   1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
       +//   1.11 (2016-04-02) fix unused-variable warning
       +//   1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef
       +//   1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly
       +//   1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
       +//   1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
       +//                     variant PackFontRanges to pack and render in separate phases;
       +//                     fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
       +//                     fixed an assert() bug in the new rasterizer
       +//                     replace assert() with STBTT_assert() in new rasterizer
       +//
       +//   Full history can be found at the end of this file.
       +//
       +// LICENSE
       +//
       +//   This software is dual-licensed to the public domain and under the following
       +//   license: you are granted a perpetual, irrevocable license to copy, modify,
       +//   publish, and distribute this file as you see fit.
       +//
       +// USAGE
       +//
       +//   Include this file in whatever places neeed to refer to it. In ONE C/C++
       +//   file, write:
       +//      #define STB_TRUETYPE_IMPLEMENTATION
       +//   before the #include of this file. This expands out the actual
       +//   implementation into that C/C++ file.
       +//
       +//   To make the implementation private to the file that generates the implementation,
       +//      #define STBTT_STATIC
       +//
       +//   Simple 3D API (don't ship this, but it's fine for tools and quick start)
       +//           stbtt_BakeFontBitmap()               -- bake a font to a bitmap for use as texture
       +//           stbtt_GetBakedQuad()                 -- compute quad to draw for a given char
       +//
       +//   Improved 3D API (more shippable):
       +//           #include "stb_rect_pack.h"           -- optional, but you really want it
       +//           stbtt_PackBegin()
       +//           stbtt_PackSetOversample()            -- for improved quality on small fonts
       +//           stbtt_PackFontRanges()               -- pack and renders
       +//           stbtt_PackEnd()
       +//           stbtt_GetPackedQuad()
       +//
       +//   "Load" a font file from a memory buffer (you have to keep the buffer loaded)
       +//           stbtt_InitFont()
       +//           stbtt_GetFontOffsetForIndex()        -- indexing for TTC font collections
       +//           stbtt_GetNumberOfFonts()             -- number of fonts for TTC font collections
       +//
       +//   Render a unicode codepoint to a bitmap
       +//           stbtt_GetCodepointBitmap()           -- allocates and returns a bitmap
       +//           stbtt_MakeCodepointBitmap()          -- renders into bitmap you provide
       +//           stbtt_GetCodepointBitmapBox()        -- how big the bitmap must be
       +//
       +//   Character advance/positioning
       +//           stbtt_GetCodepointHMetrics()
       +//           stbtt_GetFontVMetrics()
       +//           stbtt_GetCodepointKernAdvance()
       +//
       +//   Starting with version 1.06, the rasterizer was replaced with a new,
       +//   faster and generally-more-precise rasterizer. The new rasterizer more
       +//   accurately measures pixel coverage for anti-aliasing, except in the case
       +//   where multiple shapes overlap, in which case it overestimates the AA pixel
       +//   coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If
       +//   this turns out to be a problem, you can re-enable the old rasterizer with
       +//        #define STBTT_RASTERIZER_VERSION 1
       +//   which will incur about a 15% speed hit.
       +//
       +// ADDITIONAL DOCUMENTATION
       +//
       +//   Immediately after this block comment are a series of sample programs.
       +//
       +//   After the sample programs is the "header file" section. This section
       +//   includes documentation for each API function.
       +//
       +//   Some important concepts to understand to use this library:
       +//
       +//      Codepoint
       +//         Characters are defined by unicode codepoints, e.g. 65 is
       +//         uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is
       +//         the hiragana for "ma".
       +//
       +//      Glyph
       +//         A visual character shape (every codepoint is rendered as
       +//         some glyph)
       +//
       +//      Glyph index
       +//         A font-specific integer ID representing a glyph
       +//
       +//      Baseline
       +//         Glyph shapes are defined relative to a baseline, which is the
       +//         bottom of uppercase characters. Characters extend both above
       +//         and below the baseline.
       +//
       +//      Current Point
       +//         As you draw text to the screen, you keep track of a "current point"
       +//         which is the origin of each character. The current point's vertical
       +//         position is the baseline. Even "baked fonts" use this model.
       +//
       +//      Vertical Font Metrics
       +//         The vertical qualities of the font, used to vertically position
       +//         and space the characters. See docs for stbtt_GetFontVMetrics.
       +//
       +//      Font Size in Pixels or Points
       +//         The preferred interface for specifying font sizes in stb_truetype
       +//         is to specify how tall the font's vertical extent should be in pixels.
       +//         If that sounds good enough, skip the next paragraph.
       +//
       +//         Most font APIs instead use "points", which are a common typographic
       +//         measurement for describing font size, defined as 72 points per inch.
       +//         stb_truetype provides a point API for compatibility. However, true
       +//         "per inch" conventions don't make much sense on computer displays
       +//         since they different monitors have different number of pixels per
       +//         inch. For example, Windows traditionally uses a convention that
       +//         there are 96 pixels per inch, thus making 'inch' measurements have
       +//         nothing to do with inches, and thus effectively defining a point to
       +//         be 1.333 pixels. Additionally, the TrueType font data provides
       +//         an explicit scale factor to scale a given font's glyphs to points,
       +//         but the author has observed that this scale factor is often wrong
       +//         for non-commercial fonts, thus making fonts scaled in points
       +//         according to the TrueType spec incoherently sized in practice.
       +//
       +// ADVANCED USAGE
       +//
       +//   Quality:
       +//
       +//    - Use the functions with Subpixel at the end to allow your characters
       +//      to have subpixel positioning. Since the font is anti-aliased, not
       +//      hinted, this is very import for quality. (This is not possible with
       +//      baked fonts.)
       +//
       +//    - Kerning is now supported, and if you're supporting subpixel rendering
       +//      then kerning is worth using to give your text a polished look.
       +//
       +//   Performance:
       +//
       +//    - Convert Unicode codepoints to glyph indexes and operate on the glyphs;
       +//      if you don't do this, stb_truetype is forced to do the conversion on
       +//      every call.
       +//
       +//    - There are a lot of memory allocations. We should modify it to take
       +//      a temp buffer and allocate from the temp buffer (without freeing),
       +//      should help performance a lot.
       +//
       +// NOTES
       +//
       +//   The system uses the raw data found in the .ttf file without changing it
       +//   and without building auxiliary data structures. This is a bit inefficient
       +//   on little-endian systems (the data is big-endian), but assuming you're
       +//   caching the bitmaps or glyph shapes this shouldn't be a big deal.
       +//
       +//   It appears to be very hard to programmatically determine what font a
       +//   given file is in a general way. I provide an API for this, but I don't
       +//   recommend it.
       +//
       +//
       +// SOURCE STATISTICS (based on v0.6c, 2050 LOC)
       +//
       +//   Documentation & header file        520 LOC  \___ 660 LOC documentation
       +//   Sample code                        140 LOC  /
       +//   Truetype parsing                   620 LOC  ---- 620 LOC TrueType
       +//   Software rasterization             240 LOC  \                           .
       +//   Curve tesselation                  120 LOC   \__ 550 LOC Bitmap creation
       +//   Bitmap management                  100 LOC   /
       +//   Baked bitmap interface              70 LOC  /
       +//   Font name matching & access        150 LOC  ---- 150 
       +//   C runtime library abstraction       60 LOC  ----  60
       +//
       +//
       +// PERFORMANCE MEASUREMENTS FOR 1.06:
       +//
       +//                      32-bit     64-bit
       +//   Previous release:  8.83 s     7.68 s
       +//   Pool allocations:  7.72 s     6.34 s
       +//   Inline sort     :  6.54 s     5.65 s
       +//   New rasterizer  :  5.63 s     5.00 s
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//////////////////////////////////////////////////////////////////////////////
       +////
       +////  SAMPLE PROGRAMS
       +////
       +//
       +//  Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
       +//
       +#if 0
       +#define STB_TRUETYPE_IMPLEMENTATION  // force following include to generate implementation
       +#include "stb_truetype.h"
       +
       +unsigned char ttf_buffer[1<<20];
       +unsigned char temp_bitmap[512*512];
       +
       +stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs
       +GLuint ftex;
       +
       +void my_stbtt_initfont(void)
       +{
       +   fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
       +   stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
       +   // can free ttf_buffer at this point
       +   glGenTextures(1, &ftex);
       +   glBindTexture(GL_TEXTURE_2D, ftex);
       +   glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
       +   // can free temp_bitmap at this point
       +   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
       +}
       +
       +void my_stbtt_print(float x, float y, char *text)
       +{
       +   // assume orthographic projection with units = screen pixels, origin at top left
       +   glEnable(GL_TEXTURE_2D);
       +   glBindTexture(GL_TEXTURE_2D, ftex);
       +   glBegin(GL_QUADS);
       +   while (*text) {
       +      if (*text >= 32 && *text < 128) {
       +         stbtt_aligned_quad q;
       +         stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
       +         glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
       +         glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
       +         glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
       +         glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
       +      }
       +      ++text;
       +   }
       +   glEnd();
       +}
       +#endif
       +//
       +//
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// Complete program (this compiles): get a single bitmap, print as ASCII art
       +//
       +#if 0
       +#include <stdio.h>
       +#define STB_TRUETYPE_IMPLEMENTATION  // force following include to generate implementation
       +#include "stb_truetype.h"
       +
       +char ttf_buffer[1<<25];
       +
       +int main(int argc, char **argv)
       +{
       +   stbtt_fontinfo font;
       +   unsigned char *bitmap;
       +   int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);
       +
       +   fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));
       +
       +   stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
       +   bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);
       +
       +   for (j=0; j < h; ++j) {
       +      for (i=0; i < w; ++i)
       +         putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
       +      putchar('\n');
       +   }
       +   return 0;
       +}
       +#endif 
       +//
       +// Output:
       +//
       +//     .ii.
       +//    @@@@@@.
       +//   V@Mio@@o
       +//   :i.  V@V
       +//     :oM@@M
       +//   :@@@MM@M
       +//   @@o  o@M
       +//  :@@.  M@M
       +//   @@@o@@@@
       +//   :M@@V:@@.
       +//  
       +//////////////////////////////////////////////////////////////////////////////
       +// 
       +// Complete program: print "Hello World!" banner, with bugs
       +//
       +#if 0
       +char buffer[24<<20];
       +unsigned char screen[20][79];
       +
       +int main(int arg, char **argv)
       +{
       +   stbtt_fontinfo font;
       +   int i,j,ascent,baseline,ch=0;
       +   float scale, xpos=2; // leave a little padding in case the character extends left
       +   char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness
       +
       +   fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
       +   stbtt_InitFont(&font, buffer, 0);
       +
       +   scale = stbtt_ScaleForPixelHeight(&font, 15);
       +   stbtt_GetFontVMetrics(&font, &ascent,0,0);
       +   baseline = (int) (ascent*scale);
       +
       +   while (text[ch]) {
       +      int advance,lsb,x0,y0,x1,y1;
       +      float x_shift = xpos - (float) floor(xpos);
       +      stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb);
       +      stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1);
       +      stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]);
       +      // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
       +      // because this API is really for baking character bitmaps into textures. if you want to render
       +      // a sequence of characters, you really need to render each bitmap to a temp buffer, then
       +      // "alpha blend" that into the working buffer
       +      xpos += (advance * scale);
       +      if (text[ch+1])
       +         xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]);
       +      ++ch;
       +   }
       +
       +   for (j=0; j < 20; ++j) {
       +      for (i=0; i < 78; ++i)
       +         putchar(" .:ioVM@"[screen[j][i]>>5]);
       +      putchar('\n');
       +   }
       +
       +   return 0;
       +}
       +#endif
       +
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//////////////////////////////////////////////////////////////////////////////
       +////
       +////   INTEGRATION WITH YOUR CODEBASE
       +////
       +////   The following sections allow you to supply alternate definitions
       +////   of C library functions used by stb_truetype.
       +
       +#ifdef STB_TRUETYPE_IMPLEMENTATION
       +   // #define your own (u)stbtt_int8/16/32 before including to override this
       +   #ifndef stbtt_uint8
       +   typedef unsigned char   stbtt_uint8;
       +   typedef signed   char   stbtt_int8;
       +   typedef unsigned short  stbtt_uint16;
       +   typedef signed   short  stbtt_int16;
       +   typedef unsigned int    stbtt_uint32;
       +   typedef signed   int    stbtt_int32;
       +   #endif
       +
       +   typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
       +   typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
       +
       +   // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
       +   #ifndef STBTT_ifloor
       +   #include <math.h>
       +   #define STBTT_ifloor(x)   ((int) floor(x))
       +   #define STBTT_iceil(x)    ((int) ceil(x))
       +   #endif
       +
       +   #ifndef STBTT_sqrt
       +   #include <math.h>
       +   #define STBTT_sqrt(x)      sqrt(x)
       +   #endif
       +
       +   #ifndef STBTT_fabs
       +   #include <math.h>
       +   #define STBTT_fabs(x)      fabs(x)
       +   #endif
       +
       +   // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
       +   #ifndef STBTT_malloc
       +   #include <stdlib.h>
       +   #define STBTT_malloc(x,u)  ((void)(u),malloc(x))
       +   #define STBTT_free(x,u)    ((void)(u),free(x))
       +   #endif
       +
       +   #ifndef STBTT_assert
       +   #include <assert.h>
       +   #define STBTT_assert(x)    assert(x)
       +   #endif
       +
       +   #ifndef STBTT_strlen
       +   #include <string.h>
       +   #define STBTT_strlen(x)    strlen(x)
       +   #endif
       +
       +   #ifndef STBTT_memcpy
       +   #include <memory.h>
       +   #define STBTT_memcpy       memcpy
       +   #define STBTT_memset       memset
       +   #endif
       +#endif
       +
       +///////////////////////////////////////////////////////////////////////////////
       +///////////////////////////////////////////////////////////////////////////////
       +////
       +////   INTERFACE
       +////
       +////
       +
       +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
       +#define __STB_INCLUDE_STB_TRUETYPE_H__
       +
       +#ifdef STBTT_STATIC
       +#define STBTT_DEF static
       +#else
       +#define STBTT_DEF extern
       +#endif
       +
       +#ifdef __cplusplus
       +extern "C" {
       +#endif
       +
       +// private structure
       +typedef struct
       +{
       +   unsigned char *data;
       +   int cursor;
       +   int size;
       +} stbtt__buf;
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// TEXTURE BAKING API
       +//
       +// If you use this API, you only have to call two functions ever.
       +//
       +
       +typedef struct
       +{
       +   unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
       +   float xoff,yoff,xadvance;
       +} stbtt_bakedchar;
       +
       +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,  // font location (use offset=0 for plain .ttf)
       +                                float pixel_height,                     // height of font in pixels
       +                                unsigned char *pixels, int pw, int ph,  // bitmap to be filled in
       +                                int first_char, int num_chars,          // characters to bake
       +                                stbtt_bakedchar *chardata);             // you allocate this, it's num_chars long
       +// if return is positive, the first unused row of the bitmap
       +// if return is negative, returns the negative of the number of characters that fit
       +// if return is 0, no characters fit and no rows were used
       +// This uses a very crappy packing.
       +
       +typedef struct
       +{
       +   float x0,y0,s0,t0; // top-left
       +   float x1,y1,s1,t1; // bottom-right
       +} stbtt_aligned_quad;
       +
       +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph,  // same data as above
       +                               int char_index,             // character to display
       +                               float *xpos, float *ypos,   // pointers to current position in screen pixel space
       +                               stbtt_aligned_quad *q,      // output: quad to draw
       +                               int opengl_fillrule);       // true if opengl fill rule; false if DX9 or earlier
       +// Call GetBakedQuad with char_index = 'character - first_char', and it
       +// creates the quad you need to draw and advances the current position.
       +//
       +// The coordinate system used assumes y increases downwards.
       +//
       +// Characters will extend both above and below the current position;
       +// see discussion of "BASELINE" above.
       +//
       +// It's inefficient; you might want to c&p it and optimize it.
       +
       +
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// NEW TEXTURE BAKING API
       +//
       +// This provides options for packing multiple fonts into one atlas, not
       +// perfectly but better than nothing.
       +
       +typedef struct
       +{
       +   unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
       +   float xoff,yoff,xadvance;
       +   float xoff2,yoff2;
       +} stbtt_packedchar;
       +
       +typedef struct stbtt_pack_context stbtt_pack_context;
       +typedef struct stbtt_fontinfo stbtt_fontinfo;
       +#ifndef STB_RECT_PACK_VERSION
       +typedef struct stbrp_rect stbrp_rect;
       +#endif
       +
       +STBTT_DEF int  stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context);
       +// Initializes a packing context stored in the passed-in stbtt_pack_context.
       +// Future calls using this context will pack characters into the bitmap passed
       +// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is
       +// the distance from one row to the next (or 0 to mean they are packed tightly
       +// together). "padding" is the amount of padding to leave between each
       +// character (normally you want '1' for bitmaps you'll use as textures with
       +// bilinear filtering).
       +//
       +// Returns 0 on failure, 1 on success.
       +
       +STBTT_DEF void stbtt_PackEnd  (stbtt_pack_context *spc);
       +// Cleans up the packing context and frees all memory.
       +
       +#define STBTT_POINT_SIZE(x)   (-(x))
       +
       +STBTT_DEF int  stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
       +                                int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range);
       +// Creates character bitmaps from the font_index'th font found in fontdata (use
       +// font_index=0 if you don't know what that is). It creates num_chars_in_range
       +// bitmaps for characters with unicode values starting at first_unicode_char_in_range
       +// and increasing. Data for how to render them is stored in chardata_for_range;
       +// pass these to stbtt_GetPackedQuad to get back renderable quads.
       +//
       +// font_size is the full height of the character from ascender to descender,
       +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed
       +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE()
       +// and pass that result as 'font_size':
       +//       ...,                  20 , ... // font max minus min y is 20 pixels tall
       +//       ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall
       +
       +typedef struct
       +{
       +   float font_size;
       +   int first_unicode_codepoint_in_range;  // if non-zero, then the chars are continuous, and this is the first codepoint
       +   int *array_of_unicode_codepoints;       // if non-zero, then this is an array of unicode codepoints
       +   int num_chars;
       +   stbtt_packedchar *chardata_for_range; // output
       +   unsigned char h_oversample, v_oversample; // don't set these, they're used internally
       +} stbtt_pack_range;
       +
       +STBTT_DEF int  stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges);
       +// Creates character bitmaps from multiple ranges of characters stored in
       +// ranges. This will usually create a better-packed bitmap than multiple
       +// calls to stbtt_PackFontRange. Note that you can call this multiple
       +// times within a single PackBegin/PackEnd.
       +
       +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample);
       +// Oversampling a font increases the quality by allowing higher-quality subpixel
       +// positioning, and is especially valuable at smaller text sizes.
       +//
       +// This function sets the amount of oversampling for all following calls to
       +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given
       +// pack context. The default (no oversampling) is achieved by h_oversample=1
       +// and v_oversample=1. The total number of pixels required is
       +// h_oversample*v_oversample larger than the default; for example, 2x2
       +// oversampling requires 4x the storage of 1x1. For best results, render
       +// oversampled textures with bilinear filtering. Look at the readme in
       +// stb/tests/oversample for information about oversampled fonts
       +//
       +// To use with PackFontRangesGather etc., you must set it before calls
       +// call to PackFontRangesGatherRects.
       +
       +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph,  // same data as above
       +                               int char_index,             // character to display
       +                               float *xpos, float *ypos,   // pointers to current position in screen pixel space
       +                               stbtt_aligned_quad *q,      // output: quad to draw
       +                               int align_to_integer);
       +
       +STBTT_DEF int  stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
       +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects);
       +STBTT_DEF int  stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects);
       +// Calling these functions in sequence is roughly equivalent to calling
       +// stbtt_PackFontRanges(). If you more control over the packing of multiple
       +// fonts, or if you want to pack custom data into a font texture, take a look
       +// at the source to of stbtt_PackFontRanges() and create a custom version 
       +// using these functions, e.g. call GatherRects multiple times,
       +// building up a single array of rects, then call PackRects once,
       +// then call RenderIntoRects repeatedly. This may result in a
       +// better packing than calling PackFontRanges multiple times
       +// (or it may not).
       +
       +// this is an opaque structure that you shouldn't mess with which holds
       +// all the context needed from PackBegin to PackEnd.
       +struct stbtt_pack_context {
       +   void *user_allocator_context;
       +   void *pack_info;
       +   int   width;
       +   int   height;
       +   int   stride_in_bytes;
       +   int   padding;
       +   unsigned int   h_oversample, v_oversample;
       +   unsigned char *pixels;
       +   void  *nodes;
       +};
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// FONT LOADING
       +//
       +//
       +
       +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data);
       +// This function will determine the number of fonts in a font file.  TrueType
       +// collection (.ttc) files may contain multiple fonts, while TrueType font
       +// (.ttf) files only contain one font. The number of fonts can be used for
       +// indexing with the previous function where the index is between zero and one
       +// less than the total fonts. If an error occurs, -1 is returned.
       +
       +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
       +// Each .ttf/.ttc file may have more than one font. Each font has a sequential
       +// index number starting from 0. Call this function to get the font offset for
       +// a given index; it returns -1 if the index is out of range. A regular .ttf
       +// file will only define one font and it always be at offset 0, so it will
       +// return '0' for index 0, and -1 for all other indices.
       +
       +// The following structure is defined publically so you can declare one on
       +// the stack or as a global or etc, but you should treat it as opaque.
       +struct stbtt_fontinfo
       +{
       +   void           * userdata;
       +   unsigned char  * data;              // pointer to .ttf file
       +   int              fontstart;         // offset of start of font
       +
       +   int numGlyphs;                     // number of glyphs, needed for range checking
       +
       +   int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf
       +   int index_map;                     // a cmap mapping for our chosen character encoding
       +   int indexToLocFormat;              // format needed to map from glyph index to glyph
       +
       +   stbtt__buf cff;                    // cff font data
       +   stbtt__buf charstrings;            // the charstring index
       +   stbtt__buf gsubrs;                 // global charstring subroutines index
       +   stbtt__buf subrs;                  // private charstring subroutines index
       +   stbtt__buf fontdicts;              // array of font dicts
       +   stbtt__buf fdselect;               // map from glyph to fontdict
       +};
       +
       +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
       +// Given an offset into the file that defines a font, this function builds
       +// the necessary cached info for the rest of the system. You must allocate
       +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
       +// need to do anything special to free it, because the contents are pure
       +// value data with no additional data structures. Returns 0 on failure.
       +
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// CHARACTER TO GLYPH-INDEX CONVERSIOn
       +
       +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
       +// If you're going to perform multiple operations on the same character
       +// and you want a speed-up, call this function with the character you're
       +// going to process, then use glyph-based functions instead of the
       +// codepoint-based functions.
       +
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// CHARACTER PROPERTIES
       +//
       +
       +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
       +// computes a scale factor to produce a font whose "height" is 'pixels' tall.
       +// Height is measured as the distance from the highest ascender to the lowest
       +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
       +// and computing:
       +//       scale = pixels / (ascent - descent)
       +// so if you prefer to measure height by the ascent only, use a similar calculation.
       +
       +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels);
       +// computes a scale factor to produce a font whose EM size is mapped to
       +// 'pixels' tall. This is probably what traditional APIs compute, but
       +// I'm not positive.
       +
       +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
       +// ascent is the coordinate above the baseline the font extends; descent
       +// is the coordinate below the baseline the font extends (i.e. it is typically negative)
       +// lineGap is the spacing between one row's descent and the next row's ascent...
       +// so you should advance the vertical position by "*ascent - *descent + *lineGap"
       +//   these are expressed in unscaled coordinates, so you must multiply by
       +//   the scale factor for a given size
       +
       +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1);
       +// the bounding box around all possible characters
       +
       +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
       +// leftSideBearing is the offset from the current horizontal position to the left edge of the character
       +// advanceWidth is the offset from the current horizontal position to the next horizontal position
       +//   these are expressed in unscaled coordinates
       +
       +STBTT_DEF int  stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
       +// an additional amount to add to the 'advance' value between ch1 and ch2
       +
       +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
       +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
       +
       +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
       +STBTT_DEF int  stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
       +STBTT_DEF int  stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
       +// as above, but takes one or more glyph indices for greater efficiency
       +
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// GLYPH SHAPES (you probably don't need these, but they have to go before
       +// the bitmaps for C declaration-order reasons)
       +//
       +
       +#ifndef STBTT_vmove // you can predefine these to use different values (but why?)
       +   enum {
       +      STBTT_vmove=1,
       +      STBTT_vline,
       +      STBTT_vcurve,
       +      STBTT_vcubic
       +   };
       +#endif
       +
       +#ifndef stbtt_vertex // you can predefine this to use different values
       +                   // (we share this with other code at RAD)
       +   #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
       +   typedef struct
       +   {
       +      stbtt_vertex_type x,y,cx,cy,cx1,cy1;
       +      unsigned char type,padding;
       +   } stbtt_vertex;
       +#endif
       +
       +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index);
       +// returns non-zero if nothing is drawn for this glyph
       +
       +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
       +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
       +// returns # of vertices and fills *vertices with the pointer to them
       +//   these are expressed in "unscaled" coordinates
       +//
       +// The shape is a series of countours. Each one starts with
       +// a STBTT_moveto, then consists of a series of mixed
       +// STBTT_lineto and STBTT_curveto segments. A lineto
       +// draws a line from previous endpoint to its x,y; a curveto
       +// draws a quadratic bezier from previous endpoint to
       +// its x,y, using cx,cy as the bezier control point.
       +
       +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
       +// frees the data allocated above
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// BITMAP RENDERING
       +//
       +
       +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
       +// frees the bitmap allocated below
       +
       +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
       +// allocates a large-enough single-channel 8bpp bitmap and renders the
       +// specified character/glyph at the specified scale into it, with
       +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
       +// *width & *height are filled out with the width & height of the bitmap,
       +// which is stored left-to-right, top-to-bottom.
       +//
       +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
       +
       +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
       +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
       +// shift for the character
       +
       +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
       +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap
       +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
       +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the
       +// width and height and positioning info for it first.
       +
       +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint);
       +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
       +// shift for the character
       +
       +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
       +// get the bbox of the bitmap centered around the glyph origin; so the
       +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
       +// the bitmap top left is (leftSideBearing*scale,iy0).
       +// (Note that the bitmap uses y-increases-down, but the shape uses
       +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
       +
       +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
       +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel
       +// shift for the character
       +
       +// the following functions are equivalent to the above functions, but operate
       +// on glyph indices instead of Unicode codepoints (for efficiency)
       +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
       +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff);
       +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
       +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph);
       +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
       +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1);
       +
       +
       +// @TODO: don't expose this structure
       +typedef struct
       +{
       +   int w,h,stride;
       +   unsigned char *pixels;
       +} stbtt__bitmap;
       +
       +// rasterize a shape with quadratic beziers into a bitmap
       +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result,        // 1-channel bitmap to draw into
       +                               float flatness_in_pixels,     // allowable error of curve in pixels
       +                               stbtt_vertex *vertices,       // array of vertices defining shape
       +                               int num_verts,                // number of vertices in above array
       +                               float scale_x, float scale_y, // scale applied to input vertices
       +                               float shift_x, float shift_y, // translation applied to input vertices
       +                               int x_off, int y_off,         // another translation applied to input
       +                               int invert,                   // if non-zero, vertically flip shape
       +                               void *userdata);              // context for to STBTT_MALLOC
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// Finding the right font...
       +//
       +// You should really just solve this offline, keep your own tables
       +// of what font is what, and don't try to get it out of the .ttf file.
       +// That's because getting it out of the .ttf file is really hard, because
       +// the names in the file can appear in many possible encodings, in many
       +// possible languages, and e.g. if you need a case-insensitive comparison,
       +// the details of that depend on the encoding & language in a complex way
       +// (actually underspecified in truetype, but also gigantic).
       +//
       +// But you can use the provided functions in two possible ways:
       +//     stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
       +//             unicode-encoded names to try to find the font you want;
       +//             you can run this before calling stbtt_InitFont()
       +//
       +//     stbtt_GetFontNameString() lets you get any of the various strings
       +//             from the file yourself and do your own comparisons on them.
       +//             You have to have called stbtt_InitFont() first.
       +
       +
       +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
       +// returns the offset (not index) of the font that matches, or -1 if none
       +//   if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
       +//   if you use any other flag, use a font name like "Arial"; this checks
       +//     the 'macStyle' header field; i don't know if fonts set this consistently
       +#define STBTT_MACSTYLE_DONTCARE     0
       +#define STBTT_MACSTYLE_BOLD         1
       +#define STBTT_MACSTYLE_ITALIC       2
       +#define STBTT_MACSTYLE_UNDERSCORE   4
       +#define STBTT_MACSTYLE_NONE         8   // <= not same as 0, this makes us check the bitfield is 0
       +
       +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
       +// returns 1/0 whether the first string interpreted as utf8 is identical to
       +// the second string interpreted as big-endian utf16... useful for strings from next func
       +
       +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
       +// returns the string (which may be big-endian double byte, e.g. for unicode)
       +// and puts the length in bytes in *length.
       +//
       +// some of the values for the IDs are below; for more see the truetype spec:
       +//     http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
       +//     http://www.microsoft.com/typography/otspec/name.htm
       +
       +enum { // platformID
       +   STBTT_PLATFORM_ID_UNICODE   =0,
       +   STBTT_PLATFORM_ID_MAC       =1,
       +   STBTT_PLATFORM_ID_ISO       =2,
       +   STBTT_PLATFORM_ID_MICROSOFT =3
       +};
       +
       +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE
       +   STBTT_UNICODE_EID_UNICODE_1_0    =0,
       +   STBTT_UNICODE_EID_UNICODE_1_1    =1,
       +   STBTT_UNICODE_EID_ISO_10646      =2,
       +   STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,
       +   STBTT_UNICODE_EID_UNICODE_2_0_FULL=4
       +};
       +
       +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT
       +   STBTT_MS_EID_SYMBOL        =0,
       +   STBTT_MS_EID_UNICODE_BMP   =1,
       +   STBTT_MS_EID_SHIFTJIS      =2,
       +   STBTT_MS_EID_UNICODE_FULL  =10
       +};
       +
       +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes
       +   STBTT_MAC_EID_ROMAN        =0,   STBTT_MAC_EID_ARABIC       =4,
       +   STBTT_MAC_EID_JAPANESE     =1,   STBTT_MAC_EID_HEBREW       =5,
       +   STBTT_MAC_EID_CHINESE_TRAD =2,   STBTT_MAC_EID_GREEK        =6,
       +   STBTT_MAC_EID_KOREAN       =3,   STBTT_MAC_EID_RUSSIAN      =7
       +};
       +
       +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...
       +       // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
       +   STBTT_MS_LANG_ENGLISH     =0x0409,   STBTT_MS_LANG_ITALIAN     =0x0410,
       +   STBTT_MS_LANG_CHINESE     =0x0804,   STBTT_MS_LANG_JAPANESE    =0x0411,
       +   STBTT_MS_LANG_DUTCH       =0x0413,   STBTT_MS_LANG_KOREAN      =0x0412,
       +   STBTT_MS_LANG_FRENCH      =0x040c,   STBTT_MS_LANG_RUSSIAN     =0x0419,
       +   STBTT_MS_LANG_GERMAN      =0x0407,   STBTT_MS_LANG_SPANISH     =0x0409,
       +   STBTT_MS_LANG_HEBREW      =0x040d,   STBTT_MS_LANG_SWEDISH     =0x041D
       +};
       +
       +enum { // languageID for STBTT_PLATFORM_ID_MAC
       +   STBTT_MAC_LANG_ENGLISH      =0 ,   STBTT_MAC_LANG_JAPANESE     =11,
       +   STBTT_MAC_LANG_ARABIC       =12,   STBTT_MAC_LANG_KOREAN       =23,
       +   STBTT_MAC_LANG_DUTCH        =4 ,   STBTT_MAC_LANG_RUSSIAN      =32,
       +   STBTT_MAC_LANG_FRENCH       =1 ,   STBTT_MAC_LANG_SPANISH      =6 ,
       +   STBTT_MAC_LANG_GERMAN       =2 ,   STBTT_MAC_LANG_SWEDISH      =5 ,
       +   STBTT_MAC_LANG_HEBREW       =10,   STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,
       +   STBTT_MAC_LANG_ITALIAN      =3 ,   STBTT_MAC_LANG_CHINESE_TRAD =19
       +};
       +
       +#ifdef __cplusplus
       +}
       +#endif
       +
       +#endif // __STB_INCLUDE_STB_TRUETYPE_H__
       +
       +///////////////////////////////////////////////////////////////////////////////
       +///////////////////////////////////////////////////////////////////////////////
       +////
       +////   IMPLEMENTATION
       +////
       +////
       +
       +#ifdef STB_TRUETYPE_IMPLEMENTATION
       +
       +#ifndef STBTT_MAX_OVERSAMPLE
       +#define STBTT_MAX_OVERSAMPLE   8
       +#endif
       +
       +#if STBTT_MAX_OVERSAMPLE > 255
       +#error "STBTT_MAX_OVERSAMPLE cannot be > 255"
       +#endif
       +
       +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1];
       +
       +#ifndef STBTT_RASTERIZER_VERSION
       +#define STBTT_RASTERIZER_VERSION 2
       +#endif
       +
       +#ifdef _MSC_VER
       +#define STBTT__NOTUSED(v)  (void)(v)
       +#else
       +#define STBTT__NOTUSED(v)  (void)sizeof(v)
       +#endif
       +
       +//////////////////////////////////////////////////////////////////////////
       +//
       +// stbtt__buf helpers to parse data from file
       +//
       +
       +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b)
       +{
       +   if (b->cursor >= b->size)
       +      return 0;
       +   return b->data[b->cursor++];
       +}
       +
       +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b)
       +{
       +   if (b->cursor >= b->size)
       +      return 0;
       +   return b->data[b->cursor];
       +}
       +
       +static void stbtt__buf_seek(stbtt__buf *b, int o)
       +{
       +   STBTT_assert(!(o > b->size || o < 0));
       +   b->cursor = (o > b->size || o < 0) ? b->size : o;
       +}
       +
       +static void stbtt__buf_skip(stbtt__buf *b, int o)
       +{
       +   stbtt__buf_seek(b, b->cursor + o);
       +}
       +
       +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n)
       +{
       +   stbtt_uint32 v = 0;
       +   int i;
       +   STBTT_assert(n >= 1 && n <= 4);
       +   for (i = 0; i < n; i++)
       +      v = (v << 8) | stbtt__buf_get8(b);
       +   return v;
       +}
       +
       +static stbtt__buf stbtt__new_buf(const void *p, size_t size)
       +{
       +   stbtt__buf r;
       +   STBTT_assert(size < 0x40000000);
       +   r.data = (stbtt_uint8*) p;
       +   r.size = (int) size;
       +   r.cursor = 0;
       +   return r;
       +}
       +
       +#define stbtt__buf_get16(b)  stbtt__buf_get((b), 2)
       +#define stbtt__buf_get32(b)  stbtt__buf_get((b), 4)
       +
       +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s)
       +{
       +   stbtt__buf r = stbtt__new_buf(NULL, 0);
       +   if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r;
       +   r.data = b->data + o;
       +   r.size = s;
       +   return r;
       +}
       +
       +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b)
       +{
       +   int count, start, offsize;
       +   start = b->cursor;
       +   count = stbtt__buf_get16(b);
       +   if (count) {
       +      offsize = stbtt__buf_get8(b);
       +      STBTT_assert(offsize >= 1 && offsize <= 4);
       +      stbtt__buf_skip(b, offsize * count);
       +      stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1);
       +   }
       +   return stbtt__buf_range(b, start, b->cursor - start);
       +}
       +
       +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b)
       +{
       +   int b0 = stbtt__buf_get8(b);
       +   if (b0 >= 32 && b0 <= 246)       return b0 - 139;
       +   else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108;
       +   else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108;
       +   else if (b0 == 28)               return stbtt__buf_get16(b);
       +   else if (b0 == 29)               return stbtt__buf_get32(b);
       +   STBTT_assert(0);
       +   return 0;
       +}
       +
       +static void stbtt__cff_skip_operand(stbtt__buf *b) {
       +   int v, b0 = stbtt__buf_peek8(b);
       +   STBTT_assert(b0 >= 28);
       +   if (b0 == 30) {
       +      stbtt__buf_skip(b, 1);
       +      while (b->cursor < b->size) {
       +         v = stbtt__buf_get8(b);
       +         if ((v & 0xF) == 0xF || (v >> 4) == 0xF)
       +            break;
       +      }
       +   } else {
       +      stbtt__cff_int(b);
       +   }
       +}
       +
       +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key)
       +{
       +   stbtt__buf_seek(b, 0);
       +   while (b->cursor < b->size) {
       +      int start = b->cursor, end, op;
       +      while (stbtt__buf_peek8(b) >= 28)
       +         stbtt__cff_skip_operand(b);
       +      end = b->cursor;
       +      op = stbtt__buf_get8(b);
       +      if (op == 12)  op = stbtt__buf_get8(b) | 0x100;
       +      if (op == key) return stbtt__buf_range(b, start, end-start);
       +   }
       +   return stbtt__buf_range(b, 0, 0);
       +}
       +
       +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out)
       +{
       +   int i;
       +   stbtt__buf operands = stbtt__dict_get(b, key);
       +   for (i = 0; i < outcount && operands.cursor < operands.size; i++)
       +      out[i] = stbtt__cff_int(&operands);
       +}
       +
       +static int stbtt__cff_index_count(stbtt__buf *b)
       +{
       +   stbtt__buf_seek(b, 0);
       +   return stbtt__buf_get16(b);
       +}
       +
       +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i)
       +{
       +   int count, offsize, start, end;
       +   stbtt__buf_seek(&b, 0);
       +   count = stbtt__buf_get16(&b);
       +   offsize = stbtt__buf_get8(&b);
       +   STBTT_assert(i >= 0 && i < count);
       +   STBTT_assert(offsize >= 1 && offsize <= 4);
       +   stbtt__buf_skip(&b, i*offsize);
       +   start = stbtt__buf_get(&b, offsize);
       +   end = stbtt__buf_get(&b, offsize);
       +   return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start);
       +}
       +
       +//////////////////////////////////////////////////////////////////////////
       +//
       +// accessors to parse data from file
       +//
       +
       +// on platforms that don't allow misaligned reads, if we want to allow
       +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE
       +
       +#define ttBYTE(p)     (* (stbtt_uint8 *) (p))
       +#define ttCHAR(p)     (* (stbtt_int8 *) (p))
       +#define ttFixed(p)    ttLONG(p)
       +
       +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; }
       +static stbtt_int16 ttSHORT(stbtt_uint8 *p)   { return p[0]*256 + p[1]; }
       +static stbtt_uint32 ttULONG(stbtt_uint8 *p)  { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
       +static stbtt_int32 ttLONG(stbtt_uint8 *p)    { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
       +
       +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
       +#define stbtt_tag(p,str)           stbtt_tag4(p,str[0],str[1],str[2],str[3])
       +
       +static int stbtt__isfont(stbtt_uint8 *font)
       +{
       +   // check the version number
       +   if (stbtt_tag4(font, '1',0,0,0))  return 1; // TrueType 1
       +   if (stbtt_tag(font, "typ1"))   return 1; // TrueType with type 1 font -- we don't support this!
       +   if (stbtt_tag(font, "OTTO"))   return 1; // OpenType with CFF
       +   if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
       +   if (stbtt_tag(font, "true"))   return 1; // Apple specification for TrueType fonts
       +   return 0;
       +}
       +
       +// @OPTIMIZE: binary search
       +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)
       +{
       +   stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);
       +   stbtt_uint32 tabledir = fontstart + 12;
       +   stbtt_int32 i;
       +   for (i=0; i < num_tables; ++i) {
       +      stbtt_uint32 loc = tabledir + 16*i;
       +      if (stbtt_tag(data+loc+0, tag))
       +         return ttULONG(data+loc+8);
       +   }
       +   return 0;
       +}
       +
       +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index)
       +{
       +   // if it's just a font, there's only one valid index
       +   if (stbtt__isfont(font_collection))
       +      return index == 0 ? 0 : -1;
       +
       +   // check if it's a TTC
       +   if (stbtt_tag(font_collection, "ttcf")) {
       +      // version 1?
       +      if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
       +         stbtt_int32 n = ttLONG(font_collection+8);
       +         if (index >= n)
       +            return -1;
       +         return ttULONG(font_collection+12+index*4);
       +      }
       +   }
       +   return -1;
       +}
       +
       +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection)
       +{
       +   // if it's just a font, there's only one valid font
       +   if (stbtt__isfont(font_collection))
       +      return 1;
       +
       +   // check if it's a TTC
       +   if (stbtt_tag(font_collection, "ttcf")) {
       +      // version 1?
       +      if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
       +         return ttLONG(font_collection+8);
       +      }
       +   }
       +   return 0;
       +}
       +
       +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict)
       +{
       +   stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 };
       +   stbtt__buf pdict;
       +   stbtt__dict_get_ints(&fontdict, 18, 2, private_loc);
       +   if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0);
       +   pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]);
       +   stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff);
       +   if (!subrsoff) return stbtt__new_buf(NULL, 0);
       +   stbtt__buf_seek(&cff, private_loc[1]+subrsoff);
       +   return stbtt__cff_get_index(&cff);
       +}
       +
       +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart)
       +{
       +   stbtt_uint32 cmap, t;
       +   stbtt_int32 i,numTables;
       +
       +   info->data = data;
       +   info->fontstart = fontstart;
       +   info->cff = stbtt__new_buf(NULL, 0);
       +
       +   cmap = stbtt__find_table(data, fontstart, "cmap");       // required
       +   info->loca = stbtt__find_table(data, fontstart, "loca"); // required
       +   info->head = stbtt__find_table(data, fontstart, "head"); // required
       +   info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required
       +   info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required
       +   info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required
       +   info->kern = stbtt__find_table(data, fontstart, "kern"); // not required
       +
       +   if (!cmap || !info->head || !info->hhea || !info->hmtx)
       +      return 0;
       +   if (info->glyf) {
       +      // required for truetype
       +      if (!info->loca) return 0;
       +   } else {
       +      // initialization for CFF / Type2 fonts (OTF)
       +      stbtt__buf b, topdict, topdictidx;
       +      stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0;
       +      stbtt_uint32 cff;
       +
       +      cff = stbtt__find_table(data, fontstart, "CFF ");
       +      if (!cff) return 0;
       +
       +      info->fontdicts = stbtt__new_buf(NULL, 0);
       +      info->fdselect = stbtt__new_buf(NULL, 0);
       +
       +      // @TODO this should use size from table (not 512MB)
       +      info->cff = stbtt__new_buf(data+cff, 512*1024*1024);
       +      b = info->cff;
       +
       +      // read the header
       +      stbtt__buf_skip(&b, 2);
       +      stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize
       +
       +      // @TODO the name INDEX could list multiple fonts,
       +      // but we just use the first one.
       +      stbtt__cff_get_index(&b);  // name INDEX
       +      topdictidx = stbtt__cff_get_index(&b);
       +      topdict = stbtt__cff_index_get(topdictidx, 0);
       +      stbtt__cff_get_index(&b);  // string INDEX
       +      info->gsubrs = stbtt__cff_get_index(&b);
       +
       +      stbtt__dict_get_ints(&topdict, 17, 1, &charstrings);
       +      stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype);
       +      stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff);
       +      stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff);
       +      info->subrs = stbtt__get_subrs(b, topdict);
       +
       +      // we only support Type 2 charstrings
       +      if (cstype != 2) return 0;
       +      if (charstrings == 0) return 0;
       +
       +      if (fdarrayoff) {
       +         // looks like a CID font
       +         if (!fdselectoff) return 0;
       +         stbtt__buf_seek(&b, fdarrayoff);
       +         info->fontdicts = stbtt__cff_get_index(&b);
       +         info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff);
       +      }
       +
       +      stbtt__buf_seek(&b, charstrings);
       +      info->charstrings = stbtt__cff_get_index(&b);
       +   }
       +
       +   t = stbtt__find_table(data, fontstart, "maxp");
       +   if (t)
       +      info->numGlyphs = ttUSHORT(data+t+4);
       +   else
       +      info->numGlyphs = 0xffff;
       +
       +   // find a cmap encoding table we understand *now* to avoid searching
       +   // later. (todo: could make this installable)
       +   // the same regardless of glyph.
       +   numTables = ttUSHORT(data + cmap + 2);
       +   info->index_map = 0;
       +   for (i=0; i < numTables; ++i) {
       +      stbtt_uint32 encoding_record = cmap + 4 + 8 * i;
       +      // find an encoding we understand:
       +      switch(ttUSHORT(data+encoding_record)) {
       +         case STBTT_PLATFORM_ID_MICROSOFT:
       +            switch (ttUSHORT(data+encoding_record+2)) {
       +               case STBTT_MS_EID_UNICODE_BMP:
       +               case STBTT_MS_EID_UNICODE_FULL:
       +                  // MS/Unicode
       +                  info->index_map = cmap + ttULONG(data+encoding_record+4);
       +                  break;
       +            }
       +            break;
       +        case STBTT_PLATFORM_ID_UNICODE:
       +            // Mac/iOS has these
       +            // all the encodingIDs are unicode, so we don't bother to check it
       +            info->index_map = cmap + ttULONG(data+encoding_record+4);
       +            break;
       +      }
       +   }
       +   if (info->index_map == 0)
       +      return 0;
       +
       +   info->indexToLocFormat = ttUSHORT(data+info->head + 50);
       +   return 1;
       +}
       +
       +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
       +{
       +   stbtt_uint8 *data = info->data;
       +   stbtt_uint32 index_map = info->index_map;
       +
       +   stbtt_uint16 format = ttUSHORT(data + index_map + 0);
       +   if (format == 0) { // apple byte encoding
       +      stbtt_int32 bytes = ttUSHORT(data + index_map + 2);
       +      if (unicode_codepoint < bytes-6)
       +         return ttBYTE(data + index_map + 6 + unicode_codepoint);
       +      return 0;
       +   } else if (format == 6) {
       +      stbtt_uint32 first = ttUSHORT(data + index_map + 6);
       +      stbtt_uint32 count = ttUSHORT(data + index_map + 8);
       +      if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)
       +         return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);
       +      return 0;
       +   } else if (format == 2) {
       +      STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean
       +      return 0;
       +   } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges
       +      stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;
       +      stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
       +      stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
       +      stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
       +
       +      // do a binary search of the segments
       +      stbtt_uint32 endCount = index_map + 14;
       +      stbtt_uint32 search = endCount;
       +
       +      if (unicode_codepoint > 0xffff)
       +         return 0;
       +
       +      // they lie from endCount .. endCount + segCount
       +      // but searchRange is the nearest power of two, so...
       +      if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))
       +         search += rangeShift*2;
       +
       +      // now decrement to bias correctly to find smallest
       +      search -= 2;
       +      while (entrySelector) {
       +         stbtt_uint16 end;
       +         searchRange >>= 1;
       +         end = ttUSHORT(data + search + searchRange*2);
       +         if (unicode_codepoint > end)
       +            search += searchRange*2;
       +         --entrySelector;
       +      }
       +      search += 2;
       +
       +      {
       +         stbtt_uint16 offset, start;
       +         stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1);
       +
       +         STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
       +         start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
       +         if (unicode_codepoint < start)
       +            return 0;
       +
       +         offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
       +         if (offset == 0)
       +            return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item));
       +
       +         return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
       +      }
       +   } else if (format == 12 || format == 13) {
       +      stbtt_uint32 ngroups = ttULONG(data+index_map+12);
       +      stbtt_int32 low,high;
       +      low = 0; high = (stbtt_int32)ngroups;
       +      // Binary search the right group.
       +      while (low < high) {
       +         stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high
       +         stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);
       +         stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);
       +         if ((stbtt_uint32) unicode_codepoint < start_char)
       +            high = mid;
       +         else if ((stbtt_uint32) unicode_codepoint > end_char)
       +            low = mid+1;
       +         else {
       +            stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
       +            if (format == 12)
       +               return start_glyph + unicode_codepoint-start_char;
       +            else // format == 13
       +               return start_glyph;
       +         }
       +      }
       +      return 0; // not found
       +   }
       +   // @TODO
       +   STBTT_assert(0);
       +   return 0;
       +}
       +
       +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
       +{
       +   return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
       +}
       +
       +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy)
       +{
       +   v->type = type;
       +   v->x = (stbtt_int16) x;
       +   v->y = (stbtt_int16) y;
       +   v->cx = (stbtt_int16) cx;
       +   v->cy = (stbtt_int16) cy;
       +}
       +
       +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
       +{
       +   int g1,g2;
       +
       +   STBTT_assert(!info->cff.size);
       +
       +   if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
       +   if (info->indexToLocFormat >= 2)    return -1; // unknown index->glyph map format
       +
       +   if (info->indexToLocFormat == 0) {
       +      g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
       +      g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
       +   } else {
       +      g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);
       +      g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);
       +   }
       +
       +   return g1==g2 ? -1 : g1; // if length is 0, return -1
       +}
       +
       +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
       +
       +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
       +{
       +   if (info->cff.size) {
       +      stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1);
       +   } else {
       +      int g = stbtt__GetGlyfOffset(info, glyph_index);
       +      if (g < 0) return 0;
       +
       +      if (x0) *x0 = ttSHORT(info->data + g + 2);
       +      if (y0) *y0 = ttSHORT(info->data + g + 4);
       +      if (x1) *x1 = ttSHORT(info->data + g + 6);
       +      if (y1) *y1 = ttSHORT(info->data + g + 8);
       +   }
       +   return 1;
       +}
       +
       +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
       +{
       +   return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
       +}
       +
       +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index)
       +{
       +   stbtt_int16 numberOfContours;
       +   int g;
       +   if (info->cff.size)
       +      return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0;
       +   g = stbtt__GetGlyfOffset(info, glyph_index);
       +   if (g < 0) return 1;
       +   numberOfContours = ttSHORT(info->data + g);
       +   return numberOfContours == 0;
       +}
       +
       +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off,
       +    stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy)
       +{
       +   if (start_off) {
       +      if (was_off)
       +         stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy);
       +      stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy);
       +   } else {
       +      if (was_off)
       +         stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
       +      else
       +         stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
       +   }
       +   return num_vertices;
       +}
       +
       +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
       +{
       +   stbtt_int16 numberOfContours;
       +   stbtt_uint8 *endPtsOfContours;
       +   stbtt_uint8 *data = info->data;
       +   stbtt_vertex *vertices=0;
       +   int num_vertices=0;
       +   int g = stbtt__GetGlyfOffset(info, glyph_index);
       +
       +   *pvertices = NULL;
       +
       +   if (g < 0) return 0;
       +
       +   numberOfContours = ttSHORT(data + g);
       +
       +   if (numberOfContours > 0) {
       +      stbtt_uint8 flags=0,flagcount;
       +      stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0;
       +      stbtt_int32 x,y,cx,cy,sx,sy, scx,scy;
       +      stbtt_uint8 *points;
       +      endPtsOfContours = (data + g + 10);
       +      ins = ttUSHORT(data + g + 10 + numberOfContours * 2);
       +      points = data + g + 10 + numberOfContours * 2 + 2 + ins;
       +
       +      n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);
       +
       +      m = n + 2*numberOfContours;  // a loose bound on how many vertices we might need
       +      vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
       +      if (vertices == 0)
       +         return 0;
       +
       +      next_move = 0;
       +      flagcount=0;
       +
       +      // in first pass, we load uninterpreted data into the allocated array
       +      // above, shifted to the end of the array so we won't overwrite it when
       +      // we create our final data starting from the front
       +
       +      off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated
       +
       +      // first load flags
       +
       +      for (i=0; i < n; ++i) {
       +         if (flagcount == 0) {
       +            flags = *points++;
       +            if (flags & 8)
       +               flagcount = *points++;
       +         } else
       +            --flagcount;
       +         vertices[off+i].type = flags;
       +      }
       +
       +      // now load x coordinates
       +      x=0;
       +      for (i=0; i < n; ++i) {
       +         flags = vertices[off+i].type;
       +         if (flags & 2) {
       +            stbtt_int16 dx = *points++;
       +            x += (flags & 16) ? dx : -dx; // ???
       +         } else {
       +            if (!(flags & 16)) {
       +               x = x + (stbtt_int16) (points[0]*256 + points[1]);
       +               points += 2;
       +            }
       +         }
       +         vertices[off+i].x = (stbtt_int16) x;
       +      }
       +
       +      // now load y coordinates
       +      y=0;
       +      for (i=0; i < n; ++i) {
       +         flags = vertices[off+i].type;
       +         if (flags & 4) {
       +            stbtt_int16 dy = *points++;
       +            y += (flags & 32) ? dy : -dy; // ???
       +         } else {
       +            if (!(flags & 32)) {
       +               y = y + (stbtt_int16) (points[0]*256 + points[1]);
       +               points += 2;
       +            }
       +         }
       +         vertices[off+i].y = (stbtt_int16) y;
       +      }
       +
       +      // now convert them to our format
       +      num_vertices=0;
       +      sx = sy = cx = cy = scx = scy = 0;
       +      for (i=0; i < n; ++i) {
       +         flags = vertices[off+i].type;
       +         x     = (stbtt_int16) vertices[off+i].x;
       +         y     = (stbtt_int16) vertices[off+i].y;
       +
       +         if (next_move == i) {
       +            if (i != 0)
       +               num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
       +
       +            // now start the new one               
       +            start_off = !(flags & 1);
       +            if (start_off) {
       +               // if we start off with an off-curve point, then when we need to find a point on the curve
       +               // where we can start, and we need to save some state for when we wraparound.
       +               scx = x;
       +               scy = y;
       +               if (!(vertices[off+i+1].type & 1)) {
       +                  // next point is also a curve point, so interpolate an on-point curve
       +                  sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1;
       +                  sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1;
       +               } else {
       +                  // otherwise just use the next point as our start point
       +                  sx = (stbtt_int32) vertices[off+i+1].x;
       +                  sy = (stbtt_int32) vertices[off+i+1].y;
       +                  ++i; // we're using point i+1 as the starting point, so skip it
       +               }
       +            } else {
       +               sx = x;
       +               sy = y;
       +            }
       +            stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0);
       +            was_off = 0;
       +            next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
       +            ++j;
       +         } else {
       +            if (!(flags & 1)) { // if it's a curve
       +               if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint
       +                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
       +               cx = x;
       +               cy = y;
       +               was_off = 1;
       +            } else {
       +               if (was_off)
       +                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);
       +               else
       +                  stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);
       +               was_off = 0;
       +            }
       +         }
       +      }
       +      num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy);
       +   } else if (numberOfContours == -1) {
       +      // Compound shapes.
       +      int more = 1;
       +      stbtt_uint8 *comp = data + g + 10;
       +      num_vertices = 0;
       +      vertices = 0;
       +      while (more) {
       +         stbtt_uint16 flags, gidx;
       +         int comp_num_verts = 0, i;
       +         stbtt_vertex *comp_verts = 0, *tmp = 0;
       +         float mtx[6] = {1,0,0,1,0,0}, m, n;
       +         
       +         flags = ttSHORT(comp); comp+=2;
       +         gidx = ttSHORT(comp); comp+=2;
       +
       +         if (flags & 2) { // XY values
       +            if (flags & 1) { // shorts
       +               mtx[4] = ttSHORT(comp); comp+=2;
       +               mtx[5] = ttSHORT(comp); comp+=2;
       +            } else {
       +               mtx[4] = ttCHAR(comp); comp+=1;
       +               mtx[5] = ttCHAR(comp); comp+=1;
       +            }
       +         }
       +         else {
       +            // @TODO handle matching point
       +            STBTT_assert(0);
       +         }
       +         if (flags & (1<<3)) { // WE_HAVE_A_SCALE
       +            mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
       +            mtx[1] = mtx[2] = 0;
       +         } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE
       +            mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
       +            mtx[1] = mtx[2] = 0;
       +            mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
       +         } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO
       +            mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
       +            mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;
       +            mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
       +            mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
       +         }
       +         
       +         // Find transformation scales.
       +         m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
       +         n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
       +
       +         // Get indexed glyph.
       +         comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);
       +         if (comp_num_verts > 0) {
       +            // Transform vertices.
       +            for (i = 0; i < comp_num_verts; ++i) {
       +               stbtt_vertex* v = &comp_verts[i];
       +               stbtt_vertex_type x,y;
       +               x=v->x; y=v->y;
       +               v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
       +               v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
       +               x=v->cx; y=v->cy;
       +               v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
       +               v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
       +            }
       +            // Append vertices.
       +            tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
       +            if (!tmp) {
       +               if (vertices) STBTT_free(vertices, info->userdata);
       +               if (comp_verts) STBTT_free(comp_verts, info->userdata);
       +               return 0;
       +            }
       +            if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
       +            STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
       +            if (vertices) STBTT_free(vertices, info->userdata);
       +            vertices = tmp;
       +            STBTT_free(comp_verts, info->userdata);
       +            num_vertices += comp_num_verts;
       +         }
       +         // More components ?
       +         more = flags & (1<<5);
       +      }
       +   } else if (numberOfContours < 0) {
       +      // @TODO other compound variations?
       +      STBTT_assert(0);
       +   } else {
       +      // numberOfCounters == 0, do nothing
       +   }
       +
       +   *pvertices = vertices;
       +   return num_vertices;
       +}
       +
       +typedef struct
       +{
       +   int bounds;
       +   int started;
       +   float first_x, first_y;
       +   float x, y;
       +   stbtt_int32 min_x, max_x, min_y, max_y;
       +
       +   stbtt_vertex *pvertices;
       +   int num_vertices;
       +} stbtt__csctx;
       +
       +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0}
       +
       +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y)
       +{
       +   if (x > c->max_x || !c->started) c->max_x = x;
       +   if (y > c->max_y || !c->started) c->max_y = y;
       +   if (x < c->min_x || !c->started) c->min_x = x;
       +   if (y < c->min_y || !c->started) c->min_y = y;
       +   c->started = 1;
       +}
       +
       +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1)
       +{
       +   if (c->bounds) {
       +      stbtt__track_vertex(c, x, y);
       +      if (type == STBTT_vcubic) {
       +         stbtt__track_vertex(c, cx, cy);
       +         stbtt__track_vertex(c, cx1, cy1);
       +      }
       +   } else {
       +      stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy);
       +      c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1;
       +      c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1;
       +   }
       +   c->num_vertices++;
       +}
       +
       +static void stbtt__csctx_close_shape(stbtt__csctx *ctx)
       +{
       +   if (ctx->first_x != ctx->x || ctx->first_y != ctx->y)
       +      stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0);
       +}
       +
       +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy)
       +{
       +   stbtt__csctx_close_shape(ctx);
       +   ctx->first_x = ctx->x = ctx->x + dx;
       +   ctx->first_y = ctx->y = ctx->y + dy;
       +   stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
       +}
       +
       +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy)
       +{
       +   ctx->x += dx;
       +   ctx->y += dy;
       +   stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0);
       +}
       +
       +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3)
       +{
       +   float cx1 = ctx->x + dx1;
       +   float cy1 = ctx->y + dy1;
       +   float cx2 = cx1 + dx2;
       +   float cy2 = cy1 + dy2;
       +   ctx->x = cx2 + dx3;
       +   ctx->y = cy2 + dy3;
       +   stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2);
       +}
       +
       +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n)
       +{
       +   int count = stbtt__cff_index_count(&idx);
       +   int bias = 107;
       +   if (count >= 33900)
       +      bias = 32768;
       +   else if (count >= 1240)
       +      bias = 1131;
       +   n += bias;
       +   if (n < 0 || n >= count)
       +      return stbtt__new_buf(NULL, 0);
       +   return stbtt__cff_index_get(idx, n);
       +}
       +
       +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index)
       +{
       +   stbtt__buf fdselect = info->fdselect;
       +   int nranges, start, end, v, fmt, fdselector = -1, i;
       +
       +   stbtt__buf_seek(&fdselect, 0);
       +   fmt = stbtt__buf_get8(&fdselect);
       +   if (fmt == 0) {
       +      // untested
       +      stbtt__buf_skip(&fdselect, glyph_index);
       +      fdselector = stbtt__buf_get8(&fdselect);
       +   } else if (fmt == 3) {
       +      nranges = stbtt__buf_get16(&fdselect);
       +      start = stbtt__buf_get16(&fdselect);
       +      for (i = 0; i < nranges; i++) {
       +         v = stbtt__buf_get8(&fdselect);
       +         end = stbtt__buf_get16(&fdselect);
       +         if (glyph_index >= start && glyph_index < end) {
       +            fdselector = v;
       +            break;
       +         }
       +         start = end;
       +      }
       +   }
       +   if (fdselector == -1) stbtt__new_buf(NULL, 0);
       +   return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector));
       +}
       +
       +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c)
       +{
       +   int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0;
       +   int has_subrs = 0, clear_stack;
       +   float s[48];
       +   stbtt__buf subr_stack[10], subrs = info->subrs, b;
       +   float f;
       +
       +#define STBTT__CSERR(s) (0)
       +
       +   // this currently ignores the initial width value, which isn't needed if we have hmtx
       +   b = stbtt__cff_index_get(info->charstrings, glyph_index);
       +   while (b.cursor < b.size) {
       +      i = 0;
       +      clear_stack = 1;
       +      b0 = stbtt__buf_get8(&b);
       +      switch (b0) {
       +      // @TODO implement hinting
       +      case 0x13: // hintmask
       +      case 0x14: // cntrmask
       +         if (in_header)
       +            maskbits += (sp / 2); // implicit "vstem"
       +         in_header = 0;
       +         stbtt__buf_skip(&b, (maskbits + 7) / 8);
       +         break;
       +
       +      case 0x01: // hstem
       +      case 0x03: // vstem
       +      case 0x12: // hstemhm
       +      case 0x17: // vstemhm
       +         maskbits += (sp / 2);
       +         break;
       +
       +      case 0x15: // rmoveto
       +         in_header = 0;
       +         if (sp < 2) return STBTT__CSERR("rmoveto stack");
       +         stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]);
       +         break;
       +      case 0x04: // vmoveto
       +         in_header = 0;
       +         if (sp < 1) return STBTT__CSERR("vmoveto stack");
       +         stbtt__csctx_rmove_to(c, 0, s[sp-1]);
       +         break;
       +      case 0x16: // hmoveto
       +         in_header = 0;
       +         if (sp < 1) return STBTT__CSERR("hmoveto stack");
       +         stbtt__csctx_rmove_to(c, s[sp-1], 0);
       +         break;
       +
       +      case 0x05: // rlineto
       +         if (sp < 2) return STBTT__CSERR("rlineto stack");
       +         for (; i + 1 < sp; i += 2)
       +            stbtt__csctx_rline_to(c, s[i], s[i+1]);
       +         break;
       +
       +      // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical
       +      // starting from a different place.
       +
       +      case 0x07: // vlineto
       +         if (sp < 1) return STBTT__CSERR("vlineto stack");
       +         goto vlineto;
       +      case 0x06: // hlineto
       +         if (sp < 1) return STBTT__CSERR("hlineto stack");
       +         for (;;) {
       +            if (i >= sp) break;
       +            stbtt__csctx_rline_to(c, s[i], 0);
       +            i++;
       +      vlineto:
       +            if (i >= sp) break;
       +            stbtt__csctx_rline_to(c, 0, s[i]);
       +            i++;
       +         }
       +         break;
       +
       +      case 0x1F: // hvcurveto
       +         if (sp < 4) return STBTT__CSERR("hvcurveto stack");
       +         goto hvcurveto;
       +      case 0x1E: // vhcurveto
       +         if (sp < 4) return STBTT__CSERR("vhcurveto stack");
       +         for (;;) {
       +            if (i + 3 >= sp) break;
       +            stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f);
       +            i += 4;
       +      hvcurveto:
       +            if (i + 3 >= sp) break;
       +            stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]);
       +            i += 4;
       +         }
       +         break;
       +
       +      case 0x08: // rrcurveto
       +         if (sp < 6) return STBTT__CSERR("rcurveline stack");
       +         for (; i + 5 < sp; i += 6)
       +            stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
       +         break;
       +
       +      case 0x18: // rcurveline
       +         if (sp < 8) return STBTT__CSERR("rcurveline stack");
       +         for (; i + 5 < sp - 2; i += 6)
       +            stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
       +         if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack");
       +         stbtt__csctx_rline_to(c, s[i], s[i+1]);
       +         break;
       +
       +      case 0x19: // rlinecurve
       +         if (sp < 8) return STBTT__CSERR("rlinecurve stack");
       +         for (; i + 1 < sp - 6; i += 2)
       +            stbtt__csctx_rline_to(c, s[i], s[i+1]);
       +         if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack");
       +         stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]);
       +         break;
       +
       +      case 0x1A: // vvcurveto
       +      case 0x1B: // hhcurveto
       +         if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack");
       +         f = 0.0;
       +         if (sp & 1) { f = s[i]; i++; }
       +         for (; i + 3 < sp; i += 4) {
       +            if (b0 == 0x1B)
       +               stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0);
       +            else
       +               stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]);
       +            f = 0.0;
       +         }
       +         break;
       +
       +      case 0x0A: // callsubr
       +         if (!has_subrs) {
       +            if (info->fdselect.size)
       +               subrs = stbtt__cid_get_glyph_subrs(info, glyph_index);
       +            has_subrs = 1;
       +         }
       +         // fallthrough
       +      case 0x1D: // callgsubr
       +         if (sp < 1) return STBTT__CSERR("call(g|)subr stack");
       +         v = (int) s[--sp];
       +         if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit");
       +         subr_stack[subr_stack_height++] = b;
       +         b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v);
       +         if (b.size == 0) return STBTT__CSERR("subr not found");
       +         b.cursor = 0;
       +         clear_stack = 0;
       +         break;
       +
       +      case 0x0B: // return
       +         if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr");
       +         b = subr_stack[--subr_stack_height];
       +         clear_stack = 0;
       +         break;
       +
       +      case 0x0E: // endchar
       +         stbtt__csctx_close_shape(c);
       +         return 1;
       +
       +      case 0x0C: { // two-byte escape
       +         float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6;
       +         float dx, dy;
       +         int b1 = stbtt__buf_get8(&b);
       +         switch (b1) {
       +         // @TODO These "flex" implementations ignore the flex-depth and resolution,
       +         // and always draw beziers.
       +         case 0x22: // hflex
       +            if (sp < 7) return STBTT__CSERR("hflex stack");
       +            dx1 = s[0];
       +            dx2 = s[1];
       +            dy2 = s[2];
       +            dx3 = s[3];
       +            dx4 = s[4];
       +            dx5 = s[5];
       +            dx6 = s[6];
       +            stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0);
       +            stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0);
       +            break;
       +
       +         case 0x23: // flex
       +            if (sp < 13) return STBTT__CSERR("flex stack");
       +            dx1 = s[0];
       +            dy1 = s[1];
       +            dx2 = s[2];
       +            dy2 = s[3];
       +            dx3 = s[4];
       +            dy3 = s[5];
       +            dx4 = s[6];
       +            dy4 = s[7];
       +            dx5 = s[8];
       +            dy5 = s[9];
       +            dx6 = s[10];
       +            dy6 = s[11];
       +            //fd is s[12]
       +            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
       +            stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
       +            break;
       +
       +         case 0x24: // hflex1
       +            if (sp < 9) return STBTT__CSERR("hflex1 stack");
       +            dx1 = s[0];
       +            dy1 = s[1];
       +            dx2 = s[2];
       +            dy2 = s[3];
       +            dx3 = s[4];
       +            dx4 = s[5];
       +            dx5 = s[6];
       +            dy5 = s[7];
       +            dx6 = s[8];
       +            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0);
       +            stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5));
       +            break;
       +
       +         case 0x25: // flex1
       +            if (sp < 11) return STBTT__CSERR("flex1 stack");
       +            dx1 = s[0];
       +            dy1 = s[1];
       +            dx2 = s[2];
       +            dy2 = s[3];
       +            dx3 = s[4];
       +            dy3 = s[5];
       +            dx4 = s[6];
       +            dy4 = s[7];
       +            dx5 = s[8];
       +            dy5 = s[9];
       +            dx6 = dy6 = s[10];
       +            dx = dx1+dx2+dx3+dx4+dx5;
       +            dy = dy1+dy2+dy3+dy4+dy5;
       +            if (STBTT_fabs(dx) > STBTT_fabs(dy))
       +               dy6 = -dy;
       +            else
       +               dx6 = -dx;
       +            stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3);
       +            stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6);
       +            break;
       +
       +         default:
       +            return STBTT__CSERR("unimplemented");
       +         }
       +      } break;
       +
       +      default:
       +         if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254))
       +            return STBTT__CSERR("reserved operator");
       +
       +         // push immediate
       +         if (b0 == 255) {
       +            f = (float)stbtt__buf_get32(&b) / 0x10000;
       +         } else {
       +            stbtt__buf_skip(&b, -1);
       +            f = (float)(stbtt_int16)stbtt__cff_int(&b);
       +         }
       +         if (sp >= 48) return STBTT__CSERR("push stack overflow");
       +         s[sp++] = f;
       +         clear_stack = 0;
       +         break;
       +      }
       +      if (clear_stack) sp = 0;
       +   }
       +   return STBTT__CSERR("no endchar");
       +
       +#undef STBTT__CSERR
       +}
       +
       +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
       +{
       +   // runs the charstring twice, once to count and once to output (to avoid realloc)
       +   stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1);
       +   stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0);
       +   if (stbtt__run_charstring(info, glyph_index, &count_ctx)) {
       +      *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata);
       +      output_ctx.pvertices = *pvertices;
       +      if (stbtt__run_charstring(info, glyph_index, &output_ctx)) {
       +         STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices);
       +         return output_ctx.num_vertices;
       +      }
       +   }
       +   *pvertices = NULL;
       +   return 0;
       +}
       +
       +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
       +{
       +   stbtt__csctx c = STBTT__CSCTX_INIT(1);
       +   int r = stbtt__run_charstring(info, glyph_index, &c);
       +   if (x0) {
       +      *x0 = r ? c.min_x : 0;
       +      *y0 = r ? c.min_y : 0;
       +      *x1 = r ? c.max_x : 0;
       +      *y1 = r ? c.max_y : 0;
       +   }
       +   return r ? c.num_vertices : 0;
       +}
       +
       +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
       +{
       +   if (!info->cff.size)
       +      return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices);
       +   else
       +      return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices);
       +}
       +
       +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
       +{
       +   stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
       +   if (glyph_index < numOfLongHorMetrics) {
       +      if (advanceWidth)     *advanceWidth    = ttSHORT(info->data + info->hmtx + 4*glyph_index);
       +      if (leftSideBearing)  *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
       +   } else {
       +      if (advanceWidth)     *advanceWidth    = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
       +      if (leftSideBearing)  *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
       +   }
       +}
       +
       +STBTT_DEF int  stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2)
       +{
       +   stbtt_uint8 *data = info->data + info->kern;
       +   stbtt_uint32 needle, straw;
       +   int l, r, m;
       +
       +   // we only look at the first table. it must be 'horizontal' and format 0.
       +   if (!info->kern)
       +      return 0;
       +   if (ttUSHORT(data+2) < 1) // number of tables, need at least 1
       +      return 0;
       +   if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format
       +      return 0;
       +
       +   l = 0;
       +   r = ttUSHORT(data+10) - 1;
       +   needle = glyph1 << 16 | glyph2;
       +   while (l <= r) {
       +      m = (l + r) >> 1;
       +      straw = ttULONG(data+18+(m*6)); // note: unaligned read
       +      if (needle < straw)
       +         r = m - 1;
       +      else if (needle > straw)
       +         l = m + 1;
       +      else
       +         return ttSHORT(data+22+(m*6));
       +   }
       +   return 0;
       +}
       +
       +STBTT_DEF int  stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2)
       +{
       +   if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs
       +      return 0;
       +   return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2));
       +}
       +
       +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
       +{
       +   stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
       +}
       +
       +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
       +{
       +   if (ascent ) *ascent  = ttSHORT(info->data+info->hhea + 4);
       +   if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
       +   if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
       +}
       +
       +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1)
       +{
       +   *x0 = ttSHORT(info->data + info->head + 36);
       +   *y0 = ttSHORT(info->data + info->head + 38);
       +   *x1 = ttSHORT(info->data + info->head + 40);
       +   *y1 = ttSHORT(info->data + info->head + 42);
       +}
       +
       +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
       +{
       +   int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
       +   return (float) height / fheight;
       +}
       +
       +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels)
       +{
       +   int unitsPerEm = ttUSHORT(info->data + info->head + 18);
       +   return pixels / unitsPerEm;
       +}
       +
       +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
       +{
       +   STBTT_free(v, info->userdata);
       +}
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// antialiasing software rasterizer
       +//
       +
       +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
       +{
       +   int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning
       +   if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) {
       +      // e.g. space character
       +      if (ix0) *ix0 = 0;
       +      if (iy0) *iy0 = 0;
       +      if (ix1) *ix1 = 0;
       +      if (iy1) *iy1 = 0;
       +   } else {
       +      // move to integral bboxes (treating pixels as little squares, what pixels get touched)?
       +      if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x);
       +      if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y);
       +      if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x);
       +      if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y);
       +   }
       +}
       +
       +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
       +{
       +   stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1);
       +}
       +
       +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1)
       +{
       +   stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1);
       +}
       +
       +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
       +{
       +   stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1);
       +}
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +//  Rasterizer
       +
       +typedef struct stbtt__hheap_chunk
       +{
       +   struct stbtt__hheap_chunk *next;
       +} stbtt__hheap_chunk;
       +
       +typedef struct stbtt__hheap
       +{
       +   struct stbtt__hheap_chunk *head;
       +   void   *first_free;
       +   int    num_remaining_in_head_chunk;
       +} stbtt__hheap;
       +
       +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata)
       +{
       +   if (hh->first_free) {
       +      void *p = hh->first_free;
       +      hh->first_free = * (void **) p;
       +      return p;
       +   } else {
       +      if (hh->num_remaining_in_head_chunk == 0) {
       +         int count = (size < 32 ? 2000 : size < 128 ? 800 : 100);
       +         stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata);
       +         if (c == NULL)
       +            return NULL;
       +         c->next = hh->head;
       +         hh->head = c;
       +         hh->num_remaining_in_head_chunk = count;
       +      }
       +      --hh->num_remaining_in_head_chunk;
       +      return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk;
       +   }
       +}
       +
       +static void stbtt__hheap_free(stbtt__hheap *hh, void *p)
       +{
       +   *(void **) p = hh->first_free;
       +   hh->first_free = p;
       +}
       +
       +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata)
       +{
       +   stbtt__hheap_chunk *c = hh->head;
       +   while (c) {
       +      stbtt__hheap_chunk *n = c->next;
       +      STBTT_free(c, userdata);
       +      c = n;
       +   }
       +}
       +
       +typedef struct stbtt__edge {
       +   float x0,y0, x1,y1;
       +   int invert;
       +} stbtt__edge;
       +
       +
       +typedef struct stbtt__active_edge
       +{
       +   struct stbtt__active_edge *next;
       +   #if STBTT_RASTERIZER_VERSION==1
       +   int x,dx;
       +   float ey;
       +   int direction;
       +   #elif STBTT_RASTERIZER_VERSION==2
       +   float fx,fdx,fdy;
       +   float direction;
       +   float sy;
       +   float ey;
       +   #else
       +   #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
       +   #endif
       +} stbtt__active_edge;
       +
       +#if STBTT_RASTERIZER_VERSION == 1
       +#define STBTT_FIXSHIFT   10
       +#define STBTT_FIX        (1 << STBTT_FIXSHIFT)
       +#define STBTT_FIXMASK    (STBTT_FIX-1)
       +
       +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
       +{
       +   stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
       +   float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
       +   STBTT_assert(z != NULL);
       +   if (!z) return z;
       +   
       +   // round dx down to avoid overshooting
       +   if (dxdy < 0)
       +      z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy);
       +   else
       +      z->dx = STBTT_ifloor(STBTT_FIX * dxdy);
       +
       +   z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount
       +   z->x -= off_x * STBTT_FIX;
       +
       +   z->ey = e->y1;
       +   z->next = 0;
       +   z->direction = e->invert ? 1 : -1;
       +   return z;
       +}
       +#elif STBTT_RASTERIZER_VERSION == 2
       +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata)
       +{
       +   stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata);
       +   float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
       +   STBTT_assert(z != NULL);
       +   //STBTT_assert(e->y0 <= start_point);
       +   if (!z) return z;
       +   z->fdx = dxdy;
       +   z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f;
       +   z->fx = e->x0 + dxdy * (start_point - e->y0);
       +   z->fx -= off_x;
       +   z->direction = e->invert ? 1.0f : -1.0f;
       +   z->sy = e->y0;
       +   z->ey = e->y1;
       +   z->next = 0;
       +   return z;
       +}
       +#else
       +#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
       +#endif
       +
       +#if STBTT_RASTERIZER_VERSION == 1
       +// note: this routine clips fills that extend off the edges... ideally this
       +// wouldn't happen, but it could happen if the truetype glyph bounding boxes
       +// are wrong, or if the user supplies a too-small bitmap
       +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)
       +{
       +   // non-zero winding fill
       +   int x0=0, w=0;
       +
       +   while (e) {
       +      if (w == 0) {
       +         // if we're currently at zero, we need to record the edge start point
       +         x0 = e->x; w += e->direction;
       +      } else {
       +         int x1 = e->x; w += e->direction;
       +         // if we went to zero, we need to draw
       +         if (w == 0) {
       +            int i = x0 >> STBTT_FIXSHIFT;
       +            int j = x1 >> STBTT_FIXSHIFT;
       +
       +            if (i < len && j >= 0) {
       +               if (i == j) {
       +                  // x0,x1 are the same pixel, so compute combined coverage
       +                  scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT);
       +               } else {
       +                  if (i >= 0) // add antialiasing for x0
       +                     scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT);
       +                  else
       +                     i = -1; // clip
       +
       +                  if (j < len) // add antialiasing for x1
       +                     scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT);
       +                  else
       +                     j = len; // clip
       +
       +                  for (++i; i < j; ++i) // fill pixels between x0 and x1
       +                     scanline[i] = scanline[i] + (stbtt_uint8) max_weight;
       +               }
       +            }
       +         }
       +      }
       +      
       +      e = e->next;
       +   }
       +}
       +
       +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
       +{
       +   stbtt__hheap hh = { 0, 0, 0 };
       +   stbtt__active_edge *active = NULL;
       +   int y,j=0;
       +   int max_weight = (255 / vsubsample);  // weight per vertical scanline
       +   int s; // vertical subsample index
       +   unsigned char scanline_data[512], *scanline;
       +
       +   if (result->w > 512)
       +      scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
       +   else
       +      scanline = scanline_data;
       +
       +   y = off_y * vsubsample;
       +   e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
       +
       +   while (j < result->h) {
       +      STBTT_memset(scanline, 0, result->w);
       +      for (s=0; s < vsubsample; ++s) {
       +         // find center of pixel for this scanline
       +         float scan_y = y + 0.5f;
       +         stbtt__active_edge **step = &active;
       +
       +         // update all active edges;
       +         // remove all active edges that terminate before the center of this scanline
       +         while (*step) {
       +            stbtt__active_edge * z = *step;
       +            if (z->ey <= scan_y) {
       +               *step = z->next; // delete from list
       +               STBTT_assert(z->direction);
       +               z->direction = 0;
       +               stbtt__hheap_free(&hh, z);
       +            } else {
       +               z->x += z->dx; // advance to position for current scanline
       +               step = &((*step)->next); // advance through list
       +            }
       +         }
       +
       +         // resort the list if needed
       +         for(;;) {
       +            int changed=0;
       +            step = &active;
       +            while (*step && (*step)->next) {
       +               if ((*step)->x > (*step)->next->x) {
       +                  stbtt__active_edge *t = *step;
       +                  stbtt__active_edge *q = t->next;
       +
       +                  t->next = q->next;
       +                  q->next = t;
       +                  *step = q;
       +                  changed = 1;
       +               }
       +               step = &(*step)->next;
       +            }
       +            if (!changed) break;
       +         }
       +
       +         // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
       +         while (e->y0 <= scan_y) {
       +            if (e->y1 > scan_y) {
       +               stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata);
       +               if (z != NULL) {
       +                  // find insertion point
       +                  if (active == NULL)
       +                     active = z;
       +                  else if (z->x < active->x) {
       +                     // insert at front
       +                     z->next = active;
       +                     active = z;
       +                  } else {
       +                     // find thing to insert AFTER
       +                     stbtt__active_edge *p = active;
       +                     while (p->next && p->next->x < z->x)
       +                        p = p->next;
       +                     // at this point, p->next->x is NOT < z->x
       +                     z->next = p->next;
       +                     p->next = z;
       +                  }
       +               }
       +            }
       +            ++e;
       +         }
       +
       +         // now process all active edges in XOR fashion
       +         if (active)
       +            stbtt__fill_active_edges(scanline, result->w, active, max_weight);
       +
       +         ++y;
       +      }
       +      STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);
       +      ++j;
       +   }
       +
       +   stbtt__hheap_cleanup(&hh, userdata);
       +
       +   if (scanline != scanline_data)
       +      STBTT_free(scanline, userdata);
       +}
       +
       +#elif STBTT_RASTERIZER_VERSION == 2
       +
       +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1
       +// (i.e. it has already been clipped to those)
       +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1)
       +{
       +   if (y0 == y1) return;
       +   STBTT_assert(y0 < y1);
       +   STBTT_assert(e->sy <= e->ey);
       +   if (y0 > e->ey) return;
       +   if (y1 < e->sy) return;
       +   if (y0 < e->sy) {
       +      x0 += (x1-x0) * (e->sy - y0) / (y1-y0);
       +      y0 = e->sy;
       +   }
       +   if (y1 > e->ey) {
       +      x1 += (x1-x0) * (e->ey - y1) / (y1-y0);
       +      y1 = e->ey;
       +   }
       +
       +   if (x0 == x)
       +      STBTT_assert(x1 <= x+1);
       +   else if (x0 == x+1)
       +      STBTT_assert(x1 >= x);
       +   else if (x0 <= x)
       +      STBTT_assert(x1 <= x);
       +   else if (x0 >= x+1)
       +      STBTT_assert(x1 >= x+1);
       +   else
       +      STBTT_assert(x1 >= x && x1 <= x+1);
       +
       +   if (x0 <= x && x1 <= x)
       +      scanline[x] += e->direction * (y1-y0);
       +   else if (x0 >= x+1 && x1 >= x+1)
       +      ;
       +   else {
       +      STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1);
       +      scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position
       +   }
       +}
       +
       +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top)
       +{
       +   float y_bottom = y_top+1;
       +
       +   while (e) {
       +      // brute force every pixel
       +
       +      // compute intersection points with top & bottom
       +      STBTT_assert(e->ey >= y_top);
       +
       +      if (e->fdx == 0) {
       +         float x0 = e->fx;
       +         if (x0 < len) {
       +            if (x0 >= 0) {
       +               stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom);
       +               stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom);
       +            } else {
       +               stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom);
       +            }
       +         }
       +      } else {
       +         float x0 = e->fx;
       +         float dx = e->fdx;
       +         float xb = x0 + dx;
       +         float x_top, x_bottom;
       +         float sy0,sy1;
       +         float dy = e->fdy;
       +         STBTT_assert(e->sy <= y_bottom && e->ey >= y_top);
       +
       +         // compute endpoints of line segment clipped to this scanline (if the
       +         // line segment starts on this scanline. x0 is the intersection of the
       +         // line with y_top, but that may be off the line segment.
       +         if (e->sy > y_top) {
       +            x_top = x0 + dx * (e->sy - y_top);
       +            sy0 = e->sy;
       +         } else {
       +            x_top = x0;
       +            sy0 = y_top;
       +         }
       +         if (e->ey < y_bottom) {
       +            x_bottom = x0 + dx * (e->ey - y_top);
       +            sy1 = e->ey;
       +         } else {
       +            x_bottom = xb;
       +            sy1 = y_bottom;
       +         }
       +
       +         if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) {
       +            // from here on, we don't have to range check x values
       +
       +            if ((int) x_top == (int) x_bottom) {
       +               float height;
       +               // simple case, only spans one pixel
       +               int x = (int) x_top;
       +               height = sy1 - sy0;
       +               STBTT_assert(x >= 0 && x < len);
       +               scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2)  * height;
       +               scanline_fill[x] += e->direction * height; // everything right of this pixel is filled
       +            } else {
       +               int x,x1,x2;
       +               float y_crossing, step, sign, area;
       +               // covers 2+ pixels
       +               if (x_top > x_bottom) {
       +                  // flip scanline vertically; signed area is the same
       +                  float t;
       +                  sy0 = y_bottom - (sy0 - y_top);
       +                  sy1 = y_bottom - (sy1 - y_top);
       +                  t = sy0, sy0 = sy1, sy1 = t;
       +                  t = x_bottom, x_bottom = x_top, x_top = t;
       +                  dx = -dx;
       +                  dy = -dy;
       +                  t = x0, x0 = xb, xb = t;
       +               }
       +
       +               x1 = (int) x_top;
       +               x2 = (int) x_bottom;
       +               // compute intersection with y axis at x1+1
       +               y_crossing = (x1+1 - x0) * dy + y_top;
       +
       +               sign = e->direction;
       +               // area of the rectangle covered from y0..y_crossing
       +               area = sign * (y_crossing-sy0);
       +               // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing)
       +               scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2);
       +
       +               step = sign * dy;
       +               for (x = x1+1; x < x2; ++x) {
       +                  scanline[x] += area + step/2;
       +                  area += step;
       +               }
       +               y_crossing += dy * (x2 - (x1+1));
       +
       +               STBTT_assert(STBTT_fabs(area) <= 1.01f);
       +
       +               scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing);
       +
       +               scanline_fill[x2] += sign * (sy1-sy0);
       +            }
       +         } else {
       +            // if edge goes outside of box we're drawing, we require
       +            // clipping logic. since this does not match the intended use
       +            // of this library, we use a different, very slow brute
       +            // force implementation
       +            int x;
       +            for (x=0; x < len; ++x) {
       +               // cases:
       +               //
       +               // there can be up to two intersections with the pixel. any intersection
       +               // with left or right edges can be handled by splitting into two (or three)
       +               // regions. intersections with top & bottom do not necessitate case-wise logic.
       +               //
       +               // the old way of doing this found the intersections with the left & right edges,
       +               // then used some simple logic to produce up to three segments in sorted order
       +               // from top-to-bottom. however, this had a problem: if an x edge was epsilon
       +               // across the x border, then the corresponding y position might not be distinct
       +               // from the other y segment, and it might ignored as an empty segment. to avoid
       +               // that, we need to explicitly produce segments based on x positions.
       +
       +               // rename variables to clear pairs
       +               float y0 = y_top;
       +               float x1 = (float) (x);
       +               float x2 = (float) (x+1);
       +               float x3 = xb;
       +               float y3 = y_bottom;
       +               float y1,y2;
       +
       +               // x = e->x + e->dx * (y-y_top)
       +               // (y-y_top) = (x - e->x) / e->dx
       +               // y = (x - e->x) / e->dx + y_top
       +               y1 = (x - x0) / dx + y_top;
       +               y2 = (x+1 - x0) / dx + y_top;
       +
       +               if (x0 < x1 && x3 > x2) {         // three segments descending down-right
       +                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
       +                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2);
       +                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
       +               } else if (x3 < x1 && x0 > x2) {  // three segments descending down-left
       +                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
       +                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1);
       +                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
       +               } else if (x0 < x1 && x3 > x1) {  // two segments across x, down-right
       +                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
       +                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
       +               } else if (x3 < x1 && x0 > x1) {  // two segments across x, down-left
       +                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1);
       +                  stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3);
       +               } else if (x0 < x2 && x3 > x2) {  // two segments across x+1, down-right
       +                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
       +                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
       +               } else if (x3 < x2 && x0 > x2) {  // two segments across x+1, down-left
       +                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2);
       +                  stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3);
       +               } else {  // one segment
       +                  stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3);
       +               }
       +            }
       +         }
       +      }
       +      e = e->next;
       +   }
       +}
       +
       +// directly AA rasterize edges w/o supersampling
       +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
       +{
       +   stbtt__hheap hh = { 0, 0, 0 };
       +   stbtt__active_edge *active = NULL;
       +   int y,j=0, i;
       +   float scanline_data[129], *scanline, *scanline2;
       +
       +   STBTT__NOTUSED(vsubsample);
       +
       +   if (result->w > 64)
       +      scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata);
       +   else
       +      scanline = scanline_data;
       +
       +   scanline2 = scanline + result->w;
       +
       +   y = off_y;
       +   e[n].y0 = (float) (off_y + result->h) + 1;
       +
       +   while (j < result->h) {
       +      // find center of pixel for this scanline
       +      float scan_y_top    = y + 0.0f;
       +      float scan_y_bottom = y + 1.0f;
       +      stbtt__active_edge **step = &active;
       +
       +      STBTT_memset(scanline , 0, result->w*sizeof(scanline[0]));
       +      STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0]));
       +
       +      // update all active edges;
       +      // remove all active edges that terminate before the top of this scanline
       +      while (*step) {
       +         stbtt__active_edge * z = *step;
       +         if (z->ey <= scan_y_top) {
       +            *step = z->next; // delete from list
       +            STBTT_assert(z->direction);
       +            z->direction = 0;
       +            stbtt__hheap_free(&hh, z);
       +         } else {
       +            step = &((*step)->next); // advance through list
       +         }
       +      }
       +
       +      // insert all edges that start before the bottom of this scanline
       +      while (e->y0 <= scan_y_bottom) {
       +         if (e->y0 != e->y1) {
       +            stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata);
       +            if (z != NULL) {
       +               STBTT_assert(z->ey >= scan_y_top);
       +               // insert at front
       +               z->next = active;
       +               active = z;
       +            }
       +         }
       +         ++e;
       +      }
       +
       +      // now process all active edges
       +      if (active)
       +         stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top);
       +
       +      {
       +         float sum = 0;
       +         for (i=0; i < result->w; ++i) {
       +            float k;
       +            int m;
       +            sum += scanline2[i];
       +            k = scanline[i] + sum;
       +            k = (float) STBTT_fabs(k)*255 + 0.5f;
       +            m = (int) k;
       +            if (m > 255) m = 255;
       +            result->pixels[j*result->stride + i] = (unsigned char) m;
       +         }
       +      }
       +      // advance all the edges
       +      step = &active;
       +      while (*step) {
       +         stbtt__active_edge *z = *step;
       +         z->fx += z->fdx; // advance to position for current scanline
       +         step = &((*step)->next); // advance through list
       +      }
       +
       +      ++y;
       +      ++j;
       +   }
       +
       +   stbtt__hheap_cleanup(&hh, userdata);
       +
       +   if (scanline != scanline_data)
       +      STBTT_free(scanline, userdata);
       +}
       +#else
       +#error "Unrecognized value of STBTT_RASTERIZER_VERSION"
       +#endif
       +
       +#define STBTT__COMPARE(a,b)  ((a)->y0 < (b)->y0)
       +
       +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n)
       +{
       +   int i,j;
       +   for (i=1; i < n; ++i) {
       +      stbtt__edge t = p[i], *a = &t;
       +      j = i;
       +      while (j > 0) {
       +         stbtt__edge *b = &p[j-1];
       +         int c = STBTT__COMPARE(a,b);
       +         if (!c) break;
       +         p[j] = p[j-1];
       +         --j;
       +      }
       +      if (i != j)
       +         p[j] = t;
       +   }
       +}
       +
       +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n)
       +{
       +   /* threshhold for transitioning to insertion sort */
       +   while (n > 12) {
       +      stbtt__edge t;
       +      int c01,c12,c,m,i,j;
       +
       +      /* compute median of three */
       +      m = n >> 1;
       +      c01 = STBTT__COMPARE(&p[0],&p[m]);
       +      c12 = STBTT__COMPARE(&p[m],&p[n-1]);
       +      /* if 0 >= mid >= end, or 0 < mid < end, then use mid */
       +      if (c01 != c12) {
       +         /* otherwise, we'll need to swap something else to middle */
       +         int z;
       +         c = STBTT__COMPARE(&p[0],&p[n-1]);
       +         /* 0>mid && mid<n:  0>n => n; 0<n => 0 */
       +         /* 0<mid && mid>n:  0>n => 0; 0<n => n */
       +         z = (c == c12) ? 0 : n-1;
       +         t = p[z];
       +         p[z] = p[m];
       +         p[m] = t;
       +      }
       +      /* now p[m] is the median-of-three */
       +      /* swap it to the beginning so it won't move around */
       +      t = p[0];
       +      p[0] = p[m];
       +      p[m] = t;
       +
       +      /* partition loop */
       +      i=1;
       +      j=n-1;
       +      for(;;) {
       +         /* handling of equality is crucial here */
       +         /* for sentinels & efficiency with duplicates */
       +         for (;;++i) {
       +            if (!STBTT__COMPARE(&p[i], &p[0])) break;
       +         }
       +         for (;;--j) {
       +            if (!STBTT__COMPARE(&p[0], &p[j])) break;
       +         }
       +         /* make sure we haven't crossed */
       +         if (i >= j) break;
       +         t = p[i];
       +         p[i] = p[j];
       +         p[j] = t;
       +
       +         ++i;
       +         --j;
       +      }
       +      /* recurse on smaller side, iterate on larger */
       +      if (j < (n-i)) {
       +         stbtt__sort_edges_quicksort(p,j);
       +         p = p+i;
       +         n = n-i;
       +      } else {
       +         stbtt__sort_edges_quicksort(p+i, n-i);
       +         n = j;
       +      }
       +   }
       +}
       +
       +static void stbtt__sort_edges(stbtt__edge *p, int n)
       +{
       +   stbtt__sort_edges_quicksort(p, n);
       +   stbtt__sort_edges_ins_sort(p, n);
       +}
       +
       +typedef struct
       +{
       +   float x,y;
       +} stbtt__point;
       +
       +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata)
       +{
       +   float y_scale_inv = invert ? -scale_y : scale_y;
       +   stbtt__edge *e;
       +   int n,i,j,k,m;
       +#if STBTT_RASTERIZER_VERSION == 1
       +   int vsubsample = result->h < 8 ? 15 : 5;
       +#elif STBTT_RASTERIZER_VERSION == 2
       +   int vsubsample = 1;
       +#else
       +   #error "Unrecognized value of STBTT_RASTERIZER_VERSION"
       +#endif
       +   // vsubsample should divide 255 evenly; otherwise we won't reach full opacity
       +
       +   // now we have to blow out the windings into explicit edge lists
       +   n = 0;
       +   for (i=0; i < windings; ++i)
       +      n += wcount[i];
       +
       +   e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel
       +   if (e == 0) return;
       +   n = 0;
       +
       +   m=0;
       +   for (i=0; i < windings; ++i) {
       +      stbtt__point *p = pts + m;
       +      m += wcount[i];
       +      j = wcount[i]-1;
       +      for (k=0; k < wcount[i]; j=k++) {
       +         int a=k,b=j;
       +         // skip the edge if horizontal
       +         if (p[j].y == p[k].y)
       +            continue;
       +         // add edge from j to k to the list
       +         e[n].invert = 0;
       +         if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
       +            e[n].invert = 1;
       +            a=j,b=k;
       +         }
       +         e[n].x0 = p[a].x * scale_x + shift_x;
       +         e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample;
       +         e[n].x1 = p[b].x * scale_x + shift_x;
       +         e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample;
       +         ++n;
       +      }
       +   }
       +
       +   // now sort the edges by their highest point (should snap to integer, and then by x)
       +   //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);
       +   stbtt__sort_edges(e, n);
       +
       +   // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
       +   stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);
       +
       +   STBTT_free(e, userdata);
       +}
       +
       +static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
       +{
       +   if (!points) return; // during first pass, it's unallocated
       +   points[n].x = x;
       +   points[n].y = y;
       +}
       +
       +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching
       +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
       +{
       +   // midpoint
       +   float mx = (x0 + 2*x1 + x2)/4;
       +   float my = (y0 + 2*y1 + y2)/4;
       +   // versus directly drawn line
       +   float dx = (x0+x2)/2 - mx;
       +   float dy = (y0+y2)/2 - my;
       +   if (n > 16) // 65536 segments on one curve better be enough!
       +      return 1;
       +   if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA
       +      stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
       +      stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
       +   } else {
       +      stbtt__add_point(points, *num_points,x2,y2);
       +      *num_points = *num_points+1;
       +   }
       +   return 1;
       +}
       +
       +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n)
       +{
       +   // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough
       +   float dx0 = x1-x0;
       +   float dy0 = y1-y0;
       +   float dx1 = x2-x1;
       +   float dy1 = y2-y1;
       +   float dx2 = x3-x2;
       +   float dy2 = y3-y2;
       +   float dx = x3-x0;
       +   float dy = y3-y0;
       +   float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2));
       +   float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy);
       +   float flatness_squared = longlen*longlen-shortlen*shortlen;
       +
       +   if (n > 16) // 65536 segments on one curve better be enough!
       +      return;
       +
       +   if (flatness_squared > objspace_flatness_squared) {
       +      float x01 = (x0+x1)/2;
       +      float y01 = (y0+y1)/2;
       +      float x12 = (x1+x2)/2;
       +      float y12 = (y1+y2)/2;
       +      float x23 = (x2+x3)/2;
       +      float y23 = (y2+y3)/2;
       +
       +      float xa = (x01+x12)/2;
       +      float ya = (y01+y12)/2;
       +      float xb = (x12+x23)/2;
       +      float yb = (y12+y23)/2;
       +
       +      float mx = (xa+xb)/2;
       +      float my = (ya+yb)/2;
       +
       +      stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1);
       +      stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1);
       +   } else {
       +      stbtt__add_point(points, *num_points,x3,y3);
       +      *num_points = *num_points+1;
       +   }
       +}
       +
       +// returns number of contours
       +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
       +{
       +   stbtt__point *points=0;
       +   int num_points=0;
       +
       +   float objspace_flatness_squared = objspace_flatness * objspace_flatness;
       +   int i,n=0,start=0, pass;
       +
       +   // count how many "moves" there are to get the contour count
       +   for (i=0; i < num_verts; ++i)
       +      if (vertices[i].type == STBTT_vmove)
       +         ++n;
       +
       +   *num_contours = n;
       +   if (n == 0) return 0;
       +
       +   *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);
       +
       +   if (*contour_lengths == 0) {
       +      *num_contours = 0;
       +      return 0;
       +   }
       +
       +   // make two passes through the points so we don't need to realloc
       +   for (pass=0; pass < 2; ++pass) {
       +      float x=0,y=0;
       +      if (pass == 1) {
       +         points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);
       +         if (points == NULL) goto error;
       +      }
       +      num_points = 0;
       +      n= -1;
       +      for (i=0; i < num_verts; ++i) {
       +         switch (vertices[i].type) {
       +            case STBTT_vmove:
       +               // start the next contour
       +               if (n >= 0)
       +                  (*contour_lengths)[n] = num_points - start;
       +               ++n;
       +               start = num_points;
       +
       +               x = vertices[i].x, y = vertices[i].y;
       +               stbtt__add_point(points, num_points++, x,y);
       +               break;
       +            case STBTT_vline:
       +               x = vertices[i].x, y = vertices[i].y;
       +               stbtt__add_point(points, num_points++, x, y);
       +               break;
       +            case STBTT_vcurve:
       +               stbtt__tesselate_curve(points, &num_points, x,y,
       +                                        vertices[i].cx, vertices[i].cy,
       +                                        vertices[i].x,  vertices[i].y,
       +                                        objspace_flatness_squared, 0);
       +               x = vertices[i].x, y = vertices[i].y;
       +               break;
       +            case STBTT_vcubic:
       +               stbtt__tesselate_cubic(points, &num_points, x,y,
       +                                        vertices[i].cx, vertices[i].cy,
       +                                        vertices[i].cx1, vertices[i].cy1,
       +                                        vertices[i].x,  vertices[i].y,
       +                                        objspace_flatness_squared, 0);
       +               x = vertices[i].x, y = vertices[i].y;
       +               break;
       +         }
       +      }
       +      (*contour_lengths)[n] = num_points - start;
       +   }
       +
       +   return points;
       +error:
       +   STBTT_free(points, userdata);
       +   STBTT_free(*contour_lengths, userdata);
       +   *contour_lengths = 0;
       +   *num_contours = 0;
       +   return NULL;
       +}
       +
       +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata)
       +{
       +   float scale = scale_x > scale_y ? scale_y : scale_x;
       +   int winding_count, *winding_lengths;
       +   stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
       +   if (windings) {
       +      stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata);
       +      STBTT_free(winding_lengths, userdata);
       +      STBTT_free(windings, userdata);
       +   }
       +}
       +
       +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
       +{
       +   STBTT_free(bitmap, userdata);
       +}
       +
       +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff)
       +{
       +   int ix0,iy0,ix1,iy1;
       +   stbtt__bitmap gbm;
       +   stbtt_vertex *vertices;   
       +   int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
       +
       +   if (scale_x == 0) scale_x = scale_y;
       +   if (scale_y == 0) {
       +      if (scale_x == 0) {
       +         STBTT_free(vertices, info->userdata);
       +         return NULL;
       +      }
       +      scale_y = scale_x;
       +   }
       +
       +   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1);
       +
       +   // now we get the size
       +   gbm.w = (ix1 - ix0);
       +   gbm.h = (iy1 - iy0);
       +   gbm.pixels = NULL; // in case we error
       +
       +   if (width ) *width  = gbm.w;
       +   if (height) *height = gbm.h;
       +   if (xoff  ) *xoff   = ix0;
       +   if (yoff  ) *yoff   = iy0;
       +   
       +   if (gbm.w && gbm.h) {
       +      gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
       +      if (gbm.pixels) {
       +         gbm.stride = gbm.w;
       +
       +         stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata);
       +      }
       +   }
       +   STBTT_free(vertices, info->userdata);
       +   return gbm.pixels;
       +}   
       +
       +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
       +{
       +   return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff);
       +}
       +
       +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph)
       +{
       +   int ix0,iy0;
       +   stbtt_vertex *vertices;
       +   int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
       +   stbtt__bitmap gbm;   
       +
       +   stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0);
       +   gbm.pixels = output;
       +   gbm.w = out_w;
       +   gbm.h = out_h;
       +   gbm.stride = out_stride;
       +
       +   if (gbm.w && gbm.h)
       +      stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata);
       +
       +   STBTT_free(vertices, info->userdata);
       +}
       +
       +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
       +{
       +   stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph);
       +}
       +
       +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
       +{
       +   return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
       +}   
       +
       +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint)
       +{
       +   stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint));
       +}
       +
       +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
       +{
       +   return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff);
       +}   
       +
       +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
       +{
       +   stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint);
       +}
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// bitmap baking
       +//
       +// This is SUPER-CRAPPY packing to keep source code small
       +
       +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset,  // font location (use offset=0 for plain .ttf)
       +                                float pixel_height,                     // height of font in pixels
       +                                unsigned char *pixels, int pw, int ph,  // bitmap to be filled in
       +                                int first_char, int num_chars,          // characters to bake
       +                                stbtt_bakedchar *chardata)
       +{
       +   float scale;
       +   int x,y,bottom_y, i;
       +   stbtt_fontinfo f;
       +   f.userdata = NULL;
       +   if (!stbtt_InitFont(&f, data, offset))
       +      return -1;
       +   STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
       +   x=y=1;
       +   bottom_y = 1;
       +
       +   scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
       +
       +   for (i=0; i < num_chars; ++i) {
       +      int advance, lsb, x0,y0,x1,y1,gw,gh;
       +      int g = stbtt_FindGlyphIndex(&f, first_char + i);
       +      stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
       +      stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);
       +      gw = x1-x0;
       +      gh = y1-y0;
       +      if (x + gw + 1 >= pw)
       +         y = bottom_y, x = 1; // advance to next row
       +      if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row
       +         return -i;
       +      STBTT_assert(x+gw < pw);
       +      STBTT_assert(y+gh < ph);
       +      stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);
       +      chardata[i].x0 = (stbtt_int16) x;
       +      chardata[i].y0 = (stbtt_int16) y;
       +      chardata[i].x1 = (stbtt_int16) (x + gw);
       +      chardata[i].y1 = (stbtt_int16) (y + gh);
       +      chardata[i].xadvance = scale * advance;
       +      chardata[i].xoff     = (float) x0;
       +      chardata[i].yoff     = (float) y0;
       +      x = x + gw + 1;
       +      if (y+gh+1 > bottom_y)
       +         bottom_y = y+gh+1;
       +   }
       +   return bottom_y;
       +}
       +
       +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
       +{
       +   float d3d_bias = opengl_fillrule ? 0 : -0.5f;
       +   float ipw = 1.0f / pw, iph = 1.0f / ph;
       +   stbtt_bakedchar *b = chardata + char_index;
       +   int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f);
       +   int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f);
       +
       +   q->x0 = round_x + d3d_bias;
       +   q->y0 = round_y + d3d_bias;
       +   q->x1 = round_x + b->x1 - b->x0 + d3d_bias;
       +   q->y1 = round_y + b->y1 - b->y0 + d3d_bias;
       +
       +   q->s0 = b->x0 * ipw;
       +   q->t0 = b->y0 * iph;
       +   q->s1 = b->x1 * ipw;
       +   q->t1 = b->y1 * iph;
       +
       +   *xpos += b->xadvance;
       +}
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// rectangle packing replacement routines if you don't have stb_rect_pack.h
       +//
       +
       +#ifndef STB_RECT_PACK_VERSION
       +
       +typedef int stbrp_coord;
       +
       +////////////////////////////////////////////////////////////////////////////////////
       +//                                                                                //
       +//                                                                                //
       +// COMPILER WARNING ?!?!?                                                         //
       +//                                                                                //
       +//                                                                                //
       +// if you get a compile warning due to these symbols being defined more than      //
       +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h"         //
       +//                                                                                //
       +////////////////////////////////////////////////////////////////////////////////////
       +
       +typedef struct
       +{
       +   int width,height;
       +   int x,y,bottom_y;
       +} stbrp_context;
       +
       +typedef struct
       +{
       +   unsigned char x;
       +} stbrp_node;
       +
       +struct stbrp_rect
       +{
       +   stbrp_coord x,y;
       +   int id,w,h,was_packed;
       +};
       +
       +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes)
       +{
       +   con->width  = pw;
       +   con->height = ph;
       +   con->x = 0;
       +   con->y = 0;
       +   con->bottom_y = 0;
       +   STBTT__NOTUSED(nodes);
       +   STBTT__NOTUSED(num_nodes);   
       +}
       +
       +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects)
       +{
       +   int i;
       +   for (i=0; i < num_rects; ++i) {
       +      if (con->x + rects[i].w > con->width) {
       +         con->x = 0;
       +         con->y = con->bottom_y;
       +      }
       +      if (con->y + rects[i].h > con->height)
       +         break;
       +      rects[i].x = con->x;
       +      rects[i].y = con->y;
       +      rects[i].was_packed = 1;
       +      con->x += rects[i].w;
       +      if (con->y + rects[i].h > con->bottom_y)
       +         con->bottom_y = con->y + rects[i].h;
       +   }
       +   for (   ; i < num_rects; ++i)
       +      rects[i].was_packed = 0;
       +}
       +#endif
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// bitmap baking
       +//
       +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If
       +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy.
       +
       +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context)
       +{
       +   stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context)            ,alloc_context);
       +   int            num_nodes = pw - padding;
       +   stbrp_node    *nodes   = (stbrp_node    *) STBTT_malloc(sizeof(*nodes  ) * num_nodes,alloc_context);
       +
       +   if (context == NULL || nodes == NULL) {
       +      if (context != NULL) STBTT_free(context, alloc_context);
       +      if (nodes   != NULL) STBTT_free(nodes  , alloc_context);
       +      return 0;
       +   }
       +
       +   spc->user_allocator_context = alloc_context;
       +   spc->width = pw;
       +   spc->height = ph;
       +   spc->pixels = pixels;
       +   spc->pack_info = context;
       +   spc->nodes = nodes;
       +   spc->padding = padding;
       +   spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
       +   spc->h_oversample = 1;
       +   spc->v_oversample = 1;
       +
       +   stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
       +
       +   if (pixels)
       +      STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
       +
       +   return 1;
       +}
       +
       +STBTT_DEF void stbtt_PackEnd  (stbtt_pack_context *spc)
       +{
       +   STBTT_free(spc->nodes    , spc->user_allocator_context);
       +   STBTT_free(spc->pack_info, spc->user_allocator_context);
       +}
       +
       +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample)
       +{
       +   STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE);
       +   STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE);
       +   if (h_oversample <= STBTT_MAX_OVERSAMPLE)
       +      spc->h_oversample = h_oversample;
       +   if (v_oversample <= STBTT_MAX_OVERSAMPLE)
       +      spc->v_oversample = v_oversample;
       +}
       +
       +#define STBTT__OVER_MASK  (STBTT_MAX_OVERSAMPLE-1)
       +
       +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
       +{
       +   unsigned char buffer[STBTT_MAX_OVERSAMPLE];
       +   int safe_w = w - kernel_width;
       +   int j;
       +   STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
       +   for (j=0; j < h; ++j) {
       +      int i;
       +      unsigned int total;
       +      STBTT_memset(buffer, 0, kernel_width);
       +
       +      total = 0;
       +
       +      // make kernel_width a constant in common cases so compiler can optimize out the divide
       +      switch (kernel_width) {
       +         case 2:
       +            for (i=0; i <= safe_w; ++i) {
       +               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
       +               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
       +               pixels[i] = (unsigned char) (total / 2);
       +            }
       +            break;
       +         case 3:
       +            for (i=0; i <= safe_w; ++i) {
       +               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
       +               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
       +               pixels[i] = (unsigned char) (total / 3);
       +            }
       +            break;
       +         case 4:
       +            for (i=0; i <= safe_w; ++i) {
       +               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
       +               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
       +               pixels[i] = (unsigned char) (total / 4);
       +            }
       +            break;
       +         case 5:
       +            for (i=0; i <= safe_w; ++i) {
       +               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
       +               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
       +               pixels[i] = (unsigned char) (total / 5);
       +            }
       +            break;
       +         default:
       +            for (i=0; i <= safe_w; ++i) {
       +               total += pixels[i] - buffer[i & STBTT__OVER_MASK];
       +               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i];
       +               pixels[i] = (unsigned char) (total / kernel_width);
       +            }
       +            break;
       +      }
       +
       +      for (; i < w; ++i) {
       +         STBTT_assert(pixels[i] == 0);
       +         total -= buffer[i & STBTT__OVER_MASK];
       +         pixels[i] = (unsigned char) (total / kernel_width);
       +      }
       +
       +      pixels += stride_in_bytes;
       +   }
       +}
       +
       +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width)
       +{
       +   unsigned char buffer[STBTT_MAX_OVERSAMPLE];
       +   int safe_h = h - kernel_width;
       +   int j;
       +   STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze
       +   for (j=0; j < w; ++j) {
       +      int i;
       +      unsigned int total;
       +      STBTT_memset(buffer, 0, kernel_width);
       +
       +      total = 0;
       +
       +      // make kernel_width a constant in common cases so compiler can optimize out the divide
       +      switch (kernel_width) {
       +         case 2:
       +            for (i=0; i <= safe_h; ++i) {
       +               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
       +               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
       +               pixels[i*stride_in_bytes] = (unsigned char) (total / 2);
       +            }
       +            break;
       +         case 3:
       +            for (i=0; i <= safe_h; ++i) {
       +               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
       +               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
       +               pixels[i*stride_in_bytes] = (unsigned char) (total / 3);
       +            }
       +            break;
       +         case 4:
       +            for (i=0; i <= safe_h; ++i) {
       +               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
       +               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
       +               pixels[i*stride_in_bytes] = (unsigned char) (total / 4);
       +            }
       +            break;
       +         case 5:
       +            for (i=0; i <= safe_h; ++i) {
       +               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
       +               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
       +               pixels[i*stride_in_bytes] = (unsigned char) (total / 5);
       +            }
       +            break;
       +         default:
       +            for (i=0; i <= safe_h; ++i) {
       +               total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK];
       +               buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes];
       +               pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
       +            }
       +            break;
       +      }
       +
       +      for (; i < h; ++i) {
       +         STBTT_assert(pixels[i*stride_in_bytes] == 0);
       +         total -= buffer[i & STBTT__OVER_MASK];
       +         pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width);
       +      }
       +
       +      pixels += 1;
       +   }
       +}
       +
       +static float stbtt__oversample_shift(int oversample)
       +{
       +   if (!oversample)
       +      return 0.0f;
       +
       +   // The prefilter is a box filter of width "oversample",
       +   // which shifts phase by (oversample - 1)/2 pixels in
       +   // oversampled space. We want to shift in the opposite
       +   // direction to counter this.
       +   return (float)-(oversample - 1) / (2.0f * (float)oversample);
       +}
       +
       +// rects array must be big enough to accommodate all characters in the given ranges
       +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
       +{
       +   int i,j,k;
       +
       +   k=0;
       +   for (i=0; i < num_ranges; ++i) {
       +      float fh = ranges[i].font_size;
       +      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
       +      ranges[i].h_oversample = (unsigned char) spc->h_oversample;
       +      ranges[i].v_oversample = (unsigned char) spc->v_oversample;
       +      for (j=0; j < ranges[i].num_chars; ++j) {
       +         int x0,y0,x1,y1;
       +         int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
       +         int glyph = stbtt_FindGlyphIndex(info, codepoint);
       +         stbtt_GetGlyphBitmapBoxSubpixel(info,glyph,
       +                                         scale * spc->h_oversample,
       +                                         scale * spc->v_oversample,
       +                                         0,0,
       +                                         &x0,&y0,&x1,&y1);
       +         rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1);
       +         rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1);
       +         ++k;
       +      }
       +   }
       +
       +   return k;
       +}
       +
       +// rects array must be big enough to accommodate all characters in the given ranges
       +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects)
       +{
       +   int i,j,k, return_value = 1;
       +
       +   // save current values
       +   int old_h_over = spc->h_oversample;
       +   int old_v_over = spc->v_oversample;
       +
       +   k = 0;
       +   for (i=0; i < num_ranges; ++i) {
       +      float fh = ranges[i].font_size;
       +      float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh);
       +      float recip_h,recip_v,sub_x,sub_y;
       +      spc->h_oversample = ranges[i].h_oversample;
       +      spc->v_oversample = ranges[i].v_oversample;
       +      recip_h = 1.0f / spc->h_oversample;
       +      recip_v = 1.0f / spc->v_oversample;
       +      sub_x = stbtt__oversample_shift(spc->h_oversample);
       +      sub_y = stbtt__oversample_shift(spc->v_oversample);
       +      for (j=0; j < ranges[i].num_chars; ++j) {
       +         stbrp_rect *r = &rects[k];
       +         if (r->was_packed) {
       +            stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
       +            int advance, lsb, x0,y0,x1,y1;
       +            int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j];
       +            int glyph = stbtt_FindGlyphIndex(info, codepoint);
       +            stbrp_coord pad = (stbrp_coord) spc->padding;
       +
       +            // pad on left and top
       +            r->x += pad;
       +            r->y += pad;
       +            r->w -= pad;
       +            r->h -= pad;
       +            stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb);
       +            stbtt_GetGlyphBitmapBox(info, glyph,
       +                                    scale * spc->h_oversample,
       +                                    scale * spc->v_oversample,
       +                                    &x0,&y0,&x1,&y1);
       +            stbtt_MakeGlyphBitmapSubpixel(info,
       +                                          spc->pixels + r->x + r->y*spc->stride_in_bytes,
       +                                          r->w - spc->h_oversample+1,
       +                                          r->h - spc->v_oversample+1,
       +                                          spc->stride_in_bytes,
       +                                          scale * spc->h_oversample,
       +                                          scale * spc->v_oversample,
       +                                          0,0,
       +                                          glyph);
       +
       +            if (spc->h_oversample > 1)
       +               stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
       +                                  r->w, r->h, spc->stride_in_bytes,
       +                                  spc->h_oversample);
       +
       +            if (spc->v_oversample > 1)
       +               stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes,
       +                                  r->w, r->h, spc->stride_in_bytes,
       +                                  spc->v_oversample);
       +
       +            bc->x0       = (stbtt_int16)  r->x;
       +            bc->y0       = (stbtt_int16)  r->y;
       +            bc->x1       = (stbtt_int16) (r->x + r->w);
       +            bc->y1       = (stbtt_int16) (r->y + r->h);
       +            bc->xadvance =                scale * advance;
       +            bc->xoff     =       (float)  x0 * recip_h + sub_x;
       +            bc->yoff     =       (float)  y0 * recip_v + sub_y;
       +            bc->xoff2    =                (x0 + r->w) * recip_h + sub_x;
       +            bc->yoff2    =                (y0 + r->h) * recip_v + sub_y;
       +         } else {
       +            return_value = 0; // if any fail, report failure
       +         }
       +
       +         ++k;
       +      }
       +   }
       +
       +   // restore original values
       +   spc->h_oversample = old_h_over;
       +   spc->v_oversample = old_v_over;
       +
       +   return return_value;
       +}
       +
       +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects)
       +{
       +   stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects);
       +}
       +
       +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges)
       +{
       +   stbtt_fontinfo info;
       +   int i,j,n, return_value = 1;
       +   //stbrp_context *context = (stbrp_context *) spc->pack_info;
       +   stbrp_rect    *rects;
       +
       +   // flag all characters as NOT packed
       +   for (i=0; i < num_ranges; ++i)
       +      for (j=0; j < ranges[i].num_chars; ++j)
       +         ranges[i].chardata_for_range[j].x0 =
       +         ranges[i].chardata_for_range[j].y0 =
       +         ranges[i].chardata_for_range[j].x1 =
       +         ranges[i].chardata_for_range[j].y1 = 0;
       +
       +   n = 0;
       +   for (i=0; i < num_ranges; ++i)
       +      n += ranges[i].num_chars;
       +         
       +   rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
       +   if (rects == NULL)
       +      return 0;
       +
       +   info.userdata = spc->user_allocator_context;
       +   stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
       +
       +   n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects);
       +
       +   stbtt_PackFontRangesPackRects(spc, rects, n);
       +  
       +   return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects);
       +
       +   STBTT_free(rects, spc->user_allocator_context);
       +   return return_value;
       +}
       +
       +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size,
       +            int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range)
       +{
       +   stbtt_pack_range range;
       +   range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range;
       +   range.array_of_unicode_codepoints = NULL;
       +   range.num_chars                   = num_chars_in_range;
       +   range.chardata_for_range          = chardata_for_range;
       +   range.font_size                   = font_size;
       +   return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1);
       +}
       +
       +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer)
       +{
       +   float ipw = 1.0f / pw, iph = 1.0f / ph;
       +   stbtt_packedchar *b = chardata + char_index;
       +
       +   if (align_to_integer) {
       +      float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f);
       +      float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f);
       +      q->x0 = x;
       +      q->y0 = y;
       +      q->x1 = x + b->xoff2 - b->xoff;
       +      q->y1 = y + b->yoff2 - b->yoff;
       +   } else {
       +      q->x0 = *xpos + b->xoff;
       +      q->y0 = *ypos + b->yoff;
       +      q->x1 = *xpos + b->xoff2;
       +      q->y1 = *ypos + b->yoff2;
       +   }
       +
       +   q->s0 = b->x0 * ipw;
       +   q->t0 = b->y0 * iph;
       +   q->s1 = b->x1 * ipw;
       +   q->t1 = b->y1 * iph;
       +
       +   *xpos += b->xadvance;
       +}
       +
       +
       +//////////////////////////////////////////////////////////////////////////////
       +//
       +// font name matching -- recommended not to use this
       +//
       +
       +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
       +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) 
       +{
       +   stbtt_int32 i=0;
       +
       +   // convert utf16 to utf8 and compare the results while converting
       +   while (len2) {
       +      stbtt_uint16 ch = s2[0]*256 + s2[1];
       +      if (ch < 0x80) {
       +         if (i >= len1) return -1;
       +         if (s1[i++] != ch) return -1;
       +      } else if (ch < 0x800) {
       +         if (i+1 >= len1) return -1;
       +         if (s1[i++] != 0xc0 + (ch >> 6)) return -1;
       +         if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;
       +      } else if (ch >= 0xd800 && ch < 0xdc00) {
       +         stbtt_uint32 c;
       +         stbtt_uint16 ch2 = s2[2]*256 + s2[3];
       +         if (i+3 >= len1) return -1;
       +         c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;
       +         if (s1[i++] != 0xf0 + (c >> 18)) return -1;
       +         if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;
       +         if (s1[i++] != 0x80 + ((c >>  6) & 0x3f)) return -1;
       +         if (s1[i++] != 0x80 + ((c      ) & 0x3f)) return -1;
       +         s2 += 2; // plus another 2 below
       +         len2 -= 2;
       +      } else if (ch >= 0xdc00 && ch < 0xe000) {
       +         return -1;
       +      } else {
       +         if (i+2 >= len1) return -1;
       +         if (s1[i++] != 0xe0 + (ch >> 12)) return -1;
       +         if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;
       +         if (s1[i++] != 0x80 + ((ch     ) & 0x3f)) return -1;
       +      }
       +      s2 += 2;
       +      len2 -= 2;
       +   }
       +   return i;
       +}
       +
       +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) 
       +{
       +   return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);
       +}
       +
       +// returns results in whatever encoding you request... but note that 2-byte encodings
       +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
       +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
       +{
       +   stbtt_int32 i,count,stringOffset;
       +   stbtt_uint8 *fc = font->data;
       +   stbtt_uint32 offset = font->fontstart;
       +   stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");
       +   if (!nm) return NULL;
       +
       +   count = ttUSHORT(fc+nm+2);
       +   stringOffset = nm + ttUSHORT(fc+nm+4);
       +   for (i=0; i < count; ++i) {
       +      stbtt_uint32 loc = nm + 6 + 12 * i;
       +      if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)
       +          && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {
       +         *length = ttUSHORT(fc+loc+8);
       +         return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10));
       +      }
       +   }
       +   return NULL;
       +}
       +
       +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)
       +{
       +   stbtt_int32 i;
       +   stbtt_int32 count = ttUSHORT(fc+nm+2);
       +   stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);
       +
       +   for (i=0; i < count; ++i) {
       +      stbtt_uint32 loc = nm + 6 + 12 * i;
       +      stbtt_int32 id = ttUSHORT(fc+loc+6);
       +      if (id == target_id) {
       +         // find the encoding
       +         stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);
       +
       +         // is this a Unicode encoding?
       +         if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {
       +            stbtt_int32 slen = ttUSHORT(fc+loc+8);
       +            stbtt_int32 off = ttUSHORT(fc+loc+10);
       +
       +            // check if there's a prefix match
       +            stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);
       +            if (matchlen >= 0) {
       +               // check for target_id+1 immediately following, with same encoding & language
       +               if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {
       +                  slen = ttUSHORT(fc+loc+12+8);
       +                  off = ttUSHORT(fc+loc+12+10);
       +                  if (slen == 0) {
       +                     if (matchlen == nlen)
       +                        return 1;
       +                  } else if (matchlen < nlen && name[matchlen] == ' ') {
       +                     ++matchlen;
       +                     if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
       +                        return 1;
       +                  }
       +               } else {
       +                  // if nothing immediately following
       +                  if (matchlen == nlen)
       +                     return 1;
       +               }
       +            }
       +         }
       +
       +         // @TODO handle other encodings
       +      }
       +   }
       +   return 0;
       +}
       +
       +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)
       +{
       +   stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name);
       +   stbtt_uint32 nm,hd;
       +   if (!stbtt__isfont(fc+offset)) return 0;
       +
       +   // check italics/bold/underline flags in macStyle...
       +   if (flags) {
       +      hd = stbtt__find_table(fc, offset, "head");
       +      if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;
       +   }
       +
       +   nm = stbtt__find_table(fc, offset, "name");
       +   if (!nm) return 0;
       +
       +   if (flags) {
       +      // if we checked the macStyle flags, then just check the family and ignore the subfamily
       +      if (stbtt__matchpair(fc, nm, name, nlen, 16, -1))  return 1;
       +      if (stbtt__matchpair(fc, nm, name, nlen,  1, -1))  return 1;
       +      if (stbtt__matchpair(fc, nm, name, nlen,  3, -1))  return 1;
       +   } else {
       +      if (stbtt__matchpair(fc, nm, name, nlen, 16, 17))  return 1;
       +      if (stbtt__matchpair(fc, nm, name, nlen,  1,  2))  return 1;
       +      if (stbtt__matchpair(fc, nm, name, nlen,  3, -1))  return 1;
       +   }
       +
       +   return 0;
       +}
       +
       +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags)
       +{
       +   stbtt_int32 i;
       +   for (i=0;;++i) {
       +      stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);
       +      if (off < 0) return off;
       +      if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))
       +         return off;
       +   }
       +}
       +
       +#if defined(__GNUC__) || defined(__clang__)
       +#pragma GCC diagnostic push
       +#pragma GCC diagnostic ignored "-Wcast-qual"
       +#endif
       +
       +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset,
       +                                float pixel_height, unsigned char *pixels, int pw, int ph,
       +                                int first_char, int num_chars, stbtt_bakedchar *chardata)
       +{
       +   return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata);
       +}
       +
       +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index)
       +{
       +   return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index);   
       +}
       +
       +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data)
       +{
       +   return stbtt_GetNumberOfFonts_internal((unsigned char *) data);
       +}
       +
       +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset)
       +{
       +   return stbtt_InitFont_internal(info, (unsigned char *) data, offset);
       +}
       +
       +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags)
       +{
       +   return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags);
       +}
       +
       +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
       +{
       +   return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2);
       +}
       +
       +#if defined(__GNUC__) || defined(__clang__)
       +#pragma GCC diagnostic pop
       +#endif
       +
       +#endif // STB_TRUETYPE_IMPLEMENTATION
       +
       +
       +// FULL VERSION HISTORY
       +//
       +//   1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual
       +//   1.11 (2016-04-02) fix unused-variable warning
       +//   1.10 (2016-04-02) allow user-defined fabs() replacement
       +//                     fix memory leak if fontsize=0.0
       +//                     fix warning from duplicate typedef
       +//   1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges
       +//   1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges
       +//   1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;
       +//                     allow PackFontRanges to pack and render in separate phases;
       +//                     fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);
       +//                     fixed an assert() bug in the new rasterizer
       +//                     replace assert() with STBTT_assert() in new rasterizer
       +//   1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine)
       +//                     also more precise AA rasterizer, except if shapes overlap
       +//                     remove need for STBTT_sort
       +//   1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC
       +//   1.04 (2015-04-15) typo in example
       +//   1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes
       +//   1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++
       +//   1.01 (2014-12-08) fix subpixel position when oversampling to exactly match
       +//                        non-oversampled; STBTT_POINT_SIZE for packed case only
       +//   1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling
       +//   0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg)
       +//   0.9  (2014-08-07) support certain mac/iOS fonts without an MS platformID
       +//   0.8b (2014-07-07) fix a warning
       +//   0.8  (2014-05-25) fix a few more warnings
       +//   0.7  (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back
       +//   0.6c (2012-07-24) improve documentation
       +//   0.6b (2012-07-20) fix a few more warnings
       +//   0.6  (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels,
       +//                        stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty
       +//   0.5  (2011-12-09) bugfixes:
       +//                        subpixel glyph renderer computed wrong bounding box
       +//                        first vertex of shape can be off-curve (FreeSans)
       +//   0.4b (2011-12-03) fixed an error in the font baking example
       +//   0.4  (2011-12-01) kerning, subpixel rendering (tor)
       +//                    bugfixes for:
       +//                        codepoint-to-glyph conversion using table fmt=12
       +//                        codepoint-to-glyph conversion using table fmt=4
       +//                        stbtt_GetBakedQuad with non-square texture (Zer)
       +//                    updated Hello World! sample to use kerning and subpixel
       +//                    fixed some warnings
       +//   0.3  (2009-06-24) cmap fmt=12, compound shapes (MM)
       +//                    userdata, malloc-from-userdata, non-zero fill (stb)
       +//   0.2  (2009-03-11) Fix unsigned/signed char warnings
       +//   0.1  (2009-03-09) First public release
       +//
       +#endif
   DIR diff --git a/temp b/temp
       t@@ -0,0 +1 @@
       +https://tronche.com/gui/x/xlib/window-information/XGetGeometry.html
   DIR diff --git a/test1.c b/test1.c
       t@@ -2,39 +2,48 @@
        
        void bob1(void)
        {
       -    printf("bob\n");
       +        printf("bob\n");
        }
        
        void bob2(void *widget, XEvent event)
        {
       -    LtkButton *button = widget;
       -    if (button->widget.state == LTK_HOVERACTIVE)
       -    {
       -        ltk_quit();
       -    }
       +        LtkButton *button = widget;
       +        if (button->widget.state == LTK_HOVERACTIVE) {
       +                ltk_quit();
       +        }
        }
        
        int main(int argc, char *argv[])
        {
       -    ltk_init("themes/default.json");
       -    LtkWindow *window1 = ltk_create_window("Cool Window!", 0, 0, 500, 500);
       -    LtkWindow *window2 = ltk_create_window("Cool Window!", 0, 0, 500, 500);
       -    LtkGrid *grid1 = ltk_create_grid(window1, 2, 2);
       -    window1->root_widget = grid1;
       -    ltk_set_row_weight(grid1, 0, 1);
       -    ltk_set_row_weight(grid1, 1, 1);
       -    ltk_set_column_weight(grid1, 0, 1);
       -    ltk_set_column_weight(grid1, 1, 1);
       -    /* Test callback functions */
       -    LtkButton *button1 = ltk_create_button(window1, "I'm a button!", &bob1);
       -    ltk_grid_widget(button1, grid1, 0, 0, 1, 1, LTK_STICKY_LEFT | LTK_STICKY_RIGHT);
       -    /* Test manual callback functions */
       -    LtkButton *button2 = ltk_create_button(window1, "I'm a button!", NULL);
       -    button2->widget.mouse_release = &bob2;
       -    ltk_grid_widget(button2, grid1, 0, 1, 1, 1, LTK_STICKY_TOP | LTK_STICKY_BOTTOM);
       -    LtkButton *button3 = ltk_create_button(window1, "I'm a button!", NULL);
       -    ltk_grid_widget(button3, grid1, 1, 0, 1, 1, LTK_STICKY_TOP | LTK_STICKY_BOTTOM | LTK_STICKY_RIGHT);
       -    LtkButton *button4 = ltk_create_button(window1, "I'm a button!", NULL);
       -    ltk_grid_widget(button4, grid1, 1, 1, 1, 1, LTK_STICKY_LEFT | LTK_STICKY_BOTTOM);
       -    ltk_mainloop();
       +        ltk_init("themes/default.ini");
       +        LtkWindow *window1 =
       +            ltk_create_window("Cool Window!", 0, 0, 500, 500);
       +/*    LtkWindow *window2 = ltk_create_window("Cool Window!", 0, 0, 500, 500);*/
       +        LtkGrid *grid1 = ltk_create_grid(window1, 2, 2);
       +        window1->root_widget = grid1;
       +        ltk_set_row_weight(grid1, 0, 1);
       +        ltk_set_row_weight(grid1, 1, 1);
       +        ltk_set_column_weight(grid1, 0, 1);
       +        ltk_set_column_weight(grid1, 1, 1);
       +        /* Test callback functions */
       +        LtkButton *button1 =
       +            ltk_create_button(window1, "I'm a button!", &bob1);
       +        ltk_grid_widget(button1, grid1, 0, 0, 1, 1,
       +                        LTK_STICKY_LEFT | LTK_STICKY_RIGHT);
       +        /* Test manual callback functions */
       +        LtkButton *button2 =
       +            ltk_create_button(window1, "I'm a button!", NULL);
       +        button2->widget.mouse_release = &bob2;
       +        ltk_grid_widget(button2, grid1, 0, 1, 1, 1,
       +                        LTK_STICKY_TOP | LTK_STICKY_BOTTOM);
       +        LtkButton *button3 =
       +            ltk_create_button(window1, "I'm a button!", NULL);
       +        ltk_grid_widget(button3, grid1, 1, 0, 1, 1,
       +                        LTK_STICKY_TOP | LTK_STICKY_BOTTOM |
       +                        LTK_STICKY_RIGHT);
       +        LtkButton *button4 =
       +            ltk_create_button(window1, "I'm a button!", NULL);
       +        ltk_grid_widget(button4, grid1, 1, 1, 1, 1,
       +                        LTK_STICKY_LEFT | LTK_STICKY_BOTTOM);
       +        ltk_mainloop();
        }
   DIR diff --git a/text.c b/text.c
       t@@ -0,0 +1,131 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * 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 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.
       + */
       +
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <stdint.h>
       +#include "text.h"
       +
       +uint32_t u8_nextmemchar(const char *s, size_t *i)
       +{
       +    uint32_t ch = 0;
       +    size_t sz = 0;
       +    do {
       +        ch <<= 6;
       +        ch += (unsigned char)s[(*i)++];
       +        sz++;
       +    } while (!isutf(s[*i]));
       +    ch -= offsetsFromUTF8[sz-1];
       +
       +    return ch;
       +}
       +
       +stbtt_fontinfo ltk_load_font(const char *path)
       +{
       +        FILE *f;
       +        long len;
       +        char *contents;
       +        stbtt_fontinfo info;
       +        f = fopen(path, "rb");
       +        fseek(f, 0, SEEK_END);
       +        len = ftell(f);
       +        fseek(f, 0, SEEK_SET);
       +        contents = malloc(len + 1);
       +        fread(contents, 1, len, f);
       +        contents[len] = '\0';
       +        fclose(f);
       +        if (!stbtt_InitFont(&info, contents, 0))
       +        {
       +                fprintf(stderr, "Failed to load font %s\n", path);
       +                exit(1);
       +        }
       +        return info;
       +}
       +
       +int ltk_text_width(uint8_t *text, stbtt_fontinfo fontinfo, int height)
       +{
       +        float scale = stbtt_ScaleForPixelHeight(&fontinfo, height);
       +        size_t i = 0;
       +        int length = strlen(text);
       +        if (length < 1) return 0;
       +        int temp_x;
       +        int kern_advance;
       +        int width = 0;
       +        uint32_t char1;
       +        uint32_t char2;
       +        char1 = u8_nextmemchar(text, &i);
       +        while(i <= length)
       +        {
       +                stbtt_GetCodepointHMetrics(&fontinfo, char1, &temp_x, 0);
       +                width += temp_x * scale;
       +
       +                char2 = u8_nextmemchar(text, &i);
       +                if (!char2) break;
       +                kern_advance = stbtt_GetCodepointKernAdvance(&fontinfo, char1, char2);
       +                width += kern_advance * scale;
       +                char1 = char2;
       +        }
       +
       +        return width;
       +}
       +
       +unsigned char *ltk_render_text(uint8_t *text, stbtt_fontinfo fontinfo, int height, int width)
       +{
       +/*        int width = ltk_text_width(text, fontinfo, height);*/
       +        unsigned char *bitmap = calloc(sizeof(char), width * height);
       +        float scale = stbtt_ScaleForPixelHeight(&fontinfo, height);
       +
       +        int ascent, descent, line_gap;
       +        stbtt_GetFontVMetrics(&fontinfo, &ascent, &descent, &line_gap);
       +        ascent *= scale;
       +        descent *= scale;
       +
       +        size_t i = 0;
       +        int length = strlen(text);
       +        if (length < 1)
       +        {
       +                printf("WARNING: ltk_render_text: length of text is less than 1.\n");
       +                return bitmap;
       +        }
       +        uint32_t char1, char2;
       +        char1 = u8_nextmemchar(text, &i);
       +        int ax, x = 0, y, x1, y1, x2, y2, byte_offset, kern_advance;
       +        while (i <= length)
       +        {
       +                stbtt_GetCodepointBitmapBox(&fontinfo, char1, scale, scale, &x1, &y1, &x2, &y2);
       +                y = ascent + y1;
       +                byte_offset = x + (y  * width);
       +                stbtt_MakeCodepointBitmap(&fontinfo, bitmap + byte_offset, x2 - x1, y2 - y1, width, scale, scale, char1);
       +
       +                stbtt_GetCodepointHMetrics(&fontinfo, char1, &ax, 0);
       +                x += ax * scale;
       +
       +                char2 = u8_nextmemchar(text, &i);
       +                if (!char2) break;
       +                kern_advance = stbtt_GetCodepointKernAdvance(&fontinfo, char1, char2);
       +                x += kern_advance * scale;
       +                char1 = char2;
       +        }
       +
       +        return bitmap;
       +}
   DIR diff --git a/text.c.bak b/text.c.bak
       t@@ -0,0 +1,161 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * 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 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.
       + */
       +
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <stdint.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#include "text.h"
       +
       +uint32_t u8_nextmemchar(const char *s, size_t *i)
       +{
       +    uint32_t ch = 0;
       +    size_t sz = 0;
       +    do {
       +        ch <<= 6;
       +        ch += (unsigned char)s[(*i)++];
       +        sz++;
       +    } while (!isutf(s[*i]));
       +    ch -= offsetsFromUTF8[sz-1];
       +
       +    return ch;
       +}
       +
       +stbtt_fontinfo ltk_load_font(const char *path)
       +{
       +        FILE *f;
       +        long len;
       +        char *contents;
       +        stbtt_fontinfo info;
       +        f = fopen(path, "rb");
       +        fseek(f, 0, SEEK_END);
       +        len = ftell(f);
       +        fseek(f, 0, SEEK_SET);
       +        contents = malloc(len + 1);
       +        fread(contents, 1, len, f);
       +        contents[len] = '\0';
       +        fclose(f);
       +        if (!stbtt_InitFont(&info, contents, 0))
       +        {
       +                fprintf(stderr, "Failed to load font %s\n", path);
       +                exit(1);
       +        }
       +        return info;
       +}
       +
       +int ltk_text_width(uint8_t *text, stbtt_fontinfo fontinfo, int height)
       +{
       +        float scale = stbtt_ScaleForPixelHeight(&fontinfo, height);
       +        size_t i = 0;
       +        int length = strlen(text);
       +        if (length < 1) return 0;
       +        int temp_x;
       +        int kern_advance;
       +        int width = 0;
       +        uint32_t char1;
       +        uint32_t char2;
       +        char1 = u8_nextmemchar(text, &i);
       +        while(i <= length)
       +        {
       +                stbtt_GetCodepointHMetrics(&fontinfo, char1, &temp_x, 0);
       +                width += temp_x * scale;
       +
       +                char2 = u8_nextmemchar(text, &i);
       +                if (!char2) break;
       +                kern_advance = stbtt_GetCodepointKernAdvance(&fontinfo, char1, char2);
       +                width += kern_advance * scale;
       +                char1 = char2;
       +        }
       +
       +        return width;
       +}
       +
       +Pixmap ltk_render_text(
       +        Display *display,
       +        Window window,
       +        GC gc,
       +        uint8_t *text,
       +        stbtt_fontinfo fontinfo,
       +        int height,
       +        unsigned long fg,
       +        unsigned long bg)
       +{
       +        XWindowAttributes attrs;
       +        XGetWindowAttributes(display, window, &attrs);
       +        int depth = attrs.depth;
       +
       +        int width = ltk_text_width(text, fontinfo, height);
       +        unsigned char *bitmap = calloc(sizeof(char), width * height);
       +        float scale = stbtt_ScaleForPixelHeight(&fontinfo, height);
       +
       +        int ascent, descent, line_gap;
       +        stbtt_GetFontVMetrics(&fontinfo, &ascent, &descent, &line_gap);
       +        ascent *= scale;
       +        descent *= scale;
       +
       +        size_t i = 0;
       +        int length = strlen(text);
       +        if (length < 1)
       +        {
       +                printf("WARNING: ltk_render_text: length of text is less than 1.\n");
       +                return XCreatePixmap(display, window, 0, 0, depth);
       +        }
       +        uint32_t char1, char2;
       +        char1 = u8_nextmemchar(text, &i);
       +        int ax, x = 0, y, x1, y1, x2, y2, byte_offset, kern_advance;
       +        while (i <= length)
       +        {
       +                stbtt_GetCodepointBitmapBox(&fontinfo, char1, scale, scale, &x1, &y1, &x2, &y2);
       +                y = ascent + y1;
       +                byte_offset = x + (y  * width);
       +                stbtt_MakeCodepointBitmap(&fontinfo, bitmap + byte_offset, x2 - x1, y2 - y1, width, scale, scale, char1);
       +
       +                stbtt_GetCodepointHMetrics(&fontinfo, char1, &ax, 0);
       +                x += ax * scale;
       +
       +                char2 = u8_nextmemchar(text, &i);
       +                if (!char2) break;
       +                kern_advance = stbtt_GetCodepointKernAdvance(&fontinfo, char1, char2);
       +                x += kern_advance * scale;
       +                char1 = char2;
       +        }
       +
       +        /* TODO: separate this into a separate function so that one function only creates
       +         * the bitmap and other functions to turn that into a pixmap with solid color
       +         * background, image background, etc.
       +         */
       +        Pixmap rendered = XCreatePixmap(display, window, width, height, depth);
       +        XSetForeground(display, gc, bg);
       +        for (int i = 0; i < height; i++)
       +        {
       +                for (int j = 0; j < width; j++)
       +                {
       +                        /* Yay! Magic! */
       +                        XSetForeground(display, gc, (bitmap[i * width + j] / 255.0) * fg + ((255 - bitmap[i * width + j]) / 255.0) * bg);
       +                        XDrawPoint(display, rendered, gc, j, i);
       +                }
       +        }
       +        XSetForeground(display, gc, bg);
       +        return rendered;
       +}
   DIR diff --git a/text.h b/text.h
       t@@ -0,0 +1,50 @@
       +/*
       + * This file is part of the Lumidify ToolKit (LTK)
       + * Copyright (c) 2016, 2017 Lumidify Productions <lumidify@openmailbox.org>
       + *
       + * 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 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.
       + */
       +
       +#ifndef _LTK_TEXT_
       +#define _LTK_TEXT_
       +
       +/* These unicode routines are taken from
       + * https://github.com/JeffBezanson/cutef8 */
       +
       +/* is c the start of a utf8 sequence? */
       +#define isutf(c) (((c)&0xC0)!=0x80)
       +
       +static const uint32_t offsetsFromUTF8[6] = {
       +    0x00000000UL, 0x00003080UL, 0x000E2080UL,
       +    0x03C82080UL, 0xFA082080UL, 0x82082080UL
       +};
       +
       +/* next character without NUL character terminator */
       +uint32_t u8_nextmemchar(const char *s, size_t *i);
       +
       +#define STB_TRUETYPE_IMPLEMENTATION
       +#include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
       +
       +stbtt_fontinfo ltk_load_font(const char *path);
       +
       +int ltk_text_width(uint8_t *text, stbtt_fontinfo fontinfo, int height);
       +
       +unsigned char *ltk_render_text(uint8_t *text, stbtt_fontinfo fontinfo, int height, int width);
       +
       +#endif
   DIR diff --git a/text/: b/text/:
       t@@ -0,0 +1,249 @@
       +#include <stdio.h>
       +#include <stdlib.h>
       +#include <stdint.h>
       +#include <limits.h>
       +#include <X11/Xlib.h>
       +#include <X11/Xutil.h>
       +#include <harfbuzz/hb.h>
       +#include <harfbuzz/hb-ot.h>
       +#define STB_TRUETYPE_IMPLEMENTATION
       +#include "stb_truetype.h" /* http://nothings.org/stb/stb_truetype.h */
       +
       +char *
       +ltk_load_file(const char *path, unsigned long *len)
       +{
       +        FILE *f;
       +        char *contents;
       +        f = fopen(path, "rb");
       +        fseek(f, 0, SEEK_END);
       +        *len = ftell(f);
       +        fseek(f, 0, SEEK_SET);
       +        contents = malloc(*len + 1);
       +        fread(contents, 1, *len, f);
       +        contents[*len] = '\0';
       +        fclose(f);
       +        return contents;
       +}
       +
       +unsigned long
       +ltk_blend_pixel(Display *display, Colormap colormap, XColor fg, XColor bg, double a)
       +{
       +        if (a >= 1) {
       +                return fg.pixel;
       +        } else if (a == 0.0) {
       +                return bg.pixel;
       +        }
       +
       +        XColor blended;
       +        blended.red = (int)((fg.red - bg.red) * a + bg.red);
       +        blended.green = (int)((fg.green - bg.green) * a + bg.green);
       +        blended.blue = (int)((fg.blue - bg.blue) * a + bg.blue);
       +        XAllocColor(display, colormap, &blended);
       +
       +        return blended.pixel;
       +}
       +
       +typedef struct {
       +        stbtt_fontinfo font_info;
       +        hb_font_t *font;
       +} LtkFont;
       +
       +LtkFont *
       +ltk_load_font(char *path)
       +{
       +        long len;
       +        LtkFont *font = malloc(sizeof(LtkFont));
       +        if (!font) {
       +                fprintf(stderr, "Out of memory!\n");
       +                exit(1);
       +        }
       +        char *contents = ltk_load_file(path, &len);
       +        if (!stbtt_InitFont(&font->font_info, contents, 0))
       +        {
       +                fprintf(stderr, "Failed to load font %s\n", path);
       +                exit(1);
       +        }
       +        hb_blob_t *blob = hb_blob_create(contents, len, HB_MEMORY_MODE_READONLY, NULL, NULL);
       +        hb_face_t *face = hb_face_create(blob, 0);
       +        hb_blob_destroy(blob);
       +        font->font = hb_font_create(face);
       +        hb_face_destroy(face);
       +        hb_ot_font_set_funcs(font->font);
       +        return font;
       +}
       +
       +unsigned char *
       +ltk_render_text_bitmap(
       +        uint8_t *text,
       +        LtkFont *font,
       +        int size,
       +        int *width,
       +        int *height)
       +{
       +        /* (x1*, y1*): top left corner (relative to origin and absolute)
       +         * (x2*, y2*): bottom right corner (relative to origin and absolute)
       +         */
       +        int x1, x2, y1, y2, x1_abs, x2_abs, y1_abs, y2_abs, x_abs = 0, y_abs = 0;
       +        int x_min = INT_MAX, x_max = INT_MIN, y_min = INT_MAX, y_max = INT_MIN;
       +        int char_w, char_h, x_off, y_off;
       +
       +        int byte_offset;
       +        int alpha;
       +        unsigned char *bitmap;
       +        unsigned char *char_bitmap;
       +        hb_buffer_t *buf;
       +        hb_glyph_info_t *ginf, *gi;
       +        hb_glyph_position_t *gpos, *gp;
       +        unsigned int text_len = 0;
       +        int text_bytes = strlen(text);
       +        if (text_bytes < 1) {
       +                printf("WARNING: ltk_render_text: length of text is less than 1.\n");
       +                return "";
       +        }
       +
       +        buf = hb_buffer_create();
       +        hb_buffer_set_flags(buf, HB_BUFFER_FLAG_BOT | HB_BUFFER_FLAG_EOT);
       +        hb_buffer_add_utf8(buf, text, text_bytes, 0, text_bytes);
       +        hb_buffer_guess_segment_properties(buf);
       +        hb_shape(font->font, buf, NULL, 0);
       +        ginf = hb_buffer_get_glyph_infos(buf, &text_len);
       +        gpos = hb_buffer_get_glyph_positions(buf, &text_len);
       +        float scale = stbtt_ScaleForMappingEmToPixels(&font->font_info, size);
       +
       +        /* Calculate size of bitmap */
       +        for (int i = 0; i < text_len; i++) {
       +                gi = &ginf[i];
       +                gp = &gpos[i];
       +                stbtt_GetGlyphBitmapBox(&font->font_info, gi->codepoint, scale, scale, &x1, &y1, &x2, &y2);
       +                x1_abs = (int)(x_abs + x1 + gp->x_offset * scale);
       +                y1_abs = (int)(y_abs + y1 - gp->y_offset * scale);
       +                x2_abs = x1_abs + (x2 - x1);
       +                y2_abs = y1_abs + (y2 - y1);
       +                if (x1_abs < x_min) x_min = x1_abs;
       +                if (y1_abs < y_min) y_min = y1_abs;
       +                if (x2_abs > x_max) x_max = x2_abs;
       +                if (y2_abs > y_max) y_max = y2_abs;
       +                x_abs += (gp->x_advance * scale);
       +                y_abs -= (gp->y_advance * scale);
       +        }
       +        x_abs = -x_min;
       +        y_abs = -y_min;
       +        *width = x_max - x_min;
       +        *height = y_max - y_min;
       +        /* FIXME: calloc checks for integer overflow, right? */
       +        /* FIXME: check if null returned */
       +        bitmap = calloc(*width * *width, sizeof(char));
       +        if (!bitmap) {
       +                fprintf(stderr, "Can't allocate memory for bitmap!\n");
       +                exit(1);
       +        }
       +        for (int i = 0; i < text_len; i++) {
       +                gi = &ginf[i];
       +                gp = &gpos[i];
       +                stbtt_GetGlyphBitmapBox(&font->font_info, gi->codepoint, scale, scale, &x1, &y1, &x2, &y2);
       +                char_bitmap = stbtt_GetGlyphBitmap(&font->font_info, scale, scale, gi->codepoint, &char_w, &char_h, &x_off, &y_off);
       +
       +                x1_abs = (int)(x_abs + x1 + gp->x_offset * scale);
       +                y1_abs = (int)(y_abs + y1 - gp->y_offset * scale);
       +                for (int k = 0; k < char_h; k++)
       +                {
       +                        for (int j = 0; j < char_w; j++)
       +                        {
       +                                byte_offset = (y1_abs + k) * *width + x1_abs + j;
       +                                alpha = bitmap[byte_offset] + char_bitmap[k * char_w + j];
       +                                if (alpha < 0) printf("%d|", alpha);
       +                                /* Cap at 255 so char doesn't overflow */
       +                                bitmap[byte_offset] = alpha > 255 ? 255 : alpha;
       +                        }
       +                }
       +                free(char_bitmap);
       +
       +                x_abs += gp->x_advance * scale;
       +                y_abs -= gp->y_advance * scale;
       +        }
       +        return bitmap;
       +}
       +
       +Pixmap
       +ltk_render_text(
       +        Display *dpy,
       +        Window window,
       +        GC gc,
       +        XColor fg,
       +        XColor bg,
       +        Colormap colormap,
       +        char *bitmap,
       +        int width,
       +        int height)
       +{
       +        XWindowAttributes attrs;
       +        XGetWindowAttributes(dpy, window, &attrs);
       +        int depth = attrs.depth;
       +        Pixmap pix = XCreatePixmap(dpy, window, width, height, depth);
       +        XSetForeground(dpy, gc, bg.pixel);
       +        for (int i = 0; i < height; i++) {
       +                for (int j = 0; j < width; j++) {
       +                        XSetForeground(dpy, gc, ltk_blend_pixel(dpy, colormap, fg, bg, bitmap[i * width + j] / 255.0));
       +                        XDrawPoint(dpy, pix, gc, j, i);
       +                }
       +        }
       +        return pix;
       +}
       +
       +int main(int argc, char *argv[])
       +{
       +    Display *display;
       +    int screen;
       +    Window window;
       +    GC gc;
       +
       +    unsigned long black, white;
       +    Colormap colormap;
       +    display = XOpenDisplay((char *)0);
       +    screen = DefaultScreen(display);
       +    colormap = DefaultColormap(display, screen);
       +    black = BlackPixel(display, screen);
       +    white = WhitePixel(display, screen);
       +    window = XCreateSimpleWindow(display, DefaultRootWindow(display), 0, 0, 1366, 512, 0, black, white);
       +    XSetStandardProperties(display, window, "Random Window", NULL, None, NULL, 0, NULL);
       +    XSelectInput(display, window, ExposureMask|ButtonPressMask|KeyPressMask);
       +    gc = XCreateGC(display, window, 0, 0);
       +    XSetBackground(display, gc, black);
       +    XSetForeground(display, gc, black);
       +    XClearWindow(display, window);
       +    XMapRaised(display, window);
       +    XColor c1, c2;
       +    XParseColor(display, colormap, "#FFFFFF", &c1);
       +    XParseColor(display, colormap, "#FF0000", &c2);
       +    XAllocColor(display, colormap, &c1);
       +    XAllocColor(display, colormap, &c2);
       +
       +    LtkFont *font = ltk_load_font("NotoNastaliqUrdu-Regular.ttf");
       +    int w, h;
       +    unsigned char *bitmap = ltk_render_text_bitmap("چشمہ میڈیا", font, 256, &w, &h);
       +    Pixmap pix = ltk_render_text(display, window, gc, c1, c2, colormap, bitmap, w, h);
       +    XCopyArea(display, pix, window, gc, 0, 0, w, h, 0, 0);
       +
       +    XEvent event;
       +    KeySym key;
       +    char text[255];
       +
       +    while(1)
       +    {
       +        XNextEvent(display, &event);
       +        if (event.type == KeyPress && XLookupString(&event.xkey, text, 255, &key, 0) == 1)
       +        {
       +            XCopyArea(display, pix, window, gc, 0, 0, w, h, 0, 0);
       +            if (text[0] == 'q')
       +            {
       +                XFreeGC(display, gc);
       +                XFreeColormap(display, colormap);
       +                XDestroyWindow(display, window);
       +                XCloseDisplay(display);
       +                exit(0);
       +            }
       +        }
       +    }
       +    
       +    return 0;
       +}
   DIR diff --git a/text/Awami_beta3.ttf b/text/Awami_beta3.ttf
       Binary files differ.
   DIR diff --git a/text/FONTLOG.txt b/text/FONTLOG.txt
       t@@ -0,0 +1,284 @@
       +FONTLOG
       +Gentium Plus font family
       +========================
       +
       +
       +This file provides detailed information on the Gentium Plus family of fonts.
       +This information should be distributed along with the Gentium Plus fonts and
       +any derivative works.
       +
       +
       +Basic Font Information
       +----------------------
       +
       +Gentium ("belonging to the nations" in Latin) is a Unicode typeface family 
       +designed to enable the many diverse ethnic groups around the world who use 
       +the Latin script to produce readable, high-quality publications. The 
       +design is intended to be highly readable, reasonably compact, and visually 
       +attractive. Gentium has won a "Certificate of Excellence in Typeface 
       +Design" in two major international typeface design competitions: 
       +bukva:raz! (2001), TDC2003 (2003).
       +
       +The Gentium Plus font family is based on the original design. It currently 
       +comes with regular and italic face only, although additional weights are in
       +development.
       +
       +The goal for this product is to provide a single Unicode-based font family
       +that contains a comprehensive inventory of glyphs needed for almost any
       +Roman- or Cyrillic-based writing system, whether used for phonetic or
       +orthographic needs, and provide a matching Greek face. In addition, there
       +is provision for other characters and symbols useful to linguists. This
       +font makes use of state-of-the-art font technologies to support complex
       +typographic issues, such as the need to position arbitrary combinations
       +of base glyphs and diacritics optimally.
       +
       +Two fonts from this typeface family are included in this release:
       +
       +     * Gentium Plus Regular
       +     * Gentium Plus Italic
       +
       +Work is ongoing to provide bold and bold-italic weights, as well as a
       +complete book-weight family.
       +
       +For detailed documentation see the contents of the 'documentation' folder.
       +
       +
       +A Note Regarding the Open Font License and TypeTuner
       +----------------------------------------------------
       +
       +The OFL prohibits the use of Reserved Font Names "Gentium" and "SIL" in
       +the name of any font that is derived from the Original Version of Gentium
       +Plus. However, SIL International (the Copyright Holder) grants through
       +this separate written agreement the right to use the Reserved Font Names
       +in any Modified Version of this font created by using TypeTuner as long
       +as the feature information contained in the Original Version is used, 
       +unmodified, as the source for TypeTuner feature information, and 
       +"Gentium Plus" is not the resulting font family name.
       +
       +TypeTuner-produced fonts created in this manner are still bound by the
       +terms of the OFL. Specifically, the use of Reserved Font Names is
       +prohibited when making derivatives of a TypeTuner-produced font. Anyone
       +making a derivative of a TypeTuner font will need to find a different
       +name. For example a TypeTuner-produced font may be named 
       +"Gentium Plus Literacy SomeFarAwayPlace" but a derivative of that font made 
       +with other tools would not be allowed to use the "Gentium" and "SIL" 
       +Reserved Font Names and would have to be named something like "Nations Serif".
       +
       +
       +ChangeLog
       +---------
       +(This should list both major and minor changes, most recent first.)
       +
       +27 Oct 2014 (SIL NRSI team) Gentium Plus version 5.000
       +- Added Stylistic Sets to the font for OpenType support 
       +        of previously Graphite-only features
       +- Added Character Variants to the font for OpenType support 
       +        of previously Graphite-only features
       +- Added Serbian feature when Serbian language is turned on
       +- Added hook D variant feature (for U+018A/U+0257)
       +- Removed "Show deprecated PUA" feature
       +- Removed "Romanian-style diacritics" feature 
       +  (because glyphs are now encoded)
       +- Removed "Diacritic selection" feature
       +- Added U+039E, U+03BC, U+03C6 and U+03C9 in the Greek and Coptic block
       +- Added U+0528..U+052F in the Cyrillic Supplement block
       +- Added U+2041 in the General Punctuation block
       +- Added U+2095..U+209C in the Superscripts and Subscripts block
       +- Added U+20B6..U+20BD in the Currency Symbols block
       +- Added U+210C, U+2113, U+2117, U+212D, U+2135, U+214F in the 
       +  Letterlike Symbols block
       +- Added U+2150..U+2152 and U+2189 in the Number Forms block
       +- Added U+2226, U+2234..U+2235, U+2262, U+2282..U+2287 in the 
       +  Mathematical Operators block
       +- Added U+2640, U+2642, U+266D, U+266F in the Miscellaneous Symbols block
       +- Added U+27E8..U+27E9 in the Miscellaneous Mathematical Symbols-A block
       +- Added U+2C7E..U+2C7F in the Latin Extended-C block
       +- Added U+2C88 in the Coptic block
       +- Added U+2E00..U+2E0D, U+2E3A..U+2E3B in the Supplemental 
       +  Punctuation block
       +- Added U+A736..U+A73F, U+A742..U+A74D, U+A750..U+A787, U+A790..U+A7AD, 
       +  U+A7B0..U+A7B1, U+A7F7..U+A7FA in the Latin Extended-D block. 
       +  These were also added to relevant features.
       +- Added U+A92E in the Kayah Li block (to support the Kayah Li language 
       +  when using the Roman script)
       +- Added U+AB64..U+AB65 in the Latin Extended-E block
       +- Added U+1D40C, U+1D504..U+1D505, U+1D50A, U+1D50E..U+1D50F, U+1D514, 
       +  U+1D516..U+1D517, U+1D519 in the Mathematical Alphanumeric Symbols block
       +- Added PUA characters U+F26C (curl J) and U+F26D (left-hook b)
       +- Characters in our PUA that were added to Unicode have had their 
       +  codepoints updated:
       +  F1AD>A7F9, F266>A78E, F26B>A78D, F32C>0526, F32D>0527, F17B>1DFD, F209>2C70
       +- These PUA characters were deprecated (now white on black glyphs): 
       +  U+F17B, U+F1AD, U+F209, U+F247, U+F248, U+F266, U+F26B, U+F32C, U+F32D
       +- Deleted U+0149 as it is officially deprecated in Unicode
       +- Added support for shorter macrons under narrow letters (i,l,r,t).
       +  (This only works for Graphite or using precomposed characters in OpenType.)
       +- Made it possible for saltillo characters (U+A78B and U+A78C) to "carry" 
       +  diacritics
       +- Improved design of U+A722..U+A725 and U+A78D
       +- Refactored all cedilla positioning
       +- Removal of unneeded duplicate glyphs (because of improvements in smart 
       +  font code)
       +- Bug fix in Graphite code to allow for simultaneous selection of Vietnamese 
       +  alternates and Small Caps
       +- Bug fix in Graphite code to allow for simultaneous selection of Ogonek 
       +  alternates and Small Caps
       +- Subscript and Superscript parentheses, minus, plus and equals were raised.
       +        Metrics were not changed.
       +- Adjusted tails on U+2C6B, U+2C6C
       +- Arrowhead design of U+21A8 modified to match the other arrows
       +- Placement of Ogonek revisited
       +- Improved hinting
       +- Version number bumped up to match other SIL Roman fonts
       +- Slight modification to positioning of U+0361 and U+035C
       +- Narrow No-Break Space (U+202F) adjusted to be narrower 
       +  than the No-Break Space (U+00A0)
       +- Changed the strongly curved hooks on Cyrillic U+04C3..U+04C4, U+04C7..U+04C8, 
       +  U+04FC..U+04FD, U+0512..U+0513 to be more consistent with other hooks and also 
       +  reshaped U+0402, U+0452, U+0494, U+0495, U+04A6, U+04A7, U+0520..U+0523 to match
       +- Modified Cyrillic italic versions of U+04AF, U+04B1
       +- Changed postscript names for U+0218 and U+0219
       +- Changed postscript names for U+2203, U+232A and U+2329
       +- "Hide tone contour staves" feature now works with single tonebar
       +- Fixed outline for U+1DBF
       +- Allowed combining marks to render properly with U+02D0
       +- Added U+037F in the Greek and Coptic block
       +- Adjusted U+1FBD so that it is a spacing mark in Graphite
       +- Improved design of U+2C72 and U+2C73 (hook w) for collision avoidance
       +- Adjusted width of U+005F
       +- Adjusted design of U+0264
       +
       +1 Aug 2012 (SIL NRSI team) Gentium Plus version 1.510
       +- Changed Graphite feature identifiers from integers to 4-character 
       +  alphanumeric tags (no other changes)
       +  
       +12 Sep 2011 (SIL NRSI team) Gentium Plus version 1.508
       +- Removed the VDMX table
       +- Changed version number (using ttfsetver)
       +
       +25 Aug 2011 (SIL NRSI team) Gentium Plus version 1.506
       +- Double-encoded the SIL PUA characters which were added to Unicode 5.2 and 6.0
       +  (using ttfremap)
       +- Corrected problem with coverage tables (using ttfsortcover)
       +- Changed version number (using ttfsetver)
       +- Added device metric tables
       +- Added an empty dsig table
       +
       +16 Nov 2010 (SIL NRSI team) Gentium Plus version 1.504
       +- Added codepage bits for 1251 (Cyrillic), 1257 (Windows Baltic), 1258 
       +  (Vietnamese)
       +
       +1 Nov 2010 (SIL NRSI team) Gentium Plus version 1.502
       +- Regular and Italic weights only
       +- New OpenType and Graphite support
       +- Converted kerning to OpenType (but not Graphite)
       +- New extended Cyrillic script support, inc. Serbian alternates
       +- Character set and features up to same level as Charis SIL 4.106
       +- Support for more historic Greek characters and alternate seriffed beta
       +- No separate GentiumAlt fonts (replaced by font features)
       +- Added WOFF versions and examples
       +
       +28 Nov 2005 (Victor Gaultney)  Gentium version 1.02
       +- Changed licensing to the SIL Open Font License
       +- Included FontLab source files
       +- Fixed some duplicate PostScript glyph names
       +- Fixed italic angle
       +
       +19 Sep 2003 (Victor Gaultney)  Gentium version 1.01 
       +- Maintenance release focused on changing internal font
       +- Information to reflect the changeover to an SIL project
       +- There is only one bug fix - the Greek mu PS name was changed to try and fix 
       +a display/printing problem. There is still no manual hinting.
       +
       +16 Sep 2002 (Victor Gaultney)  Gentium version 1.00
       +- First public release
       +- No manual hinting is included in this version. Some has been done - with 
       +good results - but is not yet complete enough.
       +
       +
       +Information for Developers/Contributors
       +---------------------------------------
       +
       +The release of Gentium Plus version 1.502 (and any subsequent versions) under 
       +the OFL license provides a means for people to modify the fonts to meet their
       +needs and contribute to the project. For information on what you're allowed to
       +change or modify, consult the OFL and OFL-FAQ.
       +
       +Anyone can make their own modified version of Gentium Plus (using a different
       +name), but SIL International will continue to maintain and develop the
       +canonical version of the Gentium Plus fonts. As the package maintainer, we
       +welcome contributions. Here are some things to keep in mind:
       +
       +Format: We are open to contributions in various formats, but if you want to
       +maximise the chances of us including your work, please make it available to
       +us (via email or a URL) as either a FontLab database (preferred) or a
       +PostScript Type 1 (or OT-CFF) font.
       +
       +Source files: The primary source files for the fonts are the fonts themselves.
       +They contain all the important data in the fonts and can be studied and
       +modified using open font tools such as FontForge and TTX. The developer
       +release contains additional source files that might be useful. See the file
       +source/SOURCES.txt in that release archive for further information.
       +
       +Copyright attribution: If you submit something for inclusion in the main
       +Gentium Plus fonts, we will ask you to affirm that it is your original work,
       +and ask you to assign the copyright of your work to SIL International. This
       +is to ensure that future releases can be made under improved versions of the
       +OFL without needing to track you down for further permission. This follows
       +the same principle used by the FSF. Keep in mind that we are a
       +not-for-profit organization committed to free/libre and open source
       +software, and that any contributions incorporated in the fonts will always
       +be available under the OFL or a similar license.
       +
       +Quality: Because we want to be able to guarantee a high level of quality for
       +the primary Gentium Plus fonts, we will review submissions carefully. Please
       +don't be discouraged if we do not include a submission for this reason, or
       +ask you to make specific revisions.
       +
       +Types of contributions: If you wish to make a contribution - a set of
       +additional glyphs, scripts, code, etc. - please contact us before you do any
       +work to see if it is a contribution we currently need. Every addition adds
       +to the complexity of the project and needs to be carefully planned. This
       +also avoids two people working on the same type of addition at the same time.
       +
       +Linux packages: Please contact the upstream maintainer of the various 
       +packages - nicolas_spalinger@sil.org - if you want to help package or 
       +maintain a package.
       +
       +When submissions will be included: We plan to revise the fonts when major 
       +updates are needed (eg new versions of Unicode). If you wish to make 
       +submissions, contact us on the timing. 
       +
       +
       +Acknowledgements
       +----------------
       +(Here is where contributors can be acknowledged. If you make modifications be 
       +sure to add your name (N), email (E), web-address (W) and description (D). 
       +This list is sorted by last name in alphabetical order.)
       +
       +N: Victor Gaultney
       +E: victor_gaultney@sil.org
       +W: http://www.sil.org/~gaultney/
       +D: Original Designer
       +
       +N: Annie Olsen
       +E: http://scripts.sil.org/support
       +W: http://scripts.sil.org/
       +D: Contributed some extended Latin glyphs
       +
       +N: Iska Routamaa
       +E: http://scripts.sil.org/support
       +W: http://scripts.sil.org/
       +D: Contributed some extended Latin glyphs and extensive work on the italic face
       +
       +N: SIL font engineers
       +E: http://scripts.sil.org/support
       +W: http://scripts.sil.org/
       +D: Graphite, OpenType, and TypeTuner code, and build support
       +
       +The Gentium and Gentium Plus fonts are maintained by SIL International.
       +
       +For more information please visit the Gentium page on SIL International's 
       +Computers and Writing systems website: http://scripts.sil.org/gentium
       +
       +Support through the website: http://scripts.sil.org/Support
   DIR diff --git a/text/GENTIUM-FAQ.txt b/text/GENTIUM-FAQ.txt
       t@@ -0,0 +1,205 @@
       +GENTIUM-FAQ
       +Gentium Plus
       +========================
       +
       +Here are some answers to frequently asked questions about the Gentium Plus
       +fonts:
       +
       +
       +General
       +========
       +
       +How do you pronounce Gentium?
       +
       +        The preferred pronunciation is with a soft G as in 'general', not a
       +        hard one as in 'gold': JEN-tee-oom.
       +
       +
       +Licensing
       +=========
       +
       +I want to use Gentium Plus in my publication - can I?
       +
       +        Gentium Plus is released under the SIL Open Font License, which permits
       +        use for any publication, whether electronic or printed. For more answers
       +        to use questions see the OFL-FAQ. The license, alongside information
       +        specific to Gentium Plus, is in the release package.
       +
       +I would like to bundle Gentium Plus with my application - can I?
       +
       +        This is our most common question. The SIL Open Font License allows
       +        bundling with applications, even commercial closed source ones, with
       +        some restrictions. See the OFL.txt file and the OFL-FAQ.
       +
       +Can I use the font on my web site?
       +
       +        You can certainly create web pages that request that Gentium Plus be
       +        used to display them (both if that font is already available on the
       +        user's system or if it is delivered via @font-face). According to the
       +        license, you are also allowed to place the font on your site for people
       +        to download it. We would strongly recommend, however, that you direct
       +        users to our site to download the font. This ensures that they are
       +        always using the most recent version with bug fixes, etc. To make this
       +        easier, there is a simple URL for Gentium: http://scripts.sil.org/Gentium
       +        There is further important discussion of webfont issues in the OFL-FAQ.
       +
       +Is Gentium Plus going to stay unrestricted and available at no cost?
       +
       +        There is no intention to ever charge users for using Gentium and its
       +        variants. The current version is licensed under a free/open license and
       +        future versions will be similarly unencumbered.
       +
       +
       +Modification
       +============
       +
       +I would like to modify Gentium Plus to add a couple of characters I need.
       +Can I?
       +
       +        Yes - that is allowed as long as you abide by the conditions of the
       +        SIL Open Font License.
       +
       +So will you add glyphs upon request?
       +
       +        If you have a special symbol that you need (say, for a particular
       +        transcription system), the best means of doing so will be to ensure
       +        that the symbol makes it into the Unicode Standard. It is impossible
       +        for us to add every glyph that every person desires, but we do place
       +        a high priority on adding pretty much anything that falls in certain
       +        Unicode ranges (extended Latin, Greek, Cyrillic). You can send us your
       +        requests, but please understand that we are unlikely to add symbols
       +        where the user base is very small, unless they have been accepted
       +        into Unicode.
       +
       +Can I send you work I've done to be incorporated into Gentium Plus?
       +
       +        Yes. See the FONTLOG for information on becoming a contributor.
       +
       +
       +Technical
       +=========
       +
       +Can you help me get Gentium Plus working on my system?
       +
       +        We cannot afford to offer individual technical support. The best
       +        resource is this website, where we hope to offer some limited help.
       +        However, we do want to hear of any problems you encounter, so that
       +        we can add them to the list of bugs to fix in later releases.
       +        Our contact address is gentium@sil.org.  Please understand
       +        that we cannot guarantee a personal response.
       +
       +I can't find all the extended Latin letters in the font. How do I type them?
       +
       +        Gentium Plus is Unicode-encoded, which means that the computer stores a
       +        special, unique code for each letter in your document. Since most
       +        keyboards do not have hundreds of keys, special software is needed
       +        in order to type the hundreds of special characters supported by the
       +        font. See the README.txt file for more information.
       +        
       +I can't find the 'o with right hook' in the font. Where is it?
       +
       +        Combinations of base letters with diacritics are often called
       +        composite, or pre-composed glyphs. Gentium Plus has hundreds of these
       +        (the ones that are included in Unicode). There are, however, many
       +        common combinations that are not represented by a single composite.
       +        It is possible to enter these into a document, but only as
       +        individual components. So 'o with right hook' would be entered as
       +        'o', then 'right hook'. Although this may not look very good in some
       +        cases, we're not able to anticipate every possible combination.
       +        Gentium Plus includes 'smart font' support for both OpenType and 
       +        Graphite.
       +        
       +Some diacritics are not aligning well with base glyphs, and if I type more
       +than one diacritic, they run into each other. Why is that?
       +
       +        The smart diacritic positioning in Gentium Plus relies on either
       +        OpenType or Graphite. The application you are using must support
       +        one of these technologies in order to see appropriate diacritic
       +        positioning.
       +        
       +How do I type the Greek letters?
       +
       +        You need a Unicode-compatible keyboarding system, which is not
       +        included in the release. 
       +        
       +I'm having problems making PDFs -- why won't my document distill?
       +
       +        Gentium Plus is a large font, with lots of glyphs. As a result,
       +        some older printers, PDF distillers and readers can balk at PDFs
       +        that have the complete font embedded. The easiest way to avoid
       +        this is to have the PDF distiller subset the font. This is
       +        generally a good idea anyway (with any font) and can reduce the
       +        size of your files.
       +
       +
       +Plus
       +=====
       +
       +How are the Gentium Plus fonts different from Gentium?
       +
       +        This font is based on the original Gentium design, but with an expanded
       +        character and glyph repertoire. It currently comes with regular and 
       +        italic faces. It comes with near-complete support for Latin, Cyrillic
       +        and Greek. It also contains 'smart font' support for OpenType and Graphite
       +        technologies. This allows for correct diacritic placement over all base
       +        characters, whether they are tall, short, wide, narrow, with or without 
       +        descenders. It also provides for a large variety of alternates glyphs. 
       +        These are described on the Gentium website.
       +        
       +Why is the line spacing greater for the Plus fonts?
       +
       +        In some environments, stacked diacritics in Gentium could display as
       +        'chopped-off'. Gentium Plus has slightly wider default line spacing
       +        in order to avoid this problem. Most applications do, however, let you
       +        set the line spacing explicitly, so you can have the lines spaced
       +        precisely as you wish.
       +        
       +Is there an Alt version of the Basic fonts?
       +
       +        No, although you may notice that capitals and some tall lowercase
       +        letters do use 'low-profile' versions. Gentium Plus also includes
       +        OpenType and Graphite features to turn low-profile diacritics on
       +        and off.
       +        
       +        
       +Future
       +======
       +
       +What are your future plans for Gentium Plus?
       +
       +        Our next major effort is completing bold and bold italic weights
       +        of Gentium Plus alongside a new Gentium Book Plus family. These new
       +        weights are currently available for Gentium Basic/Gentium Book Basic.
       +
       +Do you plan to include other typographic enhancements (old style
       +figures, etc.)?
       +
       +        Those would be nice, wouldn't they? From a design point of view,
       +        it would be great to have these refinements, and we haven't ruled
       +        them out. But there are other needs that are much higher priority
       +        (such as bold). If you think you could contribute some of your time 
       +        and effort to these enhancements, see the FONTLOG.txt file for
       +        information on becoming a contributor.
       +
       +Sans-serif?
       +
       +        There is a definite need for a sans-serif font that shares some of
       +        Gentium's strengths -- high readability, economy of space, etc. It
       +        would also be great if that font also harmonized well with Gentium.
       +        We don't currently have any plans for a companion face, although one
       +        of our other projects - Andika - may be useful. Andika is a sans-serif
       +        font designed specifically for use in literacy programs around the
       +        world, and is available from our web site.
       +        
       +Will you be extending Gentium to cover other scripts, and Hebrew in
       +particular?
       +
       +        It is very unlikely that we would do this, as there are so many
       +        pressing needs in Latin, Greek and Cyrillic scripts.
       +        
       +Will there be a Type 1 version? What about OpenType?
       +
       +        Gentium Plus includes OpenType and Graphite support. We do not plan
       +        to produce Type 1 versions at this time, but please write us if this
       +        is important (and tell us why). We already provide the PostScript
       +        bézier curves in the 'designsource' files in the developer release.
   DIR diff --git a/text/GentiumPlus-I.ttf b/text/GentiumPlus-I.ttf
       Binary files differ.
   DIR diff --git a/text/GentiumPlus-R.ttf b/text/GentiumPlus-R.ttf
       Binary files differ.
   DIR diff --git a/text/LICENSE_OFL.txt b/text/LICENSE_OFL.txt
       t@@ -0,0 +1,92 @@
       +This Font Software is licensed under the SIL Open Font License,
       +Version 1.1.
       +
       +This license is copied below, and is also available with a FAQ at:
       +http://scripts.sil.org/OFL
       +
       +-----------------------------------------------------------
       +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
       +-----------------------------------------------------------
       +
       +PREAMBLE
       +The goals of the Open Font License (OFL) are to stimulate worldwide
       +development of collaborative font projects, to support the font
       +creation efforts of academic and linguistic communities, and to
       +provide a free and open framework in which fonts may be shared and
       +improved in partnership with others.
       +
       +The OFL allows the licensed fonts to be used, studied, modified and
       +redistributed freely as long as they are not sold by themselves. The
       +fonts, including any derivative works, can be bundled, embedded,
       +redistributed and/or sold with any software provided that any reserved
       +names are not used by derivative works. The fonts and derivatives,
       +however, cannot be released under any other type of license. The
       +requirement for fonts to remain under this license does not apply to
       +any document created using the fonts or their derivatives.
       +
       +DEFINITIONS
       +"Font Software" refers to the set of files released by the Copyright
       +Holder(s) under this license and clearly marked as such. This may
       +include source files, build scripts and documentation.
       +
       +"Reserved Font Name" refers to any names specified as such after the
       +copyright statement(s).
       +
       +"Original Version" refers to the collection of Font Software
       +components as distributed by the Copyright Holder(s).
       +
       +"Modified Version" refers to any derivative made by adding to,
       +deleting, or substituting -- in part or in whole -- any of the
       +components of the Original Version, by changing formats or by porting
       +the Font Software to a new environment.
       +
       +"Author" refers to any designer, engineer, programmer, technical
       +writer or other person who contributed to the Font Software.
       +
       +PERMISSION & CONDITIONS
       +Permission is hereby granted, free of charge, to any person obtaining
       +a copy of the Font Software, to use, study, copy, merge, embed,
       +modify, redistribute, and sell modified and unmodified copies of the
       +Font Software, subject to the following conditions:
       +
       +1) Neither the Font Software nor any of its individual components, in
       +Original or Modified Versions, may be sold by itself.
       +
       +2) Original or Modified Versions of the Font Software may be bundled,
       +redistributed and/or sold with any software, provided that each copy
       +contains the above copyright notice and this license. These can be
       +included either as stand-alone text files, human-readable headers or
       +in the appropriate machine-readable metadata fields within text or
       +binary files as long as those fields can be easily viewed by the user.
       +
       +3) No Modified Version of the Font Software may use the Reserved Font
       +Name(s) unless explicit written permission is granted by the
       +corresponding Copyright Holder. This restriction only applies to the
       +primary font name as presented to the users.
       +
       +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
       +Software shall not be used to promote, endorse or advertise any
       +Modified Version, except to acknowledge the contribution(s) of the
       +Copyright Holder(s) and the Author(s) or with their explicit written
       +permission.
       +
       +5) The Font Software, modified or unmodified, in part or in whole,
       +must be distributed entirely under this license, and must not be
       +distributed under any other license. The requirement for fonts to
       +remain under this license does not apply to any document created using
       +the Font Software.
       +
       +TERMINATION
       +This license becomes null and void if any of the above conditions are
       +not met.
       +
       +DISCLAIMER
       +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
       +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
       +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
       +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
       +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
       +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
       +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
       +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
       +OTHER DEALINGS IN THE FONT SOFTWARE.
   DIR diff --git a/text/Makefile b/text/Makefile
       t@@ -0,0 +1,2 @@
       +all: text-hb.ubernew.c
       +        gcc text-hb.ubernew.c -g -std=c99 -o test -I /usr/X11R6/include -L /usr/X11R6/lib -I /usr/local/include -L /usr/local/lib -lm -lharfbuzz -lX11 -lXrender
   DIR diff --git a/text/NotoNastaliqUrdu-Regular.ttf b/text/NotoNastaliqUrdu-Regular.ttf
       Binary files differ.
   DIR diff --git a/text/OFL-FAQ.txt b/text/OFL-FAQ.txt
       t@@ -0,0 +1,427 @@
       +OFL FAQ - Frequently Asked Questions about the SIL Open Font License (OFL)
       +Version 1.1-update4 - Sept 2014
       +(See http://scripts.sil.org/OFL for updates)
       +
       +
       +CONTENTS OF THIS FAQ
       +1  USING AND DISTRIBUTING FONTS LICENSED UNDER THE OFL
       +2  USING OFL FONTS FOR WEB PAGES AND ONLINE WEB FONT SERVICES
       +3  MODIFYING OFL-LICENSED FONTS
       +4  LICENSING YOUR ORIGINAL FONTS UNDER THE OFL
       +5  CHOOSING RESERVED FONT NAMES
       +6  ABOUT THE FONTLOG
       +7  MAKING CONTRIBUTIONS TO OFL PROJECTS
       +8  ABOUT THE LICENSE ITSELF
       +9  ABOUT SIL INTERNATIONAL
       +APPENDIX A - FONTLOG EXAMPLE
       +
       +1  USING AND DISTRIBUTING FONTS LICENSED UNDER THE OFL
       +
       +1.1  Can I use the fonts for a book or other print publication, to create logos or other graphics or even to manufacture objects based on their outlines?
       +Yes. You are very welcome to do so. Authors of fonts released under the OFL allow you to use their font software as such for any kind of design work. No additional license or permission is required, unlike with some other licenses. Some examples of these uses are: logos, posters, business cards, stationery, video titling, signage, t-shirts, personalised fabric, 3D-printed/laser-cut shapes, sculptures, rubber stamps, cookie cutters and lead type.
       +
       +1.1.1  Does that restrict the license or distribution of that artwork?
       +No. You remain the author and copyright holder of that newly derived graphic or object. You are simply using an open font in the design process. It is only when you redistribute, bundle or modify the font itself that other conditions of the license have to be respected (see below for more details).
       +
       +1.1.2  Is any kind of acknowledgement required?
       +No. Font authors may appreciate being mentioned in your artwork's acknowledgements alongside the name of the font, possibly with a link to their website, but that is not required.
       +
       +1.2  Can the fonts be included with Free/Libre and Open Source Software collections such as GNU/Linux and BSD distributions and repositories?
       +Yes! Fonts licensed under the OFL can be freely included alongside other software under FLOSS (Free/Libre and Open Source Software) licenses. Since fonts are typically aggregated with, not merged into, existing software, there is little need to be concerned about incompatibility with existing software licenses. You may also repackage the fonts and the accompanying components in a .rpm or .deb package (or other similar package formats or installers) and include them in distribution CD/DVDs and online repositories. (Also see section 5.9 about rebuilding from source.)
       +
       +1.3  I want to distribute the fonts with my program. Does this mean my program also has to be Free/Libre and Open Source Software?
       +No. Only the portions based on the Font Software are required to be released under the OFL. The intent of the license is to allow aggregation or bundling with software under restricted licensing as well.
       +
       +1.4  Can I sell a software package that includes these fonts?
       +Yes, you can do this with both the Original Version and a Modified Version of the fonts. Examples of bundling made possible by the OFL would include: word processors, design and publishing applications, training and educational software, games and entertainment software, mobile device applications, etc.
       +
       +1.5  Can I include the fonts on a CD of freeware or commercial fonts?
       +Yes, as long some other font or software is also on the disk, so the OFL font is not sold by itself.
       +
       +1.6  Why won't the OFL let me sell the fonts alone?
       +The intent is to keep people from making money by simply redistributing the fonts. The only people who ought to profit directly from the fonts should be the original authors, and those authors have kindly given up potential direct income to distribute their fonts under the OFL. Please honour and respect their contribution!
       +
       +1.7  What about sharing OFL fonts with friends on a CD, DVD or USB stick?
       +You are very welcome to share open fonts with friends, family and colleagues through removable media. Just remember to include the full font package, including any copyright notices and licensing information as available in OFL.txt. In the case where you sell the font, it has to come bundled with software.
       +
       +1.8  Can I host the fonts on a web site for others to use?
       +Yes, as long as you make the full font package available. In most cases it may be best to point users to the main site that distributes the Original Version so they always get the most recent stable and complete version. See also discussion of web fonts in Section 2.
       +
       +1.9  Can I host the fonts on a server for use over our internal network?
       +Yes. If the fonts are transferred from the server to the client computer by means that allow them to be used even if the computer is no longer attached to the network, the full package (copyright notices, licensing information, etc.) should be included.
       +
       +1.10  Does the full OFL license text always need to accompany the font?
       +The only situation in which an OFL font can be distributed without the text of the OFL (either in a separate file or in font metadata), is when a font is embedded in a document or bundled within a program. In the case of metadata included within a font, it is legally sufficient to include only a link to the text of the OFL on http://scripts.sil.org/OFL, but we strongly recommend against this. Most modern font formats include metadata fields that will accept the full OFL text, and full inclusion increases the likelihood that users will understand and properly apply the license.
       +
       +1.11  What do you mean by 'embedding'? How does that differ from other means of distribution?
       +By 'embedding' we mean inclusion of the font in a document or file in a way that makes extraction (and redistribution) difficult or clearly discouraged. In many cases the names of embedded fonts might also not be obvious to those reading the document, the font data format might be altered, and only a subset of the font - only the glyphs required for the text - might be included. Any other means of delivering a font to another person is considered 'distribution', and needs to be accompanied by any copyright notices and licensing information available in OFL.txt.
       +
       +1.12  So can I embed OFL fonts in my document?
       +Yes, either in full or a subset. The restrictions regarding font modification and redistribution do not apply, as the font is not intended for use outside the document.
       +
       +1.13  Does embedding alter the license of the document itself?
       +No. Referencing or embedding an OFL font in any document does not change the license of the document itself. The requirement for fonts to remain under the OFL does not apply to any document created using the fonts and their derivatives. Similarly, creating any kind of graphic using a font under OFL does not make the resulting artwork subject to the OFL.
       +
       +1.14  If OFL fonts are extracted from a document in which they are embedded (such as a PDF file), what can be done with them? Is this a risk to author(s)?
       +The few utilities that can extract fonts embedded in a PDF will typically output limited amounts of outlines - not a complete font. To create a working font from this method is much more difficult and time consuming than finding the source of the original OFL font. So there is little chance that an OFL font would be extracted and redistributed inappropriately through this method. Even so, copyright laws address any misrepresentation of authorship. All Font Software released under the OFL and marked as such by the author(s) is intended to remain under this license regardless of the distribution method, and cannot be redistributed under any other license. We strongly discourage any font extraction - we recommend directly using the font sources instead - but if you extract font outlines from a document, please be considerate: respect the work of the author(s) and the licensing model.
       +
       +1.15  What about distributing fonts with a document? Within a compressed folder structure? Is it distribution, bundling or embedding?
       +Certain document formats may allow the inclusion of an unmodified font within their file structure which may consist of a compressed folder containing the various resources forming the document (such as pictures and thumbnails). Including fonts within such a structure is understood as being different from embedding but rather similar to bundling (or mere aggregation) which the license explicitly allows. In this case the font is conveyed unchanged whereas embedding a font usually transforms it from the original format. The OFL does not allow anyone to extract the font from such a structure to then redistribute it under another license. The explicit permission to redistribute and embed does not cancel the requirement for the Font Software to remain under the license chosen by its author(s). Even if the font travels inside the document as one of its assets, it should not lose its authorship information and licensing.
       +
       +1.16  What about ebooks shipping with open fonts?
       +The requirements differ depending on whether the fonts are linked, embedded or distributed (bundled or aggregated). Some ebook formats use web technologies to do font linking via @font-face, others are designed for font embedding, some use fonts distributed with the document or reading software, and a few rely solely on the fonts already present on the target system. The license requirements depend on the type of inclusion as discussed in 1.15.
       +
       +1.17  Can Font Software released under the OFL be subject to URL-based access restrictions methods or DRM (Digital Rights Management) mechanisms?
lumidify.org:70 /git/ltkx/commit/1f9c27cd41ea59996f06c0ac265ef8f7ac6a7856.gph:9784: line too long