not quite so much needs to be delayed to the init() function
[personal-kdebase.git] / workspace / kcontrol / kfontinst / strigi-analyzer / FontEngine.cpp
blob8e00bd550c9e2a3e1b25ef6cf6a3d510576e67d8
1 /*
2 * KFontInst - KDE Font Installer
4 * Copyright 2003-2007 Craig Drummond <craig@kde.org>
6 * ----
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.
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.
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.
25 // This class contains code inspired/copied/nicked from mkfontscale. Specifically
26 // the getName(), lookupName(), and the getFoundry() routines...
28 // mkfontscale's (C) notice is:
30 Copyright (c) 2002 by Juliusz Chroboczek
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:
39 The above copyright notice and this permission notice shall be included in
40 all copies or substantial portions of the Software.
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.
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>
72 namespace KFI
75 static unsigned long ftStreamRead(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count)
77 QByteArray *in((QByteArray *)stream->descriptor.pointer);
79 if((offset+count)<=(unsigned long)in->size())
81 memcpy(buffer, &(in->data()[offset]), count);
82 return count;
85 return 0;
88 static FT_Error openFtFace(FT_Library library, QByteArray &in, FT_Long index, FT_Face *face)
90 FT_Open_Args args;
91 FT_Stream stream;
92 FT_Error error;
94 if(NULL==(stream=(FT_Stream)calloc(1, sizeof(*stream))))
95 return FT_Err_Out_Of_Memory;
97 stream->descriptor.pointer = &in;
98 stream->pathname.pointer = NULL;
99 stream->size = in.size();
100 stream->pos = 0;
102 stream->read = ftStreamRead;
103 args.flags = FT_OPEN_STREAM;
104 args.stream = stream;
106 error = FT_Open_Face(library, &args, index, face);
108 if (FT_Err_Ok!=error)
109 free(stream);
110 else
111 (*face)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;
113 return error;
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 constHeaderLen=69;
132 CFontEngine::EType CFontEngine::getType(const char *fileName)
134 EType type(TYPE_UNKNOWN);
135 int fd=::open(fileName, O_RDONLY);
137 if(-1!=fd)
139 unsigned char header[constHeaderLen];
141 if(constHeaderLen==::read(fd, header, constHeaderLen))
142 type=getType(fileName, header);
143 ::close(fd);
146 return type;
149 CFontEngine::EType CFontEngine::getType(const char *fileName, Strigi::InputStream *in)
151 Q_ASSERT( in );
153 static const int constHeaderLen=69;
154 const char * h;
155 int n=in->read(h, constHeaderLen, constHeaderLen);
157 in->reset(0);
159 return getType(fileName, n==constHeaderLen ? (const unsigned char *)h : NULL);
162 CFontEngine::EType CFontEngine::getType(const char *fileName, const unsigned char *header)
164 if(header)
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;
171 if('t'==header[0] && 't'==header[1] && 'c'==header[2] && 'f'==header[3])
172 return TYPE_TTC;
174 if('O'==header[0] && 'T'==header[1] && 'T'==header[2] && 'O'==header[3])
175 return TYPE_OTF;
177 if(0x01==header[0] && 'f'==header[1] && 'c'==header[2] && 'p'==header[3])
178 return TYPE_PCF;
180 if(0==memcmp(header, "STARTFONT", 9) && 0x20==header[9])
181 return TYPE_BDF;
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;
191 if(0==memcmp(header, "StartFontMetrics", 16))
192 return TYPE_AFM;
195 // Right mime 'magic' failed, try by extension...
196 if(Misc::checkExt(fileName, "ttf"))
197 return TYPE_TTF;
199 if(Misc::checkExt(fileName, "ttc"))
200 return TYPE_TTC;
202 if(Misc::checkExt(fileName, "otf"))
203 return TYPE_OTF;
205 if(Misc::checkExt(fileName, "pfa") || Misc::checkExt(fileName, "pfb"))
206 return TYPE_TYPE1;
209 // NOTE: Do not accept .gz extension - strigi will decompress for us.
210 if(Misc::checkExt(fileName, "pcf"))
211 return TYPE_PCF;
213 if(Misc::checkExt(fileName, "bdf"))
214 return TYPE_BDF;
216 if(Misc::checkExt(fileName, "afm"))
217 return TYPE_AFM;
219 return TYPE_UNKNOWN;
222 QString & CFontEngine::fixFoundry(QString &foundry)
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
237 QChar *ch(foundry.data());
238 int len(foundry.length());
239 bool isSpace(true);
241 while(len--)
243 if (isSpace)
244 *ch=ch->toUpper();
246 isSpace=ch->isSpace();
247 ++ch;
251 return foundry;
254 bool CFontEngine::openFont(EType type, QByteArray &in, const char *fileName, int face)
256 bool ok=false;
258 closeFont();
260 itsWeight=FC_WEIGHT_MEDIUM;
261 itsWidth=FC_WIDTH_NORMAL;
262 itsSpacing=FC_PROPORTIONAL;
263 itsItalic=FC_SLANT_ROMAN;
264 itsFamily=itsFoundry=itsVersion=QString();
266 if(in.size()<=0 && fileName && TYPE_UNKNOWN==type)
267 type=getType(fileName); // Read file header to obtain type...
269 switch(type)
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;
288 return ok;
291 void CFontEngine::closeFont()
293 if(itsFt.open)
295 FT_Done_Face(itsFt.face);
296 itsFt.open=false;
300 static int strToWeight(const QString &str)
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;
340 static int strToWidth(const QString &str)
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;
364 static int checkItalic(int it, const QString &full)
366 return (FC_SLANT_ITALIC==it && (-1!=full.indexOf("Oblique") || -1!=full.indexOf("Slanted")))
367 ? FC_SLANT_OBLIQUE
368 : it;
371 static const char * getFoundry(const char *notice)
373 struct Map
375 const char *noticeStr,
376 *foundry;
379 static const Map map[]=
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 }
399 const Map *entry;
401 if(notice)
402 for(entry=map; NULL!=entry->foundry; entry++)
403 if(strstr(notice, entry->noticeStr)!=NULL)
404 return entry->foundry;
406 return NULL;
409 #ifndef HAVE_FcFreeTypeQueryFace
410 static bool lookupName(FT_Face face, int nid, int pid, int eid, FT_SfntName *nameReturn)
412 int n = FT_Get_Sfnt_Name_Count(face);
414 if(n>0)
416 int i;
417 FT_SfntName name;
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))
423 switch(name.platform_id)
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;
439 if(name.string_len > 0)
441 *nameReturn = name;
442 return true;
447 return false;
450 static QByteArray getName(FT_Face face, int nid)
452 FT_SfntName name;
453 QByteArray str;
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];
463 return str;
466 static const char * getFoundry(const FT_Face face, TT_OS2 *os2)
468 struct Map
470 const char *vendorId,
471 *foundry;
474 static const int constVendLen=4;
476 // These are taken from ttmkfdir
477 static const Map map[]=
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}
511 static char vendor[constVendLen+1];
513 vendor[0]='\0';
515 if(NULL!=os2 && 0xFFFF!=os2->version)
517 const Map *entry;
518 char vend[constVendLen+1];
520 strncpy(vendor, (const char *)(os2->achVendID), constVendLen);
521 vendor[constVendLen]='\0';
523 for(int i=0; i<constVendLen; ++i)
524 vend[i]=toupper(vendor[i]);
526 for(entry=map; NULL!=entry->vendorId; entry++)
528 unsigned int len=strlen(entry->vendorId);
530 if(0==memcmp(entry->vendorId, vend, len))
532 bool ok=true;
534 for(int i=len; i<constVendLen && ok; i++)
535 if(vend[i]!=' ' && entry->vendorId[i]!='\0')
536 ok=false;
538 if(ok)
539 return entry->foundry;
544 const char *foundry=NULL;
546 if(!foundry)
547 foundry=getFoundry(getName(face, TT_NAME_ID_TRADEMARK));
549 if(!foundry)
550 foundry=getFoundry(getName(face, TT_NAME_ID_MANUFACTURER));
552 if(!foundry && vendor[0] && !isspace(vendor[0]) && '-'!=vendor[0]) // Some fonts have a totally blank vendor field
554 int i;
556 // Remove any dashes...
557 for(int i=constVendLen-1; i>0; i--)
558 if('-'==vendor[i])
559 vendor[i]=' ';
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;
568 foundry=vendor;
571 return foundry ;
573 #endif
575 bool CFontEngine::openFontFt(QByteArray &in, const char *fileName, int face, bool type1)
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;
581 if(status)
582 itsFt.open=true;
584 if(status)
586 PS_FontInfoRec t1info;
588 if(type1)
589 FT_Get_PS_Font_Info(itsFt.face, &t1info);
591 #ifdef HAVE_FcFreeTypeQueryFace
592 FcPattern *pat=FcFreeTypeQueryFace(itsFt.face, (FcChar8*) fileName, face, NULL);
594 itsWeight=FC_WEIGHT_REGULAR;
595 itsWidth=KFI_FC_WIDTH_NORMAL;
596 itsSpacing=FC_PROPORTIONAL;
598 if(pat)
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);
609 if(type1)
610 itsVersion=t1info.version;
611 else
613 int version;
615 FcPatternGetInteger(pat, FC_FONTVERSION, face, &version);
616 if(version>0)
617 itsVersion.setNum(decodeFixed(version));
620 FcPatternDestroy(pat);
621 fixFoundry(itsFoundry);
623 else
624 status=false;
626 #else
628 enum ETtfWidth
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
641 QString fullName;
643 if(type1)
645 fullName=t1info.full_name;
646 itsFamily=t1info.family_name;
648 else
650 fullName=getName(itsFt.face, TT_NAME_ID_FULL_NAME);
651 itsFamily=getName(itsFt.face, TT_NAME_ID_FONT_FAMILY);
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;
663 if(fullName.isEmpty())
664 status=false; // Hmm... couldn't find any of the names!
666 if(status)
668 if(type1)
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;
678 else // TrueType...
680 #define WEIGHT_UNKNOWN 0xFFFF
681 #define WIDTH_UNKNOWN 0xFFFF
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;
688 itsWidth=WIDTH_UNKNOWN;
689 itsWeight=WEIGHT_UNKNOWN;
691 if(NULL!=os2 && 0xFFFF!=os2->version)
693 if (os2->usWeightClass == 0)
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;
714 switch(os2->usWidthClass)
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;
745 itsItalic=os2->fsSelection&(1 << 0) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
746 gotItalic=true;
749 if(WEIGHT_UNKNOWN==itsWeight)
750 itsWeight=itsFt.face->style_flags&FT_STYLE_FLAG_BOLD
751 ? FC_WEIGHT_BOLD
752 : FC_WEIGHT_MEDIUM;
754 if(WIDTH_UNKNOWN==itsWidth)
755 itsWidth=FC_WIDTH_NORMAL;
757 if(!gotItalic && head!=NULL)
759 gotItalic=true;
760 itsItalic=head->Mac_Style & 2 ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
763 if(head)
765 double version=decodeFixed(head->Font_Revision);
767 if(version>0)
768 itsVersion.setNum(version);
771 if(!gotItalic && NULL!=post)
773 gotItalic=true;
774 itsItalic=0.0f==decodeFixed(post->italicAngle) ? FC_SLANT_ROMAN : FC_SLANT_ITALIC;
777 itsItalic=checkItalic(itsItalic, fullName);
779 if(NULL!=post && post->isFixedPitch)
781 TT_HoriHeader *hhea=NULL;
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;
789 else
790 itsSpacing=FC_PROPORTIONAL;
792 itsFoundry=KFI::getFoundry(itsFt.face, os2);
795 #endif
797 if(!status)
798 closeFont();
800 return status;
803 bool CFontEngine::openFontAfm(QByteArray &in)
805 bool inMetrics=false;
806 QString full;
807 QTextStream ds(&in, QIODevice::ReadOnly);
809 while(!ds.atEnd())
811 QString line(ds.readLine());
812 line=line.simplified();
814 if(inMetrics)
816 if(0==line.indexOf("FullName "))
818 full=line.mid(9);
819 itsWidth=strToWidth(full);
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;
837 else
838 if(0==line.indexOf("StartFontMetrics"))
839 inMetrics=true;
842 if(itsFamily.isEmpty() && !full.isEmpty())
843 itsFamily=full;
845 checkItalic(itsItalic, full);
847 return !full.isEmpty() && !itsFamily.isEmpty();
850 #ifndef HAVE_FcFreeTypeQueryFace
851 static int charToItalic(char c)
853 switch(c)
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;
868 static int charToSpacing(char c)
870 switch(c)
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;
885 void CFontEngine::parseXlfdBmp(const QString &xlfd)
887 enum EXlfd
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
905 int pos=0,
906 oldPos=1;
907 int entry;
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)
913 switch(entry)
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;
948 oldPos=pos+1;
952 bool CFontEngine::openFontBdf(QByteArray &in)
954 QTextStream ds(&in, QIODevice::ReadOnly);
956 ds.readLine(); // Skip 1st line.
957 while(!ds.atEnd())
959 QString line(ds.readLine());
961 if(0==line.indexOf("FONT ", Qt::CaseInsensitive))
963 parseXlfdBmp(line.mid(5));
964 return true;
968 return false;
971 static unsigned int readLsb32(QBuffer &in)
973 unsigned char num[4];
975 return 4==in.read((char *)num, 4)
976 ? (num[0])+(num[1]<<8)+(num[2]<<16)+(num[3]<<24)
977 : 0;
980 static unsigned int read32(QBuffer &in, bool msb)
982 if(msb)
984 unsigned char num[4];
986 return 4==in.read((char *)num, 4)
987 ? (num[0]<<24)+(num[1]<<16)+(num[2]<<8)+(num[3])
988 : 0;
991 return readLsb32(in);
994 static const unsigned int constBitmapMaxProps=1024;
996 bool CFontEngine::openFontPcf(QByteArray &in)
998 bool foundXlfd=false;
999 const unsigned int contPcfVersion=(('p'<<24)|('c'<<16)|('f'<<8)|1);
1000 QBuffer buf;
1002 buf.setBuffer(&in);
1003 buf.open(QIODevice::ReadOnly);
1005 if(contPcfVersion==readLsb32(buf))
1007 const unsigned int constPropertiesType=1;
1009 unsigned int numTables=readLsb32(buf),
1010 table,
1011 type,
1012 format,
1013 size,
1014 offset;
1016 for(table=0; table<numTables && !buf.atEnd() && !foundXlfd; ++table)
1018 type=readLsb32(buf);
1019 format=readLsb32(buf);
1020 size=readLsb32(buf);
1021 offset=readLsb32(buf);
1022 if(constPropertiesType==type)
1024 if(buf.seek(offset))
1026 const unsigned int constFormatMask=0xffffff00;
1028 format=readLsb32(buf);
1029 if(0==(format&constFormatMask))
1031 const unsigned int constByteMask=0x4;
1033 bool msb=format&constByteMask;
1034 unsigned int numProps=read32(buf, msb);
1036 if(numProps>0 && numProps<constBitmapMaxProps)
1038 unsigned int strSize,
1039 skip;
1041 struct TProp
1043 unsigned int name,
1044 value;
1045 bool isString;
1046 } *props=new struct TProp [numProps];
1048 if(props)
1050 char tmp;
1051 unsigned short prop;
1053 for(prop=0; prop<numProps; ++prop)
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);
1061 skip=4-((numProps*9)%4);
1062 if(skip!=4)
1063 buf.seek(buf.pos()+skip);
1065 strSize=read32(buf, msb);
1067 if(strSize>0)
1069 QByteArray str=buf.read(strSize);
1071 if(str.size()==(int)strSize)
1073 // Finally we have the data............
1074 const int constMaxStrLen=1024;
1076 char tmp[constMaxStrLen];
1078 for(prop=0; prop<numProps && !foundXlfd; ++prop)
1079 if(kasciistricmp(&(str.data()[props[prop].name]), "FONT")==0)
1081 if(props[prop].isString && strlen(&(str.data()[props[prop].value])))
1083 foundXlfd=true;
1084 strncpy(tmp, &(str.data()[props[prop].value]), constMaxStrLen);
1085 tmp[constMaxStrLen-1]='\0';
1086 parseXlfdBmp(tmp);
1088 break;
1092 delete [] props;
1097 break; // Forget the other tables...
1102 return foundXlfd;
1104 #endif
1106 CFontEngine::TFtData::TFtData()
1107 : open(false)
1109 if(FT_Init_FreeType(&library))
1111 std::cerr << "ERROR: FreeType2 failed to initialise\n";
1112 exit(0);
1116 CFontEngine::TFtData::~TFtData()
1118 FT_Done_FreeType(library);