/* For general Scribus (>=1.3.2) copyright and licensing information please refer to the COPYING file provided with the program. Following this notice may exist a copyright and/or license notice that predates the release of Scribus 1.3.2 for which a new license (GPL+exception) is in place. */ #include #include #include #include #include #include "fonts/scface_ttf.h" #include "fonts/scfontmetrics.h" #include "util.h" #include "scconfig.h" KernFeature::KernFeature ( FT_Face face ) :m_valid ( true ) { FontName = QString ( face->family_name ) + " " + QString ( face->style_name ) ; // qDebug() <<"KF"< 32 ) { GPOSTableRaw.resize ( length ); FT_Load_Sfnt_Table ( face, TTAG_GPOS, 0, reinterpret_cast ( GPOSTableRaw.data() ), &length ); makeCoverage(); } else m_valid = false; GPOSTableRaw.clear(); // coverages.clear(); } else m_valid = false; if ( !m_valid ) pairs.clear(); // qDebug() <<"\t"<& gl(cdt[classIndex]); if(gl.contains(glyph1)) { //qDebug()<<"Found G1"<& gl2(cdt2[classIndex2]); if(gl2.contains(glyph2)) { //qDebug()<<"Found G2"< FeatureKern_Offset; for ( quint16 FeatureRecord ( 0 ); FeatureRecord < FeatureCount; ++ FeatureRecord ) { int rawIdx ( FeatureList_Offset + 2 + ( 6 * FeatureRecord ) ); quint32 tag ( FT_MAKE_TAG ( GPOSTableRaw.at ( rawIdx ), GPOSTableRaw.at ( rawIdx + 1 ), GPOSTableRaw.at ( rawIdx + 2 ), GPOSTableRaw.at ( rawIdx + 3 ) ) ); if ( tag == TTAG_kern ) { FeatureKern_Offset << ( toUint16 ( rawIdx + 4 ) + FeatureList_Offset ); } } // Extract indices of lookups for feture kern QList LookupListIndex; foreach ( quint16 kern, FeatureKern_Offset ) { quint16 LookupCount ( toUint16 ( kern + 2 ) ); for ( int llio ( 0 ) ; llio < LookupCount; ++llio ) { quint16 Idx ( toUint16 ( kern + 4 + ( llio * 2 ) ) ); if ( !LookupListIndex.contains ( Idx ) ) { LookupListIndex < LookupTables; QList PairAdjustmentSubTables; for ( int i ( 0 ); i < LookupListIndex.count(); ++i ) { int rawIdx ( LookupList_Offset + 2 + ( LookupListIndex[i] * 2 ) ); quint16 Lookup ( toUint16 ( rawIdx ) + LookupList_Offset ); quint16 SubTableCount ( toUint16 ( Lookup + 4 ) ); for ( int stIdx ( 0 ); stIdx < SubTableCount; ++ stIdx ) { quint16 SubTable ( toUint16 ( Lookup + 6 + ( 2 * stIdx ) ) + Lookup ); // quint16 PosFormat ( toUint16 ( SubTable ) ); quint16 Coverage_Offset ( toUint16 ( SubTable + 2 ) + SubTable ); quint16 CoverageFormat ( toUint16 ( Coverage_Offset ) ); if ( 1 == CoverageFormat ) // glyph indices based { quint16 GlyphCount ( toUint16 ( Coverage_Offset + 2 ) ); quint16 GlyphID ( Coverage_Offset + 4 ); if (GlyphCount == 0) continue; for ( unsigned int gl ( 0 ); gl < GlyphCount; ++gl ) { coverages[SubTable] << toUint16 ( GlyphID + ( gl * 2 ) ); } } else if ( 2 == CoverageFormat ) // Coverage Format2 => ranges based { quint16 RangeCount ( toUint16 ( Coverage_Offset + 2 ) ); if (RangeCount == 0) continue; // int gl_base ( 0 ); for ( int r ( 0 ); r < RangeCount; ++r ) { quint16 rBase ( Coverage_Offset + 4 + ( r * 6 ) ); quint16 Start ( toUint16 ( rBase ) ); quint16 End ( toUint16 ( rBase + 2 ) ); // quint16 StartCoverageIndex ( toUint16 ( rBase + 4 ) ); // #9842 : for some font such as Gabriola Regular // the range maybe be specified in reverse order if (Start <= End) { for ( unsigned int gl ( Start ); gl <= End; ++gl ) coverages[SubTable] << gl; } else { for ( int gl ( Start ); gl >= (int) End; --gl ) coverages[SubTable] << gl; } } } else { // qDebug() <<"Unknow Coverage Format:"< excludeList; quint16 ClassFormat ( toUint16 ( classDefOffset ) ); if ( ClassFormat == 1 ) { quint16 StartGlyph ( toUint16 ( classDefOffset +2 ) ); quint16 GlyphCount ( toUint16 ( classDefOffset +4 ) ); quint16 ClassValueArray ( classDefOffset + 6 ); for ( quint16 CV ( 0 );CV < GlyphCount; ++CV ) { excludeList<= (int) End; --gl ) { excludeList<< (quint16) gl; ret[Class] << gl; } } } } else qDebug() <<"Unknown Class Table type"; // if possible (all glyphs are "classed"), avoid to pass through this slow piece of code. if(excludeList.count() != coverages[coverageId].count()) { foreach(const quint16& gidx, coverages[coverageId]) { if(!excludeList.contains(gidx)) ret[0] << gidx; } } if(leftGlyph) classGlyphFirst[coverageId][classDefOffset] = ret; else classGlyphSecond[coverageId][classDefOffset] = ret; return ret; } quint16 KernFeature::toUint16 ( quint16 index ) { if ( ( index + 2 ) > GPOSTableRaw.count() ) { // qDebug() << "HORROR!" << index << GPOSTableRaw.count() << FontName ; // Rather no kerning at all than random kerning // m_valid = false; return 0; } // FIXME I just do not know how it has to be done *properly* quint16 c1 ( GPOSTableRaw.at ( index ) ); quint16 c2 ( GPOSTableRaw.at ( index + 1 ) ); c1 &= 0xFF; c2 &= 0xFF; quint16 ret ( ( c1 << 8 ) | c2 ); return ret; } qint16 KernFeature::toInt16 ( quint16 index ) { if ( ( index + 2 ) > GPOSTableRaw.count() ) { return 0; } // FIXME I just do not know how it has to be done *properly* quint16 c1 ( GPOSTableRaw.at ( index ) ); quint16 c2 ( GPOSTableRaw.at ( index + 1 ) ); c1 &= 0xFF; c2 &= 0xFF; qint16 ret ( ( c1 << 8 ) | c2 ); return ret; } namespace { uint word(QByteArray const & bb, uint pos) { const unsigned char * pp = reinterpret_cast(bb.data()) + pos; return pp[0] << 24 | pp[1] << 16 | pp[2] << 8 | pp[3]; } uint word16(QByteArray const & bb, uint pos) { const unsigned char * pp = reinterpret_cast(bb.data()) + pos; return pp[0] << 8 | pp[1]; } QString tag(QByteArray const & bb, uint pos) { char buf[5] = "1234"; buf[0] = bb.data()[pos]; buf[1] = bb.data()[pos+1]; buf[2] = bb.data()[pos+2]; buf[3] = bb.data()[pos+3]; return buf; } bool copy(QByteArray & dst, uint to, QByteArray & src, uint from, uint len) { if (!dst.data()) return false; if (!src.data()) return false; if (to + len > static_cast(dst.size())) return false; if (from + len > static_cast(src.size())) return false; memcpy(dst.data() + to, src.data() + from, len); return true; } } //namespace ScFace_ttf::ScFace_ttf ( QString fam, QString sty, QString alt, QString scname, QString psname, QString path, int face ) : FtFace ( fam, sty, alt, scname, psname, path, face ) { formatCode = ScFace::SFNT; kernFeature = 0; } ScFace_ttf::~ ScFace_ttf() { if ( kernFeature ) delete kernFeature; } void ScFace_ttf::load() const { if ( !kernFeature ) kernFeature = new KernFeature ( ftFace() ); FtFace::load(); } void ScFace_ttf::unload() const { if ( kernFeature ) delete kernFeature; kernFeature = 0; FtFace::unload(); } qreal ScFace_ttf::glyphKerning ( uint gl1, uint gl2, qreal sz ) const { if ( kernFeature->isValid() ) return kernFeature->getPairValue ( gl1,gl2 ) / m_uniEM * sz; return FtFace::glyphKerning ( gl1, gl2, sz ); } void ScFace_ttf::RawData(QByteArray & bb) const { if (formatCode == ScFace::TTCF) { QByteArray coll; FtFace::RawData(coll); // access table for faceIndex if (faceIndex >= static_cast(word(coll, 8))) { bb.resize(0); return; } static const uint OFFSET_TABLE_LEN = 12; static const uint TDIR_ENTRY_LEN = 16; uint faceOffset = word(coll, 12 + 4 * faceIndex); uint nTables = word16(coll, faceOffset + 4); sDebug(QObject::tr("extracting face %1 from font %2 (offset=%3, nTables=%4)").arg(faceIndex).arg(fontFile).arg(faceOffset).arg(nTables)); uint headerLength = OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * nTables; uint tableLengths = 0; // sum table lengths incl padding for (uint i=0; i < nTables; ++i) { tableLengths += word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 12); tableLengths = (tableLengths+3) & ~3; } bb.resize(headerLength + tableLengths); if (! bb.data()) return; // write header sDebug(QObject::tr("memcpy header: %1 %2 %3").arg(0).arg(faceOffset).arg(headerLength)); if (!copy(bb, 0, coll, faceOffset, headerLength)) return; uint pos = headerLength; for (uint i=0; i < nTables; ++i) { uint tableSize = word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 12); uint tableStart = word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8); sDebug(QObject::tr("table '%1'").arg(tag(coll, tableStart))); sDebug(QObject::tr("memcpy table: %1 %2 %3").arg(pos).arg(tableStart).arg(tableSize)); if (!copy(bb, pos, coll, tableStart, tableSize)) break; // write new offset to table entry sDebug(QObject::tr("memcpy offset: %1 %2 %3").arg(OFFSET_TABLE_LEN + TDIR_ENTRY_LEN*i + 8).arg(pos).arg(4)); memcpy(bb.data() + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8, &pos, 4); pos += tableSize; // pad while ((pos & 3) != 0) bb.data()[pos++] = '\0'; } } else if (formatCode == ScFace::TYPE42) { FtFace::RawData(bb); } else { FtFace::RawData(bb); } } bool ScFace_ttf::EmbedFont(QString &str) const { if (formatCode == ScFace::TYPE42) { //easy: QByteArray bb; FtFace::RawData(bb); str += bb; return true; } QString tmp4; QString tmp2 = ""; QString tmp3 = ""; int counter = 0; char *buf[50]; FT_ULong charcode; FT_UInt gindex; FT_Face face = ftFace(); if (!face) { return false; } const FT_Stream fts = face->stream; if (ftIOFunc(fts, 0L, NULL, 0)) { return(false); } str+="%!PS-TrueTypeFont\n"; str+="11 dict begin\n"; str+="/FontName /" + psName + " def\n"; str+="/Encoding /ISOLatin1Encoding where {pop ISOLatin1Encoding} {StandardEncoding} ifelse def\n"; str+="/PaintType 0 def\n/FontMatrix [1 0 0 1 0 0] def\n"; str+="/FontBBox ["+m_pdfFontBBox+"] def\n"; str+="/FontType 42 def\n"; str+="/FontInfo 8 dict dup begin\n"; str+="/FamilyName (" + psName + ") def\n"; str+="end readonly def\n"; unsigned char *tmp = new unsigned char[65535]; int length; char linebuf[80]; str += "/sfnts ["; int poso=0; do { int posi=0; length= fts->size - fts->pos; if (length > 65534) { length = 65534; } if (!ftIOFunc(fts, 0L, tmp, length)) { str+="\n<\n"; for (int j = 0; j < length; j++) { unsigned char u=tmp[posi]; linebuf[poso]=((u >> 4) & 15) + '0'; if(u>0x9f) linebuf[poso]+='a'-':'; ++poso; u&=15; linebuf[poso]=u + '0'; if(u>0x9) linebuf[poso]+='a'-':'; ++posi; ++poso; if (poso > 70) { linebuf[poso++]='\n'; linebuf[poso++]=0; str += linebuf; poso = 0; } } linebuf[poso++]=0; str += linebuf; poso = 0; str += "00\n>"; } else { sDebug(QObject::tr("Font %1 is broken (read stream), no embedding").arg(fontFile)); str += "\n] def\n"; status = qMax(status,ScFace::BROKENGLYPHS); return false; } } while (length==65534); str += "\n] def\n"; delete[] tmp; gindex = 0; charcode = FT_Get_First_Char(face, &gindex ); while (gindex != 0) { FT_Get_Glyph_Name(face, gindex, buf, 50); tmp2 += "/"+QString(reinterpret_cast(buf))+" "+tmp3.setNum(gindex)+" def\n"; charcode = FT_Get_Next_Char(face, charcode, &gindex ); counter++; } tmp4.setNum(counter); str += "/CharStrings " + tmp4 + " dict dup begin\n"+tmp2; str += "end readonly def\n"; str += "FontName currentdict end definefont pop\n"; return(true); }