Subversion Repositories Scribus

Rev

Rev 8978 | 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;
7868 fschmid 92
 
93
/* Temporary fix for the broken "Dutch Initials" font */
94
        if ((m_ascent == 0) && (m_descent == 0))
95
        {
96
                m_ascent = (m_face->bbox.yMax - m_face->bbox.yMin) / m_uniEM;
97
                m_height = m_ascent;
98
        }
99
 
5980 avox 100
        m_xHeight = m_height;
101
        m_capHeight = m_height;
102
        m_maxAdvanceWidth = m_face->max_advance_width / m_uniEM;
103
        m_underlinePos = m_face->underline_position / m_uniEM;
104
        m_strikeoutPos = m_ascent / 3;
105
        m_strokeWidth = m_face->underline_thickness / m_uniEM;
106
        const_cast<FtFace*>(this)->isFixedPitch = m_face->face_flags & 4;
6213 fschmid 107
        Ascent = QString::number(m_face->ascender);
108
        CapHeight = QString::number(m_face->height);
109
        Descender = QString::number(m_face->descender);
110
        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);
111
        ItalicAngle = "0";
5980 avox 112
 
113
//FIXME:        FT_Set_Charmap(m_face, m_face->charmaps[m_encoding]);
114
        setBestEncoding(m_face);
115
 
116
        FT_UInt gindex = 0;
117
        FT_ULong charcode = FT_Get_First_Char( m_face, &gindex );
118
        int goodGlyph = 0;
119
        int invalidGlyph = 0;
120
        bool error;
121
 
122
        while ( gindex != 0 )
123
        {
124
                error = FT_Load_Glyph( m_face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
125
                if (error)
126
                {
127
                        ++invalidGlyph;
128
                        qDebug(QObject::tr("Font %1 has broken glyph %2 (charcode %3)").arg(fontFile).arg(gindex).arg(charcode));
129
                        charcode = FT_Get_Next_Char( m_face, charcode, &gindex );
130
                        continue;
131
                }
132
 
133
                if (gindex > maxGlyph)
134
                        const_cast<FtFace*>(this)->maxGlyph = gindex;
135
 
136
                ++goodGlyph;
137
                if (m_face->glyph->format == FT_GLYPH_FORMAT_PLOTTER)
138
                        const_cast<FtFace*>(this)->isStroked = true;
139
                charcode = FT_Get_Next_Char( m_face, charcode, &gindex );
140
        }
141
        if (invalidGlyph > 0) {
142
                status = ScFace::BROKENGLYPHS;
143
        }
144
}
145
 
146
 
147
void FtFace::unload() const
148
{
149
        if (m_face) {
150
                FT_Done_Face( m_face );
151
                m_face = NULL;
152
        }
153
        // clear caches
154
        ScFaceData::unload();
155
}
156
 
157
 
158
uint FtFace::char2CMap(QChar ch) const
159
{
160
        // FIXME use cMap cache
161
        FT_Face face = ftFace();
162
        uint gl = FT_Get_Char_Index(face, ch.unicode());
163
        return gl;
164
}
165
 
166
 
167
void FtFace::loadGlyph(uint gl) const
168
{
6348 avox 169
        if (m_glyphWidth.contains(gl))
170
                return;
171
 
5980 avox 172
        ScFace::GlyphData GRec;
173
        FT_Face face = ftFace();
174
        if (FT_Load_Glyph( face, gl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ))
175
        {
176
                qDebug(QObject::tr("Font %1 has broken glyph %2").arg(fontFile).arg(gl));
177
                m_glyphWidth[gl] = 1;
178
        }
179
        else {
8978 avox 180
                double ww = double(face->glyph->metrics.horiAdvance) / m_uniEM;
181
                double w  = (face->glyph->metrics.width + QABS(double(face->glyph->metrics.horiBearingX))) / m_uniEM;
182
                GRec.bbox_width = QMAX(w, ww);
183
                double height = double(face->glyph->metrics.height) / m_uniEM;
184
                GRec.bbox_ascent = double(face->glyph->metrics.horiBearingY) / m_uniEM;
185
                GRec.bbox_descent = height - GRec.bbox_ascent;
8979 avox 186
//              qDebug(QString("glyphmetrics %1: EM %2 bearing (%3,%4) size (%5,%6) advance %7 bbox (%8,%9)")
187
//                         .arg(gl).arg(m_uniEM).arg(face->glyph->metrics.horiBearingX).arg(face->glyph->metrics.horiBearingY)
188
//                         .arg(face->glyph->metrics.width).arg(face->glyph->metrics.height).arg(face->glyph->metrics.horiAdvance)
189
//                         .arg(w).arg(height));
8978 avox 190
 
5980 avox 191
                double x, y;
192
                bool error = false;
6220 fschmid 193
                error = FT_Set_Char_Size( face, 0, 10, 72, 72 );
194
                if (error)
195
                        m_glyphWidth[gl] = 1;
5980 avox 196
                FPointArray outlines = traceGlyph(face, gl, 10, &x, &y, &error);
197
                if (!error)
198
                {
199
                        m_glyphWidth[gl] = ww;
200
                        GRec.Outlines = outlines;
201
                        GRec.x = x;
202
                        GRec.y = y;
203
                        GRec.broken = false;
204
                }
205
                else {
206
                        m_glyphWidth[gl] = 1;
207
                }
208
        }
209
        m_glyphOutline[gl] = GRec;
210
        if (GRec.broken && status < ScFace::BROKENGLYPHS)
211
                status = ScFace::BROKENGLYPHS;
212
}
213
 
214
 
215
double FtFace::glyphKerning(uint gl, uint gl2, double size) const
216
{
217
        FT_Vector  delta;
218
        FT_Face    face = ftFace();
219
        double result = 0;
220
        /****
221
                Ok, this looks like a regression between Freetype 2.1.9 -> 2.1.10.
222
                Ignoring the value of FT_HAS_KERNING for now -- AV
223
                ****/
224
        if (true || FT_HAS_KERNING(face) )
225
        {
226
                FT_Error error = FT_Get_Kerning(face, gl, gl2, FT_KERNING_UNSCALED, &delta);
227
                if (error) {
228
                        qDebug(QString("Error %2 when accessing kerning pair for font %1").arg(scName).arg(error));
229
                }
230
                else {
231
                        result = delta.x / m_uniEM * size;
232
                }
233
        }
234
        else {
235
                qDebug(QString("Font %1 has no kerning pairs (according to Freetype)").arg(scName));
236
        }
237
        return result;
238
}
239
 
8972 avox 240
/*
5980 avox 241
GlyphMetrics FtFace::glyphBBox (uint gl, double sz) const
242
{
243
        FT_Face    face = ftFace();
244
        GlyphMetrics result;
245
        FT_Error error = FT_Load_Glyph( face, gl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
246
        if (!error) {
247
                double w  = (face->glyph->metrics.width + QABS((double)face->glyph->metrics.horiBearingX)) / m_uniEM * sz;
248
                result.width = QMAX(w, face->glyph->metrics.horiAdvance / m_uniEM * sz);
249
                double height = face->glyph->metrics.height / m_uniEM * sz;
250
                result.ascent = face->glyph->metrics.horiBearingY / m_uniEM * sz;
6371 avox 251
                result.descent = height - result.ascent;
5980 avox 252
        }
253
        else {
254
                result.width = result.ascent = sz;
255
                result.descent = 0;
256
        }
257
        return result;
258
}
8972 avox 259
*/
5980 avox 260
 
8972 avox 261
 
5980 avox 262
/// copied from Freetype's FT_Stream_ReadAt()
263
FT_Error ftIOFunc( FT_Stream stream, unsigned long pos, unsigned char* buffer, unsigned long count)
264
{
265
    FT_Error  error = FT_Err_Ok;
266
    FT_ULong  read_bytes;
267
 
268
    if ( pos >= stream->size )
269
    {
270
                qDebug( "ftIOFunc: invalid i/o; pos = 0x%lx, size = 0x%lx\n",
271
                                   pos, stream->size );
272
 
273
                return FT_Err_Invalid_Stream_Operation;
274
    }
275
 
276
    if ( stream->read )
277
                read_bytes = stream->read( stream, pos, buffer, count );
278
    else
279
    {
280
                read_bytes = stream->size - pos;
281
                if ( read_bytes > count )
282
                        read_bytes = count;
283
 
284
                memcpy( buffer, stream->base + pos, read_bytes );
285
    }
286
 
287
    stream->pos = pos + read_bytes;
288
 
289
    if ( read_bytes < count )
290
    {
291
                qDebug( "ftIOFunc: invalid read; expected %lu bytes, got %lu\n",
292
                                   count, read_bytes );
293
 
294
                error = FT_Err_Invalid_Stream_Operation;
295
    }
296
 
297
    return error;
298
}
299
 
300
 
6163 avox 301
bool FtFace::glyphNames(QMap<uint, std::pair<QChar, QString> >& GList) const
5980 avox 302
{
303
        return GlyNames(ftFace(), GList);
304
}
305
 
306
 
307
void FtFace::RawData(QByteArray & bb) const
308
{
309
        FT_Stream fts = ftFace()->stream;
310
        bb.resize(fts->size);
311
        bool error = ftIOFunc(fts, 0L, reinterpret_cast<FT_Byte *>(bb.data()), fts->size);
312
        if (error)
313
        {
314
                qDebug(QObject::tr("Font %1 is broken (read stream), no embedding").arg(fontFile));
315
                bb.resize(0);
316
                status = QMAX(status, ScFace::BROKENGLYPHS);
317
        }
6307 avox 318
/*     
319
//       if (showFontInformation)
5980 avox 320
         {
321
                 QFile f(fontFile);
6307 avox 322
                 qDebug(QObject::tr("RawData for Font %1(%2): size=%3 filesize=%4").arg(fontFile).arg(faceIndex).arg(bb.size()).arg(f.size()));
5980 avox 323
         }
6307 avox 324
*/       
5980 avox 325
}
326
 
327