Back to index

scribus-ng  1.3.4.dfsg+svn20071115
ftface.cpp
Go to the documentation of this file.
00001 
00002 #include "fonts/ftface.h"
00003 
00004 #include FT_OUTLINE_H
00005 #include FT_GLYPH_H
00006 
00007 #include <qobject.h>
00008 #include <qfile.h>
00009 
00010 #include "scfonts.h"
00011 #include "util.h"
00012 #include "fonts/scfontmetrics.h"
00013 
00014 // static:
00015 FT_Library FtFace::library = NULL;
00016 
00017 /*****
00018    ScFace lifecycle:  unchecked -> loaded -> glyphs checked
00019                                |         \-> broken glyphs
00020                                                     \-> broken
00021    usable() == ! broken
00022    embeddable() == glyphs_checked
00023    
00024    canRender(unicode) -> CharMap cache? -> loadChar/Glyph -> !broken
00025    Glyphs:  width    status
00026             -1000    unkown
00027             -2000    broken
00028             >= 0     ok, outline valid
00029    CharMap:  unicode -> glyph index
00030              uint[256][256]
00031    unicode ignores: < 32, ...
00032    unicode emulate: spaces, hyphen, ligatures?, diacritics?
00033  *****/
00034 
00035 FtFace::FtFace(QString fam, QString sty, QString vari, QString scname, 
00036                         QString psname, QString path, int face) 
00037 : ScFaceData(), m_face(NULL)
00038 {
00039        family = fam;
00040        style = sty;
00041        variant = vari;
00042        scName = scname;
00043        psName = psname;
00044        fontFile = path;
00045        faceIndex = face;
00046        if (!library) {
00047               if (FT_Init_FreeType( &library ))
00048                      sDebug(QObject::tr("Freetype2 library not available"));
00049        }
00050 }
00051 
00052 
00053 FtFace::~FtFace() {
00054        unload();
00055 }
00056 
00057 
00058 FT_Face FtFace::ftFace() const {
00059        if (!m_face) {
00060               if (FT_New_Face( library, QFile::encodeName(fontFile), faceIndex, & m_face )) {
00061                      status = ScFace::BROKEN;
00062                      m_face = NULL;
00063                      sDebug(QObject::tr("Font %1(%2) is broken").arg(fontFile).arg(faceIndex));
00064               }
00065               else {
00066                      load();
00067               }
00068        }
00069        return m_face;
00070 }
00071 
00072 void FtFace::load() const
00073 {
00074        ScFaceData::load();
00075 
00076        if (!m_face) {
00077               if (FT_New_Face( library, QFile::encodeName(fontFile), faceIndex, & m_face )) {
00078                      status = ScFace::BROKEN;
00079                      m_face = NULL;
00080                      sDebug(QObject::tr("Font %1(%2) is broken").arg(fontFile).arg(faceIndex));
00081                      return;
00082               }
00083        }
00084        
00085        const_cast<FtFace*>(this)->isStroked = false;
00086        m_encoding = 0;
00087        
00088        m_uniEM = static_cast<double>(m_face->units_per_EM);
00089 
00090        m_descent = m_face->descender / m_uniEM;
00091        m_ascent = m_face->ascender / m_uniEM;
00092        m_height = m_face->height / m_uniEM;
00093 
00094 /* Temporary fix for the broken "Dutch Initials" font */
00095        if ((m_ascent == 0) && (m_descent == 0))
00096        {
00097               m_ascent = (m_face->bbox.yMax - m_face->bbox.yMin) / m_uniEM;
00098               m_height = m_ascent;
00099        }
00100 
00101        m_xHeight = m_height;
00102        m_capHeight = m_height;
00103        m_maxAdvanceWidth = m_face->max_advance_width / m_uniEM;
00104        m_underlinePos = m_face->underline_position / m_uniEM;
00105        m_strikeoutPos = m_ascent / 3;
00106        m_strokeWidth = m_face->underline_thickness / m_uniEM;
00107        const_cast<FtFace*>(this)->isFixedPitch = m_face->face_flags & 4;
00108        Ascent = QString::number(m_face->ascender);
00109        CapHeight = QString::number(m_face->height);
00110        Descender = QString::number(m_face->descender);
00111        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);
00112        ItalicAngle = "0";
00113 
00114 //FIXME:      FT_Set_Charmap(m_face, m_face->charmaps[m_encoding]);
00115        setBestEncoding(m_face);
00116        
00117        FT_UInt gindex = 0;
00118        FT_ULong charcode = FT_Get_First_Char( m_face, &gindex );
00119        int goodGlyph = 0;
00120        int invalidGlyph = 0;
00121        bool error;
00122        
00123        while ( gindex != 0 )
00124        {
00125               error = FT_Load_Glyph( m_face, gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
00126               if (error)
00127               {
00128                      ++invalidGlyph;
00129                      sDebug(QObject::tr("Font %1 has broken glyph %2 (charcode %3)").arg(fontFile).arg(gindex).arg(charcode));
00130                      charcode = FT_Get_Next_Char( m_face, charcode, &gindex );
00131                      continue;
00132               }
00133               
00134               if (gindex > maxGlyph)
00135                      const_cast<FtFace*>(this)->maxGlyph = gindex;
00136               
00137               ++goodGlyph;
00138               if (m_face->glyph->format == FT_GLYPH_FORMAT_PLOTTER)
00139                      const_cast<FtFace*>(this)->isStroked = true;
00140               charcode = FT_Get_Next_Char( m_face, charcode, &gindex );
00141        }
00142        if (invalidGlyph > 0) {
00143               status = ScFace::BROKENGLYPHS;
00144        }
00145 }
00146 
00147 
00148 void FtFace::unload() const
00149 {
00150        if (m_face) {
00151               FT_Done_Face( m_face );
00152               m_face = NULL;
00153        }
00154        // clear caches
00155        ScFaceData::unload();
00156 }
00157 
00158 
00159 uint FtFace::char2CMap(QChar ch) const
00160 {
00161        // FIXME use cMap cache
00162        FT_Face face = ftFace();
00163        uint gl = FT_Get_Char_Index(face, ch.unicode());
00164        return gl;
00165 }
00166 
00167 
00168 void FtFace::loadGlyph(uint gl) const
00169 {
00170        if (m_glyphWidth.contains(gl))
00171               return;
00172        
00173        ScFace::GlyphData GRec;
00174        FT_Face face = ftFace();
00175        if (FT_Load_Glyph( face, gl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP ))
00176        {
00177               sDebug(QObject::tr("Font %1 has broken glyph %2").arg(fontFile).arg(gl));
00178               m_glyphWidth[gl] = 1;
00179        }
00180        else {
00181               double ww = double(face->glyph->metrics.horiAdvance) / m_uniEM;
00182               double w  = (face->glyph->metrics.width + QABS(double(face->glyph->metrics.horiBearingX))) / m_uniEM;
00183               GRec.bbox_width = QMAX(w, ww);
00184               double height = double(face->glyph->metrics.height) / m_uniEM;
00185               GRec.bbox_ascent = double(face->glyph->metrics.horiBearingY) / m_uniEM;
00186               GRec.bbox_descent = height - GRec.bbox_ascent;
00187 //            qDebug(QString("glyphmetrics %1: EM %2 bearing (%3,%4) size (%5,%6) advance %7 bbox (%8,%9)")
00188 //                      .arg(gl).arg(m_uniEM).arg(face->glyph->metrics.horiBearingX).arg(face->glyph->metrics.horiBearingY)
00189 //                      .arg(face->glyph->metrics.width).arg(face->glyph->metrics.height).arg(face->glyph->metrics.horiAdvance)
00190 //                      .arg(w).arg(height));
00191 
00192               double x, y;
00193               bool error = false;
00194               error = FT_Set_Char_Size( face, 0, 10, 72, 72 );
00195               if (error)
00196                      m_glyphWidth[gl] = 1;
00197               FPointArray outlines = traceGlyph(face, gl, 10, &x, &y, &error);
00198               if (!error)
00199               {
00200                      m_glyphWidth[gl] = ww;
00201                      GRec.Outlines = outlines;
00202                      GRec.x = x;
00203                      GRec.y = y;
00204                      GRec.broken = false;
00205               }
00206               else {
00207                      m_glyphWidth[gl] = 1;
00208               }
00209        }
00210        m_glyphOutline[gl] = GRec;
00211        if (GRec.broken && status < ScFace::BROKENGLYPHS)
00212               status = ScFace::BROKENGLYPHS;
00213 }
00214 
00215 
00216 double FtFace::glyphKerning(uint gl, uint gl2, double size) const
00217 {
00218        FT_Vector  delta;
00219        FT_Face    face = ftFace();
00220        double result = 0;
00221        /****
00222               Ok, this looks like a regression between Freetype 2.1.9 -> 2.1.10.
00223               Ignoring the value of FT_HAS_KERNING for now -- AV
00224               ****/
00225        if (true || FT_HAS_KERNING(face) )
00226        {
00227               FT_Error error = FT_Get_Kerning(face, gl, gl2, FT_KERNING_UNSCALED, &delta);
00228               if (error) {
00229                      sDebug(QString("Error %2 when accessing kerning pair for font %1").arg(scName).arg(error));
00230               }
00231               else {
00232                      result = delta.x / m_uniEM * size;
00233               }
00234        }
00235        else {
00236               sDebug(QString("Font %1 has no kerning pairs (according to Freetype)").arg(scName));
00237        }
00238        return result;
00239 }
00240 
00241 /*
00242 GlyphMetrics FtFace::glyphBBox (uint gl, double sz) const
00243 {
00244        FT_Face    face = ftFace();
00245        GlyphMetrics result;
00246        FT_Error error = FT_Load_Glyph( face, gl, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP );
00247        if (!error) {
00248               double w  = (face->glyph->metrics.width + QABS((double)face->glyph->metrics.horiBearingX)) / m_uniEM * sz;
00249               result.width = QMAX(w, face->glyph->metrics.horiAdvance / m_uniEM * sz);
00250               double height = face->glyph->metrics.height / m_uniEM * sz;
00251               result.ascent = face->glyph->metrics.horiBearingY / m_uniEM * sz;
00252               result.descent = height - result.ascent;
00253        }
00254        else {
00255               result.width = result.ascent = sz;
00256               result.descent = 0;
00257        }
00258        return result;
00259 }
00260 */
00261 
00262 
00264 FT_Error ftIOFunc( FT_Stream stream, unsigned long pos, unsigned char* buffer, unsigned long count)
00265 {
00266     FT_Error  error = FT_Err_Ok;
00267     FT_ULong  read_bytes;
00268        
00269     if ( pos >= stream->size )
00270     {
00271               qDebug( "ftIOFunc: invalid i/o; pos = 0x%lx, size = 0x%lx\n",
00272                                pos, stream->size );
00273               
00274               return FT_Err_Invalid_Stream_Operation;
00275     }
00276        
00277     if ( stream->read )
00278               read_bytes = stream->read( stream, pos, buffer, count );
00279     else
00280     {
00281               read_bytes = stream->size - pos;
00282               if ( read_bytes > count )
00283                      read_bytes = count;
00284               
00285               memcpy( buffer, stream->base + pos, read_bytes );
00286     }
00287        
00288     stream->pos = pos + read_bytes;
00289        
00290     if ( read_bytes < count )
00291     {
00292               qDebug( "ftIOFunc: invalid read; expected %lu bytes, got %lu\n",
00293                                count, read_bytes );
00294               
00295               error = FT_Err_Invalid_Stream_Operation;
00296     }
00297        
00298     return error;
00299 }
00300 
00301 
00302 bool FtFace::glyphNames(QMap<uint, std::pair<QChar, QString> >& GList) const
00303 {
00304        return GlyNames(ftFace(), GList);
00305 }
00306 
00307 
00308 void FtFace::RawData(QByteArray & bb) const
00309 {
00310        FT_Stream fts = ftFace()->stream;
00311        bb.resize(fts->size);
00312        bool error = ftIOFunc(fts, 0L, reinterpret_cast<FT_Byte *>(bb.data()), fts->size);
00313        if (error) 
00314        {
00315               sDebug(QObject::tr("Font %1 is broken (read stream), no embedding").arg(fontFile));
00316               bb.resize(0);
00317               status = QMAX(status, ScFace::BROKENGLYPHS);
00318        }
00319 /*     
00320 //      if (showFontInformation)
00321         {
00322                QFile f(fontFile);
00323                qDebug(QObject::tr("RawData for Font %1(%2): size=%3 filesize=%4").arg(fontFile).arg(faceIndex).arg(bb.size()).arg(f.size()));
00324         }
00325 */      
00326 }
00327 
00328