1 | /* |
2 | * KFontInst - KDE Font Installer |
3 | * |
4 | * Copyright 2003-2007 Craig Drummond <craig@kde.org> |
5 | * |
6 | * ---- |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify |
9 | * it under the terms of the GNU General Public License as published by |
10 | * the Free Software Foundation; either version 2 of the License, or |
11 | * (at your option) any later version. |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU General Public License |
19 | * along with this program; see the file COPYING. If not, write to |
20 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
21 | * Boston, MA 02110-1301, USA. |
22 | */ |
23 | |
24 | // |
25 | // This class contains code inspired/copied/nicked from mkfontscale. Specifically |
26 | // the getName(), lookupName(), and the getFoundry() routines... |
27 | // |
28 | // mkfontscale's (C) notice is: |
29 | /* |
30 | Copyright (c) 2002 by Juliusz Chroboczek |
31 | |
32 | Permission is hereby granted, free of charge, to any person obtaining a copy |
33 | of this software and associated documentation files (the "Software"), to deal |
34 | in the Software without restriction, including without limitation the rights |
35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
36 | copies of the Software, and to permit persons to whom the Software is |
37 | furnished to do so, subject to the following conditions: |
38 | |
39 | The above copyright notice and this permission notice shall be included in |
40 | all copies or substantial portions of the Software. |
41 | |
42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
44 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
45 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
46 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
47 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
48 | THE SOFTWARE. |
49 | */ |
50 | |
51 | #include "FontEngine.h" |
52 | #include "Misc.h" |
53 | #include "Fc.h" |
54 | #include <KDE/KGlobal> |
55 | #include <kascii.h> |
56 | #include <ctype.h> |
57 | #include <string.h> |
58 | #include <stdlib.h> |
59 | #include <stdio.h> |
60 | #include <iostream> |
61 | #include <sys/types.h> |
62 | #include <sys/stat.h> |
63 | #include <fcntl.h> |
64 | #include <ft2build.h> |
65 | #include FT_SFNT_NAMES_H |
66 | #include FT_TRUETYPE_IDS_H |
67 | #include FT_TRUETYPE_TABLES_H |
68 | #include FT_TYPE1_TABLES_H |
69 | #include <QtCore/QBuffer> |
70 | #include <QtCore/QTextStream> |
71 | |
72 | namespace KFI |
73 | { |
74 | |
75 | static unsigned long ftStreamRead(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count) |
76 | { |
77 | QByteArray *in((QByteArray *)stream->descriptor.pointer); |
78 | |
79 | if((offset+count)<=(unsigned long)in->size()) |
80 | { |
81 | memcpy(buffer, &(in->data()[offset]), count); |
82 | return count; |
83 | } |
84 | |
85 | return 0; |
86 | } |
87 | |
88 | static FT_Error openFtFace(FT_Library library, QByteArray &in, FT_Long index, FT_Face *face) |
89 | { |
90 | FT_Open_Args args; |
91 | FT_Stream stream; |
92 | FT_Error error; |
93 | |
94 | if(NULL==(stream=(FT_Stream)calloc(1, sizeof(*stream)))) |
95 | return FT_Err_Out_Of_Memory; |
96 | |
97 | stream->descriptor.pointer = ∈ |
98 | stream->pathname.pointer = NULL; |
99 | stream->size = in.size(); |
100 | stream->pos = 0; |
101 | |
102 | stream->read = ftStreamRead; |
103 | args.flags = FT_OPEN_STREAM; |
104 | args.stream = stream; |
105 | |
106 | error = FT_Open_Face(library, &args, index, face); |
107 | |
108 | if (FT_Err_Ok!=error) |
109 | free(stream); |
110 | else |
111 | (*face)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM; |
112 | |
113 | return error; |
114 | } |
115 | |
116 | // TTF : 00 01 00 00 00 5 |
117 | // : FFIL 4 |
118 | // : <65> FFIL 69 |
119 | // TTC : ttcf 4 |
120 | // OTF : OTTO 4 |
121 | // Type1: %!PS-AdobeFont-1. 17 |
122 | // : ?? ?? ?? ?? ?? ?? %!PS-AdobeFont-1. 23 |
123 | // : %!FontType1-1. 14 |
124 | // : ?? ?? ?? ?? ?? ?? %!FontType1-1. 20 |
125 | // : LWFN 4 |
126 | // : <65> LWFN 69 |
127 | // AFM : StartFontMetrics 16 |
128 | // BDF : STARTFONT 20 10 |
129 | // PCF : 01 fcp 4 |
130 | static const int =69; |
131 | |
132 | CFontEngine::EType CFontEngine::getType(const char *fileName) |
133 | { |
134 | EType type(TYPE_UNKNOWN); |
135 | int fd=::open(fileName, O_RDONLY); |
136 | |
137 | if(-1!=fd) |
138 | { |
139 | unsigned char [constHeaderLen]; |
140 | |
141 | if(constHeaderLen==::read(fd, header, constHeaderLen)) |
142 | type=getType(fileName, header); |
143 | ::close(fd); |
144 | } |
145 | |
146 | return type; |
147 | } |
148 | |
149 | CFontEngine::EType CFontEngine::getType(const char *fileName, Strigi::InputStream *in) |
150 | { |
151 | Q_ASSERT( in ); |
152 | |
153 | static const int =69; |
154 | const char * h; |
155 | int n=in->read(h, constHeaderLen, constHeaderLen); |
156 | |
157 | in->reset(0); |
158 | |
159 | return getType(fileName, n==constHeaderLen ? (const unsigned char *)h : NULL); |
160 | } |
161 | |
162 | CFontEngine::EType CFontEngine::getType(const char *fileName, const unsigned char *) |
163 | { |
164 | if(header) |
165 | { |
166 | if( (0x00==header[0] && 0x01==header[1] && 0x00==header[2] && 0x00==header[3] && 0x00==header[4]) || |
167 | ('F'==header[0] && 'F'==header[1] && 'I'==header[2] && 'L'==header[3]) || |
168 | ('F'==header[65] && 'F'==header[66] && 'I'==header[67] && 'L'==header[68])) |
169 | return TYPE_TTF; |
170 | |
171 | if('t'==header[0] && 't'==header[1] && 'c'==header[2] && 'f'==header[3]) |
172 | return TYPE_TTC; |
173 | |
174 | if('O'==header[0] && 'T'==header[1] && 'T'==header[2] && 'O'==header[3]) |
175 | return TYPE_OTF; |
176 | |
177 | if(0x01==header[0] && 'f'==header[1] && 'c'==header[2] && 'p'==header[3]) |
178 | return TYPE_PCF; |
179 | |
180 | if(0==memcmp(header, "STARTFONT" , 9) && 0x20==header[9]) |
181 | return TYPE_BDF; |
182 | |
183 | if(0==memcmp(header, "%!PS-AdobeFont-1." , 17) || |
184 | 0==memcmp(&header[6], "%!PS-AdobeFont-1." , 17) || |
185 | 0==memcmp(header, "%!FontType1-1." , 14) || |
186 | 0==memcmp(&header[6], "%!FontType1-1." , 14) || |
187 | ('L'==header[0] && 'W'==header[1] && 'F'==header[2] && 'N'==header[3]) || |
188 | ('L'==header[65] && 'W'==header[66] && 'F'==header[67] && 'N'==header[68])) |
189 | return TYPE_TYPE1; |
190 | |
191 | if(0==memcmp(header, "StartFontMetrics" , 16)) |
192 | return TYPE_AFM; |
193 | } |
194 | |
195 | // Right mime 'magic' failed, try by extension... |
196 | if(Misc::checkExt(fileName, "ttf" )) |
197 | return TYPE_TTF; |
198 | |
199 | if(Misc::checkExt(fileName, "ttc" )) |
200 | return TYPE_TTC; |
201 | |
202 | if(Misc::checkExt(fileName, "otf" )) |
203 | return TYPE_OTF; |
204 | |
205 | if(Misc::checkExt(fileName, "pfa" ) || Misc::checkExt(fileName, "pfb" )) |
206 | return TYPE_TYPE1; |
207 | |
208 | // |
209 | // NOTE: Do not accept .gz extension - strigi will decompress for us. |
210 | if(Misc::checkExt(fileName, "pcf" )) |
211 | return TYPE_PCF; |
212 | |
213 | if(Misc::checkExt(fileName, "bdf" )) |
214 | return TYPE_BDF; |
215 | |
216 | if(Misc::checkExt(fileName, "afm" )) |
217 | return TYPE_AFM; |
218 | |
219 | return TYPE_UNKNOWN; |
220 | } |
221 | |
222 | QString & CFontEngine::fixFoundry(QString &foundry) |
223 | { |
224 | // Try to make foundry similar to that of AFMs... |
225 | if(foundry==QString::fromLatin1("ibm" )) |
226 | foundry=QString::fromLatin1("IBM" ); |
227 | else if(foundry==QString::fromLatin1("urw" )) |
228 | foundry=QString::fromLatin1("URW" ); |
229 | else if(foundry==QString::fromLatin1("itc" )) |
230 | foundry=QString::fromLatin1("ITC" ); |
231 | else if(foundry==QString::fromLatin1("nec" )) |
232 | foundry=QString::fromLatin1("NEC" ); |
233 | else if(foundry==QString::fromLatin1("b&h" )) |
234 | foundry=QString::fromLatin1("B&H" ); |
235 | else |
236 | { |
237 | QChar *ch(foundry.data()); |
238 | int len(foundry.length()); |
239 | bool isSpace(true); |
240 | |
241 | while(len--) |
242 | { |
243 | if (isSpace) |
244 | *ch=ch->toUpper(); |
245 | |
246 | isSpace=ch->isSpace(); |
247 | ++ch; |
248 | } |
249 | } |
250 | |
251 | return foundry; |
252 | } |
253 | |
254 | bool CFontEngine::openFont(EType type, QByteArray &in, const char *fileName, int face) |
255 | { |
256 | bool ok=false; |
257 | |
258 | closeFont(); |
259 | |
260 | itsWeight=FC_WEIGHT_MEDIUM; |
261 | itsWidth=FC_WIDTH_NORMAL; |
262 | itsSpacing=FC_PROPORTIONAL; |
263 | itsItalic=FC_SLANT_ROMAN; |
264 | itsFamily=itsFoundry=itsVersion=QString(); |
265 | |
266 | if(in.size()<=0 && fileName && TYPE_UNKNOWN==type) |
267 | type=getType(fileName); // Read file header to obtain type... |
268 | |
269 | switch(type) |
270 | { |
271 | default: |
272 | ok=openFontFt(in, fileName, face, TYPE_TYPE1==type); |
273 | break; |
274 | #ifndef HAVE_FcFreeTypeQueryFace |
275 | case TYPE_PCF: |
276 | ok=openFontPcf(in); |
277 | break; |
278 | case TYPE_BDF: |
279 | ok=openFontBdf(in); |
280 | break; |
281 | #endif |
282 | case TYPE_AFM: |
283 | ok=openFontAfm(in); |
284 | case TYPE_UNKNOWN: |
285 | break; |
286 | } |
287 | |
288 | return ok; |
289 | } |
290 | |
291 | void CFontEngine::closeFont() |
292 | { |
293 | if(itsFt.open) |
294 | { |
295 | FT_Done_Face(itsFt.face); |
296 | itsFt.open=false; |
297 | } |
298 | } |
299 | |
300 | static int strToWeight(const QString &str) |
301 | { |
302 | if(str.isEmpty()) |
303 | return FC_WEIGHT_MEDIUM; |
304 | else if(str.contains("Bold" , Qt::CaseInsensitive)) |
305 | return FC_WEIGHT_BOLD; |
306 | else if(str.contains("Heavy" , Qt::CaseInsensitive)) |
307 | return FC_WEIGHT_HEAVY; |
308 | else if(str.contains("Black" , Qt::CaseInsensitive)) |
309 | return FC_WEIGHT_BLACK; |
310 | else if(str.contains("ExtraBold" , Qt::CaseInsensitive)) |
311 | return FC_WEIGHT_EXTRABOLD; |
312 | else if(str.contains("UltraBold" , Qt::CaseInsensitive)) |
313 | return FC_WEIGHT_ULTRABOLD; |
314 | else if(str.contains("ExtraLight" , Qt::CaseInsensitive)) |
315 | return FC_WEIGHT_EXTRALIGHT; |
316 | else if(str.contains("UltraLight" , Qt::CaseInsensitive)) |
317 | return FC_WEIGHT_ULTRALIGHT; |
318 | else if(str.contains("Light" , Qt::CaseInsensitive)) |
319 | return FC_WEIGHT_LIGHT; |
320 | else if(str.contains("Medium" , Qt::CaseInsensitive) || |
321 | str.contains("Normal" , Qt::CaseInsensitive) || |
322 | str.contains("Roman" , Qt::CaseInsensitive)) |
323 | return FC_WEIGHT_MEDIUM; |
324 | else if(str.contains("Regular" , Qt::CaseInsensitive)) |
325 | return FC_WEIGHT_REGULAR; |
326 | else if(str.contains("SemiBold" , Qt::CaseInsensitive)) |
327 | return FC_WEIGHT_SEMIBOLD; |
328 | else if(str.contains("DemiBold" , Qt::CaseInsensitive)) |
329 | return FC_WEIGHT_DEMIBOLD; |
330 | else if(str.contains("Thin" , Qt::CaseInsensitive)) |
331 | return FC_WEIGHT_THIN; |
332 | else if(str.contains("Book" , Qt::CaseInsensitive)) |
333 | return FC_WEIGHT_NORMAL; |
334 | else if(str.contains("Demi" , Qt::CaseInsensitive)) |
335 | return FC_WEIGHT_NORMAL; |
336 | else |
337 | return FC_WEIGHT_MEDIUM; |
338 | } |
339 | |
340 | static int strToWidth(const QString &str) |
341 | { |
342 | if(str.isEmpty()) |
343 | return KFI_FC_WIDTH_NORMAL; |
344 | else if(str.contains("UltraCondensed" , Qt::CaseInsensitive)) |
345 | return KFI_FC_WIDTH_ULTRACONDENSED; |
346 | else if(str.contains("ExtraCondensed" , Qt::CaseInsensitive)) |
347 | return KFI_FC_WIDTH_EXTRACONDENSED; |
348 | else if(str.contains("SemiCondensed" , Qt::CaseInsensitive)) |
349 | return KFI_FC_WIDTH_SEMICONDENSED; |
350 | else if(str.contains("Condensed" , Qt::CaseInsensitive)) |
351 | return KFI_FC_WIDTH_CONDENSED; |
352 | else if(str.contains("SemiExpanded" , Qt::CaseInsensitive)) |
353 | return KFI_FC_WIDTH_SEMIEXPANDED; |
354 | else if(str.contains("UltraExpanded" , Qt::CaseInsensitive)) |
355 | return KFI_FC_WIDTH_ULTRAEXPANDED; |
356 | else if(str.contains("ExtraExpanded" , Qt::CaseInsensitive)) |
357 | return KFI_FC_WIDTH_EXTRAEXPANDED; |
358 | else if(str.contains("Expanded" , Qt::CaseInsensitive)) |
359 | return KFI_FC_WIDTH_EXPANDED; |
360 | else |
361 | return KFI_FC_WIDTH_NORMAL; |
362 | } |
363 | |
364 | static int checkItalic(int it, const QString &full) |
365 | { |
366 | return (FC_SLANT_ITALIC==it && (-1!=full.indexOf("Oblique" ) || -1!=full.indexOf("Slanted" ))) |
367 | ? FC_SLANT_OBLIQUE |
368 | : it; |
369 | } |
370 | |
371 | static const char * getFoundry(const char *notice) |
372 | { |
373 | struct Map |
374 | { |
375 | const char *noticeStr, |
376 | *foundry; |
377 | }; |
378 | |
379 | static const Map map[]= |
380 | { |
381 | { "Bigelow" , "B&H" }, |
382 | { "Adobe" , "Adobe" }, |
383 | { "Bitstream" , "Bitstream" }, |
384 | { "Monotype" , "Monotype" }, |
385 | { "Linotype" , "Linotype" }, |
386 | { "LINOTYPE-HELL" , "Linotype" }, |
387 | { "IBM" , "IBM" }, |
388 | { "URW" , "URW" }, |
389 | { "International Typeface Corporation" , "ITC" }, |
390 | { "Tiro Typeworks" , "Tiro" }, |
391 | { "XFree86" , "XFree86" }, |
392 | { "Microsoft" , "Microsoft" }, |
393 | { "Omega" , "Omega" }, |
394 | { "Font21" , "Hwan" }, |
395 | { "HanYang System" , "Hanyang" }, |
396 | { NULL, NULL } |
397 | }; |
398 | |
399 | const Map *entry; |
400 | |
401 | if(notice) |
402 | for(entry=map; NULL!=entry->foundry; entry++) |
403 | if(strstr(notice, entry->noticeStr)!=NULL) |
404 | return entry->foundry; |
405 | |
406 | return NULL; |
407 | } |
408 | |
409 | #ifndef HAVE_FcFreeTypeQueryFace |
410 | static bool lookupName(FT_Face face, int nid, int pid, int eid, FT_SfntName *nameReturn) |
411 | { |
412 | int n = FT_Get_Sfnt_Name_Count(face); |
413 | |
414 | if(n>0) |
415 | { |
416 | int i; |
417 | FT_SfntName name; |
418 | |
419 | for(i=0; i<n; i++) |
420 | if(0==FT_Get_Sfnt_Name(face, i, &name) && name.name_id == nid && name.platform_id == pid && |
421 | (eid < 0 || name.encoding_id == eid)) |
422 | { |
423 | switch(name.platform_id) |
424 | { |
425 | case TT_PLATFORM_APPLE_UNICODE: |
426 | case TT_PLATFORM_MACINTOSH: |
427 | if(name.language_id != TT_MAC_LANGID_ENGLISH) |
428 | continue; |
429 | break; |
430 | case TT_PLATFORM_MICROSOFT: |
431 | if(name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES && |
432 | name.language_id != TT_MS_LANGID_ENGLISH_UNITED_KINGDOM) |
433 | continue; |
434 | break; |
435 | default: |
436 | continue; |
437 | } |
438 | |
439 | if(name.string_len > 0) |
440 | { |
441 | *nameReturn = name; |
442 | return true; |
443 | } |
444 | } |
445 | } |
446 | |
447 | return false; |
448 | } |
449 | |
450 | static QByteArray getName(FT_Face face, int nid) |
451 | { |
452 | FT_SfntName name; |
453 | QByteArray str; |
454 | |
455 | if(lookupName(face, nid, TT_PLATFORM_MICROSOFT, TT_MS_ID_UNICODE_CS, &name) || |
456 | lookupName(face, nid, TT_PLATFORM_APPLE_UNICODE, -1, &name)) |
457 | for(unsigned int i=0; i < name.string_len / 2; i++) |
458 | str+=0 == name.string[2*i] ? name.string[(2*i)+1] : '_'; |
459 | else if(lookupName(face, nid, TT_PLATFORM_MACINTOSH, TT_MAC_ID_ROMAN, &name)) // Pretend that Apple Roman is ISO 8859-1 |
460 | for(unsigned int i=0; i < name.string_len; i++) |
461 | str+=name.string[i]; |
462 | |
463 | return str; |
464 | } |
465 | |
466 | static const char * getFoundry(const FT_Face face, TT_OS2 *os2) |
467 | { |
468 | struct Map |
469 | { |
470 | const char *vendorId, |
471 | *foundry; |
472 | }; |
473 | |
474 | static const int constVendLen=4; |
475 | |
476 | // These are taken from ttmkfdir |
477 | static const Map map[]= |
478 | { |
479 | { "ADBE" , "Adobe" }, |
480 | { "AGFA" , "Agfa" }, |
481 | { "ALTS" , "Altsys" }, |
482 | { "APPL" , "Apple" }, |
483 | { "ARPH" , "Arphic" }, |
484 | { "ATEC" , "Alltype" }, |
485 | { "B&H" , "B&H" }, |
486 | { "BITS" , "Bitstream" }, |
487 | { "CANO" , "Cannon" }, |
488 | { "DYNA" , "DynaLab" }, |
489 | { "EPSN" , "Epson" }, |
490 | { "FJ" , "Fujitsu" }, |
491 | { "IBM" , "IBM" }, |
492 | { "ITC" , "ITC" }, |
493 | { "IMPR" , "Impress" }, |
494 | { "LARA" , "Larabie" }, |
495 | { "LEAF" , "Interleaf" }, |
496 | { "LETR" , "letraset" }, |
497 | { "LINO" , "Linotype" }, |
498 | { "MACR" , "Macromedia" }, |
499 | { "MONO" , "Monotype" }, |
500 | { "MS" , "Microsoft" }, |
501 | { "MT" , "Monotype" }, |
502 | { "NEC" , "NEC" }, |
503 | { "PARA" , "ParaType" }, |
504 | { "QMSI" , "QMS" }, |
505 | { "RICO" , "Ricoh" }, |
506 | { "URW" , "URW" }, |
507 | { "Y&Y" , "Z&Y" }, |
508 | { NULL , NULL} |
509 | }; |
510 | |
511 | static char vendor[constVendLen+1]; |
512 | |
513 | vendor[0]='\0'; |
514 | |
515 | if(NULL!=os2 && 0xFFFF!=os2->version) |
516 | { |
517 | const Map *entry; |
518 | char vend[constVendLen+1]; |
519 | |
520 | strncpy(vendor, (const char *)(os2->achVendID), constVendLen); |
521 | vendor[constVendLen]='\0'; |
522 | |
523 | for(int i=0; i<constVendLen; ++i) |
524 | vend[i]=toupper(vendor[i]); |
525 | |
526 | for(entry=map; NULL!=entry->vendorId; entry++) |
527 | { |
528 | unsigned int len=strlen(entry->vendorId); |
529 | |
530 | if(0==memcmp(entry->vendorId, vend, len)) |
531 | { |
532 | bool ok=true; |
533 | |
534 | for(int i=len; i<constVendLen && ok; i++) |
535 | if(vend[i]!=' ' && entry->vendorId[i]!='\0') |
536 | ok=false; |
537 | |
538 | if(ok) |
539 | return entry->foundry; |
540 | } |
541 | } |
542 | } |
543 | |
544 | const char *foundry=NULL; |
545 | |
546 | if(!foundry) |
547 | foundry=getFoundry(getName(face, TT_NAME_ID_TRADEMARK)); |
548 | |
549 | if(!foundry) |
550 | foundry=getFoundry(getName(face, TT_NAME_ID_MANUFACTURER)); |
551 | |
552 | if(!foundry && vendor[0] && !isspace(vendor[0]) && '-'!=vendor[0]) // Some fonts have a totally blank vendor field |
553 | { |
554 | int i; |
555 | |
556 | // Remove any dashes... |
557 | for(int i=constVendLen-1; i>0; i--) |
558 | if('-'==vendor[i]) |
559 | vendor[i]=' '; |
560 | |
561 | // Strip any trailing whitepace |
562 | for(i=constVendLen-1; i>0; i--) |
563 | if(isspace(vendor[i])) |
564 | vendor[i]='\0'; |
565 | else |
566 | break; |
567 | |
568 | foundry=vendor; |
569 | } |
570 | |
571 | return foundry ; |
572 | } |
573 | #endif |
574 | |
575 | bool CFontEngine::openFontFt(QByteArray &in, const char *fileName, int face, bool type1) |
576 | { |
577 | bool status=in.size()<=0 |
578 | ? FT_New_Face(itsFt.library, fileName, face, &itsFt.face) ? false : true |
579 | : openFtFace(itsFt.library, in, face, &itsFt.face) ? false : true; |
580 | |
581 | if(status) |
582 | itsFt.open=true; |
583 | |
584 | if(status) |
585 | { |
586 | PS_FontInfoRec t1info; |
587 | |
588 | if(type1) |
589 | FT_Get_PS_Font_Info(itsFt.face, &t1info); |
590 | |
591 | #ifdef HAVE_FcFreeTypeQueryFace |
592 | FcPattern *pat=FcFreeTypeQueryFace(itsFt.face, (FcChar8*) fileName, face, NULL); |
593 | |
594 | itsWeight=FC_WEIGHT_REGULAR; |
595 | itsWidth=KFI_FC_WIDTH_NORMAL; |
596 | itsSpacing=FC_PROPORTIONAL; |
597 | |
598 | if(pat) |
599 | { |
600 | itsFamily=FC::getFcString(pat, FC_FAMILY, face); |
601 | FcPatternGetInteger(pat, FC_WEIGHT, face, &itsWeight); |
602 | #ifndef KFI_FC_NO_WIDTHS |
603 | FcPatternGetInteger(pat, FC_WIDTH, face, &itsWidth); |
604 | #endif |
605 | FcPatternGetInteger(pat, FC_SLANT, face, &itsItalic); |
606 | FcPatternGetInteger(pat, FC_SPACING, face, &itsSpacing); |
607 | itsFoundry=FC::getFcString(pat, FC_FOUNDRY, face); |
608 | |
609 | if(type1) |
610 | itsVersion=t1info.version; |
611 | else |
612 | { |
613 | int version; |
614 | |
615 | FcPatternGetInteger(pat, FC_FONTVERSION, face, &version); |
616 | if(version>0) |
617 | itsVersion.setNum(decodeFixed(version)); |
618 | } |
619 | |
620 | FcPatternDestroy(pat); |
621 | fixFoundry(itsFoundry); |
622 | } |
623 | else |
624 | status=false; |
625 | |
626 | #else |
627 | |
628 | enum ETtfWidth |
629 | { |
630 | TTF_WIDTH_ULTRA_CONDENSED = 1, |
631 | TTF_WIDTH_EXTRA_CONDENSED = 2, |
632 | TTF_WIDTH_CONDENSED = 3, |
633 | TTF_WIDTH_SEMI_CONDENSED = 4, |
634 | TTF_WIDTH_NORMAL = 5, |
635 | TTF_WIDTH_SEMI_EXPANDED = 6, |
636 | TTF_WIDTH_EXPANDED = 7, |
637 | TTF_WIDTH_EXTRA_EXPANDED = 8, |
638 | TTF_WIDTH_ULTRA_EXPANDED = 9 |
639 | }; |
640 | |
641 | QString fullName; |
642 | |
643 | if(type1) |
644 | { |
645 | fullName=t1info.full_name; |
646 | itsFamily=t1info.family_name; |
647 | } |
648 | else |
649 | { |
650 | fullName=getName(itsFt.face, TT_NAME_ID_FULL_NAME); |
651 | itsFamily=getName(itsFt.face, TT_NAME_ID_FONT_FAMILY); |
652 | } |
653 | |
654 | if(itsFamily.isEmpty()) |
655 | if(fullName.isEmpty()) |
656 | itsFamily=fullName=FT_Get_Postscript_Name(itsFt.face); |
657 | else |
658 | itsFamily=fullName; |
659 | else |
660 | if(fullName.isEmpty()) |
661 | fullName=itsFamily; |
662 | |
663 | if(fullName.isEmpty()) |
664 | status=false; // Hmm... couldn't find any of the names! |
665 | |
666 | if(status) |
667 | { |
668 | if(type1) |
669 | { |
670 | itsWeight=strToWeight(t1info.weight); |
671 | itsItalic=t1info.italic_angle <= -4 || t1info.italic_angle >= 4 ? FC_SLANT_ITALIC : FC_SLANT_ROMAN; |
672 | itsWidth=strToWidth(fullName); |
673 | itsItalic=checkItalic(itsItalic, fullName); |
674 | itsSpacing=t1info.is_fixed_pitch ? FC_MONO : FC_PROPORTIONAL; |
675 | itsFoundry=KFI::getFoundry(t1info.notice); |
676 | itsVersion=t1info.version; |
677 | } |
678 | else // TrueType... |
679 | { |
680 | #define WEIGHT_UNKNOWN 0xFFFF |
681 | #define WIDTH_UNKNOWN 0xFFFF |
682 | |
683 | TT_Postscript *post=(TT_Postscript *)FT_Get_Sfnt_Table(itsFt.face, ft_sfnt_post); |
684 | TT_OS2 *os2=(TT_OS2 *)FT_Get_Sfnt_Table(itsFt.face, ft_sfnt_os2); |
685 | TT_Header *head=(TT_Header *)FT_Get_Sfnt_Table(itsFt.face, ft_sfnt_head); |
686 | bool gotItalic=false; |
687 | |
688 | itsWidth=WIDTH_UNKNOWN; |
689 | itsWeight=WEIGHT_UNKNOWN; |
690 | |
691 | if(NULL!=os2 && 0xFFFF!=os2->version) |
692 | { |
693 | if (os2->usWeightClass == 0) |
694 | ; |
695 | else if (os2->usWeightClass < 150) |
696 | itsWeight=FC_WEIGHT_THIN; |
697 | else if (os2->usWeightClass < 250) |
698 | itsWeight=FC_WEIGHT_EXTRALIGHT; |
699 | else if (os2->usWeightClass < 350) |
700 | itsWeight=FC_WEIGHT_LIGHT; |
701 | else if (os2->usWeightClass < 450) |
702 | itsWeight=FC_WEIGHT_REGULAR; |
703 | else if (os2->usWeightClass < 550) |
704 | itsWeight=FC_WEIGHT_MEDIUM; |
705 | else if (os2->usWeightClass < 650) |
706 | itsWeight=FC_WEIGHT_SEMIBOLD; |
707 | else if (os2->usWeightClass < 750) |
708 | itsWeight=FC_WEIGHT_BOLD; |
709 | else if (os2->usWeightClass < 850) |
710 | itsWeight=FC_WEIGHT_EXTRABOLD; |
711 | else if (os2->usWeightClass < 950) |
712 | itsWeight=FC_WEIGHT_BLACK; |
713 | |
714 | switch(os2->usWidthClass) |
715 | { |
716 | case TTF_WIDTH_ULTRA_CONDENSED: |
717 | itsWidth=FC_WIDTH_ULTRACONDENSED; |
718 | break; |
719 | case TTF_WIDTH_EXTRA_CONDENSED: |
720 | itsWidth=FC_WIDTH_EXTRACONDENSED; |
721 | break; |
722 | case TTF_WIDTH_CONDENSED: |
723 | itsWidth=FC_WIDTH_CONDENSED; |
724 | break; |
725 | case TTF_WIDTH_SEMI_CONDENSED: |
726 | itsWidth=FC_WIDTH_SEMICONDENSED; |
727 | break; |
728 | case TTF_WIDTH_NORMAL: |
729 | itsWidth=FC_WIDTH_NORMAL; |
730 | break; |
731 | case TTF_WIDTH_SEMI_EXPANDED: |
732 | itsWidth=FC_WIDTH_SEMIEXPANDED; |
733 | break; |
734 | case TTF_WIDTH_EXPANDED: |
735 | itsWidth=FC_WIDTH_EXPANDED; |
736 | break; |
737 | case TTF_WIDTH_EXTRA_EXPANDED: |
738 | itsWidth=FC_WIDTH_EXTRAEXPANDED; |
739 | break; |
740 | case TTF_WIDTH_ULTRA_EXPANDED: |
741 | itsWidth=FC_WIDTH_ULTRAEXPANDED; |
742 | break; |
743 | } |
744 | |
745 | itsItalic=os2->fsSelection&(1 << 0) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN; |
746 | gotItalic=true; |
747 | } |
748 | |
749 | if(WEIGHT_UNKNOWN==itsWeight) |
750 | itsWeight=itsFt.face->style_flags&FT_STYLE_FLAG_BOLD |
751 | ? FC_WEIGHT_BOLD |
752 | : FC_WEIGHT_MEDIUM; |
753 | |
754 | if(WIDTH_UNKNOWN==itsWidth) |
755 | itsWidth=FC_WIDTH_NORMAL; |
756 | |
757 | if(!gotItalic && head!=NULL) |
758 | { |
759 | gotItalic=true; |
760 | itsItalic=head->Mac_Style & 2 ? FC_SLANT_ITALIC : FC_SLANT_ROMAN; |
761 | } |
762 | |
763 | if(head) |
764 | { |
765 | double version=decodeFixed(head->Font_Revision); |
766 | |
767 | if(version>0) |
768 | itsVersion.setNum(version); |
769 | } |
770 | |
771 | if(!gotItalic && NULL!=post) |
772 | { |
773 | gotItalic=true; |
774 | itsItalic=0.0f==decodeFixed(post->italicAngle) ? FC_SLANT_ROMAN : FC_SLANT_ITALIC; |
775 | } |
776 | |
777 | itsItalic=checkItalic(itsItalic, fullName); |
778 | |
779 | if(NULL!=post && post->isFixedPitch) |
780 | { |
781 | TT_HoriHeader *hhea=NULL; |
782 | |
783 | if(NULL!=(hhea=(TT_HoriHeader *)FT_Get_Sfnt_Table(itsFt.face, ft_sfnt_hhea)) && |
784 | hhea->min_Left_Side_Bearing >= 0 && hhea->xMax_Extent <= hhea->advance_Width_Max) |
785 | itsSpacing=FC_CHARCELL; |
786 | else |
787 | itsSpacing=FC_MONO; |
788 | } |
789 | else |
790 | itsSpacing=FC_PROPORTIONAL; |
791 | |
792 | itsFoundry=KFI::getFoundry(itsFt.face, os2); |
793 | } |
794 | } |
795 | #endif |
796 | } |
797 | if(!status) |
798 | closeFont(); |
799 | |
800 | return status; |
801 | } |
802 | |
803 | bool CFontEngine::openFontAfm(QByteArray &in) |
804 | { |
805 | bool inMetrics=false; |
806 | QString full; |
807 | QTextStream ds(&in, QIODevice::ReadOnly); |
808 | |
809 | while(!ds.atEnd()) |
810 | { |
811 | QString line(ds.readLine()); |
812 | line=line.simplified(); |
813 | |
814 | if(inMetrics) |
815 | { |
816 | if(0==line.indexOf("FullName " )) |
817 | { |
818 | full=line.mid(9); |
819 | itsWidth=strToWidth(full); |
820 | } |
821 | else if(0==line.indexOf("FamilyName " )) |
822 | itsFamily=line.mid(11); |
823 | else if(0==line.indexOf("Weight " )) |
824 | itsWeight=strToWeight(line.mid(7)); |
825 | else if(0==line.indexOf("ItalicAngle " )) |
826 | itsItalic=0.0f==line.mid(12).toFloat() ? FC_SLANT_ROMAN : FC_SLANT_ITALIC; |
827 | else if(0==line.indexOf("IsFixedPitch " )) |
828 | itsSpacing= ( line.mid(13).contains("false" , Qt::CaseInsensitive) |
829 | ? FC_PROPORTIONAL : FC_MONO ); |
830 | else if(0==line.indexOf("Notice " )) |
831 | itsFoundry=KFI::getFoundry(line.mid(7).toLatin1()); |
832 | else if(0==line.indexOf("Version " )) |
833 | itsVersion=line.mid(8); |
834 | else if(0==line.indexOf("StartCharMetrics" )) |
835 | break; |
836 | } |
837 | else |
838 | if(0==line.indexOf("StartFontMetrics" )) |
839 | inMetrics=true; |
840 | }; |
841 | |
842 | if(itsFamily.isEmpty() && !full.isEmpty()) |
843 | itsFamily=full; |
844 | |
845 | checkItalic(itsItalic, full); |
846 | |
847 | return !full.isEmpty() && !itsFamily.isEmpty(); |
848 | } |
849 | |
850 | #ifndef HAVE_FcFreeTypeQueryFace |
851 | static int charToItalic(char c) |
852 | { |
853 | switch(c) |
854 | { |
855 | case 'i': |
856 | case 'I': |
857 | return FC_SLANT_ITALIC; |
858 | case 'o': |
859 | case 'O': |
860 | return FC_SLANT_OBLIQUE; |
861 | case 'r': |
862 | case 'R': |
863 | default: |
864 | return FC_SLANT_ROMAN; |
865 | } |
866 | } |
867 | |
868 | static int charToSpacing(char c) |
869 | { |
870 | switch(c) |
871 | { |
872 | case 'm': |
873 | case 'M': |
874 | return FC_MONO; |
875 | case 'c': |
876 | case 'C': |
877 | return FC_CHARCELL; |
878 | default: |
879 | case 'p': |
880 | case 'P': |
881 | return FC_PROPORTIONAL; |
882 | } |
883 | } |
884 | |
885 | void CFontEngine::parseXlfdBmp(const QString &xlfd) |
886 | { |
887 | enum EXlfd |
888 | { |
889 | XLFD_FOUNDRY=0, |
890 | XLFD_FAMILY, |
891 | XLFD_WEIGHT, |
892 | XLFD_SLANT, |
893 | XLFD_WIDTH, |
894 | XLFD_STYLE, |
895 | XLFD_PIXEL_SIZE, |
896 | XLFD_POINT_SIZE, |
897 | XLFD_RESX, |
898 | XLFD_RESY, |
899 | XLFD_SPACING, |
900 | XLFD_AV_WIDTH, |
901 | XLFD_ENCODING, |
902 | XLFD_END |
903 | }; |
904 | |
905 | int pos=0, |
906 | oldPos=1; |
907 | int entry; |
908 | |
909 | // XLFD: |
910 | // -foundry-family-weight-slant-width-?-pixelSize-pointSize-resX-resY-spacing-avWidth-csReg-csEnc |
911 | for(entry=XLFD_FOUNDRY; -1!=(pos=xlfd.indexOf('-', pos+1)) && entry<XLFD_END; ++entry) |
912 | { |
913 | switch(entry) |
914 | { |
915 | default: |
916 | break; |
917 | case XLFD_FOUNDRY: |
918 | itsFoundry=xlfd.mid(oldPos, pos-oldPos); |
919 | break; |
920 | case XLFD_FAMILY: |
921 | itsFamily=xlfd.mid(oldPos, pos-oldPos); |
922 | break; |
923 | case XLFD_WEIGHT: |
924 | itsWeight=strToWeight(xlfd.mid(oldPos, pos-oldPos).toLocal8Bit()); |
925 | break; |
926 | case XLFD_SLANT: |
927 | if(pos>0) |
928 | itsItalic=charToItalic(xlfd[pos-1].toLatin1()); |
929 | break; |
930 | case XLFD_WIDTH: |
931 | itsWidth=strToWidth(xlfd.mid(oldPos, pos-oldPos)); |
932 | break; |
933 | case XLFD_STYLE: |
934 | case XLFD_PIXEL_SIZE: |
935 | case XLFD_POINT_SIZE: |
936 | case XLFD_RESX: |
937 | case XLFD_RESY: |
938 | break; |
939 | case XLFD_SPACING: |
940 | if(pos>0) |
941 | itsSpacing=charToSpacing(xlfd[pos-1].toLatin1()); |
942 | break; |
943 | case XLFD_AV_WIDTH: |
944 | case XLFD_ENCODING: |
945 | break; |
946 | } |
947 | |
948 | oldPos=pos+1; |
949 | } |
950 | } |
951 | |
952 | bool CFontEngine::openFontBdf(QByteArray &in) |
953 | { |
954 | QTextStream ds(&in, QIODevice::ReadOnly); |
955 | |
956 | ds.readLine(); // Skip 1st line. |
957 | while(!ds.atEnd()) |
958 | { |
959 | QString line(ds.readLine()); |
960 | |
961 | if(0==line.indexOf("FONT " , Qt::CaseInsensitive)) |
962 | { |
963 | parseXlfdBmp(line.mid(5)); |
964 | return true; |
965 | } |
966 | } |
967 | |
968 | return false; |
969 | } |
970 | |
971 | static unsigned int readLsb32(QBuffer &in) |
972 | { |
973 | unsigned char num[4]; |
974 | |
975 | return 4==in.read((char *)num, 4) |
976 | ? (num[0])+(num[1]<<8)+(num[2]<<16)+(num[3]<<24) |
977 | : 0; |
978 | } |
979 | |
980 | static unsigned int read32(QBuffer &in, bool msb) |
981 | { |
982 | if(msb) |
983 | { |
984 | unsigned char num[4]; |
985 | |
986 | return 4==in.read((char *)num, 4) |
987 | ? (num[0]<<24)+(num[1]<<16)+(num[2]<<8)+(num[3]) |
988 | : 0; |
989 | } |
990 | |
991 | return readLsb32(in); |
992 | } |
993 | |
994 | static const unsigned int constBitmapMaxProps=1024; |
995 | |
996 | bool CFontEngine::openFontPcf(QByteArray &in) |
997 | { |
998 | bool foundXlfd=false; |
999 | const unsigned int contPcfVersion=(('p'<<24)|('c'<<16)|('f'<<8)|1); |
1000 | QBuffer buf; |
1001 | |
1002 | buf.setBuffer(&in); |
1003 | buf.open(QIODevice::ReadOnly); |
1004 | |
1005 | if(contPcfVersion==readLsb32(buf)) |
1006 | { |
1007 | const unsigned int constPropertiesType=1; |
1008 | |
1009 | unsigned int numTables=readLsb32(buf), |
1010 | table, |
1011 | type, |
1012 | format, |
1013 | size, |
1014 | offset; |
1015 | |
1016 | for(table=0; table<numTables && !buf.atEnd() && !foundXlfd; ++table) |
1017 | { |
1018 | type=readLsb32(buf); |
1019 | format=readLsb32(buf); |
1020 | size=readLsb32(buf); |
1021 | offset=readLsb32(buf); |
1022 | if(constPropertiesType==type) |
1023 | { |
1024 | if(buf.seek(offset)) |
1025 | { |
1026 | const unsigned int constFormatMask=0xffffff00; |
1027 | |
1028 | format=readLsb32(buf); |
1029 | if(0==(format&constFormatMask)) |
1030 | { |
1031 | const unsigned int constByteMask=0x4; |
1032 | |
1033 | bool msb=format&constByteMask; |
1034 | unsigned int numProps=read32(buf, msb); |
1035 | |
1036 | if(numProps>0 && numProps<constBitmapMaxProps) |
1037 | { |
1038 | unsigned int strSize, |
1039 | skip; |
1040 | |
1041 | struct TProp |
1042 | { |
1043 | unsigned int name, |
1044 | value; |
1045 | bool isString; |
1046 | } *props=new struct TProp [numProps]; |
1047 | |
1048 | if(props) |
1049 | { |
1050 | char tmp; |
1051 | unsigned short prop; |
1052 | |
1053 | for(prop=0; prop<numProps; ++prop) |
1054 | { |
1055 | props[prop].name=read32(buf, msb); |
1056 | buf.read(&tmp, 1); |
1057 | props[prop].isString=tmp ? true : false; |
1058 | props[prop].value=read32(buf, msb); |
1059 | } |
1060 | |
1061 | skip=4-((numProps*9)%4); |
1062 | if(skip!=4) |
1063 | buf.seek(buf.pos()+skip); |
1064 | |
1065 | strSize=read32(buf, msb); |
1066 | |
1067 | if(strSize>0) |
1068 | { |
1069 | QByteArray str=buf.read(strSize); |
1070 | |
1071 | if(str.size()==(int)strSize) |
1072 | { |
1073 | // Finally we have the data............ |
1074 | const int constMaxStrLen=1024; |
1075 | |
1076 | char tmp[constMaxStrLen]; |
1077 | |
1078 | for(prop=0; prop<numProps && !foundXlfd; ++prop) |
1079 | if(kasciistricmp(&(str.data()[props[prop].name]), "FONT" )==0) |
1080 | { |
1081 | if(props[prop].isString && strlen(&(str.data()[props[prop].value]))) |
1082 | { |
1083 | foundXlfd=true; |
1084 | strncpy(tmp, &(str.data()[props[prop].value]), constMaxStrLen); |
1085 | tmp[constMaxStrLen-1]='\0'; |
1086 | parseXlfdBmp(tmp); |
1087 | } |
1088 | break; |
1089 | } |
1090 | } |
1091 | } |
1092 | delete [] props; |
1093 | } |
1094 | } |
1095 | } |
1096 | } |
1097 | break; // Forget the other tables... |
1098 | } |
1099 | } |
1100 | } |
1101 | |
1102 | return foundXlfd; |
1103 | } |
1104 | #endif |
1105 | |
1106 | CFontEngine::TFtData::TFtData() |
1107 | : open(false) |
1108 | { |
1109 | if(FT_Init_FreeType(&library)) |
1110 | { |
1111 | std::cerr << "ERROR: FreeType2 failed to initialise\n" ; |
1112 | exit(0); |
1113 | } |
1114 | } |
1115 | |
1116 | CFontEngine::TFtData::~TFtData() |
1117 | { |
1118 | FT_Done_FreeType(library); |
1119 | } |
1120 | |
1121 | } |
1122 | |