1/* ICUTImport.m
2 * i-cut import object
3 *
4 * Copyright (C) 2012 by vhf interservice GmbH
5 * Author:   Ilonka Fleischmann
6 *
7 * created:  2011-09-16
8 * modified: 2012-06-22 (shape added, any layer with names possible)
9 *           2012-02-29 (ignore stuff after ops.cutcontour)
10 *           2012-02-13 (ops.cutcontour like ops.corner, state.path = 0 after else)
11 *
12 * This file is part of the vhf Import Library.
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the vhf Public License as
16 * published by the vhf interservice GmbH. Among other things,
17 * the License requires that the copyright notices and this notice
18 * be preserved on all copies.
19 *
20 * This library is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23 * See the vhf Public License for more details.
24 *
25 * You should have received a copy of the vhf Public License along
26 * with this library; see the file LICENSE. If not, write to vhf.
27 *
28 * If you want to link this library to your proprietary software,
29 * or for other uses which are not covered by the definitions
30 * laid down in the vhf Public License, vhf also offers a proprietary
31 * license scheme. See the vhf internet pages or ask for details.
32 *
33 * vhf interservice GmbH, Im Marxle 3, 72119 Altingen, Germany
34 * eMail: info@vhf.de
35 * http://www.vhf.de
36 */
37
38#include <math.h>
39
40#include "ICUTImport.h"
41#include "functions.h"
42//#include "../VHFShared/vhfCFGFunctions.h"
43#include "../VHFShared/types.h"
44
45#define DIGITS		@".+-0123456789"
46#define JUMPDIGITS	@",.+-0123456789"
47#define NOP		@" \t\r\n,"
48
49/* r in points/inch */
50#define	InternalToDeviceRes(a, r)	((float)(a) * (float)(r) / 72.0)
51#define	DeviceResToInternal(a, r)	((float)(a) * 72.0 / (float)(r))
52
53//static int linePattern[9][9] = {{0, -1, -1, -1, -1, -1, -1, -1, -1}, {0, 100, -1, -1, -1, -1, -1, -1, -1}, {50, 50, -1, -1, -1, -1, -1, -1, -1}, {70, 30, -1, -1, -1, -1, -1, -1, -1}, {80, 10, 0, 10, -1, -1, -1, -1, -1}, {70, 10, 10, 10, -1, -1, -1, -1, -1}, {50, 10, 10, 10, 10, 10, -1, -1, -1}, {70, 10, 0, 10, 0, 10, -1, -1, -1}, {50, 10, 0, 10, 10, 10, 0, 10, -1}};
54
55//static void addToBeginNew(NSString *newOp, NSMutableString *beginNew);
56
57@interface ICUTImport(PrivateMethods)
58- (float)unitFromUnit:(int)u :(float)v;
59- (void)initParameter;
60- (BOOL)setUnitFromData:(NSData*)icutData;
61- (BOOL)interpret:(NSString*)dataP;
62- (BOOL)getGraphicFromData:(NSScanner*)scanner :cList;
63//- (void)setPath:(NSScanner*)scanner :cList;
64- (void)setPath:(NSScanner*)scanner :(NSString*)layerName;
65- (void)updateBounds:(NSPoint)p;
66@end
67
68@implementation ICUTImport
69
70- (float)unitFromUnit:(int)u :(float)v
71{
72    switch ( u )
73    {
74        case UNIT_MM:
75            switch ( unit )
76            {
77                case UNIT_MM:		return v;                   // mm to mm
78                case UNIT_INCH:		return ((v)*25.4);          // inch to mm
79                default:            return ((v) / 72.0*25.4);   // point to mm
80            }
81        case UNIT_INCH:
82            switch ( unit )
83            {
84                case UNIT_MM:		return ((v)/25.4);  // mm to inch
85                case UNIT_INCH:		return v;           // inch to inch
86                default:            return ((v)/72.0);  // point/internal to inch ??????
87            }
88        default:
89            switch ( unit )
90            {
91                case UNIT_MM:		return ((v)*72.0/25.4); // mm to point
92                case UNIT_INCH:		return ((v)*72.0);      // inch to point
93                default:            return v;               // point point do degree to point
94            }
95    }
96    return v;
97}
98
99- init
100{
101    [super init];
102
103    unit = UNIT_INCH;
104    fillClosedPaths = NO;
105    originUL = NO;
106
107    state.mode = 0;
108    state.path = 0;
109    state.pindex = 0;
110    state.p0.x = state.p0.y = 0.0;
111    state.p1.x = state.p1.y = 0.0;
112    state.p2.x = state.p2.y = 0.0;
113    state.p3.x = state.p3.y = 0.0;
114
115    return self;
116}
117
118/* created:   03.05.96
119 * modified:  09.03.97
120 * parameter: fileName
121 * purpose:   load parameter file
122 */
123- (void)initParameter
124{
125    ops.moveto = @"Moveto";
126    ops.lineto = @"Lineto";
127    ops.regmark = @"Regmark";
128    ops.shape = @"Shape";
129    ops.corner = @"Corner";
130    ops.bezier = @"Bezier";
131    ops.open   = @"Open";
132    ops.closed = @"Closed";
133    ops.cutcontour = @"CutContour";
134    ops.comma  = @",";
135    ops.termi  = @"\n";
136}
137
138- (void)fillClosedPaths:(BOOL)flag
139{
140    fillClosedPaths = flag;
141}
142- (void)originUL:(BOOL)flag
143{
144    originUL = flag;
145}
146/*- (void)changeXY:(BOOL)flag
147{
148    changeXY = flag;
149}*/
150
151- (BOOL)setUnitFromData:(NSData*)icutData
152{   NSString		*dataStr;
153    NSMutableString *unitLineStr = nil;
154    NSScanner		*scanner;
155    NSRange         range;
156    int             location, firstLoc;
157
158    dataStr = [[[NSString alloc] initWithData:icutData encoding:NSASCIIStringEncoding] autorelease];
159
160    //if ( !(toolData = [NSMutableString stringWithContentsOfFile:fileName]) )
161    if ( !dataStr )
162        return NO;
163
164    scanner = [NSScanner scannerWithString:dataStr];
165    [scanner setCaseSensitive:YES];
166    firstLoc  = location = [scanner scanLocation];
167
168    [scanner scanUpToString:@"i-cut script" intoString:NULL];
169    if ( ![scanner scanString:@"i-cut script" intoString:NULL] )
170    {   NSLog(@"ICUTImport: No i-cut File");
171        return NO;
172    }
173    [scanner scanUpToString:@"SystemUnits" intoString:NULL];
174    [scanner scanUpToCharactersFromSet:termiSet intoString:&unitLineStr];
175
176    range = [unitLineStr rangeOfString:@"Inch"];
177    if ( range.length )
178        unit = UNIT_INCH; // inch
179    else
180//    range = [unitLineStr rangeOfString:@"MM"];    // fixme: we dont know mm or MM
181//    if ( range.length )
182        unit = UNIT_MM; // millimeters
183
184    return YES;
185}
186
187/* created:   1996-01-25
188 * modified:  2002-10-26
189 * parameter: hpglData	the HPGL data stream
190 * purpose:   start interpretation of the contents of hpglData
191 */
192- importICUT:(NSData*)icutData
193{
194    [self initParameter];
195
196    digitsSet = [NSCharacterSet characterSetWithCharactersInString:DIGITS];
197    invDigitsSet = [digitsSet invertedSet];
198    jumpSet = [NSCharacterSet characterSetWithCharactersInString:JUMPDIGITS];
199    termiSet = [NSCharacterSet characterSetWithCharactersInString:ops.termi];
200    newLineSet = [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
201
202    if ( ![self setUnitFromData:icutData] )
203        return nil;
204
205    /* interpret data */
206    if ( ![self interpret:[[[NSString alloc] initWithData:icutData
207                                                 encoding:NSASCIIStringEncoding] autorelease]] )
208        return nil;
209
210    return [list autorelease];
211}
212
213/* private methods
214 */
215- (BOOL)interpret:(NSString*)dataP
216{   NSRect          bounds;
217    NSScanner       *scanner = [NSScanner scannerWithString:dataP];
218    NSCharacterSet  *skipSet = [NSCharacterSet characterSetWithCharactersInString:NOP];
219
220    /* init bounds */
221    ll.x = ll.y = LARGE_COORD;
222    ur.x = ur.y = LARGENEG_COORD;
223
224    list = [self allocateList];
225
226    [scanner setCharactersToBeSkipped:skipSet];
227    while ( ![scanner isAtEnd] )
228        if ( ![self getGraphicFromData:scanner :list] )
229            break;
230
231
232    bounds.origin = ll;
233    bounds.size.width = ur.x - ll.x;
234    bounds.size.height = ur.y - ll.y;
235    [self setBounds:bounds];
236
237    return YES;
238}
239
240/* the graphics list
241 */
242- (id)list;
243{
244    return list;
245}
246
247/* we need cp on a number !
248 * modified: 2008-06-15
249 */
250- (BOOL)getGraphicFromData:(NSScanner*)scanner :cList
251{   int location;
252
253    /* one line per -getGraphicFromData: */
254
255    location = [scanner scanLocation];
256    if ( [scanner scanString:ops.regmark intoString:NULL] ) // RegMark f, f, RegMark
257    {   float   xval, yval;
258
259        if ( state.path )
260        {
261            [scanner setScanLocation:location];
262            state.path = 0;
263            return YES; // return to -setPath:
264        }
265        [scanner scanFloat:&xval];
266        [scanner scanString:ops.comma intoString:NULL];
267        [scanner scanFloat:&yval];
268        [scanner scanString:ops.comma intoString:NULL];
269        if ( [scanner scanString:ops.regmark intoString:NULL] )
270        {
271            state.p0.x = [self unitFromUnit:UNIT_POINT :xval]; // get internal from Inch
272            state.p0.y = [self unitFromUnit:UNIT_POINT :yval]; // get internal from Inch
273            /*if ( changeXY )
274            {   float   bufy = state.p0.y;
275
276                state.p0.y = state.p0.x;
277                state.p0.x = bufy;
278            }*/
279            [self updateBounds:state.p0];
280            //[self addMark:state.p0 toList:cList];
281            [self addMark:state.p0 toLayer:nil];
282            return YES;
283        }
284        else return NO;
285    }
286    else if ( [scanner scanString:ops.shape intoString:NULL] ) // Shape f, f, f, f, 1, LayerName
287    {   float           xval, yval, wval, hval;
288        int             intval;
289        NSMutableString *layerName = nil;
290
291        if ( state.path )
292        {
293            [scanner setScanLocation:location];
294            state.path = 0;
295            return YES; // return to -setPath:
296        }
297        [scanner scanFloat:&xval];
298        [scanner scanString:ops.comma intoString:NULL];
299        [scanner scanFloat:&yval];
300        [scanner scanString:ops.comma intoString:NULL];
301        [scanner scanFloat:&wval];
302        [scanner scanString:ops.comma intoString:NULL];
303        [scanner scanFloat:&hval];
304        [scanner scanString:ops.comma intoString:NULL];
305        [scanner scanInt:&intval];  // no idea what this is
306        [scanner scanString:ops.comma intoString:NULL];
307        //if ( [scanner scanString:ops.regmark intoString:NULL] )
308        if ( [scanner scanUpToCharactersFromSet:newLineSet intoString:&layerName] )
309        {   NSPoint rsize;
310
311            state.p0.x = [self unitFromUnit:UNIT_POINT :xval]; // get internal from Inch
312            state.p0.y = [self unitFromUnit:UNIT_POINT :yval]; // get internal from Inch
313            rsize.x = [self unitFromUnit:UNIT_POINT :wval]; // get internal from Inch
314            rsize.y = [self unitFromUnit:UNIT_POINT :hval]; // get internal from Inch
315            /*if ( changeXY )
316            {   float   bufy = state.p0.y;
317
318                state.p0.y = state.p0.x;
319                state.p0.x = bufy;
320            }*/
321            [self updateBounds:state.p0];
322            [self updateBounds:NSMakePoint(state.p0.x+rsize.x, state.p0.y)];
323            [self updateBounds:NSMakePoint(state.p0.x+rsize.x, state.p0.y+rsize.y)];
324            [self updateBounds:NSMakePoint(state.p0.x, state.p0.y+rsize.y)];
325            [self addRect:state.p0 :rsize toLayer:layerName];
326            return YES;
327        }
328        else return NO;
329    }
330    else if ( [scanner scanString:ops.moveto intoString:NULL] ) // Moveto f, f, Open/Closed, CutContour
331    {   float           xval, yval;
332        NSMutableString *layerName = nil;
333
334        if ( state.path )
335        {
336            [scanner setScanLocation:location];
337            state.path = 0;
338            return YES; // return to -setPath:
339        }
340        state.path = 1;
341
342        [scanner scanFloat:&xval];
343        [scanner scanString:ops.comma intoString:NULL];
344        [scanner scanFloat:&yval];
345        [scanner scanString:ops.comma intoString:NULL];
346        if ( [scanner scanString:ops.open intoString:NULL] )
347            state.mode = 0; // openPath
348        if ( [scanner scanString:ops.closed intoString:NULL] )
349            state.mode = 1; // openPath
350        //if ( [scanner scanString:ops.cutcontour intoString:NULL] )
351        if ( [scanner scanUpToCharactersFromSet:newLineSet intoString:&layerName] )
352        {
353            state.p0.x = [self unitFromUnit:UNIT_POINT :xval]; // get internal from Inch
354            state.p0.y = [self unitFromUnit:UNIT_POINT :yval]; // get internal from Inch
355            state.pindex = 1; // p0 ist set, next is p1
356            [self updateBounds:state.p0];
357            //[self setPath:scanner :cList];
358            [self setPath:scanner :layerName];
359            return YES;
360        }
361        else return NO;
362    }
363    else if ( [scanner scanString:ops.lineto intoString:NULL] ) // Lineto f, f, Corner/Bezier
364    {   float   xval, yval;
365
366        [scanner scanFloat:&xval];
367        [scanner scanString:ops.comma intoString:NULL];
368        [scanner scanFloat:&yval];
369        [scanner scanString:ops.comma intoString:NULL];
370        if ( [scanner scanString:ops.bezier intoString:NULL] )
371        {
372            switch (state.pindex)
373            {   case 1:
374                        state.p1.x = [self unitFromUnit:UNIT_POINT :xval]; // get internal from Inch
375                        state.p1.y = [self unitFromUnit:UNIT_POINT :yval]; // get internal from Inch
376                        state.pindex = 2; // first crv pt, next is second crv pt
377                        [self updateBounds:state.p1];
378                        break;
379                default:
380                //case 2:
381                        state.p2.x = [self unitFromUnit:UNIT_POINT :xval]; // get internal from Inch
382                        state.p2.y = [self unitFromUnit:UNIT_POINT :yval]; // get internal from Inch
383                        state.pindex = 3; // second crv pt, next is end pt
384                        [self updateBounds:state.p2];
385                        break;
386            }
387            return YES;
388        }
389        else //if ( [scanner scanString:ops.corner intoString:NULL] ||     // Corner
390             //[scanner scanString:ops.cutcontour intoString:NULL] )  // CutContour - mh ?
391        {
392            switch (state.pindex)
393            {   case 1:
394                        state.p1.x = [self unitFromUnit:UNIT_POINT :xval]; // get internal from Inch
395                        state.p1.y = [self unitFromUnit:UNIT_POINT :yval]; // get internal from Inch
396                        state.pindex = 1; // line end already 1
397                        [self updateBounds:state.p1];
398                        [self addLine:state.p0 :state.p1 toList:cList];
399                        state.p0 = state.p1; // is our new p0 !
400                        break;
401                default:
402                //case 3:
403                        state.p3.x = [self unitFromUnit:UNIT_POINT :xval]; // get internal from Inch
404                        state.p3.y = [self unitFromUnit:UNIT_POINT :yval]; // get internal from Inch
405                        state.pindex = 1; // curve end already 1
406                        [self updateBounds:state.p3];
407                        [self addCurve:state.p0 :state.p1 :state.p2 :state.p3 toList:cList];
408                        state.p0 = state.p3; // is our new p0 !
409                        break;
410            }
411            return YES;
412        }
413       /* else if ( [scanner scanString:ops.bezier intoString:NULL] )
414        {
415            switch (state.pindex)
416            {   case 1:
417                        state.p1.x = [self unitFromUnit:UNIT_POINT :xval]; // get internal from Inch
418                        state.p1.y = [self unitFromUnit:UNIT_POINT :yval]; // get internal from Inch
419                        state.pindex = 2; // first crv pt, next is second crv pt
420                        [self updateBounds:state.p1];
421                        break;
422                default:
423                //case 2:
424                        state.p2.x = [self unitFromUnit:UNIT_POINT :xval]; // get internal from Inch
425                        state.p2.y = [self unitFromUnit:UNIT_POINT :yval]; // get internal from Inch
426                        state.pindex = 3; // second crv pt, next is end pt
427                        [self updateBounds:state.p2];
428                        break;
429            }
430            return YES;
431        }
432        else return NO;*/
433    }
434    else if ( [scanner scanUpToString:ops.termi intoString:NULL] )
435    {
436        /* if ( state.path ) // hier nicht evtl muell hinter CutContour verhindert sonst Pfadbildung
437        {
438            [scanner setScanLocation:location];
439            state.path = 0;
440            return YES; // return to -setPath:
441        }*/
442        return YES; // continue;
443    }
444    else if ( state.path )
445    {
446        [scanner setScanLocation:location];
447        state.path = 0;
448        return YES; // return to -setPath:
449    }
450    return YES;
451}
452
453/*
454 * created:   07.05.93
455 * modified:  17.06.93 05.05.96
456 * purpose:   set arc ccw
457 * parameter: g
458 */
459//- (void)setPath:(NSScanner*)scanner :cList // cList is the array of the layer here
460- (void)setPath:(NSScanner*)scanner :(NSString*)layerName // cList is the array of the layer here
461{   NSMutableArray	*myList;				/* current list */
462
463    [scanner scanString:ops.termi intoString:NULL]; // little bit faster
464
465    myList = [[[NSMutableArray allocWithZone:[self zone]] init] autorelease];
466    while (1)
467    {
468        if ( ![self getGraphicFromData:scanner :myList] )
469            break;
470
471        if ( !state.path ) // end of path ( new Moveto)
472            break;
473    }
474    //add to layer
475    if ( [myList count] > 1 )
476    {
477        if ( state.mode == 1 && fillClosedPaths ) // openPath
478            //[self addFillList:myList toList:cList];
479            [self addFillList:myList toLayer:layerName];
480        else
481            //[self addStrokeList:myList toList:cList];
482            [self addStrokeList:myList toLayer:layerName];
483        [myList removeAllObjects];
484    }
485    else if ( [myList count] ) // add single graphic in myList to cList
486    {
487        //[cList addObject:[myList objectAtIndex:0]];
488        [self addStrokeList:myList toLayer:layerName]; // add only the single graphic
489        [myList removeAllObjects];
490    }
491}
492
493- (void)updateBounds:(NSPoint)p
494{
495    ll.x = Min(ll.x, p.x);
496    ll.y = Min(ll.y, p.y);
497    ur.x = Max(ur.x, p.x);
498    ur.y = Max(ur.y, p.y);
499}
500
501- (void)dealloc
502{
503    [super dealloc];
504}
505
506/* methods to be subclassed
507 */
508- (id)allocateList
509{
510    return nil;
511}
512
513- (void)addFillList:aList toLayer:(NSString*)layerNamer
514{
515    NSLog(@"filled path. to layer");
516}
517- (void)addFillList:aList toList:(NSMutableArray*)bList
518{
519    NSLog(@"filled path.");
520}
521
522- (void)addStrokeList:aList toLayer:(NSString*)layerNamer
523{
524    NSLog(@"stroked path. to layer");
525}
526- (void)addStrokeList:aList toList:(NSMutableArray*)bList
527{
528    NSLog(@"stroked path.");
529}
530
531- (void)addLine:(NSPoint)beg :(NSPoint)end toLayer:(NSString*)layerNamer
532{
533    NSLog(@"line: %f %f %f %f, to layer", beg.x, beg.y, end.x, end.y);
534}
535- (void)addLine:(NSPoint)beg :(NSPoint)end toList:(NSMutableArray*)aList
536{
537    NSLog(@"line: %f %f %f %f", beg.x, beg.y, end.x, end.y);
538}
539
540- (void)addMark:(NSPoint)origin toLayer:(NSString*)layerNamer
541{
542    NSLog(@"regmark: %f %f, to layer", origin.x, origin.y);
543}
544- (void)addMark:(NSPoint)origin toList:(NSMutableArray*)aList
545{
546    NSLog(@"regmark: %f %f", origin.x, origin.y);
547}
548
549- (void)addRect:(NSPoint)origin :(NSPoint)rsize toLayer:(NSString*)layerNamer
550{
551    NSLog(@"rectangle: %f %f %f %f, to layer", origin.x, origin.y, rsize.x, rsize.y);
552}
553- (void)addRect:(NSPoint)origin :(NSPoint)rsize toList:(NSMutableArray*)aList
554{
555    NSLog(@"rectangle: %f %f %f %f", origin.x, origin.y, rsize.x, rsize.y);
556}
557
558- (void)addCurve:(NSPoint)p0 :(NSPoint)p1 :(NSPoint)p2 :(NSPoint)p3 toLayer:(NSString*)layerNamer
559{
560    NSLog(@"curve: %f %f %f %f %f %f %f %f, to layer", p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
561}
562- (void)addCurve:(NSPoint)p0 :(NSPoint)p1 :(NSPoint)p2 :(NSPoint)p3 toList:(NSMutableArray*)aList
563{
564    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);
565}
566
567- (void)setBounds:(NSRect)bounds
568{
569    NSLog(@"bounds: %f %f %f %f", bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
570}
571@end
572