mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-18 06:06:01 -06:00
This might bring some incompatibilities in the code that expects wchar to be 16 bit, which escpecially involves dealing with Win32 (and to lesser extend MacOS) APIs, so if your application is doing that, please check all instances of WCHAR (UniChar on MacOS) or even wchar especially type casts. To support host APIs, char16 is introduced (but there is no 16-bit String varian). Use ToSystemCharsetW, FromSystemCharsetW to convert texts to Win32 API. - Support of drawing non-BMP characters in GUI - Vastly improved character font replacement code (when drawing characters missing with requested font, replacement font is used) - Last instances of Win32 ANSI calls (those ending with A) are removed - UTF handling routines are refactored and their's naming is unified - RTF is now being able to handle non-BMP characters (RTF is used as clipboard format for RichText) Other minor changes: - fixed TryRealloc issue - improved MemoryCheck - Removed MemoryAlloc48/MemoryFree48 - In theide Background parsing should less often cause delays in the main thread
426 lines
11 KiB
C++
426 lines
11 KiB
C++
#include "Draw.h"
|
|
|
|
#define LLOG(x) // LOG(x)
|
|
#define LTIMING(x) // TIMING(x)
|
|
|
|
#if !defined(CUSTOM_FONTSYS) && !defined(PLATFORM_COCOA)
|
|
|
|
#ifdef PLATFORM_POSIX
|
|
|
|
#include <fontconfig/fontconfig.h>
|
|
#include <fontconfig/fcfreetype.h>
|
|
|
|
namespace Upp {
|
|
|
|
static FT_Library sFTlib;
|
|
|
|
EXITBLOCK
|
|
{
|
|
if(sFTlib)
|
|
FT_Done_FreeType(sFTlib);
|
|
}
|
|
|
|
bool sInitFt(void)
|
|
{
|
|
if(sFTlib)
|
|
return true;
|
|
return FT_Init_FreeType(&sFTlib) == 0;
|
|
}
|
|
|
|
FcPattern *CreateFcPattern(Font font)
|
|
{
|
|
LTIMING("CreateXftFont");
|
|
int hg = abs(font.GetHeight());
|
|
if(hg == 0) hg = 10;
|
|
String face = font.GetFaceName();
|
|
FcPattern *p = FcPatternCreate();
|
|
FcPatternAddString(p, FC_FAMILY, (FcChar8*)~face);
|
|
FcPatternAddInteger(p, FC_SLANT, font.IsItalic() ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
|
|
FcPatternAddInteger(p, FC_PIXEL_SIZE, hg);
|
|
FcPatternAddInteger(p, FC_WEIGHT, font.IsBold() ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL);
|
|
FcPatternAddBool(p, FC_MINSPACE, 1);
|
|
FcConfigSubstitute(0, p, FcMatchPattern);
|
|
FcDefaultSubstitute(p);
|
|
FcResult result;
|
|
FcPattern *m = FcFontMatch(0, p, &result);
|
|
FcPatternDestroy(p);
|
|
return m;
|
|
}
|
|
|
|
FT_Face CreateFTFace(const FcPattern *pattern, String *rpath) {
|
|
FT_Face face = NULL;
|
|
|
|
double dsize;
|
|
double aspect;
|
|
FcChar8 *filename;
|
|
|
|
if(!sInitFt())
|
|
return NULL;
|
|
|
|
if(FcPatternGetString(pattern, FC_FILE, 0, &filename) != FcResultMatch)
|
|
return NULL;
|
|
if(rpath)
|
|
*rpath = (const char *)filename;
|
|
|
|
if(FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &dsize) != FcResultMatch)
|
|
dsize = 16;
|
|
|
|
if (FcPatternGetDouble(pattern, FC_ASPECT, 0, &aspect) != FcResultMatch)
|
|
aspect = 1.0;
|
|
|
|
FT_F26Dot6 ysize = (FT_F26Dot6) (dsize * 64.0);
|
|
FT_F26Dot6 xsize = (FT_F26Dot6) (dsize * aspect * 64.0);
|
|
|
|
if(FT_New_Face(sFTlib, (const char *)filename, 0, &face))
|
|
return NULL;
|
|
|
|
FT_Set_Char_Size(face, xsize, ysize, 0, 0);
|
|
return face;
|
|
}
|
|
|
|
#define FONTCACHE 37
|
|
|
|
struct FtFaceEntry {
|
|
Font font;
|
|
FT_Face face;
|
|
String path;
|
|
};
|
|
|
|
FT_Face (*FTFaceXft)(Font fnt, String *rpath);
|
|
|
|
FT_Face FTFace(Font fnt, String *rpath = NULL)
|
|
{
|
|
LTIMING("FTFace");
|
|
if(FTFaceXft)
|
|
return (*FTFaceXft)(fnt, rpath);
|
|
static FtFaceEntry ft_cache[FONTCACHE];
|
|
ONCELOCK {
|
|
for(int i = 0; i < FONTCACHE; i++)
|
|
ft_cache[i].font.Height(-30000);
|
|
}
|
|
FtFaceEntry be;
|
|
be = ft_cache[0];
|
|
for(int i = 0; i < FONTCACHE; i++) {
|
|
FtFaceEntry e = ft_cache[i];
|
|
if(i)
|
|
ft_cache[i] = be;
|
|
if(e.font == fnt) {
|
|
if(rpath)
|
|
*rpath = e.path;
|
|
if(i)
|
|
ft_cache[0] = e;
|
|
return e.face;
|
|
}
|
|
be = e;
|
|
}
|
|
LTIMING("FTFace2");
|
|
if(be.face) {
|
|
LLOG("Removing " << be.font << " - " << (void *)be.face);
|
|
FT_Done_Face(be.face);
|
|
}
|
|
be.font = fnt;
|
|
FcPattern *p = CreateFcPattern(fnt);
|
|
be.face = CreateFTFace(p, &be.path);
|
|
FcPatternDestroy(p);
|
|
ft_cache[0] = be;
|
|
if(rpath)
|
|
*rpath = be.path;
|
|
return be.face;
|
|
}
|
|
|
|
CommonFontInfo (*GetFontInfoSysXft)(Font font);
|
|
|
|
CommonFontInfo GetFontInfoSys(Font font)
|
|
{
|
|
sInitFt();
|
|
if(GetFontInfoSysXft)
|
|
return (*GetFontInfoSysXft)(font);
|
|
CommonFontInfo fi;
|
|
String path;
|
|
FT_Face face = FTFace(font, &path);
|
|
if(face) {
|
|
fi.ascent = face->size->metrics.ascender >> 6;
|
|
fi.descent = -(face->size->metrics.descender >> 6);
|
|
fi.external = (face->size->metrics.height >> 6) - fi.ascent - fi.descent;
|
|
fi.internal = 0;
|
|
fi.overhang = 0;
|
|
fi.maxwidth = face->size->metrics.max_advance >> 6;
|
|
fi.avewidth = fi.maxwidth;
|
|
fi.default_char = '?';
|
|
fi.fixedpitch = font.GetFaceInfo() & Font::FIXEDPITCH;
|
|
fi.ttf = path.EndsWith(".ttf") || path.EndsWith(".otf") || path.EndsWith(".otc") || path.EndsWith(".ttc");
|
|
fi.fonti = face->face_index;
|
|
if(path.GetCount() < 250)
|
|
strcpy(fi.path, ~path);
|
|
else
|
|
*fi.path = 0;
|
|
}
|
|
return fi;
|
|
}
|
|
|
|
#define FLOOR(x) ((x) & -64)
|
|
#define CEIL(x) (((x)+63) & -64)
|
|
#define TRUNC(x) ((x) >> 6)
|
|
#define ROUND(x) (((x)+32) & -64)
|
|
|
|
GlyphInfo (*GetGlyphInfoSysXft)(Font font, int chr);
|
|
|
|
GlyphInfo GetGlyphInfoSys(Font font, int chr)
|
|
{
|
|
LTIMING("GetGlyphInfoSys");
|
|
GlyphInfo gi;
|
|
FT_Face face = FTFace(font, NULL);
|
|
gi.lspc = gi.rspc = 0;
|
|
gi.width = 0x8000;
|
|
LLOG("GetGlyphInfoSys " << font << " " << (char)chr << " " << FormatIntHex(chr));
|
|
if(face) {
|
|
LTIMING("GetGlyphInfoSys 2");
|
|
int glyph_index = FT_Get_Char_Index(face, chr);
|
|
if(glyph_index) {
|
|
// if(GetGlyphInfoSysXft)
|
|
// return (*GetGlyphInfoSysXft)(font, chr);
|
|
if(FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP) == 0 ||
|
|
FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT) == 0) {
|
|
FT_Glyph_Metrics& m = face->glyph->metrics;
|
|
int left = FLOOR(m.horiBearingX);
|
|
int width = TRUNC(CEIL(m.horiBearingX + m.width) - left);
|
|
gi.width = TRUNC(ROUND(face->glyph->advance.x));
|
|
gi.lspc = TRUNC(left);
|
|
gi.rspc = gi.width - width - gi.lspc;
|
|
gi.glyphi = glyph_index;
|
|
}
|
|
}
|
|
}
|
|
return gi;
|
|
}
|
|
|
|
Vector<FaceInfo> GetAllFacesSys()
|
|
{
|
|
static const char *basic_fonts[] = {
|
|
"sans-serif",
|
|
"serif",
|
|
"sans-serif",
|
|
"monospace",
|
|
};
|
|
|
|
VectorMap<String, FaceInfo> list;
|
|
for(int i = 0; i < __countof(basic_fonts); i++) {
|
|
String name = (const char *)basic_fonts[i];
|
|
FaceInfo& fi = list.Add(name);
|
|
fi.name = name;
|
|
fi.info = Font::SCALEABLE;
|
|
if(i == Font::SERIF)
|
|
fi.info |= Font::SERIFSTYLE;
|
|
if(i == Font::MONOSPACE)
|
|
fi.info |= Font::FIXEDPITCH;
|
|
}
|
|
FcPattern *p = FcPatternCreate();
|
|
FcObjectSet *os = FcObjectSetBuild(FC_FAMILY, FC_SPACING, FC_SCALABLE, FC_SYMBOL, FC_COLOR, (void *)0);
|
|
FcFontSet *fs = FcFontList(NULL, p, os);
|
|
FcPatternDestroy(p);
|
|
FcObjectSetDestroy(os);
|
|
for(int i = 0; i < fs->nfont; i++) {
|
|
FcChar8 *family = NULL;
|
|
FcPattern *pt = fs->fonts[i];
|
|
if(FcPatternGetString(pt, FC_FAMILY, 0, &family) == 0 && family) {
|
|
String name = (const char *)family;
|
|
FaceInfo& fi = list.GetAdd(name);
|
|
fi.name = name;
|
|
int iv;
|
|
FcBool bv;
|
|
if(FcPatternGetInteger(pt, FC_SPACING, 0, &iv) == 0 && iv == FC_MONO)
|
|
fi.info |= Font::FIXEDPITCH;
|
|
if(FcPatternGetBool(pt, FC_SYMBOL, 0, &bv) == 0 && bv)
|
|
fi.info |= Font::SPECIAL;
|
|
if(FcPatternGetBool(pt, FC_COLOR, 0, &bv) == 0 && bv)
|
|
fi.info |= Font::SPECIAL;
|
|
if(FcPatternGetBool(pt, FC_SCALABLE, 0, &bv) == 0 && bv)
|
|
fi.info |= Font::SCALEABLE;
|
|
String h = ToLower(fi.name);
|
|
if((h.Find("serif") >= 0 || h.Find("roman") >= 0) && h.Find("sans") < 0)
|
|
fi.info |= Font::SERIFSTYLE;
|
|
if(h.Find("script") >= 0)
|
|
fi.info |= Font::SCRIPTSTYLE;
|
|
}
|
|
}
|
|
FcFontSetDestroy(fs);
|
|
return list.PickValues();
|
|
}
|
|
|
|
String GetFontDataSys(Font font, const char *table, int offset, int size)
|
|
{ // read truetype or opentype table
|
|
FileIn in(font.Fi().path);
|
|
int q = in.Get32be();
|
|
if(q == 0x74746366) { // font collection
|
|
in.Get32(); // skip major/minor version
|
|
int nfonts = in.Get32be();
|
|
if(font.Fi().fonti >= nfonts)
|
|
return Null;
|
|
in.SeekCur(font.Fi().fonti * 4);
|
|
int offset = in.Get32be();
|
|
if(offset < 0 || offset >= in.GetSize())
|
|
return Null;
|
|
in.Seek(offset);
|
|
q = in.Get32be();
|
|
}
|
|
if(q != 0x74727565 && q != 0x00010000 && q != 0x4f54544f) // 0x4f54544f means CCF font!
|
|
return Null;
|
|
int n = in.Get16be();
|
|
in.Get32();
|
|
in.Get16();
|
|
while(n--) {
|
|
if(in.IsError() || in.IsEof()) return Null;
|
|
String tab = in.Get(4);
|
|
in.Get32();
|
|
int off = in.Get32be();
|
|
int len = in.Get32be();
|
|
if(tab == table) {
|
|
if(off < 0 || len < 0 || off + len > in.GetSize())
|
|
return Null;
|
|
len = min(len - offset, size);
|
|
if(len < 0)
|
|
return Null;
|
|
in.Seek(off + offset);
|
|
return in.Get(len);
|
|
}
|
|
}
|
|
return Null;
|
|
}
|
|
|
|
static inline double ft_dbl(int p)
|
|
{
|
|
return double(p) / 64.0;
|
|
}
|
|
|
|
struct ConvertOutlinePoint {
|
|
bool sheer;
|
|
double xx;
|
|
double yy;
|
|
|
|
Pointf operator()(int x, int y) {
|
|
double fy = ft_dbl(y);
|
|
return Pointf(ft_dbl(x) + xx + (sheer ? 0.2 * fy : 0), yy - fy);
|
|
}
|
|
};
|
|
|
|
bool RenderOutline(const FT_Outline& outline, FontGlyphConsumer& path, double xx, double yy, bool sheer)
|
|
{
|
|
ConvertOutlinePoint cp;
|
|
cp.xx = xx;
|
|
cp.yy = yy;
|
|
cp.sheer = sheer;
|
|
|
|
FT_Vector v_last;
|
|
FT_Vector v_control;
|
|
FT_Vector v_start;
|
|
FT_Vector* point;
|
|
FT_Vector* limit;
|
|
char* tags;
|
|
int n; // index of contour in outline
|
|
char tag; // current point's state
|
|
int first = 0; // index of first point in contour
|
|
for(n = 0; n < outline.n_contours; n++) {
|
|
int last = outline.contours[n];
|
|
limit = outline.points + last;
|
|
v_start = outline.points[first];
|
|
v_last = outline.points[last];
|
|
v_control = v_start;
|
|
point = outline.points + first;
|
|
tags = outline.tags + first;
|
|
tag = FT_CURVE_TAG(tags[0]);
|
|
if(tag == FT_CURVE_TAG_CUBIC) return false;
|
|
if(tag == FT_CURVE_TAG_CONIC) {
|
|
if(FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) {
|
|
// start at last point if it is on the curve
|
|
v_start = v_last;
|
|
limit--;
|
|
}
|
|
else {
|
|
// if both first and last points are conic,
|
|
// start at their middle and record its position
|
|
// for closure
|
|
v_start.x = (v_start.x + v_last.x) / 2;
|
|
v_start.y = (v_start.y + v_last.y) / 2;
|
|
v_last = v_start;
|
|
}
|
|
point--;
|
|
tags--;
|
|
}
|
|
path.Move(cp(v_start.x, v_start.y));
|
|
while(point < limit) {
|
|
point++;
|
|
tags++;
|
|
|
|
tag = FT_CURVE_TAG(tags[0]);
|
|
switch(tag) {
|
|
case FT_CURVE_TAG_ON:
|
|
path.Line(cp(point->x, point->y));
|
|
continue;
|
|
case FT_CURVE_TAG_CONIC:
|
|
v_control.x = point->x;
|
|
v_control.y = point->y;
|
|
Do_Conic:
|
|
if(point < limit) {
|
|
FT_Vector vec;
|
|
FT_Vector v_middle;
|
|
point++;
|
|
tags++;
|
|
tag = FT_CURVE_TAG(tags[0]);
|
|
vec.x = point->x;
|
|
vec.y = point->y;
|
|
if(tag == FT_CURVE_TAG_ON) {
|
|
path.Quadratic(cp(v_control.x, v_control.y), cp(vec.x, vec.y));
|
|
continue;
|
|
}
|
|
if(tag != FT_CURVE_TAG_CONIC) return false;
|
|
v_middle.x = (v_control.x + vec.x) / 2;
|
|
v_middle.y = (v_control.y + vec.y) / 2;
|
|
path.Quadratic(cp(v_control.x, v_control.y), cp(v_middle.x, v_middle.y));
|
|
v_control = vec;
|
|
goto Do_Conic;
|
|
}
|
|
path.Quadratic(cp(v_control.x, v_control.y), cp(v_start.x, v_start.y));
|
|
goto Close;
|
|
|
|
default:
|
|
FT_Vector vec1, vec2;
|
|
if(point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC)
|
|
return false;
|
|
vec1.x = point[0].x;
|
|
vec1.y = point[0].y;
|
|
vec2.x = point[1].x;
|
|
vec2.y = point[1].y;
|
|
point += 2;
|
|
tags += 2;
|
|
if(point <= limit) {
|
|
FT_Vector vec;
|
|
vec.x = point->x;
|
|
vec.y = point->y;
|
|
path.Cubic(cp(vec1.x, vec1.y), cp(vec2.x, vec2.y), cp(vec.x, vec.y));
|
|
continue;
|
|
}
|
|
path.Cubic(cp(vec1.x, vec1.y), cp(vec2.x, vec2.y), cp(v_start.x, v_start.y));
|
|
goto Close;
|
|
}
|
|
}
|
|
Close:
|
|
path.Close();
|
|
first = last + 1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void RenderCharacterSys(FontGlyphConsumer& sw, double x, double y, int ch, Font fnt)
|
|
{
|
|
FT_Face face = FTFace(fnt, NULL);
|
|
int glyph_index = FT_Get_Char_Index(face, ch);
|
|
if(glyph_index && FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT) == 0)
|
|
RenderOutline(face->glyph->outline, sw, x, y + fnt.GetAscent(),
|
|
fnt.IsItalic() && !(face->style_flags & FT_STYLE_FLAG_ITALIC));
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|