Rev 20216 | 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> |
||
16365 | mrdocs | 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 | |||
16365 | mrdocs | 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 | |
16365 | mrdocs | 36 | makeCoverage(); |
37 | } |
||
38 | else |
||
39 | m_valid = false; |
||
5979 | avox | 40 | |
16365 | mrdocs | 41 | GPOSTableRaw.clear(); |
16540 | pierremarc | 42 | // coverages.clear(); |
16365 | mrdocs | 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 ) |
||
54 | { |
||
55 | m_valid = kf.m_valid; |
||
56 | if ( m_valid ) |
||
57 | pairs = kf.pairs; |
||
58 | |||
59 | } |
||
60 | |||
61 | |||
62 | KernFeature::~ KernFeature() |
||
63 | { |
||
64 | } |
||
65 | |||
66 | double KernFeature::getPairValue ( unsigned int glyph1, unsigned int glyph2 ) const |
||
67 | { |
||
68 | if ( m_valid ) |
||
69 | { |
||
16540 | pierremarc | 70 | |
71 | if ( pairs.contains( glyph1 ) |
||
72 | && pairs[glyph1].contains(glyph2)) |
||
16365 | mrdocs | 73 | { |
16540 | pierremarc | 74 | return pairs[glyph1][glyph2]; |
75 | } |
||
76 | else |
||
77 | { |
||
16544 | jghali | 78 | //qDebug()<<"Search in classes"; |
16540 | pierremarc | 79 | foreach (const quint16& coverageId, coverages.keys()) |
16365 | mrdocs | 80 | { |
16540 | pierremarc | 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 | } |
||
16365 | mrdocs | 115 | } |
116 | } |
||
117 | } |
||
118 | return 0.0; |
||
119 | } |
||
120 | |||
121 | void KernFeature::makeCoverage() |
||
122 | { |
||
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 ), |
||
136 | GPOSTableRaw.at ( rawIdx + 1 ), |
||
137 | GPOSTableRaw.at ( rawIdx + 2 ), |
||
138 | GPOSTableRaw.at ( rawIdx + 3 ) ) ); |
||
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 ); |
||
16455 | jghali | 182 | if (GlyphCount == 0) continue; |
16365 | mrdocs | 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 ) ); |
||
16455 | jghali | 192 | if (RangeCount == 0) continue; |
16365 | mrdocs | 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 ) ); |
||
16455 | 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 | { |
||
16574 | jghali | 210 | for ( int gl ( Start ); gl >= (int) End; --gl ) |
16455 | jghali | 211 | coverages[SubTable] << gl; |
212 | } |
||
16365 | mrdocs | 213 | } |
214 | } |
||
215 | else |
||
216 | { |
||
217 | // qDebug() <<"Unknow Coverage Format:"<<CoverageFormat; |
||
218 | continue; |
||
219 | } |
||
220 | |||
221 | makePairs ( SubTable ); |
||
222 | } |
||
223 | |||
224 | } |
||
225 | |||
226 | |||
227 | } |
||
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 | } |
||
259 | |||
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 ); |
||
276 | } |
||
277 | } |
||
278 | } |
||
279 | else |
||
280 | { |
||
281 | // qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null"; |
||
282 | } |
||
283 | } |
||
16540 | pierremarc | 284 | else if ( PosFormat == 2 ) // class kerning |
16365 | mrdocs | 285 | { |
286 | quint16 ValueFormat1 ( toUint16 ( subtableOffset +4 ) ); |
||
287 | quint16 ValueFormat2 ( toUint16 ( subtableOffset +6 ) ); |
||
288 | quint16 ClassDef1 ( toUint16 ( subtableOffset +8 ) + subtableOffset ); |
||
289 | quint16 ClassDef2 ( toUint16 ( subtableOffset +10 ) + subtableOffset ); |
||
290 | quint16 Class1Count ( toUint16 ( subtableOffset +12 ) ); |
||
291 | quint16 Class2Count ( toUint16 ( subtableOffset +14 ) ); |
||
292 | quint16 Class1Record ( subtableOffset +16 ); |
||
293 | |||
294 | // first extract classses |
||
16540 | pierremarc | 295 | getClass(true, ClassDef1 , subtableOffset ); |
296 | getClass(false, ClassDef2 , subtableOffset ); |
||
16365 | mrdocs | 297 | |
298 | if ( ValueFormat1 && ValueFormat2 ) |
||
299 | { |
||
300 | for ( quint16 C1 ( 0 );C1 < Class1Count; ++C1 ) |
||
301 | { |
||
302 | quint16 Class2Record ( Class1Record + ( C1 * ( 2 * 2 * Class2Count ) ) ); |
||
303 | for ( quint16 C2 ( 0 );C2 < Class2Count; ++C2 ) |
||
304 | { |
||
305 | qint16 Value1 ( toInt16 ( Class2Record + ( C2 * ( 2 * 2 ) ) ) ); |
||
16540 | pierremarc | 306 | if(Value1 != 0) |
16365 | mrdocs | 307 | { |
16540 | pierremarc | 308 | classValue[subtableOffset][C1][C2] = double ( Value1 ); |
16365 | mrdocs | 309 | } |
310 | } |
||
311 | } |
||
312 | } |
||
313 | else if ( ValueFormat1 && ( !ValueFormat2 ) ) |
||
314 | { |
||
16540 | pierremarc | 315 | for ( quint16 C1 ( 1 );C1 < Class1Count; ++C1 ) |
16365 | mrdocs | 316 | { |
317 | quint16 Class2Record ( Class1Record + ( C1 * ( 2 * Class2Count ) ) ); |
||
16540 | pierremarc | 318 | for ( quint16 C2 ( 1 );C2 < Class2Count; ++C2 ) |
16365 | mrdocs | 319 | { |
320 | qint16 Value1 ( toInt16 ( Class2Record + ( C2 * 2 ) ) ); |
||
16540 | pierremarc | 321 | if(Value1 != 0) |
16365 | mrdocs | 322 | { |
16540 | pierremarc | 323 | classValue[subtableOffset][C1][C2] = double ( Value1 ); |
16365 | mrdocs | 324 | } |
325 | } |
||
326 | } |
||
327 | } |
||
328 | else |
||
329 | { |
||
330 | // qDebug() <<"ValueFormat1 is null or both ValueFormat1 and ValueFormat2 are null"; |
||
331 | } |
||
332 | |||
333 | } |
||
334 | else |
||
335 | qDebug() <<"unknown PosFormat"<<PosFormat; |
||
336 | } |
||
337 | |||
16540 | pierremarc | 338 | KernFeature::ClassDefTable KernFeature::getClass ( bool leftGlyph, quint16 classDefOffset, quint16 coverageId ) |
16365 | mrdocs | 339 | { |
16540 | pierremarc | 340 | if(leftGlyph) |
341 | { |
||
342 | if(classGlyphFirst.contains(coverageId) && classGlyphFirst[coverageId].contains(classDefOffset)) |
||
343 | return classGlyphFirst[coverageId][classDefOffset]; |
||
344 | } |
||
345 | else |
||
346 | { |
||
347 | if(classGlyphSecond.contains(coverageId) && classGlyphSecond[coverageId].contains(classDefOffset)) |
||
348 | return classGlyphSecond[coverageId][classDefOffset]; |
||
349 | } |
||
350 | |||
16365 | mrdocs | 351 | ClassDefTable ret; |
16540 | pierremarc | 352 | |
353 | QList<quint16> excludeList; |
||
16365 | mrdocs | 354 | quint16 ClassFormat ( toUint16 ( classDefOffset ) ); |
355 | if ( ClassFormat == 1 ) |
||
356 | { |
||
357 | quint16 StartGlyph ( toUint16 ( classDefOffset +2 ) ); |
||
358 | quint16 GlyphCount ( toUint16 ( classDefOffset +4 ) ); |
||
359 | quint16 ClassValueArray ( classDefOffset + 6 ); |
||
360 | |||
361 | for ( quint16 CV ( 0 );CV < GlyphCount; ++CV ) |
||
362 | { |
||
16540 | pierremarc | 363 | excludeList<<StartGlyph + CV; |
16365 | mrdocs | 364 | ret[ toUint16 ( ClassValueArray + ( CV * 2 ) ) ] << StartGlyph + CV; |
365 | } |
||
366 | } |
||
367 | else if ( ClassFormat == 2 ) |
||
368 | { |
||
369 | quint16 ClassRangeCount ( toUint16 ( classDefOffset + 2 ) ); |
||
370 | quint16 ClassRangeRecord ( classDefOffset + 4 ); |
||
371 | for ( int CRR ( 0 ); CRR < ClassRangeCount; ++CRR ) |
||
372 | { |
||
373 | quint16 Start ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) ) ); |
||
374 | quint16 End ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 2 ) ); |
||
375 | quint16 Class ( toUint16 ( ClassRangeRecord + ( CRR * 6 ) + 4 ) ); |
||
376 | |||
17339 | jghali | 377 | if (Start <= End) |
16365 | mrdocs | 378 | { |
17339 | jghali | 379 | for ( int gl ( Start ); gl <= (int) End; ++gl ) |
380 | { |
||
381 | excludeList<< (quint16) gl; |
||
382 | ret[Class] << gl; |
||
383 | } |
||
16365 | mrdocs | 384 | } |
17339 | jghali | 385 | else |
386 | { |
||
387 | for ( int gl ( Start ); gl >= (int) End; --gl ) |
||
388 | { |
||
389 | excludeList<< (quint16) gl; |
||
390 | ret[Class] << gl; |
||
391 | } |
||
392 | } |
||
16365 | mrdocs | 393 | } |
394 | } |
||
395 | else |
||
396 | qDebug() <<"Unknown Class Table type"; |
||
397 | |||
16540 | pierremarc | 398 | // if possible (all glyphs are "classed"), avoid to pass through this slow piece of code. |
399 | if(excludeList.count() != coverages[coverageId].count()) |
||
400 | { |
||
401 | foreach(const quint16& gidx, coverages[coverageId]) |
||
402 | { |
||
403 | if(!excludeList.contains(gidx)) |
||
404 | ret[0] << gidx; |
||
405 | } |
||
406 | } |
||
407 | if(leftGlyph) |
||
408 | classGlyphFirst[coverageId][classDefOffset] = ret; |
||
409 | else |
||
410 | classGlyphSecond[coverageId][classDefOffset] = ret; |
||
411 | |||
16365 | mrdocs | 412 | return ret; |
413 | } |
||
414 | |||
415 | quint16 KernFeature::toUint16 ( quint16 index ) |
||
416 | { |
||
417 | if ( ( index + 2 ) > GPOSTableRaw.count() ) |
||
418 | { |
||
419 | // qDebug() << "HORROR!" << index << GPOSTableRaw.count() << FontName ; |
||
420 | // Rather no kerning at all than random kerning |
||
421 | // m_valid = false; |
||
422 | return 0; |
||
423 | } |
||
424 | // FIXME I just do not know how it has to be done *properly* |
||
425 | quint16 c1 ( GPOSTableRaw.at ( index ) ); |
||
426 | quint16 c2 ( GPOSTableRaw.at ( index + 1 ) ); |
||
427 | c1 &= 0xFF; |
||
428 | c2 &= 0xFF; |
||
429 | quint16 ret ( ( c1 << 8 ) | c2 ); |
||
430 | return ret; |
||
431 | } |
||
432 | |||
433 | qint16 KernFeature::toInt16 ( quint16 index ) |
||
434 | { |
||
435 | if ( ( index + 2 ) > GPOSTableRaw.count() ) |
||
436 | { |
||
437 | return 0; |
||
438 | } |
||
439 | // FIXME I just do not know how it has to be done *properly* |
||
440 | quint16 c1 ( GPOSTableRaw.at ( index ) ); |
||
441 | quint16 c2 ( GPOSTableRaw.at ( index + 1 ) ); |
||
442 | c1 &= 0xFF; |
||
443 | c2 &= 0xFF; |
||
444 | qint16 ret ( ( c1 << 8 ) | c2 ); |
||
445 | return ret; |
||
446 | } |
||
447 | |||
448 | |||
13345 | pierre | 449 | namespace { |
450 | uint word(QByteArray const & bb, uint pos) |
||
5979 | avox | 451 | { |
13345 | pierre | 452 | const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos; |
453 | return pp[0] << 24 | pp[1] << 16 | pp[2] << 8 | pp[3]; |
||
5979 | avox | 454 | } |
13345 | pierre | 455 | uint word16(QByteArray const & bb, uint pos) |
5979 | avox | 456 | { |
13345 | pierre | 457 | const unsigned char * pp = reinterpret_cast<const unsigned char*>(bb.data()) + pos; |
458 | return pp[0] << 8 | pp[1]; |
||
5979 | avox | 459 | } |
13345 | pierre | 460 | QString tag(QByteArray const & bb, uint pos) |
5979 | avox | 461 | { |
13345 | pierre | 462 | char buf[5] = "1234"; |
463 | buf[0] = bb.data()[pos]; |
||
464 | buf[1] = bb.data()[pos+1]; |
||
465 | buf[2] = bb.data()[pos+2]; |
||
466 | buf[3] = bb.data()[pos+3]; |
||
467 | return buf; |
||
5979 | avox | 468 | } |
13345 | pierre | 469 | bool copy(QByteArray & dst, uint to, QByteArray & src, uint from, uint len) |
5979 | avox | 470 | { |
13345 | pierre | 471 | if (!dst.data()) |
472 | return false; |
||
473 | if (!src.data()) |
||
474 | return false; |
||
475 | if (to + len > static_cast<uint>(dst.size())) |
||
476 | return false; |
||
477 | if (from + len > static_cast<uint>(src.size())) |
||
478 | return false; |
||
479 | |||
480 | memcpy(dst.data() + to, src.data() + from, len); |
||
481 | return true; |
||
5979 | avox | 482 | } |
483 | } //namespace |
||
484 | |||
16365 | mrdocs | 485 | ScFace_ttf::ScFace_ttf ( QString fam, QString sty, QString alt, QString scname, QString psname, QString path, int face ) |
486 | : FtFace ( fam, sty, alt, scname, psname, path, face ) |
||
487 | { |
||
488 | formatCode = ScFace::SFNT; |
||
489 | kernFeature = 0; |
||
490 | } |
||
5979 | avox | 491 | |
16365 | mrdocs | 492 | ScFace_ttf::~ ScFace_ttf() |
493 | { |
||
494 | if ( kernFeature ) |
||
495 | delete kernFeature; |
||
496 | } |
||
497 | |||
20216 | jghali | 498 | bool ScFace_ttf::isSymbolic() const |
499 | { |
||
500 | FT_Face face = ftFace(); |
||
501 | if (!face || !face->charmap) |
||
502 | return false; |
||
503 | return (m_face->charmap->encoding == FT_ENCODING_MS_SYMBOL); |
||
504 | } |
||
16365 | mrdocs | 505 | |
506 | void ScFace_ttf::load() const |
||
507 | { |
||
21073 | jghali | 508 | FtFace::load(); |
509 | if (!kernFeature) |
||
16365 | mrdocs | 510 | kernFeature = new KernFeature ( ftFace() ); |
511 | } |
||
512 | |||
513 | void ScFace_ttf::unload() const |
||
514 | { |
||
515 | if ( kernFeature ) |
||
516 | delete kernFeature; |
||
517 | kernFeature = 0; |
||
518 | FtFace::unload(); |
||
519 | } |
||
520 | |||
521 | qreal ScFace_ttf::glyphKerning ( uint gl1, uint gl2, qreal sz ) const |
||
522 | { |
||
523 | if ( kernFeature->isValid() ) |
||
524 | return kernFeature->getPairValue ( gl1,gl2 ) / m_uniEM * sz; |
||
16619 | jghali | 525 | return FtFace::glyphKerning ( gl1, gl2, sz ); |
16365 | mrdocs | 526 | } |
527 | |||
13345 | pierre | 528 | void ScFace_ttf::RawData(QByteArray & bb) const { |
529 | if (formatCode == ScFace::TTCF) { |
||
5979 | avox | 530 | QByteArray coll; |
13345 | pierre | 531 | FtFace::RawData(coll); |
5979 | avox | 532 | // access table for faceIndex |
13345 | pierre | 533 | if (faceIndex >= static_cast<int>(word(coll, 8))) |
5979 | avox | 534 | { |
13345 | pierre | 535 | bb.resize(0); |
5979 | avox | 536 | return; |
537 | } |
||
538 | static const uint OFFSET_TABLE_LEN = 12; |
||
539 | static const uint TDIR_ENTRY_LEN = 16; |
||
13345 | pierre | 540 | uint faceOffset = word(coll, 12 + 4 * faceIndex); |
541 | uint nTables = word16(coll, faceOffset + 4); |
||
542 | sDebug(QObject::tr("extracting face %1 from font %2 (offset=%3, nTables=%4)").arg(faceIndex).arg(fontFile).arg(faceOffset).arg(nTables)); |
||
5979 | avox | 543 | uint headerLength = OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * nTables; |
544 | uint tableLengths = 0; |
||
545 | // sum table lengths incl padding |
||
13345 | pierre | 546 | for (uint i=0; i < nTables; ++i) |
5979 | avox | 547 | { |
13345 | pierre | 548 | tableLengths += word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 12); |
549 | tableLengths = (tableLengths+3) & ~3; |
||
5979 | avox | 550 | } |
13345 | pierre | 551 | bb.resize(headerLength + tableLengths); |
552 | if (! bb.data()) |
||
5979 | avox | 553 | return; |
554 | // write header |
||
13345 | pierre | 555 | sDebug(QObject::tr("memcpy header: %1 %2 %3").arg(0).arg(faceOffset).arg(headerLength)); |
556 | if (!copy(bb, 0, coll, faceOffset, headerLength)) |
||
5979 | avox | 557 | return; |
558 | |||
559 | uint pos = headerLength; |
||
13345 | pierre | 560 | for (uint i=0; i < nTables; ++i) |
5979 | avox | 561 | { |
13345 | pierre | 562 | uint tableSize = word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 12); |
563 | uint tableStart = word(coll, faceOffset + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8); |
||
564 | sDebug(QObject::tr("table '%1'").arg(tag(coll, tableStart))); |
||
565 | sDebug(QObject::tr("memcpy table: %1 %2 %3").arg(pos).arg(tableStart).arg(tableSize)); |
||
566 | if (!copy(bb, pos, coll, tableStart, tableSize)) break; |
||
5979 | avox | 567 | // write new offset to table entry |
13345 | pierre | 568 | sDebug(QObject::tr("memcpy offset: %1 %2 %3").arg(OFFSET_TABLE_LEN + TDIR_ENTRY_LEN*i + 8).arg(pos).arg(4)); |
569 | memcpy(bb.data() + OFFSET_TABLE_LEN + TDIR_ENTRY_LEN * i + 8, &pos, 4); |
||
5979 | avox | 570 | pos += tableSize; |
571 | // pad |
||
13345 | pierre | 572 | while ((pos & 3) != 0) |
573 | bb.data()[pos++] = '\0'; |
||
5979 | avox | 574 | } |
13345 | pierre | 575 | } |
576 | else if (formatCode == ScFace::TYPE42) { |
||
577 | FtFace::RawData(bb); |
||
5979 | avox | 578 | } |
13345 | pierre | 579 | else { |
580 | FtFace::RawData(bb); |
||
5979 | avox | 581 | } |
582 | } |
||
583 | |||
13345 | pierre | 584 | bool ScFace_ttf::EmbedFont(QString &str) const |
5979 | avox | 585 | { |
13345 | pierre | 586 | if (formatCode == ScFace::TYPE42) { |
5979 | avox | 587 | //easy: |
588 | QByteArray bb; |
||
13345 | pierre | 589 | FtFace::RawData(bb); |
5979 | avox | 590 | str += bb; |
591 | return true; |
||
592 | } |
||
593 | QString tmp4; |
||
594 | QString tmp2 = ""; |
||
595 | QString tmp3 = ""; |
||
596 | int counter = 0; |
||
597 | char *buf[50]; |
||
598 | FT_ULong charcode; |
||
599 | FT_UInt gindex; |
||
600 | FT_Face face = ftFace(); |
||
13345 | pierre | 601 | if (!face) { |
5979 | avox | 602 | return false; |
603 | } |
||
604 | const FT_Stream fts = face->stream; |
||
13345 | pierre | 605 | if (ftIOFunc(fts, 0L, NULL, 0)) { |
606 | return(false); |
||
5979 | avox | 607 | } |
608 | str+="%!PS-TrueTypeFont\n"; |
||
609 | str+="11 dict begin\n"; |
||
610 | str+="/FontName /" + psName + " def\n"; |
||
611 | str+="/Encoding /ISOLatin1Encoding where {pop ISOLatin1Encoding} {StandardEncoding} ifelse def\n"; |
||
612 | str+="/PaintType 0 def\n/FontMatrix [1 0 0 1 0 0] def\n"; |
||
17777 | jghali | 613 | str+="/FontBBox ["+m_pdfFontBBox+"] def\n"; |
5979 | avox | 614 | str+="/FontType 42 def\n"; |
615 | str+="/FontInfo 8 dict dup begin\n"; |
||
616 | str+="/FamilyName (" + psName + ") def\n"; |
||
617 | str+="end readonly def\n"; |
||
618 | unsigned char *tmp = new unsigned char[65535]; |
||
619 | int length; |
||
620 | char linebuf[80]; |
||
621 | str += "/sfnts ["; |
||
622 | int poso=0; |
||
13345 | pierre | 623 | do { |
5979 | avox | 624 | int posi=0; |
625 | length= fts->size - fts->pos; |
||
13345 | pierre | 626 | if (length > 65534) { |
5979 | avox | 627 | length = 65534; |
628 | } |
||
13345 | pierre | 629 | if (!ftIOFunc(fts, 0L, tmp, length)) |
5979 | avox | 630 | { |
631 | str+="\n<\n"; |
||
13345 | pierre | 632 | for (int j = 0; j < length; j++) |
5979 | avox | 633 | { |
634 | unsigned char u=tmp[posi]; |
||
13345 | pierre | 635 | linebuf[poso]=((u >> 4) & 15) + '0'; |
636 | if(u>0x9f) linebuf[poso]+='a'-':'; |
||
5979 | avox | 637 | ++poso; |
638 | u&=15; linebuf[poso]=u + '0'; |
||
13345 | pierre | 639 | if(u>0x9) linebuf[poso]+='a'-':'; |
5979 | avox | 640 | ++posi; |
641 | ++poso; |
||
13345 | pierre | 642 | if (poso > 70) |
5979 | avox | 643 | { |
644 | linebuf[poso++]='\n'; |
||
645 | linebuf[poso++]=0; |
||
646 | str += linebuf; |
||
647 | poso = 0; |
||
648 | } |
||
649 | } |
||
650 | linebuf[poso++]=0; |
||
651 | str += linebuf; |
||
652 | poso = 0; |
||
653 | str += "00\n>"; |
||
654 | } |
||
13345 | pierre | 655 | else { |
656 | sDebug(QObject::tr("Font %1 is broken (read stream), no embedding").arg(fontFile)); |
||
5979 | avox | 657 | str += "\n] def\n"; |
13345 | pierre | 658 | status = qMax(status,ScFace::BROKENGLYPHS); |
5979 | avox | 659 | return false; |
660 | } |
||
13345 | pierre | 661 | } while (length==65534); |
662 | |||
5979 | avox | 663 | str += "\n] def\n"; |
9556 | avox | 664 | delete[] tmp; |
5979 | avox | 665 | gindex = 0; |
13345 | pierre | 666 | charcode = FT_Get_First_Char(face, &gindex ); |
667 | while (gindex != 0) |
||
5979 | avox | 668 | { |
13345 | pierre | 669 | FT_Get_Glyph_Name(face, gindex, buf, 50); |
670 | tmp2 += "/"+QString(reinterpret_cast<char*>(buf))+" "+tmp3.setNum(gindex)+" def\n"; |
||
671 | charcode = FT_Get_Next_Char(face, charcode, &gindex ); |
||
5979 | avox | 672 | counter++; |
673 | } |
||
13345 | pierre | 674 | tmp4.setNum(counter); |
5979 | avox | 675 | str += "/CharStrings " + tmp4 + " dict dup begin\n"+tmp2; |
676 | str += "end readonly def\n"; |
||
677 | str += "FontName currentdict end definefont pop\n"; |
||
13345 | pierre | 678 | return(true); |
5979 | avox | 679 | } |
680 |