1/* propertyList.m
2 * Functions helping to read and write property lists
3 *
4 * Copyright (C) 2002-2012 by vhf interservice GmbH
5 * Author:   Georg Fleischmann
6 *
7 * created:  ?
8 * modified: 2009-12-12 (save coordinated with 9 digits precision instead of 6 before)
9 *           2008-01-12 (propertyListFromNSColor() Apple >= 10.4 workaround)
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the vhf Public License as
13 * published by vhf interservice GmbH. Among other things, the
14 * License requires that the copyright notices and this notice
15 * be preserved on all copies.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 * See the vhf Public License for more details.
21 *
22 * You should have received a copy of the vhf Public License along
23 * with this program; see the file LICENSE. If not, write to vhf.
24 *
25 * vhf interservice GmbH, Im Marxle 3, 72119 Altingen, Germany
26 * eMail: info@vhf.de
27 * http://www.vhf.de
28 */
29
30#include <AppKit/AppKit.h>
31#include <VHFShared/VHFDictionaryAdditions.h>
32#include <VHFShared/vhfCommonFunctions.h>	// vhfStringWithFloat()
33#include <VHFShared/vhfCompatibility.h>     // CGFloat for OS X 10.4
34#include "propertyList.h"
35
36#ifndef NSAppKitVersionNumber10_4
37#  define NSAppKitVersionNumber10_4 824
38#endif
39
40/* we don't always have VGraphic objects, so we declare the used method here */
41@interface PropertyList
42- (id)initFromPropertyList:(id)plist inDirectory:(NSString*)directory;
43@end
44@implementation PropertyList
45- (id)initFromPropertyList:(id)plist inDirectory:(NSString*)directory	{ return nil; }
46@end
47
48/* backward compatibility before moving to new classnames
49 */
50NSString *newClassName(NSString *className)
51{
52    if ([className isEqual:@"TextGraphic"])
53        return @"VText";
54    return [@"V" stringByAppendingString:className];
55}
56
57id propertyListFromArray(NSArray *array)
58{   id              realObject;
59    NSMutableArray  *plistArray;
60    NSEnumerator    *enumerator;
61
62    plistArray = [NSMutableArray arrayWithCapacity:[array count]];
63    enumerator = [array objectEnumerator];
64    while ((realObject = [enumerator nextObject]))
65    {
66        if ([realObject respondsToSelector:@selector(propertyList)])
67            [plistArray addObject:[realObject propertyList]];
68        else
69        {
70            /* Should probably raise here. */
71        }
72    }
73    return plistArray;
74}
75
76id propertyListFromFloat(float f)
77{
78    return [NSString stringWithFormat:@"%.12g", f];
79}
80
81id propertyListFromInt(int i)
82{
83    return [NSString stringWithFormat:@"%d", i];
84}
85
86/* modified: 2008-01-11 (Apple workaround)
87 */
88id propertyListFromNSColor(NSColor *color)
89{   NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:5];
90
91    if ([[color colorSpaceName] isEqualToString:NSCalibratedWhiteColorSpace])
92    {
93        [dictionary setObject:@"CalWhite" forKey:@"s"];
94        [dictionary setObject:vhfStringWithFloat([color alphaComponent]) forKey:@"A"];
95        [dictionary setObject:vhfStringWithFloat([color whiteComponent]) forKey:@"W"];
96    }
97    else if ( [[color colorSpaceName] isEqualToString:NSCalibratedRGBColorSpace] )
98    {
99        [dictionary setObject:@"CalRGB" forKey:@"s"];
100        [dictionary setObject:vhfStringWithFloat([color alphaComponent]) forKey:@"A"];
101        [dictionary setObject:vhfStringWithFloat([color blueComponent])  forKey:@"B"];
102        [dictionary setObject:vhfStringWithFloat([color greenComponent]) forKey:@"G"];
103        [dictionary setObject:vhfStringWithFloat([color redComponent])   forKey:@"R"];
104    }
105    else if ([[color colorSpaceName] isEqualToString:NSDeviceWhiteColorSpace])
106    {
107        [dictionary setObject:@"DevWhite" forKey:@"s"];
108        [dictionary setObject:vhfStringWithFloat([color alphaComponent]) forKey:@"A"];
109        [dictionary setObject:vhfStringWithFloat([color whiteComponent]) forKey:@"W"];
110    }
111    else if ([[color colorSpaceName] isEqualToString:NSDeviceRGBColorSpace])
112    {
113        [dictionary setObject:@"DevRGB" forKey:@"s"];
114        [dictionary setObject:vhfStringWithFloat([color alphaComponent]) forKey:@"A"];
115        [dictionary setObject:vhfStringWithFloat([color blueComponent])  forKey:@"B"];
116        [dictionary setObject:vhfStringWithFloat([color greenComponent]) forKey:@"G"];
117        [dictionary setObject:vhfStringWithFloat([color redComponent])   forKey:@"R"];
118    }
119    else if ([[color colorSpaceName] isEqualToString:NSDeviceCMYKColorSpace])
120    {
121        [dictionary setObject:@"DevCMYK" forKey:@"s"];
122        [dictionary setObject:vhfStringWithFloat([color alphaComponent])   forKey:@"A"];
123        [dictionary setObject:vhfStringWithFloat([color cyanComponent])    forKey:@"C"];
124        [dictionary setObject:vhfStringWithFloat([color magentaComponent]) forKey:@"M"];
125        [dictionary setObject:vhfStringWithFloat([color yellowComponent])  forKey:@"Y"];
126        [dictionary setObject:vhfStringWithFloat([color blackComponent])   forKey:@"B"];
127    }
128    else if ([[color colorSpaceName] isEqualToString:NSNamedColorSpace])
129    {
130        [dictionary setObject:@"Named" forKey:@"s"];
131        [dictionary setObject:[color catalogNameComponent]          forKey:@"CId"];
132        [dictionary setObject:[color colorNameComponent]            forKey:@"NId"];
133        [dictionary setObject:[color localizedCatalogNameComponent] forKey:@"Catalog"];
134        [dictionary setObject:[color localizedColorNameComponent]   forKey:@"Name"];
135    }
136#ifdef __APPLE__   // Fat: a 2nd layer of same functionality added in Mac OS 10.4
137    else if ( NSAppKitVersionNumber >= NSAppKitVersionNumber10_4 &&   // >= 10.4
138              [[color colorSpaceName] isEqualToString:NSCustomColorSpace] )
139    {   int     cnt = [color numberOfComponents];
140        CGFloat compo[cnt];
141
142        [color getComponents:compo];
143        switch ([[color colorSpace] colorSpaceModel])
144        {
145            case NSGrayColorSpaceModel: // NSCustomColorSpace Generic Gray colorspace 0.5 1
146                [dictionary setObject:@"CalWhite" forKey:@"s"];
147                [dictionary setObject:vhfStringWithFloat(compo[0]) forKey:@"W"];
148                [dictionary setObject:vhfStringWithFloat(compo[1]) forKey:@"A"];
149                break;
150                //return propertyListFromNSColor([color colorUsingColorSpace:[NSColorSpace deviceGrayColorSpace]]);
151            case NSRGBColorSpaceModel:
152                [dictionary setObject:@"DevRGB" forKey:@"s"];
153                [dictionary setObject:vhfStringWithFloat(compo[0]) forKey:@"R"];
154                [dictionary setObject:vhfStringWithFloat(compo[1]) forKey:@"G"];
155                [dictionary setObject:vhfStringWithFloat(compo[2]) forKey:@"B"];
156                [dictionary setObject:vhfStringWithFloat(compo[3]) forKey:@"A"];
157                break;
158            case NSCMYKColorSpaceModel:
159                [dictionary setObject:@"DevCMYK" forKey:@"s"];
160                [dictionary setObject:vhfStringWithFloat(compo[0]) forKey:@"C"];
161                [dictionary setObject:vhfStringWithFloat(compo[1]) forKey:@"M"];
162                [dictionary setObject:vhfStringWithFloat(compo[2]) forKey:@"Y"];
163                [dictionary setObject:vhfStringWithFloat(compo[3]) forKey:@"B"];
164                [dictionary setObject:vhfStringWithFloat(compo[4]) forKey:@"A"];
165                break;
166            //case NSLABColorSpaceModel:
167            //case NSDeviceNColorSpaceModel:
168            default:
169                break;
170        }
171    }
172#endif
173    /*else
174    {
175        [dictionary setObject:@"Unknown" forKey:@"s"];
176        [dictionary setObject:[NSArchiver archivedDataWithRootObject:color] forKey:@"Data"];
177    }*/
178    return dictionary;
179}
180
181id propertyListFromNSPrintInfo(NSPrintInfo *printInfo)
182{   NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:5];
183
184    if (!printInfo)
185        return dictionary;
186    [dictionary setFloat:[printInfo leftMargin] forKey:@"leftMargin"];
187    [dictionary setFloat:[printInfo rightMargin] forKey:@"rightMargin"];
188    [dictionary setFloat:[printInfo topMargin] forKey:@"topMargin"];
189    [dictionary setFloat:[printInfo bottomMargin] forKey:@"bottomMargin"];
190    [dictionary setObject:propertyListFromNSSize([printInfo paperSize]) forKey:@"paperSize"];
191    [dictionary setObject:[printInfo paperName] forKey:@"paperName"];
192    [dictionary setInt:[printInfo orientation] forKey:@"orientation"];
193
194    return dictionary;
195}
196
197id propertyListFromNSRect(NSRect rect)
198{
199    return [NSString stringWithFormat:@"%.12g %.12g %.12g %.12g", rect.origin.x, rect.origin.y, rect.size.width, rect.size.height];
200}
201
202id propertyListFromNSSize(NSSize size)
203{
204    return [NSString stringWithFormat:@"%.12g %.12g", size.width, size.height];
205}
206
207id propertyListFromNSPoint(NSPoint point)
208{   //int digX = 9;
209
210    //if ( floor(point.x*1000.0)/1000.0 == point.x )
211    //    digX = 6;
212    return [NSString stringWithFormat:@"%.12g %.12g", point.x, point.y];
213}
214
215id propertyListFromV3Point(V3Point point)
216{
217    return [NSString stringWithFormat:@"%.12g %.12g %.12g", point.x, point.y, point.z];
218}
219
220NSMutableArray *arrayFromPropertyList(id plist, NSString *directory, NSZone *zone)
221{   id              realObject;
222    NSMutableArray  *realArray;
223    NSDictionary    *plistObject;
224    NSEnumerator    *enumerator;
225    NSString        *className;
226
227    realArray = [[NSMutableArray allocWithZone:zone] initWithCapacity:[plist count]];
228    enumerator = [plist objectEnumerator];
229    while ((plistObject = [enumerator nextObject]))
230    {
231        className = [plistObject objectForKey:@"Class"];
232        if (className)
233        {   id	obj = [NSClassFromString(className) allocWithZone:zone];
234
235            if (!obj)	// load old projects
236                obj = [NSClassFromString(newClassName(className)) allocWithZone:zone];
237            realObject = [obj initFromPropertyList:plistObject inDirectory:directory];
238            [realArray addObject:realObject];
239            [realObject release];
240        }
241        else
242            NSLog(@"arrayFromPropertyList(): Class expected !");
243    }
244
245    return realArray;
246}
247
248NSColor *colorFromPropertyList(id plist, NSZone *zone)
249{
250    if ([plist isKindOfClass:[NSDictionary class]])
251    {   NSString *colorSpaceName = [plist objectForKey:@"s"];
252
253        if (!colorSpaceName)
254            colorSpaceName = [plist objectForKey:@"ColorSpace"];
255
256        if ([colorSpaceName isEqualToString:@"CalWhite"] || [colorSpaceName isEqualToString:@"NSCalibratedWhiteColorSpace"])
257        {
258            if ([plist objectForKey:@"W"])
259                return [[NSColor colorWithCalibratedWhite:[[plist objectForKey:@"W"] floatValue] alpha:[[plist objectForKey:@"A"] floatValue]] retain];
260            else
261                return [[NSColor colorWithCalibratedWhite:[[plist objectForKey:@"White"] floatValue] alpha:[[plist objectForKey:@"Alpha"] floatValue]] retain];
262        }
263        else if ([colorSpaceName isEqualToString:@"CalRGB"] || [colorSpaceName isEqualToString:@"NSCalibratedRGBColorSpace"])
264        {
265            if ([plist objectForKey:@"R"])
266            return [[NSColor colorWithCalibratedRed:[[plist objectForKey:@"R"] floatValue] green:[[plist objectForKey:@"G"] floatValue] blue:[[plist objectForKey:@"B"] floatValue] alpha:[[plist objectForKey:@"A"] floatValue]] retain];
267            else
268                return [[NSColor colorWithCalibratedRed:[[plist objectForKey:@"Red"] floatValue] green:[[plist objectForKey:@"Green"] floatValue] blue:[[plist objectForKey:@"Blue"] floatValue] alpha:[[plist objectForKey:@"Alpha"] floatValue]] retain];
269        }
270        else if ([colorSpaceName isEqualToString:@"DevWhite"] || [colorSpaceName isEqualToString:@"NSDeviceWhiteColorSpace"])
271        {
272            if ([plist objectForKey:@"W"])
273                return [[NSColor colorWithDeviceWhite:[[plist objectForKey:@"W"] floatValue] alpha:[[plist objectForKey:@"A"] floatValue]] retain];
274            else
275                return [[NSColor colorWithDeviceWhite:[[plist objectForKey:@"White"] floatValue] alpha:[[plist objectForKey:@"Alpha"] floatValue]] retain];
276        }
277        else if ([colorSpaceName isEqualToString:@"DevRGB"] || [colorSpaceName isEqualToString:@"NSDeviceRGBColorSpace"])
278        {
279            if ([plist objectForKey:@"R"])
280                return [[NSColor colorWithDeviceRed:[[plist objectForKey:@"R"] floatValue] green:[[plist objectForKey:@"G"] floatValue] blue:[[plist objectForKey:@"B"] floatValue] alpha:[[plist objectForKey:@"A"] floatValue]] retain];
281            else
282                return [[NSColor colorWithDeviceRed:[[plist objectForKey:@"Red"] floatValue] green:[[plist objectForKey:@"Green"] floatValue] blue:[[plist objectForKey:@"Blue"] floatValue] alpha:[[plist objectForKey:@"Alpha"] floatValue]] retain];
283        }
284        else if ([colorSpaceName isEqualToString:@"DevCMYK"] || [colorSpaceName isEqualToString:@"NSDeviceCMYKColorSpace"])
285        {
286            if ([plist objectForKey:@"C"])
287                return [[NSColor colorWithDeviceCyan:[[plist objectForKey:@"C"] floatValue] magenta:[[plist objectForKey:@"M"] floatValue] yellow:[[plist objectForKey:@"Y"] floatValue] black:[[plist objectForKey:@"B"] floatValue] alpha:[[plist objectForKey:@"A"] floatValue]] retain];
288            else
289                return [[NSColor colorWithDeviceCyan:[[plist objectForKey:@"Cyan"] floatValue] magenta:[[plist objectForKey:@"Magenta"] floatValue] yellow:[[plist objectForKey:@"Yellow"] floatValue] black:[[plist objectForKey:@"Black"] floatValue] alpha:[[plist objectForKey:@"Alpha"] floatValue]] retain];
290        }
291        else if ([colorSpaceName isEqualToString:@"Named"] || [colorSpaceName isEqualToString:@"NSNamedColorSpace"])
292        {
293            return [[NSColor colorWithCatalogName:[plist objectForKey:@"CId"] colorName:[plist objectForKey:@"NId"]] retain];
294        }
295        else if ([colorSpaceName isEqualToString:@"Unknown"])
296            return [[NSUnarchiver unarchiveObjectWithData:[plist objectForKey:@"Data"]] retain];
297        else
298            return nil;
299    }
300    else if ([plist isKindOfClass:[NSData class]])
301        return plist ? [[NSUnarchiver unarchiveObjectWithData:plist] retain] : nil;
302    else	// should never happen
303        return nil;
304}
305
306NSPrintInfo *printInfoFromPropertyList(id plist, NSZone *zone)
307{   NSPrintInfo	*printInfo = [[NSPrintInfo sharedPrintInfo] copy];
308
309    //[printInfo setVerticallyCentered:NO];
310    //[printInfo setHorizontallyCentered:NO];
311    //[printInfo setHorizontalPagination:NSFitPagination];
312
313    [printInfo setLeftMargin:[plist floatForKey:@"leftMargin"]];
314    [printInfo setRightMargin:[plist floatForKey:@"rightMargin"]];
315    [printInfo setTopMargin:[plist floatForKey:@"topMargin"]];
316    [printInfo setBottomMargin:[plist floatForKey:@"bottomMargin"]];
317
318    [printInfo setPaperSize:sizeFromPropertyList([plist objectForKey:@"paperSize"])];
319    [printInfo setPaperName:[plist objectForKey:@"paperName"]];
320    [printInfo setOrientation:[plist intForKey:@"orientation"]];
321    return printInfo;
322}
323
324NSRect rectFromPropertyList(NSString *plist)
325{   NSRect	retval;
326    NSArray	*components = [plist componentsSeparatedByString:@" "];
327
328    retval.origin.x = [[components objectAtIndex:0] floatValue];
329    retval.origin.y = [[components objectAtIndex:1] floatValue];
330    retval.size.width  = [[components objectAtIndex:2] floatValue];
331    retval.size.height = [[components objectAtIndex:3] floatValue];
332    return retval;
333}
334
335NSSize sizeFromPropertyList(id plist)
336{   NSSize	retval;
337    NSArray	*components = [plist componentsSeparatedByString:@" "];
338
339    retval.width = [[components objectAtIndex:0] floatValue];
340    retval.height = [[components objectAtIndex:1] floatValue];
341    return retval;
342}
343
344NSPoint pointFromPropertyList(id plist)
345{   NSPoint	retval;
346    NSArray	*components = [plist componentsSeparatedByString:@" "];
347
348    if ([components count] < 2)
349        return NSMakePoint(0.0, 0.0);
350    retval.x = [[components objectAtIndex:0] floatValue];
351    retval.y = [[components objectAtIndex:1] floatValue];
352    return retval;
353}
354
355V3Point v3pointFromPropertyList(id plist)
356{   V3Point retval;
357    NSArray *components = [plist componentsSeparatedByString:@" "];
358
359    if ([components count] < 3)
360        return V3MakePoint(0.0, 0.0, 0.0);
361    retval.x = [[components objectAtIndex:0] floatValue];
362    retval.y = [[components objectAtIndex:1] floatValue];
363    retval.z = [[components objectAtIndex:2] floatValue];
364    return retval;
365}
366