1 #include "Draw.h"
2 
3 #define LLOG(x)     //  LOG(x)
4 #define LTIMING(x)  //  TIMING(x)
5 
6 #if !defined(CUSTOM_FONTSYS) && !defined(PLATFORM_COCOA)
7 
8 #ifdef PLATFORM_POSIX
9 
10 #include <fontconfig/fontconfig.h>
11 #include <fontconfig/fcfreetype.h>
12 
13 namespace Upp {
14 
15 static FT_Library sFTlib;
16 
17 EXITBLOCK
18 {
19 	if(sFTlib)
20 		FT_Done_FreeType(sFTlib);
21 }
22 
23 bool sInitFt(void)
24 {
25 	if(sFTlib)
26 		return true;
27 	return FT_Init_FreeType(&sFTlib) == 0;
28 }
29 
CreateFcPattern(Font font)30 FcPattern *CreateFcPattern(Font font)
31 {
32 	LTIMING("CreateXftFont");
33 	int hg = abs(font.GetHeight());
34 	if(hg == 0) hg = 10;
35 	String face = font.GetFaceName();
36 	FcPattern *p = FcPatternCreate();
37 	FcPatternAddString(p, FC_FAMILY, (FcChar8*)~face);
38 	FcPatternAddInteger(p, FC_SLANT, font.IsItalic() ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
39 	FcPatternAddInteger(p, FC_PIXEL_SIZE, hg);
40 	FcPatternAddInteger(p, FC_WEIGHT, font.IsBold() ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL);
41 	FcPatternAddBool(p, FC_MINSPACE, 1);
42 	FcConfigSubstitute(0, p, FcMatchPattern);
43 	FcDefaultSubstitute(p);
44 	FcResult result;
45 	FcPattern *m = FcFontMatch(0, p, &result);
46 	FcPatternDestroy(p);
47 	return m;
48 }
49 
CreateFTFace(const FcPattern * pattern,String * rpath)50 FT_Face CreateFTFace(const FcPattern *pattern, String *rpath) {
51 	FT_Face	    face = NULL;
52 
53 	double	    dsize;
54 	double	    aspect;
55 	FcChar8    *filename;
56 
57 	if(!sInitFt())
58 		return NULL;
59 
60 	if(FcPatternGetString(pattern, FC_FILE, 0, &filename) != FcResultMatch)
61 		return NULL;
62 	if(rpath)
63 		*rpath = (const char *)filename;
64 
65 	if(FcPatternGetDouble(pattern, FC_PIXEL_SIZE, 0, &dsize) != FcResultMatch)
66 		dsize = 16;
67 
68 	if (FcPatternGetDouble(pattern, FC_ASPECT, 0, &aspect) != FcResultMatch)
69 		aspect = 1.0;
70 
71 	FT_F26Dot6 ysize = (FT_F26Dot6) (dsize * 64.0);
72 	FT_F26Dot6 xsize = (FT_F26Dot6) (dsize * aspect * 64.0);
73 
74 	if(FT_New_Face(sFTlib, (const char *)filename, 0, &face))
75 		return NULL;
76 
77 	FT_Set_Char_Size(face, xsize, ysize, 0, 0);
78 	return face;
79 }
80 
81 #define FONTCACHE 37
82 
83 struct FtFaceEntry {
84 	Font    font;
85 	FT_Face face;
86 	String  path;
87 };
88 
89 FT_Face (*FTFaceXft)(Font fnt, String *rpath);
90 
FTFace(Font fnt,String * rpath=NULL)91 FT_Face FTFace(Font fnt, String *rpath = NULL)
92 {
93 	LTIMING("FTFace");
94 	if(FTFaceXft)
95 		return (*FTFaceXft)(fnt, rpath);
96 	static FtFaceEntry ft_cache[FONTCACHE];
97 	ONCELOCK {
98 		for(int i = 0; i < FONTCACHE; i++)
99 			ft_cache[i].font.Height(-30000);
100 	}
101 	FtFaceEntry be;
102 	be = ft_cache[0];
103 	for(int i = 0; i < FONTCACHE; i++) {
104 		FtFaceEntry e = ft_cache[i];
105 		if(i)
106 			ft_cache[i] = be;
107 		if(e.font == fnt) {
108 			if(rpath)
109 				*rpath = e.path;
110 			if(i)
111 				ft_cache[0] = e;
112 			return e.face;
113 		}
114 		be = e;
115 	}
116 	LTIMING("FTFace2");
117 	if(be.face) {
118 		LLOG("Removing " << be.font << " - " << (void *)be.face);
119 		FT_Done_Face(be.face);
120 	}
121 	be.font = fnt;
122 	FcPattern *p = CreateFcPattern(fnt);
123 	be.face = CreateFTFace(p, &be.path);
124 	FcPatternDestroy(p);
125 	ft_cache[0] = be;
126 	if(rpath)
127 		*rpath = be.path;
128 	return be.face;
129 }
130 
131 CommonFontInfo (*GetFontInfoSysXft)(Font font);
132 
GetFontInfoSys(Font font)133 CommonFontInfo GetFontInfoSys(Font font)
134 {
135 	sInitFt();
136 	if(GetFontInfoSysXft)
137 		return (*GetFontInfoSysXft)(font);
138 	CommonFontInfo fi;
139 	String path;
140 	FT_Face face = FTFace(font, &path);
141 	if(face) {
142 		fi.ascent = face->size->metrics.ascender >> 6;
143 		fi.descent = -(face->size->metrics.descender >> 6);
144 		fi.external = (face->size->metrics.height >> 6) - fi.ascent - fi.descent;
145 		fi.internal = 0;
146 		fi.overhang = 0;
147 		fi.maxwidth = face->size->metrics.max_advance >> 6;
148 		fi.avewidth = fi.maxwidth;
149 		fi.default_char = '?';
150 		fi.fixedpitch = font.GetFaceInfo() & Font::FIXEDPITCH;
151 		fi.ttf = path.EndsWith(".ttf") || path.EndsWith(".otf");
152 		if(path.GetCount() < 250)
153 			strcpy(fi.path, ~path);
154 		else
155 			*fi.path = 0;
156 	}
157 	return fi;
158 }
159 
160 #define FLOOR(x)    ((x) & -64)
161 #define CEIL(x)	    (((x)+63) & -64)
162 #define TRUNC(x)    ((x) >> 6)
163 #define ROUND(x)    (((x)+32) & -64)
164 
165 GlyphInfo (*GetGlyphInfoSysXft)(Font font, int chr);
166 
167 #ifdef flagLINUXGL
168 #include <LinuxGl/FontGl.h>
169 #include <LinuxGl/ResGl.h>
GetGlyphInfoSys(Font font,int chr)170 GlyphInfo  GetGlyphInfoSys(Font font, int chr)
171 {
172 	static GlyphInfo gi;
173 	const OpenGLFont& fi = resources.GetFont(font);
174 	gi.width = chr < fi.chars.GetCount()
175 		? int(fi.chars[chr].xadvance * fi.scale + 0.5f)
176 		: 0;
177 	gi.lspc = 0;
178 	gi.rspc = 0;
179 	return gi;
180 }
181 #else
GetGlyphInfoSys(Font font,int chr)182 GlyphInfo  GetGlyphInfoSys(Font font, int chr)
183 {
184 	LTIMING("GetGlyphInfoSys");
185 	GlyphInfo gi;
186 	FT_Face face = FTFace(font, NULL);
187 	gi.lspc = gi.rspc = 0;
188 	gi.width = 0x8000;
189 	LLOG("GetGlyphInfoSys " << font << " " << (char)chr << " " << FormatIntHex(chr));
190 	if(face) {
191 		LTIMING("GetGlyphInfoSys 2");
192 		int glyph_index = FT_Get_Char_Index(face, chr);
193 		if(glyph_index) {
194 //			if(GetGlyphInfoSysXft)
195 //				return (*GetGlyphInfoSysXft)(font, chr);
196 			if(FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT|FT_LOAD_NO_BITMAP) == 0 ||
197 			   FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT) == 0) {
198 				FT_Glyph_Metrics& m = face->glyph->metrics;
199 				int left  = FLOOR(m.horiBearingX);
200 				int width = TRUNC(CEIL(m.horiBearingX + m.width) - left);
201 				gi.width = TRUNC(ROUND(face->glyph->advance.x));
202 				gi.lspc = TRUNC(left);
203 				gi.rspc = gi.width - width - gi.lspc;
204 				gi.glyphi = glyph_index;
205 			}
206 		}
207 	}
208 	return gi;
209 }
210 #endif
211 
GetAllFacesSys()212 Vector<FaceInfo> GetAllFacesSys()
213 {
214 	static const char *basic_fonts[] = {
215 		"sans-serif",
216 		"serif",
217 		"sans-serif",
218 		"monospace",
219 	};
220 
221 	Vector<FaceInfo> list;
222 	for(int i = 0; i < __countof(basic_fonts); i++) {
223 		FaceInfo& fi = list.Add();
224 		fi.name = basic_fonts[i];
225 		fi.info = Font::SCALEABLE;
226 		if(i == Font::SERIF)
227 			fi.info |= Font::SERIFSTYLE;
228 		if(i == Font::MONOSPACE)
229 			fi.info |= Font::FIXEDPITCH;
230 	}
231 	FcPattern *p = FcPatternCreate();
232 	FcObjectSet *os = FcObjectSetBuild(FC_FAMILY, FC_SPACING, FC_SCALABLE, (void *)0);
233 	FcFontSet *fs = FcFontList(NULL, p, os);
234 	FcPatternDestroy(p);
235 	FcObjectSetDestroy(os);
236 	for(int i = 0; i < fs->nfont; i++) {
237 		FcChar8 *family = NULL;
238 		FcPattern *pt = fs->fonts[i];
239 		if(FcPatternGetString(pt, FC_FAMILY, 0, &family) == 0 && family) {
240 			FaceInfo& fi = list.Add();
241 			fi.name = (const char *)family;
242 			fi.info = 0;
243 			int iv;
244 			if(FcPatternGetInteger(pt, FC_SPACING, 0, &iv) == 0 && iv == FC_MONO)
245 				fi.info |= Font::FIXEDPITCH;
246 			FcBool bv;
247 			if(FcPatternGetBool(pt, FC_SCALABLE, 0, &bv) == 0 && bv)
248 				fi.info |= Font::SCALEABLE;
249 			String h = ToLower(fi.name);
250 			if(h.Find("serif") >= 0 && h.Find("sans") < 0)
251 				fi.info |= Font::SERIFSTYLE;
252 			if(h.Find("script") >= 0)
253 				fi.info |= Font::SCRIPTSTYLE;
254 		}
255 	}
256 	FcFontSetDestroy(fs);
257 	return list;
258 }
259 
GetFontDataSys(Font font)260 String GetFontDataSys(Font font)
261 {
262 	return LoadFile(font.Fi().path);
263 }
264 
ft_dbl(int p)265 static inline double ft_dbl(int p)
266 {
267     return double(p) / 64.0;
268 }
269 
270 struct ConvertOutlinePoint {
271 	bool sheer;
272 	double xx;
273 	double yy;
274 
operator ()Upp::ConvertOutlinePoint275 	Pointf operator()(int x, int y) {
276 		double fy = ft_dbl(y);
277 		return Pointf(ft_dbl(x) + xx + (sheer ? 0.2 * fy : 0), yy - fy);
278 	}
279 };
280 
RenderOutline(const FT_Outline & outline,FontGlyphConsumer & path,double xx,double yy,bool sheer)281 bool RenderOutline(const FT_Outline& outline, FontGlyphConsumer& path, double xx, double yy, bool sheer)
282 {
283 	ConvertOutlinePoint cp;
284 	cp.xx = xx;
285 	cp.yy = yy;
286 	cp.sheer = sheer;
287 
288 	FT_Vector   v_last;
289 	FT_Vector   v_control;
290 	FT_Vector   v_start;
291 	FT_Vector*  point;
292 	FT_Vector*  limit;
293 	char*       tags;
294 	int   n;         // index of contour in outline
295 	char  tag;       // current point's state
296 	int   first = 0; // index of first point in contour
297 	for(n = 0; n < outline.n_contours; n++) {
298 		int  last = outline.contours[n];
299 		limit = outline.points + last;
300 		v_start = outline.points[first];
301 		v_last  = outline.points[last];
302 		v_control = v_start;
303 		point = outline.points + first;
304 		tags  = outline.tags  + first;
305 		tag   = FT_CURVE_TAG(tags[0]);
306 		if(tag == FT_CURVE_TAG_CUBIC) return false;
307 		if(tag == FT_CURVE_TAG_CONIC) {
308 			if(FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) {
309 				// start at last point if it is on the curve
310 				v_start = v_last;
311 				limit--;
312 			}
313 			else {
314 				// if both first and last points are conic,
315 				// start at their middle and record its position
316 				// for closure
317 				v_start.x = (v_start.x + v_last.x) / 2;
318 				v_start.y = (v_start.y + v_last.y) / 2;
319 				v_last = v_start;
320 			}
321 			point--;
322 			tags--;
323 		}
324 		path.Move(cp(v_start.x, v_start.y));
325 		while(point < limit) {
326 			point++;
327 			tags++;
328 
329 			tag = FT_CURVE_TAG(tags[0]);
330 			switch(tag) {
331 			case FT_CURVE_TAG_ON:
332 				path.Line(cp(point->x, point->y));
333 				continue;
334 			case FT_CURVE_TAG_CONIC:
335 				v_control.x = point->x;
336 				v_control.y = point->y;
337 			Do_Conic:
338 				if(point < limit) {
339 					FT_Vector vec;
340 					FT_Vector v_middle;
341 					point++;
342 					tags++;
343 					tag = FT_CURVE_TAG(tags[0]);
344 					vec.x = point->x;
345 					vec.y = point->y;
346 					if(tag == FT_CURVE_TAG_ON) {
347 						path.Quadratic(cp(v_control.x, v_control.y), cp(vec.x, vec.y));
348 						continue;
349 					}
350 					if(tag != FT_CURVE_TAG_CONIC) return false;
351 					v_middle.x = (v_control.x + vec.x) / 2;
352 					v_middle.y = (v_control.y + vec.y) / 2;
353 					path.Quadratic(cp(v_control.x, v_control.y), cp(v_middle.x, v_middle.y));
354 					v_control = vec;
355 					goto Do_Conic;
356 				}
357 				path.Quadratic(cp(v_control.x, v_control.y), cp(v_start.x, v_start.y));
358 				goto Close;
359 
360 			default:
361 				FT_Vector vec1, vec2;
362 				if(point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC)
363 				    return false;
364 				vec1.x = point[0].x;
365 				vec1.y = point[0].y;
366 				vec2.x = point[1].x;
367 				vec2.y = point[1].y;
368 				point += 2;
369 				tags  += 2;
370 				if(point <= limit) {
371 					FT_Vector vec;
372 					vec.x = point->x;
373 					vec.y = point->y;
374 					path.Cubic(cp(vec1.x, vec1.y), cp(vec2.x, vec2.y), cp(vec.x, vec.y));
375 					continue;
376 				}
377 				path.Cubic(cp(vec1.x, vec1.y), cp(vec2.x, vec2.y), cp(v_start.x, v_start.y));
378 				goto Close;
379 			}
380 		}
381 	Close:
382 		path.Close();
383 		first = last + 1;
384     }
385 	return true;
386 }
387 
RenderCharacterSys(FontGlyphConsumer & sw,double x,double y,int ch,Font fnt)388 void RenderCharacterSys(FontGlyphConsumer& sw, double x, double y, int ch, Font fnt)
389 {
390 	FT_Face face = FTFace(fnt, NULL);
391 	int glyph_index = FT_Get_Char_Index(face, ch);
392 	if(glyph_index && FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT) == 0)
393 		RenderOutline(face->glyph->outline, sw, x, y + fnt.GetAscent(),
394 		              fnt.IsItalic() && !(face->style_flags & FT_STYLE_FLAG_ITALIC));
395 }
396 
397 }
398 
399 #endif
400 
401 #endif
402