1/* EPSExport.m
2 *
3 * Copyright (C) 2000-2012 by vhf interservice GmbH
4 * Author:   Ilonka Fleischmann
5 *
6 * created:  2000-12-21
7 * modified: 2012-09-13 (-writeToFile: workaround: keep scaleFactor >= 1.0 not to clip graphics)
8 *           2012-02-07 (-writeToFile: use writeToFile:...encoding:error:)
9 *
10 * This file is part of the vhf Export 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 <AppKit/AppKit.h>
37#include <VHFShared/vhfCommonFunctions.h>
38#include <VHFShared/VHFStringAdditions.h> // stringByReplacing -> eps export, -writeToFile:... 10.5 compatibility
39#include "App.h"
40#include "EPSExport.h"
41#include "Graphics.h"
42#include "messages.h"
43#include "locations.h"
44#include "PreferencesMacros.h"
45
46@interface EPSExport(PrivateMethods)
47@end
48
49@implementation EPSExport
50
51+ (EPSExport*)epsExport
52{
53    return [[[EPSExport allocWithZone:[self zone]] init] autorelease];
54}
55
56/* initialize
57 */
58- init
59{
60    return [super init];
61}
62
63- (void)setDocumentView:view
64{
65    docView = view;
66}
67
68typedef struct _EPSState
69{
70    int		noPoint;
71    NSPoint	point;
72    BOOL	setBounds; // if we have an clipRect
73    float	maxW;
74    NSPoint	ll;
75    NSPoint	ur;
76    float	width;
77    NSColor	*color;
78    BOOL	fill;
79}EPSState;
80
81- (NSString*)epsLine:(VLine*)g :(EPSState*)epsState
82{   NSPoint	s, e;
83    NSString	*str=@"";
84    [(VLine*)g getVertices:&s :&e];
85
86    if ( /*!epsState->pathMode &&*/
87         (epsState->noPoint || Diff(epsState->point.x, s.x) > TOLERANCE || Diff(epsState->point.y, s.y) > TOLERANCE) )
88    {
89        if ( !epsState->noPoint )
90            str = [str stringByAppendingFormat:@"stroke\n"]; // so we stroke previous graphics
91
92        str = [str stringByAppendingFormat:@"%.0f %.0f moveto\n", s.x, s.y];
93        if ( epsState->setBounds )
94        {   if ( epsState->ll.x > s.x ) epsState->ll.x = s.x;
95            if ( epsState->ll.y > s.y ) epsState->ll.y = s.y;
96            if ( epsState->ur.x < s.x ) epsState->ur.x = s.x;
97            if ( epsState->ur.y < s.y ) epsState->ur.y = s.y;
98        }
99    }
100    str = [str stringByAppendingFormat:@"%.0f %.0f lineto\n", e.x, e.y];
101    if ( epsState->setBounds )
102    {   if ( epsState->ll.x > e.x ) epsState->ll.x = e.x;
103        if ( epsState->ll.y > e.y ) epsState->ll.y = e.y;
104        if ( epsState->ur.x < e.x ) epsState->ur.x = e.x;
105        if ( epsState->ur.y < e.y ) epsState->ur.y = e.y;
106    }
107    epsState->point = e;
108    epsState->noPoint = 0;
109    return str;
110}
111
112- (NSString*)epsRectangle:(VRectangle*)g :(EPSState*)epsState
113{   NSPoint	o, s;
114    NSString	*str=@"";
115    [(VRectangle*)g getVertices:&o :&s];
116
117    if ( !epsState->noPoint ) // else we have no previous graphics
118        str = [str stringByAppendingFormat:@"stroke\n"]; // so we stroke previous graphics
119
120//    if ( /*!epsState->pathMode &&*/
121//         (epsState->noPoint || Diff(epsState->point.x, o.x) > TOLERANCE || Diff(epsState->point.y, o.y) > TOLERANCE) )
122    {   str = [str stringByAppendingFormat:@"%.0f %.0f moveto\n", o.x, o.y];
123        if ( epsState->setBounds )
124        {   if ( epsState->ll.x > o.x ) epsState->ll.x = o.x;
125            if ( epsState->ll.y > o.y ) epsState->ll.y = o.y;
126        }
127    }
128    if ( [g filled] )
129        str = [str stringByAppendingFormat:@"%.0f %.0f %.0f %.0f rectfill\n", o.x, o.y, s.x, s.y];
130    else
131        str = [str stringByAppendingFormat:@"%.0f %.0f %.0f %.0f rectstroke\n", o.x, o.y, s.x, s.y];
132    if ( epsState->setBounds )
133    {   if ( epsState->ur.x < o.x+s.x ) epsState->ur.x = o.x+s.x;
134        if ( epsState->ur.y < o.y+s.y ) epsState->ur.y = o.y+s.y;
135    }
136    return str;
137}
138
139- (NSString*)epsArc:(VArc*)g :(EPSState*)epsState
140{   NSString	*str=@"";
141    NSPoint	c, s;
142    float	a, ba, r;
143
144    [(VArc*)g getCenter:&c start:&s angle:&a];
145    ba = [(VArc*)g begAngle];
146    r = [(VArc*)g radius];
147
148    if ( a == 360.0 && !epsState->noPoint ) // else we have no previous graphics
149        str = [str stringByAppendingFormat:@"stroke\n"]; // so we stroke previous graphics
150
151    if ( /*!epsState->pathMode &&*/
152         (epsState->noPoint || Diff(epsState->point.x, s.x) > TOLERANCE || Diff(epsState->point.y, s.y) > TOLERANCE) )
153        str = [str stringByAppendingFormat:@"%.0f %.0f moveto\n", s.x, s.y];
154    if ( a > 0 )
155    {   float           ea = ba+a;
156        if (ea > 360.0) ea -= 360.0;
157        str = [str stringByAppendingFormat:@"%.0f %.0f %.0f %.0f %.0f arc\n", c.x, c.y, r, ba, ea];
158    }
159    else
160    {   float           ea = ba+a;
161        if (ea < 0)     ea += 360.0;
162        str = [str stringByAppendingFormat:@"%.0f %.0f %.0f %.0f %.0f arcn\n", c.x, c.y, r, ba, ea];
163    }
164    if ( epsState->setBounds )
165    {   NSRect	b = [(VArc*)g bounds];
166        if ( epsState->ll.x > b.origin.x ) epsState->ll.x = b.origin.x;
167        if ( epsState->ll.y > b.origin.y ) epsState->ll.y = b.origin.y;
168        if ( epsState->ur.x < b.origin.x+b.size.width ) epsState->ur.x = b.origin.x+b.size.width;
169        if ( epsState->ur.y < b.origin.y+b.size.height ) epsState->ur.y = b.origin.y+b.size.height;
170    }
171    epsState->point = [(VArc*)g pointWithNum:-1]; // default is end
172    epsState->noPoint = 0;
173
174    return str;
175}
176
177- (NSString*)epsCurve:(VCurve*)g :(EPSState*)epsState
178{   NSPoint	p0, p1, p2, p3;
179    NSString	*str=@"";
180    [(VCurve*)g getVertices:&p0 :&p1 :&p2 :&p3];
181
182    if ( /*!epsState->pathMode &&*/
183         (epsState->noPoint || Diff(epsState->point.x, p0.x) > TOLERANCE || Diff(epsState->point.y, p0.y) > TOLERANCE) )
184        str = [str stringByAppendingFormat:@"%.0f %.0f moveto\n", p0.x, p0.y];
185    str = [str stringByAppendingFormat:@"%.0f %.0f %.0f %.0f %.0f %.0f curveto\n", p1.x, p1.y, p2.x, p2.y, p3.x, p3.y];
186    if ( epsState->setBounds )
187    {   NSRect	b = [(VArc*)g bounds];
188        if ( epsState->ll.x > b.origin.x ) epsState->ll.x = b.origin.x;
189        if ( epsState->ll.y > b.origin.y ) epsState->ll.y = b.origin.y;
190        if ( epsState->ur.x < b.origin.x+b.size.width ) epsState->ur.x = b.origin.x+b.size.width;
191        if ( epsState->ur.y < b.origin.y+b.size.height ) epsState->ur.y = b.origin.y+b.size.height;
192    }
193    epsState->point = p3;
194    epsState->noPoint = 0;
195    return str;
196}
197
198- (NSString*)epsPath:(VPath*)g :(EPSState*)epsState
199{   NSString	*str=@"";
200    int		i, cnt=[[g list] count];
201
202    if ( !cnt )
203        return str;
204
205    str = [str stringByAppendingFormat:@"gsave\n"];
206
207    str = [str stringByAppendingFormat:@"newpath\n"];
208epsState->noPoint = 1;
209    for (i=0; i<cnt; i++)
210    {   VGraphic	*gr = [[g list] objectAtIndex:i];
211
212//        if ( i == 1 ) epsState->pathMode = 1; // first graphic need a moveto
213
214        if (  [gr isKindOfClass:[VLine class]] )
215            str = [str stringByAppendingString:[self epsLine:(VLine*)gr :epsState]]; // move if possible and lineto
216        else if (  [gr isKindOfClass:[VRectangle class]] )
217            str = [str stringByAppendingString:[self epsRectangle:(VRectangle*)gr :epsState]];
218        else if (  [gr isKindOfClass:[VArc class]] )
219            str = [str stringByAppendingString:[self epsArc:(VArc*)gr :epsState]];
220        else if (  [gr isKindOfClass:[VCurve class]] )
221            str = [str stringByAppendingString:[self epsCurve:(VCurve*)gr :epsState]];
222        else NSLog(@"epsPath gr type not implemented\n");
223//        else if (  [g isKindOfClass:[VGroup class]] && [gr isKindOfClass:[VPath class]] )
224//            str = [str stringByAppendingString:[self epsPath:gr :epsState]];
225    }
226    str = [str stringByAppendingFormat:@"closepath\n"];
227    if ( [g filled] )
228        str = [str stringByAppendingFormat:@"eofill\n"];
229    else
230        str = [str stringByAppendingFormat:@"stroke\n"];
231
232    str = [str stringByAppendingFormat:@"grestore\n"]; // state is the same as before path !
233    return str;
234}
235
236- (NSString*)epsGroup:(VGroup*)g :(EPSState*)epsState
237{   NSString	*str=@"";
238    int		i, cnt=[[g list] count];
239
240    if ( !cnt )
241        return str;
242
243    for (i=0; i<cnt; i++)
244    {   VGraphic	*gr = [[g list] objectAtIndex:i];
245
246        if ( ![[gr color] isEqual:epsState->color] )
247        {
248            if ( [[[gr color] colorSpaceName] isEqual:@"NSCalibratedWhiteColorSpace"] )
249                str = [str stringByAppendingFormat:@"%.2f setgray\n", [[gr color] whiteComponent]];
250            else
251                str = [str stringByAppendingFormat:@"%.2f %.2f %.2f setrgbcolor\n",
252                    [[gr color] redComponent], [[gr color] greenComponent], [[gr color] blueComponent]];
253            epsState->color = [gr color];
254        }
255        if ( epsState->width != [gr width] )
256        {   str = [str stringByAppendingFormat:@"%.2f setlinewidth\n", [gr width]];
257            epsState->width = [gr width];
258            if ( epsState->maxW < epsState->width ) epsState->maxW = epsState->width;
259        }
260
261        if (  [gr isKindOfClass:[VLine class]] )
262            str = [str stringByAppendingString:[self epsLine:(VLine*)gr :epsState]]; // move if possible and lineto
263        else if (  [gr isKindOfClass:[VRectangle class]] )
264            str = [str stringByAppendingString:[self epsRectangle:(VRectangle*)gr :epsState]];
265        else if (  [gr isKindOfClass:[VArc class]] )
266            str = [str stringByAppendingString:[self epsArc:(VArc*)gr :epsState]];
267        else if (  [gr isKindOfClass:[VCurve class]] )
268            str = [str stringByAppendingString:[self epsCurve:(VCurve*)gr :epsState]];
269        else if (  [gr isKindOfClass:[VPath class]] )
270            str = [str stringByAppendingString:[self epsPath:(VPath*)gr :epsState]];
271        else if (  [gr isKindOfClass:[VGroup class]] )
272            str = [str stringByAppendingString:[self epsGroup:(VGroup*)gr :epsState]];
273        else NSLog(@"epsGroup gr type not implemented\n");
274    }
275    return str;
276}
277
278- (NSString*)epsImage:(VImage*)g :(EPSState*)epsState
279{   NSString	*str=@"";
280
281
282    return str;
283}
284
285- (NSString*)epsVText:(VText*)g :(EPSState*)epsState
286{   NSString	*str = @"";
287    NSRect	b = [g bounds];
288    NSPoint	o;
289    NSString	*curStr; // , *text = [g string];
290    BOOL	filled = [g filled];
291    float	lineHeight = [g lineHeight];
292    NSScanner		*scanner = [NSScanner scannerWithString:[g string]];
293    NSCharacterSet	*skipSet = [NSCharacterSet characterSetWithCharactersInString:@"\n\r"];
294    int			first = 1;
295
296    o = b.origin;
297    o.y += b.size.height - lineHeight;  // ???
298    str = [str stringByAppendingFormat:@"/%@ findfont\n", [[g font] fontName]];
299    str = [str stringByAppendingFormat:@"%.1f scalefont setfont\n", [[g font] pointSize]];
300    [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@""]];
301    [scanner scanCharactersFromSet:skipSet intoString:NULL];
302    while ( ![scanner isAtEnd] )
303    {
304        /* up to location != ' ' */
305        if ( ![scanner scanUpToCharactersFromSet:skipSet intoString:&curStr] )
306            curStr = @"";
307
308        curStr = [curStr stringByReplacing:@"\t" by:@"    "]; // \t -> 4 or 8 blanks
309
310        if ( !first ) o.y -= lineHeight;
311        first = 0;
312        if ( ![curStr length] ) continue; // simpy \n
313
314        if ( epsState->noPoint || Diff(epsState->point.x, o.x) > TOLERANCE || Diff(epsState->point.y, o.y) > TOLERANCE )
315            str = [str stringByAppendingFormat:@"%.0f %.0f moveto\n", o.x, o.y];
316        if ( filled )
317        {   str = [str stringByAppendingFormat:@"(%@) true charpath\n", curStr]; // true -> no stroke !
318            str = [str stringByAppendingFormat:@"eofill\n"];
319        }
320        else
321        {   str = [str stringByAppendingFormat:@"(%@) false charpath\n"];
322            str = [str stringByAppendingFormat:@"stroke\n"];
323        }
324        [scanner scanCharactersFromSet:skipSet intoString:NULL];
325    }
326    if ( filled ) str = [str stringByAppendingFormat:@"eofill\n"];
327    else          str = [str stringByAppendingFormat:@"stroke\n"];
328
329    if ( epsState->setBounds )
330    {   if ( epsState->ll.x > b.origin.x ) epsState->ll.x = b.origin.x;
331        if ( epsState->ll.y > b.origin.y ) epsState->ll.y = b.origin.y;
332        if ( epsState->ur.x < b.origin.x+b.size.width ) epsState->ur.x = b.origin.x+b.size.width;
333        if ( epsState->ur.y < b.origin.y+b.size.height ) epsState->ur.y = b.origin.y+b.size.height;
334    }
335    epsState->point = [g pointWithNum:PT_LOWERRIGHT];
336    epsState->noPoint = 0;
337    return str;
338}
339
340- (BOOL)writeToFile:(NSString*)filename
341{   BOOL            savedOk = NO;
342    NSString        *backupFilename;
343    NSFileManager   *fileManager = [NSFileManager defaultManager];
344
345    backupFilename = [[[filename stringByDeletingPathExtension] stringByAppendingString:@"~"]
346                      stringByAppendingPathExtension:EPS_EXT];
347    /* file not writable */
348    if ( [fileManager fileExistsAtPath:filename] && ![fileManager isWritableFileAtPath:filename] )
349    {   NSRunAlertPanel(SAVE_TITLE, CANT_CREATE_BACKUP, nil, nil, nil);
350        return NO;
351    }
352    /* rename to backup */
353    if ( ([fileManager fileExistsAtPath:backupFilename] && ![fileManager removeFileAtPath:backupFilename handler:nil]) || ([fileManager fileExistsAtPath:filename] && ![fileManager movePath:filename toPath:backupFilename handler:nil]) )
354    {   NSRunAlertPanel(SAVE_TITLE, CANT_CREATE_BACKUP, nil, nil, nil);
355        return NO;
356    }
357
358    {   NSArray		*list = [docView layerList];
359        NSString	*string;
360        NSData		*psData;
361        NSRect		bRect;
362        NSPoint		ll, ur;
363        int         i, cnt;
364        VFloat      sf = [docView scaleFactor];
365
366        // bRect = [docView bounds];
367        ll.x = ll.y = MAXCOORD;
368        ur.x = ur.y = 0.0;
369        cnt = [list count];
370        for (i=0; i<cnt; i++)
371        {   NSRect	rect;
372            LayerObject	*lObj = [list objectAtIndex:i];
373
374            //if ( [lObj type] == LAYER_CLIPPING ) // type
375            //    continue;
376            rect = [docView boundsOfArray:[lObj list]];
377            if ( ![lObj state] || (!rect.size.width && !rect.size.height) )
378                continue;
379            if ( ll.x > rect.origin.x ) ll.x = rect.origin.x;
380            if ( ll.y > rect.origin.y ) ll.y = rect.origin.y;
381            if ( ur.x < rect.origin.x+rect.size.width )  ur.x = rect.origin.x+rect.size.width;
382            if ( ur.y < rect.origin.y+rect.size.height ) ur.y = rect.origin.y+rect.size.height;
383        }
384        bRect.origin.x   = ll.x;        bRect.origin.y    = ll.y;
385        bRect.size.width = ur.x - ll.x; bRect.size.height = ur.y - ll.y;
386        //bRect.size.width  *= (sf > 0.0) ? (sf) : (1.0);
387        //bRect.size.height *= (sf > 0.0) ? (sf) : (1.0);
388        bRect.size.width  *= (sf < 1.0) ? (1.0) : (sf); // workaround Apple: otherwise graphics gets clipped
389        bRect.size.height *= (sf < 1.0) ? (1.0) : (sf);
390        psData = [docView dataWithEPSInsideRect:bRect];
391        string = [[NSString alloc] initWithData:psData encoding:NSASCIIStringEncoding];
392        //savedOk = [string writeToFile:filename atomically:YES];
393        savedOk = [string writeToFile:filename atomically:YES
394                             encoding:NSUTF8StringEncoding error:NULL]; // >= 10.5
395        [string release];
396    }
397
398#if 0
399    //else if ( isDirectory && [fileManager createDirectoryAtPath:filename attributes:nil] )
400    //fileDirectory = [filename stringByDeletingLastPathComponent];
401    /* save */
402    //if ([fileManager isWritableFileAtPath:filename])
403    {   NSString	*epsStr, *grStr=@"";
404        NSRect		clipRect;
405        id		layerList = [docView layerList];
406        int		i, lCnt = [layerList count];
407        EPSState	epsState;
408
409        epsState.noPoint = 1;
410        epsState.fill = 0;
411        epsState.color = [NSColor blackColor];
412        epsState.setBounds = 1;
413        epsState.ll.x = epsState.ll.y = MAXCOORD;
414        epsState.ur.x = epsState.ur.y = 0.0;
415        epsState.maxW = 0;
416
417        grStr = [grStr stringByAppendingFormat:@"1 setlinecap\n"];
418        grStr = [grStr stringByAppendingFormat:@"1 setlinejoin\n"];
419
420// 72 25400 div 72 25400 div scale\n
421
422        // first clip rect
423        clipRect.size.width = clipRect.size.height = 0.0;
424        clipRect = [[docView clipObject] bounds];
425        if ( clipRect.size.width && clipRect.size.height )
426        {   grStr = [grStr stringByAppendingFormat:@"%.0f %.0f %.0f %.0f rectclip\n", clipRect.origin.x, clipRect.origin.y, clipRect.size.width, clipRect.size.height];
427            epsState.ll.x = clipRect.origin.x;
428            epsState.ll.y = clipRect.origin.y;
429            epsState.ur.x = clipRect.origin.x + clipRect.size.width;
430            epsState.ur.y = clipRect.origin.y + clipRect.size.height;
431            epsState.setBounds = 0;
432        }
433        for (i=(int)[layerList count]-1 ; i >=0  ; i--)
434        {   LayerObject	*lObj = [layerList objectAtIndex:i];
435
436            if ([lObj type] == LAYER_CLIPPING || ![lObj state]) // visible - ????????????????????????
437                continue;
438            {   int	j, cnt = [[lObj list] count];
439
440// PageBoundingBox
441//        grStr = [grStr stringByAppendingFormat:@"%%Page: 0 1\n"]; // BeginPageSetup EndPageSetup PageTrailer
442                for (j=0; j<cnt; j++)
443                {   id	g = [[lObj list] objectAtIndex:j];
444
445                    if ( ![g isKindOfClass:[VGroup class]] && ![[g color] isEqual:epsState.color] )
446                    {
447                        if ( [[[g color] colorSpaceName] isEqual:@"NSCalibratedWhiteColorSpace"] )
448                            grStr = [grStr stringByAppendingFormat:@"%.2f setgray\n", [[g color] whiteComponent]];
449                        else
450                            grStr = [grStr stringByAppendingFormat:@"%.2f %.2f %.2f setrgbcolor\n",
451                                [[g color] redComponent], [[g color] greenComponent], [[g color] blueComponent]];
452                        epsState.color = [g color];
453                    }
454                    if ( ![g isKindOfClass:[VGroup class]] && epsState.width != [g width] )
455                    {   grStr = [grStr stringByAppendingFormat:@"%.2f setlinewidth\n", [g width]];
456                        epsState.width = [g width];
457                        if ( epsState.maxW < epsState.width ) epsState.maxW = epsState.width;
458                    }
459
460                    if (  [g isKindOfClass:[VLine class]] )
461                        grStr = [grStr stringByAppendingString:[self epsLine:g :&epsState]]; // move if possible and lineto
462                    else if (  [g isKindOfClass:[VCurve class]] )
463                        grStr = [grStr stringByAppendingString:[self epsCurve:(VCurve*)g :&epsState]];
464                    else if (  [g isKindOfClass:[VRectangle class]] )
465                        grStr = [grStr stringByAppendingString:[self epsRectangle:g :&epsState]];
466                    else if (  [g isKindOfClass:[VArc class]] )
467                        grStr = [grStr stringByAppendingString:[self epsArc:g :&epsState]];
468                    else if (  [g isKindOfClass:[VPath class]] )
469                        grStr = [grStr stringByAppendingString:[self epsPath:g :&epsState]];
470                    else if (  [g isKindOfClass:[VGroup class]] )
471                        grStr = [grStr stringByAppendingString:[self epsGroup:g :&epsState]];
472                    else if (  [g isKindOfClass:[VImage class]] )
473                        grStr = [grStr stringByAppendingString:[self epsImage:g :&epsState]];
474                    else if (  [g isKindOfClass:[VText class]] )
475                        grStr = [grStr stringByAppendingString:[self epsVText:(VText*)g :&epsState]];
476                }
477            }
478        }
479        if ( !epsState.noPoint && !epsState.fill )
480            grStr = [grStr stringByAppendingFormat:@"stroke\n"];
481        else if ( !epsState.noPoint && epsState.fill )
482            grStr = [grStr stringByAppendingFormat:@"eofill\n"];
483
484        epsState.ll.x -= epsState.maxW/2.0; epsState.ll.y -= epsState.maxW/2.0;
485        epsState.ur.x += epsState.maxW/2.0; epsState.ur.y += epsState.maxW/2.0;
486
487        // header
488        epsStr = [NSString stringWithFormat:@"%%!PS-Adobe-3.0 EPSF-3.0\n"];
489        epsStr = [epsStr stringByAppendingFormat:@"%%%%Creator: Cenon\n"];
490        epsStr = [epsStr stringByAppendingFormat:@"%%%%Copyright 2003 by vhf interservice - all rights reserved\n"];
491        epsStr = [epsStr stringByAppendingFormat:@"%%%%CreationDate: %@\n", [[NSCalendarDate date] descriptionWithCalendarFormat:@"%Y-%m-%d"]];
492        epsStr = [epsStr stringByAppendingFormat:@"%%%%BoundingBox: %.0f %.0f %.0f %.0f\n", epsState.ll.x, epsState.ll.y, epsState.ll.x+Diff(epsState.ll.x, epsState.ur.x), epsState.ll.y+Diff(epsState.ll.y, epsState.ur.y)];
493        epsStr = [epsStr stringByAppendingFormat:@"%%%%Pages: 0 1\n"];
494        epsStr = [epsStr stringByAppendingFormat:@"%%%%EndComments\n"];
495
496        // graphics
497        epsStr = [epsStr stringByAppendingString:grStr];
498
499        // Trailer
500        epsStr = [epsStr stringByAppendingFormat:@"showpage\n"];
501        epsStr = [epsStr stringByAppendingFormat:@"%%%%Trailer\n"];
502/*%DocumentFonts: Helvetica-Light
503%+ Helvetica-Bold
504%+ Helvetica*/
505
506        savedOk = [epsStr writeToFile:filename atomically:YES];
507    }
508    //else
509    //    NSRunAlertPanel(SAVE_TITLE, DIR_NOT_WRITABLE, nil, nil, nil);
510#endif
511
512    /* restore backup */
513    if (!savedOk)
514    {
515        [fileManager removeFileAtPath:filename handler:nil];	// remove what we just started to write
516        [fileManager movePath:backupFilename toPath:filename handler:nil];	// restore backup
517        NSRunAlertPanel(SAVE_TITLE, CANT_SAVE, nil, nil, nil);
518    }
519    else
520    {
521        if (Prefs_RemoveBackups)
522            [fileManager removeFileAtPath:backupFilename handler:nil];
523    }
524    return YES;
525}
526
527- (void)dealloc
528{
529    [super dealloc];
530}
531
532@end
533