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