I had two main problems: One was that my texture coordinates were entered into integers, although they are supposed to by smaller than 1. (Two days for this problem... *facepalm*)
The other was that I am using a different coordinate system: My ordinate axis runs from top to bottom and not bottom to top.
Anyway, working code, if it may help anybody:
#include <SDL.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <vector>
#include <string>
#include <iostream>
using namespace std;
struct font_data
{
int h;
GLuint *textures;
GLuint listBase;
void init(string fontName, unsigned int h); //create font of required height
void clean(); //clean font data
};
void write(const font_data &ft_font, int x, int y, string text);
//get power of two right above given number
inline int nextP2(int nb)
{
int ret=1;
while(ret < nb)
ret *= 2;
return ret;
}
//create a display lit corresponding to the given character
void makeDList(FT_Face font, unsigned char ch, GLuint listBase, GLuint *texBase)
{
if(FT_Load_Char(font, ch, FT_LOAD_RENDER))
cout << "FT_Load_Char failed in makeDList." << endl;
FT_GlyphSlot glyph = font->glyph;
FT_Bitmap &bitmap = glyph->bitmap;
//get required width and height to get correct texture size
int width = nextP2(bitmap.width);
int height = nextP2(bitmap.rows);
GLubyte *expandedData = new GLubyte [2*width*height];
for(int j=0;j<height;j++)
{
for(int i=0;i<width;i++)
{
expandedData[2*(i+j*width)] = 255;
expandedData[2*(i+j*width)+1] = (i>=bitmap.width || j>=bitmap.rows) ? 0 : bitmap.buffer[i+bitmap.width*j];
}
}
glBindTexture(GL_TEXTURE_2D, texBase[ch]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expandedData);
delete [] expandedData;
glNewList(listBase + ch, GL_COMPILE);
glBindTexture(GL_TEXTURE_2D, texBase[ch]);
glPushMatrix();
glTranslatef(glyph->bitmap_left,0,0); //shift character to have right amount of space before
glTranslatef(0, (font->bbox.yMax - glyph->metrics.horiBearingY)/64,0); //move down a little if previous line has lower character
double x = (double)bitmap.width /(double)width; //take empty space around char into account
double y = (double)bitmap.rows / (double)height;
glBegin(GL_QUADS);
glTexCoord2d(0,y);
glVertex2f(0,bitmap.rows);
glTexCoord2d(0,0);
glVertex2f(0,0);
glTexCoord2d(x,0);
glVertex2f(bitmap.width,0);
glTexCoord2d(x,y);
glVertex2f(bitmap.width,bitmap.rows);
glEnd();
glPopMatrix();
glTranslatef(glyph->metrics.horiAdvance/64,0,0);
glEndList();
}
void font_data::init(string fontName, unsigned int h)
{
textures = new GLuint[128];
this->h=h;
FT_Library library;
if(FT_Init_FreeType(&library))
cout << "Could not initialize Freetype library" << endl;
FT_Face font;
if(FT_New_Face(library, fontName.c_str(), 0, &font))
cout << "Could not open font \"" <<fontName<<"\"."<<endl;
FT_Set_Char_Size(font, h*64,h*64,96,96); //set font size
//allocate resources for textures and display lists
listBase=glGenLists(128);
glGenTextures(128,textures);
//create each of the fonts display lists
for(unsigned char i=0;i<128;i++)
makeDList(font,i,listBase,textures);
FT_Done_Face(font);
FT_Done_FreeType(library);
}
void font_data::clean()
{
glDeleteLists(listBase,128);
glDeleteTextures(128,textures);
delete [] textures;
}
void write(const font_data &ft_font, int x, int y, string text)
{
GLuint font = ft_font.listBase;
glListBase(font);
glPushMatrix();
glTranslatef(x,y,0); //move to text
glCallLists(text.length(), GL_UNSIGNED_BYTE,text.c_str());
glPopMatrix();
}
int main ( int argc, char** argv )
{
SDL_Init( SDL_INIT_VIDEO );
SDL_SetVideoMode(800, 600, 32, SDL_OPENGL);
//set openGL parameters
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0,800,600,0);
glDisable(GL_DEPTH_TEST); //depth buffering
glEnable(GL_TEXTURE_2D); //texture using
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
font_data myFont;
myFont.init("../TD/fonts/jester.ttf", 20);
SDL_Event event;
bool quit = false;
while(!quit)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3ub(255,255,255);
glDisable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
glVertex2d(0,0);
glVertex2d(800,0);
glVertex2d(800,600);
glVertex2d(0,600);
glEnd();
glEnable(GL_TEXTURE_2D);
glColor3ub(180,180,0);
write(myFont,200,300, "Hello World!");
glFlush();
SDL_GL_SwapBuffers();
while (SDL_PollEvent(&event))
{
if(event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && (event.key.keysym.sym == SDLK_ESCAPE || event.key.keysym.sym == SDLK_RETURN)))
quit=true;
}
}
myFont.clean();
SDL_Quit();
}

Find content
Not Telling