1 //
2 // C++ Implementation: fmkernfeat
3 //
4 // Description: try to make it the more compact & simple as possible
5 //
6 //
7 // Author: Pierre Marchand <pierremarc@oep-h.com>, (C) 2009
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //
12 
13 #include "fmkernfeat.h"
14 
15 #include FT_TRUETYPE_TABLES_H
16 #include FT_TRUETYPE_TAGS_H
17 
18 #include <QStringList>
19 #include <QDebug>
20 
21 
FMKernFeature(FT_Face face)22 FMKernFeature::FMKernFeature ( FT_Face face )
23 		:p_face ( face )
24 {
25 	FT_ULong length = 0;
26 	if ( !FT_Load_Sfnt_Table ( face, TTAG_GPOS , 0, NULL, &length ) )
27 	{
28 		if ( length > 32 )
29 		{
30 			GPOSTableRaw.resize ( length );
31 			FT_Load_Sfnt_Table ( face, TTAG_GPOS, 0, ( FT_Byte * ) GPOSTableRaw.data (), &length );
32 
33 			makeCoverage();
34 		}
35 		else
36 			GPOSTableRaw.clear();
37 	}
38 }
39 
~FMKernFeature()40 FMKernFeature::~ FMKernFeature()
41 {
42 }
43 
44 
makeCoverage()45 void FMKernFeature::makeCoverage()
46 {
47 	if ( GPOSTableRaw.isEmpty() )
48 		return;
49 
50 	bool out ( true );
51 	quint16 FeatureList_Offset= toUint16 ( 6 );
52 	quint16 LookupList_Offset = toUint16 ( 8 );
53 
54 	// Find the offsets of the kern feature tables
55 	quint16 FeatureCount = toUint16 ( FeatureList_Offset );;
56 	QList<quint16> FeatureKern_Offset;
57 	for ( quint16 FeatureRecord ( 0 ); FeatureRecord < FeatureCount; ++ FeatureRecord )
58 	{
59 		int rawIdx ( FeatureList_Offset + 2 + ( 6 * FeatureRecord ) );
60 		quint32 tag ( FT_MAKE_TAG ( GPOSTableRaw.at ( rawIdx ),
61 		                            GPOSTableRaw.at ( rawIdx + 1 ),
62 		                            GPOSTableRaw.at ( rawIdx + 2 ),
63 		                            GPOSTableRaw.at ( rawIdx + 3 ) ) );
64 		if ( tag == TTAG_kern )
65 		{
66 			FeatureKern_Offset << ( toUint16 ( rawIdx + 4 ) + FeatureList_Offset );
67 			if ( out )
68 			{
69 				qDebug() <<"KERN"<<FeatureKern_Offset;
70 			}
71 
72 		}
73 	}
74 
75 	// Extract indices of lookups for feture kern
76 	QList<quint16> LookupListIndex;
77 	foreach ( quint16 kern, FeatureKern_Offset )
78 	{
79 		quint16 LookupCount ( toUint16 ( kern + 2 ) );
80 		if ( out )
81 		{
82 			qDebug() <<"\tParams"<<toUint16 ( kern );
83 			qDebug() <<"\tLookupCount"<<LookupCount;
84 		}
85 		for ( int llio ( 0 ) ; llio < LookupCount; ++llio )
86 		{
87 			quint16 Idx ( toUint16 ( kern + 4 + ( llio * 2 ) ) );
88 			if ( !LookupListIndex.contains ( Idx ) )
89 			{
90 				LookupListIndex <<Idx ;
91 			}
92 		}
93 		if ( out )
94 			qDebug() <<"\tLookupIndex"<<LookupListIndex;
95 	}
96 
97 
98 	// Extract offsets of lookup tables for feature kern
99 	QList<quint16> LookupTables;
100 	QList<quint16> PairAdjustmentSubTables;
101 	for ( int i ( 0 ); i < LookupListIndex.count(); ++i )
102 	{
103 		int rawIdx ( LookupList_Offset + 2 + ( LookupListIndex[i] * 2 ) );
104 		quint16 Lookup ( toUint16 ( rawIdx )  + LookupList_Offset );
105 		quint16 SubTableCount ( toUint16 ( Lookup + 4 ) );
106 		for ( int stIdx ( 0 ); stIdx < SubTableCount; ++ stIdx )
107 		{
108 			quint16 SubTable ( toUint16 ( Lookup + 6 + ( 2 * stIdx ) ) + Lookup );
109 
110 			quint16 PosFormat ( toUint16 ( SubTable ) );
111 			quint16 Coverage_Offset ( toUint16 ( SubTable + 2 ) + SubTable );
112 			quint16 CoverageFormat ( toUint16 ( Coverage_Offset ) );
113 
114 			if ( out )
115 			{
116 				qDebug() <<"\t\tPosFormat"<<PosFormat;
117 				qDebug() <<"\t\tCoverageFormat"<<CoverageFormat;
118 			}
119 			if ( 1 == CoverageFormat ) // glyph indices based
120 			{
121 				quint16 GlyphCount ( toUint16 ( Coverage_Offset + 2 ) );
122 				quint16 GlyphID ( Coverage_Offset + 4 );
123 				if ( out )
124 					qDebug() <<"\t\t\tGlyphCount"<<GlyphCount;
125 				for ( unsigned int gl ( 0 ); gl < GlyphCount; ++gl )
126 				{
127 					coverages[SubTable] << toUint16 ( GlyphID + ( gl * 2 ) );
128 				}
129 			}
130 			else if ( 2 == CoverageFormat ) // Coverage Format2 => ranges based
131 			{
132 				quint16 RangeCount ( toUint16 ( Coverage_Offset + 2 ) );
133 				if ( out )
134 					qDebug() <<"\t\t\tRangeCount" <<RangeCount;
135 				int gl_base ( 0 );
136 				for ( int r ( 0 ); r < RangeCount; ++r )
137 				{
138 					quint16 rBase ( Coverage_Offset + 4 + ( r * 6 ) );
139 					quint16 Start ( toUint16 ( rBase ) );
140 					quint16 End ( toUint16 ( rBase + 2 ) );
141 					quint16 StartCoverageIndex ( toUint16 ( rBase + 4 ) );
142 					if(out)
143 						qDebug()<<"\t\t\t\tRange"<<Start<<End<<StartCoverageIndex;
144 					for ( unsigned int gl ( Start ); gl <= End; ++gl )
145 						coverages[SubTable]  << gl;
146 				}
147 			}
148 			else
149 				qDebug() <<"Unknow Coverage Format:"<<CoverageFormat;
150 
151 			if(out)
152 				qDebug()<<"\t\t**Built a coverage array of length:"<<coverages[SubTable].count();
153 			makePairs ( SubTable );
154 		}
155 
156 	}
157 
158 }
159 
160 
makePairs(quint16 subtableOffset)161 void FMKernFeature::makePairs ( quint16 subtableOffset )
162 {
163 	/*
164 	Lookup Type 2:
165 	Pair Adjustment Positioning Subtable
166 	*/
167 
168 	quint16 PosFormat ( toUint16 ( subtableOffset ) );
169 
170 	if ( PosFormat == 1 )
171 	{
172 		quint16 ValueFormat1 ( toUint16 ( subtableOffset +4 ) );
173 		quint16 ValueFormat2 ( toUint16 ( subtableOffset +6 ) );
174 		quint16 PairSetCount ( toUint16 ( subtableOffset +8 ) );
175 		if ( ValueFormat1 && ValueFormat2 )
176 		{
177 			for ( int psIdx ( 0 ); psIdx < PairSetCount; ++ psIdx )
178 			{
179 				unsigned int FirstGlyph ( coverages[subtableOffset][psIdx] );
180 				quint16 PairSetOffset ( toUint16 ( subtableOffset +10 + ( 2 * psIdx ) ) +  subtableOffset );
181 				quint16 PairValueCount ( toUint16 ( PairSetOffset ) );
182 				quint16 PairValueRecord ( PairSetOffset + 2 );
183 				for ( int pvIdx ( 0 );pvIdx < PairValueCount; ++pvIdx )
184 				{
185 					quint16 recordBase ( PairValueRecord + ( ( 2 + 2 + 2 ) * pvIdx ) );
186 					quint16 SecondGlyph ( toUint16 ( recordBase ) );
187 					qint16 Value1 ( toInt16 ( recordBase + 2 ) );
188 					pairs[FirstGlyph][SecondGlyph] = double ( Value1 );
189 
190 				}
191 
192 			}
193 		}
194 		else if ( ValueFormat1 && ( !ValueFormat2 ) )
195 		{
196 			for ( int psIdx ( 0 ); psIdx < PairSetCount; ++ psIdx )
197 			{
198 				unsigned int FirstGlyph ( coverages[subtableOffset][psIdx] );
199 				quint16 PairSetOffset ( toUint16 ( subtableOffset +10 + ( 2 * psIdx ) ) +  subtableOffset );
200 				quint16 PairValueCount ( toUint16 ( PairSetOffset ) );
201 				quint16 PairValueRecord ( PairSetOffset + 2 );
202 				for ( int pvIdx ( 0 );pvIdx < PairValueCount; ++pvIdx )
203 				{
204 					quint16 recordBase ( PairValueRecord + ( ( 2 + 2 ) * pvIdx ) );
205 					quint16 SecondGlyph ( toUint16 ( recordBase ) );
206 					qint16 Value1 ( toInt16 ( recordBase + 2 ) );
207 					pairs[FirstGlyph][SecondGlyph] = double ( Value1 );
208 
209 				}
210 
211 			}
212 		}
213 		else
214 		{
215 			qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null";
216 		}
217 
218 	}
219 	else if ( PosFormat == 2 )
220 	{
221 		quint16 ValueFormat1 ( toUint16 ( subtableOffset +4 ) );
222 		quint16 ValueFormat2 ( toUint16 ( subtableOffset +6 ) );
223 		quint16 ClassDef1 ( toUint16 ( subtableOffset +8 )  + subtableOffset );
224 		quint16 ClassDef2 ( toUint16 ( subtableOffset +10 ) + subtableOffset );
225 		quint16 Class1Count ( toUint16 ( subtableOffset +12 ) );
226 		quint16 Class2Count ( toUint16 ( subtableOffset +14 ) );
227 		quint16 Class1Record ( subtableOffset +16 );
228 
229 		// first extract classses
230 		ClassDefTable Class1Data ( getClass ( ClassDef1 , subtableOffset ) );
231 		ClassDefTable Class2Data ( getClass ( ClassDef2 , subtableOffset ) );
232 
233 		if ( ValueFormat1 && ValueFormat2 )
234 		{
235 			for ( quint16 C1 ( 0 );C1 < Class1Count; ++C1 )
236 			{
237 				QList<quint16> Class1 ( Class1Data[C1] );
238 				quint16 Class2Record ( Class1Record + ( C1 * ( 2 * 2 * Class2Count ) ) );
239 				for ( quint16 C2 ( 0 );C2 < Class2Count; ++C2 )
240 				{
241 					qint16 Value1 ( toInt16 ( Class2Record + ( C2 * ( 2 * 2 ) ) ) );
242 					QList<quint16> Class2 ( Class2Data[C2] );
243 					// keep it barbarian :D
244 					foreach ( quint16 FirstGlyph, Class1 )
245 					{
246 						foreach ( quint16 SecondGlyph, Class2 )
247 						{
248 							if ( Value1 )
249 								pairs[FirstGlyph][SecondGlyph] = double ( Value1 );
250 						}
251 					}
252 				}
253 			}
254 		}
255 		else if ( ValueFormat1 && ( !ValueFormat2 ) )
256 		{
257 			for ( quint16 C1 ( 0 );C1 < Class1Count; ++C1 )
258 			{
259 				QString cdbg ( QString::number ( C1 ).rightJustified ( 5,QChar ( 32 ) ) );
260 				QList<quint16> Class1 ( Class1Data[C1] );
261 				quint16 Class2Record ( Class1Record + ( C1 * ( 2 * Class2Count ) ) );
262 				for ( quint16 C2 ( 0 );C2 < Class2Count; ++C2 )
263 				{
264 					qint16 Value1 ( toInt16 ( Class2Record + ( C2 * 2 ) ) );
265 					QList<quint16> Class2 ( Class2Data[C2] );
266 
267 					foreach ( quint16 FirstGlyph, Class1 )
268 					{
269 						foreach ( quint16 SecondGlyph, Class2 )
270 						{
271 							if ( Value1 )
272 								pairs[FirstGlyph][SecondGlyph] = double ( Value1 );
273 						}
274 					}
275 				}
276 			}
277 		}
278 		else
279 		{
280 			qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null";
281 		}
282 
283 	}
284 	else
285 		qDebug() <<"unknown PosFormat"<<PosFormat;
286 }
287 
toUint16(quint16 index)288 quint16 FMKernFeature::toUint16 ( quint16 index )
289 {
290 	if ( ( index + 2 ) >= GPOSTableRaw.count() )
291 	{
292 		return 0;
293 	}
294 	quint16 c1 ( GPOSTableRaw.at ( index ) );
295 	quint16 c2 ( GPOSTableRaw.at ( index + 1 ) );
296 	c1 &= 0xFF;
297 	c2 &= 0xFF;
298 	quint16 ret ( ( c1 << 8 ) | c2 );
299 // 	qDebug()<<"**"<<index<<"("<<c1 << c2 <<")"<<ret;
300 	return ret;
301 }
302 
toInt16(quint16 index)303 qint16 FMKernFeature::toInt16 ( quint16 index )
304 {
305 	if ( ( index + 2 ) > GPOSTableRaw.count() )
306 	{
307 		return 0;
308 	}
309 	// FIXME I just do not know how it has to be done *properly*
310 	quint16 c1 ( GPOSTableRaw.at ( index ) );
311 	quint16 c2 ( GPOSTableRaw.at ( index + 1 ) );
312 	c1 &= 0xFF;
313 	c2 &= 0xFF;
314 	qint16 ret ( ( c1 << 8 ) | c2 );
315 	return ret;
316 }
317 
318 
glyphname(int index)319 QString FMKernFeature::glyphname ( int index )
320 {
321 	QByteArray key ( 1001,0 );
322 	if ( FT_HAS_GLYPH_NAMES ( p_face ) )
323 	{
324 		FT_Get_Glyph_Name ( p_face, index, key.data() , 1000 );
325 		if ( key[0] == char ( 0 ) )
326 		{
327 			key = "noname";
328 		}
329 	}
330 	else
331 	{
332 		key = "noname";
333 	}
334 	return QString ( key );
335 }
336 
getClass(quint16 classDefOffset,quint16 coverageId)337 FMKernFeature::ClassDefTable FMKernFeature::getClass ( quint16 classDefOffset, quint16 coverageId )
338 {
339 	ClassDefTable ret;
340 	ret[0] = coverages[coverageId];
341 // 	ret[0] = QList<quint16>();
342 	quint16 ClassFormat ( toUint16 ( classDefOffset ) );
343 	if ( ClassFormat == 1 )
344 	{
345 // 		qDebug()<<"ClassFormat1";
346 		quint16 StartGlyph ( toUint16 ( classDefOffset +2 ) );
347 		quint16 GlyphCount ( toUint16 ( classDefOffset +4 ) );
348 		quint16 ClassValueArray ( classDefOffset + 6 );
349 		for ( quint16 CV ( 0 );CV < GlyphCount; ++CV )
350 		{
351 			ret[0].removeAll(StartGlyph + CV);
352 			ret[ toUint16 ( ClassValueArray + ( CV * 2 ) ) ] << StartGlyph + CV;
353 		}
354 	}
355 	else if ( ClassFormat == 2 )
356 	{
357 // 		qDebug()<<"ClassFormat2";
358 		quint16 ClassRangeCount ( toUint16 ( classDefOffset + 2 ) );
359 		quint16 ClassRangeRecord ( classDefOffset + 4 );
360 		for ( int CRR ( 0 ); CRR < ClassRangeCount; ++CRR )
361 		{
362 			quint16 Start ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) ) );
363 			quint16 End ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 2 ) );
364 			quint16 Class ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 4 ) );
365 // 			qDebug()<<"CRC"<<Start<<End<<Class;
366 			for ( quint16 gl ( Start ); gl <= End; ++gl )
367 			{
368 				ret[0].removeAll(gl);
369 				ret[Class] << gl;
370 			}
371 		}
372 	}
373 	else
374 		qDebug() <<"Unknown Class Table type";
375 
376 // 	foreach(quint16 c, ret.keys())
377 // 	{
378 // 		if(c>0)
379 // 		{
380 // 			QStringList dl;
381 // 			foreach(quint16 lg, ret[c])
382 // 			{
383 // 				dl << glyphname(lg);
384 // 			}
385 // 			if(!dl.contains("noname"))
386 // 				qDebug()<<"\t"<< c <<dl.join(";");
387 // 			else
388 // 				qDebug()<<"ERROR nonames"<<dl.count();
389 // 		}
390 // 		else
391 // 			qDebug()<<"\t"<< c <<ret[c].count();
392 // 	}
393 
394 	return ret;
395 }
396 
397