iup-stack/iup/srcmglplot/src/font.cpp

934 lines
33 KiB
C++
Raw Permalink Normal View History

2023-02-20 16:44:45 +00:00
/***************************************************************************
* font.cpp is part of Math Graphic Library
* Copyright (C) 2007-2016 Alexey Balakin <mathgl.abalakin@gmail.ru> *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Library General Public License as *
* published by the Free Software Foundation; either version 3 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 Library 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. *
***************************************************************************/
#include <locale.h>
#include <ctype.h>
#include <wctype.h>
#if !defined(__BORLANDC__) || (__CODEGEARC__ >= 0x0630)
#include <algorithm>
#else
#include <algorithm.h>
#endif
#include "mgl2/base.h"
#include "mgl2/font.h"
#include "def_font.cc"
#include "tex_table.cc"
//-----------------------------------------------------------------------------
//mglFont mglDefFont("nofont");
mglFont mglDefFont;
//-----------------------------------------------------------------------------
long MGL_EXPORT_PURE mgl_internal_code(unsigned s, const std::vector<mglGlyphDescr> &glyphs)
{
register long i1=0,i2=glyphs.size()-1;
register wchar_t j = wchar_t(s & MGL_FONT_MASK);
// let suppose that id[i]<id[i+1]
while(i1<i2)
{
register long i = (i1+i2)/2;
if(j<glyphs[i].id) i2 = i;
else if(j>glyphs[i].id) i1=i+1;
else return i;
}
return j==glyphs[i2].id ? i2 : -1;
}
//-----------------------------------------------------------------------------
bool MGL_EXPORT mglGetStyle(const char *how, int *font, int *align)
{
bool col=false;
if(align) *align = 1; // centering text by default
if(!how || *how==0) return col;
// NOTE: no brightness for text color
for(;*how && *how!=':';how++)
{
if(strchr(MGL_COLORS,*how)) col = true;
if(*how=='{' && how[1]=='x') col = true;
}
if(align)
{
*align = 1;
if(strchr(how,'R')) *align = 2;
if(strchr(how,'C')) *align = 1;
if(strchr(how,'L')) *align = 0;
if(strchr(how,'D')) *align+= 4;
}
if(font)
{
*font = 0;
if(strchr(how,'b')) *font = *font|MGL_FONT_BOLD;
if(strchr(how,'i')) *font = *font|MGL_FONT_ITAL;
if(strchr(how,'w')) *font = *font|MGL_FONT_WIRE;
if(strchr(how,'o')) *font = *font|MGL_FONT_OLINE;
if(strchr(how,'u')) *font = *font|MGL_FONT_ULINE;
}
return col;
}
//-----------------------------------------------------------------------------
float mglFont::Puts(const char *str,const char *how,float c1,float c2) const
{
int font=0, align=1; float w=0;
mglGetStyle(how,&font,&align);
MGL_TO_WCS(str,w = Puts(wcs,font,align,c1,c2));
return w;
}
//-----------------------------------------------------------------------------
float mglFont::Width(const char *str,const char *how) const
{
int font=0; float w=0;
mglGetStyle(how,&font);
MGL_TO_WCS(str,w = Width(wcs,font));
return w;
}
//-----------------------------------------------------------------------------
float mglFont::Puts(const wchar_t *str,const char *how,float c1,float c2) const
{
int font=0, align=1;
mglGetStyle(how,&font,&align);
return Puts(str, font, align,c1,c2);
}
//-----------------------------------------------------------------------------
float mglFont::Width(const wchar_t *str,const char *how) const
{
int font=0;
mglGetStyle(how,&font);
return Width(str, font);
}
//-----------------------------------------------------------------------------
float mglFont::Puts(const wchar_t *str,int font,int align, float c1,float c2) const
{
if(GetNumGlyph()==0 || !str || *str==0) return 0;
float ww=0,w=0,h = (align&4) ? 500./fact[0] : 0;
size_t size = mgl_wcslen(str)+1,num=0;
if(parse)
{
unsigned *wcs = new unsigned[size], *buf=wcs;
memcpy(wcs,str,size*sizeof(wchar_t));
Convert(str, wcs);
for(size_t i=0;wcs[i];i++)
{
if(wcs[i]=='\n') // parse '\n' symbol
{
wcs[i]=0; w = Puts(buf,0,0,1.f,0x10|font,c1,c2); // find width
Puts(buf,-w*(align&3)/2.f,-h - 500.*num/fact[0],1.f,font,c1,c2); // draw it really
buf=wcs+i+1; num++; if(w>ww) ww=w;
}
if(wcs[i]=='\\' && wcs[i+1]=='n' && (wcs[i+2]>' ' || wcschr(L"{}[]()!@#$%^&*/-?.,_=+\\\"", wcs[i+2]))) // parse '\n' symbol
{
wcs[i]=0; w = Puts(buf,0,0,1.f,0x10|font,c1,c2); // find width
Puts(buf,-w*(align&3)/2.f,-h - 500.*num/fact[0],1.f,font,c1,c2); // draw it really
buf=wcs+i+2; num++; if(w>ww) ww=w;
}
}
// draw string itself
w = Puts(buf,0,0,1.f,0x10|font,c1,c2); // find width
Puts(buf,-w*(align&3)/2.f,-h - 500.*num/fact[0],1.f,font,c1,c2); // draw it really
if(w>ww) ww=w;
delete []wcs;
}
else
{
int s = (font/MGL_FONT_BOLD)&3;
h *= fact[0]/fact[s];
for(size_t i=0;i<size;i++) // find width
{
long j = str[i]!=' ' ? Internal(str[i]) : Internal('!');
if(j==-1) continue;
w+= GetWidth(s,j)/fact[s];
}
ww = w; w *= -(align&3)/2.f;
if(gr) for(size_t i=0;i<size;i++) // draw it
{
long j=0; //Internal('!');
if(str[i]!=' ')
{
j = Internal(str[i]);
if(j==-1) continue;
gr->Glyph(w, -h, 1, (s+(font&MGL_FONT_WIRE))?4:0, j, c1+i*(c2-c1)/(size-1));
}
w+= GetWidth(s,j)/fact[s];
}
}
return ww;
}
//-----------------------------------------------------------------------------
float mglFont::Width(const wchar_t *str,int font) const
{
if(GetNumGlyph()==0 || !str || *str==0) return 0;
float ww=0,w=0;
size_t size = mgl_wcslen(str)+1;
if(parse)
{
unsigned *wcs = new unsigned[size], *buf=wcs;
memcpy(wcs,str,size*sizeof(wchar_t));
Convert(str, wcs);
for(size_t i=0;wcs[i];i++) if(wcs[i]=='\n') // parse '\n' symbol
{
wcs[i]=0; w = Puts(buf,0,0,1.,0x10|font,'k','k'); // find width
buf=wcs+i+1; if(w>ww) ww=w;
}
w = Puts(buf,0,0,1.,0x10|font,'k','k');
if(w<ww) w=ww;
delete []wcs;
}
else
{
int s = (font/MGL_FONT_BOLD)&3;
for(size_t i=0;i<size;i++)
{
long j = str[i]!=' ' ? Internal(str[i]) : Internal('!');
if(j==-1) continue;
w+= GetWidth(s,j)/fact[s];
}
}
return w;
}
//-----------------------------------------------------------------------------
float mglFont::Height(int font) const
{
if(GetNumGlyph()==0) return 0;
int s = (font/MGL_FONT_BOLD)&3;
return (500.f)/fact[s];
}
//-----------------------------------------------------------------------------
float mglFont::Height(const char *how) const
{
if(GetNumGlyph()==0) return 0;
int s=0;
if(how)
{
if(strchr(how,'b')) s = s|1;
if(strchr(how,'i')) s = s|2;
}
return (500.f)/fact[s];
}
//-----------------------------------------------------------------------------
/// Table of acents and its UTF8 codes
MGL_NO_EXPORT mglTeXsymb mgl_act_symb[] = {
{0x02c6, L"hat"}, {0x02dc, L"tilde"}, {0x02d9, L"dot"}, {0x00a8, L"ddot"}, {0x20db, L"dddot"}, {0x20dc, L"ddddot"}, {0x02ca, L"acute"}, {0x02c7, L"check"}, {0x02cb, L"grave"}, {0x20d7, L"vec"}, {0x02c9, L"bar"}, {0x02d8, L"breve"},
/*end*/{0, L"\0"}};
//-----------------------------------------------------------------------------
int MGL_LOCAL_PURE mgl_tex_symb_cmp(const void *a, const void *b)
{
const mglTeXsymb *aa = (const mglTeXsymb *)a;
const mglTeXsymb *bb = (const mglTeXsymb *)b;
return wcscmp(aa->tex, bb->tex);
}
//-----------------------------------------------------------------------------
// parse LaTeX commands (mostly symbols and acents, and some font-style commands)
unsigned mglFont::Parse(const wchar_t *s) const
{
register long k;
unsigned res = unsigned(-2); // Default is no symbol
if(!s || !s[0]) return res;
// for(k=0;mgl_tex_symb[k].kod;k++); // determine the number of symbols
mglTeXsymb tst, *rts;
tst.tex = s;
rts = (mglTeXsymb *) bsearch(&tst, mgl_tex_symb, mgl_tex_num, sizeof(mglTeXsymb), mgl_tex_symb_cmp);
if(rts) return rts->kod;
// for(k=0;mgl_tex_symb[k].kod;k++) // special symbols
// if(!wcscmp(s,mgl_tex_symb[k].tex))
// return mgl_tex_symb[k].kod;
for(k=0;mgl_act_symb[k].kod;k++) // acents
if(!wcscmp(s,mgl_act_symb[k].tex))
return mgl_act_symb[k].kod | MGL_FONT_ZEROW;
// arbitrary UTF symbol
if(s[0]=='u' && s[1]=='t' && s[2]=='f')
// { wscanf(s+3,"%lx",&k); return wchar_t(k); }
{ k = wcstoul(s+3,NULL,16); return wchar_t(k); }
// font/style changes for next symbol
if(!wcscmp(s,L"big")) res = unsigned(-5);
else if(!wcscmp(s,L"frac")) res = unsigned(-6);
else if(!wcscmp(s,L"stack")) res = unsigned(-7);
else if(!wcscmp(s,L"overset")) res = unsigned(-8);
else if(!wcscmp(s,L"underset")) res = unsigned(-9);
else if(!wcscmp(s,L"stackr")) res = unsigned(-10);
else if(!wcscmp(s,L"stackl")) res = unsigned(-11);
else if(!wcscmp(s,L"sub")) res = unsigned(-12);
else if(!wcscmp(s,L"sup")) res = unsigned(-13);
else if(!wcscmp(s,L"textsc")) res = unsigned(-14); // new
else if(!wcscmp(s,L"b")) res = MGL_FONT_BOLD;
else if(!wcscmp(s,L"i")) res = MGL_FONT_ITAL;
else if(!wcscmp(s,L"bi")) res = MGL_FONT_BOLD|MGL_FONT_ITAL;
else if(!wcscmp(s,L"r")) res = unsigned(-1);
else if(!wcscmp(s,L"a")) res = MGL_FONT_OLINE;
else if(!wcscmp(s,L"u")) res = MGL_FONT_ULINE;
else if(!wcscmp(s,L"n")) res = '\n';
else if(!wcscmp(s,L"overline")) res = MGL_FONT_OLINE;
else if(!wcscmp(s,L"underline"))res = MGL_FONT_ULINE;
else if(!wcscmp(s,L"textbf")) res = MGL_FONT_BOLD;
else if(!wcscmp(s,L"textit")) res = MGL_FONT_ITAL;
else if(!wcscmp(s,L"textrm")) res = unsigned(-1);
else if(!wcscmp(s,L"T2A")) res = unsigned(-1);
else if(!wcscmp(s,L"w")) res = MGL_FONT_WIRE;
else if(!wcscmp(s,L"wire")) res = MGL_FONT_WIRE;
else if(!wcsncmp(s,L"color",5)) res = MGL_COLOR_MASK + (0xff & s[5]);
return res;
}
//-----------------------------------------------------------------------------
void mglFont::Convert(const wchar_t *str, unsigned *res) const
{
register size_t r,i,j,k,i0;
wchar_t s[128]=L""; // TeX command and current char
for(i=j=0;str[i];i++)
{
wchar_t ch = str[i];
if(ch=='\\') // It can be TeX command
{
if(wcschr(L"{}_^\\@# ",str[i+1])) // No, it is usual symbol
res[j++] = str[++i];
else // Yes, it is TeX command
{
i0=i+1;
for(k=0;isalnum(str[++i]) && k<127;k++) s[k] = str[i];
s[k] = 0;
r = Parse(s);
if(r==unsigned(-2)) // command not found, so use next symbol itself
{ res[j++] = str[i0]; i = i0; }
else if(r)
{
res[j++] = r;
if(str[i]>' ') i--;
if(str[i]==0) break;
}
}
}
else if(ch=='-' && str[i+1]=='-') { res[j++] = 0x2212; i++; }
else if(ch=='\b'){}
else if(ch<=' ' && ch!='\n') res[j++] = ' '; // no \t at this moment :(
else if(ch=='_') res[j++] = MGL_FONT_LOWER;
else if(ch=='^') res[j++] = MGL_FONT_UPPER;
else if(ch=='@') res[j++] = MGL_FONT_UPPER|MGL_FONT_LOWER;
else if(ch=='{') res[j++] = unsigned(-3);
else if(ch=='}') res[j++] = unsigned(-4);
else if(ch=='#' && str[i+1]>' ')
res[j++] = MGL_COLOR_MASK + (0xff & str[++i]); // TODO inline colors -- stack of RGBA colors + index
else res[j++] = ch; // It is just symbol
}
res[j] = 0;
}
//-----------------------------------------------------------------------------
float mglFont::get_ptr(long &i,unsigned *str, unsigned **b1, unsigned **b2,float &w1,float &w2, float f1, float f2, int st) const
{
static unsigned s1[2]={0,0}, s2[2]={0,0};
register long k;
i++;
if(str[i]==unsigned(-3))
{
i++; *b1 = str+i;
for(k=1;k>0 && str[i];i++)
{
if(str[i]==unsigned(-4)) k--;
if(str[i]==unsigned(-3)) k++;
}
str[i-1]=0;
}
else { s1[0] = str[i]; *b1 = s1; i++; }
if(str[i]==unsigned(-3))
{
i++; *b2 = str+i;
for(k=1;k>0 && str[i];i++)
{
if(str[i]==unsigned(-4)) k--;
if(str[i]==unsigned(-3)) k++;
}
str[i-1]=0;
}
else { s2[0] = str[i]; *b2 = s2; i++; }
i--;
w1 = Puts(*b1, 0, 0, f1, 0x10|st,'k','k');
w2 = Puts(*b2, 0, 0, f2, 0x10|st,'k','k');
return w1>w2 ? w1 : w2;
}
//-----------------------------------------------------------------------------
void mglFont::draw_ouline(int st, float x, float y, float f, float g, float ww, float ccol) const
{
if(st&MGL_FONT_OLINE)
gr->Glyph(x,y+499*f/g, ww*g, (st&MGL_FONT_WIRE)?12:8, 0, ccol);
if(st&MGL_FONT_ULINE)
gr->Glyph(x,y-200*f/g, ww*g, (st&MGL_FONT_WIRE)?12:8, 0, ccol);
}
//-----------------------------------------------------------------------------
#define MGL_CLEAR_STYLE {st = style; yy = y; ff = f; ccol=c1+dc*i; a = (st/MGL_FONT_BOLD)&3;}
float mglFont::Puts(const unsigned *text, float x,float y,float f,int style,float c1,float c2) const
{
if(GetNumGlyph()==0) return 0;
float w=0; // string width
int st = style; // current style
unsigned *b1, *b2; // pointer to substring
unsigned *str; // string itself
float yy=y, ff=f, ww, w1, w2;
int a = (st/MGL_FONT_BOLD)&3;
long i;
for(i=0;text[i];i++);
float dc=i>1?(c2-c1)/(i-1):0;
str = new unsigned[i+1];
memcpy(str,text,(i+1)*sizeof(unsigned));
float ccol = 0;
for(long i=0;str[i];i++)
{
ccol = ccol<0?ccol:c1+dc*i;
unsigned s = str[i]; ww = 0;
if(s==unsigned(-3)) // recursion call here
{
i++; b1 = str+i;
for(long k=1;k>0 && str[i];i++)
{
if(str[i]==unsigned(-4)) k--;
if(str[i]==unsigned(-3)) k++;
}
str[i-1]=0; i--;
ww = Puts(b1, x, yy, ff, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
if(gr && !(style&0x10)) // add under-/over- line now
draw_ouline(st,x,y,f,fact[a],ww,ccol);
MGL_CLEAR_STYLE
}
else if(s=='\n') // newline
{
ww = get_ptr(i, str, &b1, &b2, w1, w2, ff, ff, st);
Puts(b1, x+(ww-w1)/2, yy, ff, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
Puts(b2, x+(ww-w2)/2, yy-600*ff/fact[a], ff, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
MGL_CLEAR_STYLE
}
else if(s==unsigned(-9)) // underset
{
ww = get_ptr(i, str, &b1, &b2, w1, w2, ff, ff/4, st);
Puts(b1, x+(ww-w1)/2, yy, ff, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
Puts(b2, x+(ww-w2)/2, yy-150*ff/fact[a], ff/4, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
if(gr && !(style&0x10)) // add under-/over- line now
draw_ouline(st,x,y,f,fact[a],ww,ccol);
MGL_CLEAR_STYLE
}
else if(s==unsigned(-8)) // overset
{
ww = get_ptr(i, str, &b1, &b2, w1, w2, ff, ff/4, st);
Puts(b1, x+(ww-w1)/2, yy, ff, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
Puts(b2, x+(ww-w2)/2, yy+375*ff/fact[a], ff/4, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
if(gr && !(style&0x10)) // add under-/over- line now
draw_ouline(st,x,y,f,fact[a],ww,ccol);
MGL_CLEAR_STYLE
}
else if(s==unsigned(-12)) // sub
{
ww = get_ptr(i, str, &b1, &b2, w1, w2, ff, ff/4, st);
Puts(b1, x+(ww-w1)/2, yy, ff, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
Puts(b2, x+(ww-w2)/2, yy-250*ff/fact[a], ff/4, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
if(gr && !(style&0x10)) // add under-/over- line now
draw_ouline(st,x,y,f,fact[a],ww,ccol);
MGL_CLEAR_STYLE
}
else if(s==unsigned(-13)) // sup
{
ww = get_ptr(i, str, &b1, &b2, w1, w2, ff, ff/4, st);
Puts(b1, x+(ww-w1)/2, yy, ff, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
Puts(b2, x+(ww-w2)/2, yy+450*ff/fact[a], ff/4, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
if(gr && !(style&0x10)) // add under-/over- line now
draw_ouline(st,x,y,f,fact[a],ww,ccol);
MGL_CLEAR_STYLE
}
else if(s==unsigned(-11)) // stackl
{
ww = get_ptr(i, str, &b1, &b2, w1, w2, ff*0.45, ff*0.45, st);
Puts(b1, x, yy+250*ff/fact[a], ff*0.45, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
Puts(b2, x, yy-110*ff/fact[a], ff*0.45, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
if(gr && !(style&0x10)) // add under-/over- line now
draw_ouline(st,x,y,f,fact[a],ww,ccol);
MGL_CLEAR_STYLE
}
else if(s==unsigned(-10)) // stacr
{
ww = get_ptr(i, str, &b1, &b2, w1, w2, ff*0.45, ff*0.45, st);
Puts(b1, x+(ww-w1), yy+250*ff/fact[a], ff*0.45, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
Puts(b2, x+(ww-w2), yy-110*ff/fact[a], ff*0.45, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
if(gr && !(style&0x10)) // add under-/over- line now
draw_ouline(st,x,y,f,fact[a],ww,ccol);
MGL_CLEAR_STYLE
}
else if(s==unsigned(-7)) // stack
{
ww = get_ptr(i, str, &b1, &b2, w1, w2, ff*0.45, ff*0.45, st);
Puts(b1, x+(ww-w1)/2, yy+250*ff/fact[a], ff*0.45, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
Puts(b2, x+(ww-w2)/2, yy-110*ff/fact[a], ff*0.45, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
if(gr && !(style&0x10)) // add under-/over- line now
draw_ouline(st,x,y,f,fact[a],ww,ccol);
MGL_CLEAR_STYLE
}
else if(s==unsigned(-6)) // frac
{
ww = get_ptr(i, str, &b1, &b2, w1, w2, ff*0.45, ff*0.45, st);
Puts(b1, x+(ww-w1)/2, yy+250*ff/fact[a], ff*0.45, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
Puts(b2, x+(ww-w2)/2, yy-110*ff/fact[a], ff*0.45, (st&(~MGL_FONT_OLINE)&(~MGL_FONT_ULINE)), ccol,ccol);
if(gr && !(style&0x10)) // add under-/over- line now
{
draw_ouline(st,x,y,f,fact[a],ww,ccol);
gr->Glyph(x,y+150*f/fact[a], ww*fact[a], (st&MGL_FONT_WIRE)?12:8, 0, ccol);
}
MGL_CLEAR_STYLE
}
else if(s==unsigned(-4)) MGL_CLEAR_STYLE // should be never here but if I miss sth ...
else if(s==unsigned(-14)) // script symbols
{
long k=1;
if(str[i+1]==unsigned(-3)) for(long j=i+2;k>0 && str[j];j++)
{
if(str[j]==unsigned(-3)) k++;
if(str[j]==unsigned(-4)) k--;
if(iswlower(str[j]))
str[j] = MGL_FONT_UPPER|MGL_FONT_LOWER|towupper(str[j]);
}
}
else if(s==unsigned(-5)) // large symbol
ff *= 1.5;
else if(s==unsigned(-1)) // set normal font
st = style & MGL_FONT_ROMAN;
else if((s&MGL_COLOR_MASK)==MGL_COLOR_MASK) // color specification
ccol = -float(s & 0xff); // TODO inline colors -- make textures
else
{
unsigned ss = s&MGL_FONT_MASK;
if(ss) // draw symbol (glyph)
{
long j = Internal('!');
float dx=0;
if(ss>' ')
{
j = Internal(ss);
if(j==-1) continue;
if(s & MGL_FONT_ZEROW)
{
long j=1;
yy += 100*ff/fact[a];
while(str[i+j]>=unsigned(-15)) j++;
unsigned sn = str[i+j];
if(sn<unsigned(-15) && (sn&MGL_FONT_MASK)>' ') // specially center
{
dx = 0.75*ff*(GetWidth(a,Internal(sn&MGL_FONT_MASK))-GetWidth(a,j))/fact[a];
if(dx<0) dx=0;
}
}
if(gr && !(style&0x10))
{
if(st & MGL_FONT_WIRE) gr->Glyph(x+dx,yy,ff,a+4,j,ccol);
else gr->Glyph(x+dx,yy,ff,a,j,ccol);
}
}
ww = ff*GetWidth(a,j)/fact[a];
if(gr && !(style&0x10)) // add under-/over- line now
draw_ouline(st,x,y,f,fact[a],ww,ccol);
if(s & MGL_FONT_ZEROW) ww = 0;
MGL_CLEAR_STYLE
}
// apply new styles
if(s/MGL_FONT_BOLD) st = st | (s & MGL_FONT_STYLE);
a = (st/MGL_FONT_BOLD)&3;
ss = (s/MGL_FONT_UPPER)%4;
if(ss)
{
if(ss==1) { ff *=0.6; yy += 200*ff/fact[a]; } // = 500*0.4
else if(ss==2) { ff *=0.6; yy -= 80*ff/fact[a]; } // = -500*0.16
else if(ss==3) { ff *=0.8; yy += 0*60*ff/fact[a]; } // = 500*0.12
}
}
x += ww; w += ww;
}
delete []str;
return w;
}
//-----------------------------------------------------------------------------
// copy normal style as default for other styles
void mglFont::main_copy()
{
#pragma omp parallel for
for(long i=0;i<long(glyphs.size());i++)
{
mglGlyphDescr &g = glyphs[i];
g.numl[1] = g.numl[2] = g.numl[3] = g.numl[0];
g.numt[1] = g.numt[2] = g.numt[3] = g.numt[0];
g.ln[1] = g.ln[2] = g.ln[3] = g.ln[0];
g.tr[1] = g.tr[2] = g.tr[3] = g.tr[0];
g.width[1] = g.width[2] = g.width[3] = g.width[0];
}
}
//-----------------------------------------------------------------------------
bool mglFont::read_def()
{
// copy default factor for other font styles;
fact[1] = fact[2] = fact[3] = fact[0] = mgl_fact*mgl_fgen;
Buf = new short[mgl_cur]; // prealocate buffer
memset(Buf,0,mgl_cur*sizeof(short));
// now allocate memory for all fonts
mem_alloc(mgl_numg);
// and load symbols itself
#ifndef WIN32 // win32 don't initialized threads before main()
#pragma omp parallel for
#endif
for(size_t i=0;i<mgl_numg;i++)
{
mglGlyphDescr &g = glyphs[i];
g.id = mgl_gen_fnt[i][0];
g.width[0] = g.width[1] = g.width[2] = g.width[3] = mgl_gen_fnt[i][1];
g.numl[0] = g.numl[1] = g.numl[2] = g.numl[3] = mgl_gen_fnt[i][2];
g.ln[0] = g.ln[1] = g.ln[2] = g.ln[3] = mgl_gen_fnt[i][3];
g.numt[0] = g.numt[1] = g.numt[2] = g.numt[3] = mgl_gen_fnt[i][4];
g.tr[0] = g.tr[1] = g.tr[2] = g.tr[3] = mgl_gen_fnt[i][5];
}
memcpy(Buf, mgl_buf_fnt, mgl_cur*sizeof(short));
numb = mgl_cur;
return true;
}
//-----------------------------------------------------------------------------
bool mglFont::read_data(const char *fname, int s, std::vector<short> &buf, std::vector<mglGlyphDescr> &extra)
{
gzFile fp;
char str[256];
int n, tmpw, tmpnl, tmpnt, retVal;
unsigned ss, tmpi, tmppl, tmppt;
fp = gzopen(fname,"r"); if(!fp) return false; // false if no file
// first string is comment (not used), second string have information
if(!gzgets(fp,str,256) || strncmp(str,"# font",6) || !gzgets(fp,str,256))
{ gzclose(fp); return false; }
retVal = sscanf(str, "%d%f%u", &n, fact+s, &ss);
//Check sscanf read all data (3 items)
if(retVal != 3) { gzclose(fp); return false; }
for(int i=0;i<n;i++)
{
gzgets(fp,str,256);
retVal = sscanf(str,"%u%d%d%u%d%u", &tmpi, &tmpw, &tmpnl, &tmppl, &tmpnt, &tmppt);
if(retVal != 6) { gzclose(fp); buf.clear(); return false; }
long j=Internal(unsigned(tmpi));
if(j>=0) // known symbol
{
mglGlyphDescr &g = glyphs[j]; g.width[s] = tmpw;
g.ln[s] = -1-tmppl; g.tr[s] = -1-tmppt;
g.numl[s] = tmpnl; g.numt[s] = tmpnt;
}
else
{
mglGlyphDescr g; g.id = tmpi;
g.width[0] = g.width[1] = g.width[2] = g.width[3] = tmpw;
g.numl[0] = g.numl[1] = g.numl[2] = g.numl[3] = tmpnl;
g.ln[0] = g.ln[1] = g.ln[2] = g.ln[3] = -1-tmppl;
g.numt[0] = g.numt[1] = g.numt[2] = g.numt[3] = tmpnt;
g.tr[0] = g.tr[1] = g.tr[2] = g.tr[3] = -1-tmppt;
#pragma omp critical
extra.push_back(g);
}
}
for(unsigned i=0;i<ss;i++)
{
for(int j=0;j<256;j++) if((str[j] = gzgetc(fp))<=' ') break;
buf.push_back(atoi(str));
}
gzclose(fp); // finish wire normal font
return true;
}
//-----------------------------------------------------------------------------
bool mglFont::read_main(const char *fname, std::vector<short> &buf)
{
gzFile fp;
int tmpi, tmpw, tmpnl, tmpnt;
unsigned s, tmppl, tmppt,numg;
char str[256];
fp = gzopen(fname,"r"); if(!fp) return false; // this font must be in any case
// first string is comment (not used), second string have information
if(!gzgets(fp,str,256) || strncmp(str,"# font",6) || !gzgets(fp,str,256))
{ gzclose(fp); return false; }
sscanf(str, "%u%f%u", &numg, fact, &s);
fact[1] = fact[2] = fact[3] = fact[0]; // copy default factor for other font styles;
// now allocate memory for all fonts
mem_alloc(numg);
// and load symbols itself
for(size_t i=0;i<numg;i++)
{
gzgets(fp,str,256);
sscanf(str,"%d%d%d%u%d%u", &tmpi, &tmpw, &tmpnl, &tmppl, &tmpnt, &tmppt);
mglGlyphDescr &g = glyphs[i]; g.id = tmpi;
g.width[0] = g.width[1] = g.width[2] = g.width[3] = tmpw;
g.numl[0] = g.numl[1] = g.numl[2] = g.numl[3] = tmpnl;
g.ln[0] = g.ln[1] = g.ln[2] = g.ln[3] = tmppl;
g.numt[0] = g.numt[1] = g.numt[2] = g.numt[3] = tmpnt;
g.tr[0] = g.tr[1] = g.tr[2] = g.tr[3] = tmppt;
}
for(unsigned i=0;i<s;i++)
{
for(int j=0;j<256;j++) if((str[j] = gzgetc(fp))<=' ') break;
buf.push_back(atoi(str));
}
gzclose(fp); // finish wire normal font
return true;
}
//-----------------------------------------------------------------------------
size_t mglFont::SaveBin(const char *fname)
{
FILE *fp = fopen(fname,"wb");
if(!fp) return 0;
size_t sum=0;
fwrite(&numb,sizeof(size_t),1,fp); sum += sizeof(size_t);
fwrite(fact,sizeof(float),4,fp); sum += sizeof(float)*4;
fwrite(Buf,sizeof(short),numb,fp); sum += sizeof(short)*numb;
size_t len = glyphs.size();
fwrite(&len,sizeof(size_t),1,fp); sum += sizeof(long);
fwrite(&(glyphs[0]),sizeof(mglGlyphDescr),len,fp); sum += sizeof(mglGlyphDescr)*len;
fclose(fp); return sum;
}
//-----------------------------------------------------------------------------
bool mglFont::LoadBin(const char *base, const char *path)
{
Clear(); // first clear old
if(!path) path = MGL_FONT_PATH;
char str[256], sep='/';
snprintf(str,256,"%s%c%s.vfmb",path,sep,base?base:""); str[255]=0;
FILE *fp = fopen(str,"rb"); if(!fp) return false;
size_t s, len;
bool res = true;
s = fread(&numb,sizeof(size_t),1,fp);
if(s<1) res = false;
s = fread(fact,sizeof(float),4,fp);
if(s<4) res = false;
Buf = new short[numb];
s = fread(Buf,sizeof(short),numb,fp);
if(s<numb) res = false;
s = fread(&len,sizeof(size_t),1,fp);
if(s<1) res = false;
if(res)
{
glyphs.clear(); glyphs.resize(len);
s = fread(&(glyphs[0]),sizeof(mglGlyphDescr),len,fp);
if(s<len) res = false;
}
// if(!res) Clear();
fclose(fp); return res;
}
//-----------------------------------------------------------------------------
bool mglFont::Load(const char *base, const char *path)
{
// base = 0;
char *buf=0,sep='/';
#ifdef WIN32
sep='\\';
#endif
char str[256];
std::string loc = setlocale(LC_NUMERIC,"C");
if(!path) path = MGL_FONT_PATH;
if(base && *base)
{
buf = new char[strlen(base)+1];
strcpy(buf,base);
if(strchr(buf,sep))
{
int i;
for(i=strlen(buf);i>=0 && buf[i]!=sep;i--);
path = buf; buf[i]=0; base = buf+i+1;
}
if(LoadBin(base,path))
{ delete []buf; return true; }
}
Clear(); // first clear old
snprintf(str,256,"%s%c%s.vfm",path,sep,base?base:""); str[255]=0;
std::vector<short> norm, bold, ital, both;
if(!(base && *base) || !read_main(str,norm))
{
read_def(); setlocale(LC_NUMERIC,loc.c_str());
if(buf) delete []buf; return true;
}
fact[1] = fact[2] = fact[3] = fact[0];
std::vector<mglGlyphDescr> ex_b,ex_i,ex_bi;
#pragma omp parallel sections
{
//================== bold ===========================================
#pragma omp section
{ char str[256]; snprintf(str,256,"%s%c%s_b.vfm",path,sep,base); // this file may absent
str[255]=0; read_data(str, 1, bold, ex_b); }
//================== italic =========================================
#pragma omp section
{ char str[256]; snprintf(str,256,"%s%c%s_i.vfm",path,sep,base);
str[255]=0; read_data(str, 2, ital, ex_i); }
//================== bold-italic ====================================
#pragma omp section
{ char str[256]; snprintf(str,256,"%s%c%s_bi.vfm",path,sep,base);
str[255]=0; read_data(str, 3, both, ex_bi); }
}
// now collect data
numb = norm.size()+bold.size()+ital.size()+both.size();
Buf = new short[numb];
memcpy(Buf,&norm[0],norm.size()*sizeof(short));
long cur = norm.size(), len = long(bold.size());
if(bold.size()>0)
memcpy(Buf+cur,&bold[0],bold.size()*sizeof(short));
#pragma omp parallel for
for(long i=0;i<long(GetNumGlyph());i++) if(glyphs[i].ln[1]<0)
{ glyphs[i].ln[1] = cur-1-glyphs[i].ln[1]; glyphs[i].tr[1] = cur-1-glyphs[i].tr[1]; }
#pragma omp parallel for
for(long i=0;i<long(ex_b.size());i++) if(ex_b[i].ln[1]<0)
{
mglGlyphDescr &g = ex_b[i];
g.ln[0] = g.ln[1] = g.ln[2] = g.ln[3] = cur-1-g.ln[1];
g.tr[0] = g.tr[1] = g.tr[2] = g.tr[3] = cur-1-g.tr[1];
}
cur += len; len = long(ital.size());
if(ital.size()>0)
memcpy(Buf+cur,&ital[0],ital.size()*sizeof(short));
#pragma omp parallel for
for(long i=0;i<long(GetNumGlyph());i++) if(glyphs[i].ln[2]<0)
{ glyphs[i].ln[2] = cur-1-glyphs[i].ln[2]; glyphs[i].tr[2] = cur-1-glyphs[i].tr[2]; }
#pragma omp parallel for
for(long i=0;i<long(ex_i.size());i++) if(ex_i[i].ln[2]<0)
{
mglGlyphDescr &g = ex_i[i];
g.ln[0] = g.ln[1] = g.ln[2] = g.ln[3] = cur-1-g.ln[2];
g.tr[0] = g.tr[1] = g.tr[2] = g.tr[3] = cur-1-g.tr[2];
}
cur += len;
if(both.size()>0)
memcpy(Buf+cur,&both[0],both.size()*sizeof(short));
#pragma omp parallel for
for(long i=0;i<long(GetNumGlyph());i++) if(glyphs[i].ln[3]<0)
{ glyphs[i].ln[3] = cur-1-glyphs[i].ln[3]; glyphs[i].tr[3] = cur-1-glyphs[i].tr[3]; }
#pragma omp parallel for
for(long i=0;i<long(ex_bi.size());i++) if(ex_bi[i].ln[3]<0)
{
mglGlyphDescr &g = ex_bi[i];
g.ln[0] = g.ln[1] = g.ln[2] = g.ln[3] = cur-1-g.ln[3];
g.tr[0] = g.tr[1] = g.tr[2] = g.tr[3] = cur-1-g.tr[3];
}
// now add missing symbols
if(ex_b.size()==0) ex_b = ex_i;
else
{
for(size_t i=0;i<ex_i.size();i++) // add from ex_i
{
long j = mgl_internal_code(ex_i[i].id, ex_b);
if(j>=0) // known symbol
{
mglGlyphDescr &g = ex_b[j], &f = ex_i[i];
g.width[2] = f.width[2];
g.ln[2] = f.ln[2]; g.tr[2] = f.tr[2];
g.numl[2] = f.numl[2]; g.numt[2] = f.numt[2];
}
else ex_b.push_back(ex_i[i]);
}
std::sort(ex_b.begin(),ex_b.end());
}
if(ex_b.size()==0) ex_b = ex_bi;
else
{
for(size_t i=0;i<ex_bi.size();i++) // add from ex_bi
{
long j = mgl_internal_code(ex_bi[i].id, ex_b);
if(j>=0) // known symbol
{
mglGlyphDescr &g = ex_b[j], &f = ex_bi[i];
g.width[2] = f.width[3];
g.ln[2] = f.ln[3]; g.tr[2] = f.tr[3];
g.numl[2] = f.numl[3]; g.numt[2] = f.numt[3];
}
else ex_b.push_back(ex_bi[i]);
}
std::sort(ex_b.begin(),ex_b.end());
}
if(ex_b.size()>0)
{
glyphs.reserve(ex_b.size()); // preallocate memory
glyphs.insert(glyphs.end(), ex_b.begin(), ex_b.end());
std::sort(glyphs.begin(),glyphs.end());
}
// Finally normalize all factors
fact[0] *= mgl_fgen; fact[1] *= mgl_fgen;
fact[2] *= mgl_fgen; fact[3] *= mgl_fgen;
setlocale(LC_NUMERIC,loc.c_str());
if(buf) delete []buf;
return true;
}
//-----------------------------------------------------------------------------
#if MGL_HAVE_PTHREAD
extern pthread_mutex_t mutexRnd;
#endif
//-----------------------------------------------------------------------------
float mgl_cos[360];
void MGL_NO_EXPORT mgl_init()
{
#if MGL_HAVE_PTHREAD
pthread_mutex_init(&mutexRnd,0);
#endif
#ifndef WIN32 // win32 don't initialized threads before main()
#pragma omp parallel for
#endif
for(long i=0;i<360;i++) mgl_cos[i] = cos(i*M_PI/180.);
}
//-----------------------------------------------------------------------------
mglFont::mglFont(const char *name, const char *path)
{
parse = true; gr=0; Buf=0;
// if(this==&mglDefFont) Load(name, path); else Copy(&mglDefFont);
if(name && *name) Load(name, path);
else if(this!=&mglDefFont) Copy(&mglDefFont);
else
{
mgl_init(); // NOTE: this call init function for the library.
Load(MGL_DEF_FONT_NAME,0);
}
}
mglFont::~mglFont() { if(Buf) delete []Buf; }
void mglFont::Restore() { Copy(&mglDefFont); }
//-----------------------------------------------------------------------------
void mglFont::Clear()
{
//#pragma omp critical(font)
{ if(Buf) delete []Buf; Buf=0; glyphs.clear(); }
}
//-----------------------------------------------------------------------------
void mglFont::Copy(mglFont *f)
{
if(!f || f==this) return;
#pragma omp critical(font)
{ if(Buf) delete []Buf; Buf=0; }
// copy scale factors
memcpy(fact,f->fact,4*sizeof(float));
// copy symbols descriptions
numb = f->numb; Buf = new short[numb]; memcpy(Buf, f->Buf, numb*sizeof(short));
// copy symbol parameters
glyphs.resize(f->glyphs.size());
memcpy(&glyphs[0],&(f->glyphs)[0],glyphs.size()*sizeof(mglGlyphDescr));
}
//-----------------------------------------------------------------------------
long MGL_EXPORT mgl_check_tex_table()
{
size_t i=0; while(mgl_tex_symb[i].tex[0]) i++;
long res = 0;
if(mgl_tex_num!=i)
{ printf("real=%zu, set=%zu\n",i,mgl_tex_num); res = -1; }
for(i=0;mgl_tex_symb[i].tex[0];i++)
{
mglTeXsymb tst, *rts; tst.tex = mgl_tex_symb[i].tex;
rts = (mglTeXsymb *) bsearch(&tst, mgl_tex_symb, mgl_tex_num, sizeof(mglTeXsymb), mgl_tex_symb_cmp);
if(!rts)
{ printf("Bad '%ls' at %zu\n",mgl_tex_symb[i].tex,i); res = 1+i; }
}
return res;
}