]> git.tdb.fi Git - ttf2png.git/blobdiff - ttf2png.c
Round kerning values to nearest integer rather than down
[ttf2png.git] / ttf2png.c
index b8f48a816c712612049dd21b0ee2b41fc3e83c16..e890e33643084f7784c8d23e280555c425e8fec4 100644 (file)
--- a/ttf2png.c
+++ b/ttf2png.c
@@ -28,34 +28,61 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 typedef struct sImage
 {
        unsigned w, h;
-       char     *data;
+       unsigned char *data;
 } Image;
 
 typedef struct sGlyph
 {
+       unsigned index;
        unsigned code;
-       Image    image;
+       Image image;
        unsigned x, y;
-       int      offset_x;
-       int      offset_y;
-       int      advance;
+       int offset_x;
+       int offset_y;
+       int advance;
 } Glyph;
 
+typedef struct sKerning
+{
+       unsigned left_code;
+       unsigned right_code;
+       int distance;
+} Kerning;
+
 typedef struct sFont
 {
        unsigned size;
-       int      ascent;
-       int      descent;
+       int ascent;
+       int descent;
        unsigned n_glyphs;
-       Glyph    *glyphs;
-       Image    image;
+       Glyph *glyphs;
+       unsigned n_kerning;
+       Kerning *kerning;
+       Image image;
 } Font;
 
+typedef struct sRange
+{
+       unsigned first;
+       unsigned last;
+} Range;
+
+typedef int bool;
+
+void usage(void);
+int convert_numeric_option(char, int);
+void convert_code_point_range(char, Range *);
+unsigned str_to_code_point(const char *, char **);
+void convert_size(char, unsigned *, unsigned *);
+void sort_and_compact_ranges(Range *, unsigned *);
+int range_cmp(const void *, const void *);
 unsigned round_to_pot(unsigned);
-void usage();
-void init_font(Font *, FT_Face, unsigned, unsigned, int);
-void render_grid(Font *, unsigned, unsigned, unsigned, int);
-void render_packed(Font *);
+void *alloc_image_data(size_t, size_t);
+int init_font(Font *, FT_Face, const Range *, unsigned, bool);
+int init_glyphs(Font *, FT_Face, const Range *, bool);
+int copy_bitmap(const FT_Bitmap *, Image *);
+int render_grid(Font *, unsigned, unsigned, unsigned, bool, bool);
+int render_packed(Font *, unsigned, unsigned, bool);
 int save_defs(const char *, const Font *);
 int save_png(const char *, const Image *, char);
 
@@ -64,23 +91,26 @@ char verbose = 0;
 int main(int argc, char **argv)
 {
        char *fn;
-       int  begin = 0;
-       int  end = 255;
-       int  size = 10;
-       int  cpl = 0;
-       int  cellw = 0;
-       int  cellh = 0;
-       char autohinter = 0;
-       char seq = 0;
-       char alpha = 0;
-       char invert = 0;
-       char pack = 0;
+       Range *ranges = NULL;
+       unsigned n_ranges = 0;
+       unsigned size = 10;
+       unsigned cpl = 0;
+       unsigned cellw = 0;
+       unsigned cellh = 0;
+       bool autohinter = 0;
+       bool seq = 0;
+       bool alpha = 0;
+       bool invert = 0;
+       bool pack = 0;
+       unsigned margin = 0;
+       unsigned padding = 1;
+       bool npot = 0;
 
        FT_Library freetype;
-       FT_Face    face;
+       FT_Face face;
 
-       int  err;
-       int  i;
+       int err;
+       int i;
 
        char *out_fn = "font.png";
        char *def_fn = NULL;
@@ -93,63 +123,22 @@ int main(int argc, char **argv)
                return 1;
        }
 
-       while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pi")) != -1)
+       while((i = getopt(argc, argv, "r:s:l:c:o:atvh?ed:pim:n:f")) != -1)
        {
-               char *ptr;
-               int  temp;
                switch(i)
                {
                case 'r':
-                       if(!strcmp(optarg, "all"))
-                       {
-                               begin = 0;
-                               end = 0x110000;
-                       }
-                       else
-                       {
-                               if(!isdigit(optarg[0]))
-                                       temp = -1;
-                               else
-                               {
-                                       temp = strtol(optarg, &ptr, 0);
-                                       if(ptr[0]!=',' || !isdigit(ptr[1]))
-                                               temp = -1;
-                               }
-                               if(temp<0)
-                               {
-                                       printf("Not a valid range: %s\n", optarg);
-                                       exit(1);
-                               }
-                               else
-                               {
-                                       begin = temp;
-                                       end = strtol(ptr+1, NULL, 0);
-                               }
-                       }
+                       ranges = (Range *)realloc(ranges, (++n_ranges)*sizeof(Range));
+                       convert_code_point_range('r', &ranges[n_ranges-1]);
                        break;
                case 's':
-                       size = strtol(optarg, NULL, 0);
+                       size = convert_numeric_option('s', 1);
                        break;
                case 'l':
-                       cpl = strtol(optarg, NULL, 0);
+                       cpl = convert_numeric_option('l', 1);
                        break;
                case 'c':
-                       if(!strcmp(optarg, "auto"))
-                       {
-                               cellw = 0;
-                               cellh = 0;
-                       }
-                       else if(!strcmp(optarg, "autorect"))
-                       {
-                               cellw = 0;
-                               cellh = 1;
-                       }
-                       else
-                       {
-                               cellw = strtol(optarg, &ptr, 0);
-                               if(ptr[0]=='x' && isdigit(ptr[1]))
-                                       cellh = strtol(ptr+1, NULL, 0);
-                       }
+                       convert_size('c', &cellw, &cellh);
                        break;
                case 'o':
                        out_fn = optarg;
@@ -179,6 +168,15 @@ int main(int argc, char **argv)
                case 'i':
                        invert = 1;
                        break;
+               case 'm':
+                       margin = convert_numeric_option('m', 0);
+                       break;
+               case 'n':
+                       padding = convert_numeric_option('n', 0);
+                       break;
+               case 'f':
+                       npot = 1;
+                       break;
                }
        }
        if(!strcmp(out_fn, "-"))
@@ -222,25 +220,52 @@ int main(int argc, char **argv)
                return 1;
        }
 
+       if(!n_ranges)
+       {
+               ranges = malloc(sizeof(Range));
+               ranges[0].first = 0;
+               ranges[0].last = 255;
+               n_ranges = 1;
+       }
+       else
+               sort_and_compact_ranges(ranges, &n_ranges);
+
        font.size = size;
-       init_font(&font, face, begin, end, autohinter);
+       err = init_font(&font, face, ranges, n_ranges, autohinter);
+       if(err)
+               return 1;
+
+       if(!font.n_glyphs)
+       {
+               fprintf(stderr, "No glyphs found in the requested range\n");
+               return 1;
+       }
+
        if(pack)
-               render_packed(&font);
+               err = render_packed(&font, margin, padding, npot);
        else
-               render_grid(&font, cellw, cellh, cpl, seq);
+               err = render_grid(&font, cellw, cellh, cpl, seq, npot);
+       if(err)
+               return 1;
+
        if(invert)
        {
-               for(i=0; i<font.image.w*font.image.h; ++i)
+               for(i=0; (unsigned)i<font.image.w*font.image.h; ++i)
                        font.image.data[i] = 255-font.image.data[i];
        }
-       save_png(out_fn, &font.image, alpha);
+       err = save_png(out_fn, &font.image, alpha);
+       if(err)
+               return 1;
+
        if(def_fn)
                save_defs(def_fn, &font);
 
-       for(i=0; i<font.n_glyphs; ++i)
+       for(i=0; (unsigned)i<font.n_glyphs; ++i)
                free(font.glyphs[i].image.data);
        free(font.glyphs);
+       free(font.kerning);
        free(font.image.data);
+       free(ranges);
 
        FT_Done_Face(face);
        FT_Done_FreeType(freetype);
@@ -248,6 +273,194 @@ int main(int argc, char **argv)
        return 0;
 }
 
+void usage(void)
+{
+       printf("ttf2png 1.1 - True Type Font to PNG converter\n"
+               "Copyright (c) 2004-2018  Mikko Rasa, Mikkosoft Productions\n"
+               "Distributed under the GNU General Public License\n\n");
+
+       printf("Usage: ttf2png [options] <TTF file>\n\n");
+
+       printf("Accepted options (default values in [brackets])\n"
+               "  -r  Range of code points to convert [0,255]\n"
+               "  -s  Font size to use, in pixels [10]\n"
+               "  -l  Number of glyphs to put in one line [auto]\n"
+               "  -c  Glyph cell size, in pixels (grid mode only) [auto]\n"
+               "  -o  Output file name (or - for stdout) [font.png]\n");
+       printf("  -a  Force autohinter\n"
+               "  -t  Render glyphs to alpha channel\n"
+               "  -i  Invert colors of the glyphs\n"
+               "  -v  Increase the level of verbosity\n"
+               "  -e  Use cells in sequence, without gaps (grid mode only)\n"
+               "  -p  Pack the glyphs tightly instead of in a grid\n"
+               "  -m  Margin around image edges (packed mode only) [0]\n"
+               "  -n  Padding between glyphs (packed mode only) [1]\n"
+               "  -f  Allow non-power-of-two result\n"
+               "  -d  File name for writing glyph definitions\n"
+               "  -h  Print this message\n");
+}
+
+int convert_numeric_option(char opt, int min_value)
+{
+       int value;
+       char *ptr;
+
+       value = strtol(optarg, &ptr, 0);
+       if(value<min_value || *ptr)
+       {
+               printf("Invalid option argument in -%c %s\n", opt, optarg);
+               exit(1);
+       }
+
+       return value;
+}
+
+void convert_code_point_range(char opt, Range *range)
+{
+       int value;
+       char *ptr;
+
+       if(!strcmp(optarg, "all"))
+       {
+               range->first = 0;
+               range->last = 0x10FFFF;
+               return;
+       }
+
+       value = str_to_code_point(optarg, &ptr);
+       if(value>0 && *ptr==',')
+       {
+               range->first = value;
+               value = str_to_code_point(ptr+1, &ptr);
+               if(value>0 && !*ptr)
+               {
+                       range->last = value;
+                       return;
+               }
+       }
+
+       printf("Invalid option argument in -%c %s\n", opt, optarg);
+       exit(1);
+}
+
+unsigned str_to_code_point(const char *nptr, char **endptr)
+{
+       if(nptr[0]=='U' && nptr[1]=='+')
+               return strtoul(nptr+2, endptr, 16);
+       else if(nptr[0]&0x80)
+       {
+               unsigned bytes;
+               unsigned code;
+               unsigned i;
+
+               if(endptr)
+                       *endptr = (char *)nptr;
+
+               for(bytes=1; (bytes<4 && (nptr[0]&(0x80>>bytes))); ++bytes)
+                       if((nptr[bytes]&0xC0)!=0x80)
+                               return 0;
+               if(bytes<2)
+                       return 0;
+
+               code = nptr[0]&(0x3F>>bytes);
+               for(i=1; i<bytes; ++i)
+                       code = (code<<6)|(nptr[i]&0x3F);
+
+               if(endptr)
+                       *endptr = (char *)nptr+bytes;
+
+               return code;
+       }
+       else if(isdigit(nptr[0]))
+               return strtoul(nptr, endptr, 0);
+       else
+       {
+               if(endptr)
+                       *endptr = (char *)nptr+1;
+               return *nptr;
+       }
+}
+
+void convert_size(char opt, unsigned *width, unsigned *height)
+{
+       int value;
+       char *ptr;
+
+       if(!strcmp(optarg, "auto"))
+       {
+               *width = 0;
+               *height = 0;
+               return;
+       }
+       else if(!strcmp(optarg, "autorect"))
+       {
+               *width = 0;
+               *height = 1;
+               return;
+       }
+
+       value = strtol(optarg, &ptr, 0);
+       if(value>0)
+       {
+               *width = value;
+               if(*ptr=='x')
+               {
+                       value = strtol(ptr+1, &ptr, 0);
+                       if(value>0 && !*ptr)
+                       {
+                               *height = value;
+                               return;
+                       }
+               }
+               else if(!*ptr)
+               {
+                       *height = *width;
+                       return;
+               }
+       }
+
+       printf("Invalid option argument in -%c %s\n", opt, optarg);
+       exit(1);
+}
+
+void sort_and_compact_ranges(Range *ranges, unsigned *n_ranges)
+{
+       unsigned i, j;
+
+       if(!*n_ranges)
+               return;
+
+       qsort(ranges, *n_ranges, sizeof(Range), &range_cmp);
+       for(i=0, j=1; j<*n_ranges; ++j)
+       {
+               if(ranges[i].last+1>=ranges[j].first)
+               {
+                       if(ranges[j].last>ranges[i].last)
+                               ranges[i].last = ranges[j].last;
+               }
+               else
+               {
+                       ++i;
+                       if(i!=j)
+                               ranges[i] = ranges[j];
+               }
+       }
+
+       *n_ranges = i+1;
+}
+
+int range_cmp(const void *p1, const void *p2)
+{
+       const Range *r1 = (const Range *)p1;
+       const Range *r2 = (const Range *)p2;
+       if(r1->first!=r2->first)
+               return (r1->first<r2->first ? -1 : 1);
+       else if(r1->last!=r2->last)
+               return (r1->last<r2->last ? -1 : 1);
+       else
+               return 0;
+}
+
 unsigned round_to_pot(unsigned n)
 {
        n -= 1;
@@ -260,35 +473,37 @@ unsigned round_to_pot(unsigned n)
        return n+1;
 }
 
-void usage()
+void *alloc_image_data(size_t a, size_t b)
 {
-       printf("ttf2png - True Type Font to PNG converter\n"
-               "Copyright (c) 2004-2008  Mikko Rasa, Mikkosoft Productions\n"
-               "Distributed under the GNU General Public License\n\n"
-               "Usage: ttf2png [options] <TTF file>\n\n"
-               "Accepted options (default values in [brackets])\n"
-               "  -r  Range of characters to convert [0,255]\n"
-               "  -s  Font size to use, in pixels [10]\n"
-               "  -l  Number of characters to put in one line [auto]\n"
-               "  -c  Character cell size, in pixels [auto]\n"
-               "  -o  Output file name (or - for stdout) [font.png]\n"
-               "  -a  Force autohinter\n"
-               "  -t  Render glyphs to alpha channel\n"
-               "  -i  Invert colors of the glyphs\n"
-               "  -v  Increase the level of verbosity\n"
-               "  -e  Use cells in sequence, without gaps\n"
-               "  -p  Pack the glyphs tightly instead of in a grid\n"
-               "  -d  File name for writing glyph definitions\n"
-               "  -h  Print this message\n");
+       void *ptr;
+
+       /* Carry out the multiplication manually so we can check for overflow. */
+       while(b>1)
+       {
+               size_t c = a;
+               a *= 2;
+               if(b&1)
+                       a += c;
+               if(a<c)
+               {
+                       fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(c/1024*b));
+                       return NULL;
+               }
+               b /= 2;
+       }
+       ptr = malloc(a);
+       if(!ptr)
+               fprintf(stderr, "Cannot allocate %lu kbytes of memory for image\n", (unsigned long)(a/1024*b));
+       return ptr;
 }
 
-void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int autohinter)
+int init_font(Font *font, FT_Face face, const Range *ranges, unsigned n_ranges, bool autohinter)
 {
-       unsigned i;
+       unsigned i, j;
        unsigned size = 0;
 
-       font->ascent = (face->size->metrics.ascender+63)>>6;
-       font->descent = (face->size->metrics.descender+63)>>6;
+       font->ascent = (face->size->metrics.ascender+63)/64;
+       font->descent = (face->size->metrics.descender-63)/64;
 
        if(verbose>=1)
        {
@@ -298,13 +513,57 @@ void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int auto
 
        font->n_glyphs = 0;
        font->glyphs = NULL;
-       for(i=first; i<=last; ++i)
+       for(i=0; i<n_ranges; ++i)
+               if(init_glyphs(font, face, &ranges[i], autohinter))
+                       return -1;
+
+       if(verbose>=1)
+               printf("Loaded %u glyphs\n", font->n_glyphs);
+
+       font->n_kerning = 0;
+       font->kerning = NULL;
+       for(i=0; i<font->n_glyphs; ++i) for(j=0; j<font->n_glyphs; ++j)
+               if(j!=i)
+               {
+                       FT_Vector kerning;
+                       FT_Get_Kerning(face, font->glyphs[i].index, font->glyphs[j].index, FT_KERNING_DEFAULT, &kerning);
+
+                       /* FreeType documentation says that vertical kerning is practically
+                       never used, so we ignore it. */
+                       if(kerning.x)
+                       {
+                               Kerning *kern;
+
+                               if(font->n_kerning>=size)
+                               {
+                                       size += 16;
+                                       font->kerning = (Kerning *)realloc(font->kerning, size*sizeof(Kerning));
+                               }
+
+                               kern = &font->kerning[font->n_kerning++];
+                               kern->left_code = font->glyphs[i].code;
+                               kern->right_code = font->glyphs[j].code;
+                               kern->distance = (kerning.x+32)/64;
+                       }
+               }
+
+       if(verbose>=1)
+               printf("Loaded %d kerning pairs\n", font->n_kerning);
+
+       return 0;
+}
+
+int init_glyphs(Font *font, FT_Face face, const Range *range, bool autohinter)
+{
+       unsigned i, j;
+       unsigned size = font->n_glyphs;
+
+       for(i=range->first; i<=range->last; ++i)
        {
-               unsigned  n;
+               unsigned n;
                FT_Bitmap *bmp = &face->glyph->bitmap;
-               int       x, y;
-               int       flags = 0;
-               Glyph     *glyph;
+               int flags = 0;
+               Glyph *glyph;
 
                n = FT_Get_Char_Index(face, i);
                if(!n)
@@ -316,11 +575,29 @@ void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int auto
                FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
 
                if(verbose>=2)
-                       printf("  Char %u: glyph %u, size %dx%d\n", i, n, bmp->width, bmp->rows);
+               {
+                       printf("  Code point U+%04X", i);
+                       if(i>=0x20 && i<0x7F)
+                               printf(" (%c)", i);
+                       else if(i>=0xA0 && i<=0x10FFFF)
+                       {
+                               char utf8[5];
+                               unsigned bytes;
+
+                               for(bytes=2; i>>(1+bytes*5); ++bytes) ;
+                               for(j=0; j<bytes; ++j)
+                                       utf8[j] = 0x80 | ((i>>((bytes-j-1)*6))&0x3F);
+                               utf8[0] |= 0xF0<<(4-bytes);
+                               utf8[j] = 0;
 
-               if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY)
+                               printf(" (%s)", utf8);
+                       }
+                       printf(": glyph %u, size %dx%d\n", n, bmp->width, bmp->rows);
+               }
+
+               if(bmp->pixel_mode!=FT_PIXEL_MODE_GRAY && bmp->pixel_mode!=FT_PIXEL_MODE_MONO)
                {
-                       fprintf(stderr, "Warning: Glyph %u skipped, not grayscale\n", n);
+                       fprintf(stderr, "Warning: Glyph %u skipped, incompatible pixel mode\n", n);
                        continue;
                }
 
@@ -331,37 +608,73 @@ void init_font(Font *font, FT_Face face, unsigned first, unsigned last, int auto
                }
 
                glyph = &font->glyphs[font->n_glyphs++];
+               glyph->index = n;
                glyph->code = i;
-               glyph->image.w = bmp->width;
-               glyph->image.h = bmp->rows;
-               glyph->image.data = (char *)malloc(bmp->width*bmp->rows);
                glyph->offset_x = face->glyph->bitmap_left;
                glyph->offset_y = face->glyph->bitmap_top-bmp->rows;
                glyph->advance = (int)(face->glyph->advance.x+32)/64;
 
                /* Copy the glyph image since FreeType uses a global buffer, which would
                be overwritten by the next glyph.  Negative pitch means the scanlines
-               start from the bottom */
-               if(bmp->pitch<0)
+               start from the bottom. */
+               if(copy_bitmap(bmp, &glyph->image))
+                       return -1;
+       }
+
+       return 0;
+}
+
+int copy_bitmap(const FT_Bitmap *bmp, Image *image)
+{
+       unsigned x, y;
+       unsigned char *src;
+       unsigned char *dst;
+
+       image->w = bmp->width;
+       image->h = bmp->rows;
+       if(!image->w || !image->h)
+       {
+               image->data = NULL;
+               return 0;
+       }
+
+       image->data = (unsigned char *)malloc(image->w*image->h);
+       if(!image->data)
+       {
+               fprintf(stderr, "Cannot allocate %d bytes of memory for glyph\n", image->w*image->h);
+               return -1;
+       }
+
+       if(bmp->pitch<0)
+               src = bmp->buffer+(bmp->rows-1)*-bmp->pitch;
+       else
+               src = bmp->buffer;
+       dst = image->data;
+
+       for(y=0; y<bmp->rows; ++y)
+       {
+               if(bmp->pixel_mode==FT_PIXEL_MODE_MONO)
                {
-                       for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
-                               glyph->image.data[x+(glyph->image.h-1-y)*glyph->image.w] = bmp->buffer[x-y*bmp->pitch];
+                       for(x=0; x<bmp->width; ++x)
+                               dst[x] = ((src[x/8]&(0x80>>(x%8))) ? 0xFF : 0x00);
                }
                else
                {
-                       for(y=0; y<bmp->rows; ++y) for(x=0; x<bmp->width; ++x)
-                               glyph->image.data[x+y*glyph->image.w] = bmp->buffer[x+y*bmp->pitch];
+                       for(x=0; x<bmp->width; ++x)
+                               dst[x] = src[x];
                }
+
+               src += bmp->pitch;
+               dst += image->w;
        }
 
-       if(verbose>=1)
-               printf("Loaded %u glyphs\n", font->n_glyphs);
+       return 0;
 }
 
-void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int seq)
+int render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, bool seq, bool npot)
 {
        unsigned i;
-       int      top = 0, bot = 0;
+       int top = 0, bot = 0;
        unsigned first, last;
        unsigned maxw = 0, maxh = 0;
 
@@ -401,7 +714,7 @@ void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int s
                printf("Max size:  %u x %u\n", maxw, maxh);
                printf("Y range:   [%d %d]\n", bot, top);
                printf("Cell size: %u x %u\n", cellw, cellh);
-               if(maxw>cellw || top-bot>cellh)
+               if(maxw>cellw || (unsigned)(top-bot)>cellh)
                        fprintf(stderr, "Warning: character size exceeds cell size\n");
        }
 
@@ -422,16 +735,25 @@ void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int s
                first -= first%cpl;
        last = font->glyphs[font->n_glyphs-1].code;
 
-       font->image.w = round_to_pot(cpl*cellw);
-       font->image.h = round_to_pot((last-first+cpl)/cpl*cellh);
+       font->image.w = cpl*cellw;
+       if(!npot)
+               font->image.w = round_to_pot(font->image.w);
+       if(seq)
+               font->image.h = (font->n_glyphs+cpl-1)/cpl*cellh;
+       else
+               font->image.h = (last-first+cpl)/cpl*cellh;
+       if(!npot)
+               font->image.h = round_to_pot(font->image.h);
 
-       font->image.data = (char *)malloc(font->image.w*font->image.h);
+       font->image.data = (unsigned char *)alloc_image_data(font->image.w, font->image.h);
+       if(!font->image.data)
+               return -1;
        memset(font->image.data, 255, font->image.w*font->image.h);
 
        for(i=0; i<font->n_glyphs; ++i)
        {
-               Glyph    *glyph;
-               int      ci, cx, cy;
+               Glyph *glyph;
+               unsigned ci, cx, cy;
                unsigned x, y;
 
                glyph = &font->glyphs[i];
@@ -453,70 +775,85 @@ void render_grid(Font *font, unsigned cellw, unsigned cellh, unsigned cpl, int s
 
                for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
                {
-                       if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || cy+y>=font->image.h)
+                       if(cx+x>=font->image.w || cy+y>=font->image.h)
                                continue;
                        font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
                }
        }
+
+       return 0;
 }
 
-void render_packed(Font *font)
+int render_packed(Font *font, unsigned margin, unsigned padding, bool npot)
 {
        unsigned i;
-       unsigned area = 0;
-       char     *used_glyphs;
+       size_t area = 0;
+       char *used_glyphs;
        unsigned *used_pixels;
-       unsigned cx = 0, cy;
+       unsigned cx = margin, cy;
        unsigned used_h = 0;
 
        /* Compute the total area occupied by glyphs and padding. */
        for(i=0; i<font->n_glyphs; ++i)
-               area += (font->glyphs[i].image.w+1)*(font->glyphs[i].image.h+1);
+       {
+               size_t a = area+(font->glyphs[i].image.w+padding)*(font->glyphs[i].image.h+padding);
+               if(a<area)
+               {
+                       fprintf(stderr, "Overflow in counting total glyph area\n");
+                       return -1;
+               }
+               area = a;
+       }
 
        /* Find an image size that's no higher than wide, allowing for some
        imperfections in the packing. */
        for(font->image.w=1;; font->image.w<<=1)
        {
-               font->image.h = (area*5/4)/font->image.w;
+               if(font->image.w<=margin*2)
+                       continue;
+               font->image.h = (area*5/4)/(font->image.w-margin*2)+margin*2;
                if(font->image.h<=font->image.w)
                        break;
        }
-       font->image.h = round_to_pot(font->image.h);
+       if(!npot)
+               font->image.h = round_to_pot(font->image.h);
 
        /* Allocate arrays for storing the image and keeping track of used pixels and
        glyphs.  Since glyphs are rectangular and the image is filled starting from
        the top, it's enough to track the number of used pixels at the top of each
        column. */
-       font->image.data = (char *)malloc(font->image.w*font->image.h);
+       font->image.data = (unsigned char *)alloc_image_data(font->image.w, font->image.h);
+       if(!font->image.data)
+               return -1;
        memset(font->image.data, 255, font->image.w*font->image.h);
        used_pixels = (unsigned *)malloc(font->image.w*sizeof(unsigned));
        memset(used_pixels, 0, font->image.w*sizeof(unsigned));
        used_glyphs = (char *)malloc(font->n_glyphs);
        memset(used_glyphs, 0, font->n_glyphs);
 
-       for(cy=0; cy<font->image.h;)
+       for(cy=margin; cy+margin<font->image.h;)
        {
                unsigned w;
                unsigned x, y;
-               Glyph    *glyph = NULL;
+               Glyph *glyph = NULL;
                unsigned best_score = 0;
                unsigned target_h = 0;
 
-               /* Find the leftmost free pixel on this row.    Also record the lowest extent of glyphs
-               to the left of the free position. */
-               for(; (cx<font->image.w && used_pixels[cx]>cy); ++cx)
-                       if(used_pixels[cx]-cy-1>target_h)
-                               target_h = used_pixels[cx]-cy-1;
+               /* Find the leftmost free pixel on this row.  Also record the lowest
+               extent of glyphs to the left of the free position. */
+               for(; (cx+margin<font->image.w && used_pixels[cx]>cy); ++cx)
+                       if(used_pixels[cx]-cy-padding>target_h)
+                               target_h = used_pixels[cx]-cy-padding;
 
-               if(cx>=font->image.w)
+               if(cx+margin>=font->image.w)
                {
-                       cx = 0;
+                       cx = margin;
                        ++cy;
                        continue;
                }
 
                /* Count the free pixel at this position. */
-               for(w=0; (cx+w<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
+               for(w=0; (cx+w+margin<font->image.w && used_pixels[cx+w]<=cy); ++w) ;
 
                /* Find a suitable glyph to put here. */
                for(i=0; i<font->n_glyphs; ++i)
@@ -531,7 +868,7 @@ void render_packed(Font *font)
                                /* Prefer glyphs that would reach exactly as low as the ones left
                                of here.  This aims to create a straight edge at the bottom for
                                lining up further glyphs. */
-                               score = g->image.h+1;
+                               score = g->image.h+padding;
                                if(g->image.h==target_h)
                                        score *= g->image.w;
                                else
@@ -557,30 +894,37 @@ void render_packed(Font *font)
 
                for(y=0; y<glyph->image.h; ++y) for(x=0; x<glyph->image.w; ++x)
                {
-                       if(cx+x<0 || cx+x>=font->image.w || cy+y<0 || cy+y>=font->image.h)
+                       if(cx+x>=font->image.w || cy+y>=font->image.h)
                                continue;
                        font->image.data[cx+x+(cy+y)*font->image.w] = 255-glyph->image.data[x+y*glyph->image.w];
                }
-               for(x=0; x<glyph->image.w+2; ++x)
+               for(x=0; x<glyph->image.w+2*padding; ++x)
                {
-                       if(cx+x<1 || cx+x>font->image.w)
+                       if(cx+x<padding || cx+x>=font->image.w+padding)
                                continue;
-                       if(used_pixels[cx+x-1]<cy+glyph->image.h+1)
-                               used_pixels[cx+x-1] = cy+glyph->image.h+1;
+                       if(used_pixels[cx+x-padding]<cy+glyph->image.h+padding)
+                               used_pixels[cx+x-padding] = cy+glyph->image.h+padding;
                }
 
-               if(cy+glyph->image.h>used_h)
-                       used_h = cy+glyph->image.h;
+               if(cy+glyph->image.h+margin>used_h)
+                       used_h = cy+glyph->image.h+margin;
        }
 
        /* Trim the image to the actually used size, in case the original estimate
        was too pessimistic. */
-       font->image.h = round_to_pot(used_h);
+       font->image.h = used_h;
+       if(!npot)
+               font->image.h = round_to_pot(font->image.h);
+
+       free(used_glyphs);
+       free(used_pixels);
+
+       return 0;
 }
 
 int save_defs(const char *fn, const Font *font)
 {
-       FILE     *out;
+       FILE *out;
        unsigned i;
 
        out = fopen(fn, "w");
@@ -592,13 +936,20 @@ int save_defs(const char *fn, const Font *font)
 
        fprintf(out, "# Image/font info:\n");
        fprintf(out, "# width height size ascent descent\n");
-       fprintf(out, "%d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
+       fprintf(out, "font %d %d %d %d %d\n", font->image.w, font->image.h, font->size, font->ascent, font->descent);
        fprintf(out, "\n# Glyph info:\n");
        fprintf(out, "# code x y width height offset_x offset_y advance\n");
        for(i=0; i<font->n_glyphs; ++i)
        {
                const Glyph *g = &font->glyphs[i];
-               fprintf(out, "%u %u %u %u %u %d %d %d\n", g->code, g->x, g->y, g->image.w, g->image.h, g->offset_x, g->offset_y, g->advance);
+               fprintf(out, "glyph %u %u %u %u %u %d %d %d\n", g->code, g->x, g->y, g->image.w, g->image.h, g->offset_x, g->offset_y, g->advance);
+       }
+       fprintf(out, "\n# Kerning info:\n");
+       fprintf(out, "# left right distance\n");
+       for(i=0; i<font->n_kerning; ++i)
+       {
+               const Kerning *k = &font->kerning[i];
+               fprintf(out, "kern %u %u %d\n", k->left_code, k->right_code, k->distance);
        }
 
        fclose(out);
@@ -608,13 +959,13 @@ int save_defs(const char *fn, const Font *font)
 
 int save_png(const char *fn, const Image *image, char alpha)
 {
-       FILE       *out;
+       FILE *out;
        png_struct *pngs;
-       png_info   *pngi;
-       png_byte   *rows[image->h];
-       int        i;
-       png_byte   *data2;
-       int        color;
+       png_info *pngi;
+       png_byte **rows;
+       unsigned i;
+       png_byte *data2 = 0;
+       int color;
 
        if(!strcmp(fn, "-"))
                out = stdout;
@@ -643,9 +994,12 @@ int save_png(const char *fn, const Image *image, char alpha)
        }
 
        png_init_io(pngs, out);
+       rows = (png_byte **)malloc(image->h*sizeof(png_byte *));
        if(alpha)
        {
-               data2 = (png_byte *)malloc(image->w*image->h*2);
+               data2 = (png_byte *)alloc_image_data(image->w*2, image->h);
+               if(!data2)
+                       return -1;
                for(i=0; i<image->w*image->h; ++i)
                {
                        data2[i*2] = 255;
@@ -665,6 +1019,7 @@ int save_png(const char *fn, const Image *image, char alpha)
        png_set_rows(pngs, pngi, rows);
        png_write_png(pngs, pngi, PNG_TRANSFORM_IDENTITY, NULL);
        png_destroy_write_struct(&pngs, &pngi);
+       free(rows);
        if(alpha)
                free(data2);