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 (&currentTime, 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