/* vface.cpp */ #include "vface.h" VFace::VFace(QString fam, QString sty, QString scname, QString path) : ScFaceData() { family = fam; style = sty; scName = scname; variant =""; psName = scname.simplifyWhiteSpace().lower().replace(" ","_") ; fontFile = path; faceIndex = 0; } VFace::~VFace() { unload(); } void VFace::unload() const { ScFaceData::unload(); v_font.clear(); v_path.clear(); v_name.clear(); v_char.clear(); v_kern.clear(); v.clear(); } void VFace::load() const { ScFaceData::load(); loadRessource(); loadGlyphs(); } void VFace::loadRessource() const { QFile ufr(QFile::encodeName( fontFile )); if(!ufr.open( IO_ReadOnly ) ) { qDebug(QString("VFace::loadRessource() : Can't open ufrFile[%1]").arg(QFile::encodeName( fontFile ))); return; } QDomDocument ufrdom( "ufr" ); if ( !ufrdom.setContent( &ufr ) ) { ufr.close(); qDebug(QString("VFace::loadRessource() : Can't build DOM for ufrFile[%1]").arg(QFile::encodeName( fontFile ))); return; } ufr.close(); // Load Fonts QDomNodeList fontlist(ufrdom.elementsByTagName ( "font")); QString fn, fp;//fontname fontpath if(!v_font.isEmpty()) v_font.clear(); if(fontlist.count()) { for(uint i=0; i < fontlist.count(); ++i) { fn = fontlist.item(i).attributes().namedItem("name").nodeValue(); fp = fontlist.item(i).attributes().namedItem("path").nodeValue(); v_path.insert(fn,fp); } } // Load codepoints and map them whith font names QString cd;//codepoint QDomNodeList gnod;//glyphnode list bool ok;// required by toUInt if(fontlist.count()) { for(uint i=0; i < fontlist.count(); ++i) { fn = fontlist.item(i).attributes().namedItem("name").nodeValue(); gnod = fontlist.item(i).childNodes(); if(gnod.count()) { for(uint ii=0; ii < gnod.count() ; ++ii) { cd = gnod.item(ii).attributes().namedItem("codepoint").nodeValue(); v_font.insert( QChar(cd.toUInt(&ok,16)), fn); } } } } // Load kerning pairs QDomNodeList kernpair(ufrdom.elementsByTagName ( "kernpair")); QString left; QString right; QString value; if(kernpair.count()) { for(uint i=0;i(left.toUInt(),right.toUInt()), value.toDouble()); qDebug(QString("pair[%1,%2] inserted with value '%3'").arg(left).arg(right).arg(value)); } } } void VFace::loadGlyphs() const { // For each codepoint in v_font, we want to load the corresponding glyph index // and once we have glyph index, load the glyph in v // first we load as many fontfiles as there are font elements FT_Library ftlib; if (FT_Init_FreeType( &ftlib )) { qDebug(QString("VFace::loadGlyphs() : Freetype2 library not available")); return; } QDict face; QDictIterator f_it( face ); FT_Face tmpface; face.setAutoDelete(true); for(QMap ::iterator it=v_path.begin(); it != v_path.end(); ++it) face.insert(it.key(), new FT_FaceRec); Glyph *grec; // a glyph slot uint gindex = 0;// a glyph index double uniEM; for(;f_it.current(); ++f_it) { tmpface = f_it.current(); if(FT_New_Face(ftlib, QFile::encodeName( v_path[f_it.currentKey()]), 0, &tmpface )) { qDebug(QString("VFace::loadGlyphs() : Unable to load face [%1]").arg(f_it.currentKey())); } else { uniEM = static_cast(f_it.current()->units_per_EM); v_ascent = v_ascent > (f_it.current()->ascender / uniEM) ? v_ascent : (f_it.current()->ascender / uniEM); v_descent = v_descent > (f_it.current()->descender / uniEM) ? v_descent : (f_it.current()->descender / uniEM); v_height = v_height > (f_it.current()->height / uniEM) ? v_height : (f_it.current()->height / uniEM); v_xHeight = v_height; v_capHeight = v_height; v_maxAdvanceWidth = v_maxAdvanceWidth > (f_it.current()->max_advance_width / uniEM) ? v_maxAdvanceWidth : (f_it.current()->max_advance_width / uniEM); v_strikeoutPos = v_ascent / 3; v_underlinePos = v_underlinePos > (f_it.current()->underline_position / uniEM) ? v_underlinePos : (f_it.current()->underline_position / uniEM); v_strokeWidth = v_strokeWidth > (f_it.current()->underline_thickness / uniEM) ? v_strokeWidth : (f_it.current()->underline_thickness / uniEM) ; // We have valid face. begin to load codepoints from v_font. Because it's just test, we don't check it too much ;-) for(QMap ::iterator it=v_font.begin(); it != v_font.end(); ++it) { if(it.data() == f_it.currentKey()) { gindex = FT_Get_Char_Index(f_it.current(),it.key()); v_char.insert(it.key(), gindex ); if(FT_Load_Glyph(f_it.current(), gindex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP )) { qDebug(QString("VFace::loadGlyphs() : Unable to load glyph [%1] of face [%2]").arg(gindex).arg(f_it.currentKey())); } else { grec = new(Glyph); double x, y; bool error = false; FPointArray outlines = traceGlyph( f_it.current(), gindex, 10, &x, &y, &error); if(!error) { grec->outline = outlines.copy(); grec->x = x; grec->y = y; grec->width = f_it.current()->glyph->metrics.horiAdvance / uniEM; grec->ascent = f_it.current()->glyph->metrics.horiBearingY / uniEM; grec->descent = (f_it.current()->glyph->metrics.height / uniEM) - grec->ascent ; grec->broken = false; } v.insert(gindex, *grec); updateGlyphNames( f_it.current(), gindex, it.key()); delete(grec); } } } } FT_Done_Face(tmpface); } FT_Done_FreeType(ftlib); } QString uniName(FT_ULong charcode) { static const char HEX[] = "0123456789ABCDEF"; QString result; if (charcode < 0x10000) { result = QString("uni") + HEX[charcode>>12 & 0xF] + HEX[charcode>> 8 & 0xF] + HEX[charcode>> 4 & 0xF] + HEX[charcode & 0xF]; } else { result = QString("u"); for (int i= 28; i >= 0; i-=4) { if (charcode & (0xF << i)) result += HEX[charcode >> i & 0xF]; } } return result; } void VFace::updateGlyphNames(FT_Face face, uint gl, QChar ch) const { char buf[50]; FT_ULong charcode = ch.unicode(); FT_UInt gindex = gl; setBestEncoding(face); const bool hasPSNames = FT_HAS_GLYPH_NAMES(face); bool notfound = true; if (hasPSNames) notfound = FT_Get_Glyph_Name(face, gindex, &buf, 50); // just in case FT gives empty string or ".notdef" // no valid glyphname except ".notdef" starts with '.' if (notfound || buf[0] == '\0' || buf[0] == '.') { v_name.insert(gindex, std::make_pair( QChar(static_cast(charcode)), uniName(charcode) ) ); v_char.insert(QChar(static_cast(charcode)), gindex); } else { v_name.insert(gindex, std::make_pair( QChar(static_cast(charcode)), QString(reinterpret_cast(buf)) ) ); v_char.insert(QChar(static_cast(charcode)), gindex); } } GlyphMetrics VFace::glyphBBox (uint gl, double sz) const { GlyphMetrics ret; ret.width = v[gl].width * sz; ret.ascent = v[gl].ascent * sz; ret.descent = v[gl].descent *sz; return ret; } double VFace::glyphWidth(uint gl, double sz) const { if (gl >= ScFace::CONTROL_GLYPHS) return 0.0; else return v[gl].width * sz; } FPointArray VFace::glyphOutline(uint gl, double sz) const { if (gl >= ScFace::CONTROL_GLYPHS) return FPointArray(); FPointArray res = v[gl].outline.copy(); if (sz != 1.0) res.scale(sz, sz); return res; } FPoint VFace::glyphOrigin(uint gl, double sz) const { if (gl >= ScFace::CONTROL_GLYPHS) return FPoint(0,0); return FPoint(v[gl].x, v[gl].y) * sz; } double VFace::glyphKerning (uint gl1, uint gl2, double sz) const { std::pair kp(gl1,gl2); if(v_kern.contains(kp)) return v_kern[kp]; return 0.0; // TODO if gl1 and gl2 come from the same font file, load their kerning pair if exists. } /* old stuff, I put it here to keep the code. // Experiences with cache and other cool stuff void ScFace::ScFaceData::loadUserFontRessource() const { QString ufrPath(QDir::homeDirPath () + "/.scribus/fonts/"+psName+".ufr"); QFile ufr(QFile::encodeName( ufrPath )); if(!ufr.exists()) { qDebug(QString("load -> ufrFile[%1] doesn't exist").arg(QFile::encodeName( ufrPath ))); return; } if(!ufr.open( IO_ReadOnly ) ) { qDebug(QString("load -> Can't open ufrFile[%1]").arg(QFile::encodeName( ufrPath ))); return; } QDomDocument ufrdom( "ufr" ); if ( !ufrdom.setContent( &ufr ) ) { ufr.close(); return; } ufr.close(); // Load kerning pair QDomNodeList kernpair(ufrdom.elementsByTagName ( "kernpair")); QString left; QString right; QString value; if(!kerningCache.isEmpty()) kerningCache.clear(); if(kernpair.count()) { for(uint i=0;i(nameGlyph(left),nameGlyph(right)), value.toDouble()); qDebug(QString("load -> pair[%1,%2] inserted with value '%3'").arg(left).arg(right).arg(value)); } } // Load metrics QDomNodeList met(ufrdom.elementsByTagName ( "metrics")); QString g,w,a,d; if(!metricsCache.isEmpty()) metricsCache.clear(); if(met.count()) { for(uint i = 0; i < met.count(); ++i) { g = met.item(i).attributes().namedItem("glyph").nodeValue(); w = met.item(i).attributes().namedItem("width").nodeValue(); a = met.item(i).attributes().namedItem("ascent").nodeValue(); d = met.item(i).attributes().namedItem("descent").nodeValue(); updateMetricsCache(nameGlyph(g),w.toDouble(),a.toDouble(),d.toDouble()); } } // Load extra font files QDomNodeList font(ufrdom.elementsByTagName ( "extrafont")); QString name,path; if(!extraFontFile.isEmpty()) extraFontFile.clear(); if(font.count()) { for(uint i = 0; i pairkern(gl,gl2); kerningCache.insert(pairkern,adj); } void ScFace::ScFaceData::updateMetricsCache(uint gl, double w, double a, double d) const { GlyphMetrics gm; gm.width = w; gm.ascent = a; gm.descent = d; metricsCache.insert(gl, gm); } void ScFace::ScFaceData::saveKerningCache() const { if(kerningCache.isEmpty()) { qDebug(QString("kerningCache is Empty")); return; } QString ufrPath(QDir::homeDirPath () + "/.scribus/fonts/"+psName+".ufr"); QFile ufr(QFile::encodeName( ufrPath)); if(!psName.isEmpty()) { if(!ufr.open( IO_WriteOnly ) ) { qDebug(QString("save -> Can't open ufrFile[%1]").arg(QFile::encodeName( ufrPath ))); return; } qDebug(QString("save-> ufrFile[%1] is opened").arg(QFile::encodeName( ufrPath ))); } else { return; } QDomDocument kerningCache_dom( "MyML" ); QDomElement root = kerningCache_dom.createElement( "ufr" ); kerningCache_dom.appendChild( root ); QDomElement kern = kerningCache_dom.createElement( "kern" ); root.appendChild( kern ); QDomElement kernpair; for(QMap,double>::iterator it=kerningCache.begin();it != kerningCache.end(); ++it) { kernpair = kerningCache_dom.createElement("kernpair"); kernpair.setAttribute("left",glyphName(it.key().first)); kernpair.setAttribute("right",glyphName(it.key().second)); kernpair.setAttribute("value",it.data()); kern.appendChild(kernpair); } QDomElement gl = kerningCache_dom.createElement( "glyphs" ); root.appendChild(gl); QDomElement met; for(QMap::iterator it=metricsCache.begin(); it != metricsCache.end(); ++it) { met = kerningCache_dom.createElement("metrics"); met.setAttribute("glyph",glyphName(it.key())); met.setAttribute("width",it.data().width); met.setAttribute("ascent",it.data().ascent); met.setAttribute("descent",it.data().descent); gl.appendChild(met); } //Save to file QTextStream ufrstream( &ufr ); kerningCache_dom.save(ufrstream , 1); ufr.flush(); ufr.close(); } QString ScFace::ScFaceData::glyphName(uint g) const { if(glyphList.isEmpty()) glyphNames(glyphList); return glyphList[g].second; } uint ScFace::ScFaceData::nameGlyph(QString n) const { if(nameList.isEmpty()) { glyphNames(glyphList); for(QMap >::iterator it=glyphList.begin(); it != glyphList.end(); ++it) { nameList.insert(it.data().second, it.key()); } } return nameList[n]; } */