/*  font.c     
 *  
 * 
 * $Id: font.c 14976 2008-05-26 09:50:46Z campbellbarton $
 *
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 */

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

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "MEM_guardedalloc.h"

#include "BLI_arithb.h"
#include "BLI_blenlib.h"
#include "BLI_vfontdata.h"

#include "DNA_packedFile_types.h"
#include "DNA_curve_types.h"
#include "DNA_object_types.h"
#include "DNA_view3d_types.h"
#include "DNA_vfont_types.h"
#include "DNA_scene_types.h"

#include "BKE_utildefines.h"
#include "BKE_bad_level_calls.h"

#include "BKE_packedFile.h"

#include "BKE_library.h"
#include "BKE_font.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_screen.h"
#include "BKE_anim.h"
#include "BKE_curve.h"
#include "BKE_displist.h"

#define callocstructN(x,y,name) (x*)MEM_callocN((y)* sizeof(x),name)
 
struct SelBox *selboxes= NULL;

/* UTF-8 <-> wchar transformations */
void
chtoutf8(unsigned long c, char *o)
{
	// Variables and initialization
/*	memset(o, 0, 16);	*/

	// Create the utf-8 string
	if (c < 0x80)
	{
		o[0] = (char) c;
	}
	else if (c < 0x800)
	{
		o[0] = (0xC0 | (c>>6));
		o[1] = (0x80 | (c & 0x3f));
	}
	else if (c < 0x10000)
	{
		o[0] = (0xe0 | (c >> 12));
		o[1] = (0x80 | (c >>6 & 0x3f));
		o[2] = (0x80 | (c & 0x3f));
	}
	else if (c < 0x200000)
	{
	o[0] = (0xf0 | (c>>18));
	o[1] = (0x80 | (c >>12 & 0x3f));
	o[2] = (0x80 | (c >> 6 & 0x3f));
	o[3] = (0x80 | (c & 0x3f));
	}
}

void
wcs2utf8s(char *dst, wchar_t *src)
{
	char ch[5];

	while(*src)
	{
		memset(ch, 0, 5);
		chtoutf8(*src++, ch);
		strcat(dst, ch);
	}
}

int
wcsleninu8(wchar_t *src)
{
	char ch[16];
	int len = 0;

	while(*src)
	{
		memset(ch, 0, 16);
		chtoutf8(*src++, ch);
		len = len + strlen(ch);
	}

	return len;
}

int
utf8slen(char *src)
{
	int size = 0, index = 0;
	unsigned char c;
	
	c = src[index++];
	while(c)
	{    
		if((c & 0x80) == 0)
		{
			index += 0;
		}
		else if((c & 0xe0) == 0xe0)
		{
			index += 2;
		}
		else
		{
			index += 1;
		}
		size += 1;
		c = src[index++];		
	}
	
	return size;
}


/* Converts Unicode to wchar

According to RFC 3629 "UTF-8, a transformation format of ISO 10646"
(http://tools.ietf.org/html/rfc3629), the valid UTF-8 encoding are:

  Char. number range  |        UTF-8 octet sequence
      (hexadecimal)    |              (binary)
   --------------------+---------------------------------------------
   0000 0000-0000 007F | 0xxxxxxx
   0000 0080-0000 07FF | 110xxxxx 10xxxxxx
   0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
   0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

If the encoding incidated by the first character is incorrect (because the
1 to 3 following characters do not match 10xxxxxx), the output is a '?' and
only a single input character is consumed.

*/

int utf8towchar(wchar_t *w, char *c)
{
	int len=0;

	if(w==NULL || c==NULL) return(0);

	while(*c) {
		if ((*c & 0xe0) == 0xc0) {
			if((c[1] & 0x80) && (c[1] & 0x40) == 0x00) {
				*w=((c[0] &0x1f)<<6) | (c[1]&0x3f);
				c++;
			} else {
				*w = '?';
			}
		} else if ((*c & 0xf0) == 0xe0) {
			if((c[1] & c[2] & 0x80) && ((c[1] | c[2]) & 0x40) == 0x00) {
				*w=((c[0] & 0x0f)<<12) | ((c[1]&0x3f)<<6) | (c[2]&0x3f);
				c += 2;
			} else {
				*w = '?';
			}
		} else if ((*c & 0xf8) == 0xf0) {
			if((c[1] & c[2] & c[3] & 0x80) && ((c[1] | c[2] | c[3]) & 0x40) == 0x00) {
				*w=((c[0] & 0x07)<<18) | ((c[1]&0x1f)<<12) | ((c[2]&0x3f)<<6) | (c[3]&0x3f);
				c += 3;
			} else {
				*w = '?';
			}
		} else
		    *w=(c[0] & 0x7f);

		c++;
		w++;
		len++;
	}
	return len;
}

/* The vfont code */
void free_vfont(struct VFont *vf)
{
	if (vf == 0) return;

	if (vf->data) {
		while(vf->data->characters.first)
		{
			VChar *che = vf->data->characters.first;
			
			while (che->nurbsbase.first) {
				Nurb *nu = che->nurbsbase.first;
				if (nu->bezt) MEM_freeN(nu->bezt);
				BLI_freelinkN(&che->nurbsbase, nu);
			}
	
			BLI_freelinkN(&vf->data->characters, che);
		}

		MEM_freeN(vf->data);
		vf->data = NULL;
	}
	
	if (vf->packedfile) {
		freePackedFile(vf->packedfile);
		vf->packedfile = NULL;
	}
}

static void *builtin_font_data= NULL;
static int builtin_font_size= 0;

void BKE_font_register_builtin(void *mem, int size)
{
	builtin_font_data= mem;
	builtin_font_size= size;
}

static PackedFile *get_builtin_packedfile(void)
{
	if (!builtin_font_data) {
		printf("Internal error, builtin font not loaded\n");

		return NULL;
	} else {
		void *mem= MEM_mallocN(builtin_font_size, "vfd_builtin");

		memcpy(mem, builtin_font_data, builtin_font_size);
	
		return newPackedFileMemory(mem, builtin_font_size);
	}
}

static VFontData *vfont_get_data(VFont *vfont)
{
	struct TmpFont *tmpfnt = NULL;
	PackedFile *tpf;
	
	if(vfont==NULL) return NULL;
	
	// Try finding the font from font list
	tmpfnt = G.ttfdata.first;
	
	while(tmpfnt)
	{
		if(tmpfnt->vfont == vfont)
			break;
		tmpfnt = tmpfnt->next;
	}
	
	// And then set the data	
	if (!vfont->data) {
		PackedFile *pf;
		
		if (BLI_streq(vfont->name, "<builtin>")) {
			pf= get_builtin_packedfile();
		} else {
			if (vfont->packedfile) {
				pf= vfont->packedfile;
				
				// We need to copy a tmp font to memory unless it is already there
				if(!tmpfnt)
				{
					tpf= MEM_callocN(sizeof(*tpf), "PackedFile");
					tpf->data= MEM_mallocN(pf->size, "packFile");
					tpf->size= pf->size;
					memcpy(tpf->data, pf->data, pf->size);
					
					// Add temporary packed file to globals
					tmpfnt= (struct TmpFont *) MEM_callocN(sizeof(struct TmpFont), "temp_font");
					tmpfnt->pf= tpf;
					tmpfnt->vfont= vfont;
					BLI_addtail(&G.ttfdata, tmpfnt);
				}
			} else {
				pf= newPackedFile(vfont->name);
				
				if(!tmpfnt)
				{
					tpf= newPackedFile(vfont->name);
					
					// Add temporary packed file to globals
					tmpfnt= (struct TmpFont *) MEM_callocN(sizeof(struct TmpFont), "temp_font");
					tmpfnt->pf= tpf;
					tmpfnt->vfont= vfont;
					BLI_addtail(&G.ttfdata, tmpfnt);
				}
			}
			if(!pf) {
				printf("Font file doesn't exist: %s\n", vfont->name);

				strcpy(vfont->name, "<builtin>");
				pf= get_builtin_packedfile();
			}
		}
		
		if (pf) {
#ifdef WITH_FREETYPE2
			vfont->data= BLI_vfontdata_from_freetypefont(pf);
#else
			vfont->data= BLI_vfontdata_from_psfont(pf);
#endif			
			if (pf != vfont->packedfile) {
				freePackedFile(pf);
			}
		}
	}
	
	return vfont->data;	
}

VFont *load_vfont(char *name)
{
	char filename[FILE_MAXFILE];
	VFont *vfont= NULL;
	PackedFile *pf;
	PackedFile *tpf = NULL;	
	int is_builtin;
	struct TmpFont *tmpfnt;
	
	if (BLI_streq(name, "<builtin>")) {
		strcpy(filename, name);
		
		pf= get_builtin_packedfile();
		is_builtin= 1;
	} else {
		char dir[FILE_MAXDIR];
		
		strcpy(dir, name);
		BLI_splitdirstring(dir, filename);

		pf= newPackedFile(name);
		tpf= newPackedFile(name);		
		
		is_builtin= 0;
	}

	if (pf) {
		VFontData *vfd;
		
		waitcursor(1);

#ifdef WITH_FREETYPE2
		vfd= BLI_vfontdata_from_freetypefont(pf);
#else
		vfd= BLI_vfontdata_from_psfont(pf);
#endif
		
		if (vfd) {
			vfont = alloc_libblock(&G.main->vfont, ID_VF, filename);
			vfont->data = vfd;
			
			BLI_strncpy(vfont->name, name, sizeof(vfont->name));

			// if autopack is on store the packedfile in de font structure
			if (!is_builtin && (G.fileflags & G_AUTOPACK)) {
				vfont->packedfile = pf;
			}
			
			// Do not add <builtin> to temporary listbase
			if(strcmp(filename, "<builtin>"))
			{
				tmpfnt= (struct TmpFont *) MEM_callocN(sizeof(struct TmpFont), "temp_font");
				tmpfnt->pf= tpf;
				tmpfnt->vfont= vfont;
				BLI_addtail(&G.ttfdata, tmpfnt);
			}			
		}
		
		// Free the packed file
		if (!vfont || vfont->packedfile != pf) {
			freePackedFile(pf);
		}
	
		waitcursor(0);
	}
	
	return vfont;
}

static VFont *which_vfont(Curve *cu, CharInfo *info)
{
	switch(info->flag & CU_STYLE) {
		case CU_BOLD:
			if (cu->vfontb) return(cu->vfontb); else return(cu->vfont);
		case CU_ITALIC:
			if (cu->vfonti) return(cu->vfonti); else return(cu->vfont);
		case (CU_BOLD|CU_ITALIC):
			if (cu->vfontbi) return(cu->vfontbi); else return(cu->vfont);
		default:
			return(cu->vfont);
	}			
}

static void build_underline(Curve *cu, float x1, float y1, float x2, float y2, int charidx, short mat_nr)
{
	Nurb *nu2;
	BPoint *bp;
	
	nu2 =(Nurb*) MEM_callocN(sizeof(Nurb),"underline_nurb");
	if (nu2 == NULL) return;
	nu2->resolu= cu->resolu;
	nu2->bezt = NULL;
	nu2->knotsu = nu2->knotsv = NULL;
	nu2->flag= 0;
	nu2->charidx = charidx+1000;
	if (mat_nr > 0) nu2->mat_nr= mat_nr-1;
	nu2->pntsu = 4;
	nu2->pntsv = 1;
	nu2->orderu = 4;
	nu2->orderv = 1;
	nu2->flagu = CU_CYCLIC;

	bp = (BPoint*)MEM_callocN(4 * sizeof(BPoint),"underline_bp"); 
	if (bp == 0){
		MEM_freeN(nu2);
		return;
	}
	nu2->bp = bp;

	nu2->bp[0].vec[0] = x1;
	nu2->bp[0].vec[1] = y1;	
	nu2->bp[0].vec[2] = 0;
	nu2->bp[0].vec[3] = 1.0;
	nu2->bp[1].vec[0] = x2;
	nu2->bp[1].vec[1] = y1;
	nu2->bp[1].vec[2] = 0;	
	nu2->bp[1].vec[3] = 1.0;	
	nu2->bp[2].vec[0] = x2;
	nu2->bp[2].vec[1] = y2;	
	nu2->bp[2].vec[2] = 0;
	nu2->bp[2].vec[3] = 1.0; 
	nu2->bp[3].vec[0] = x1;
	nu2->bp[3].vec[1] = y2;
	nu2->bp[3].vec[2] = 0;	
	nu2->bp[3].vec[3] = 1.0;	
	
	nu2->type = CU_2D;
	BLI_addtail(&(cu->nurb), nu2);	

}

static void buildchar(Curve *cu, unsigned long character, CharInfo *info, float ofsx, float ofsy, float rot, int charidx)
{
	BezTriple *bezt1, *bezt2;
	Nurb *nu1 = NULL, *nu2 = NULL;
	float *fp, fsize, shear, x, si, co;
	VFontData *vfd = NULL;
	VChar *che = NULL;
	int i, sel=0;

	vfd= vfont_get_data(which_vfont(cu, info));	
	if (!vfd) return;

	if (cu->selend < cu->selstart) {
		if ((charidx >= (cu->selend)) && (charidx <= (cu->selstart-2)))
			sel= 1;
	}
	else {
		if ((charidx >= (cu->selstart-1)) && (charidx <= (cu->selend-1)))
			sel= 1;
	}

	/* make a copy at distance ofsx,ofsy with shear*/
	fsize= cu->fsize;
	shear= cu->shear;
	si= (float)sin(rot);
	co= (float)cos(rot);

	// Find the correct character from the font
	che = vfd->characters.first;
	while(che)
	{
		if(che->index == character)
			break;
		che = che->next;
	}
	
	// Select the glyph data
	if(che)
		nu1 = che->nurbsbase.first;

	// Create the character
	while(nu1)
	{
		bezt1 = nu1->bezt;
		if (bezt1){
			nu2 =(Nurb*) MEM_mallocN(sizeof(Nurb),"duplichar_nurb");
			if (nu2 == 0) break;
			memcpy(nu2, nu1, sizeof(struct Nurb));
			nu2->resolu= cu->resolu;
			nu2->bp = 0;
			nu2->knotsu = nu2->knotsv = NULL;
			nu2->flag= CU_SMOOTH;
			nu2->charidx = charidx;
			if (info->mat_nr) {
				nu2->mat_nr= info->mat_nr-1;
			}
			else {
				nu2->mat_nr= 0;
			}
			/* nu2->trim.first = 0; */
			/* nu2->trim.last = 0; */
			i = nu2->pntsu;

			bezt2 = (BezTriple*)MEM_mallocN(i * sizeof(BezTriple),"duplichar_bezt2"); 
			if (bezt2 == 0){
				MEM_freeN(nu2);
				break;
			}
			memcpy(bezt2, bezt1, i * sizeof(struct BezTriple));
			nu2->bezt = bezt2;
			
			if (shear != 0.0) {
				bezt2 = nu2->bezt;
				
				for (i= nu2->pntsu; i > 0; i--) {
					bezt2->vec[0][0] += shear * bezt2->vec[0][1];
					bezt2->vec[1][0] += shear * bezt2->vec[1][1];
					bezt2->vec[2][0] += shear * bezt2->vec[2][1];
					bezt2++;
				}
			}
			if(rot!=0.0) {
				bezt2= nu2->bezt;
				for (i=nu2->pntsu; i > 0; i--) {
					fp= bezt2->vec[0];
					
					x= fp[0];
					fp[0]= co*x + si*fp[1];
					fp[1]= -si*x + co*fp[1];
					x= fp[3];
					fp[3]= co*x + si*fp[4];
					fp[4]= -si*x + co*fp[4];
					x= fp[6];
					fp[6]= co*x + si*fp[7];
					fp[7]= -si*x + co*fp[7];

					bezt2++;
				}
			}
			bezt2 = nu2->bezt;

			for (i= nu2->pntsu; i > 0; i--) {
				fp= bezt2->vec[0];

				fp[0]= (fp[0]+ofsx)*fsize;
				fp[1]= (fp[1]+ofsy)*fsize;
				fp[3]= (fp[3]+ofsx)*fsize;
				fp[4]= (fp[4]+ofsy)*fsize;
				fp[6]= (fp[6]+ofsx)*fsize;
				fp[7]= (fp[7]+ofsy)*fsize;
				bezt2++;
			}
			
			BLI_addtail(&(cu->nurb), nu2);
		}
		
		nu1 = nu1->next;
	}
}

int getselection(int *start, int *end)
{
	Curve *cu;
	
	if (G.obedit==NULL || G.obedit->type != OB_FONT) return 0;
	
	cu= G.obedit->data;

	if (cu->selstart == 0) return 0;
	if (cu->selstart <= cu->selend) {
		*start = cu->selstart-1;
		*end = cu->selend-1;
		return 1;
	}
	else {
		*start = cu->selend;
		*end = cu->selstart-2;
		return -1;
	}
}

struct chartrans *text_to_curve(Object *ob, int mode) 
{
	VFont *vfont, *oldvfont;
	VFontData *vfd= NULL;
	Curve *cu, *cucu;
	struct chartrans *chartransdata=NULL, *ct;
	float distfac, tabfac, ctime, dtime, tvec[4], vec[4], rotvec[3], minx, maxx, miny, maxy;
	float cmat[3][3], timeofs, si, co, sizefac;
	float *f, maxlen=0, xof, yof, xtrax, linedist, *linedata, *linedata2, *linedata3, *linedata4;
	int i, slen, oldflag, j;
	short cnr=0, lnr=0, wsnr= 0;
	wchar_t *mem, *tmp, ascii;
	int outta;
	float vecyo[3], curofs;
	CharInfo *info;
	float wsfac;
	float ulwidth, uloverlap;
	TextBox *tb;
	int curbox;
	int selstart, selend;
	SelBox *sb= NULL;	/* to please gcc */
	VChar *che;
	float twidth;
	int utf8len;

	/* renark: do calculations including the trailing '\0' of a string
	   because the cursor can be at that location */

	if(ob->type!=OB_FONT) return 0;

	// Set font data
	cu= (Curve *) ob->data;
	vfont= cu->vfont;

	if(cu->str == 0) return 0;
	if(vfont == 0) return 0;

	// Create unicode string
	utf8len = utf8slen(cu->str);
	tmp = mem = MEM_callocN(((utf8len + 1) * sizeof(wchar_t)), "convertedmem");
	
	utf8towchar(mem, cu->str);

	// Count the wchar_t string length
	slen = wcslen(mem);

	if (cu->ulheight == 0.0) cu->ulheight = 0.05;
	if (cu->strinfo==NULL) {	/* old file */
		cu->strinfo = MEM_callocN((slen+1) * sizeof(CharInfo), "strinfo compat");
	}
	if (cu->tb==NULL) {
		cu->tb= MEM_callocN(MAXTEXTBOX*sizeof(TextBox), "TextBox compat");
	}

	vfd= vfont_get_data(vfont);

	/* The VFont Data can not be found */
	if(!vfd)
	{
		if(mem)
			MEM_freeN(mem);	
		return 0;
	}

	/* calc offset and rotation of each char */
	ct = chartransdata =
		(struct chartrans*)MEM_callocN((slen+1)* sizeof(struct chartrans),"buildtext");

	/* We assume the worst case: 1 character per line (is freed at end anyway) */

	linedata= MEM_mallocN(sizeof(float)*(slen+2),"buildtext2");
	linedata2= MEM_mallocN(sizeof(float)*(slen+2),"buildtext3");
	linedata3= MEM_callocN(sizeof(float)*(slen+2),"buildtext4");	
	linedata4= MEM_callocN(sizeof(float)*(slen+2),"buildtext5");		
	
	linedist= cu->linedist;
	
	xof= cu->xof + (cu->tb[0].x/cu->fsize);
	yof= cu->yof + (cu->tb[0].y/cu->fsize);

	xtrax= 0.5f*cu->spacing-0.5f;

	oldvfont = NULL;

	for (i=0; i<slen; i++) cu->strinfo[i].flag &= ~CU_WRAP;

	if (selboxes) MEM_freeN(selboxes);
	selboxes = NULL;
	if (getselection(&selstart, &selend))
		selboxes = MEM_callocN((selend-selstart+1)*sizeof(SelBox), "font selboxes");

	tb = &(cu->tb[0]);
	curbox= 0;
	for (i = 0 ; i<=slen ; i++) {
	makebreak:
		// Characters in the list
		che = vfd->characters.first;
		ascii = mem[i];
		info = &(cu->strinfo[i]);
		vfont = which_vfont(cu, info);
		
		if(vfont==NULL) break;
		
		// Find the character
		while(che)
		{
			if(che->index == ascii)
				break;
			che = che->next;
		}

#ifdef WITH_FREETYPE2
		// The character wasn't in the current curve base so load it
		// But if the font is <builtin> then do not try loading since whole font is in the memory already
		if(che == NULL && strcmp(vfont->name, "<builtin>"))
		{
			BLI_vfontchar_from_freetypefont(vfont, ascii);
		}

		// Try getting the character again from the list
		che = vfd->characters.first;
		while(che)
		{
			if(che->index == ascii)
				break;
			che = che->next;
		}
#endif

		/* No VFont found */
		if (vfont==0) 
		{
			if(mem)
				MEM_freeN(mem);
			MEM_freeN(chartransdata);
			return 0;
		}

		if (vfont != oldvfont) {
			vfd= vfont_get_data(vfont);
			oldvfont = vfont;
		}

		/* VFont Data for VFont couldn't be found */
		if (!vfd)
		{
			if(mem)
				MEM_freeN(mem);
			MEM_freeN(chartransdata);
			return 0;
		}

		// The character wasn't found, propably ascii = 0, then the width shall be 0 as well
		if(!che)
		{
			twidth = 0;
		}
		else
		{
			twidth = che->width;
		}

		// Calculate positions
		if((tb->w != 0.0) && (ct->dobreak==0) && ((xof-(tb->x/cu->fsize)+twidth)*cu->fsize) > tb->w) {
	//		fprintf(stderr, "linewidth exceeded: %c%c%c...\n", mem[i], mem[i+1], mem[i+2]);
			for (j=i; j && (mem[j] != '\n') && (mem[j] != '\r') && (chartransdata[j].dobreak==0); j--) {
				if (mem[j]==' ' || mem[j]=='-') {
					ct -= (i-(j-1));
					cnr -= (i-(j-1));
					if (mem[j] == ' ') wsnr--;
					if (mem[j] == '-') wsnr++;
					i = j-1;
					xof = ct->xof;
					ct[1].dobreak = 1;
					cu->strinfo[i+1].flag |= CU_WRAP;
					goto makebreak;
				}
				if (chartransdata[j].dobreak) {
	//				fprintf(stderr, "word too long: %c%c%c...\n", mem[j], mem[j+1], mem[j+2]);
					ct->dobreak= 1;
					cu->strinfo[i+1].flag |= CU_WRAP;
					ct -= 1;
					cnr -= 1;
					i--;
					xof = ct->xof;
					goto makebreak;
				}
			}
		}
		if(ascii== '\n' || ascii== '\r' || ascii==0 || ct->dobreak) {
			ct->xof= xof;
			ct->yof= yof;
			ct->linenr= lnr;
			ct->charnr= cnr;
			
			yof-= linedist;
			
			maxlen= MAX2(maxlen, (xof-tb->x/cu->fsize));
			linedata[lnr]= xof-tb->x/cu->fsize;
			linedata2[lnr]= cnr;
			linedata3[lnr]= tb->w/cu->fsize;
			linedata4[lnr]= wsnr;
			
			if ( (tb->h != 0.0) &&
			     ((-(yof-(tb->y/cu->fsize))) > ((tb->h/cu->fsize)-(linedist*cu->fsize))) &&
			     (cu->totbox > (curbox+1)) ) {
				maxlen= 0;
				tb++;
				curbox++;
				yof= cu->yof + tb->y/cu->fsize;
			}

			if(ascii == '\n' || ascii == '\r')
				xof = cu->xof;
			else
				xof= cu->xof + (tb->x/cu->fsize);

			xof= cu->xof + (tb->x/cu->fsize);
			lnr++;
			cnr= 0;
			wsnr= 0;
		}
		else if(ascii==9) {	/* TAB */
			ct->xof= xof;
			ct->yof= yof;
			ct->linenr= lnr;
			ct->charnr= cnr++;

			tabfac= (xof-cu->xof+0.01f);
			tabfac= (float)(2.0*ceil(tabfac/2.0));
			xof= cu->xof+tabfac;
		}
		else {
			ct->xof= xof;
			ct->yof= yof;
			ct->linenr= lnr;
			ct->charnr= cnr++;

			if (selboxes && (i>=selstart) && (i<=selend)) {
				sb = &(selboxes[i-selstart]);
				sb->y = yof*cu->fsize-linedist*cu->fsize*0.1;
				sb->h = linedist*cu->fsize;
				sb->w = xof*cu->fsize;
			}
	
			if (ascii==32) {
				wsfac = cu->wordspace; 
				wsnr++;
			} else wsfac = 1.0;
			// Set the width of the character
			if(!che)
			{
				twidth = 0;
			}
			else
			{
				twidth = che->width;
			}
			xof += (twidth*wsfac*(1.0+(info->kern/40.0)) ) + xtrax;
			
			if (selboxes && (i>=selstart) && (i<=selend)) sb->w = (xof*cu->fsize) - sb->w;
		}
		ct++;
	}
	

	
	cu->lines= 1;
	ct= chartransdata;
	tmp = mem;
	for (i= 0; i<=slen; i++, tmp++, ct++) {
		ascii = *tmp;
		if(ascii== '\n' || ascii== '\r' || ct->dobreak) cu->lines++;
	}	

	// linedata is now: width of line
	// linedata2 is now: number of characters
	// linedata3 is now: maxlen of that line
	// linedata4 is now: number of whitespaces of line

	if(cu->spacemode!=CU_LEFT) {
		ct= chartransdata;

		if(cu->spacemode==CU_RIGHT) {
			for(i=0;i<lnr;i++) linedata[i]= linedata3[i]-linedata[i];
			for (i=0; i<=slen; i++) {
				ct->xof+= linedata[ct->linenr];
				ct++;
			}
		} else if(cu->spacemode==CU_MIDDLE) {
			for(i=0;i<lnr;i++) linedata[i]= (linedata3[i]-linedata[i])/2;
			for (i=0; i<=slen; i++) {
				ct->xof+= linedata[ct->linenr];
				ct++;
			}
		} else if((cu->spacemode==CU_FLUSH) &&
		          (cu->tb[0].w != 0.0)) {
			for(i=0;i<lnr;i++)
				if(linedata2[i]>1)
					linedata[i]= (linedata3[i]-linedata[i])/(linedata2[i]-1);
			for (i=0; i<=slen; i++) {
				for (j=i; (mem[j]) && (mem[j]!='\n') && 
				          (mem[j]!='\r') && (chartransdata[j].dobreak==0) && (j<slen); j++);
//				if ((mem[j]!='\r') && (mem[j]!='\n') && (mem[j])) {
					ct->xof+= ct->charnr*linedata[ct->linenr];
//				}
				ct++;
			}
		} else if((cu->spacemode==CU_JUSTIFY) &&
		          (cu->tb[0].w != 0.0)) {
			curofs= 0;
			for (i=0; i<=slen; i++) {
				for (j=i; (mem[j]) && (mem[j]!='\n') && 
				          (mem[j]!='\r') && (chartransdata[j].dobreak==0) && (j<slen); j++);
				if ((mem[j]!='\r') && (mem[j]!='\n') &&
				    ((chartransdata[j].dobreak!=0))) {
				    if (mem[i]==' ') curofs += (linedata3[ct->linenr]-linedata[ct->linenr])/linedata4[ct->linenr];
					ct->xof+= curofs;
				}
				if (mem[i]=='\n' || mem[i]=='\r' || chartransdata[i].dobreak) curofs= 0;
				ct++;
			}			
		}
	}
	
	/* TEXT ON CURVE */
	if(cu->textoncurve) {
		cucu= cu->textoncurve->data;
		
		oldflag= cucu->flag;
		cucu->flag |= (CU_PATH+CU_FOLLOW);
		
		if(cucu->path==NULL) makeDispListCurveTypes(cu->textoncurve, 0);
		if(cucu->path) {
			float imat[4][4], imat3[3][3];
			Mat4Invert(imat, ob->obmat);
			Mat3CpyMat4(imat3, imat);

			Mat3CpyMat4(cmat, cu->textoncurve->obmat);
			Mat3MulMat3(cmat, cmat, imat3);
			sizefac= Normalize(cmat[0])/cu->fsize;
			
			minx=miny= 1.0e20f;
			maxx=maxy= -1.0e20f;
			ct= chartransdata;
			for (i=0; i<=slen; i++, ct++) {
				if(minx>ct->xof) minx= ct->xof;
				if(maxx<ct->xof) maxx= ct->xof;
				if(miny>ct->yof) miny= ct->yof;
				if(maxy<ct->yof) maxy= ct->yof;
			}
			
			/* we put the x-coordinaat exact at the curve, the y is rotated */
			
			/* length correction */
			distfac= sizefac*cucu->path->totdist/(maxx-minx);
			timeofs= 0.0;
			
			if(distfac > 1.0) {
				/* path longer than text: spacemode involves */
				distfac= 1.0f/distfac;
				
				if(cu->spacemode==CU_RIGHT) {
					timeofs= 1.0f-distfac;
				}
				else if(cu->spacemode==CU_MIDDLE) {
					timeofs= (1.0f-distfac)/2.0f;
				}
				else if(cu->spacemode==CU_FLUSH) distfac= 1.0f;
				
			}
			else distfac= 1.0;
			
			distfac/= (maxx-minx);
			
			timeofs+= distfac*cu->xof;	/* not cyclic */
			
			ct= chartransdata;
			for (i=0; i<=slen; i++, ct++) {
				
				/* rotate around center character */
				ascii = mem[i];
				
				// Find the character
				che = vfd->characters.first;
				while(che)		
				{
					if(che->index == ascii)
						break;
					che = che->next;
				}
	
				if(che)
				{
					twidth = che->width;
				}
				else
				{
					twidth = 0;
				}
				
				dtime= distfac*0.35f*twidth;	/* why not 0.5? */
				dtime= distfac*0.5f*twidth;	/* why not 0.5? */
				
				ctime= timeofs + distfac*( ct->xof - minx);
				CLAMP(ctime, 0.0, 1.0);

				/* calc the right loc AND the right rot separately */
				/* vec, tvec need 4 items */
				where_on_path(cu->textoncurve, ctime, vec, tvec);
				where_on_path(cu->textoncurve, ctime+dtime, tvec, rotvec);
				
				VecMulf(vec, sizefac);
				
				ct->rot= (float)(M_PI-atan2(rotvec[1], rotvec[0]));

				si= (float)sin(ct->rot);
				co= (float)cos(ct->rot);

				yof= ct->yof;
				
				ct->xof= vec[0] + si*yof;
				ct->yof= vec[1] + co*yof;
				
			}
			cucu->flag= oldflag;
		}
	}

	if (selboxes) {
		ct= chartransdata;
		for (i=0; i<=selend; i++, ct++) {
			if (i>=selstart) {
				selboxes[i-selstart].x = ct->xof*cu->fsize;
				selboxes[i-selstart].y = ct->yof*cu->fsize;				
			}
		}
	}

	if(mode==FO_CURSUP || mode==FO_CURSDOWN || mode==FO_PAGEUP || mode==FO_PAGEDOWN) {
		/* 2: curs up
		   3: curs down */
		ct= chartransdata+cu->pos;
		
		if((mode==FO_CURSUP || mode==FO_PAGEUP) && ct->linenr==0);
		else if((mode==FO_CURSDOWN || mode==FO_PAGEDOWN) && ct->linenr==lnr);
		else {
			switch(mode) {
				case FO_CURSUP:		lnr= ct->linenr-1; break;
				case FO_CURSDOWN:	lnr= ct->linenr+1; break;
				case FO_PAGEUP:		lnr= ct->linenr-10; break;
				case FO_PAGEDOWN:	lnr= ct->linenr+10; break;
			}
			cnr= ct->charnr;
			/* seek for char with lnr en cnr */
			cu->pos= 0;
			ct= chartransdata;
			for (i= 0; i<slen; i++) {
				if(ct->linenr==lnr) {
					if(ct->charnr==cnr) break;
					if( (ct+1)->charnr==0) break;
				}
				else if(ct->linenr>lnr) break;
				cu->pos++;
				ct++;
			}
		}
	}
	
	/* cursor first */
	if(ob==G.obedit) {
		ct= chartransdata+cu->pos;
		si= (float)sin(ct->rot);
		co= (float)cos(ct->rot);
				
		f= G.textcurs[0];
		
		f[0]= cu->fsize*(-0.1f*co + ct->xof);
		f[1]= cu->fsize*(0.1f*si + ct->yof);
		
		f[2]= cu->fsize*(0.1f*co + ct->xof);
		f[3]= cu->fsize*(-0.1f*si + ct->yof);
		
		f[4]= cu->fsize*( 0.1f*co + 0.8f*si + ct->xof);
		f[5]= cu->fsize*(-0.1f*si + 0.8f*co + ct->yof);
		
		f[6]= cu->fsize*(-0.1f*co + 0.8f*si + ct->xof);
		f[7]= cu->fsize*( 0.1f*si + 0.8f*co + ct->yof);
		
	}

	MEM_freeN(linedata);
	MEM_freeN(linedata2);		
	MEM_freeN(linedata3);
	MEM_freeN(linedata4);

	if (mode == FO_SELCHANGE) {
		MEM_freeN(chartransdata);
		MEM_freeN(mem);
		return NULL;
	}

	if(mode==0) {
		/* make nurbdata */
		unsigned long cha;

		freeNurblist(&cu->nurb);
		
		ct= chartransdata;
		if (cu->sepchar==0) {
		for (i= 0; i<slen; i++) {
			cha = (unsigned long) mem[i];
			info = &(cu->strinfo[i]);
			if (info->mat_nr > (ob->totcol)) {
				/* printf("Error: Illegal material index (%d) in text object, setting to 0\n", info->mat_nr); */
				info->mat_nr = 0;
			}
				// We do not want to see any character for \n or \r
				if(cha != '\n' && cha != '\r')
					buildchar(cu, cha, info, ct->xof, ct->yof, ct->rot, i);
				if ((info->flag & CU_UNDERLINE) && (cu->textoncurve == NULL) && (cha != '\n') && (cha != '\r')) {
					uloverlap = 0;
					if ( (i<(slen-1)) && (mem[i+1] != '\n') && (mem[i+1] != '\r') &&
					     ((mem[i+1] != ' ') || (cu->strinfo[i+1].flag & CU_UNDERLINE)) && ((cu->strinfo[i+1].flag & CU_WRAP)==0)
					   ) {
						uloverlap = xtrax + 0.1;
					}
					// Find the character, the characters has to be in the memory already 
					// since character checking has been done earlier already.
					che = vfd->characters.first;
					while(che)
					{
						if(che->index == cha)
							break;
						che = che->next;
					}

					if(!che) twidth =0; else twidth=che->width;
					ulwidth = cu->fsize * ((twidth* (1.0+(info->kern/40.0)))+uloverlap);
					build_underline(cu, ct->xof*cu->fsize, ct->yof*cu->fsize + (cu->ulpos-0.05)*cu->fsize, 
							ct->xof*cu->fsize + ulwidth, 
							ct->yof*cu->fsize + (cu->ulpos-0.05)*cu->fsize - cu->ulheight*cu->fsize, 
							i, info->mat_nr);
				}
			ct++;
		}
		}
		else {
		outta = 0;
		for (i= 0; (i<slen) && (outta==0); i++) {
			ascii = mem[i];
			info = &(cu->strinfo[i]);
				if (cu->sepchar == (i+1)) {
					mem[0] = ascii;
					mem[1] = 0;
					cu->strinfo[0]= *info;
					cu->pos = 1;
					cu->len = 1;
					vecyo[0] = ct->xof;
					vecyo[1] = ct->yof;
					vecyo[2] = 0;
					Mat4MulVecfl(ob->obmat, vecyo);
					VECCOPY(ob->loc, vecyo);
					outta = 1;
					cu->sepchar = 0;
				}
			ct++;
		}
	}
	}

	if(mode==FO_DUPLI) {
		MEM_freeN(mem);
		return chartransdata;
	}

	if(mem)
		MEM_freeN(mem);

	MEM_freeN(chartransdata);
	return 0;
}


