1 
2 /*
3  * Argyll Color Correction System
4  * Named Color Access Library
5  *
6  * Author: Graeme W. Gill
7  * Date:   3/12/2013
8  *
9  * Copyright 2013 Graeme W. Gill
10  * All rights reserved.
11  *
12  * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
13  * see the License.txt file for licencing details.
14  */
15 
16 /*
17  * Currently supports CxF2, CxF3 & ICC names color profiles.
18  */
19 
20 /*
21  * TTBD:
22  *
23  * Probably want to add some other formst:
24  * (see <http://www.selapa.net/swatchbooker/>
25  *  and <http://www.selapa.net/swatches/colors/fileformats.php>  )
26  *
27  * 	Adobe .aco
28  * 	      .acb
29  * 	      .acbl
30  * 	      .ase
31  * 	      .bcf
32  * corel  .cpl
33  * corel  .xml
34  * autocad .acb			RGB only
35  *
36  * and lower quality RGB formats:
37  *
38  * Adobe  .acf		?? May support Lab
39  * corel  .acb
40  *
41  * Paint Shop Pro .pal ???
42  *
43  * RAL ?
44  */
45 
46 #undef DEBUG
47 
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <math.h>
51 #include <sys/types.h>
52 #include <time.h>
53 #include "copyright.h"
54 #include "aconfig.h"
55 #ifndef SALONEINSTLIB
56 #include "numlib.h"
57 #include "icc.h"
58 #else
59 #include "numsup.h"
60 #endif
61 #include "conv.h"
62 #include "cgats.h"
63 #include "xspect.h"
64 #include "mxml.h"
65 #include "namedc.h"
66 #ifdef STANDALONE_TEST
67 #include "ui.h"
68 #endif /* STANDALONE_TEST */
69 
70 #ifndef STANDALONE_TEST
71 
72 # define DEB6 6
73 # define DEB4 4
74 
75 #ifdef NT       /* You'd think there might be some standards.... */
76 # ifndef __BORLANDC__
77 #  define stricmp _stricmp
78 # endif
79 #else
80 # define stricmp strcasecmp
81 #endif
82 
83 /* Forward declarations */
84 static void clear_namedc(namedc *p);
85 static void clear_nce(nce *p);
86 
do_hash(ORD32 hash,const char * s)87 static ORD32 do_hash(ORD32 hash, const char *s) {
88 	int i;
89     for (i = 0; s[i] != '\000'; i++) {
90         hash += s[i];
91         hash += (hash << 10);
92         hash ^= (hash >> 6);
93     }
94     return hash;
95 }
96 
do_hash2(const char * s1,const char * s2)97 static int do_hash2(const char *s1, const char *s2) {
98 	ORD32 hash;
99 
100 	hash = do_hash(0, s1);
101 	hash = do_hash(hash, s2);
102 
103 	return (int)hash;
104 }
105 
106 /* Compare while ignoring possible prefix on s1 */
pfxcmp(const char * s1,const char * s2)107 static int pfxcmp(const char *s1, const char *s2) {
108 	const char *cp;
109 
110 	if ((cp = strchr(s1, ':')) == NULL)
111 		cp = s1;
112 	else
113 		cp++;
114 
115 	return strcmp(cp, s2);
116 }
117 
118 /* XML data type callback for mxmlLoadFile() */
119 mxml_type_t
type_cb(mxml_node_t * node)120 type_cb(mxml_node_t *node) {
121 	mxml_node_t *parent = mxmlGetParent(node);
122 	const char *pname;
123 	const char *name = node->value.element.name;
124 
125 	if (parent == NULL)
126 		return MXML_TEXT;
127 
128 	pname = parent->value.element.name;
129 
130 //	printf("~1 type_cb got node named '%s'\n",name);
131 
132 	if ((pfxcmp(pname, "ColorCIELab") == 0
133 	  || pfxcmp(pname, "ColorSpaceCIELab") == 0)
134 	 && (pfxcmp(name, "L") == 0
135 	  || pfxcmp(name, "A") == 0
136 	  || pfxcmp(name, "B") == 0))
137 		return MXML_REAL;
138 
139 	if ((pfxcmp(pname, "ColorCIEXYZ") == 0
140 	  || pfxcmp(pname, "ColorSpaceCIEXYZ") == 0)
141 	 && (pfxcmp(name, "X") == 0
142 	  || pfxcmp(name, "Y") == 0
143 	  || pfxcmp(name, "Z") == 0))
144 		return MXML_REAL;
145 
146 	// ReflectanceSpectrum
147 	// Example doesn't have NumPoints, Increment
148 	// <ReflectanceSpectrum MeasureDate="2003-09-28T12:15:33-05:00"_ColorSpecification="CSD65-2" Name="45/0 Spectral" StartWL="400" NumPoints="31" Increment="10">
149 	// 0.0580 0.0594 0.0594 0.0584 0.0581 0.0591 0.0599 0.0601 0.0603 0.0610 0.0634 0.0695 0.0760 0.0786 0.0798 0.0826 0.0897 0.1024 0.1197 0.1350 0.1434 0.1455 0.1499 0.1594 0.1721 0.1842 0.1913 0.1928 0.1878 0.1734 0.1704
150 
151 	if ((pfxcmp(pname, "ColorSRGB") == 0
152 	  || pfxcmp(pname, "ColorSpaceSRGB") == 0)
153 	 && (pfxcmp(name, "R") == 0
154 	  || pfxcmp(name, "G") == 0
155 	  || pfxcmp(name, "B") == 0))
156 		return MXML_REAL;
157 
158 	if ((pfxcmp(pname, "ColorCMYK") == 0
159 	  || pfxcmp(pname, "ColorSpaceCMYK") == 0)
160 	 && (pfxcmp(name, "Cyan") == 0
161 	  || pfxcmp(name, "Magenta") == 0
162 	  || pfxcmp(name, "Yellow") == 0
163 	  || pfxcmp(name, "Black") == 0))
164 		return MXML_REAL;
165 
166 	if ((pfxcmp(pname, "FileInformation") == 0
167 	  || pfxcmp(pname, "Header") == 0)
168 	 && (pfxcmp(name, "Creator") == 0
169 	  || pfxcmp(name, "CreationDate") == 0
170 	  || pfxcmp(name, "Description") == 0))
171 		return MXML_OPAQUE;		/* Don't split strings up */
172 
173 	if ((pfxcmp(pname, "IlluminationOptions") == 0
174 	  || pfxcmp(pname, "TristimulusSpec") == 0
175 	  || pfxcmp(pname, "ColorSpaceSpecificationSpectrumTristimulus") == 0)
176 	 && (pfxcmp(name, "Illuminant") == 0
177 	  || pfxcmp(name, "Observer") == 0
178 	  || pfxcmp(name, "FieldOfView") == 0))
179 		return MXML_OPAQUE;		/* Don't split strings up */
180 
181 
182 	return MXML_TEXT;
183 }
184 
185 
sax_cb(mxml_node_t * node,mxml_sax_event_t event,void * data)186 void sax_cb(mxml_node_t *node, mxml_sax_event_t event, void *data) {
187 	namedc *p = (namedc *)data;
188 
189 	if (event == MXML_SAX_ELEMENT_OPEN) {
190 		const char *pname = mxmlGetElement(node);
191 
192 		if (pfxcmp(pname, "Resources") == 0) {
193 //printf("~1 open of Resources\n");
194 			p->indata = 1;
195 		}
196 
197 		if ((p->options & NAMEDC_OP_NODATA) == 0
198 		 || !p->indata) {
199 //printf("~1 options 0x%x, indata %d, retaining '%s'\n",p->options,p->indata,pname);
200 //a1logd(p->log, 4, "sax_cb: retaining %s\n",pname);
201 			mxmlRetain(node);
202 		}
203 
204 	} else if (event == MXML_SAX_DIRECTIVE) {
205 		mxmlRetain(node);
206 	} else if (event == MXML_SAX_DATA) {
207 		/* If the parent was retained, then retain this data node as well. */
208 		if (mxmlGetRefCount(mxmlGetParent(node)) > 1) {
209 			mxmlRetain(node);
210 		}
211 	} else if (event == MXML_SAX_ELEMENT_CLOSE) {
212 		const char *pname = mxmlGetElement(node);
213 		if(pfxcmp(pname, "Resources") == 0) {
214 //printf("~1 close of Resources\n");
215 			p->indata = 0;
216 		}
217 	}
218 }
219 
220 
221 /* Return a temporary key with prefix */
pfx(namedc * p,char * key)222 char *pfx(namedc *p, char *key) {
223 	if (p->pfx[0] == '\000')
224 		return key;
225 
226 	snprintf(p->prefix, 200, "%s:%s", p->pfx, key);
227 
228 	return p->prefix;
229 }
230 
231 /* Return a temporary key with prefix at front and after each '/' */
pfxp(namedc * p,char * path)232 char *pfxp(namedc *p, char *path) {
233 	char *cp = p->prefix;
234 	char *sp, *ep;
235 	int plen, slen;
236 
237 	if (p->pfx[0] == '\000')
238 		return path;
239 
240 	plen = strlen(p->pfx);
241 
242 	/* Figure out the first source key */
243 	sp = path;
244 	if ((ep = strchr(sp, '/')) != NULL) {
245 		slen = ep - sp;
246 		ep++;				/* Point beyond '/' */
247 	} else {
248 		slen = strlen(sp);
249 		ep = sp + slen;		/* point at '\000' */
250 	}
251 
252 	for (;;) {
253 		if ((NAMEDC_PLEN -1 -(cp - p->prefix)) < (plen + 1 + slen))
254 			break;			/* No room for prefix + path element */
255 
256 		strcpy(cp, p->pfx);
257 		cp += plen;
258 		*cp++ = ':';
259 		strncpy(cp, sp, slen);
260 		cp += slen;
261 
262 		/* Figure out the next source key */
263 		sp = ep;
264 
265 		if ((ep = strchr(sp, '/')) != NULL) {
266 			slen = ep - sp;
267 			ep++;				/* Point beyond '/' */
268 		} else {
269 			slen = strlen(sp);
270 			ep = sp + slen;		/* point at '\000' */
271 		}
272 
273 		if (slen <= 0)			/* No more path elements */
274 			break;
275 
276 		if ((NAMEDC_PLEN -1 -(cp - p->prefix)) < 1)
277 			break;			/* No room for '/' */
278 		*cp++ = '/';
279 	}
280 	*cp++ = '\000';
281 
282 	return p->prefix;
283 }
284 
285 /* Return a temporary key with suffix */
sfx(namedc * p,char * key)286 char *sfx(namedc *p, char *key) {
287 	if (p->pfx[0] == '\000')
288 		return key;
289 
290 	snprintf(p->prefix, 200, "%s:%s", key, p->pfx);
291 
292 	return p->prefix;
293 }
294 
295 /* Read in a namedc from a cxf2 or cxf3 file */
296 /* Return nz on error */
read_cxf(namedc * p,const char * filename,int options)297 static int read_cxf(namedc *p, const char *filename, int options) {
298     FILE *fp;
299 	char *pfilename;
300     mxml_node_t *tree, *cxf, *pnode, *node;
301 	const char *attr, *name;
302 	int cxf2 = 0;
303 	int i, j;
304 
305 	a1logd(p->log, 1, "read_cxf: file '%s' options 0x%x\n",filename,options);
306 
307 	if (p->filename == NULL) {
308 		if ((pfilename = strdup(filename)) == NULL) {
309 			snprintf(p->err, NAMEDC_ERRL, "Malloc of filename failed");
310 			a1logd(p->log, 1, "read_cxf: %s\n",p->err);
311 			return p->errc = 2;
312 		}
313 		clear_namedc(p);
314 		p->filename = pfilename;
315 	}
316 	p->options = options;
317 
318     if ((fp = fopen(p->filename, "r")) == NULL) {
319 		snprintf(p->err, NAMEDC_ERRL, "Opening XML file '%s' failed",p->filename);
320 		a1logd(p->log, 1, "read_cxf: %s\n",p->err);
321 		return p->errc = 1;
322 	}
323 
324 #ifdef NEVER
325     tree = mxmlLoadFile(NULL, fp, type_cb);
326 #else
327 	p->indata = 0;
328 	tree = mxmlSAXLoadFd(NULL, fileno(fp), type_cb, sax_cb, (void *)p);
329 #endif
330     fclose(fp);
331 
332 	if (tree == NULL) {
333 		snprintf(p->err, NAMEDC_ERRL, "Parsing XML file '%s' failed",p->filename);
334 		a1logd(p->log, 1, "read_cxf: %s\n",p->err);
335 		return p->errc = 1;
336 	}
337 
338 	if ((cxf = mxmlFindElement(tree, tree, NULL, NULL, NULL, MXML_DESCEND_FIRST)) == NULL
339 	 || mxmlGetType(cxf) != MXML_ELEMENT) {
340 		snprintf(p->err, NAMEDC_ERRL, "Failed to find top element in '%s'",p->filename);
341 		a1logd(p->log, 1, "read_cxf: %s\n",p->err);
342 		mxmlDelete(tree);
343 		return p->errc = 1;
344 	}
345 	name = cxf->value.element.name;
346 
347 	if ((attr = strchr(cxf->value.element.name, ':')) != NULL) {
348 		int len = attr - name;
349 		if (len > 99)
350 			len = 99;
351 		strncpy(p->pfx, name, len);
352 		p->pfx[len] = '\000';
353 	} else {
354 		p->pfx[0] = '\000';
355 	}
356 
357 	a1logd(p->log, 4, "read_cxf: prefix '%s'\n",p->pfx);
358 
359 	if (strcmp(name, pfx(p, "CxF")) != 0) {
360 		snprintf(p->err, NAMEDC_ERRL, "Top element not called CxF in '%s'",p->filename);
361 		a1logd(p->log, 1, "read_cxf: %s\n",p->err);
362 		mxmlDelete(tree);
363 		return p->errc = 1;
364 	}
365 
366 	/* Check that its CxF3 */
367 	if ((attr = mxmlElementGetAttr(cxf, sfx(p, "xmlns"))) == NULL) {
368 		snprintf(p->err, NAMEDC_ERRL, "Failed to find CxF attribute %s in '%s'",sfx(p, "xmlns"), p->filename);
369 		a1logd(p->log, 1, "read_cxf: %s\n",p->err);
370 		mxmlDelete(tree);
371 		return p->errc = 1;
372 	}
373 
374 	if (strcmp(attr, "http://colorexchangeformat.com/CxF3-core") != 0) {
375 		if (strcmp(attr, "http://colorexchangeformat.com/v2") != 0) {
376 			snprintf(p->err, NAMEDC_ERRL, "File '%s' is not CxF format",p->filename);
377 			a1logd(p->log, 1, "read_cxf: %s\n",p->err);
378 			mxmlDelete(tree);
379 			return p->errc = 1;
380 		} else {
381 			cxf2 = 1;
382 			a1logd(p->log, 1, "read_cxf: This is a CxF2 file\n");
383 		}
384 	} else {
385 		a1logd(p->log, 1, "read_cxf: This is a CxF3 file\n");
386 	}
387 
388 	/* Grab the description */
389 	if (cxf2) {
390 		if ((node = mxmlFindPathNode(cxf, pfxp(p,"Palette"))) == NULL
391 		 || (name = mxmlElementGetAttr(node, "PaletteName")) == NULL)
392 			name = NULL;
393 
394 		if (name == NULL)
395 			name = p->filename;
396 		if ((p->description = strdup(name)) == NULL) {
397 			snprintf(p->err, NAMEDC_ERRL, "Malloc of description string failed");
398 			a1logd(p->log, 1, "read_cxf: %s\n",p->err);
399 			mxmlDelete(tree);
400 			return p->errc = 2;
401 		}
402 		a1logd(p->log, 2, "read_cxf: description '%s'\n",p->description);
403 
404 		p->hash = do_hash2(p->filename, p->description);
405 
406 	} else {	/* else cxf3 */
407 		if ((node = mxmlFindPathNode(cxf, pfxp(p,"FileInformation/Description"))) == NULL)
408 			name = NULL;
409 		else
410 			name = mxmlGetOpaque(node);
411 		if (name == NULL)
412 			name = p->filename;
413 		if ((p->description = strdup(name)) == NULL) {
414 			snprintf(p->err, NAMEDC_ERRL, "Malloc of description string failed");
415 			a1logd(p->log, 1, "read_cxf: %s\n",p->err);
416 			mxmlDelete(tree);
417 			return p->errc = 2;
418 		}
419 		a1logd(p->log, 2, "read_cxf: description '%s'\n",p->description);
420 
421 		p->hash = do_hash2(p->filename, p->description);
422 	}
423 
424 	if ((p->options & NAMEDC_OP_NODATA) == 0) {
425 		char *SampleKey = NULL;
426 		char *SampleNameKey = NULL;
427 		char *SampleLabKey = NULL;
428 		char *SampleXYZKey = NULL;
429 		char *SampleCMYKKey = NULL;
430 		const char *colorSpecification = NULL;	/* cxf3 ColorSpecification of Lab or XYZ */
431 
432 		if (cxf2) {
433 			/* Locate the Palette/ColorSet node */
434 			if ((pnode = mxmlFindPathNode(cxf, pfxp(p,"Palette/ColorSet"))) == NULL) {
435 				snprintf(p->err, NAMEDC_ERRL, "Failed to find Resources/ObjectCollection in '%s'",p->filename);
436 				a1logd(p->log, 1, "read_cxf: %s\n",p->err);
437 				mxmlDelete(tree);
438 				return p->errc = 1;
439 			}
440 			SampleKey = "Color";
441 			SampleNameKey = "ColorName";
442 			SampleLabKey = "ColorSpaceCIELab";
443 			SampleXYZKey = "ColorSpaceCIEXYZ";
444 			SampleCMYKKey = "ColorSpaceCMYK";
445 
446 		} else {
447 			/* Locate the Resources/ObjectCollection node */
448 			if ((pnode = mxmlFindPathNode(cxf, pfxp(p,"Resources/ObjectCollection"))) == NULL) {
449 				snprintf(p->err, NAMEDC_ERRL, "Failed to find Resources/ObjectCollection in '%s'",p->filename);
450 				a1logd(p->log, 1, "read_cxf: %s\n",p->err);
451 				mxmlDelete(tree);
452 				return p->errc = 1;
453 			}
454 			SampleKey = "Object";
455 			SampleNameKey = "Name";
456 			SampleLabKey = "ColorCIELab";
457 			SampleXYZKey = "ColorCIEXYZ";
458 			SampleCMYKKey = "ColorCMYK";
459 		}
460 
461 		/* - - - - - - - - - - - - */
462 		/* Read all the colors. */
463 		/* Start with first node */
464 		node = mxmlFindElement(pnode, pnode, pfx(p,SampleKey), NULL, NULL, MXML_DESCEND_FIRST);
465 		for (i = 0; node != NULL; i++) {
466 			int j;
467 		    mxml_node_t *ppvals, *pvals, *val;
468 			const char *name;
469 			double Lab[3];
470 			int Lab_v = 0;
471 			double dev[MAX_CHAN];
472 			int dev_n = 0;
473 			icColorSpaceSignature devSig = icMaxEnumData;
474 
475 			if (mxmlGetType(node) != MXML_ELEMENT) {
476 				a1logd(p->log, DEB6, "read_cxf: skipping non element node type %d\n",mxmlGetType(node));
477 				goto next;
478 			}
479 			a1logd(p->log, DEB6, "read_cxf: read node '%s'\n",node->value.element.name);
480 
481 			if (strcmp(node->value.element.name, pfx(p,SampleKey)) != 0) {
482 				a1logd(p->log, DEB6, "read_cxf: skipping non %s node\n",SampleKey);
483 				goto next;
484 			}
485 
486 			if (!cxf2) {
487 				/* Get the ObjectType attribute of Object */
488 				if ((attr = mxmlElementGetAttr(node, "ObjectType")) == NULL) {
489 					a1logd(p->log, DEB6, "read_cxf: skipping node without ObjectType\n");
490 					goto next;							/* Skip this one */
491 				}
492 
493 #ifdef NEVER
494 				/* Check the attribute value (Should we ?) */
495 				/* Known values are: Standard, Color */
496 				/* May be Trial, Target, Substrate, Colorant, ... ? */
497 				if (strcmp(attr, "Standard") != 0
498 				 && strcmp(attr, "Color") != 0) {
499 					a1logd(p->log, DEB6, "read_cxf: skipping node with ObjectType = '%s'\n",attr);
500 					goto next;							/* Skip this one */
501 				}
502 #endif
503 			}
504 
505 			if ((attr = mxmlElementGetAttr(node, SampleNameKey)) == NULL) {
506 				a1logd(p->log, DEB6, "read_cxf: skipping without %s\n",SampleNameKey);
507 				goto next;							/* Skip this one */
508 			}
509 			name = attr;
510 
511 			a1logd(p->log, DEB4, "read_cxf: got color %d name '%s'\n",i,name);
512 
513 			if (!cxf2) {
514 				if ((ppvals = mxmlFindElement(node, node, pfx(p,"ColorValues"), NULL, NULL, MXML_DESCEND_FIRST)) == NULL) {
515 					a1logd(p->log, DEB4, "read_cxf: no reference ColorValuesname  - skipping\n");
516 					goto next;
517 				}
518 			} else {
519 				ppvals = node;
520 			}
521 
522 			if ((p->options & NAMEDC_OP_NOSPEC) == 0) {
523 				/* ~~~~9999 should look for spectral */
524 			}
525 
526 			/* See if there is ColorCIELab */
527 			if ((pvals = mxmlFindElement(ppvals, ppvals, pfx(p,SampleLabKey), NULL, NULL, MXML_DESCEND_FIRST)) != NULL) {
528 				char *key[3] = { "L", "A", "B" };
529 
530 				/* Check which color specification is being used with Lab */
531 				if (!cxf2 && colorSpecification == NULL) {
532 					colorSpecification = mxmlElementGetAttr(pvals, "ColorSpecification");
533 				}
534 
535 				for (j = 0; j < 3; j++) {
536 					if ((val = mxmlFindElement(pvals, pvals, pfx(p, key[j]), NULL, NULL, MXML_DESCEND_FIRST)) == NULL) {
537 						a1logd(p->log, DEB6, "read_cxf: failed to find ColorCIELab component %s\n",key[j]);
538 						break;		/* oops */
539 					}
540 					Lab[j] = mxmlGetReal(val);
541 					a1logd(p->log, DEB6, "read_cxf: got ColorCIELab component %s value %f\n",key[j],Lab[j]);
542 				}
543 				if (j >= 3)
544 					Lab_v = 1;
545 			}
546 
547 			if (!Lab_v) {
548 
549 				a1logd(p->log, DEB6, "read_cxf: no valid ColorCIELab value, look for XYZ\n");
550 
551 				/* See if there is ColorCIEXYZ instead */
552 				if ((pvals = mxmlFindElement(ppvals, ppvals, pfx(p,SampleXYZKey), NULL, NULL, MXML_DESCEND_FIRST)) != NULL) {
553 					char *key[3] = { "X", "Y", "Z" };
554 
555 					/* Check which color specification is being used with Lab */
556 					if (!cxf2 && colorSpecification == NULL) {
557 						colorSpecification = mxmlElementGetAttr(pvals, "ColorSpecification");
558 					}
559 
560 					for (j = 0; j < 3; j++) {
561 						if ((val = mxmlFindElement(pvals, pvals, pfx(p, key[j]), NULL, NULL, MXML_DESCEND_FIRST)) == NULL) {
562 							a1logd(p->log, DEB6, "read_cxf: failed to find ColorCIEXYZ component %s\n",key[j]);
563 							break;		/* oops */
564 						}
565 						Lab[j] = mxmlGetReal(val);
566 						a1logd(p->log, DEB6, "read_cxf: got ColorCIEXYZ component %s value %f\n",key[j],Lab[j]);
567 					}
568 					if (j >= 3) {
569 						icmXYZ2Lab(&icmD50, Lab, Lab);
570 						Lab_v = 1;
571 					}
572 				}
573 			}
574 
575 			if (!Lab_v) {
576 				a1logd(p->log, DEB6, "read_cxf: no CIE value found - skipping color\n");
577 				goto next;
578 			}
579 
580 			/* Read any device color values */
581 			if (!cxf2) {
582 				if ((ppvals = mxmlFindElement(node, node, pfx(p,"DeviceColorValues"), NULL, NULL, MXML_DESCEND_FIRST)) != NULL) {
583 
584 					a1logd(p->log, DEB4, "read_cxf: got DeviceColorValues\n");
585 				}
586 			}
587 
588 			if (ppvals != NULL) {
589 
590 				// ~~99 could add read of other device values here
591 
592 				/* See if there is ColorCMYK */
593 				if ((pvals = mxmlFindElement(ppvals, ppvals, pfx(p,SampleCMYKKey), NULL, NULL, MXML_DESCEND_FIRST)) != NULL) {
594 					char *key[4] = { "Cyan", "Magenta", "Yellow", "Black" };
595 
596 					a1logd(p->log, DEB4, "read_cxf: got ColorCMYK\n");
597 					for (j = 0; j < 4; j++) {
598 						if ((val = mxmlFindElement(pvals, pvals, pfx(p, key[j]), NULL, NULL, MXML_DESCEND_FIRST)) == NULL) {
599 							a1logd(p->log, DEB6, "read_cxf: failed to find ColorCMYK component %s\n",key[j]);
600 							break;		/* oops */
601 						}
602 						dev[j] = mxmlGetReal(val);
603 						a1logd(p->log, DEB6, "read_cxf: got ColorCMYK component %s value %f\n",key[j],dev[j]);
604 					}
605 					if (j >= 4) {
606 						dev_n = j;
607 						devSig = icSigCmykData;
608 					}
609 				}
610 			}
611 
612 			/* Add an entry */
613 			if (p->count >= p->count_a) {
614 				unsigned int count_n;
615 				count_n = p->count_a + 4096/sizeof(nce);
616 
617 				a1logd(p->log, 8, "read_cxf: increasing data array size to %d\n",count_n);
618 				if ((p->data = recalloc(p->data, p->count_a, sizeof(nce), count_n, sizeof(nce))) == NULL) {
619 					snprintf(p->err, NAMEDC_ERRL, "Malloc of data size %d failed",p->count_a);
620 					a1logd(p->log, 1, "read_cxf: %s\n",p->err);
621 					mxmlDelete(tree);
622 					return p->errc = 2;
623 				}
624 				p->count_a = count_n;
625 			}
626 			clear_nce(&p->data[p->count]);
627 
628 			if ((p->data[p->count].name = strdup(name)) == NULL) {
629 				snprintf(p->err, NAMEDC_ERRL, "Malloc of color name string failed");
630 				a1logd(p->log, 1, "read_cxf: %s\n",p->err);
631 				mxmlDelete(tree);
632 				return p->errc = 2;
633 			}
634 
635 			if (Lab_v) {
636 				p->data[p->count].Lab[0] = Lab[0];
637 				p->data[p->count].Lab[1] = Lab[1];
638 				p->data[p->count].Lab[2] = Lab[2];
639 				p->data[p->count].Lab_v = 1;
640 			}
641 
642 			if (dev_n > 0 && devSig != icMaxEnumData) {
643 				for (j = 0; j < dev_n; j++)
644 					p->data[p->count].dev[j] = dev[j];
645 				p->data[p->count].dev_n = dev_n;
646 				p->data[p->count].devSig = devSig;
647 			} else {
648 				p->data[p->count].dev_n = 0;
649 				p->data[p->count].devSig = icMaxEnumData;
650 			}
651 
652 			a1logd(p->log, 8, "read_cxf: added color %d\n",p->count);
653 
654 			p->count++;
655 
656 		next:;	/* Next color */
657 			node = mxmlGetNextSibling(node);
658 		}
659 
660 		/* - - - - - - - - - - - - */
661 		/* Read extra information */
662 		if (cxf2) {
663 			/* Grab the creator */
664 			if ((node = mxmlFindPathNode(cxf, pfxp(p,"Preamble/Header/Creator"))) == NULL)
665 				name = NULL;
666 			else
667 				name = mxmlGetOpaque(node);
668 
669 			if (name == NULL)
670 				name = "[Unknown]";
671 			if ((p->creator = strdup(name)) == NULL) {
672 				snprintf(p->err, NAMEDC_ERRL, "Malloc of creator string failed");
673 				a1logd(p->log, 1, "read_cxf: %s\n",p->err);
674 				mxmlDelete(tree);
675 				return p->errc = 2;
676 			}
677 			a1logd(p->log, 2, "read_cxf: creator '%s'\n",p->creator);
678 
679 			/* Grab the illuminant type */
680 			if ((node = mxmlFindPathNode(cxf, pfxp(p,"Palette/ColorSet/CollectionColorSpaceSpecification/ColorSpaceSpecificationSpectrumTristimulus/IlluminationOptions/Illuminant"))) == NULL
681 			 || (name = mxmlGetOpaque(node)) == NULL) {
682 				a1logd(p->log, 2, "read_cxf: failed to locate Illuminant - assuming D50\n");
683 				p->ill = icxIT_D50;
684 			} else {
685 				if (strcmp(name, "Illuminant_A") == 0)
686 					p->ill = icxIT_A;
687 				else if (strcmp(name, "Illuminant_D50") == 0)
688 					p->ill = icxIT_D50;
689 				else if (strcmp(name, "Illuminant_D55") == 0)
690 					p->ill = icxIT_D55;
691 				else if (strcmp(name, "Illuminant_D65") == 0)
692 					p->ill = icxIT_D65;
693 				else if (strcmp(name, "Illuminant_E") == 0)
694 					p->ill = icxIT_E;
695 				else {
696 					a1logd(p->log, 2, "read_cxf: Illuminant '%s' unrecognised\n",name);
697 					p->ill = icxIT_D50;
698 				}
699 			}
700 			a1logd(p->log, 2, "read_cxf: illuminant '%s'\n",icm2str(icmIlluminant, p->ill));
701 
702 			/* Grab the observer type */
703 			if ((node = mxmlFindPathNode(cxf, pfxp(p,"Palette/ColorSet/CollectionColorSpaceSpecification/ColorSpaceSpecificationSpectrumTristimulus/FieldOfView"))) == NULL
704 			 || (name = mxmlGetOpaque(node)) == NULL) {
705 				a1logd(p->log, 2, "read_cxf: failed to locate FieldOfView - assuming 2 degree\n");
706 				p->obs = icxOT_CIE_1931_2;
707 			} else {
708 				if (strcmp(name, "FieldOfView_2_Degree") == 0)
709 					p->obs = icxOT_CIE_1931_2;
710 				else if (strcmp(name, "FieldOfView_10_Degree") == 0)
711 					p->obs = icxOT_CIE_1964_10;
712 				else {
713 					a1logd(p->log, 2, "read_cxf: Illuminant '%s' unrecognised\n",name);
714 					p->obs = icxOT_CIE_1931_2;
715 				}
716 			}
717 			a1logd(p->log, 2, "read_cxf: observer '%s'\n",icm2str(icmStandardObserver, p->obs));
718 
719 		} else {
720 			int found_io = 0;
721 
722 			/* Grab the creator */
723 			if ((node = mxmlFindPathNode(cxf, pfxp(p,"FileInformation/Creator"))) == NULL)
724 				name = NULL;
725 			else
726 				name = mxmlGetOpaque(node);
727 
728 			if (name == NULL)
729 				name = "[Unknown]";
730 			if ((p->creator = strdup(name)) == NULL) {
731 				snprintf(p->err, NAMEDC_ERRL, "Malloc of creator string failed");
732 				a1logd(p->log, 1, "read_cxf: %s\n",p->err);
733 				mxmlDelete(tree);
734 				return p->errc = 2;
735 			}
736 			a1logd(p->log, 2, "read_cxf: creator '%s'\n",p->creator);
737 
738 			if (colorSpecification != NULL) {
739 				a1logd(p->log, 2, "read_cxf: colorSpecification '%s'\n",colorSpecification);
740 			}
741 
742 			/* Look through the color specifications and find the one that matches */
743 			/* the Lab or XYZ color specification */
744 			pnode = mxmlFindPathNode(cxf, pfxp(p,"Resources/ColorSpecificationCollection/ColorSpecification"));
745 			while (pnode != NULL) {
746 				name = mxmlElementGetAttr(pnode, "Id");
747 
748 				if (colorSpecification == NULL
749 				 || (name != NULL && strcmp(name, colorSpecification) == 0)) {
750 
751 					a1logd(p->log, 2, "read_cxf: found node matching colorSpecification '%s'\n",colorSpecification);
752 
753 					/* Grab the illuminant type */
754 					if ((node = mxmlFindPathNode(pnode, pfxp(p,"TristimulusSpec/Illuminant"))) == NULL
755 					 || (name = mxmlGetOpaque(node)) == NULL) {
756 						a1logd(p->log, 2, "read_cxf: failed to locate Illuminant - assuming D50\n");
757 						p->ill = icxIT_D50;
758 					} else {
759 						if (strcmp(name, "A") == 0)
760 							p->ill = icxIT_A;
761 						else if (strcmp(name, "D50") == 0)
762 							p->ill = icxIT_D50;
763 						else if (strcmp(name, "D55") == 0)
764 							p->ill = icxIT_D55;
765 						else if (strcmp(name, "D65") == 0)
766 							p->ill = icxIT_D65;
767 						else if (strcmp(name, "E") == 0)
768 							p->ill = icxIT_E;
769 						else {
770 							a1logd(p->log, 2, "read_cxf: Illuminant '%s' unrecognised\n",name);
771 							p->ill = icxIT_D50;
772 						}
773 					}
774 					a1logd(p->log, 2, "read_cxf: illuminant '%s'\n",icm2str(icmIlluminant, p->ill));
775 
776 					/* Grab the first observer type */
777 					if ((node = mxmlFindPathNode(cxf, pfxp(p,"Resources/ColorSpecificationCollection/ColorSpecification/TristimulusSpec/Observer"))) == NULL
778 					 || (name = mxmlGetOpaque(node)) == NULL) {
779 						a1logd(p->log, 2, "read_cxf: failed to locate Observer - assuming 2 degree\n");
780 						p->obs = icxOT_CIE_1931_2;
781 					} else {
782 						if (strcmp(name, "2_Degree") == 0)
783 							p->obs = icxOT_CIE_1931_2;
784 						else if (strcmp(name, "10_Degree") == 0)
785 							p->obs = icxOT_CIE_1964_10;
786 						else {
787 							a1logd(p->log, 2, "read_cxf: Illuminant '%s' unrecognised\n",name);
788 							p->obs = icxOT_CIE_1931_2;
789 						}
790 					}
791 					a1logd(p->log, 2, "read_cxf: observer '%s'\n",icm2str(icmStandardObserver, p->obs));
792 					found_io = 1;
793 					break;
794 				}
795 				pnode = mxmlGetNextSibling(pnode);
796 			}
797 
798 			if (!found_io) {
799 				p->ill = icxIT_D50;
800 				p->obs = icxOT_CIE_1931_2;
801 				a1logd(p->log, 2, "read_cxf: failed to locate ColorSpecification - assuming D50 2 degree observer\n");
802 			}
803 
804 			/* Other values of interest:
805 			   Resources/ColorSpecificationCollection/ColorSpecification/ColorSpecification/MeasurementSpec/MeasurementType	ie. "Spectrum_Reflectance"
806 			   Resources/ColorSpecificationCollection/ColorSpecification/ColorSpecification/MeasurementSpec/CalibrationStandard	ie. "GMDI" for Gretag Macbeth Calibration, "XRGA" for X-Rite Graphic Arts Standard.
807 			   Resources/ColorSpecificationCollection/ColorSpecification/ColorSpecification/MeasurementSpec/Device/DeviceFilter	ie. "Filter_None" "Filter_UVExcluded",
808 			 */
809 		}
810 	}
811 
812 	mxmlDelete(tree);
813 
814 	a1logd(p->log, 1, "read_cxf: done - %d colors\n",p->count);
815 	return 0;
816 }
817 
818 /* Read in a namedc from a ICC file */
819 /* Return nz on error */
read_icc(namedc * p,const char * filename,int options)820 static int read_icc(namedc *p, const char *filename, int options) {
821 	char *pfilename;
822 	icmFile *fp;
823 	icc *icco;
824 
825 	a1logd(p->log, 1, "read_icc: file '%s' options 0x%x\n",filename,options);
826 
827 	if (p->filename == NULL) {
828 		if ((pfilename = strdup(filename)) == NULL) {
829 			snprintf(p->err, NAMEDC_ERRL, "Malloc of filename failed");
830 			a1logd(p->log, 1, "read_icc: %s\n",p->err);
831 			return p->errc = 2;
832 		}
833 		clear_namedc(p);
834 		p->filename = pfilename;
835 	}
836 	p->options = options;
837 
838 	/* Open up the file for reading */
839 	if ((fp = new_icmFileStd_name(p->filename,"r")) == NULL) {
840 		snprintf(p->err, NAMEDC_ERRL, "Opening ICC file '%s' failed",p->filename);
841 		a1logd(p->log, 1, "read_icc: %s\n",p->err);
842 		return p->errc = 1;
843 	}
844 
845 	if ((icco = new_icc()) == NULL) {
846 		snprintf(p->err, NAMEDC_ERRL, "Creation ICC object failed");
847 		a1logd(p->log, 1, "read_icc: %s\n",p->err);
848 		fp->del(fp);
849 		return p->errc = 1;
850 	}
851 
852 	if (icco->read(icco, fp, 0) != 0) {
853 		snprintf(p->err, NAMEDC_ERRL, "Failed to read '%s'\n",p->filename);
854 		a1logd(p->log, 1, "read_icc: %s\n",p->err);
855 		icco->del(icco);
856 		fp->del(fp);
857 		return p->errc = 1;
858 	}
859 
860 	if (icco->header->deviceClass != icSigNamedColorClass) {
861 		snprintf(p->err, NAMEDC_ERRL, "Not a named color profile");
862 		a1logd(p->log, 1, "read_icc: %s\n",p->err);
863 		icco->del(icco);
864 		fp->del(fp);
865 		return p->errc = 1;
866 	}
867 
868 	if (icco->header->pcs != icSigXYZData
869 	 && icco->header->pcs != icSigLabData) {
870 		snprintf(p->err, NAMEDC_ERRL, "Unrecognised PCS");
871 		a1logd(p->log, 1, "read_icc: %s\n",p->err);
872 		icco->del(icco);
873 		fp->del(fp);
874 		return p->errc = 1;
875 	}
876 
877 	if (icco->find_tag(icco, icSigNamedColor2Tag) != 0) {
878 		snprintf(p->err, NAMEDC_ERRL, "Can't find ncl2 tag");
879 		a1logd(p->log, 1, "read_icc: %s\n",p->err);
880 		icco->del(icco);
881 		fp->del(fp);
882 		return p->errc = 1;
883 	}
884 
885 	{
886 		icmTextDescription *txd;
887 
888 		/* Try and read the tag from the file */
889 		if ((txd = (icmTextDescription *)icco->read_tag(icco, icSigProfileDescriptionTag)) == NULL) {
890 			snprintf(p->err, NAMEDC_ERRL, "No description tag");
891 			a1logd(p->log, 1, "read_icc: %s\n",p->err);
892 			icco->del(icco);
893 			fp->del(fp);
894 			return p->errc = 1;
895 		}
896 
897 		if ((p->description = strdup(txd->desc)) == NULL) {
898 			snprintf(p->err, NAMEDC_ERRL, "Malloc of description string failed");
899 			a1logd(p->log, 1, "read_icc: %s\n",p->err);
900 			icco->del(icco);
901 			fp->del(fp);
902 			return p->errc = 2;
903 		}
904 		a1logd(p->log, 2, "read_icc: description '%s'\n",p->description);
905 	}
906 
907 	p->hash = do_hash2(p->filename, p->description);
908 
909 	if ((p->options & NAMEDC_OP_NODATA) == 0) {
910 		icmNamedColor *tag;
911 		char name[3 * 32];
912 		double Lab[3];
913 		int Lab_v = 0;
914 		double dev[MAX_CHAN];
915 		int dev_n = 0;
916 		icColorSpaceSignature devSig = icMaxEnumData;
917 		int i, j;
918 
919 #ifdef NEVER
920 		/* See if there is a measurementType tag */
921 		icmMeasurement *meastag;
922 		if ((meastag = (icmMeasurement *)icco->read_tag(icco, icSigMeasurementTag)) != NULL) {
923 			p->ill = meastag->illuminant;
924 			p->obs = meastag->observer;
925 			a1logd(p->log, 2, "read_cxf: assuming D50 illuminant and 2 degree observer\n");
926 		} else {
927 			p->ill = icIlluminantD50;
928 			p->obs = icStdObs1931TwoDegrees;
929 		}
930 		a1logd(p->log, 2, "read_cxf: illuminant '%s'\n",icm2str(icmIlluminant, p->ill));
931 		a1logd(p->log, 2, "read_cxf: observer '%s'\n",icm2str(icmStandardObserver, p->obs));
932 #endif
933 
934 		if ((tag = (icmNamedColor *)icco->read_tag(icco, icSigNamedColor2Tag)) == NULL) {
935 			snprintf(p->err, NAMEDC_ERRL, "Can't read ncl2 tag");
936 			a1logd(p->log, 1, "read_icc: %s\n",p->err);
937 			icco->del(icco);
938 			fp->del(fp);
939 			return p->errc = 1;
940 		}
941 
942 		for (i = 0; i < tag->count; i++) {
943 
944 			/* Get the name */
945 			strcpy(name, tag->prefix);
946 			strcat(name, tag->data[i].root);
947 			strcat(name, tag->suffix);
948 
949 			a1logd(p->log, DEB4, "read_icc: got color %d name '%s'\n",i,name);
950 
951 			if (icco->header->pcs == icSigXYZData) {
952 				Lab[0] = tag->data[i].pcsCoords[0];
953 				Lab[1] = tag->data[i].pcsCoords[1];
954 				Lab[2] = tag->data[i].pcsCoords[2];
955 				icmXYZ2Lab(&icmD50, Lab, Lab);
956 
957 			} else {		/* Lab */
958 				Lab[0] = tag->data[i].pcsCoords[0];
959 				Lab[1] = tag->data[i].pcsCoords[1];
960 				Lab[2] = tag->data[i].pcsCoords[2];
961 			}
962 			Lab_v = 1;
963 			a1logd(p->log, DEB6, "read_icc: got ColorCIELab value %s\n",icmPdv(3, Lab));
964 
965 			/* Add any device space */
966 			devSig = icco->header->colorSpace;
967 			dev_n = icmCSSig2nchan(devSig);
968 			for (j = 0; j < dev_n; j++)
969 				dev[j] = tag->data[i].deviceCoords[j];
970 
971 			a1logd(p->log, DEB6, "read_icc: got %s value %s\n",
972 			       icm2str(icmColorSpaceSignature, devSig), icmPdv(dev_n, dev));
973 
974 			/* Add an entry */
975 			if (p->count >= p->count_a) {
976 				unsigned int count_n;
977 				count_n = p->count_a + 4096/sizeof(nce);
978 
979 				a1logd(p->log, 8, "read_icc: increasing data array size to %d\n",count_n);
980 				if ((p->data = recalloc(p->data, p->count_a, sizeof(nce), count_n, sizeof(nce))) == NULL) {
981 					snprintf(p->err, NAMEDC_ERRL, "Malloc of data size %d failed",p->count_a);
982 					a1logd(p->log, 1, "read_icc: %s\n",p->err);
983 					icco->del(icco);
984 					fp->del(fp);
985 					return p->errc = 2;
986 				}
987 				p->count_a = count_n;
988 			}
989 			clear_nce(&p->data[p->count]);
990 
991 			if ((p->data[p->count].name = strdup(name)) == NULL) {
992 				snprintf(p->err, NAMEDC_ERRL, "Malloc of color name string failed");
993 				a1logd(p->log, 1, "read_icc: %s\n",p->err);
994 				icco->del(icco);
995 				fp->del(fp);
996 				return p->errc = 2;
997 			}
998 
999 			if (Lab_v) {
1000 				p->data[p->count].Lab[0] = Lab[0];
1001 				p->data[p->count].Lab[1] = Lab[1];
1002 				p->data[p->count].Lab[2] = Lab[2];
1003 				p->data[p->count].Lab_v = 1;
1004 			}
1005 
1006 			if (dev_n > 0 && devSig != icMaxEnumData) {
1007 				for (j = 0; j < dev_n; j++)
1008 					p->data[p->count].dev[j] = dev[j];
1009 				p->data[p->count].dev_n = dev_n;
1010 				p->data[p->count].devSig = devSig;
1011 			} else {
1012 				p->data[p->count].dev_n = 0;
1013 				p->data[p->count].devSig = icMaxEnumData;
1014 			}
1015 
1016 			a1logd(p->log, 8, "read_icc: added color %d\n",p->count);
1017 
1018 			p->count++;
1019 
1020 		next:;	/* Next color */
1021 		}
1022 	}
1023 
1024 	icco->del(icco);
1025 	fp->del(fp);
1026 
1027 	return 0;
1028 }
1029 
1030 /* Read any format named color files */
1031 /* Return nz on error */
read_nc(namedc * p,const char * filename,int options)1032 static int read_nc(namedc *p, const char *filename, int options) {
1033 	int rv;
1034 
1035 	if ((p->format == 0 || p->format == 1)
1036 	 && (rv = read_cxf(p, filename, options)) == 0) {
1037 		p->format = 1;
1038 		return rv;
1039 	}
1040 
1041 	if ((p->format == 0 || p->format == 2)
1042 	 && (rv = read_icc(p, filename, options)) == 0) {
1043 		p->format = 2;
1044 		return rv;
1045 	}
1046 
1047 	/* Try other file types here */
1048 
1049 	return rv;
1050 }
1051 
1052 
1053 /* Return the index of the best mataching color, -1 on error. */
1054 /* Lab[] is assumed to be D50, 2 degree standard observer based CIE value, */
1055 /* and the spec value should only be provided if this is a reflective or */
1056 /* transmissive measurement, NULL if emissive. */
1057 /* If named color library is expects other than D50, 2 degree, then */
1058 /* it will use the spectral value if not NULL, or chromatically */
1059 /* adapt the Lab value. */
1060 /* deType == 0 DE76 */
1061 /* deType == 1 DE94 */
1062 /* deType == 2 DE2000 */
1063 /* if de != NULL, return the delta E */
match(struct _namedc * p,double * de,double * pLab,xspect * rspect,int deType)1064 int match(struct _namedc *p, double *de, double *pLab, xspect *rspect, int deType) {
1065 	int i, bix = -1;
1066 	double bde = 1e99;
1067 	double Lab[3];
1068 
1069 	if (p->filename == NULL) {		/* We haven't been opened */
1070 		snprintf(p->err, NAMEDC_ERRL, "We haven't been opened");
1071 		a1logd(p->log, 1, "match: %s\n",p->err);
1072 		return -1;
1073 	}
1074 
1075 	/* If the colors haven't been read yet, read them now */
1076 	if (p->data == NULL || (p->options & NAMEDC_OP_NODATA)) {
1077 		if (read_nc(p, NULL, (p->options & ~NAMEDC_OP_NODATA))) {
1078 			a1logd(p->log, 1, "match: on demand data load failed with '%s'\n",p->err);
1079 			return -1;
1080 		}
1081 		a1logd(p->log, 1, "match: after loading there are %d colors\n",p->count);
1082 	}
1083 
1084 	icmCpy3(Lab, pLab);
1085 
1086 	if (p->ill != icIlluminantD50 || p->obs != icStdObs1931TwoDegrees) {
1087 		if (rspect != NULL) {
1088 
1089 			if (p->sp2cie == NULL) {
1090 				if ((p->sp2cie = new_xsp2cie(p->ill, NULL, p->obs, NULL, icSigLabData, 0)) == NULL) {
1091 					snprintf(p->err, NAMEDC_ERRL, "creating spetral conversion failed");
1092 					a1logd(p->log, 1, "match: %s\n",p->err);
1093 					return -1;
1094 
1095 				}
1096 			}
1097 
1098 			/* Convert spectrum to the XYZ we want */
1099 			p->sp2cie->convert(p->sp2cie, Lab, rspect);
1100 
1101 		} else {
1102 			if (p->chrom[0][0] <= -1e38) {
1103 				double wXYZ[3];
1104 
1105 				// Special case this for a consistent value with ICC profiles
1106 				if (p->obs == icxOT_CIE_1931_2 && p->ill == icxIT_D65) {
1107 					icmCpy3(wXYZ, icmD65_ary3);
1108 				} else {
1109 					xsp2cie *tt;
1110 					xspect ts;
1111 					// Get the XYZ of the given white point for the illuminant and observer
1112 					if ((tt = new_xsp2cie(p->ill, NULL, p->obs, NULL, icSigXYZData, 0)) == NULL) {
1113 						snprintf(p->err, NAMEDC_ERRL, "creating spetral conversion failed");
1114 						a1logd(p->log, 1, "match: %s\n",p->err);
1115 						return -1;
1116 					}
1117 					if (standardIlluminant(&ts, icxIT_E, 0.0)) {
1118 						snprintf(p->err, NAMEDC_ERRL, "match: creating E type spectrum failed");
1119 						a1logd(p->log, 1, "match: %s\n",p->err);
1120 						return -1;
1121 					}
1122 					tt->convert(tt, wXYZ, &ts);
1123 					tt->del(tt);
1124 				}
1125 				icmAry2XYZ(p->dXYZ, wXYZ);
1126 
1127 				/* Chreate chromatic adapation matrix from D50 to named color */
1128 				icmChromAdaptMatrix(ICM_CAM_BRADFORD, p->dXYZ, icmD50, p->chrom);
1129 			}
1130 			icmLab2XYZ(&icmD50, Lab, pLab);
1131 			icmMulBy3x3(Lab, p->chrom, Lab);
1132 			icmXYZ2Lab(&p->dXYZ, Lab, Lab);
1133 		}
1134 	}
1135 
1136 	if (deType == 0) {
1137 		for (i = 0; i < p->count; i++) {
1138 			double de = icmLabDEsq(Lab, p->data[i].Lab);
1139 			if (de < bde) {
1140 				bde = de;
1141 				bix = i;
1142 			}
1143 		}
1144 
1145 	} else if (deType == 1) {
1146 		for (i = 0; i < p->count; i++) {
1147 			double de = icmCIE94sq(Lab, p->data[i].Lab);
1148 			if (de < bde) {
1149 				bde = de;
1150 				bix = i;
1151 			}
1152 		}
1153 
1154 	} else if (deType == 2) {
1155 		for (i = 0; i < p->count; i++) {
1156 			double de = icmCIE2Ksq(Lab, p->data[i].Lab);
1157 			if (de < bde) {
1158 				bde = de;
1159 				bix = i;
1160 			}
1161 		}
1162 	} else {
1163 		snprintf(p->err, NAMEDC_ERRL, "Unnown deType %d",deType);
1164 		a1logd(p->log, 1, "match: %s\n",p->err);
1165 		return -1;
1166 	}
1167 
1168 	if (bix < 0) {
1169 		snprintf(p->err, NAMEDC_ERRL, "No colors to match against");
1170 		a1logd(p->log, 1, "match: %s\n",p->err);
1171 		return -1;
1172 	}
1173 	if (de != NULL) {
1174 		*de = sqrt(bde);
1175 	}
1176 	return bix;
1177 }
1178 
1179 /* Free an entry */
clear_nce(nce * p)1180 static void clear_nce(nce *p) {
1181 	if (p != NULL) {
1182 		if (p->name != NULL)
1183 			free(p->name);
1184 		p->name = NULL;
1185 		if (p->sp != NULL)
1186 			free(p->sp);
1187 		p->sp = NULL;
1188 	}
1189 }
1190 
1191 /* Free the contents */
clear_namedc(namedc * p)1192 static void clear_namedc(namedc *p) {
1193 	if (p != NULL) {
1194 		int i;
1195 		if (p->filename != NULL)
1196 			free(p->filename);
1197 		p->filename = NULL;
1198 		if (p->creator != NULL)
1199 			free(p->creator);
1200 		p->creator = NULL;
1201 		if (p->description != NULL)
1202 			free(p->description);
1203 		p->description = NULL;
1204 		if (p->data != NULL) {
1205 			for (i = 0; i < p->count; i++)
1206 				clear_nce(&p->data[i]);
1207 			free(p->data);
1208 			p->data = NULL;
1209 		}
1210 		p->count = 0;
1211 	}
1212 }
1213 
1214 /* Delete it */
del_namedc(namedc * p)1215 static void del_namedc(namedc *p) {
1216 	if (p != NULL) {
1217 		clear_namedc(p);
1218 		p->log = del_a1log(p->log);
1219 		if (p->sp2cie != NULL)
1220 			p->sp2cie->del(p->sp2cie);
1221 		free(p);
1222 	}
1223 }
1224 
1225 /* Allocate a new, uninitialised namedc */
1226 /* Note thate black and white points aren't allocated */
new_namedc(a1log * log)1227 namedc *new_namedc(a1log *log) {
1228 	namedc *p;
1229 
1230 	a1logd(log, 1, "new_cxf\n");
1231 
1232 	if ((p = (namedc *)calloc(1, sizeof(namedc))) == NULL) {
1233 		a1logd(p->log, 1, "new_cxf: calloc failed\n");
1234 		return NULL;
1235 	}
1236 
1237 	p->log = new_a1log_d(log);
1238 
1239 	/* Init method pointers */
1240 	p->del        = del_namedc;
1241 	p->read_cxf   = read_cxf;
1242 	p->read_icc   = read_icc;
1243 	p->read       = read_nc;
1244 	p->match      = match;
1245 
1246 	p->chrom[0][0] = -1e38;
1247 
1248 	return p;
1249 }
1250 
1251 
1252 /* =========================================================================== */
1253 
1254 #else /* STANDALONE_TEST */
1255 
usage(char * diag,...)1256 void usage(char *diag, ...) {
1257 	fprintf(stderr,"Test namedc library\n");
1258 	fprintf(stderr,"Author: Graeme W. Gill\n");
1259 	if (diag != NULL) {
1260 		va_list args;
1261 		fprintf(stderr,"  Diagnostic: ");
1262 		va_start(args, diag);
1263 		vfprintf(stderr, diag, args);
1264 		va_end(args);
1265 		fprintf(stderr,"\n");
1266 	}
1267 	fprintf(stderr,"usage: namedc [-v level] infile\n");
1268 	fprintf(stderr," -D level               Debug level 1-9\n");
1269 	exit(1);
1270 }
1271 
1272 int
main(int argc,char * argv[])1273 main(int argc, char *argv[]) {
1274 	int fa,nfa;				/* argument we're looking at */
1275 	char inname[MAXNAMEL+1];
1276 	int debug = 0, i;
1277 	namedc *p;
1278 
1279 	/* Process the arguments */
1280 	for(fa = 1;fa < argc;fa++) {
1281 		nfa = fa;					/* skip to nfa if next argument is used */
1282 		if (argv[fa][0] == '-')	{	/* Look for any flags */
1283 			char *na = NULL;		/* next argument after flag, null if none */
1284 
1285 			if (argv[fa][2] != '\000')
1286 				na = &argv[fa][2];		/* next is directly after flag */
1287 			else {
1288 				if ((fa+1) < argc) {
1289 					if (argv[fa+1][0] != '-') {
1290 						nfa = fa + 1;
1291 						na = argv[nfa];		/* next is seperate non-flag argument */
1292 					}
1293 				}
1294 			}
1295 
1296 			if (argv[fa][1] == '?')
1297 				usage(NULL);
1298 
1299 			/* Debug level */
1300 			else if (argv[fa][1] == 'D') {
1301 				fa = nfa;
1302 				if (na != NULL)
1303 					debug = atoi(na);
1304 			}
1305 
1306 			else
1307 				usage("Unknown option '%c'",argv[fa][1]);
1308 		}
1309 		else
1310 			break;
1311 	}
1312 
1313 	if (fa >= argc || argv[fa][0] == '-') usage("Missing input filename");
1314 	strncpy(inname,argv[fa++],MAXNAMEL); inname[MAXNAMEL] = '\000';
1315 
1316 	g_log->debug = debug;
1317 
1318 	if ((p = new_namedc(g_log)) == NULL)
1319 		error("new_namedc failed\n");
1320 
1321 	/* Open non-data */
1322 	if (p->read(p, inname, NAMEDC_OP_NODATA)) {
1323 		error("read failed with '%s'\n",p->err);
1324 	}
1325 
1326 	printf("Palette is '%s'\n",p->description);
1327 	{
1328 		double Lab[3], de;
1329 		int ix;
1330 
1331 		Lab[0] = 50.0;
1332 		Lab[1] = 20.0;
1333 		Lab[2] = -10.0;
1334 
1335 		if ((ix = p->match(p, &de, Lab, NULL, 0)) < 0)
1336 			error(" match failed with '%s'\n",p->err);
1337 		printf("Matched color '%s' with DE76 %f\n",p->data[ix].name,de);
1338 
1339 		if ((ix = p->match(p, &de, Lab, NULL, 1)) < 0)
1340 			error(" match failed with '%s'\n",p->err);
1341 		printf("Matched color '%s' with DE94 %f\n",p->data[ix].name,de);
1342 
1343 		if ((ix = p->match(p, &de, Lab, NULL, 2)) < 0)
1344 			error(" match failed with '%s'\n",p->err);
1345 		printf("Matched color '%s' with DE00 %f\n",p->data[ix].name,de);
1346 	}
1347 
1348 #ifdef NEVER
1349 	printf("Loaded %d colors\n",p->count);
1350 	for (i = 0; i < p->count; i++) {
1351 		printf("Color %d name '%s' = %f %f %f\n",
1352 		       i, p->data[i].name, p->data[i].Lab[0], p->data[i].Lab[1], p->data[i].Lab[2]);
1353 		if (p->data[i].devSig != icMaxEnumData) {
1354 			printf("%s = %s\n", icm2str(icmColorSpaceSignature, p->data[i].devSig),
1355 			             icmPdv(p->data[i].dev_n, p->data[i].dev));
1356 		}
1357 	}
1358 #endif
1359 	p->del(p);
1360 
1361 	return 0;
1362 }
1363 
1364 
1365 
1366 #endif /* STANDALONE_TEST */
1367 
1368 
1369 
1370 
1371 
1372 
1373 
1374 
1375 
1376 
1377 
1378 
1379 
1380 
1381 
1382 
1383 
1384 
1385 
1386 
1387 
1388 
1389 
1390 
1391