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
72namespace KFI
73{
74
75static 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
88static 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 = &in;
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
130static const int constHeaderLen=69;
131
132CFontEngine::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 header[constHeaderLen];
140
141 if(constHeaderLen==::read(fd, header, constHeaderLen))
142 type=getType(fileName, header);
143 ::close(fd);
144 }
145
146 return type;
147}
148
149CFontEngine::EType CFontEngine::getType(const char *fileName, Strigi::InputStream *in)
150{
151 Q_ASSERT( in );
152
153 static const int constHeaderLen=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
162CFontEngine::EType CFontEngine::getType(const char *fileName, const unsigned char *header)
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
222QString & 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
254bool 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
291void CFontEngine::closeFont()
292{
293 if(itsFt.open)
294 {
295 FT_Done_Face(itsFt.face);
296 itsFt.open=false;
297 }
298}
299
300static 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
340static 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
364static 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
371static 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
410static 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
450static 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
466static 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
575bool 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
803bool 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
851static 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
868static 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
885void 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
952bool 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
971static 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
980static 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
994static const unsigned int constBitmapMaxProps=1024;
995
996bool 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
1106CFontEngine::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
1116CFontEngine::TFtData::~TFtData()
1117{
1118 FT_Done_FreeType(library);
1119}
1120
1121}
1122