Rev 6213 | Rev 6302 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5980 | avox | 1 | |
2 | #include "fonts/ftface.h" |
||
3 | |||
4 | #include FT_OUTLINE_H |
||
5 | #include FT_GLYPH_H |
||
6 | |||
7 | #include <qobject.h> |
||
8 | #include <qfile.h> |
||
9 | |||
10 | #include "scfonts.h" |
||
11 | #include "fonts/scfontmetrics.h" |
||
12 | |||
13 | // static: |
||
14 | FT_Library FtFace::library = NULL; |
||
15 | |||
16 | /***** |
||
17 | ScFace lifecycle: unchecked -> loaded -> glyphs checked |
||
18 | | \-> broken glyphs |
||
19 | \-> broken |
||
20 | usable() == ! broken |
||
21 | embeddable() == glyphs_checked |
||
22 | |||
23 | canRender(unicode) -> CharMap cache? -> loadChar/Glyph -> !broken |
||
24 | Glyphs: width status |
||
25 | -1000 unkown |
||
26 | -2000 broken |
||
27 | >= 0 ok, outline valid |
||
28 | CharMap: unicode -> glyph index |
||
29 | uint[256][256] |
||
30 | unicode ignores: < 32, ... |
||
31 | unicode emulate: spaces, hyphen, ligatures?, diacritics? |
||
32 | *****/ |
||
33 | |||
34 | FtFace::FtFace(QString fam, QString sty, QString vari, QString scname, |
||
35 | QString psname, QString path, int face) |
||
36 | : ScFaceData(), m_face(NULL) |
||
37 | { |
||
38 | family = fam; |
||
39 | style = sty; |
||
40 | variant = vari; |
||
41 | scName = scname; |
||
42 | psName = psname; |
||
43 | fontFile = path; |
||
44 | faceIndex = face; |
||
45 | if (!library) { |
||
46 | if (FT_Init_FreeType( &library )) |
||
47 | qDebug(QObject::tr("Freetype2 library not available")); |
||
48 | } |
||
49 | } |
||
50 | |||
51 | |||
52 | FtFace::~FtFace() { |
||
53 | unload(); |
||
54 | } |
||
55 | |||
56 | |||
57 | FT_Face FtFace::ftFace() const { |
||
58 | if (!m_face) { |
||
59 | if (FT_New_Face( library, QFile::encodeName(fontFile), faceIndex, & m_face )) { |
||
60 | status = ScFace::BROKEN; |
||
61 | m_face = NULL; |
||
62 | qDebug(QObject::tr("Font %1(%2) is broken").arg(fontFile).arg(faceIndex)); |
||
63 | } |
||
64 | else { |
||
65 | load(); |
||
66 | } |
||
67 | } |
||
68 | return m_face; |
||
69 | } |
||
70 | |||
71 | void FtFace::load() const |
||
72 | { |
||
73 | ScFaceData::load(); |
||
74 | |||
75 | if (!m_face) { |
||
76 | if (FT_New_Face( library, QFile::encodeName(fontFile), faceIndex, & m_face )) { |
||
77 | status = ScFace::BROKEN; |
||
78 | m_face = NULL; |
||
79 | qDebug(QObject::tr("Font %1(%2) is broken").arg(fontFile).arg(faceIndex)); |
||
80 | return; |
||
81 | } |
||
82 | } |
||
83 | |||
84 | const_cast<FtFace*>(this)->isStroked = false; |
||
85 | m_encoding = 0; |
||
86 | |||
87 | m_uniEM = static_cast<double>(m_face->units_per_EM); |
||
88 | |||
89 | m_descent = m_face->descender / m_uniEM; |
||
90 | m_ascent = m_face->ascender / m_uniEM; |
||
91 | m_height = m_face->height / m_uniEM; |
||
92 | m_xHeight = m_height; |
||
93 | m_capHeight = m_height; |
||
94 | m_maxAdvanceWidth = m_face->max_advance_width / m_uniEM; |
||
95 | m_underlinePos = m_face->underline_position / m_uniEM; |
||
96 | m_strikeoutPos = m_ascent / 3; |
||
97 | m_strokeWidth = m_face->underline_thickness / m_uniEM; |
||
98 | const_cast<FtFace*>(this)->isFixedPitch = m_face->face_flags & 4; |
||
6213 | fschmid | 99 | Ascent = QString::number(m_face->ascender); |
100 | CapHeight = QString::number(m_face->height); |
||
101 | Descender = QString::number(m_face->descender); |
||
102 | FontBBox = QString::number(m_face->bbox.xMin)+" "+QString::number(m_face->bbox.yMin)+" "+QString::number(m_face->bbox.xMax)+" "+QString::number(m_face->bbox.yMax); |
||
103 | ItalicAngle = "0"; |
||
5980 | avox | 104 | |
105 | //FIXME: FT_Set_Charmap(m_face, m_face->charmaps[m_encoding]); |
||
106 | setBestEncoding(m_face); |
||
107 | |||
108 | FT_UInt gindex = 0; |
||
109 | FT_ULong charcode = FT_Get_First_Char( m_face, &gindex ); |
||
110 | int goodGlyph = 0; |
||
111 | int invalidGlyph = 0; |
||
112 | bool error; |
||
113 | |||
114 | while ( gindex != 0 ) |
||
115 | { |
||
116 | error = FT_Load_Glyph( m_face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ); |
||
117 | if (error) |
||
118 | { |
||
119 | ++invalidGlyph; |
||
120 | qDebug(QObject::tr("Font %1 has broken glyph %2 (charcode %3)").arg(fontFile).arg(gindex).arg(charcode)); |
||
121 | charcode = FT_Get_Next_Char( m_face, charcode, &gindex ); |
||
122 | continue; |
||
123 | } |
||
124 | |||
125 | if (gindex > maxGlyph) |
||
126 | const_cast<FtFace*>(this)->maxGlyph = gindex; |
||
127 | |||
128 | ++goodGlyph; |
||
129 | if (m_face->glyph->format == FT_GLYPH_FORMAT_PLOTTER) |
||
130 | const_cast<FtFace*>(this)->isStroked = true; |
||
131 | charcode = FT_Get_Next_Char( m_face, charcode, &gindex ); |
||
132 | } |
||
133 | if (invalidGlyph > 0) { |
||
134 | status = ScFace::BROKENGLYPHS; |
||
135 | } |
||
136 | } |
||
137 | |||
138 | |||
139 | void FtFace::unload() const |
||
140 | { |
||
141 | if (m_face) { |
||
142 | FT_Done_Face( m_face ); |
||
143 | m_face = NULL; |
||
144 | } |
||
145 | // clear caches |
||
146 | ScFaceData::unload(); |
||
147 | } |
||
148 | |||
149 | |||
150 | uint FtFace::char2CMap(QChar ch) const |
||
151 | { |
||
152 | // FIXME use cMap cache |
||
153 | FT_Face face = ftFace(); |
||
154 | uint gl = FT_Get_Char_Index(face, ch.unicode()); |
||
155 | return gl; |
||
156 | } |
||
157 | |||
158 | |||
159 | void FtFace::loadGlyph(uint gl) const |
||
160 | { |
||
161 | ScFace::GlyphData GRec; |
||
162 | FT_Face face = ftFace(); |
||
163 | if (FT_Load_Glyph( face, gl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP )) |
||
164 | { |
||
165 | qDebug(QObject::tr("Font %1 has broken glyph %2").arg(fontFile).arg(gl)); |
||
166 | m_glyphWidth[gl] = 1; |
||
167 | } |
||
168 | else { |
||
169 | double ww = face->glyph->metrics.horiAdvance / m_uniEM; |
||
170 | double x, y; |
||
171 | bool error = false; |
||
6220 | fschmid | 172 | error = FT_Set_Char_Size( face, 0, 10, 72, 72 ); |
173 | if (error) |
||
174 | m_glyphWidth[gl] = 1; |
||
5980 | avox | 175 | FPointArray outlines = traceGlyph(face, gl, 10, &x, &y, &error); |
176 | if (!error) |
||
177 | { |
||
178 | m_glyphWidth[gl] = ww; |
||
179 | GRec.Outlines = outlines; |
||
180 | GRec.x = x; |
||
181 | GRec.y = y; |
||
182 | GRec.broken = false; |
||
183 | } |
||
184 | else { |
||
185 | m_glyphWidth[gl] = 1; |
||
186 | } |
||
187 | } |
||
188 | m_glyphOutline[gl] = GRec; |
||
189 | if (GRec.broken && status < ScFace::BROKENGLYPHS) |
||
190 | status = ScFace::BROKENGLYPHS; |
||
191 | } |
||
192 | |||
193 | |||
194 | double FtFace::glyphKerning(uint gl, uint gl2, double size) const |
||
195 | { |
||
196 | FT_Vector delta; |
||
197 | FT_Face face = ftFace(); |
||
198 | double result = 0; |
||
199 | /**** |
||
200 | Ok, this looks like a regression between Freetype 2.1.9 -> 2.1.10. |
||
201 | Ignoring the value of FT_HAS_KERNING for now -- AV |
||
202 | ****/ |
||
203 | if (true || FT_HAS_KERNING(face) ) |
||
204 | { |
||
205 | FT_Error error = FT_Get_Kerning(face, gl, gl2, FT_KERNING_UNSCALED, &delta); |
||
206 | if (error) { |
||
207 | qDebug(QString("Error %2 when accessing kerning pair for font %1").arg(scName).arg(error)); |
||
208 | } |
||
209 | else { |
||
210 | result = delta.x / m_uniEM * size; |
||
211 | } |
||
212 | } |
||
213 | else { |
||
214 | qDebug(QString("Font %1 has no kerning pairs (according to Freetype)").arg(scName)); |
||
215 | } |
||
216 | return result; |
||
217 | } |
||
218 | |||
219 | |||
220 | GlyphMetrics FtFace::glyphBBox (uint gl, double sz) const |
||
221 | { |
||
222 | FT_Face face = ftFace(); |
||
223 | GlyphMetrics result; |
||
224 | FT_Error error = FT_Load_Glyph( face, gl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ); |
||
225 | if (!error) { |
||
226 | double w = (face->glyph->metrics.width + QABS((double)face->glyph->metrics.horiBearingX)) / m_uniEM * sz; |
||
227 | result.width = QMAX(w, face->glyph->metrics.horiAdvance / m_uniEM * sz); |
||
228 | double height = face->glyph->metrics.height / m_uniEM * sz; |
||
229 | result.ascent = face->glyph->metrics.horiBearingY / m_uniEM * sz; |
||
230 | result.descent = result.ascent - height; |
||
231 | } |
||
232 | else { |
||
233 | result.width = result.ascent = sz; |
||
234 | result.descent = 0; |
||
235 | } |
||
236 | return result; |
||
237 | } |
||
238 | |||
239 | /// copied from Freetype's FT_Stream_ReadAt() |
||
240 | FT_Error ftIOFunc( FT_Stream stream, unsigned long pos, unsigned char* buffer, unsigned long count) |
||
241 | { |
||
242 | FT_Error error = FT_Err_Ok; |
||
243 | FT_ULong read_bytes; |
||
244 | |||
245 | if ( pos >= stream->size ) |
||
246 | { |
||
247 | qDebug( "ftIOFunc: invalid i/o; pos = 0x%lx, size = 0x%lx\n", |
||
248 | pos, stream->size ); |
||
249 | |||
250 | return FT_Err_Invalid_Stream_Operation; |
||
251 | } |
||
252 | |||
253 | if ( stream->read ) |
||
254 | read_bytes = stream->read( stream, pos, buffer, count ); |
||
255 | else |
||
256 | { |
||
257 | read_bytes = stream->size - pos; |
||
258 | if ( read_bytes > count ) |
||
259 | read_bytes = count; |
||
260 | |||
261 | memcpy( buffer, stream->base + pos, read_bytes ); |
||
262 | } |
||
263 | |||
264 | stream->pos = pos + read_bytes; |
||
265 | |||
266 | if ( read_bytes < count ) |
||
267 | { |
||
268 | qDebug( "ftIOFunc: invalid read; expected %lu bytes, got %lu\n", |
||
269 | count, read_bytes ); |
||
270 | |||
271 | error = FT_Err_Invalid_Stream_Operation; |
||
272 | } |
||
273 | |||
274 | return error; |
||
275 | } |
||
276 | |||
277 | |||
6163 | avox | 278 | bool FtFace::glyphNames(QMap<uint, std::pair<QChar, QString> >& GList) const |
5980 | avox | 279 | { |
280 | return GlyNames(ftFace(), GList); |
||
281 | } |
||
282 | |||
283 | |||
6213 | fschmid | 284 | bool FtFace::glyphNameIndex(QMap<uint, std::pair<uint, QString> >& GList) const |
285 | { |
||
286 | return GlyIndex(ftFace(), GList); |
||
287 | } |
||
288 | |||
5980 | avox | 289 | void FtFace::RawData(QByteArray & bb) const |
290 | { |
||
291 | FT_Stream fts = ftFace()->stream; |
||
292 | bb.resize(fts->size); |
||
293 | bool error = ftIOFunc(fts, 0L, reinterpret_cast<FT_Byte *>(bb.data()), fts->size); |
||
294 | if (error) |
||
295 | { |
||
296 | qDebug(QObject::tr("Font %1 is broken (read stream), no embedding").arg(fontFile)); |
||
297 | bb.resize(0); |
||
298 | status = QMAX(status, ScFace::BROKENGLYPHS); |
||
299 | } |
||
300 | /* |
||
301 | if (showFontInformation) |
||
302 | { |
||
303 | QFile f(fontFile); |
||
304 | qDebug(QObject::tr("RawData for Font %1(%2): size=%3 filesize=%4").arg(fontFile).arg(faceIndex_).arg(bb.size()).arg(f.size())); |
||
305 | } |
||
306 | */ |
||
307 | } |
||
308 | |||
309 |