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