1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 #include <QColor>
8 #include <QDebug>
9 #include <QMap>
10 #include <QPainter>
11 #include <QPixmap>
12 #include <QRegExp>
13 #include <QStringList>
14 #include <QTransform>
15 
16 #include <ft2build.h>
17 
18 #include FT_FREETYPE_H
19 #include FT_TRUETYPE_TABLES_H
20 #include FT_TRUETYPE_IDS_H
21 
22 #include "fpoint.h"
23 #include "fpointarray.h"
24 #include "ftface.h"
25 #include "scfontmetrics.h"
26 #include "scfonts.h"
27 #include "scpage.h"
28 #include "scpainter.h"
29 #include "scribusdoc.h"
30 #include "style.h"
31 #include "util_math.h"
32 
33 // this code contains a set of font related functions
34 // that don't really fit within ScFonts.
35 
36 static FPoint firstP;
37 static bool FirstM;
38 static QMap<FT_ULong, QString> adobeGlyphNames;
39 #if 0
40 static const char* table[] = {
41 //#include "glyphlist.txt.q"
42 					0};
43 #endif
44 
45 // private functions
46 #if 0
47 static void readAdobeGlyphNames();
48 #endif
49 //static QString adobeGlyphName(FT_ULong charcode);
50 static int traceMoveto( FT_Vector *to, FPointArray *composite );
51 static int traceLineto( FT_Vector *to, FPointArray *composite );
52 static int traceQuadraticBezier( FT_Vector *control, FT_Vector *to, FPointArray *composite );
53 static int traceCubicBezier( FT_Vector *p, FT_Vector *q, FT_Vector *to, FPointArray *composite );
54 
55 FT_Outline_Funcs OutlineMethods =
56 	{
57 		(FT_Outline_MoveTo_Func) traceMoveto,
58 		(FT_Outline_LineTo_Func) traceLineto,
59 		(FT_Outline_ConicTo_Func) traceQuadraticBezier,
60 		(FT_Outline_CubicTo_Func) traceCubicBezier,
61 		0,
62 		0
63 	};
64 
65 
66 const qreal FTSCALE = 64.0;
67 
68 
setBestEncoding(FT_Face face)69 int setBestEncoding(FT_Face face)
70 {
71 	FT_ULong  charcode;
72 	FT_UInt   gindex;
73 	bool foundEncoding = false;
74 	int countUnicode = 0;
75 	int chmapUnicode = -1;
76 	int chmapCustom = -1;
77 	int retVal = 0;
78 	//FT_CharMap defaultEncoding = face->charmap;
79 //	int defaultchmap=face->charmap ? FT_Get_Charmap_Index(face->charmap) : 0;
80 // Since the above function is only available in FreeType 2.1.10 its replaced by
81 // the following line, assuming that the default charmap has the index 0
82 	int defaultchmap = 0;
83 	FT_ULong dbgInfo = 0;
84 
85 	FT_Load_Sfnt_Table( face, FT_MAKE_TAG('p','o','s','t'), 0, nullptr, &dbgInfo );
86 	qDebug() << "setBestEncoding for " << FT_Get_Postscript_Name(face) << " with " << face->num_glyphs << "glyphs, hasNames=" << FT_HAS_GLYPH_NAMES(face) << ", POST size=" << dbgInfo ;
87 
88 	for (int i = 0; i < face->num_charmaps; i++)
89 	{
90 		FT_CharMap charmap = face->charmaps[i];
91 		qDebug() << "Checking cmap " << i << "(" << charmap->platform_id << "," << charmap->encoding_id << "," << FT_Get_CMap_Language_ID(charmap) << ") format " << FT_Get_CMap_Format(charmap);
92 		if (charmap->encoding == FT_ENCODING_UNICODE)
93 		{
94 			FT_Set_Charmap(face, face->charmaps[i]);
95 			chmapUnicode = i;
96 			gindex = 0;
97 			charcode = FT_Get_First_Char(face, &gindex);
98 			while (gindex != 0)
99 			{
100 				countUnicode++;
101 				charcode = FT_Get_Next_Char(face, charcode, &gindex);
102 			}
103 			qDebug() << "found Unicode enc for" << face->family_name << face->style_name  << "as map" << chmapUnicode << "with" << countUnicode << "glyphs";
104 		}
105 		if (charmap->encoding == FT_ENCODING_ADOBE_CUSTOM)
106 		{
107 			chmapCustom = i;
108 			foundEncoding = true;
109 			retVal = 1;
110 			qDebug() << "found Custom enc for" << face->family_name << face->style_name;
111 			break;
112 		}
113 		if (charmap->encoding == FT_ENCODING_MS_SYMBOL)
114 		{
115 			qDebug() << "found Symbol enc for" << face->family_name << face->style_name;
116 
117 			chmapCustom = i;
118 			foundEncoding = true;
119 			retVal = 2;
120 			break;
121 		}
122 	}
123 	int mapToSet = defaultchmap;
124 	if (chmapUnicode >= 0 && countUnicode >= face->num_glyphs-1)
125 	{
126 		qDebug() << "using Unicode enc for" << face->family_name << face->style_name;
127 		mapToSet = chmapUnicode;
128 		retVal = 0;
129 	}
130 	else if (foundEncoding)
131 	{
132 		qDebug() << "using special enc for" << face->family_name << face->style_name;
133 		mapToSet = chmapCustom;
134 	}
135 	else
136 	{
137 		qDebug() << "using default enc for" << face->family_name << face->style_name;
138 		mapToSet = defaultchmap;
139 		retVal = 0;
140 	}
141 
142 	//Fixes #2199, missing glyphs from 1.2.1->1.2.2
143 	//If the currently wanted character map is not already Unicode...
144 	//if (FT_Get_Charmap_Index(face->charmap)!=chmapUnicode)
145 	if (mapToSet != chmapUnicode)
146 	{
147 		//Change map so we can count the chars in it
148 		FT_Set_Charmap(face, face->charmaps[mapToSet]);
149 		//Count the characters in the current map
150 		gindex = 0;
151 		int countCurrMap = 0;
152 		charcode = FT_Get_First_Char(face, &gindex);
153 		while (gindex != 0)
154 		{
155 			countCurrMap++;
156 			charcode = FT_Get_Next_Char(face, charcode, &gindex);
157 		}
158 		//If the last Unicode map we found before has more characters,
159 		//then set it to be the current map.
160 
161 		if (countUnicode > countCurrMap)
162 		{
163 //			qDebug() << "override with Unicode enc for" << face->family_name << face->style_name << "map" << mapToSet << "has only" << countCurrMap << "glyphs";
164 			mapToSet = chmapUnicode;
165 			retVal = 0;
166 		}
167 	}
168 	FT_Set_Charmap(face, face->charmaps[mapToSet]);
169 //	qDebug() << "set map" << mapToSet << "for" << face->family_name << face->style_name;
170 //	qDebug() << "glyphsForNumbers 0-9:" << FT_Get_Char_Index(face, QChar('0').unicode())
171 //		<< FT_Get_Char_Index(face, QChar('1').unicode()) << FT_Get_Char_Index(face, QChar('2').unicode()) << FT_Get_Char_Index(face, QChar('3').unicode())
172 //		<< FT_Get_Char_Index(face, QChar('4').unicode()) << FT_Get_Char_Index(face, QChar('5').unicode()) << FT_Get_Char_Index(face, QChar('6').unicode())
173 //		<< FT_Get_Char_Index(face, QChar('7').unicode()) << FT_Get_Char_Index(face, QChar('8').unicode()) << FT_Get_Char_Index(face, QChar('9').unicode());
174 	return retVal;
175 }
176 
traceGlyph(FT_Face face,ScFace::gid_type glyphIndex,int chs,qreal * x,qreal * y,bool * err)177 FPointArray traceGlyph(FT_Face face, ScFace::gid_type glyphIndex, int chs, qreal *x, qreal *y, bool *err)
178 {
179 	bool error = false;
180 	//AV: not threadsave, but tracechar is only used in ReadMetrics() and fontSample()
181 	static FPointArray pts;
182 	FPointArray pts2;
183 	pts.resize(0);
184 	pts2.resize(0);
185 	firstP = FPoint(0,0);
186 	FirstM = true;
187 	error = FT_Set_Char_Size( face, 0, chs*6400, 72, 72 );
188 	if (error)
189 	{
190 		*err = error;
191 		return pts2;
192 	}
193 
194 	error = FT_Load_Glyph( face, glyphIndex, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP );
195 	if (error)
196 	{
197 		*err = error;
198 		return pts2;
199 	}
200 	error = FT_Outline_Decompose(&face->glyph->outline, &OutlineMethods, reinterpret_cast<void*>(&pts));
201 	if (error)
202 	{
203 		*err = error;
204 		return pts2;
205 	}
206 	*x = face->glyph->metrics.horiBearingX / 6400.0;
207 	*y = face->glyph->metrics.horiBearingY / 6400.0;
208 	QTransform ma;
209 	ma.scale(0.01, -0.01);
210 	pts.map(ma);
211 	pts.translate(0, chs);
212 	pts2.putPoints(0, pts.size()-2, pts, 0);
213 
214 	return pts2;
215 }
216 
217 
traceChar(FT_Face face,ScFace::ucs4_type chr,int chs,qreal * x,qreal * y,bool * err)218 FPointArray traceChar(FT_Face face, ScFace::ucs4_type chr, int chs, qreal *x, qreal *y, bool *err)
219 {
220 	bool error = false;
221 	FT_UInt glyphIndex;
222 	error = FT_Set_Char_Size( face, 0, chs*64, 72, 72 );
223 	if (error)
224 	{
225 		*err = error;
226 		return FPointArray();
227 	}
228 	glyphIndex = FT_Get_Char_Index(face, chr);
229 	return traceGlyph(face, glyphIndex, chs, x, y, err);
230 }
231 
232 
FontSample(const ScFace & fnt,int s,QVector<uint> ts,const QColor & back,bool force)233 QPixmap FontSample(const ScFace& fnt, int s, QVector<uint> ts, const QColor& back, bool force)
234 {
235 	FT_Face face;
236 	FT_Library library;
237 	qreal x, y, ymax;
238 	bool error;
239 	int  pen_x;
240 	FPoint gp;
241 	error = FT_Init_FreeType( &library );
242 	error = FT_New_Face( library, QFile::encodeName(fnt.fontFilePath()), fnt.faceIndex(), &face );
243 	int encode = setBestEncoding(face);
244 	qreal uniEM = static_cast<qreal>(face->units_per_EM);
245 
246 //	qreal m_descent = face->descender / uniEM;
247 	qreal m_height = qMax(face->height / uniEM, (face->bbox.yMax - face->bbox.yMin) / uniEM);
248 //	if (m_height == 0)
249 //		m_height = (face->bbox.yMax - face->bbox.yMin) / uniEM;
250 
251 	int h = qRound(m_height * s) + 1;
252 	qreal a = 0;//m_descent * s + 1;
253 	int w = qRound((face->bbox.xMax - face->bbox.xMin) / uniEM) * s * (ts.length()+1);
254 	if (w < 1)
255 		w = s * (ts.length()+1);
256 	if (h < 1)
257 		h = s;
258 	QImage pm(w, h, QImage::Format_ARGB32_Premultiplied);
259 	pen_x = 0;
260 	ymax = 0.0;
261 	ScPainter *p = new ScPainter(&pm, pm.width(), pm.height());
262 	p->clear(back);
263 	p->setFillMode(ScPainter::Solid);
264 	p->setLineWidth(0.0);
265 //	p->setBrush(back);
266 //	p->drawRect(0.0, 0.0, static_cast<qreal>(w), static_cast<qreal>(h));
267 	p->setBrush(Qt::black);
268 	FPointArray gly;
269 	ScFace::ucs4_type dv;
270 	dv = ts[0];
271 	error = false;
272 	gly = traceChar(face, dv, s, &x, &y, &error);
273 	if (((encode != 0) || (error)) && (!force))
274 	{
275 		error = false;
276 		FT_ULong  charcode;
277 		FT_UInt gindex;
278 		gindex = 0;
279 		charcode = FT_Get_First_Char(face, &gindex );
280 		for (int n = 0; n < ts.length(); ++n)
281 		{
282 			gly = traceChar(face, charcode, s, &x, &y, &error);
283 			if (error)
284 				break;
285 			if (gly.size() > 3)
286 			{
287 				gly.translate(static_cast<qreal>(pen_x) / 6400.0, a);
288 				gp = getMaxClipF(&gly);
289 				ymax = qMax(ymax, gp.y());
290 				p->setupPolygon(&gly);
291 				p->fillPath();
292 			}
293 			pen_x += face->glyph->advance.x;
294 			charcode = FT_Get_Next_Char(face, charcode, &gindex );
295 			if (gindex == 0)
296 				break;
297 		}
298 	}
299 	else
300 	{
301 		for (int n = 0; n < ts.length(); ++n)
302 		{
303 			dv = ts[n];
304 			error = false;
305 			gly = traceChar(face, dv, s, &x, &y, &error);
306 			if (gly.size() > 3)
307 			{
308 				gly.translate(static_cast<qreal>(pen_x) / 6400.0, a);
309 				gp = getMaxClipF(&gly);
310 				ymax = qMax(ymax, gp.y());
311 				p->setupPolygon(&gly);
312 				p->fillPath();
313 			}
314 			pen_x += face->glyph->advance.x;
315 		}
316 	}
317 	p->end();
318 	QPixmap pmr;
319 	pmr=QPixmap::fromImage(pm.copy(0, 0, qMin(qRound(gp.x()), w), qMin(qRound(ymax), h)));
320 // this one below gives some funny results
321 //	pmr.convertFromImage(pm.scaled(qMin(qRound(gp.x()), w), qMin(qRound(ymax), h), Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
322 //	pmr.resize(qMin(qRound(gp.x()), w), qMin(qRound(ymax), h));
323 	delete p;
324 	FT_Done_FreeType( library );
325 	return pmr;
326 }
327 
328 #if 0
329 bool GlyphNames(const FtFace& fnt, FaceEncoding& GList)
330 {
331 	char buf[50];
332 	FT_ULong  charcode;
333 	FT_UInt gindex = 0;
334 
335 	FT_Face face = fnt.ftFace();
336 	if (!face)
337 		return false;
338 
339 	if (adobeGlyphNames.empty())
340 		readAdobeGlyphNames();
341 
342 	// The glyph name table embedded in Truetype fonts is not reliable.
343 	// For those fonts we consequently use Adobe Glyph names whenever possible.
344 	const bool avoidFntNames = (fnt.formatCode != ScFace::TYPE42 && fnt.typeCode == ScFace::TTF) &&
345 	                           (face->charmap && face->charmap->encoding == FT_ENCODING_UNICODE && face->charmap->platform_id == TT_PLATFORM_MICROSOFT);
346 
347 	const bool hasPSNames = FT_HAS_GLYPH_NAMES(face);
348 
349 //	qDebug() << "reading metrics for" << face->family_name << face->style_name;
350 	charcode = FT_Get_First_Char(face, &gindex );
351 	while (gindex != 0)
352 	{
353 		bool notfound = true;
354 		if (hasPSNames && !avoidFntNames)
355 			notfound = FT_Get_Glyph_Name(face, gindex, &buf, 50);
356 
357 		// just in case FT gives empty string or ".notdef"
358 		// no valid glyphname except ".notdef" starts with '.'
359 //		qDebug() << "\t" << gindex << " '" << charcode << "' --> '" << (notfound? "notfound" : buf) << "'";
360 		if (notfound || buf[0] == '\0' || buf[0] == '.')
361 			GList.insert(gindex, std::make_pair(static_cast<ucs4_type>(charcode), adobeGlyphName(charcode)));
362 		else
363 			GList.insert(gindex, std::make_pair(static_cast<ucs4_type>(charcode), QString(reinterpret_cast<char*>(buf))));
364 
365 		charcode = FT_Get_Next_Char(face, charcode, &gindex );
366 	}
367 
368 	if (!hasPSNames)
369 		return true;
370 
371 	// Let's see if we can find some more...
372 	int maxSlot1 = face->num_glyphs;
373 	for (int gindex = 1; gindex < maxSlot1; ++gindex)
374 	{
375 		if (GList.contains(gindex))
376 			continue;
377 		if (FT_Get_Glyph_Name(face, gindex, &buf, 50))
378 			continue;
379 		QString glyphname(reinterpret_cast<char*>(buf));
380 
381 		charcode = 0;
382 		faceEncoding::Iterator gli;
383 		for (gli = GList.begin(); gli != GList.end(); ++gli)
384 		{
385 			if (glyphname == gli.value().second)
386 			{
387 				charcode = gli.value().first.unicode();
388 				break;
389 			}
390 		}
391 //		qDebug() << "\tmore: " << gindex << " '" << charcode << "' --> '" << buf << "'";
392 		if (avoidFntNames && buf[0] != '.' && buf[0] != '\0')
393 			glyphname = adobeGlyphName(charcode);
394 		GList.insert(gindex, std::make_pair(static_cast<ucs4_type>(charcode), glyphname));
395 	}
396 
397 	return true;
398 }
399 #endif
400 
traceMoveto(FT_Vector * to,FPointArray * composite)401 static int traceMoveto( FT_Vector *to, FPointArray *composite )
402 {
403 	qreal tox = ( to->x / FTSCALE );
404 	qreal toy = ( to->y / FTSCALE );
405 	if (!FirstM)
406 	{
407 		composite->addPoint(firstP);
408 		composite->addPoint(firstP);
409 		composite->setMarker();
410 	}
411 	else
412 		FirstM = false;
413 	composite->addPoint(tox, toy);
414 	composite->addPoint(tox, toy);
415 	firstP.setXY(tox, toy);
416 	return 0;
417 }
418 
traceLineto(FT_Vector * to,FPointArray * composite)419 static int traceLineto( FT_Vector *to, FPointArray *composite )
420 {
421 	qreal tox = ( to->x / FTSCALE );
422 	qreal toy = ( to->y / FTSCALE );
423 	if (!composite->hasLastQuadPoint(tox, toy, tox, toy, tox, toy, tox, toy))
424 		composite->addQuadPoint(tox, toy, tox, toy, tox, toy, tox, toy);
425 	return 0;
426 }
427 
traceQuadraticBezier(FT_Vector * control,FT_Vector * to,FPointArray * composite)428 static int traceQuadraticBezier( FT_Vector *control, FT_Vector *to, FPointArray *composite )
429 {
430 	const FPoint& prev = composite->last();
431 	qreal x1 = (prev.x() + 2 * control->x / FTSCALE) / 3.0;
432 	qreal y1 = (prev.y() + 2 * control->y / FTSCALE) / 3.0;
433 	qreal x2 = (to->x / FTSCALE + 2 * (control->x / FTSCALE)) / 3.0;
434 	qreal y2 = (to->y / FTSCALE + 2 * (control->y / FTSCALE)) / 3.0;
435 	qreal x3 = (to->x / FTSCALE);
436 	qreal y3 = (to->y / FTSCALE);
437 	if ( !composite->hasLastQuadPoint(x3, y3, x2, y2, x3, y3, x3, y3) )
438 	{
439 		composite->setPoint(composite->size() - 1, FPoint(x1, y1));
440 		composite->addQuadPoint(x3, y3, x2, y2, x3, y3, x3, y3);
441 	}
442 	return 0;
443 }
444 
traceCubicBezier(FT_Vector * p,FT_Vector * q,FT_Vector * to,FPointArray * composite)445 static int traceCubicBezier( FT_Vector *p, FT_Vector *q, FT_Vector *to, FPointArray *composite )
446 {
447 	qreal x1 = ( p->x / FTSCALE );
448 	qreal y1 = ( p->y / FTSCALE );
449 	qreal x2 = ( q->x / FTSCALE );
450 	qreal y2 = ( q->y / FTSCALE );
451 	qreal x3 = ( to->x / FTSCALE );
452 	qreal y3 = ( to->y / FTSCALE );
453 	if ( !composite->hasLastQuadPoint(x3, y3, x2, y2, x3, y3, x3, y3) )
454 	{
455 		composite->setPoint(composite->size() - 1, FPoint(x1, y1));
456 		composite->addQuadPoint(x3, y3, x2, y2, x3, y3, x3, y3);
457 	}
458 	return 0;
459 }
460 
461 /// init the Adobe Glyph List
462 #if 0
463 void readAdobeGlyphNames()
464 {
465 	adobeGlyphNames.clear();
466 	QRegExp pattern("(\\w*);([0-9A-Fa-f]{4})");
467 	for (uint i=0; table[i]; ++i) {
468 		if (pattern.indexIn(table[i]) >= 0) {
469 			FT_ULong unicode = pattern.cap(2).toULong(0, 16);
470 			qDebug() << QString("reading glyph name %1 for unicode %2(%3)").arg(pattern.cap(1)).arg(unicode).arg(pattern.cap(2));
471 			adobeGlyphNames.insert(unicode, pattern.cap(1));
472 		}
473 	}
474 }
475 #endif
476 
477 /// if in AGL, use that name, else use "uni1234" or "u12345"
adobeGlyphName(FT_ULong charcode)478 QString adobeGlyphName(FT_ULong charcode)
479 {
480 	static const char HEX[] = "0123456789ABCDEF";
481 	QString result;
482 	if (adobeGlyphNames.contains(charcode))
483 		return adobeGlyphNames[charcode];
484 	if (charcode < 0x10000)
485 	{
486 		result = QString("uni") + HEX[charcode>>12 & 0xF]
487 		                        + HEX[charcode>> 8 & 0xF]
488 		                        + HEX[charcode>> 4 & 0xF]
489 		                        + HEX[charcode     & 0xF];
490 	}
491 	else
492 	{
493 		result = QString("u");
494 		for (int i= 28; i >= 0; i-=4)
495 		{
496 			if (charcode & (0xF << i))
497 				result += HEX[charcode >> i & 0xF];
498 		}
499 	}
500 	return result;
501 }
502