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
8 #include "fonts/ftface.h"
9
10 #include FT_CID_H
11 #include FT_OUTLINE_H
12 #include FT_GLYPH_H
13 #include FT_TRUETYPE_IDS_H
14
15 #include <QObject>
16 #include <QFile>
17
18 #include "scfonts.h"
19 #include "util_debug.h"
20 #include "fonts/scfontmetrics.h"
21
22 // static:
23 FT_Library FtFace::m_library = nullptr;
24
25 /*****
26 ScFace lifecycle: unchecked -> loaded -> glyphs checked
27 | \-> broken glyphs
28 \-> broken
29 usable() == ! broken
30 embeddable() == glyphs_checked
31
32 loadChar/Glyph -> !broken
33 Glyphs: width status
34 -1000 unknown
35 -2000 broken
36 >= 0 ok, outline valid
37 CharMap: unicode -> glyph index
38 gid_type[256][256]
39 unicode ignores: < 32, ...
40 unicode emulate: spaces, hyphen, ligatures?, diacritics?
41 *****/
42
FtFace(const QString & fam,const QString & sty,const QString & vari,const QString & scname,const QString & psname,const QString & path,int face,const QStringList & features)43 FtFace::FtFace(const QString& fam, const QString& sty, const QString& vari, const QString& scname, const QString& psname, const QString& path, int face, const QStringList& features)
44 : m_face(nullptr),
45 m_isBold(false),
46 m_isItalic(false),
47 m_encoding(0.0),
48 m_uniEM(0.0),
49 m_ascent(0.0),
50 m_descent(0.0),
51 m_height(0.0),
52 m_xHeight(0.0),
53 m_capHeight(0.0),
54 m_maxAdvanceWidth(0.0),
55 m_underlinePos(0.0),
56 m_strikeoutPos(0.0),
57 m_strokeWidth(0.0)
58 {
59 family = fam;
60 style = sty;
61 variant = vari;
62 scName = scname;
63 psName = psname;
64 fontFile = path;
65 faceIndex = face;
66 fontFeatures = features;
67 if (!m_library)
68 if (FT_Init_FreeType( &m_library ))
69 sDebug(QObject::tr("Freetype2 library not available"));
70 }
71
72
~FtFace()73 FtFace::~FtFace() {
74 unload();
75 }
76
77
ftFace() const78 FT_Face FtFace::ftFace() const
79 {
80 if (!m_face)
81 {
82 if (FT_New_Face( m_library, QFile::encodeName(fontFile), faceIndex, & m_face ))
83 {
84 status = ScFace::BROKEN;
85 m_face = nullptr;
86 sDebug(QObject::tr("Font %1(%2) is broken").arg(fontFile).arg(faceIndex));
87 }
88 else
89 load();
90 }
91 return m_face;
92 }
93
load() const94 void FtFace::load() const
95 {
96 ScFaceData::load();
97
98 if (!m_face) {
99 if (FT_New_Face( m_library, QFile::encodeName(fontFile), faceIndex, & m_face ))
100 {
101 status = ScFace::BROKEN;
102 m_face = nullptr;
103 sDebug(QObject::tr("Font %1(%2) is broken").arg(fontFile).arg(faceIndex));
104 return;
105 }
106 }
107
108 FT_Bool isCID = false;
109 FT_Get_CID_Is_Internally_CID_Keyed(m_face, &isCID);
110
111 const_cast<FtFace*>(this)->isCIDFont = isCID;
112 const_cast<FtFace*>(this)->isStroked = false;
113 const_cast<FtFace*>(this)->hasGlyphNames = FT_HAS_GLYPH_NAMES(m_face);
114
115 m_encoding = 0;
116 m_uniEM = static_cast<qreal>(m_face->units_per_EM);
117
118 m_descent = m_face->descender / m_uniEM;
119 m_ascent = m_face->ascender / m_uniEM;
120 m_height = m_face->height / m_uniEM;
121
122 /* Temporary fix for the broken "Dutch Initials" font */
123 if ((m_ascent == 0) && (m_descent == 0))
124 {
125 m_ascent = (m_face->bbox.yMax - m_face->bbox.yMin) / m_uniEM;
126 m_height = m_ascent;
127 }
128
129 m_xHeight = m_height;
130 m_capHeight = m_height;
131 m_maxAdvanceWidth = m_face->max_advance_width / m_uniEM;
132 m_underlinePos = m_face->underline_position / m_uniEM;
133 m_strikeoutPos = m_ascent / 3;
134 m_strokeWidth = m_face->underline_thickness / m_uniEM;
135 const_cast<FtFace*>(this)->isFixedPitch = m_face->face_flags & 4;
136 m_pdfAscent = QString::number(m_face->ascender / m_uniEM * 1000);
137 m_pdfCapHeight = QString::number(m_face->height / m_uniEM * 1000);
138 m_pdfDescender = QString::number(m_face->descender / m_uniEM * 1000);
139 m_pdfFontBBox = QString::number(m_face->bbox.xMin / m_uniEM * 1000) + " " +
140 QString::number(m_face->bbox.yMin / m_uniEM * 1000) + " " +
141 QString::number(m_face->bbox.xMax / m_uniEM * 1000) + " " +
142 QString::number(m_face->bbox.yMax / m_uniEM * 1000);
143 m_italicAngle = "0";
144
145 m_isItalic = (m_face->style_flags == 1 || m_face->style_flags == 3);
146 m_isBold = (m_face->style_flags == 2 || m_face->style_flags == 3);
147
148 //FIXME: FT_Set_Charmap(m_face, m_face->charmaps[m_encoding]);
149 setBestEncoding(m_face);
150
151 FT_UInt gindex = 0;
152 FT_ULong charcode = FT_Get_First_Char( m_face, &gindex );
153 int goodGlyph = 0;
154 int invalidGlyph = 0;
155 bool error;
156
157 while ( gindex != 0 )
158 {
159 error = FT_Load_Glyph( m_face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
160 if (error)
161 {
162 ++invalidGlyph;
163 sDebug(QObject::tr("Font %1 has broken glyph %2 (charcode %3)").arg(fontFile).arg(gindex).arg(charcode));
164 charcode = FT_Get_Next_Char( m_face, charcode, &gindex );
165 continue;
166 }
167
168 if (gindex > maxGlyph)
169 const_cast<FtFace*>(this)->maxGlyph = gindex;
170
171 ++goodGlyph;
172 if (m_face->glyph->format == FT_GLYPH_FORMAT_PLOTTER)
173 const_cast<FtFace*>(this)->isStroked = true;
174 charcode = FT_Get_Next_Char( m_face, charcode, &gindex );
175 }
176 if (invalidGlyph > 0) {
177 status = ScFace::BROKENGLYPHS;
178 }
179 }
180
181
unload() const182 void FtFace::unload() const
183 {
184 if (m_face) {
185 FT_Done_Face( m_face );
186 m_face = nullptr;
187 }
188 // clear caches
189 ScFaceData::unload();
190 }
191
192
char2CMap(uint ch) const193 ScFace::gid_type FtFace::char2CMap(uint ch) const
194 {
195 // FIXME use cMap cache
196 FT_Face face = ftFace();
197 ScFace::gid_type gl = FT_Get_Char_Index(face, ch);
198 return gl;
199 }
200
glyphIndexToCID(ScFace::gid_type index) const201 ScFace::cid_type FtFace::glyphIndexToCID(ScFace::gid_type index) const
202 {
203 FT_Face face = ftFace();
204
205 ScFace::cid_type cid = 0;
206 FT_Error err = FT_Get_CID_From_Glyph_Index(face, index, &cid);
207 if (err)
208 cid = index;
209 return cid;
210 }
211
loadGlyph(ScFace::gid_type gl) const212 void FtFace::loadGlyph(ScFace::gid_type gl) const
213 {
214 if (m_glyphWidth.contains(gl))
215 return;
216
217 ScFace::GlyphData GRec;
218 FT_Face face = ftFace();
219 if (FT_Load_Glyph( face, gl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ))
220 {
221 sDebug(QObject::tr("Font %1 has broken glyph %2").arg(fontFile).arg(gl));
222 m_glyphWidth[gl] = 1;
223 }
224 else
225 {
226 qreal ww = qreal(face->glyph->metrics.horiAdvance) / m_uniEM;
227 qreal w = (face->glyph->metrics.width + qAbs(qreal(face->glyph->metrics.horiBearingX))) / m_uniEM;
228 GRec.bbox_width = qMax(w, ww);
229 qreal height = qreal(face->glyph->metrics.height) / m_uniEM;
230 GRec.bbox_ascent = qreal(face->glyph->metrics.horiBearingY) / m_uniEM;
231 GRec.bbox_descent = height - GRec.bbox_ascent;
232 // qDebug() << QString("glyphmetrics %1: EM %2 bearing (%3,%4) size (%5,%6) advance %7 bbox (%8,%9)")
233 // .arg(gl).arg(m_uniEM).arg(face->glyph->metrics.horiBearingX).arg(face->glyph->metrics.horiBearingY)
234 // .arg(face->glyph->metrics.width).arg(face->glyph->metrics.height).arg(face->glyph->metrics.horiAdvance)
235 // .arg(w).arg(height);
236
237 qreal x, y;
238 bool error = false;
239 error = FT_Set_Char_Size( face, 0, 10, 72, 72 );
240 if (error)
241 m_glyphWidth[gl] = 1;
242 FPointArray outlines = traceGlyph(face, gl, 10, &x, &y, &error);
243 if (!error)
244 {
245 m_glyphWidth[gl] = ww;
246 GRec.Outlines = outlines;
247 GRec.x = x;
248 GRec.y = y;
249 GRec.broken = false;
250 }
251 else {
252 m_glyphWidth[gl] = 1;
253 }
254 }
255 m_glyphOutline[gl] = GRec;
256 if (GRec.broken && status < ScFace::BROKENGLYPHS)
257 status = ScFace::BROKENGLYPHS;
258 }
259
260
261 /*
262 GlyphMetrics FtFace::glyphBBox (gid_type gl, qreal sz) const
263 {
264 FT_Face face = ftFace();
265 GlyphMetrics result;
266 FT_Error error = FT_Load_Glyph( face, gl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
267 if (!error) {
268 qreal w = (face->glyph->metrics.width + QABS((qreal)face->glyph->metrics.horiBearingX)) / m_uniEM * sz;
269 result.width = qMax(w, face->glyph->metrics.horiAdvance / m_uniEM * sz);
270 qreal height = face->glyph->metrics.height / m_uniEM * sz;
271 result.ascent = face->glyph->metrics.horiBearingY / m_uniEM * sz;
272 result.descent = height - result.ascent;
273 }
274 else {
275 result.width = result.ascent = sz;
276 result.descent = 0;
277 }
278 return result;
279 }
280 */
281
282
283 /// copied from Freetype's FT_Stream_ReadAt()
ftIOFunc(FT_Stream stream,unsigned long pos,unsigned char * buffer,unsigned long count)284 FT_Error ftIOFunc( FT_Stream stream, unsigned long pos, unsigned char* buffer, unsigned long count)
285 {
286 FT_Error error = FT_Err_Ok;
287 FT_ULong read_bytes;
288
289 if ( pos >= stream->size )
290 {
291 qDebug( "ftIOFunc: invalid i/o; pos = 0x%lx, size = 0x%lx\n", pos, stream->size );
292
293 return FT_Err_Invalid_Stream_Operation;
294 }
295
296 if ( stream->read )
297 read_bytes = stream->read( stream, pos, buffer, count );
298 else
299 {
300 read_bytes = stream->size - pos;
301 if ( read_bytes > count )
302 read_bytes = count;
303
304 memcpy( buffer, stream->base + pos, read_bytes );
305 }
306
307 stream->pos = pos + read_bytes;
308
309 if ( read_bytes < count )
310 {
311 qDebug( "ftIOFunc: invalid read; expected %lu bytes, got %lu\n", count, read_bytes );
312
313 error = FT_Err_Invalid_Stream_Operation;
314 }
315
316 return error;
317 }
318
adobeGlyphName(FT_ULong charcode)319 QString FtFace::adobeGlyphName(FT_ULong charcode)
320 {
321 return ::adobeGlyphName(charcode);
322 }
323
324
hasMicrosoftUnicodeCmap(FT_Face face)325 bool FtFace::hasMicrosoftUnicodeCmap(FT_Face face)
326 {
327 return (face->charmap && face->charmap->encoding == FT_ENCODING_UNICODE && face->charmap->platform_id == TT_PLATFORM_MICROSOFT);
328 }
329
330
glyphNames(ScFace::FaceEncoding & glyphList) const331 bool FtFace::glyphNames(ScFace::FaceEncoding& glyphList) const
332 {
333 char buf[50];
334 FT_ULong charcode;
335 FT_UInt gindex = 0;
336
337 FT_Face face = ftFace();
338 if (!face)
339 return false;
340
341 const bool hasPSNames = FT_HAS_GLYPH_NAMES(face);
342
343 // qDebug() << "reading metrics for" << face->family_name << face->style_name;
344 charcode = FT_Get_First_Char(face, &gindex);
345 while (gindex != 0)
346 {
347 bool notfound = true;
348 if (hasPSNames)
349 notfound = FT_Get_Glyph_Name(face, gindex, &buf, 50);
350
351 // just in case FT gives empty string or ".notdef"
352 // no valid glyphname except ".notdef" starts with '.'
353 // qDebug() << "\t" << gindex << " '" << charcode << "' --> '" << (notfound? "notfound" : buf) << "'";
354 ScFace::GlyphEncoding glEncoding;
355 glEncoding.charcode = charcode;
356 if (notfound || buf[0] == '\0' || buf[0] == '.')
357 glEncoding.glyphName = adobeGlyphName(charcode);
358 else
359 glEncoding.glyphName = QString(reinterpret_cast<char*>(buf));
360 glEncoding.toUnicode = QString::asprintf("%04lX", charcode);
361 glyphList.insert(gindex, glEncoding);
362
363 charcode = FT_Get_Next_Char(face, charcode, &gindex);
364 }
365
366 if (!hasPSNames)
367 return true;
368
369 // Let's see if we can find some more...
370 int maxSlot1 = face->num_glyphs;
371 for (int gindex = 1; gindex < maxSlot1; ++gindex)
372 {
373 if (glyphList.contains(gindex))
374 continue;
375 if (FT_Get_Glyph_Name(face, gindex, &buf, 50))
376 continue;
377 QString glyphName(reinterpret_cast<char*>(buf));
378
379 charcode = 0;
380 for (auto gli = glyphList.cbegin(); gli != glyphList.cend(); ++gli)
381 {
382 const ScFace::GlyphEncoding& glEncoding = gli.value();
383 if (glyphName == glEncoding.glyphName)
384 {
385 charcode = glEncoding.charcode;
386 break;
387 }
388 }
389 // qDebug() << "\tmore: " << gindex << " '" << charcode << "' --> '" << buf << "'";
390 ScFace::GlyphEncoding glEncoding;
391 glEncoding.charcode = static_cast<ScFace::ucs4_type>(charcode);
392 glEncoding.glyphName = glyphName;
393 glEncoding.toUnicode = QString::asprintf("%04lX", charcode);
394 if ((charcode == 0) && glyphName.startsWith("uni"))
395 {
396 QString uniHexStr = uniGlyphNameToUnicode(glyphName);
397 if (uniHexStr.length() > 0)
398 glEncoding.toUnicode = uniHexStr;
399 }
400 glyphList.insert(gindex, glEncoding);
401 }
402
403 return true;
404 }
405
uniGlyphNameToUnicode(const QString & glyphName) const406 QString FtFace::uniGlyphNameToUnicode(const QString& glyphName) const
407 {
408 if (!glyphName.startsWith("uni"))
409 return QString();
410 if (glyphName.length() < 7)
411 return QString();
412
413 QString uniStr = glyphName.mid(3);
414 int firstDot = uniStr.indexOf('.');
415 if (firstDot >= 0)
416 uniStr = uniStr.left(firstDot);
417
418 bool isHexString = true;
419 if ((uniStr.length() <= 0) || (uniStr.length() % 4 != 0))
420 return QString();
421
422 int len = uniStr.length();
423 for (int i = 0; i < len; ++i)
424 {
425 int uni = uniStr[i].unicode();
426 isHexString &= (uni >= '0' && uni <= '9') ||
427 (uni >= 'a' && uni <= 'f') ||
428 (uni >= 'A' && uni <= 'F');
429 if (!isHexString)
430 break;
431 }
432
433 if (!isHexString)
434 return QString();
435 return uniStr.toUpper();
436 }
437
rawData(QByteArray & bb) const438 void FtFace::rawData(QByteArray & bb) const
439 {
440 FT_Stream fts = ftFace()->stream;
441 bb.resize(fts->size);
442 bool error = ftIOFunc(fts, 0L, reinterpret_cast<FT_Byte *>(bb.data()), fts->size);
443 if (error)
444 {
445 sDebug(QObject::tr("Font %1 is broken (read stream), no embedding").arg(fontFile));
446 bb.resize(0);
447 status = qMax(status, ScFace::BROKENGLYPHS);
448 }
449 }
450
451
452