1/* 2 3Copyright (c) 2001, 2002 OpenBeOS. 4 5Authors: 6 Philippe Houdoin 7 Simon Gauvin 8 Michael Pfeiffer 9 10Permission is hereby granted, free of charge, to any person obtaining a copy of 11this software and associated documentation files (the "Software"), to deal in 12the Software without restriction, including without limitation the rights to 13use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 14of the Software, and to permit persons to whom the Software is furnished to do 15so, subject to the following conditions: 16 17The above copyright notice and this permission notice shall be included in all 18copies or substantial portions of the Software. 19 20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26THE SOFTWARE. 27 28*/ 29 30#include <stdio.h> 31#include <malloc.h> 32#include <StorageKit.h> 33#include "Fonts.h" 34#include "Report.h" 35 36// FontFile 37 38// --------------------------------------- 39// Implementation of BArchivable interface 40// --------------------------------------- 41 42 43// -------------------------------------------------- 44FontFile::FontFile(BMessage *archive) 45{ 46 int8 type; 47 archive->FindString("name", &fName); 48 archive->FindString("path", &fPath); 49 archive->FindInt64 ("size", &fSize); 50 if (B_OK == archive->FindInt8 ("type", &type)) 51 fType = (font_type)type; 52 else 53 fType = unknown_type; 54 archive->FindBool ("embed", &fEmbed); 55 archive->FindString("subst", &fSubst); 56} 57 58// -------------------------------------------------- 59BArchivable *FontFile::Instantiate(BMessage *archive) 60{ 61 if (!validate_instantiation(archive, "FontFile")) { 62 return NULL; 63 } 64 return new FontFile(archive); 65} 66 67 68// -------------------------------------------------- 69status_t FontFile::Archive(BMessage *archive, bool deep) const 70{ 71 archive->AddString("class", "FontFile"); 72 archive->AddString("name", fName); 73 archive->AddString("path", fPath); 74 archive->AddInt64 ("size", fSize); 75 archive->AddInt8 ("type", (int8)fType); 76 archive->AddBool ("embed", fEmbed); 77 archive->AddString("subst", fSubst); 78 return B_OK; 79} 80 81 82// Fonts 83 84#define fmin(x, y) ( (x < y) ? x : y); 85 86#define TRUETYPE_VERSION 0x00010000 87#define OPENTYPE_CFF_VERSION 'OTTO' 88 89#define TRUETTYPE_TABLE_NAME_TAG 'name' 90 91static uint16 ttf_get_uint16(FILE * ttf); 92static uint32 ttf_get_uint32(FILE *ttf); 93static status_t ttf_get_fontname(const char * path, char * fontname, size_t fn_size); 94static status_t psf_get_fontname(const char * path, char * fontname, size_t fn_size); 95 96 97// -------------------------------------------------- 98Fonts::Fonts() 99{ 100 SetDefaultCJKOrder(); 101} 102 103 104void 105Fonts::SetDefaultCJKOrder() 106{ 107 SetCJKOrder(0, japanese_encoding, true); 108 SetCJKOrder(1, chinese_gb1_encoding, true); 109 SetCJKOrder(2, chinese_cns1_encoding, true); 110 SetCJKOrder(3, korean_encoding, true); 111} 112 113// -------------------------------------------------- 114bool 115Fonts::SetCJKOrder(int i, font_encoding enc, bool active) 116{ 117 if (0 <= i && i < no_of_cjk_encodings) { 118 fCJKOrder[i].encoding = enc; 119 fCJKOrder[i].active = active; 120 return true; 121 } 122 return false; 123} 124 125 126// -------------------------------------------------- 127bool 128Fonts::GetCJKOrder(int i, font_encoding& enc, bool& active) const 129{ 130 if (0 <= i && i < no_of_cjk_encodings) { 131 enc = fCJKOrder[i].encoding; 132 active = fCJKOrder[i].active; 133 return true; 134 } 135 return false; 136} 137 138 139// --------------------------------------- 140// Implementation of BArchivable interface 141// --------------------------------------- 142 143 144// -------------------------------------------------- 145Fonts::Fonts(BMessage *archive) 146{ 147 BMessage m; 148 for (int i = 0; archive->FindMessage("fontfile", i, &m) == B_OK; i++) { 149 BArchivable* base = instantiate_object(&m); 150 if (base) { 151 FontFile* f = dynamic_cast<FontFile*>(base); 152 if (f) fFontFiles.AddItem(f); 153 else delete f; 154 } 155 } 156 if (archive->FindMessage("cjk_order", &m) == B_OK) { 157 for (int i = 0; i < no_of_cjk_encodings; i++) { 158 bool active; int32 encoding; 159 if (m.FindInt32("encoding", i, &encoding) == B_OK && 160 m.FindBool("active", i, &active) == B_OK && 161 first_cjk_encoding <= encoding && 162 encoding < first_cjk_encoding + no_of_cjk_encodings) { 163 SetCJKOrder(i, (font_encoding)encoding, active); 164 } else { 165 SetDefaultCJKOrder(); return; 166 } 167 } 168 return; 169 } 170 SetDefaultCJKOrder(); 171} 172 173 174// -------------------------------------------------- 175BArchivable *Fonts::Instantiate(BMessage *archive) 176{ 177 if (!validate_instantiation(archive, "Fonts")) { 178 return NULL; 179 } 180 return new Fonts(archive); 181} 182 183 184// -------------------------------------------------- 185status_t Fonts::Archive(BMessage *archive, bool deep) const 186{ 187 archive->AddString("class", "Fonts"); 188 const int n = Length(); 189 for (int i = 0; i < n; i++) { 190 FontFile* f = At(i); 191 BMessage m; 192 if (f->Archive(&m) == B_OK) { 193 archive->AddMessage("fontfile", &m); 194 } 195 } 196 BMessage m; 197 font_encoding enc; 198 bool active; 199 for (int i = 0; GetCJKOrder(i, enc, active); i++) { 200 m.AddInt32("encoding", enc); 201 m.AddBool("active", active); 202 } 203 archive->AddMessage("cjk_order", &m); 204 return B_OK; 205} 206 207 208// -------------------------------------------------- 209status_t 210Fonts::CollectFonts() 211{ 212 BPath path; 213 directory_which * which_dir; 214 directory_which lookup_dirs[] = { 215 B_BEOS_FONTS_DIRECTORY, 216 // B_COMMON_FONTS_DIRECTORY, // seem to be the same directory than B_USER_FONTS_DIRECTORY!!! 217 B_USER_FONTS_DIRECTORY, 218 (directory_which) -1 219 }; 220 221 which_dir = lookup_dirs; 222 while (*which_dir >= 0) { 223 if ( find_directory(*which_dir, &path) == B_OK ) 224 LookupFontFiles(path); 225 226 which_dir++; 227 }; 228 229 return B_OK; 230} 231 232 233// -------------------------------------------------- 234status_t 235Fonts::LookupFontFiles(BPath path) 236{ 237 BDirectory dir(path.Path()); 238 BEntry entry; 239 240 if (dir.InitCheck() != B_OK) 241 return B_ERROR; 242 243 dir.Rewind(); 244 while (dir.GetNextEntry(&entry) >= 0) { 245 BPath name; 246 char fn[512]; 247 font_type ft = unknown_type; // to keep the compiler silent. 248 off_t size; 249 status_t status; 250 251 entry.GetPath(&name); 252 if (entry.IsDirectory()) 253 // recursivly lookup in sub-directories... 254 LookupFontFiles(name); 255 256 if (! entry.IsFile()) 257 continue; 258 259 fn[0] = 0; 260 ft = unknown_type; 261 262 // is it a truetype file? 263 status = ttf_get_fontname(name.Path(), fn, sizeof(fn)); 264 if (status == B_OK ) { 265 ft = true_type_type; 266 } else { 267 // okay, maybe it's a postscript type file? 268 status = psf_get_fontname(name.Path(), fn, sizeof(fn)); 269 if (status == B_OK) { 270 ft = type1_type; 271 } 272 } 273 274 if (ft == unknown_type) 275 // not a font file... 276 continue; 277 278 if (entry.GetSize(&size) != B_OK) 279 size = 1024*1024*1024; 280 281 REPORT(kDebug, -1, "Installed font %s -> %s", fn, name.Path()); 282 fFontFiles.AddItem(new FontFile(fn, name.Path(), size, ft, size < 100*1024)); 283 } // while dir.GetNextEntry()... 284 285 return B_OK; 286} 287 288// -------------------------------------------------- 289void 290Fonts::SetTo(BMessage *archive) 291{ 292 BArchivable *a = Instantiate(archive); 293 Fonts *f = a ? dynamic_cast<Fonts*>(a) : NULL; 294 if (f) { 295 const int n = Length(); 296 const int m = f->Length(); 297 for (int i = 0; i < m; i ++) { 298 FontFile *font = f->At(i); 299 for (int j = 0; j < n; j ++) { 300 if (strcmp(font->Path(), At(j)->Path()) == 0) { 301 At(j)->SetEmbed(font->Embed()); 302 At(j)->SetSubst(font->Subst()); 303 break; 304 } 305 } 306 } 307 308 font_encoding enc; 309 bool active; 310 for (int i = 0; f->GetCJKOrder(i, enc, active); i++) { 311 SetCJKOrder(i, enc, active); 312 } 313 delete f; 314 } 315} 316 317 318// -------------------------------------------------- 319static uint16 ttf_get_uint16(FILE * ttf) 320{ 321 uint16 v; 322 323 if (fread(&v, 1, 2, ttf) != 2) 324 return 0; 325 326 return B_BENDIAN_TO_HOST_INT16(v); 327} 328 329 330// -------------------------------------------------- 331static uint32 ttf_get_uint32(FILE *ttf) 332{ 333 uint32 buf; 334 335 if (fread(&buf, 1, 4, ttf) != 4) 336 return 0; 337 338 return B_BENDIAN_TO_HOST_INT32(buf); 339} 340 341 342// -------------------------------------------------- 343static status_t ttf_get_fontname(const char * path, char * fontname, size_t fn_size) 344{ 345 FILE * ttf; 346 status_t status; 347 uint16 nb_tables, nb_records; 348 uint16 i; 349 uint32 tag; 350 uint32 table_offset; 351 uint32 strings_offset; 352 char family_name[256]; 353 char face_name[256]; 354 int names_found; 355 356 status = B_ERROR; 357 358 ttf = fopen(path, "rb"); 359 if (! ttf) 360 return status; 361 362 tag = ttf_get_uint32(ttf); /* version */ 363 switch(tag) { 364 case TRUETYPE_VERSION: 365 case OPENTYPE_CFF_VERSION: 366 break; 367 368 default: 369 goto exit; 370 } 371 372 /* set up table directory */ 373 nb_tables = ttf_get_uint16(ttf); 374 375 fseek(ttf, 12, SEEK_SET); 376 377 table_offset = 0; // quiet the compiler... 378 379 for (i = 0; i < nb_tables; ++i) { 380 tag = ttf_get_uint32(ttf); 381 ttf_get_uint32(ttf); // checksum 382 table_offset = ttf_get_uint32(ttf); 383 384 if (tag == TRUETTYPE_TABLE_NAME_TAG) 385 break; 386 } 387 388 if (tag != TRUETTYPE_TABLE_NAME_TAG) 389 // Mandatory name table not found! 390 goto exit; 391 392 // move to name table start 393 fseek(ttf, table_offset, SEEK_SET); 394 395 ttf_get_uint16(ttf); // name table format (must be 0!) 396 nb_records = ttf_get_uint16(ttf); 397 strings_offset = table_offset + ttf_get_uint16(ttf); // string storage offset is from table offset 398 399 // offs = ttf->dir[idx].offset + tp->offsetStrings; 400 401 // printf(" pid eid lid nid len offset value\n"); 402 // 65536 65536 65536 65536 65536 65536 ...... 403 404 family_name[0] = 0; 405 face_name[0] = 0; 406 names_found = 0; 407 408 for (i = 0; i < nb_records; ++i) { 409 uint16 platform_id, encoding_id, name_id; 410 uint16 string_len, string_offset; 411 412 platform_id = ttf_get_uint16(ttf); 413 encoding_id = ttf_get_uint16(ttf); 414 ttf_get_uint16(ttf); // language_id 415 name_id = ttf_get_uint16(ttf); 416 string_len = ttf_get_uint16(ttf); 417 string_offset = ttf_get_uint16(ttf); 418 419 if ( name_id != 1 && name_id != 2 ) 420 continue; 421 422 // printf("%5d %5d %5d %5d %5d %5d ", 423 // platform_id, encoding_id, language_id, name_id, string_len, string_offset); 424 425 if (string_len != 0) { 426 long pos; 427 char * buffer; 428 429 pos = ftell(ttf); 430 fseek(ttf, strings_offset + string_offset, SEEK_SET); 431 432 buffer = (char *) malloc(string_len + 16); 433 434 fread(buffer, 1, string_len, ttf); 435 buffer[string_len] = '\0'; 436 437 fseek(ttf, pos, SEEK_SET); 438 439 if ( (platform_id == 3 && encoding_id == 1) || // Windows Unicode 440 (platform_id == 0) ) { // Unicode 441 // dirty unicode -> ascii conversion 442 int k; 443 444 for (k=0; k < string_len/2; k++) 445 buffer[k] = buffer[2*k + 1]; 446 buffer[k] = '\0'; 447 } 448 449 // printf("%s\n", buffer); 450 451 if (name_id == 1) 452 strncpy(family_name, buffer, sizeof(family_name)); 453 else if (name_id == 2) 454 strncpy(face_name, buffer, sizeof(face_name)); 455 456 names_found += name_id; 457 458 free(buffer); 459 } 460 // else 461 // printf("<null>\n"); 462 463 if (names_found == 3) 464 break; 465 } 466 467 if (names_found == 3) { 468#ifndef __POWERPC__ 469 snprintf(fontname, fn_size, "%s-%s", family_name, face_name); 470#else 471 sprintf(fontname, "%s-%s", family_name, face_name); 472#endif 473 status = B_OK; 474 } 475 476exit: 477 fclose(ttf); 478 return status; 479} 480 481 482// -------------------------------------------------- 483static status_t psf_get_fontname(const char * path, char * fontname, size_t fn_size) 484{ 485 FILE * psf; 486 status_t status; 487 int i; 488 char line[1024]; 489 char * token; 490 char * name; 491 492 // *.afm search for "FontName <font_name_without_blank>" line 493 // *.pfa search for "/FontName /<font_name_without_blank> def" line 494 495 status = B_ERROR; 496 497 psf = fopen(path, "r"); 498 if (! psf) 499 return status; 500 501 name = NULL; 502 503 i = 0; 504 while ( fgets(line, sizeof(line), psf) != NULL ) { 505 i++; 506 if ( i > 64 ) 507 // only check the first 64 lines of files... 508 break; 509 510 token = strtok(line, " \r\n"); 511 if (! token) 512 continue; 513 514 if (strcmp(token, "FontName") == 0) 515 name = strtok(NULL, " \r\n"); 516 else if (strcmp(token, "/FontName") == 0) { 517 name = strtok(NULL, " \r\n"); 518 if (name) 519 name++; // skip the '/' 520 } 521 522 if (name) 523 break; 524 } 525 526 if (name) { 527 strncpy(fontname, name, fn_size); 528 status = B_OK; 529 } 530 531 fclose(psf); 532 return status; 533} 534 535// Implementation of UserDefinedEncodings 536 537UserDefinedEncodings::UserDefinedEncodings() 538 : fCurrentEncoding(0) 539 , fCurrentIndex(0) 540{ 541 memset(fUsedMask, 0, sizeof(fUsedMask)); 542 memset(fEncoding, 0, sizeof(fEncoding)); 543 memset(fIndex, 0, sizeof(fIndex)); 544} 545 546bool UserDefinedEncodings::Get(uint16 unicode, uint8 &encoding, uint8 &index) { 547 bool missing = !IsUsed(unicode); 548 if (missing) { 549 SetUsed(unicode); 550 fEncoding[unicode] = fCurrentEncoding; fIndex[unicode] = fCurrentIndex; 551 if (fCurrentIndex == 255) { 552 fCurrentIndex = 0; fCurrentEncoding ++; 553 } else { 554 fCurrentIndex ++; 555 } 556 } 557 encoding = fEncoding[unicode]; index = fIndex[unicode]; 558 return missing; 559} 560