Rev 16508 | Rev 16550 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
5979 | avox | 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 | |||
10223 | cbradney | 8 | #include <QFile> |
9 | #include <QString> |
||
10 | #include <QObject> |
||
13531 | pierre | 11 | #include <QDebug> |
5979 | avox | 12 | |
13 | #include <sys/types.h> |
||
14 | |||
15 | #include "fonts/scface_ttf.h" |
||
16 | #include "fonts/scfontmetrics.h" |
||
17 | #include "util.h" |
||
18 | #include "scconfig.h" |
||
19 | |||
13531 | pierre | 20 | KernFeature::KernFeature ( FT_Face face ) |
21 | :m_valid ( true ) |
||
22 | { |
||
23 | FontName = QString ( face->family_name ) + " " + QString ( face->style_name ) ; |
||
24 | // qDebug() <<"KF"<<FontName; |
||
25 | // QTime t; |
||
26 | // t.start(); |
||
27 | FT_ULong length = 0; |
||
28 | if ( !FT_Load_Sfnt_Table ( face, TTAG_GPOS , 0, NULL, &length ) ) |
||
29 | { |
||
30 | // qDebug() <<"\t"<<"GPOS table len"<<length; |
||
31 | if ( length > 32 ) |
||
32 | { |
||
33 | GPOSTableRaw.resize ( length ); |
||
34 | FT_Load_Sfnt_Table ( face, TTAG_GPOS, 0, reinterpret_cast<FT_Byte*> ( GPOSTableRaw.data() ), &length ); |
||
5979 | avox | 35 | |
13531 | pierre | 36 | makeCoverage(); |
37 | } |
||
38 | else |
||
39 | m_valid = false; |
||
5979 | avox | 40 | |
13531 | pierre | 41 | GPOSTableRaw.clear(); |
16541 | jghali | 42 | // coverages.clear(); |
13531 | pierre | 43 | } |
44 | else |
||
45 | m_valid = false; |
||
46 | |||
47 | if ( !m_valid ) |
||
48 | pairs.clear(); |
||
49 | // qDebug() <<"\t"<<m_valid; |
||
50 | // qDebug() <<"\t"<<t.elapsed(); |
||
51 | } |
||
52 | |||
53 | KernFeature::KernFeature ( const KernFeature & kf ) |
||
5979 | avox | 54 | { |
13531 | pierre | 55 | m_valid = kf.m_valid; |
56 | if ( m_valid ) |
||
57 | pairs = kf.pairs; |
||
58 | |||
5979 | avox | 59 | } |
13531 | pierre | 60 | |
61 | |||
62 | KernFeature::~ KernFeature() |
||
5979 | avox | 63 | { |
64 | } |
||
13531 | pierre | 65 | |
66 | double KernFeature::getPairValue ( unsigned int glyph1, unsigned int glyph2 ) const |
||
5979 | avox | 67 | { |
13531 | pierre | 68 | if ( m_valid ) |
69 | { |
||
16541 | jghali | 70 | |
71 | if ( pairs.contains( glyph1 ) |
||
72 | && pairs[glyph1].contains(glyph2)) |
||
13531 | pierre | 73 | { |
16541 | jghali | 74 | return pairs[glyph1][glyph2]; |
75 | } |
||
76 | else |
||
77 | { |
||
78 | qDebug()<<"Search in classes"; |
||
79 | foreach (const quint16& coverageId, coverages.keys()) |
||
13531 | pierre | 80 | { |
16541 | jghali | 81 | // for each pairpos table, coverage lists covered _first_ (left) glyph |
82 | if(coverages[coverageId].contains(glyph1)) |
||
83 | { |
||
84 | foreach(const quint16& classDefOffset, classGlyphFirst[coverageId].keys()) |
||
85 | { |
||
86 | const ClassDefTable& cdt(classGlyphFirst[coverageId][classDefOffset]); |
||
87 | foreach(const quint16& classIndex, cdt.keys()) |
||
88 | { |
||
89 | const QList<quint16>& gl(cdt[classIndex]); |
||
90 | if(gl.contains(glyph1)) |
||
91 | { |
||
92 | //qDebug()<<"Found G1"<<glyph1<<"in Class"<<classIndex<<"at pos"<<gl.indexOf(glyph1); |
||
93 | // Now we got the index of the first glyph class, see if glyph2 is in one of the left glyphs classes attached to this subtable. |
||
94 | foreach(const quint16& classDefOffset2, classGlyphSecond[coverageId].keys()) |
||
95 | { |
||
96 | const ClassDefTable& cdt2(classGlyphSecond[coverageId][classDefOffset2]); |
||
97 | foreach(const quint16& classIndex2, cdt2.keys()) |
||
98 | { |
||
99 | const QList<quint16>& gl2(cdt2[classIndex2]); |
||
100 | if(gl2.contains(glyph2)) |
||
101 | { |
||
102 | //qDebug()<<"Found G2"<<glyph2<<"in Class"<<classIndex2<<"at pos"<<gl2.indexOf(glyph2); |
||
103 | |||
104 | double v(classValue[coverageId][classIndex][classIndex2]); |
||
105 | // Cache this pair into "pairs" map. |
||
106 | pairs[glyph1][glyph2] = v; |
||
107 | return v; |
||
108 | } |
||
109 | } |
||
110 | } |
||
111 | } |
||
112 | } |
||
113 | } |
||
114 | } |
||
13531 | pierre | 115 | } |
116 | } |
||
117 | } |
||
118 | return 0.0; |
||
5979 | avox | 119 | } |
13531 | pierre | 120 | |
121 | void KernFeature::makeCoverage() |
||
5979 | avox | 122 | { |
13531 | pierre | 123 | if ( GPOSTableRaw.isEmpty() ) |
124 | return; |
||
125 | |||
126 | quint16 FeatureList_Offset= toUint16 ( 6 ); |
||
127 | quint16 LookupList_Offset = toUint16 ( 8 ); |
||
128 | |||
129 | // Find the offsets of the kern feature tables |
||
130 | quint16 FeatureCount = toUint16 ( FeatureList_Offset );; |
||
131 | QList<quint16> FeatureKern_Offset; |
||
132 | for ( quint16 FeatureRecord ( 0 ); FeatureRecord < FeatureCount; ++ FeatureRecord ) |
||
133 | { |
||
134 | int rawIdx ( FeatureList_Offset + 2 + ( 6 * FeatureRecord ) ); |
||
135 | quint32 tag ( FT_MAKE_TAG ( GPOSTableRaw.at ( rawIdx ), |
||
16541 | jghali | 136 | GPOSTableRaw.at ( rawIdx + 1 ), |
137 | GPOSTableRaw.at ( rawIdx + 2 ), |
||
138 | GPOSTableRaw.at ( rawIdx + 3 ) ) ); |
||
13531 | pierre | 139 | if ( tag == TTAG_kern ) |
140 | { |
||
141 | FeatureKern_Offset << ( toUint16 ( rawIdx + 4 ) + FeatureList_Offset ); |
||
142 | |||
143 | } |
||
144 | } |
||
145 | |||
146 | // Extract indices of lookups for feture kern |
||
147 | QList<quint16> LookupListIndex; |
||
148 | foreach ( quint16 kern, FeatureKern_Offset ) |
||
149 | { |
||
150 | quint16 LookupCount ( toUint16 ( kern + 2 ) ); |
||
151 | for ( int llio ( 0 ) ; llio < LookupCount; ++llio ) |
||
152 | { |
||
153 | quint16 Idx ( toUint16 ( kern + 4 + ( llio * 2 ) ) ); |
||
154 | if ( !LookupListIndex.contains ( Idx ) ) |
||
155 | { |
||
156 | LookupListIndex <<Idx ; |
||
157 | } |
||
158 | } |
||
159 | } |
||
160 | |||
161 | |||
162 | // Extract offsets of lookup tables for feature kern |
||
163 | QList<quint16> LookupTables; |
||
164 | QList<quint16> PairAdjustmentSubTables; |
||
165 | for ( int i ( 0 ); i < LookupListIndex.count(); ++i ) |
||
166 | { |
||
167 | int rawIdx ( LookupList_Offset + 2 + ( LookupListIndex[i] * 2 ) ); |
||
168 | quint16 Lookup ( toUint16 ( rawIdx ) + LookupList_Offset ); |
||
169 | quint16 SubTableCount ( toUint16 ( Lookup + 4 ) ); |
||
170 | for ( int stIdx ( 0 ); stIdx < SubTableCount; ++ stIdx ) |
||
171 | { |
||
172 | quint16 SubTable ( toUint16 ( Lookup + 6 + ( 2 * stIdx ) ) + Lookup ); |
||
173 | |||
174 | // quint16 PosFormat ( toUint16 ( SubTable ) ); |
||
175 | quint16 Coverage_Offset ( toUint16 ( SubTable + 2 ) + SubTable ); |
||
176 | quint16 CoverageFormat ( toUint16 ( Coverage_Offset ) ); |
||
177 | |||
178 | if ( 1 == CoverageFormat ) // glyph indices based |
||
179 | { |
||
180 | quint16 GlyphCount ( toUint16 ( Coverage_Offset + 2 ) ); |
||
181 | quint16 GlyphID ( Coverage_Offset + 4 ); |
||
16541 | jghali | 182 | if (GlyphCount == 0) continue; |
13531 | pierre | 183 | |
184 | for ( unsigned int gl ( 0 ); gl < GlyphCount; ++gl ) |
||
185 | { |
||
186 | coverages[SubTable] << toUint16 ( GlyphID + ( gl * 2 ) ); |
||
187 | } |
||
188 | } |
||
189 | else if ( 2 == CoverageFormat ) // Coverage Format2 => ranges based |
||
190 | { |
||
191 | quint16 RangeCount ( toUint16 ( Coverage_Offset + 2 ) ); |
||
16541 | jghali | 192 | if (RangeCount == 0) continue; |
13531 | pierre | 193 | |
194 | // int gl_base ( 0 ); |
||
195 | for ( int r ( 0 ); r < RangeCount; ++r ) |
||
196 | { |
||
197 | quint16 rBase ( Coverage_Offset + 4 + ( r * 6 ) ); |
||
198 | quint16 Start ( toUint16 ( rBase ) ); |
||
199 | quint16 End ( toUint16 ( rBase + 2 ) ); |
||
200 | // quint16 StartCoverageIndex ( toUint16 ( rBase + 4 ) ); |
||
16456 | jghali | 201 | // #9842 : for some font such as Gabriola Regular |
202 | // the range maybe be specified in reverse order |
||
203 | if (Start <= End) |
||
204 | { |
||
205 | for ( unsigned int gl ( Start ); gl <= End; ++gl ) |
||
206 | coverages[SubTable] << gl; |
||
207 | } |
||
208 | else |
||
209 | { |
||
210 | for ( unsigned int gl ( Start ); gl >= End; --gl ) |
||
211 | coverages[SubTable] << gl; |
||
212 | } |
||
13531 | pierre | 213 | } |
214 | } |
||
215 | else |
||
14819 | fschmid | 216 | { |
16541 | jghali | 217 | // qDebug() <<"Unknow Coverage Format:"<<CoverageFormat; |
14819 | fschmid | 218 | continue; |
219 | } |
||
13531 | pierre | 220 | |
221 | makePairs ( SubTable ); |
||
222 | } |
||
223 | |||
224 | } |
||
225 | |||
226 | |||
5979 | avox | 227 | } |
13531 | pierre | 228 | |
229 | |||
230 | void KernFeature::makePairs ( quint16 subtableOffset ) |
||
231 | { |
||
232 | /* |
||
233 | Lookup Type 2: |
||
234 | Pair Adjustment Positioning Subtable |
||
235 | */ |
||
236 | |||
237 | quint16 PosFormat ( toUint16 ( subtableOffset ) ); |
||
238 | |||
239 | if ( PosFormat == 1 ) |
||
240 | { |
||
241 | quint16 ValueFormat1 ( toUint16 ( subtableOffset +4 ) ); |
||
242 | quint16 ValueFormat2 ( toUint16 ( subtableOffset +6 ) ); |
||
243 | quint16 PairSetCount ( toUint16 ( subtableOffset +8 ) ); |
||
244 | if ( ValueFormat1 && ValueFormat2 ) |
||
245 | { |
||
246 | for ( int psIdx ( 0 ); psIdx < PairSetCount; ++ psIdx ) |
||
247 | { |
||
248 | unsigned int FirstGlyph ( coverages[subtableOffset][psIdx] ); |
||
249 | quint16 PairSetOffset ( toUint16 ( subtableOffset +10 + ( 2 * psIdx ) ) + subtableOffset ); |
||
250 | quint16 PairValueCount ( toUint16 ( PairSetOffset ) ); |
||
251 | quint16 PairValueRecord ( PairSetOffset + 2 ); |
||
252 | for ( int pvIdx ( 0 );pvIdx < PairValueCount; ++pvIdx ) |
||
253 | { |
||
254 | quint16 recordBase ( PairValueRecord + ( ( 2 + 2 + 2 ) * pvIdx ) ); |
||
255 | quint16 SecondGlyph ( toUint16 ( recordBase ) ); |
||
256 | qint16 Value1 ( toInt16 ( recordBase + 2 ) ); |
||
257 | pairs[FirstGlyph][SecondGlyph] = double ( Value1 ); |
||
258 | } |
||
16541 | jghali | 259 | |
13531 | pierre | 260 | } |
261 | } |
||
262 | else if ( ValueFormat1 && ( !ValueFormat2 ) ) |
||
263 | { |
||
264 | for ( int psIdx ( 0 ); psIdx < PairSetCount; ++ psIdx ) |
||
265 | { |
||
266 | unsigned int FirstGlyph ( coverages[subtableOffset][psIdx] ); |
||
267 | quint16 PairSetOffset ( toUint16 ( subtableOffset +10 + ( 2 * psIdx ) ) + subtableOffset ); |
||
268 | quint16 PairValueCount ( toUint16 ( PairSetOffset ) ); |
||
269 | quint16 PairValueRecord ( PairSetOffset + 2 ); |
||
270 | for ( int pvIdx ( 0 );pvIdx < PairValueCount; ++pvIdx ) |
||
271 | { |
||
272 | quint16 recordBase ( PairValueRecord + ( ( 2 + 2 ) * pvIdx ) ); |
||
273 | quint16 SecondGlyph ( toUint16 ( recordBase ) ); |
||
274 | qint16 Value1 ( toInt16 ( recordBase + 2 ) ); |
||
275 | pairs[FirstGlyph][SecondGlyph] = double ( Value1 ); |
||
16541 | jghali | 276 | |
13531 | pierre | 277 | } |
16541 | jghali | 278 | |
13531 | pierre | 279 | } |
280 | } |
||
281 | else |
||
282 | { |
||
16541 | jghali | 283 | // qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null"; |
13531 | pierre | 284 | } |
285 | |||
286 | } |
||
16541 | jghali | 287 | else if ( PosFormat == 2 ) // class kerning |
13531 | pierre | 288 | { |
289 | quint16 ValueFormat1 ( toUint16 ( subtableOffset +4 ) ); |
||
290 | quint16 ValueFormat2 ( toUint16 ( subtableOffset +6 ) ); |
||
291 | quint16 ClassDef1 ( toUint16 ( subtableOffset +8 ) + subtableOffset ); |
||
292 | quint16 ClassDef2 ( toUint16 ( subtableOffset +10 ) + subtableOffset ); |
||
293 | quint16 Class1Count ( toUint16 ( subtableOffset +12 ) ); |
||
294 | quint16 Class2Count ( toUint16 ( subtableOffset +14 ) ); |
||
295 | quint16 Class1Record ( subtableOffset +16 ); |
||
296 | |||
297 | // first extract classses |
||
16541 | jghali | 298 | getClass(true, ClassDef1 , subtableOffset ); |
299 | getClass(false, ClassDef2 , subtableOffset ); |
||
13531 | pierre | 300 | |
301 | if ( ValueFormat1 && ValueFormat2 ) |
||
302 | { |
||
303 | for ( quint16 C1 ( 0 );C1 < Class1Count; ++C1 ) |
||
304 | { |
||
305 | quint16 Class2Record ( Class1Record + ( C1 * ( 2 * 2 * Class2Count ) ) ); |
||
306 | for ( quint16 C2 ( 0 );C2 < Class2Count; ++C2 ) |
||
307 | { |
||
308 | qint16 Value1 ( toInt16 ( Class2Record + ( C2 * ( 2 * 2 ) ) ) ); |
||
16541 | jghali | 309 | if(Value1 != 0) |
13531 | pierre | 310 | { |
16541 | jghali | 311 | classValue[subtableOffset][C1][C2] = double ( Value1 ); |
13531 | pierre | 312 | } |
313 | } |
||
314 | } |
||
315 | } |
||
316 | else if ( ValueFormat1 && ( !ValueFormat2 ) ) |
||
317 | { |
||
16541 | jghali | 318 | for ( quint16 C1 ( 1 );C1 < Class1Count; ++C1 ) |
13531 | pierre | 319 | { |
320 | quint16 Class2Record ( Class1Record + ( C1 * ( 2 * Class2Count ) ) ); |
||
16541 | jghali | 321 | for ( quint16 C2 ( 1 );C2 < Class2Count; ++C2 ) |
13531 | pierre | 322 | { |
323 | qint16 Value1 ( toInt16 ( Class2Record + ( C2 * 2 ) ) ); |
||
16541 | jghali | 324 | if(Value1 != 0) |
13531 | pierre | 325 | { |
16541 | jghali | 326 | classValue[subtableOffset][C1][C2] = double ( Value1 ); |
13531 | pierre | 327 | } |
328 | } |
||
329 | } |
||
330 | } |
||
331 | else |
||
332 | { |
||
16541 | jghali | 333 | // qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null"; |
13531 | pierre | 334 | } |
335 | |||
336 | } |
||
337 | else |
||
338 | qDebug() <<"unknown PosFormat"<<PosFormat; |
||
339 | } |
||
340 | |||
16541 | jghali | 341 | KernFeature::ClassDefTable KernFeature::getClass ( bool leftGlyph, quint16 classDefOffset, quint16 coverageId ) |
13531 | pierre | 342 | { |
16541 | jghali | 343 | if(leftGlyph) |
344 | { |
||
345 | if(classGlyphFirst.contains(coverageId) && classGlyphFirst[coverageId].contains(classDefOffset)) |
||
346 | return classGlyphFirst[coverageId][classDefOffset]; |
||
347 | } |
||
348 | else |
||
349 | { |
||
350 | if(classGlyphSecond.contains(coverageId) && classGlyphSecond[coverageId].contains(classDefOffset)) |
||
351 | return classGlyphSecond[coverageId][classDefOffset]; |
||
352 | } |
||
353 | |||
13531 | pierre | 354 | ClassDefTable ret; |
16541 | jghali | 355 | |
356 | QList<quint16> excludeList; |
||
13531 | pierre | 357 | quint16 ClassFormat ( toUint16 ( classDefOffset ) ); |
358 | if ( ClassFormat == 1 ) |
||
359 | { |
||
360 | quint16 StartGlyph ( toUint16 ( classDefOffset +2 ) ); |
||
361 | quint16 GlyphCount ( toUint16 ( classDefOffset +4 ) ); |
||
362 | quint16 ClassValueArray ( classDefOffset + 6 ); |
||
363 | |||
364 | for ( quint16 CV ( 0 );CV < GlyphCount; ++CV ) |
||
365 | { |
||
16541 | jghali | 366 | excludeList<<StartGlyph + CV; |
13531 | pierre | 367 | ret[ toUint16 ( ClassValueArray + ( CV * 2 ) ) ] << StartGlyph + CV; |
368 | } |
||
369 | } |
||
370 | else if ( ClassFormat == 2 ) |
||
371 | { |
||
372 | quint16 ClassRangeCount ( toUint16 ( classDefOffset + 2 ) ); |
||
373 | quint16 ClassRangeRecord ( classDefOffset + 4 ); |
||
374 | for ( int CRR ( 0 ); CRR < ClassRangeCount; ++CRR ) |
||
375 | { |
||
376 | quint16 Start ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) ) ); |
||
377 | quint16 End ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 2 ) ); |
||
378 | quint16 Class ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 4 ) ); |
||
379 | |||
16508 | jghali | 380 | for ( int gl ( Start ); gl <= (int) End; ++gl ) |
13531 | pierre | 381 | { |
16541 | jghali | 382 | excludeList<< (quint16) gl; |
383 | ret[Class] << gl; |
||
13531 | pierre | 384 | } |
385 | } |
||
386 | } |
||
387 | else |
||
388 | qDebug() <<"Unknown Class Table type"; |
||
389 | |||
16541 | jghali | 390 | // if possible (all glyphs are "classed"), avoid to pass through this slow piece of code. |
391 | if(excludeList.count() != coverages[coverageId].count()) |
||
392 | { |
||
393 | foreach(const quint16& gidx, coverages[coverageId]) |
||
394 | { |
||
395 | if(!excludeList.contains(gidx)) |
||
396 | ret[0] << gidx; |
||
397 | } |
||
398 | } |
||
399 | if(leftGlyph) |
||
400 | classGlyphFirst[coverageId][classDefOffset] = ret; |
||
401 | else |
||
402 | classGlyphSecond[coverageId][classDefOffset] = ret; |
||
403 | |||
13531 | pierre | 404 | return ret; |
405 | } |
||
406 | |||
407 | quint16 KernFeature::toUint16 ( quint16 index ) |
||
408 | { |
||
409 | if ( ( index + 2 ) > GPOSTableRaw.count() ) |
||
410 | { |
||
16541 | jghali | 411 | // qDebug() << "HORROR!" << index << GPOSTableRaw.count() << FontName ; |
13531 | pierre | 412 | // Rather no kerning at all than random kerning |
413 | // m_valid = false; |
||
414 | return 0; |
||
415 | } |
||
416 | // FIXME I just do not know how it has to be done *properly* |
||
417 | quint16 c1 ( GPOSTableRaw.at ( index ) ); |
||
418 | quint16 c2 ( GPOSTableRaw.at ( index + 1 ) ); |
||
419 | c1 &= 0xFF; |
||
420 | c2 &= 0xFF; |
||
421 | quint16 ret ( ( c1 << 8 ) | c2 ); |
||
422 | return ret; |
||
423 | } |
||
424 | |||
425 | qint16 KernFeature::toInt16 ( quint16 index ) |
||
426 | { |
||
427 | if ( ( index + 2 ) > GPOSTableRaw.count() ) |
||
428 | { |
||
429 | return 0; |
||
430 | } |
||
431 | // FIXME I just do not know how it has to be done *properly* |
||
432 | quint16 c1 ( GPOSTableRaw.at ( index ) ); |
||
433 | quint16 c2 ( GPOSTableRaw.at ( index + 1 ) ); |
||
434 | c1 &= 0xFF; |
||
435 | c2 &= 0xFF; |
||
436 | qint16 ret ( ( c1 << 8 ) | c2 ); |
||
437 | return ret; |
||
438 | } |
||
439 | |||
440 | |||
16541 | jghali | 441 | namespace { |
442 | uint word(QByteArray const & bb, uint pos) |
||
13531 | pierre | 443 | { |
16541 | jghali | 444 | const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos; |
445 | return pp[0] << 24 | pp[1] << 16 | pp[2] << 8 | pp[3]; |
||
446 | } |
||
447 | uint word16(QByteArray const & bb, uint pos) |
||
448 | { |
||
449 | const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos; |
||
450 | return pp[0] << 8 | pp[1]; |
||
451 | } |
||
452 | QString tag(QByteArray const & bb, uint pos) |
||
453 | { |
||
454 | char buf[5] = "1234"; |
||
455 | buf[0] = bb.data()[pos]; |
||
456 | buf[1] = bb.data()[pos+1]; |
||
457 | buf[2] = bb.data()[pos+2]; |
||
458 | buf[3] = bb.data()[pos+3]; |
||
459 | return buf; |
||
460 | } |
||
461 | bool copy(QByteArray & dst, uint to, QByteArray & src, uint from, uint len) |
||
462 | { |
||
463 | if (!dst.data()) |
||
464 | return false; |
||
465 | if (!src.data()) |
||
466 | return false; |
||
467 | if (to + len > static_cast<uint>(dst.size())) |
||
468 | return false; |
||
469 | if (from + len > static_cast<uint>(src.size())) |
||
470 | return false; |
||
471 | |||
472 | memcpy(dst.data() + to, src.data() + from, len); |
||
473 | return true; |
||
474 | } |
||
5979 | avox | 475 | } //namespace |
476 | |||
13531 | pierre | 477 | ScFace_ttf::ScFace_ttf ( QString fam, QString sty, QString alt, QString scname, QString psname, QString path, int face ) |
478 | : FtFace ( fam, sty, alt, scname, psname, path, face ) |
||
479 | { |
||
480 | formatCode = ScFace::SFNT; |
||
481 | kernFeature = 0; |
||
482 | } |
||
5979 | avox | 483 | |
13531 | pierre | 484 | ScFace_ttf::~ ScFace_ttf() |
485 | { |
||
486 | if ( kernFeature ) |
||
487 | delete kernFeature; |
||
488 | } |
||
489 | |||
490 | |||
491 | void ScFace_ttf::load() const |
||
492 | { |
||
493 | if ( !kernFeature ) |
||
494 | kernFeature = new KernFeature ( ftFace() ); |
||
495 | FtFace::load(); |
||
496 | } |
||
497 | |||
498 | void ScFace_ttf::unload() const |
||
499 | { |
||
500 | if ( kernFeature ) |
||
501 | delete kernFeature; |
||
502 | kernFeature = 0; |
||
503 | FtFace::unload(); |
||
504 | } |
||
505 | |||
16541 | jghali | 506 | qreal ScFace_ttf::glyphKerning ( uint gl1, uint gl2, qreal sz ) const |
13531 | pierre | 507 | { |
16541 | jghali | 508 | if ( kernFeature->isValid() ) |
13531 | pierre | 509 | { |
16541 | jghali | 510 | return kernFeature->getPairValue ( gl1,gl2 ) / m_uniEM * sz; |
511 | |||
512 | } |
||
513 | else |
||
514 | return FtFace::glyphKerning ( gl1, gl2, sz ); |
||
515 | } |
||
516 | |||
517 | |||
518 | void ScFace_ttf::RawData(QByteArray & bb) const { |
||
519 | if (formatCode == ScFace::TTCF) { |
||
5979 | avox | 520 | QByteArray coll; |
16541 | jghali | 521 | FtFace::RawData(coll); |
5979 | avox | 522 | // access table for faceIndex |
16541 | jghali | 523 | if (faceIndex >= static_cast<int>(word(coll, 8))) |
5979 | avox | 524 | { |
16541 | jghali | 525 | bb.resize(0); |
5979 | avox | 526 | return; |
527 | } |
||
528 | static const uint OFFSET_TABLE_LEN = 12; |
||
529 | static const uint TDIR_ENTRY_LEN = 16; |
||
16541 | jghali | 530 | uint faceOffset = word(coll, 12 + 4 * faceIndex); |
531 | uint nTables = word16(coll, faceOffset + 4); |
||
532 | sDebug(QObject::tr("extracting face %1 from font %2 (offset=%3, nTables=%4)").arg(faceIndex).arg(fontFile).arg(faceOffset).arg(nTables)); |
||
5979 | avox | 533 | uint headerLength = OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * nTables; |
534 | uint tableLengths = 0; |
||
535 | // sum table lengths incl padding |
||
16541 | jghali | 536 | for (uint i=0; i < nTables; ++i) |
5979 | avox | 537 | { |
16541 | jghali | 538 | tableLengths += word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 12); |
539 | tableLengths = (tableLengths+3) & ~3; |
||
5979 | avox | 540 | } |
16541 | jghali | 541 | bb.resize(headerLength + tableLengths); |
542 | if (! bb.data()) |
||
5979 | avox | 543 | return; |
544 | // write header |
||
16541 | jghali | 545 | sDebug(QObject::tr("memcpy header: %1 %2 %3").arg(0).arg(faceOffset).arg(headerLength)); |
546 | if (!copy(bb, 0, coll, faceOffset, headerLength)) |
||
5979 | avox | 547 | return; |
548 | |||
549 | uint pos = headerLength; |
||
16541 | jghali | 550 | for (uint i=0; i < nTables; ++i) |
5979 | avox | 551 | { |
16541 | jghali | 552 | uint tableSize = word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 12); |
553 | uint tableStart = word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8); |
||
554 | sDebug(QObject::tr("table '%1'").arg(tag(coll, tableStart))); |
||
555 | sDebug(QObject::tr("memcpy table: %1 %2 %3").arg(pos).arg(tableStart).arg(tableSize)); |
||
556 | if (!copy(bb, pos, coll, tableStart, tableSize)) break; |
||
5979 | avox | 557 | // write new offset to table entry |
16541 | jghali | 558 | sDebug(QObject::tr("memcpy offset: %1 %2 %3").arg(OFFSET_TABLE_LEN + TDIR_ENTRY_LEN*i + 8).arg(pos).arg(4)); |
559 | memcpy(bb.data() + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8, &pos, 4); |
||
5979 | avox | 560 | pos += tableSize; |
561 | // pad |
||
16541 | jghali | 562 | while ((pos & 3) != 0) |
563 | bb.data()[pos++] = '\0'; |
||
5979 | avox | 564 | } |
16541 | jghali | 565 | } |
566 | else if (formatCode == ScFace::TYPE42) { |
||
567 | FtFace::RawData(bb); |
||
5979 | avox | 568 | } |
16541 | jghali | 569 | else { |
570 | FtFace::RawData(bb); |
||
5979 | avox | 571 | } |
572 | } |
||
573 | |||
16541 | jghali | 574 | bool ScFace_ttf::EmbedFont(QString &str) const |
5979 | avox | 575 | { |
16541 | jghali | 576 | if (formatCode == ScFace::TYPE42) { |
5979 | avox | 577 | //easy: |
578 | QByteArray bb; |
||
16541 | jghali | 579 | FtFace::RawData(bb); |
5979 | avox | 580 | str += bb; |
581 | return true; |
||
582 | } |
||
583 | QString tmp4; |
||
584 | QString tmp2 = ""; |
||
585 | QString tmp3 = ""; |
||
586 | int counter = 0; |
||
587 | char *buf[50]; |
||
588 | FT_ULong charcode; |
||
589 | FT_UInt gindex; |
||
590 | FT_Face face = ftFace(); |
||
16541 | jghali | 591 | if (!face) { |
5979 | avox | 592 | return false; |
593 | } |
||
594 | const FT_Stream fts = face->stream; |
||
16541 | jghali | 595 | if (ftIOFunc(fts, 0L, NULL, 0)) { |
596 | return(false); |
||
5979 | avox | 597 | } |
598 | str+="%!PS-TrueTypeFont\n"; |
||
599 | str+="11 dict begin\n"; |
||
600 | str+="/FontName /" + psName + " def\n"; |
||
601 | str+="/Encoding /ISOLatin1Encoding where {pop ISOLatin1Encoding} {StandardEncoding} ifelse def\n"; |
||
602 | str+="/PaintType 0 def\n/FontMatrix [1 0 0 1 0 0] def\n"; |
||
603 | str+="/FontBBox ["+FontBBox+"] def\n"; |
||
604 | str+="/FontType 42 def\n"; |
||
605 | str+="/FontInfo 8 dict dup begin\n"; |
||
606 | str+="/FamilyName (" + psName + ") def\n"; |
||
607 | str+="end readonly def\n"; |
||
608 | unsigned char *tmp = new unsigned char[65535]; |
||
609 | int length; |
||
610 | char linebuf[80]; |
||
611 | str += "/sfnts ["; |
||
612 | int poso=0; |
||
16541 | jghali | 613 | do { |
5979 | avox | 614 | int posi=0; |
615 | length= fts->size - fts->pos; |
||
16541 | jghali | 616 | if (length > 65534) { |
5979 | avox | 617 | length = 65534; |
618 | } |
||
16541 | jghali | 619 | if (!ftIOFunc(fts, 0L, tmp, length)) |
5979 | avox | 620 | { |
621 | str+="\n<\n"; |
||
16541 | jghali | 622 | for (int j = 0; j < length; j++) |
5979 | avox | 623 | { |
624 | unsigned char u=tmp[posi]; |
||
16541 | jghali | 625 | linebuf[poso]=((u >> 4) & 15) + '0'; |
626 | if(u>0x9f) linebuf[poso]+='a'-':'; |
||
5979 | avox | 627 | ++poso; |
628 | u&=15; linebuf[poso]=u + '0'; |
||
16541 | jghali | 629 | if(u>0x9) linebuf[poso]+='a'-':'; |
5979 | avox | 630 | ++posi; |
631 | ++poso; |
||
16541 | jghali | 632 | if (poso > 70) |
5979 | avox | 633 | { |
634 | linebuf[poso++]='\n'; |
||
635 | linebuf[poso++]=0; |
||
636 | str += linebuf; |
||
637 | poso = 0; |
||
638 | } |
||
639 | } |
||
640 | linebuf[poso++]=0; |
||
641 | str += linebuf; |
||
642 | poso = 0; |
||
643 | str += "00\n>"; |
||
644 | } |
||
16541 | jghali | 645 | else { |
646 | sDebug(QObject::tr("Font %1 is broken (read stream), no embedding").arg(fontFile)); |
||
5979 | avox | 647 | str += "\n] def\n"; |
16541 | jghali | 648 | status = qMax(status,ScFace::BROKENGLYPHS); |
5979 | avox | 649 | return false; |
650 | } |
||
16541 | jghali | 651 | } while (length==65534); |
652 | |||
5979 | avox | 653 | str += "\n] def\n"; |
9556 | avox | 654 | delete[] tmp; |
5979 | avox | 655 | gindex = 0; |
16541 | jghali | 656 | charcode = FT_Get_First_Char(face, &gindex ); |
657 | while (gindex != 0) |
||
5979 | avox | 658 | { |
16541 | jghali | 659 | FT_Get_Glyph_Name(face, gindex, buf, 50); |
660 | tmp2 += "/"+QString(reinterpret_cast<char*>(buf))+" "+tmp3.setNum(gindex)+" def\n"; |
||
661 | charcode = FT_Get_Next_Char(face, charcode, &gindex ); |
||
5979 | avox | 662 | counter++; |
663 | } |
||
16541 | jghali | 664 | tmp4.setNum(counter); |
5979 | avox | 665 | str += "/CharStrings " + tmp4 + " dict dup begin\n"+tmp2; |
666 | str += "end readonly def\n"; |
||
667 | str += "FontName currentdict end definefont pop\n"; |
||
16541 | jghali | 668 | return(true); |
5979 | avox | 669 | } |
670 |