1/* Type1Import.m 2 * Type1 import object 3 * 4 * Copyright (C) 2000-2006 by vhf interservice GmbH 5 * Author: Ilonka Fleischmann 6 * 7 * created: 2000-11-14 8 * modified: 2006-02-08 9 * 10 * This file is part of the vhf Import Library. 11 * 12 * This library is free software; you can redistribute it and/or 13 * modify it under the terms of the vhf Public License as 14 * published by the vhf interservice GmbH. Among other things, 15 * the License requires that the copyright notices and this notice 16 * be preserved on all copies. 17 * 18 * This library is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 21 * See the vhf Public License for more details. 22 * 23 * You should have received a copy of the vhf Public License along 24 * with this library; see the file LICENSE. If not, write to vhf. 25 * 26 * If you want to link this library to your proprietary software, 27 * or for other uses which are not covered by the definitions 28 * laid down in the vhf Public License, vhf also offers a proprietary 29 * license scheme. See the vhf internet pages or ask for details. 30 * 31 * vhf interservice GmbH, Im Marxle 3, 72119 Altingen, Germany 32 * eMail: info@vhf.de 33 * http://www.vhf.de 34 */ 35 36#include "Type1Import.h" 37#include <VHFShared/types.h> 38 39@interface Type1Import(PrivateMethods) 40- (void)createFontGrid; 41- (BOOL)interpret:(NSString*)data; 42- (BOOL) encodeCharStrings; 43- newChar:(char*)name index:(int)index; 44- sidebearing:(int)x :(int)y width:(int)w; 45- hstem:(int)y :(int)dy; 46- vstem:(int)x :(int)dx; 47- moveto:(int)x :(int)y; 48- lineto:(int)x :(int)y; 49- curveto:(int)x1 :(int)y1 :(int)x2 :(int)y2 :(int)x3 :(int)y3; 50- closepath; 51- (void)updateBounds:(NSPoint)p; 52@end 53 54@implementation Type1Import 55 56/* created: 25.01.96 57 * modified: 18.09.96 19.06.00 58 * parameter: psData the PostScript stream 59 * purpose: start interpretation of the contents of psData 60 */ 61- importType1:(NSData*)fontData 62{ 63 fontObject = [Type1Font font]; 64 //fontObject = [self allocateFontObject]; 65 list = [self allocateList]; 66 ll.x = ll.y = 10.0; 67 ur.x = ur.y = 0.0; 68 69 state.width = 0.0; 70 gridOffset = [fontObject gridOffset]; 71 state.color = [NSColor blackColor]; 72 73 /* interpret data 74 */ 75 if ( ![self interpret:[[[NSMutableString alloc] initWithData:fontData 76 encoding:NSASCIIStringEncoding] autorelease]] ) 77 return nil; 78 79 [fontObject setFontList:list]; 80 [list release]; 81 return fontObject; 82} 83 84/* private methods 85 */ 86typedef struct _Token 87{ 88 char string[30]; 89 int index; 90}Token; 91#define T_COMMENT 2 92#define T_END 3 93#define T_UNIQUEID 4 94#define T_FONTINFO 10 95#define T_FONTNAME 11 96#define T_PAINTTYPE 12 97#define T_FONTTYPE 13 98#define T_FONTMATRIX 14 99#define T_ENCODING 15 100#define T_FONTBBOX 16 101Token documentTokens[] = {{"%", 2}, {"/UniqueID", 4}, {"/FontInfo", 10}, {"/FontName", 11}, {"/PaintType", 12}, {"/FontType", 13}, {"/FontMatrix", 14}, {"/Encoding", 15}, {"/FontBBox", 16}, {"", 0}}; 102#define T_VERSION 10 103#define T_NOTICE 11 104#define T_FULLNAME 12 105#define T_FAMILYNAME 13 106#define T_WEIGHT 14 107#define T_ITALICANGLE 15 108#define T_ISFIXEDPITCH 16 109#define T_UNDERLINEPOSITION 17 110#define T_UNDERLINETHICKNESS 18 111#define T_COPYRIGHT 19 112Token fontInfoTokens[] = {{"%", 2}, {"end", 3}, {"/version", 10}, {"/Notice", 11}, {"/FullName", 12}, {"/FamilyName", 13}, {"/Weight", 14}, {"/ItalicAngle", 15}, {"/isFixedPitch", 16}, {"/UnderlinePosition", 17}, {"/UnderlineThickness", 18}, {"/Copyright", 19}, {"", 0}}; 113#define T_DUP 10 114#define T_DEF 11 115Token encodingTokens[] = {{"%", 2}, {"dup", 10}, {"def", 11}, {"", 0}}; 116#define T_RD 12 117#define T_ND 13 118#define T__X 14 119#define T_X_ 15 120Token subrsTokens[] = {{"%", 2}, {"end", 3}, {"dup", 10}, {"def", 11}, {"RD", 12}, {"ND", 13}, {"-|", 14}, {"|-", 15}, {"", 0}}; 121#define T_BLUEVALUES 10 122#define T_MINFEATURE 11 123#define T_PASSWORD 12 124#define T_SUBRS 13 125#define T_SOURCE 14 126#define T_OTHERSUBRS 15 127#define T_CHARSTRINGS 16 128Token privateTokens[] = {{"%", 2}, {"end", 3}, {"/UniqueID", 4}, {"/BlueValues", 10}, {"/MinFeature", 11}, {"password", 12}, {"/Subrs", 13}, {"/Source", 14}, {"/OtherSubrs", 15}, {"/CharStrings", 16}, {"", 0}}; 129#define T_CHARSTRING 10 130Token charStringsTokens[] = {{"%", 2}, {"end", 3}, {"/", 10}, {"", 0}}; 131 132//#define GotoNextData(c) (c)+=strspn((c)+=strcspn((c), SEPARATORS), SEPARATORS) 133static __inline__ char* gotoNextData(char *c) 134{ 135 c += strcspn(c, SEPARATORS); 136 c += strspn(c, SEPARATORS); 137 return c; 138} 139 140// create FontGrid with gridOffset and standardEncoding 141//static NSArray *gridCharArray = (F, E, D, C, B, A, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); 142- (void)createFontGrid 143{ NSArray *gridCharArray = [NSArray arrayWithObjects:@"F", @"E", @"D", @"C", @"B", @"A", @"9", @"8", @"7", @"6", @"5", @"4", @"3", @"2", @"1", @"0", nil]; 144 NSString *charFontStr = @"Helvetica-Light"; 145 NSString *gridFontStr = @"DINMittelschrift"; 146 NSPoint begin, end; 147 float fontSize; 148 int i, c; 149 150 for (i=0; i<17; i++) // grid lines horicontal 151 { 152 begin.y = end.y = (float)i * gridOffset; 153 begin.x = gridOffset*0.5; 154 end.x = 17.0 * gridOffset; 155 [self addLine:begin :end toList:list]; 156 } 157 for (i=1; i<18; i++) // grid lines vertical 158 { 159 begin.x = end.x = (float)i * gridOffset; 160 begin.y = 0.0; 161 end.y = 16.5 * gridOffset; 162 [self addLine:begin :end toList:list]; 163 } 164 for (i=0; i<16; i++) // baseline horicontal 165 { 166 begin.y = end.y = (float)i * gridOffset + 0.25*gridOffset; 167 begin.x = gridOffset; 168 end.x = 17.0 * gridOffset; 169 [self addLine:begin :end toList:list]; 170 } 171 fontSize = gridOffset/5.0; 172 for (i=0, c=0; i<16; i++, c++) // grid chars vertical 173 { NSString *text; 174 NSPoint origin; 175 176 text = [gridCharArray objectAtIndex:c]; 177 origin.x = gridOffset - fontSize; 178 origin.y = (float)i*gridOffset + (0.5*gridOffset - fontSize*0.4); 179 180 [self addText:text :[gridFontStr copy] :0.0 :fontSize :1.2 at:origin toList:list]; 181 } 182 for (i=1, c=15; i<17; i++, c--) // grid chars horicontal 183 { NSString *text; 184 NSPoint origin; 185 186 text = [gridCharArray objectAtIndex:c]; 187 origin.x = (float)i*gridOffset + (0.5*gridOffset - fontSize*0.3); 188 origin.y = 16.0*gridOffset + 0.1*gridOffset; 189 190 [self addText:text :[gridFontStr copy] :0.0 :fontSize :1.2 at:origin toList:list]; 191 } 192 fontSize = gridOffset/11.0; 193 for (i=0; i<encodingCnt; i++) 194 { NSString *text; 195 NSPoint origin; 196 197 text = [NSString stringWithFormat:@"%s", encoding[i].name]; 198 origin.x = (encoding[i].index/16+1) * gridOffset + gridOffset*0.04; 199 origin.y = (gridOffset + (15-(encoding[i].index%16)) * gridOffset) - (fontSize + gridOffset*0.025); 200 201 [self addText:text :[charFontStr copy] :0.0 :fontSize :1.0 at:origin toList:list]; 202 } 203} 204 205- (int) getCharStringIndexFromEncoding:(int) index 206{ int i, j; 207 208 for (i=0; i<encodingCnt; i++) 209 if (encoding[i].index == index) 210 { char *np = encoding[i].name; 211 for (j=0; j<255; j++) 212 { 213 if (!strcmp(charStrings[j].name, np)) 214 return j; 215 } 216 } 217 return 0; 218} 219 220/* search for /Fontinfo, start at begin, interpret until end 221 * search for keywords until def 222 */ 223- (char*) getFontInfo:(char*)cp 224{ NSMutableDictionary *fiDict = [NSMutableDictionary dictionaryWithCapacity:10]; 225 char *cString; 226 int intVal; 227 BOOL boolVal; 228 229 if ( !(cp = strstr(cp, "begin")) ) 230 { printf("Type1Import, FontInfo: unexpected end of file\n"); 231 return 0; 232 } 233 234 while (*cp) 235 { Token *token; 236 int i; 237 238 cp += strspn(cp, SEPARATORS); /* goto begin of data */ 239 for (i=0; (token = fontInfoTokens+i) && token->index; i++) 240 { 241 if ( !strncmp(cp, token->string, strlen(token->string)) ) 242 { switch (token->index) 243 { 244 case T_COMMENT: 245 cp += strcspn(cp, NEWLINE); // goto end of line 246 break; 247 case T_VERSION: 248 cp = gotoNextData(cp); 249 cString = getString(cp); 250 [fiDict setObject:[NSString stringWithUTF8String:cString] forKey:@"version"]; 251 if (!(cp = strstr(cp, "def"))) 252 { printf("Type1Import: No 'def' for /version found"); 253 return 0; 254 } 255 cp += strlen("def"); 256 break; 257 case T_ITALICANGLE: 258 cp = gotoNextData(cp); 259 intVal = getInt(cp); 260 [fiDict setObject:[NSNumber numberWithInt:intVal] forKey:@"italicAngle"]; 261 if (!(cp = strstr(cp, "def"))) 262 { printf("Type1Import: No 'def' for /ItalicAngle found"); 263 return 0; 264 } 265 cp += strlen("def"); 266 break; 267 case T_NOTICE: 268 cp = gotoNextData(cp); 269 cString = getString(cp); 270 [fiDict setObject:[NSString stringWithUTF8String:cString] forKey:@"notice"]; 271 if (!(cp = strstr(cp, "def"))) 272 { printf("Type1Import: No 'def' for /Notice found"); 273 return 0; 274 } 275 cp += strlen("def"); 276 break; 277 case T_COPYRIGHT: 278 cp = gotoNextData(cp); 279 cString = getString(cp); 280 [fiDict setObject:[NSString stringWithUTF8String:cString] forKey:@"copyright"]; 281 if (!(cp = strstr(cp, "def"))) 282 { printf("Type1Import: No 'def' for /Copyright found"); 283 return 0; 284 } 285 cp += strlen("def"); 286 break; 287 case T_FULLNAME: 288 cp += strlen(token->string); 289 cString = getString(cp); 290 [fiDict setObject:[NSString stringWithUTF8String:cString] forKey:@"fullName"]; 291 if (!(cp = strstr(cp, "def"))) 292 { printf("Type1Import: No 'def' for /FullName found"); return 0; } 293 cp += strlen("def"); 294 break; 295 case T_FAMILYNAME: 296 cp += strlen(token->string); 297 cString = getString(cp); 298 [fiDict setObject:[NSString stringWithUTF8String:cString] forKey:@"familyName"]; 299 if (!(cp = strstr(cp, "def"))) 300 { printf("Type1Import: No 'def' for /FamilyName found"); return 0; } 301 cp += strlen("def"); 302 break; 303 case T_WEIGHT: 304 cp = gotoNextData(cp); 305 cString = getString(cp); 306 [fiDict setObject:[NSString stringWithUTF8String:cString] forKey:@"weight"]; 307 if (!(cp = strstr(cp, "def"))) 308 { printf("Type1Import: No 'def' for /Weight found"); 309 return 0; 310 } 311 cp += strlen("def"); 312 break; 313 case T_ISFIXEDPITCH: 314 cp = gotoNextData(cp); 315 boolVal = getBool(cp); 316 [fiDict setObject:[NSNumber numberWithShort:boolVal] forKey:@"isFixedPitch"]; 317 if (!(cp = strstr(cp, "def"))) 318 { printf("Type1Import: No 'def' for /isFixedPitch found"); 319 return 0; 320 } 321 cp += strlen("def"); 322 break; 323 case T_UNDERLINEPOSITION: 324 cp = gotoNextData(cp); 325 intVal = getInt(cp); 326 [fiDict setObject:[NSNumber numberWithLong:intVal] forKey:@"underlinePosition"]; 327 if (!(cp = strstr(cp, "def"))) 328 { printf("Type1Import: No 'def' for /UnderlinePosition found"); 329 return 0; 330 } 331 cp += strlen("def"); 332 break; 333 case T_UNDERLINETHICKNESS: 334 cp = gotoNextData(cp); 335 intVal = getInt(cp); 336 [fiDict setObject:[NSNumber numberWithLong:intVal] forKey:@"underlineThickness"]; 337 if (!(cp = strstr(cp, "def"))) 338 { printf("Type1Import: No 'def' for /UnderlineThickness found"); 339 return 0; 340 } 341 cp += strlen("def"); 342 break; 343 case T_END: 344 cp += strlen(token->string); 345 if (!(cp = strstr(cp, "def"))) 346 { printf("Type1Import, FontInfo: Unexpected end of file\n"); 347 return 0; 348 } 349 cp += strlen("def"); 350 [fontObject setFontInfo:fiDict]; 351 return cp; 352 default: 353 cp += strcspn(cp, SEPARATORS); /* goto next separator */ 354 break; 355 } 356 break; 357 } 358 } 359 if (!token->index) 360 cp += strcspn(cp, NEWLINE); /* goto next newline */ 361 } 362 [fontObject setFontInfo:fiDict]; 363 return cp; 364} 365 366/* get encoding vectors 367 */ 368- (char*)getEncoding:(char*)cp 369{ //int n=0; 370 371encoding = [fontObject standardEncoding]; 372encodingCnt = [fontObject standardEncodingCnt]; 373#if 0 374 cp += strspn(cp, " \t"); /* goto data */ 375 if (!strcspn(cp, "-.0123456789")) /* digits */ 376 { 377 encodingCnt = getInt(cp); 378 379 if (!(encoding = malloc(encodingCnt*sizeof(Encoding)))) 380 { printf("Type1Import, /Encoding: Out of Memory\n"); 381 return 0; 382 } 383 384 while (*cp) 385 { Token *token; 386 int i; 387 388 cp += strspn(cp, SEPARATORS); /* goto begin of data */ 389 for (i=0; (token = encodingTokens+i) && token->index; i++) 390 { 391 if ( !strncmp(cp, token->string, strlen(token->string)) ) 392 { switch (token->index) 393 { 394 case T_COMMENT: 395 cp += strcspn(cp, NEWLINE); /* goto end of line */ 396 break; 397 case T_DUP: 398 if (n>encodingCnt) 399 { printf("Type1Import, /Encoding: Too many entries in encoding table\n"); 400 return 0; 401 } 402 cp += strlen(token->string); 403 cp = gotoNextData(cp); 404 encoding[n].index = getInt(cp); 405 cp = gotoNextData(cp); 406 //strncpy(encoding[n].name, cp, strcspn(cp, SEPARATORS)); 407 if ( *cp == '/' ) 408 cp++; 409 strncpy(encoding[n].name, cp, strcspn(cp, SEPARATORS)); 410 encoding[n].name[strcspn(cp, SEPARATORS)] = 0; 411 if (!(cp = strstr(cp, "put"))) 412 { printf("Type1Import, /Encoding: unexpected end of file"); 413 return 0; 414 } 415 cp += strlen("put"); 416 n++; 417 break; 418 case T_DEF: 419 cp = gotoNextData(cp); 420 [fontObject setFontEncoding:encoding :encodingCnt]; 421 return cp; 422 break; 423 default: 424 cp += strcspn(cp, SEPARATORS); /* goto next separator */ 425 break; 426 } 427 break; 428 } 429 } 430 if (!token->index) 431 cp += strcspn(cp, SEPARATORS); /* goto next separator */ 432 } 433 } 434 else if (!strncmp(cp, "StandardEncoding", strlen("StandardEncoding"))) 435 { encoding = [fontObject standardEncoding]; 436 encodingCnt = [fontObject standardEncodingCnt]; 437 } 438 else if (!strncmp(cp, "IsoLatin1Encoding", strlen("IsoLatin1Encoding"))) 439 { 440 encoding = [fontObject standardEncoding]; 441 encodingCnt = [fontObject standardEncodingCnt]; 442 //encoding = [fontObject isoLatin1Encoding]; 443 //encodingCnt = [fontObject isoLatin1EncodingCnt]; 444 //encoding = isoLatin1Encoding; 445 //encodingCnt = IsoLatin1EncodingCnt; 446 } 447#endif 448 449 if (!(cp = strstr(cp, "def"))) 450 { printf("Type1Import, /Encoding: unexpected end of file\n"); 451 return 0; 452 } 453 cp += strlen("def"); 454 [fontObject setFontEncoding:encoding :encodingCnt]; 455 return cp; 456} 457 458/* get subrs array 459 */ 460- (char*) getSubrs:(char*)cp 461{ int n=0; 462 463 cp += strspn(cp, " \t"); // goto data 464 if (!strcspn(cp, "-.0123456789")) // digits 465 { privateDict.subrsCnt = getInt(cp); 466 467 if (!(privateDict.subrs = malloc(privateDict.subrsCnt*sizeof(Subrs)))) 468 { printf("Type1Import, /Subrs: Out of Memory\n"); 469 return 0; 470 } 471 472 while (*cp) 473 { Token *token; 474 int i, j; 475 476 cp += strspn(cp, SEPARATORS); /* goto begin of data */ 477 for (i=0; (token = subrsTokens+i) && token->index; i++) 478 { 479 if ( !strncmp(cp, token->string, strlen(token->string)) ) 480 { 481 switch (token->index) 482 { 483 case T_COMMENT: 484 cp += strcspn(cp, NEWLINE); /* goto end of line */ 485 break; 486 case T_DUP: 487 if (n>privateDict.subrsCnt) 488 { printf("Type1Import, /Subrs: Too many entries in subrs array\n"); 489 return 0; 490 } 491 cp += strlen(token->string); 492 cp = gotoNextData(cp); 493 j = getInt(cp); 494 if (j > privateDict.subrsCnt) 495 { printf("Type1Import, /Subrs: Too large id:%d\n", j); 496 return 0; 497 } 498 cp = gotoNextData(cp); 499 privateDict.subrs[j].length = getInt(cp); /* length of routine */ 500 cp = gotoNextData(cp); /* goto RD */ 501 cp += strlen("RD"); 502 cp++; 503 privateDict.subrs[j].proc = decryptCharString((unsigned char*)cp, privateDict.subrs[j].length); 504 cp += privateDict.subrs[j].length; 505 privateDict.subrs[j].length -= 4; 506 // if (!(cp = strstr(cp, "put")) && !(cp = strstr(cp, "NP")) ) 507 // { printf("Type1Import, /Subrs: unexpected end of file; 'put' or 'NP' expected\n"); 508 // return cp; 509 // } 510 cp = gotoNextData(cp); 511 n++; 512 break; 513 case T_DEF: 514 case T_ND: 515 case T_X_: 516 cp = gotoNextData(cp); 517 case T_END: 518 [fontObject setFontPrivateSubrs:privateDict.subrs :privateDict.subrsCnt]; 519 return cp; 520 break; 521 default: 522 cp += strcspn(cp, SEPARATORS); /* goto next separator */ 523 break; 524 } 525 break; 526 } 527 } 528 if (!token->index) 529 cp += strcspn(cp, SEPARATORS); /* goto next separator */ 530 } 531 } 532 if (!(cp = strstr(cp, "def"))) 533 { printf("Type1Import, /Subrs: unexpected end of file; 'def' expected!\n"); 534 return 0; 535 } 536 cp += strlen("def"); 537 [fontObject setFontPrivateSubrs:privateDict.subrs :privateDict.subrsCnt]; 538 return cp; 539} 540 541- (char*)getPrivateDict:(char*)cp 542{ NSMutableDictionary *pDict = [NSMutableDictionary dictionaryWithCapacity:26]; 543 char *cString; 544 int intVal; 545 546 if ( !(cp = strstr(cp, "begin")) ) 547 { printf("Type1Import, PrivateDict: unexpected end of file\n"); 548 return 0; 549 } 550 cp += strspn(cp, " \t"); /* goto data */ 551 while (*cp) 552 { Token *token; 553 int i; 554 555 cp += strspn(cp, SEPARATORS); /* goto begin of data */ 556 for (i=0; (token = privateTokens+i) && token->index; i++) 557 { 558 if ( !strncmp(cp, token->string, strlen(token->string)) ) 559 { switch (token->index) 560 { 561 case T_COMMENT: 562 cp += strcspn(cp, NEWLINE); /* goto end of line */ 563 break; 564 case T_BLUEVALUES: 565 { int b; 566 float blueValues[6]; 567 NSMutableArray *array = [NSMutableArray arrayWithCapacity:6]; 568 cp += strlen(token->string); 569 cp = getArray(cp, blueValues); 570 for (b=0; b<6; b++) 571 [array addObject:[NSNumber numberWithFloat:blueValues[b]]]; 572 [pDict setObject:array forKey:@"blueValues"]; 573 } 574 if (!(cp = strstr(cp, "def"))) 575 { printf("Type1Import, PrivateDict, BlueValues: unexpected end of file"); 576 return 0; 577 } 578 cp += strlen("def"); 579 break; 580 case T_MINFEATURE: 581 { float minFeature[2]; 582 NSMutableArray *array = [NSMutableArray arrayWithCapacity:2]; 583 584 cp += strlen(token->string); 585 cp = getArray(cp, minFeature); 586 587 [array addObject:[NSNumber numberWithFloat:minFeature[0]]]; 588 [array addObject:[NSNumber numberWithFloat:minFeature[1]]]; 589 [pDict setObject:array forKey:@"minFeature"]; 590 } 591 if (!(cp = strstr(cp, "def"))) 592 { printf("Type1Import, PrivateDict, MinFeature: unexpected end of file"); 593 return 0; 594 } 595 cp += strlen("def"); 596 break; 597 case T_PASSWORD: 598 cp = gotoNextData(cp); 599 intVal = getInt(cp); 600 [pDict setObject:[NSNumber numberWithLong:intVal] forKey:@"password"]; 601 if (!(cp = strstr(cp, "def"))) 602 { printf("Type1Import, PrivateDict, password: unexpected end of file"); 603 return 0; 604 } 605 cp += strlen("def"); 606 break; 607 case T_UNIQUEID: 608 cp += strlen(token->string); 609 intVal = getInt(cp); 610 [pDict setObject:[NSNumber numberWithInt:intVal] forKey:@"uniqueID"]; 611 if (!(cp = strstr(cp, "def"))) 612 { printf("Type1Import, PrivateDict, UniqueID: unexpected end of file"); 613 return 0; 614 } 615 cp += strlen("def"); 616 break; 617 case T_SOURCE: 618 cp += strlen(token->string); 619 cString = getString(cp); 620 [pDict setObject:[NSString stringWithUTF8String:cString] forKey:@"source"]; 621 if (!(cp = strstr(cp, "def"))) 622 { printf("Type1Import, PrivateDict, /Source: unexpected end of file"); 623 return 0; 624 } 625 cp += strlen("def"); 626 break; 627 case T_OTHERSUBRS: 628 cp += strlen(token->string); 629 cString = getOtherSubrs(cp); 630 [pDict setObject:[NSString stringWithUTF8String:cString] forKey:@"otherSubrs"]; 631 cp += strlen(cString); 632 break; 633 case T_SUBRS: 634 cp += strlen(token->string); 635 cp = [self getSubrs:cp]; 636 if (strstr(cp, "/Subrs")) /* to get Helvetica-Light Oblique */ 637 { cp = strstr(cp, "/Subrs")+strlen("/Subrs"); 638 cp = [self getSubrs:cp]; 639 } 640 break; 641 case T_END: 642 cp += strlen(token->string); 643 case T_CHARSTRINGS: 644 [fontObject setFontPrivate:pDict]; 645 return cp; 646 break; 647 default: 648 cp += strcspn(cp, SEPARATORS); /* goto next separator */ 649 break; 650 } 651 break; 652 } 653 } 654 if (!token->index) 655 cp += strcspn(cp, SEPARATORS); /* goto next separator */ 656 } 657 [fontObject setFontPrivate:pDict]; 658 return cp; 659} 660 661- (char*)getCharStrings:(char*)cp 662{ int cnt; 663 664 if ( !(cp = strstr(cp, "/CharStrings")) ) 665 { printf("Type1Import, CharStrings: unexpected end of file\n"); 666 return 0; 667 } 668 cp += strlen("/CharStrings"); 669 cp += strspn(cp, " \t"); /* goto data */ 670 if (strcspn(cp, "-.0123456789")) /* digits */ 671 { printf("Type1Import, /CharStrings: unexpected character; number expected!\n"); 672 return 0; 673 } 674 cnt = getInt(cp); 675 676// if (!(privateDict.subrs = malloc(cnt*sizeof(Subrs)))) 677// { printf("Type1Import, /Subrs: Out of Memory\n"); 678// return 0; 679// } 680 681 charStringCnt = 0; 682 cp += strspn(cp, " \t"); /* goto data */ 683 while (cp && *cp) 684 { int i; 685 686 cp += strspn(cp, SEPARATORS); /* goto begin of data */ 687 for (i=encodingCnt-1; i>=0; i--) 688 { char name[MAXCHARNAMELEN]; 689 690 strncpy(name, cp+1, strcspn(cp+1, SEPARATORS)); 691 name[strcspn(cp+1, SEPARATORS)] = 0; 692 if ( *cp == '/' && !strcmp(name, encoding[i].name) ) 693 { 694 strcpy(charStrings[charStringCnt].name, encoding[i].name); 695 cp = gotoNextData(cp); /* goto number */ 696 charStrings[charStringCnt].length = getInt(cp); 697 cp = gotoNextData(cp); /* goto RD */ 698 cp += strlen("RD"); 699 cp++; 700 charStrings[charStringCnt].code = decryptCharString((unsigned char*)cp, charStrings[charStringCnt].length); 701 cp += charStrings[charStringCnt].length; 702 charStrings[charStringCnt].length -= 4; 703 charStringCnt++; 704 } 705 } 706 if (i<0) 707 { Token *token; 708 int i, j; 709 710 for (i=0; (token = charStringsTokens+i) && token->index; i++) 711 { 712 if ( !strncmp(cp, token->string, strlen(token->string)) ) 713 { switch (token->index) 714 { 715 case T_COMMENT: 716 cp += strcspn(cp, NEWLINE); /* goto end of line */ 717 break; 718 case T_CHARSTRING: 719 { char str[20]; 720 int l = strcspn(cp, SEPARATORS); 721 if ( l > 19 ) l = 19; 722 strncpy(str, cp, l); 723 str[l] = 0; 724 printf("Type1Import, CharStrings: %s not in encoding table\n", str); 725 } 726 cp = gotoNextData(cp); /* goto number */ 727 if (strcspn(cp, "123456789")) /* no number! */ 728 break; 729 j = getInt(cp); 730 cp = gotoNextData(cp); /* goto RD */ 731 cp += strlen("RD"); 732 cp++; 733 cp += j; 734 break; 735 case T_END: 736 cp += strlen(token->string); 737 [fontObject setFontCharStrings:charStrings :charStringCnt]; 738 return cp; 739 break; 740 } 741 } 742 } 743 if (!token->index) 744 cp += strcspn(cp, SEPARATORS); /* goto next separator */ 745 } 746 } 747 [fontObject setFontCharStrings:charStrings :charStringCnt]; 748 return cp; 749} 750 751- (BOOL)interpret:(NSString*)fData 752{ char *cp, *decryptedData, *fontData, *cString; 753 int dataLen, decryptedLen, intVal; 754 float fontMatrix[6], fontBBox[4]; 755 NSRect bounds; 756 757 // get char from data 758 dataLen = [fData length]; // +1 -> 0 759 fontData = malloc(dataLen+1); 760 // if ( [[fData description] canBeConvertedToEncoding:NSNonLossyASCIIStringEncoding] ) //NSStringEncoding 761 [[fData description] getCString:fontData]; 762//fontData = [[fData description] lossyCString]; 763 // else return NO; 764 765 if (!(cp=strstr(fontData, "eexec"))) 766 { 767 printf("Type1Import: No eexec found"); 768 return NO; 769 } 770 cp += strlen("eexec"); 771 cp += strspn(cp, "\r\n \t"); // go to the beginning of the source 772 773 dataLen -= (cp-fontData); 774 decryptedData = (char*)decryptEexec((unsigned char*)cp, dataLen, &decryptedLen); 775 776/*{ NSString *eexecStr=[NSString stringWithCString:decryptedData length:dataLen]; 777 NSString *filename = [NSString stringWithFormat:@"/Net/nesquick/Users/ilonka/Tempo/Test/arcade1Subrs"]; 778[eexecStr writeToFile:filename atomically:YES]; 779}*/ 780 /* start interpretation 781 * search for document keywords like /Fontinfo 782 * 783 * jump from token to token 784 * check for keywords 785 */ 786 cp = fontData; 787 while (*cp) 788 { Token *token; 789 int i; 790 791 cp += strspn(cp, SEPARATORS); // goto begin of data 792 for (i=0; (token = documentTokens+i) && token->index; i++) 793 { 794 if ( !strncmp(cp, token->string, strlen(token->string)) ) 795 { 796 switch (token->index) 797 { 798 case T_COMMENT: 799 cp += strcspn(cp, NEWLINE); // goto end of line 800 break; 801 case T_FONTINFO: 802 cp = [self getFontInfo:cp]; 803 break; 804 case T_FONTNAME: 805 cp = gotoNextData(cp); 806 cString = getName(cp)+1; 807 [fontObject setFontName:[NSString stringWithUTF8String:cString]]; 808 if (!(cp = strstr(cp, "def"))) 809 { printf("Type1Import: No 'def' for FontName found"); 810 return 0; 811 } 812 cp += strlen("def"); 813 break; 814 case T_PAINTTYPE: 815 cp = gotoNextData(cp); 816 intVal = getInt(cp); 817 [fontObject setPaintType:intVal]; 818 if (!(cp = strstr(cp, "def"))) 819 { printf("Type1Import: No 'def' for PaintType found"); 820 return 0; 821 } 822 cp += strlen("def"); 823 break; 824 case T_FONTTYPE: 825 cp = gotoNextData(cp); 826 intVal = getInt(cp); 827 [fontObject setFontType:intVal]; 828 if (!(cp = strstr(cp, "def"))) 829 { printf("Type1Import: No 'def' for FontType found"); 830 return 0; 831 } 832 cp += strlen("def"); 833 break; 834 case T_FONTMATRIX: 835 cp += strlen(token->string); 836 cp = getArray(cp, fontMatrix); 837 [fontObject setFontMatrix:fontMatrix]; 838 if (!(cp = strstr(cp, "def"))) 839 { printf("Type1Import: No 'def' for FontMatrix found"); 840 return 0; 841 } 842 cp += strlen("def"); 843 break; 844 case T_ENCODING: 845 cp += strlen(token->string); 846 [self getEncoding:cp]; 847 break; 848 case T_FONTBBOX: 849 cp += strlen(token->string); 850 cp = getArray(cp, fontBBox); 851 [fontObject setFontBBox:fontBBox]; 852 if (!(cp = strstr(cp, "def"))) 853 { printf("Type1Import: No 'def' for FontBBox found"); 854 return 0; 855 } 856 cp += strlen("def"); 857 break; 858 break; 859 case T_UNIQUEID: 860 cp = gotoNextData(cp); 861 intVal = getInt(cp); 862 [fontObject setUniqueID:intVal]; 863 if (!(cp = strstr(cp, "def"))) 864 { printf("Type1Import: No 'def' for UniqueID found"); 865 return 0; 866 } 867 cp += strlen("def"); 868 break; 869 default: 870 cp += strcspn(cp, SEPARATORS); /* goto next separator */ 871 break; 872 } 873 break; 874 } 875 } 876 if (!token->index) 877 { 878 cp += strcspn(cp, NEWLINE); /* goto next newline */ 879 } 880 } 881 882 cp = [self getPrivateDict:decryptedData+4]; 883 [self getCharStrings:cp]; 884 885 if ( ![self encodeCharStrings] ) 886 return NO; 887 free(decryptedData); 888 free(fontData); 889 890 /* font grid, character name */ 891 state.color = [NSColor grayColor]; 892 state.width = 0.0; 893 gridOffset = [fontObject gridOffset]; 894 [self createFontGrid]; // added to list 895 896 bounds.origin = ll; 897 bounds.size.width = ur.x - ll.x; 898 bounds.size.height = ur.y - ll.y; 899 [self setBounds:bounds]; 900 901 return YES; 902} 903 904#define ERR_STACKUNDERFLOW 1 905- psErrorCode:(int)code commandName:(char*)name 906{// struct timeval currentTime; 907// char *time; 908 NSString *time = [[NSCalendarDate date] descriptionWithCalendarFormat:@"%Y-%m-%d %H:%M:%S"]; 909 NSString *type1Str = [NSString stringWithFormat:@"Type1Import"]; 910 /* ask for the time here */ 911// gettimeofday (¤tTime, NULL); 912 /* set the month, day and year */ 913// time = ctime(&(currentTime.tv_sec)); 914 915 switch (code) 916 { 917 case ERR_STACKUNDERFLOW: 918 printf("%s, %s: %%%%[ Error:stackunderflow; OffendingCommand:%s ]%%%%\n", [time UTF8String], [type1Str UTF8String], name); 919 break; 920 default: 921 printf("%s, %s: %%%%[ Error:unknown Error Code, %s ]%%%%\n", [time UTF8String], [type1Str UTF8String], name); 922 } 923 924 return self; 925} 926 927- (void)decodeCharString:(unsigned char*)code length:(int)len 928{ static int stack[50], stackTop = 0, seac = 0; 929 static NSPoint currentPoint = {0.0, 0.0}; 930 static int flex = 0, flexStack[20], flexTop = 0; 931 Proc *cp, *end; 932 int psStack[2] = {0, 0}; 933 934 if (!code) 935 { NSLog(@"Type1Import decodeCharString: got Null code"); 936 return; 937 } 938 for (cp=(unsigned char*)code, end=cp+len; cp<end; cp++) 939 { 940 if (stackTop < 0) 941 stackTop = 0; 942 943 switch (*cp) 944 { 945 case 1: /* y dy hstem */ 946 if (stackTop < 2) 947 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"hstem"]; 948 [self hstem:stack[stackTop-2] :stack[stackTop-1]]; 949 stackTop = 0; 950 break; 951 case 3: /* x dx vstem */ 952 if (stackTop < 2) 953 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"vstem"]; 954 [self vstem:stack[stackTop-2] :stack[stackTop-1]]; 955 stackTop = 0; 956 break; 957 case 4: /* dy vmoveto */ 958 if (stackTop < 1) 959 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"vmoveto"]; 960 if (flex) 961 { flexStack[flexTop++] = 0; 962 flexStack[flexTop++] = stack[stackTop-1]; 963 } 964 else 965 { currentPoint.y += stack[stackTop-1]; 966 [self moveto:currentPoint.x :currentPoint.y]; 967 } 968 stackTop = 0; 969 break; 970 case 5: /* dx dy rlineto */ 971 if (stackTop < 2) 972 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"rlineto"]; 973 currentPoint.x += stack[stackTop-2]; 974 currentPoint.y += stack[stackTop-1]; 975 [self lineto:currentPoint.x :currentPoint.y]; 976 stackTop = 0; 977 break; 978 case 6: /* dx hlineto */ 979 if (stackTop < 1) 980 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"hlineto"]; 981 currentPoint.x += stack[stackTop-1]; 982 [self lineto:currentPoint.x :currentPoint.y]; 983 stackTop = 0; 984 break; 985 case 7: /* dy vlineto */ 986 if (stackTop < 1) 987 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"vlineto"]; 988 currentPoint.y += stack[stackTop-1]; 989 [self lineto:currentPoint.x :currentPoint.y]; 990 stackTop = 0; 991 break; 992 case 8: /* dx1 dy1 dx2 dy2 dx3 dy3 rrcurveto */ 993 { NSPoint d1, d2; 994 995 if (stackTop < 6) 996 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"rrcurveto"]; 997 d1.x = currentPoint.x + stack[stackTop-6]; 998 d1.y = currentPoint.y + stack[stackTop-5]; 999 d2.x = d1.x + stack[stackTop-4]; 1000 d2.y = d1.y + stack[stackTop-3]; 1001 currentPoint.x = d2.x + stack[stackTop-2]; 1002 currentPoint.y = d2.y + stack[stackTop-1]; 1003 [self curveto:d1.x :d1.y :d2.x :d2.y :currentPoint.x :currentPoint.y]; 1004 stackTop = 0; 1005 break; 1006 } 1007 case 9: /* closepath */ 1008 [self closepath]; 1009 stackTop = 0; 1010 break; 1011 case 10: /* subr# callsubr */ 1012 if (stackTop < 1) 1013 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"callsubr"]; 1014 stackTop --; 1015 switch (stack[stackTop]) 1016 { NSPoint d1, d2, d3; 1017 1018 case 1: /* start flex control */ 1019 flex = 1; flexTop = 0; 1020 break; 1021 case 2: /* flex control */ 1022 break; 1023 case 0: /* end flex control */ 1024 d1.x = currentPoint.x + flexStack[flexTop-14] + flexStack[flexTop-12]; 1025 d1.y = currentPoint.y + flexStack[flexTop-13] + flexStack[flexTop-11]; 1026 d2.x = d1.x + flexStack[flexTop-10]; 1027 d2.y = d1.y + flexStack[flexTop-9]; 1028 d3.x = d2.x + flexStack[flexTop-8]; 1029 d3.y = d2.y + flexStack[flexTop-7]; 1030 [self curveto:d1.x :d1.y :d2.x :d2.y :d3.x :d3.y]; 1031 d1.x = d3.x + flexStack[flexTop-6]; 1032 d1.y = d3.y + flexStack[flexTop-5]; 1033 d2.x = d1.x + flexStack[flexTop-4]; 1034 d2.y = d1.y + flexStack[flexTop-3]; 1035 d3.x = d2.x + flexStack[flexTop-2]; 1036 d3.y = d2.y + flexStack[flexTop-1]; 1037 [self curveto:d1.x :d1.y :d2.x :d2.y :d3.x :d3.y]; 1038 currentPoint = d3; 1039 flex = 0; flexTop = 0; 1040 break; 1041 default: 1042 [self decodeCharString:privateDict.subrs[stack[stackTop]].proc 1043 length:privateDict.subrs[stack[stackTop]].length]; 1044 } 1045 break; 1046 case 11: /* return */ 1047 return; 1048 break; 1049 case 12: /* 2-byte command */ 1050 cp++; 1051 switch (*cp) 1052 { 1053 case 0: /* dotsection */ 1054// printf("dotsection\n"); 1055 stackTop = 0; 1056 break; 1057 case 1: /* x0 dx0 x1 dx1 x2 dx2 vstem3 */ 1058 if (stackTop < 6) 1059 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"vstem3"]; 1060 [self vstem:stack[stackTop-6] :stack[stackTop-5]]; 1061 [self vstem:stack[stackTop-4] :stack[stackTop-3]]; 1062 [self vstem:stack[stackTop-2] :stack[stackTop-1]]; 1063 stackTop = 0; 1064 break; 1065 case 2: /* y0 dy0 y1 dy1 y2 dy2 hstem3 */ 1066 if (stackTop < 6) 1067 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"hstem3"]; 1068 [self hstem:stack[stackTop-6] :stack[stackTop-5]]; 1069 [self hstem:stack[stackTop-4] :stack[stackTop-3]]; 1070 [self hstem:stack[stackTop-2] :stack[stackTop-1]]; 1071 stackTop = 0; 1072 break; 1073 case 6: /* asb adx ady bchar achar seac */ 1074 { int b, a; 1075 NSPoint p; 1076 1077 if (stackTop < 5) 1078 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"seac"]; 1079 b = [self getCharStringIndexFromEncoding: stack[stackTop-2]]; 1080 a = [self getCharStringIndexFromEncoding: stack[stackTop-1]]; 1081 // relativ to leftsidebearing point not to origin 1082 // -> add leftsidebearing point - only x direction ! 1083 p.x = currentPoint.x + stack[stackTop-4]; 1084 p.y = stack[stackTop-3]; 1085 [self decodeCharString:charStrings[b].code length:charStrings[b].length]; 1086 currentPoint = p; 1087 seac = 1; 1088 [self decodeCharString:charStrings[a].code length:charStrings[a].length]; 1089 seac = 0; 1090 stackTop = 0; 1091 break; 1092 } 1093 case 7: /* sbx sby wx wy sbw */ 1094 if (stackTop < 4) 1095 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"sbw"]; 1096 if (!seac) 1097 { currentPoint.x = stack[stackTop-4]; 1098 currentPoint.y = stack[stackTop-3]; 1099 [self sidebearing:currentPoint.x :currentPoint.y width:stack[stackTop-2]]; 1100 } 1101 stackTop = 0; 1102 break; 1103 case 12: /* num1 num2 div */ 1104 if (stackTop < 2) 1105 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"div"]; 1106 stack[stackTop-2] = stack[stackTop-2] / stack[stackTop-1]; 1107 stackTop --; 1108 break; 1109 case 16: /* arg1 ... argn n othersubr# callothersubr */ 1110 if (stackTop < 1) 1111 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"callothersubr"]; 1112 // printf("%d callothersubr\n", stack[stackTop-1]); 1113 psStack[0] = stack[0]; 1114 psStack[1] = stack[1]; 1115 stackTop = 0; 1116 break; 1117 case 17: /* pop */ 1118 // printf("pop\n"); 1119 stack[stackTop] = psStack[0]; 1120 psStack[0] = psStack[1]; 1121 stackTop ++; 1122 break; 1123 case 33: /* x y setcurrentpoint */ 1124 if (stackTop < 2) 1125 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"setcurrentpoint"]; 1126 printf("setcurrentpoint\n"); 1127 currentPoint.x = stack[stackTop-2]; 1128 currentPoint.y = stack[stackTop-1]; 1129 [self moveto:currentPoint.x :currentPoint.x]; 1130 stackTop = 0; 1131 break; 1132 default: 1133 printf("Type1Import: bad command: 12 %d\n", *cp); 1134 break; 1135 } 1136 break; 1137 case 13: // sbx wx hsbw 1138 if (stackTop < 2) 1139 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"hsbw"]; 1140 if (!seac) 1141 { currentPoint.x = stack[stackTop-2]; 1142 [self sidebearing:currentPoint.x :0 width:stack[stackTop-1]]; 1143 } 1144 stackTop = 0; 1145 break; 1146 case 14: // endchar 1147 stackTop = 0; 1148 currentPoint.x = currentPoint.y = 0; 1149 return; 1150 case 21: // dx dy rmoveto 1151 if (stackTop < 2) 1152 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"rmoveto"]; 1153 if (flex) 1154 { flexStack[flexTop++] = stack[stackTop-2]; 1155 flexStack[flexTop++] = stack[stackTop-1]; 1156 } 1157 else 1158 { currentPoint.x += stack[stackTop-2]; 1159 currentPoint.y += stack[stackTop-1]; 1160 [self moveto:currentPoint.x :currentPoint.y]; 1161 } 1162 stackTop = 0; 1163 break; 1164 case 22: /* dx hmoveto */ 1165 if (stackTop < 1) 1166 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"rmoveto"]; 1167 if (flex) 1168 { flexStack[flexTop++] = stack[stackTop-1]; 1169 flexStack[flexTop++] = 0; 1170 } 1171 else 1172 { currentPoint.x += stack[stackTop-1]; 1173 [self moveto:currentPoint.x :currentPoint.y]; 1174 } 1175 stackTop = 0; 1176 break; 1177 case 30: /* dy1 dx2 dy2 dx3 vhcurveto */ 1178 { NSPoint d1, d2; 1179 1180 if (stackTop < 4) 1181 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"vhcurveto"]; 1182 d1.x = currentPoint.x; 1183 d1.y = currentPoint.y + stack[stackTop-4]; 1184 d2.x = d1.x + stack[stackTop-3]; 1185 d2.y = d1.y + stack[stackTop-2]; 1186 currentPoint.x = d2.x + stack[stackTop-1]; 1187 currentPoint.y = d2.y; 1188 [self curveto:d1.x :d1.y :d2.x :d2.y :currentPoint.x :currentPoint.y]; 1189 stackTop = 0; 1190 break; 1191 } 1192 case 31: /* dx1 dx2 dy2 dy3 hvcurveto */ 1193 { NSPoint d1, d2; 1194 1195 if (stackTop < 4) 1196 [self psErrorCode:ERR_STACKUNDERFLOW commandName:"hvcurveto"]; 1197 d1.x = currentPoint.x + stack[stackTop-4]; 1198 d1.y = currentPoint.y; 1199 d2.x = d1.x + stack[stackTop-3]; 1200 d2.y = d1.y + stack[stackTop-2]; 1201 currentPoint.x = d2.x; 1202 currentPoint.y = d2.y + stack[stackTop-1]; 1203 [self curveto:d1.x :d1.y :d2.x :d2.y :currentPoint.x :currentPoint.y]; 1204 stackTop = 0; 1205 break; 1206 } 1207 default: 1208 if (*cp >= 32) 1209 { cp = decodeNumber(cp, stack+stackTop)-1; 1210 stackTop++; 1211 } 1212 } 1213 } 1214} 1215 1216- (int) indexForCharName:(char*)name 1217{ int i; 1218 1219 for (i=0; i<encodingCnt; i++) 1220 { 1221 if ( !strcmp(encoding[i].name, name) ) 1222 return encoding[i].index; 1223 } 1224 return 0; 1225} 1226 1227NSPoint currentPoint={0.0, 0.0}, startPoint, offset={0.0, 0.0}, gridCell; 1228NSPoint scale, sidebearing; 1229 1230- (BOOL)encodeCharStrings 1231{ int cnt = charStringCnt; // 256 1232 int i; 1233 1234 scale.x = scale.y = 0.1; // 0.01; 1235 1236 for (i=0; i<cnt; i++) 1237 { 1238 /* make new character at index */ 1239 if (*charStrings[i].name) 1240 { 1241 pList = [self allocateList]; 1242 [self newChar:charStrings[i].name index:[self indexForCharName:charStrings[i].name]]; 1243 [self decodeCharString:charStrings[i].code length:charStrings[i].length]; 1244 1245 [self addStrokeList:pList toList:list]; 1246 [pList release]; // ?? grs in list will not copied 1247 } 1248 } 1249 return YES; 1250} 1251 1252int allreadySet; 1253- newChar:(char*)name index:(int)index 1254{ 1255 allreadySet = 0; 1256 gridCell.x = offset.x = (index/16+1) * gridOffset; 1257 offset.y = (15-(index%16)) * gridOffset + gridOffset*0.25; 1258 return self; 1259} 1260 1261- sidebearing:(int)x :(int)y width:(int)w 1262{ NSPoint begin, end; 1263 1264 sidebearing.x = x*scale.x; 1265 sidebearing.y = y*scale.y; 1266 1267 if (!allreadySet) 1268 { 1269 offset.x += (gridOffset-(w*scale.x))/2.0; 1270 allreadySet = 1; 1271 1272 begin.x = end.x = offset.x; 1273 begin.y = offset.y - (0.15*gridOffset); 1274 end.y = begin.y + (0.8*gridOffset); 1275 state.color = [NSColor colorWithCalibratedRed:0.0 green:1.0 blue:0.0 alpha:1.0]; 1276 [self addLine:begin :end toList:list]; 1277 begin.x = end.x = offset.x + (w*scale.x); 1278 [self addLine:begin :end toList:list]; 1279 state.color = [NSColor colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:1.0]; 1280 } 1281 return self; 1282} 1283 1284- hstem:(int)y :(int)dy 1285{ NSPoint begin, end; 1286 1287 begin.y = end.y = offset.y + y*scale.y + sidebearing.y; 1288 begin.x = gridCell.x + (0.05*gridOffset); 1289 end.x = begin.x + (0.9*gridOffset); 1290 state.color = [NSColor colorWithCalibratedRed:1.0 green:1.0 blue:0.0 alpha:1.0]; 1291 [self addLine:begin :end toList:list]; 1292 begin.y = end.y = offset.y + y*scale.y + dy*scale.y; 1293 [self addLine:begin :end toList:list]; 1294 state.color = [NSColor colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:1.0]; 1295 return self; 1296} 1297 1298- vstem:(int)x :(int)dx 1299{ NSPoint begin, end; 1300 1301 begin.x = end.x = offset.x + x*scale.x + sidebearing.x; 1302 begin.y = offset.y - (0.2*gridOffset); 1303 end.y = begin.y + (0.9*gridOffset); 1304 state.color = [NSColor colorWithCalibratedRed:1.0 green:1.0 blue:0.0 alpha:1.0]; 1305 [self addLine:begin :end toList:list]; 1306 begin.x = end.x += dx*scale.x; 1307 [self addLine:begin :end toList:list]; 1308 state.color = [NSColor colorWithCalibratedRed:0.0 green:0.0 blue:0.0 alpha:1.0]; 1309 return self; 1310} 1311 1312- moveto:(int)x :(int)y 1313{ 1314 startPoint.x = currentPoint.x = offset.x + x*scale.x; 1315 startPoint.y = currentPoint.y = offset.y + y*scale.y; 1316 [self updateBounds:currentPoint]; 1317 1318 return self; 1319} 1320 1321- lineto:(int)x :(int)y 1322{ NSPoint end; 1323 1324 end.x = offset.x + x*scale.x; 1325 end.y = offset.y + y*scale.y; 1326 if ( Diff(currentPoint.x, end.x) <= TOLERANCE && Diff(currentPoint.y, end.y) <= TOLERANCE ) 1327 return self; 1328 [self addLine:currentPoint :end toList:pList]; 1329 currentPoint = end; 1330 [self updateBounds:currentPoint]; 1331 1332 return self; 1333} 1334 1335- curveto:(int)x1 :(int)y1 :(int)x2 :(int)y2 :(int)x3 :(int)y3 1336{ NSPoint p1, p2, p3; 1337 1338 p1.x = offset.x + x1*scale.x; 1339 p1.y = offset.y + y1*scale.y; 1340 p2.x = offset.x + x2*scale.x; 1341 p2.y = offset.y + y2*scale.y; 1342 p3.x = offset.x + x3*scale.x; 1343 p3.y = offset.y + y3*scale.y; 1344 [self addCurve:currentPoint :p1 :p2 :p3 toList:pList]; 1345 currentPoint = p3; 1346 [self updateBounds:p1]; 1347 [self updateBounds:p2]; 1348 [self updateBounds:currentPoint]; 1349 1350 return self; 1351} 1352 1353- closepath 1354{ 1355 [self addLine:currentPoint :startPoint toList:pList]; 1356 return self; 1357} 1358 1359- (void)updateBounds:(NSPoint)p 1360{ 1361 ll.x = Min(ll.x, p.x); 1362 ll.y = Min(ll.y, p.y); 1363 ur.x = Max(ur.x, p.x); 1364 ur.y = Max(ur.y, p.y); 1365} 1366 1367- (void)dealloc 1368{ 1369 [super dealloc]; 1370} 1371 1372 1373/* methods to subclass 1374 */ 1375- allocateFontObject 1376{ 1377 return nil; 1378} 1379 1380- allocateList 1381{ 1382 return nil; 1383} 1384 1385- (void)addFillList:aList toList:bList 1386{ 1387 NSLog(@"filled path."); 1388} 1389 1390- (void)addStrokeList:aList toList:bList 1391{ 1392 NSLog(@"stroked path."); 1393} 1394 1395- (void)addLine:(NSPoint)beg :(NSPoint)end toList:aList 1396{ 1397 NSLog(@"line: %f %f %f %f", beg.x, beg.y, end.x, end.y); 1398} 1399 1400- (void)addArc:(NSPoint)center :(NSPoint)start :(float)angle toList:aList 1401{ 1402 NSLog(@"arc: %f %f %f %f %f", center.x, center.y, start.x, start.y, angle); 1403} 1404 1405- (void)addCurve:(NSPoint)p0 :(NSPoint)p1 :(NSPoint)p2 :(NSPoint)p3 toList:aList 1406{ 1407 NSLog(@"curve: %f %f %f %f %f %f %f %f", p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); 1408} 1409 1410/* allocate a text object and add it to aList 1411 * parameter: text the text string 1412 * font the font name 1413 * angle rotation angle 1414 * size the font size in pt 1415 * ar aspect ratio height/width 1416 * aList the destination list 1417 */ 1418- (void)addText:(NSString*)text :(NSString*)font :(float)angle :(float)size :(float)ar at:(NSPoint)p toList:aList 1419{ 1420 NSLog(@"text: %f %f %f %f %f \"%@\" \"%@\"", p.x, p.y, angle, size, ar, text, font); 1421} 1422 1423- (void)setBounds:(NSRect)bounds 1424{ 1425 NSLog(@"bounds: %f %f %f %f", bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height); 1426} 1427@end 1428