1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 #include <QDebug>
8 #include <QDir>
9 #include <QFileInfo>
10 
11 #include "ftface.h"
12 #include "scface_ps.h"
13 
14 
15 
ScFace_PostScript(const QString & fam,const QString & sty,const QString & alt,const QString & scname,const QString & psname,const QString & path,int face,const QStringList & features)16 ScFace_PostScript::ScFace_PostScript(const QString& fam, const QString& sty, const QString& alt, const QString& scname, const QString& psname, const QString& path, int face, const QStringList& features) :
17 	FtFace(fam,sty,alt,scname,psname,path,face,features)
18 {
19 	isFixedPitch = false;
20 	typeCode = ScFace::TYPE1;
21 }
22 
findFontMetrics(const QString & fontPath) const23 QStringList ScFace_PostScript::findFontMetrics(const QString& fontPath)  const
24 {
25 	QStringList metricsFiles;
26 	QFileInfo fi(fontPath);
27 
28 	QString fontDir  = fi.absolutePath();
29 	QString fontFile = fi.completeBaseName();
30 
31 	metricsFiles += findFontMetrics(fontDir, fontFile);
32 
33 	//if no metrics found look in afm and pfm subdirs
34 	if ( metricsFiles.empty() )
35 	{
36 		QDir dir;
37 		if (dir.exists(fontDir + "/AFMs"))
38 			metricsFiles += findFontMetrics(fontDir + "/AFMs", fontFile);
39 		if (dir.exists(fontDir + "/afm") && metricsFiles.empty())
40 			metricsFiles += findFontMetrics(fontDir + "/afm", fontFile);
41 		if (dir.exists(fontDir + "/Pfm") && metricsFiles.empty())
42 			metricsFiles += findFontMetrics(fontDir + "/Pfm", fontFile);
43 		if (dir.exists(fontDir + "/pfm") && metricsFiles.empty())
44 			metricsFiles += findFontMetrics(fontDir + "/pfm", fontFile);
45 	}
46 	return metricsFiles;
47 }
48 
findFontMetrics(const QString & baseDir,const QString & baseName) const49 QStringList ScFace_PostScript::findFontMetrics(const QString& baseDir, const QString& baseName) const
50 {
51 	QStringList metricsFiles;
52 	QString     afnm = baseDir + "/" + baseName + ".";
53 	// Look for afm files
54 	QString afmName(afnm + "afm");
55 	if (QFile::exists(afmName))
56 		metricsFiles.append(afmName);
57 	else
58 	{
59 		afmName = afnm + "Afm";
60 		if (QFile::exists(afmName))
61 			metricsFiles.append(afmName);
62 		else
63 		{
64 			afmName = afnm + "AFM";
65 			if (QFile::exists(afmName))
66 				metricsFiles.append(afmName);
67 		}
68 	}
69 	// Look for pfm files
70 	QString pfmName(afnm + "pfm");
71 	if (QFile::exists(pfmName))
72 		metricsFiles.append(pfmName);
73 	else
74 	{
75 		pfmName = afnm + "Pfm";
76 		if (QFile::exists(pfmName))
77 			metricsFiles.append(pfmName);
78 		else
79 		{
80 			pfmName = afnm + "PFM";
81 			if (QFile::exists(pfmName))
82 				metricsFiles.append(pfmName);
83 		}
84 	}
85 	return metricsFiles;
86 }
87 
loadFontMetrics(FT_Face face,const QString & fontPath) const88 bool ScFace_PostScript::loadFontMetrics(FT_Face face, const QString& fontPath) const
89 {
90 	QStringList fontMetrics = findFontMetrics(fontPath);
91 	if (fontMetrics.empty())
92 	{
93 		qDebug() << QObject::tr("No metrics found for font %1, ignoring font").arg(fontPath);
94 		return false;
95 	}
96 
97 	bool brokenMetric = false;
98 	bool metricsFound = false;
99 	QString metricsFile;
100 	for (int i = 0; i < fontMetrics.size(); ++i)
101 	{
102 		metricsFile = fontMetrics.at(i);
103 		if (FT_Attach_File(face, metricsFile.toLocal8Bit().constData()) == 0)
104 		{
105 			if (brokenMetric)
106 				qDebug() << QObject::tr("Valid metrics were found for font %1, using metrics in file %2").arg(fontFile, metricsFile);
107 			metricsFound = true;
108 			break;
109 		}
110 
111 		qDebug() << QObject::tr("Font %1 has broken metrics in file %2, ignoring metrics").arg(fontPath, metricsFile);
112 		brokenMetric = true;
113 	}
114 
115 	return metricsFound;
116 }
117 
load() const118 void ScFace_PostScript::load() const // routine by Franz Schmid - modified by Alastair M. Robinson
119 {
120 	FtFace::load();
121 	//			bool error;
122 	FT_Face face = ftFace();
123 	if (!face)
124 	{
125 		const_cast<ScFace_PostScript*>(this)->usable = false;
126 		qDebug("%s", QObject::tr("Font %1 is broken (no Face), discarding it").arg(fontFile).toLocal8Bit().constData());
127 		return;
128 	}
129 	if (loadFontMetrics(face, fontFile))
130 	{
131 		// re-initialize: ScFaceData::load() just clears caches,
132 		// FtFace::load() skips FT_New_Face if m_face is already defined.
133 		// don't mind checking glyphs again for now (PS files have only 255 glyphs max, anyway)
134 		FtFace::load();
135 	}
136 }
137 
ScFace_PFB(const QString & fam,const QString & sty,const QString & alt,const QString & scname,const QString & psname,const QString & path,int face,const QStringList & features)138 ScFace_PFB::ScFace_PFB(const QString& fam, const QString& sty, const QString& alt, const QString& scname, const QString& psname, const QString& path, int face, const QStringList& features) :
139 	ScFace_PostScript(fam, sty, alt, scname, psname, path, face, features)
140 {
141 	formatCode = ScFace::PFB;
142 }
143 
embedFont(QByteArray & str) const144 bool ScFace_PFB::embedFont(QByteArray& str) const
145 {
146 	QByteArray bb;
147 	rawData(bb);
148 	if ((bb.size() > 2) &&  (bb[0] == '\x80') && (static_cast<int>(bb[1]) == 1))
149 	{
150 		int posi,cxxc=0;
151 		for (posi = 6; posi < bb.size(); ++posi)
152 		{
153 			if ((bb[posi] == '\x80') && (posi + 1 < bb.size()) && (static_cast<int>(bb[posi + 1]) == 2))
154 				break;
155 			str += bb[posi];
156 		}
157 		int ulen;
158 		if (posi + 6 < bb.size())
159 		{
160 			ulen = bb[posi + 2] & 0xff;
161 			ulen |= (bb[posi + 3] << 8) & 0xff00;
162 			ulen |= (bb[posi + 4] << 16) & 0xff0000;
163 			ulen |= (bb[posi + 5] << 24) & 0xff000000;
164 			posi += 6;
165 			if (posi + ulen > bb.size())
166 				ulen = bb.size() - posi - 1;
167 			char linebuf[80];
168 			cxxc = 0;
169 			for (int j = 0; j < ulen; ++j)
170 			{
171 				unsigned char u = bb[posi];
172 				linebuf[cxxc] = ((u >> 4) & 15) + '0';
173 				if (u>0x9f)
174 					linebuf[cxxc] += 'a'-':';
175 				++cxxc;
176 				u &= 15;
177 				linebuf[cxxc] = u + '0';
178 				if (u>0x9)
179 					linebuf[cxxc] += 'a'-':';
180 				++posi;
181 				++cxxc;
182 				if (cxxc > 72)
183 				{
184 					linebuf[cxxc++] = '\n';
185 					linebuf[cxxc++] = 0;
186 					str += linebuf;
187 					cxxc = 0;
188 				}
189 			}
190 			linebuf[cxxc] = 0;
191 			str += linebuf;
192 			str += "\n";
193 		}
194 		posi += 6;
195 		for (int j = posi; j < bb.size(); ++j)
196 		{
197 			if ((bb[j] == '\x80') && (j + 1 < bb.size()) && (static_cast<int>(bb[j + 1]) == 3))
198 				break;
199 			if (bb[j] == '\r')
200 				str += "\n";
201 			else
202 				str += bb[j];
203 		}
204 		str += "\n";
205 		return true;
206 	}
207 	qDebug("%s", QObject::tr("Font %1 cannot be read, no embedding").arg(fontFile).toLatin1().constData());
208 	return false;
209 }
210 
ScFace_PFA(const QString & fam,const QString & sty,const QString & alt,const QString & scname,const QString & psname,const QString & path,int face,const QStringList & features)211 ScFace_PFA::ScFace_PFA(const QString& fam, const QString& sty, const QString& alt, const QString& scname, const QString& psname, const QString& path, int face, const QStringList& features) :
212 	ScFace_PostScript(fam, sty, alt, scname, psname, path, face, features)
213 {
214 	formatCode = ScFace::PFA;
215 }
216 
embedFont(QByteArray & str) const217 bool ScFace_PFA::embedFont(QByteArray& str) const
218 {
219 	QByteArray bb;
220 	rawData(bb);
221 	if (bb.size() > 2 && bb[0] == '%' && bb[1] == '!')
222 	{
223 		// this is ok since bb will not contain '\0'
224 		str.append(bb);
225 		return true;
226 	}
227 	qDebug("%s", QObject::tr("Font %1 cannot be read, no embedding").arg(fontFile).toLatin1().constData());
228 	return false;
229 }
230