Subversion Repositories Scribus

Rev

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