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