diff options
author | Alexander Ewering <blender@instinctive.de> | 2005-09-14 18:02:21 +0400 |
---|---|---|
committer | Alexander Ewering <blender@instinctive.de> | 2005-09-14 18:02:21 +0400 |
commit | 98bd4615b55bc66410764178148d91409bbf9ee1 (patch) | |
tree | 45e09576bdd916a2e1458abee75ccf7cd33c5a3a /source/blender/blenkernel | |
parent | 2cb24cefb2652870a457104d4f3ce6802ef9e16c (diff) |
On behalf of Mika Saari, the famous Unicode Font support!
Further information is available here:
http://wiki.blender.org/bin/view.pl/Blenderdev/UnicodeFont3D
Shortlist of features:
- Unicode character support for Font3D
- UI to select characters from Unicode character list
- UI to select Unicode table areas
- Optimized character loading (Load only those characters which are used
in font object)
Please test extensively if it breaks anything, try also loading/saving
files, packing fonts, etc.
The official text regression file in the regression suite should be a
good start.
Thanks to mikasaari for this very useful addition!
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_font.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_global.h | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/blender.c | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/font.c | 417 |
4 files changed, 379 insertions, 51 deletions
diff --git a/source/blender/blenkernel/BKE_font.h b/source/blender/blenkernel/BKE_font.h index ab0fcf6b994..fbc0d70fa22 100644 --- a/source/blender/blenkernel/BKE_font.h +++ b/source/blender/blenkernel/BKE_font.h @@ -56,6 +56,10 @@ int mat_to_sel(void); void font_duplilist(struct Object *par); int getselection(int *start, int *end); +void chtoutf8(unsigned long c, char *o); +void wcs2utf8s(char *dst, wchar_t *src); +int wcsleninu8(wchar_t *src); +int utf8towchar_(wchar_t *w, char *c); #endif diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 5ea8b398363..2335510ac2d 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -117,6 +117,11 @@ typedef struct Global { /* Rob's variables */ int have_quicktime; int ui_international; + int charstart; + int charmin; + int charmax; + struct VFont *selfont; + struct ListBase ttfdata; /* this variable is written to / read from FileGlobal->fileflags */ int fileflags; diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index b08b99a4010..2fca941a651 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -217,6 +217,10 @@ void initglobals(void) #endif clear_workob(); /* object.c */ + + G.charstart = 0x0000; + G.charmin = 0x0000; + G.charmax = 0xffff; } /***/ diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index af7a94e079e..500fed8831a 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -37,6 +37,7 @@ #include <string.h> #include <math.h> #include <stdlib.h> +#include <wchar.h> #ifdef HAVE_CONFIG_H #include <config.h> @@ -80,19 +81,151 @@ struct chartrans { char dobreak; }; -void free_vfont(struct VFont *vf) +/* 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) { - int i; + 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; +} + +int utf8towchar_(wchar_t *w, char *c) +{ + int len=0; + if(w==NULL || c==NULL) return(0); + //printf("%s\n",c); + while(*c) + { + if(*c & 0x80) + { + if(*c & 0x40) + { + if(*c & 0x20) + { + if(*c & 0x10) + { + *w=(c[0] & 0x0f)<<18 | (c[1]&0x1f)<<12 | (c[2]&0x3f)<<6 | (c[3]&0x7f); + c++; + } + else + *w=(c[0] & 0x1f)<<12 | (c[1]&0x3f)<<6 | (c[2]&0x7f); + c++; + } + else + *w=(((c[0] &0x3f)<<6) | (c[1]&0x7f)); + c++; + } + else + *w=(c[0] & 0x7f); + } + 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) { - for (i = 0; i < MAX_VF_CHARS; i++){ - while (vf->data->nurbsbase[i].first) { - Nurb *nu = vf->data->nurbsbase[i].first; + 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(&vf->data->nurbsbase[i], nu); + BLI_freelinkN(&che->nurbsbase, nu); } + + BLI_freelinkN(&vf->data->characters, che); } MEM_freeN(vf->data); @@ -131,6 +264,20 @@ static PackedFile *get_builtin_packedfile(void) static VFontData *vfont_get_data(VFont *vfont) { + struct TmpFont *tmpfnt = NULL; + PackedFile *tpf; + + // 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; @@ -139,8 +286,34 @@ static VFontData *vfont_get_data(VFont *vfont) } 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); @@ -170,7 +343,9 @@ 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); @@ -184,6 +359,8 @@ VFont *load_vfont(char *name) BLI_splitdirstring(dir, filename); pf= newPackedFile(name); + tpf= newPackedFile(name); + is_builtin= 0; } @@ -196,7 +373,7 @@ VFont *load_vfont(char *name) vfd= BLI_vfontdata_from_freetypefont(pf); #else vfd= BLI_vfontdata_from_psfont(pf); -#endif +#endif if (vfd) { vfont = alloc_libblock(&G.main->vfont, ID_VF, filename); @@ -208,7 +385,18 @@ VFont *load_vfont(char *name) 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); } @@ -281,12 +469,13 @@ static void build_underline(Curve *cu, float x1, float y1, float x2, float y2, i } -static void buildchar(Curve *cu, unsigned char ascii, CharInfo *info, float ofsx, float ofsy, float rot, int charidx) +static void buildchar(Curve *cu, unsigned long character, CharInfo *info, float ofsx, float ofsy, float rot, int charidx) { BezTriple *bezt1, *bezt2; - Nurb *nu1, *nu2; + Nurb *nu1 = NULL, *nu2 = NULL; float *fp, fsize, shear, x, si, co; - VFontData *vfd; + VFontData *vfd = NULL; + VChar *che = NULL; int i, sel=0; vfd= vfont_get_data(which_vfont(cu, info)); @@ -307,7 +496,20 @@ static void buildchar(Curve *cu, unsigned char ascii, CharInfo *info, float ofsx si= (float)sin(rot); co= (float)cos(rot); - nu1 = vfd->nurbsbase[ascii].first; + // 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; @@ -419,7 +621,7 @@ struct chartrans *text_to_curve(Object *ob, int mode) float *f, maxlen=0, xof, yof, xtrax, linedist, *linedata, *linedata2, *linedata3, *linedata4; int i, slen, oldflag, j; short cnr=0, lnr=0, wsnr= 0; - char ascii, *mem; + wchar_t *mem, *tmp, ascii; int outta; float vecyo[3], curofs; CharInfo *info; @@ -429,22 +631,39 @@ struct chartrans *text_to_curve(Object *ob, int mode) 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; - cu= ob->data; - mem= cu->str; - slen = strlen(mem); + // 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->str==0) return 0; if (cu->strinfo==NULL) { /* old file */ cu->strinfo = MEM_callocN((slen+1) * sizeof(CharInfo), "strinfo compat"); } + vfd= vfont_get_data(vfont); + if(!vfd) goto errcse; + /* calc offset and rotation of each char */ ct = chartransdata = (struct chartrans*)MEM_callocN((slen+1)* sizeof(struct chartrans),"buildtext"); @@ -476,23 +695,65 @@ struct chartrans *text_to_curve(Object *ob, int mode) curbox= 0; for (i = 0 ; i<=slen ; i++) { makebreak: - ascii = cu->str[i]; + // Characters in the list + che = vfd->characters.first; + ascii = mem[i]; info = &(cu->strinfo[i]); vfont = which_vfont(cu, info); - if (vfont==0) return 0; + + // 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 + + + if (vfont==0) goto errcse; if (vfont != oldvfont) { vfd= vfont_get_data(vfont); oldvfont = vfont; } - if (!vfd) return 0; - if((tb->w != 0.0) && (ct->dobreak==0) && ((xof-(tb->x/cu->fsize)+vfd->width[ascii])*cu->fsize) > tb->w) { -// fprintf(stderr, "linewidth exceeded: %c%c%c...\n", cu->str[i], cu->str[i+1], cu->str[i+2]); - for (j=i; j && (cu->str[j] != '\n') && (cu->str[j] != '\r') && (chartransdata[j].dobreak==0); j--) { - if (cu->str[j]==' ' || cu->str[j]=='-') { + if (!vfd) goto errcse; + + // 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 (cu->str[j] == ' ') wsnr--; - if (cu->str[j] == '-') wsnr++; + if (mem[j] == ' ') wsnr--; + if (mem[j] == '-') wsnr++; i = j-1; xof = ct->xof; ct[1].dobreak = 1; @@ -500,7 +761,7 @@ struct chartrans *text_to_curve(Object *ob, int mode) goto makebreak; } if (chartransdata[j].dobreak) { -// fprintf(stderr, "word too long: %c%c%c...\n", cu->str[j], cu->str[j+1], cu->str[j+2]); +// 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; @@ -533,7 +794,12 @@ struct chartrans *text_to_curve(Object *ob, int mode) 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; @@ -566,7 +832,16 @@ struct chartrans *text_to_curve(Object *ob, int mode) wsfac = cu->wordspace; wsnr++; } else wsfac = 1.0; - xof += (vfd->width[ascii]*wsfac*(1.0+(info->kern/40.0)) ) + xtrax; + // 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; } @@ -577,8 +852,9 @@ struct chartrans *text_to_curve(Object *ob, int mode) cu->lines= 1; ct= chartransdata; - for (i= 0; i<=slen; i++, mem++, ct++) { - ascii = *mem; + tmp = mem; + for (i= 0; i<=slen; i++, tmp++, ct++) { + ascii = *tmp; if(ascii== '\n' || ascii== '\r' || ct->dobreak) cu->lines++; } @@ -608,9 +884,9 @@ struct chartrans *text_to_curve(Object *ob, int mode) if(linedata2[i]>1) linedata[i]= (linedata3[i]-linedata[i])/(linedata2[i]-1); for (i=0; i<=slen; i++) { - for (j=i; (cu->str[j]) && (cu->str[j]!='\n') && - (cu->str[j]!='\r') && (chartransdata[j].dobreak==0) && (j<slen); j++); -// if ((cu->str[j]!='\r') && (cu->str[j]!='\n') && (cu->str[j])) { + 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++; @@ -619,14 +895,14 @@ struct chartrans *text_to_curve(Object *ob, int mode) (cu->tb[0].w != 0.0)) { curofs= 0; for (i=0; i<=slen; i++) { - for (j=i; (cu->str[j]) && (cu->str[j]!='\n') && - (cu->str[j]!='\r') && (chartransdata[j].dobreak==0) && (j<slen); j++); - if ((cu->str[j]!='\r') && (cu->str[j]!='\n') && + 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 (cu->str[i]==' ') curofs += (linedata3[ct->linenr]-linedata[ct->linenr])/linedata4[ct->linenr]; + if (mem[i]==' ') curofs += (linedata3[ct->linenr]-linedata[ct->linenr])/linedata4[ct->linenr]; ct->xof+= curofs; } - if (cu->str[i]=='\n' || cu->str[i]=='\r' || chartransdata[i].dobreak) curofs= 0; + if (mem[i]=='\n' || mem[i]=='\r' || chartransdata[i].dobreak) curofs= 0; ct++; } } @@ -685,9 +961,28 @@ struct chartrans *text_to_curve(Object *ob, int mode) for (i=0; i<=slen; i++, ct++) { /* rotate around centre character */ - ascii = cu->str[i]; - dtime= distfac*0.35f*vfd->width[ascii]; /* why not 0.5? */ - dtime= distfac*0.0f*vfd->width[ascii]; /* why not 0.5? */ + 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.0f*twidth; /* why not 0.5? */ ctime= timeofs + distfac*( ct->xof - minx); CLAMP(ctime, 0.0, 1.0); @@ -782,32 +1077,47 @@ struct chartrans *text_to_curve(Object *ob, int mode) 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++) { - ascii = cu->str[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; } - buildchar(cu, ascii, info, ct->xof, ct->yof, ct->rot, i); - if ((info->flag & CU_UNDERLINE) && (ascii != '\n') && (ascii != '\r')) { + // 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) && (cha != '\n') && (cha != '\r')) { uloverlap = 0; - if ( (i<(slen-1)) && (cu->str[i+1] != '\n') && (cu->str[i+1] != '\r') && - ((cu->str[i+1] != ' ') || (cu->strinfo[i+1].flag & CU_UNDERLINE)) && ((cu->strinfo[i+1].flag & CU_WRAP)==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; } - ulwidth = cu->fsize * ((vfd->width[ascii]* (1.0+(info->kern/40.0)))+uloverlap); + // 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, @@ -819,11 +1129,11 @@ struct chartrans *text_to_curve(Object *ob, int mode) else { outta = 0; for (i= 0; (i<slen) && (outta==0); i++) { - ascii = cu->str[i]; + ascii = mem[i]; info = &(cu->strinfo[i]); if (cu->sepchar == (i+1)) { - cu->str[0] = ascii; - cu->str[1] = 0; + mem[0] = ascii; + mem[1] = 0; cu->strinfo[0]= *info; cu->pos = 1; cu->len = 1; @@ -841,9 +1151,14 @@ struct chartrans *text_to_curve(Object *ob, int mode) } if(mode==FO_DUPLI) { + MEM_freeN(mem); return chartransdata; } +errcse: + if(mem) + MEM_freeN(mem); + MEM_freeN(chartransdata); return 0; } |