You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/libs/lprof/cmssheet.cpp

1747 lines
43 KiB

/* */
/* Little cms - profiler construction set */
/* Copyright (C) 1998-2001 Marti Maria <marti@littlecms.com> */
/* */
/* THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY */
/* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. */
/* */
/* IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL, */
/* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, */
/* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, */
/* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF */
/* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE */
/* OF THIS SOFTWARE. */
/* */
/* This file is free software; you can redistribute it and/or modify it */
/* under the terms of the GNU General Public License as published by */
/* the Free Software Foundation; either version 2 of the License, or */
/* (at your option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but */
/* WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */
/* General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
/* */
/* As a special exception to the GNU General Public License, if you */
/* distribute this file as part of a program that contains a */
/* configuration script generated by Autoconf, you may include it under */
/* the same distribution terms that you use for the rest of that program. */
/* */
/* Version 1.08a */
#include "lcmsprf.h"
/* #define _DEBUG 1 */
/* IT8.7 / CGATS.17-200x handling */
LCMSHANDLE cdecl cmsxIT8Alloc(void);
void cdecl cmsxIT8Free(LCMSHANDLE IT8);
/* Persistence */
LCMSHANDLE cdecl cmsxIT8LoadFromFile(const char* cFileName);
LCMSHANDLE cdecl cmsxIT8LoadFromMem(void *Ptr, size_t len);
BOOL cdecl cmsxIT8SaveToFile(LCMSHANDLE IT8, const char* cFileName);
/* Properties */
const char* cdecl cmsxIT8GetSheetType(LCMSHANDLE hIT8);
BOOL cdecl cmsxIT8SetSheetType(LCMSHANDLE hIT8, const char* Type);
BOOL cdecl cmsxIT8SetProperty(LCMSHANDLE hIT8, const char* cProp, const char *Str);
BOOL cdecl cmsxIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val);
const char* cdecl cmsxIT8GetProperty(LCMSHANDLE hIT8, const char* cProp);
double cdecl cmsxIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp);
int cdecl cmsxIT8EnumProperties(LCMSHANDLE IT8, char ***PropertyNames);
/* Datasets */
BOOL cdecl cmsxIT8GetDataSetByPos(LCMSHANDLE IT8, int col, int row, char* Val, int ValBufferLen);
BOOL cdecl cmsxIT8GetDataSet(LCMSHANDLE IT8, const char* cPatch,
const char* cSample,
char* Val, int ValBufferLen);
BOOL cdecl cmsxIT8GetDataSetDbl(LCMSHANDLE IT8, const char* cPatch, const char* cSample, double* d);
BOOL cdecl cmsxIT8SetDataSet(LCMSHANDLE IT8, const char* cPatch,
const char* cSample,
char *Val);
BOOL cdecl cmsxIT8SetDataFormat(LCMSHANDLE IT8, int n, const char *Sample);
int cdecl cmsxIT8EnumDataFormat(LCMSHANDLE IT8, char ***SampleNames);
const char *cdecl cmsxIT8GenericPatchName(int nPatch, char* buffer);
int cdecl cmsxIT8GenericPatchNum(const char *Name);
/* ------------------------------------------------------------- Implementation */
#define MAXID 128 /* Max length of identifier */
#define MAXSTR 255 /* Max length of string */
#ifndef NON_WINDOWS
#include <io.h>
#endif
/* Symbols */
typedef enum { SNONE,
SINUM, /* Integer */
SDNUM, /* Real */
SIDENT, /* Identifier */
SSTRING, /* string */
SCOMMENT, /* comment */
SEOLN, /* End of line */
SEOF, /* End of stream */
SSYNERROR, /* Syntax error found on stream */
/* Keywords */
SBEGIN_DATA,
SBEGIN_DATA_FORMAT,
SEND_DATA,
SEND_DATA_FORMAT,
SKEYWORD,
SSTRING_SY
} SYMBOL;
/* Linked list of variable names */
typedef struct _KeyVal {
struct _KeyVal* Next;
char* Keyword; /* Name of variable */
char* Value; /* Points to value */
} KEYVALUE, FAR* LPKEYVALUE;
/* Linked list of values (Memory sink) */
typedef struct _OwnedMem {
struct _OwnedMem* Next;
void * Ptr; /* Point to value */
} OWNEDMEM, FAR* LPOWNEDMEM;
/* This struct hold all information about an openened */
/* IT8 handler. Only one dataset is allowed. */
typedef struct {
int nSamples, nPatches; /* Rows, Cols */
int SampleID; /* Pos of ID */
LPKEYVALUE HeaderList; /* The properties */
char* FileBuffer; /* The ASCII stream */
char** DataFormat; /* The binary stream descriptor */
char** Data; /* The binary stream */
LPOWNEDMEM MemorySink; /* The storage bakend */
/* Parser state machine */
SYMBOL sy; /* Current symbol */
int ch; /* Current character */
char* Source; /* Points to loc. being parsed */
int inum; /* integer value */
double dnum; /* real value */
char id[MAXID]; /* identifier */
char str[MAXSTR]; /* string */
/* Allowed keywords & datasets */
LPKEYVALUE ValidKeywords;
LPKEYVALUE ValidSampleID;
char FileName[MAX_PATH];
int lineno; /* line counter for error reporting */
char SheetType[MAXSTR]; /* New 1.09 */
} IT8, FAR* LPIT8;
/* ------------------------------------------------------ IT8 parsing routines */
/* A keyword */
typedef struct {
const char *id;
SYMBOL sy;
} KEYWORD;
/* The keyword->symbol translation table. Sorting is required. */
static const KEYWORD TabKeys[] = {
{"BEGIN_DATA", SBEGIN_DATA },
{"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT },
{"END_DATA", SEND_DATA},
{"END_DATA_FORMAT", SEND_DATA_FORMAT},
{"KEYWORD", SKEYWORD},
{"STRING", SSTRING_SY}};
#define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))
/* Predefined properties */
static char* PredefinedProperties[] = {
"NUMBER_OF_FIELDS", /* Required - NUMBER OF FIELDS */
"NUMBER_OF_SETS", /* Required - NUMBER OF SETS */
"ORIGINATOR", /* Required - Identifies the specific system, organization or individual that created the data file. */
"CREATED", /* Required - Indicates date of creation of the data file. */
"DESCRIPTOR", /* Required - Describes the purpose or contents of the data file. */
"DIFFUSE_GEOMETRY", /* The diffuse geometry used. Allowed values are "sphere" or "opal". */
"MANUFACTURER",
"MANUFACTURE", /* Some broken Fuji targets does store this value */
"PROD_DATE", /* Identifies year and month of production of the target in the form yyyy:mm. */
"SERIAL", /* Uniquely identifies individual physical target. */
"MATERIAL", /* Identifies the material on which the target was produced using a code */
/* uniquely identifying th e material. Th is is intend ed to be used for IT8.7 */
/* physical targets only (i.e . IT8.7/1 a nd IT8.7/2). */
"INSTRUMENTATION", /* Used to report the specific instrumentation used (manufacturer and */
/* model number) to generate the data reported. This data will often */
/* provide more information about the particular data collected than an */
/* extensive list of specific details. This is particularly important for */
/* spectral data or data derived from spectrophotometry. */
"MEASUREMENT_SOURCE", /* Illumination used for spectral measurements. This data helps provide */
/* a guide to the potential for issues of paper fluorescence, etc. */
"PRINT_CONDITIONS", /* Used to define the ch aracteristics of the printed sheet being reported. */
/* Where standard conditions have been defined (e.g., SW OP at nominal) */
/* named conditions may suffice. Otherwise, detailed in formation is */
/* needed. */
"SAMPLE_BACKING", /* Identifies the backing material used behind the sample during */
/* measurement. Allowed values are <20>black<63>, <20>white<74>, or "na". */
"CHISTQ_DOF" /* Degrees of freedom associated with the Chi squared statistic */
};
#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(char *))
/* Predefined sample types on dataset */
static char* PredefinedSampleID[] = {
"CMYK_C", /* Cyan component of CMYK data expressed as a percentage */
"CMYK_M", /* Magenta component of CMYK data expressed as a percentage */
"CMYK_Y", /* Yellow component of CMYK data expressed as a percentage */
"CMYK_K", /* Black component of CMYK data expressed as a percentage */
"D_RED", /* Red filter density */
"D_GREEN", /* Green filter density */
"D_BLUE", /* Blue filter density */
"D_VIS", /* Visual filter density */
"D_MAJOR_FILTER", /* Major filter d ensity */
"RGB_R", /* Red component of RGB data */
"RGB_G", /* Green component of RGB data */
"RGB_B", /* Blue com ponent of RGB data */
"SPECTRAL_NM", /* Wavelength of measurement expressed in nanometers */
"SPECTRAL_PCT", /* Percentage reflectance/transmittance */
"SPECTRAL_DEC", /* Reflectance/transmittance */
"XYZ_X", /* X component of tristimulus data */
"XYZ_Y", /* Y component of tristimulus data */
"XYZ_Z", /* Z component of tristimulus data */
"XYY_X" /* x component of chromaticity data */
"XYY_Y", /* y component of chromaticity data */
"XYY_CAPY", /* Y component of tristimulus data */
"LAB_L", /* L* component of Lab data */
"LAB_A", /* a* component of Lab data */
"LAB_B", /* b* component of Lab data */
"LAB_C", /* C*ab component of Lab data */
"LAB_H", /* hab component of Lab data */
"LAB_DE" /* CIE dE */
"LAB_DE_94", /* CIE dE using CIE 94 */
"LAB_DE_CMC", /* dE using CMC */
"LAB_DE_2000", /* CIE dE using CIE DE 2000 */
"MEAN_DE", /* Mean Delta E (LAB_DE) of samples compared to batch average */
/* (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets) */
"STDEV_X", /* Standard deviation of X (tristimulus data) */
"STDEV_Y", /* Standard deviation of Y (tristimulus data) */
"STDEV_Z", /* Standard deviation of Z (tristimulus data) */
"STDEV_L", /* Standard deviation of L* */
"STDEV_A" /* Standard deviation of a* */
"STDEV_B", /* Standard deviation of b* */
"STDEV_DE", /* Standard deviation of CIE dE */
"CHI_STQD_PAR"}; /* The average of the standard deviations of L*, a* and b*. It is */
/* used to derive an estimate of the chi-squared parameter which is */
/* recommended as the predictor of the variability of dE */
#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
/* Checks whatsever if c is a valid identifier middle char. */
static
BOOL isidchar(int c)
{
return (isalnum(c) || c == '$' || c == '%' || c == '&' || c == '/' || c == '.' || c == '_');
}
/* Checks whatsever if c is a valid identifier first char. */
static
BOOL isfirstidchar(int c)
{
return !isdigit(c) && isidchar(c);
}
/* Checks if c is a separator */
static
BOOL isseparator(int c)
{
return (c == ' ' || c == '\t' || c == '\r');
}
/* a replacement for strupr(), just for compatibility sake */
static
void xstrupr(char *cp)
{
for (;*cp;cp++)
if (*cp >= 'a' && *cp <= 'z')
*cp += 'A'-'a';
}
/* A replacement for (the nonstandard) filelenght */
static
int xfilelength(int fd)
{
#ifdef _MSC_VER
return _filelength(fd);
#else
struct stat sb;
if (fstat(fd, &sb) < 0)
return(-1);
return(sb.st_size);
#endif
}
static
BOOL SynError(LPIT8 it8, const char *Txt, ...)
{
char Buffer[256], ErrMsg[1024];
va_list args;
va_start(args, Txt);
vsprintf(Buffer, Txt, args);
va_end(args);
sprintf(ErrMsg, "%s: Line %d, %s", it8->FileName, it8->lineno, Buffer);
it8->sy = SSYNERROR;
cmsSignalError(LCMS_ERRC_ABORTED, ErrMsg);
return false;
}
static
BOOL Check(LPIT8 it8, SYMBOL sy, const char* Err)
{
if (it8 -> sy != sy)
return SynError(it8, Err);
return true;
}
/* Read Next character from stream */
static
void NextCh(LPIT8 it8)
{
it8->ch = *it8->Source;
if (it8->ch) it8->Source++;
}
/* Try to see if current identifier is a keyword, if so return the referred symbol */
static
SYMBOL BinSrchKey(const char *id)
{
int l = 1;
int r = NUMKEYS;
int x, res;
while (r >= l)
{
x = (l+r)/2;
res = strcmp(id, TabKeys[x-1].id);
if (res == 0) return TabKeys[x-1].sy;
if (res < 0) r = x - 1;
else l = x + 1;
}
return SNONE;
}
/* 10 ^n */
static
double pow10(int n)
{
return pow(10, n);
}
/* Reads a Real number, tries to follow from integer number */
static
void ReadReal(LPIT8 it8, int inum)
{
it8->dnum = (double) inum;
while (isdigit(it8->ch)) {
it8->dnum = it8->dnum * 10.0 + (it8->ch - '0');
NextCh(it8);
}
if (it8->ch == '.') { /* Decimal point */
double frac = 0.0; /* fraction */
int prec = 0; /* precission */
NextCh(it8); /* Eats dec. point */
while (isdigit(it8->ch)) {
frac = frac * 10.0 + (it8->ch - '0');
prec++;
NextCh(it8);
}
it8->dnum = it8->dnum + (frac / pow10(prec));
}
/* Exponent, example 34.00E+20 */
if (toupper(it8->ch) == 'E') {
int e;
int sgn;
NextCh(it8); sgn = 1;
if (it8->ch == '-') {
sgn = -1; NextCh(it8);
}
else
if (it8->ch == '+') {
sgn = +1;
NextCh(it8);
}
e = 0;
while (isdigit(it8->ch)) {
if ((double) e * 10L < INT_MAX)
e = e * 10 + (it8->ch - '0');
NextCh(it8);
}
e = sgn*e;
it8 -> dnum = it8 -> dnum * pow10(e);
}
}
/* Reads next symbol */
static
void InSymbol(LPIT8 it8)
{
register char *idptr;
register int k;
SYMBOL key;
int sng;
do {
while (isseparator(it8->ch))
NextCh(it8);
if (isfirstidchar(it8->ch)) { /* Identifier */
k = 0;
idptr = it8->id;
do {
if (++k < MAXID) *idptr++ = (char) it8->ch;
NextCh(it8);
} while (isidchar(it8->ch));
*idptr = '\0';
xstrupr(it8->id);
key = BinSrchKey(it8->id);
if (key == SNONE) it8->sy = SIDENT;
else it8->sy = key;
}
else /* Is a number? */
if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
{
int sign = 1;
if (it8->ch == '-') {
sign = -1;
NextCh(it8);
}
it8->inum = 0;
it8->sy = SINUM;
while (isdigit(it8->ch))
{
if ((long) it8->inum * 10L > (long) INT_MAX)
{
ReadReal(it8, it8->inum);
it8->sy = SDNUM;
it8->dnum *= sign;
return;
}
it8->inum = it8->inum * 10 + (it8->ch - '0');
NextCh(it8);
}
if (it8->ch == '.') {
ReadReal(it8, it8->inum);
it8->sy = SDNUM;
it8->dnum *= sign;
return;
}
it8 -> inum *= sign;
return;
}
else
switch ((int) it8->ch) {
case '\0':
case '\x1a':
it8->sy = SEOF;
break;
case '\n':
NextCh(it8);
it8->sy = SEOLN;
it8->lineno++;
break;
/* Comment */
case '#':
NextCh(it8);
while (it8->ch && it8->ch != '\n')
NextCh(it8);
it8->sy = SCOMMENT;
break;
/* String. I will support \", \n, \t and \\. */
/* But otherwise I hardly doubt these will be used ... */
case '\'':
case '\"':
idptr = it8->str;
sng = it8->ch;
k = 0;
NextCh(it8);
while (k < MAXSTR && it8->ch != sng) {
if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
else {
if (it8->ch == '\\')
{
NextCh(it8);
switch (it8->ch) {
case 'n': *idptr++ = '\n'; break;
case 'r': *idptr++ = '\r'; break;
case 't': *idptr++ = '\t'; break;
case '\\': *idptr++ = '\\'; break;
default:
*idptr++ = (char) it8->ch;
}
NextCh(it8);
}
else
{
*idptr++ = (char) it8->ch;
NextCh(it8);
}
k++;
}
}
it8->sy = SSTRING;
*idptr = '\0';
NextCh(it8);
break;
default:
it8->sy = SSYNERROR;
NextCh(it8);
}
} while (it8->sy == SCOMMENT);
}
/* Checks end of line separator */
static
BOOL CheckEOLN(LPIT8 it8)
{
if (!Check(it8, SEOLN, "Expected separator")) return false;
while (it8 -> sy == SEOLN)
InSymbol(it8);
return true;
}
/* Skip a symbol */
static
void Skip(LPIT8 it8, SYMBOL sy)
{
if (it8->sy == sy && it8->sy != SEOF)
InSymbol(it8);
}
/* Returns a string holding current value */
static
BOOL GetVal(LPIT8 it8, char* Buffer)
{
switch (it8->sy) {
case SIDENT: strncpy(Buffer, it8->id, MAXID-1); break;
case SINUM: sprintf(Buffer, "%d", it8 -> inum); break;
case SDNUM: sprintf(Buffer, "%g", it8 -> dnum); break;
case SSTRING: strncpy(Buffer, it8->str, MAXSTR-1); break;
default:
return SynError(it8, "Sample data expected");
}
return true;
}
/* ---------------------------------------------------------- Memory management */
/* Frees an allocator and owned memory */
void cmsxIT8Free(LCMSHANDLE hIT8)
{
LPIT8 it8 = (LPIT8) hIT8;
if (it8 == NULL)
return;
if (it8->MemorySink) {
LPOWNEDMEM p;
LPOWNEDMEM n;
for (p = it8->MemorySink; p != NULL; p = n) {
n = p->Next;
if (p->Ptr) free(p->Ptr);
free(p);
}
}
if (it8->FileBuffer)
free(it8->FileBuffer);
free(it8);
}
/* Allocates a chunk of data, keep linked list */
static
void* AllocChunk(LPIT8 it8, size_t size)
{
LPOWNEDMEM ptr1;
void* ptr = malloc(size);
if (ptr) {
ZeroMemory(ptr, size);
ptr1 = (LPOWNEDMEM) malloc(sizeof(OWNEDMEM));
if (ptr1 == NULL) {
free(ptr);
return NULL;
}
ZeroMemory(ptr1, sizeof(OWNEDMEM));
ptr1-> Ptr = ptr;
ptr1-> Next = it8 -> MemorySink;
it8 -> MemorySink = ptr1;
}
return ptr;
}
/* Allocates a string */
static
char *AllocString(LPIT8 it8, const char* str)
{
int Size = strlen(str)+1;
char *ptr;
ptr = (char *) AllocChunk(it8, Size);
if (ptr) strncpy (ptr, str, Size);
return ptr;
}
/* Searches through linked list */
static
BOOL IsAvailableOnList(LPKEYVALUE p, const char* Key, LPKEYVALUE* LastPtr)
{
for (; p != NULL; p = p->Next) {
if (LastPtr) *LastPtr = p;
if (stricmp(Key, p->Keyword) == 0)
return true;
}
return false;
}
/* Add a property into a linked list */
static
BOOL AddToList(LPIT8 it8, LPKEYVALUE* Head, const char *Key, const char* Value)
{
LPKEYVALUE p;
LPKEYVALUE last;
/* Check if property is already in list (this is an error) */
if (IsAvailableOnList(*Head, Key, &last)) {
cmsSignalError(LCMS_ERRC_ABORTED, "duplicate key <%s>", Key);
return false;
}
/* Allocate the container */
p = (LPKEYVALUE) AllocChunk(it8, sizeof(KEYVALUE));
if (p == NULL)
{
cmsSignalError(LCMS_ERRC_ABORTED, "AddToList: out of memory");
return false;
}
/* Store name and value */
p->Keyword = AllocString(it8, Key);
if (Value)
p->Value = AllocString(it8, Value);
else
p->Value = NULL;
p->Next = NULL;
/* Keep the container in our list */
if (*Head == NULL)
*Head = p;
else
last->Next = p;
return true;
}
static
BOOL AddAvailableProperty(LPIT8 it8, const char* Key)
{
return AddToList(it8, &it8->ValidKeywords, Key, NULL);
}
static
BOOL AddAvailableSampleID(LPIT8 it8, const char* Key)
{
return AddToList(it8, &it8->ValidSampleID, Key, NULL);
}
/* Init an empty container */
LCMSHANDLE cmsxIT8Alloc(void)
{
LPIT8 it8;
int i;
it8 = (LPIT8) malloc(sizeof(IT8));
if (it8 == NULL) return NULL;
ZeroMemory(it8, sizeof(IT8));
it8->HeaderList = NULL;
it8->FileBuffer = NULL;
it8->DataFormat = NULL;
it8->Data = NULL;
it8->MemorySink = NULL;
it8->ValidKeywords = NULL;
it8->ValidSampleID = NULL;
it8 -> sy = SNONE;
it8 -> ch = ' ';
it8 -> Source = NULL;
it8 -> inum = 0;
it8 -> dnum = 0.0;
it8 -> lineno = 1;
strcpy(it8->SheetType, "IT8.7/2");
/* Initialize predefined properties & data */
for (i=0; i < NUMPREDEFINEDPROPS; i++)
AddAvailableProperty(it8, PredefinedProperties[i]);
for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)
AddAvailableSampleID(it8, PredefinedSampleID[i]);
return (LCMSHANDLE) it8;
}
const char* cdecl cmsxIT8GetSheetType(LCMSHANDLE hIT8)
{
LPIT8 it8 = (LPIT8) hIT8;
return it8 ->SheetType;
}
BOOL cmsxIT8SetSheetType(LCMSHANDLE hIT8, const char* Type)
{
LPIT8 it8 = (LPIT8) hIT8;
strncpy(it8 ->SheetType, Type, MAXSTR-1);
return true;
}
/* Sets a property */
BOOL cmsxIT8SetProperty(LCMSHANDLE hIT8, const char* Key, const char *Val)
{
LPIT8 it8 = (LPIT8) hIT8;
if (!Val) return false;
if (!*Val) return false;
return AddToList(it8, &it8 -> HeaderList, Key, Val);
}
BOOL cmsxIT8SetPropertyDbl(LCMSHANDLE hIT8, const char* cProp, double Val)
{
char Buffer[256];
sprintf(Buffer, "%g", Val);
return cmsxIT8SetProperty(hIT8, cProp, Buffer);
}
/* Gets a property */
const char* cmsxIT8GetProperty(LCMSHANDLE hIT8, const char* Key)
{
LPIT8 it8 = (LPIT8) hIT8;
LPKEYVALUE p;
if (IsAvailableOnList(it8 -> HeaderList, Key, &p))
{
return p -> Value;
}
return NULL;
}
double cmsxIT8GetPropertyDbl(LCMSHANDLE hIT8, const char* cProp)
{
const char *v = cmsxIT8GetProperty(hIT8, cProp);
if (v) return atof(v);
else return 0.0;
}
/* ----------------------------------------------------------------- Datasets */
static
void AllocateDataFormat(LPIT8 it8)
{
if (it8 -> DataFormat) return; /* Already allocated */
it8 -> nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
if (it8 -> nSamples <= 0) {
cmsSignalError(LCMS_ERRC_WARNING, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS, assuming 10");
it8 -> nSamples = 10;
}
it8 -> DataFormat = (char**) AllocChunk (it8, (it8->nSamples + 1) * sizeof(char *));
if (it8->DataFormat == NULL)
{
cmsSignalError(LCMS_ERRC_ABORTED, "AllocateDataFormat: Unable to allocate dataFormat array");
}
}
static
const char *GetDataFormat(LPIT8 it8, int n)
{
if (it8->DataFormat)
return it8->DataFormat[n];
return NULL;
}
static
BOOL SetDataFormat(LPIT8 it8, int n, const char *label)
{
if (n > it8 -> nSamples) return false;
if (!it8->DataFormat)
AllocateDataFormat(it8);
if (it8->DataFormat) {
it8->DataFormat[n] = AllocString(it8, label);
}
return true;
}
BOOL cmsxIT8SetDataFormat(LCMSHANDLE h, int n, const char *Sample)
{
LPIT8 it8 = (LPIT8) h;
return SetDataFormat(it8, n, Sample);
}
static
void AllocateDataSet(LPIT8 it8)
{
if (it8 -> Data) return; /* Already allocated */
it8-> nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
it8-> nPatches = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_SETS"));
it8-> Data = (char**)AllocChunk (it8, (it8->nSamples + 1) * (it8->nPatches + 1) *sizeof (char*));
if (it8->Data == NULL)
{
cmsSignalError(-1, "AllocateDataSet: Unable to allocate data array");
}
}
static
char* GetData(LPIT8 it8, int nSet, int nField)
{
int nSamples = it8 -> nSamples;
int nPatches = it8 -> nPatches;
if (nSet >= nPatches || nField >= nSamples)
return NULL;
if (!it8->Data) return NULL;
return it8->Data [nSet * nSamples + nField];
}
static
BOOL SetData(LPIT8 it8, int nSet, int nField, char *Val)
{
if (!it8->Data)
AllocateDataSet(it8);
if (!it8->Data) return false;
if (nSet > it8 -> nPatches) {
SynError(it8, "Patch %d out of range, there are %d datasets", nSet, it8 -> nPatches);
return false;
}
if (nField > it8 ->nSamples) {
SynError(it8, "Sample %d out of range, there are %d datasets", nField, it8 ->nSamples);
return false;
}
it8->Data [nSet * it8 -> nSamples + nField] = AllocString(it8, Val);
return true;
}
/* --------------------------------------------------------------- File I/O */
/* Writes a string to file */
static
void WriteStr(FILE *f, char *str)
{
if (str == NULL)
fwrite(" ", 1, 1, f);
else
fwrite(str, 1, strlen(str), f);
}
/* Writes full header */
static
void WriteHeader(LPIT8 it8, FILE *fp)
{
LPKEYVALUE p;
WriteStr(fp, it8->SheetType);
WriteStr(fp, "\n");
for (p = it8->HeaderList; (p != NULL); p = p->Next)
{
if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL)) {
WriteStr(fp, "KEYWORD\t\"");
WriteStr(fp, p->Keyword);
WriteStr(fp, "\"\n");
}
WriteStr(fp, p->Keyword);
if (p->Value) {
WriteStr(fp, "\t\"");
WriteStr(fp, p->Value);
WriteStr(fp, "\"");
}
WriteStr (fp, "\n");
}
}
/* Writes the data format */
static
void WriteDataFormat(FILE *fp, LPIT8 it8)
{
int i, nSamples;
if (!it8 -> DataFormat) return;
WriteStr(fp, "BEGIN_DATA_FORMAT\n");
nSamples = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
for (i = 0; i < nSamples; i++) {
WriteStr(fp, it8->DataFormat[i]);
WriteStr(fp, (char*)((i == (nSamples-1)) ? "\n" : "\t")); // C->C++ : cast
}
WriteStr (fp, "END_DATA_FORMAT\n");
}
/* Writes data array */
static
void WriteData(FILE *fp, LPIT8 it8)
{
int i, j;
if (!it8->Data) return;
WriteStr (fp, "BEGIN_DATA\n");
it8->nPatches = atoi(cmsxIT8GetProperty(it8, "NUMBER_OF_SETS"));
for (i = 0; i < it8-> nPatches; i++) {
for (j = 0; j < it8->nSamples; j++) {
char *ptr = it8->Data[i*it8->nSamples+j];
WriteStr(fp, (char*)((ptr == NULL) ? "0.00" : ptr)); // C->C++ : cast
WriteStr(fp, (char*)((j == (it8->nSamples-1)) ? "\n" : "\t")); // C->C++ : cast
}
}
WriteStr (fp, "END_DATA\n");
}
/* Saves whole file */
BOOL cmsxIT8SaveToFile(LCMSHANDLE hIT8, const char* cFileName)
{
FILE *fp;
LPIT8 it8 = (LPIT8) hIT8;
fp = fopen(cFileName, "wt");
if (!fp) return false;
WriteHeader(it8, fp);
WriteDataFormat(fp, it8);
WriteData(fp, it8);
fclose(fp);
return true;
}
/* Reads whole file in a memory block */
static
BOOL ReadFileInMemory(const char *cFileName, char **Buffer, size_t *Len)
{
FILE *fp;
size_t Size;
char *Ptr;
fp = fopen(cFileName, "rt");
if (!fp) return false;
Size = xfilelength(fileno(fp));
if (Size <= 0) {
fclose(fp);
return false;
}
Ptr = (char*)malloc(Size+1); // C->C++ : cast
Size = fread(Ptr, 1, Size, fp);
fclose(fp);
Ptr[Size] = '\0';
*Buffer = Ptr;
*Len = Size;
return true;
}
/* -------------------------------------------------------------- Higer lever parsing */
static
BOOL DataFormatSection(LPIT8 it8)
{
int iField = 0;
BOOL Ignoring = false;
InSymbol(it8); /* Eats "BEGIN_DATA_FORMAT" */
CheckEOLN(it8);
while (it8->sy != SEND_DATA_FORMAT &&
it8->sy != SEOLN &&
it8->sy != SEOF &&
it8->sy != SSYNERROR)
{
if (it8->sy != SIDENT) {
cmsSignalError(LCMS_ERRC_ABORTED, "Sample type expected");
it8->sy = SSYNERROR;
return false;
}
if (!Ignoring && iField > it8->nSamples) {
cmsSignalError(LCMS_ERRC_WARNING, "More than NUMBER_OF_FIELDS fields. Extra is ignored\n");
Ignoring = true;
}
else {
if (!SetDataFormat(it8, iField, it8->id)) return false;
iField++;
}
InSymbol(it8);
Skip(it8, SEOLN);
}
Skip(it8, SEOLN);
Skip(it8, SEND_DATA_FORMAT);
Skip(it8, SEOLN);
return true;
}
static
BOOL DataSection (LPIT8 it8)
{
int iField = 0;
int iSet = 0;
char Buffer[256];
InSymbol(it8); /* Eats "BEGIN_DATA" */
CheckEOLN(it8);
while (it8->sy != SEND_DATA && it8->sy != SEOF)
{
if (iField >= it8 -> nSamples) {
iField = 0;
iSet++;
if (!CheckEOLN(it8))
return false;
}
if (it8->sy != SEND_DATA && it8->sy != SEOF) {
if (!GetVal(it8, Buffer))
return false;
if (!SetData(it8, iSet, iField, Buffer))
return false;
iField++;
Skip(it8, SEOLN);
InSymbol(it8);
}
}
Skip(it8, SEOLN);
Skip(it8, SEND_DATA);
Skip(it8, SEOLN);
return true;
}
static
BOOL HeaderSection (LPIT8 it8)
{
char VarName[MAXID];
char Buffer[MAXSTR];
while (it8->sy != SEOF &&
it8->sy != SSYNERROR &&
it8->sy != SBEGIN_DATA_FORMAT &&
it8->sy != SBEGIN_DATA) {
switch (it8 -> sy) {
case SKEYWORD:
InSymbol(it8);
if (!Check(it8, SSTRING, "Keyword expected")) return false;
if (!AddAvailableProperty(it8, it8 -> str)) return false;
InSymbol(it8);
break;
case SIDENT:
strncpy(VarName, it8->id, MAXID-1);
if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL))
return SynError(it8, "Undefined keyword '%s'", VarName);
InSymbol(it8);
GetVal(it8, Buffer);
cmsxIT8SetProperty((LCMSHANDLE) it8, VarName, Buffer);
InSymbol(it8);
break;
case SEOLN: break;
default:
return SynError(it8, "expected keyword or identifier");
}
Skip(it8, SEOLN);
}
return true;
}
static
BOOL ParseIT8(LPIT8 it8)
{
InSymbol(it8);
if (it8->sy == SIDENT) {
strncpy(it8->SheetType, it8->id, MAXSTR-1);
InSymbol(it8);
/* if (!AddAvailableProperty(it8, it8 -> id)) return false; */
/* cmsxIT8SetProperty((LCMSHANDLE) it8, it8->id, NULL); */
}
Skip(it8, SEOLN);
while (it8-> sy != SEOF &&
it8-> sy != SSYNERROR) {
switch (it8 -> sy) {
case SBEGIN_DATA_FORMAT:
if (!DataFormatSection(it8)) return false;
break;
case SBEGIN_DATA:
if (!DataSection(it8)) return false;
break;
case SEOLN:
Skip(it8, SEOLN);
break;
default:
if (!HeaderSection(it8)) return false;
}
}
return true;
}
static
void CleanPatchName(char *cell)
{
char cleaned[256], Buffer[256], ident[256];
char *orig = cell, *id;
int n, lOneNum;
id = ident;
while (*cell && isalpha(*cell))
{
*id++ = (char) toupper(*cell);
cell++;
}
*id = 0;
strcpy(cleaned, ident);
n = 0;
lOneNum = false;
while (*cell && isdigit(*cell))
{
n = n * 10 + (*cell -'0');
cell++;
lOneNum = true;
}
if (lOneNum) {
sprintf(Buffer, "%d", n);
strcat(cleaned, Buffer);
}
if (strcmp(cleaned, "GS0") == 0)
strcpy(orig, "DMIN");
else
if (strcmp(cleaned, "GS23") == 0)
strcpy(orig, "DMAX");
else
strcpy(orig, cleaned);
}
/* Init useful pointers */
static
void CookPointers(LPIT8 it8)
{
int idField, i;
char* Fld;
it8 -> SampleID = 0;
for (idField = 0; idField < it8 -> nSamples; idField++)
{
Fld = it8->DataFormat[idField];
if (!Fld) continue;
if (strcmp(Fld, "SAMPLE_ID") == 0) {
it8 -> SampleID = idField;
for (i=0; i < it8 -> nPatches; i++) {
char *Data = GetData(it8, i, idField);
if (Data) {
char Buffer[256];
strncpy(Buffer, Data, 255);
CleanPatchName(Buffer);
if (strlen(Buffer) <= strlen(Data))
strcpy(Data, Buffer);
else
SetData(it8, i, idField, Buffer);
}
}
}
}
}
/* ---------------------------------------------------------- Exported routines */
LCMSHANDLE cmsxIT8LoadFromMem(void *Ptr, size_t len)
{
LCMSHANDLE hIT8 = cmsxIT8Alloc();
LPIT8 it8 = (LPIT8) hIT8;
if (!hIT8) return NULL;
it8 ->FileBuffer = (char*) malloc(len + 1);
strncpy(it8 ->FileBuffer, (const char*)Ptr, len); // C->C++ : cast
strncpy(it8->FileName, "", MAX_PATH-1);
it8-> Source = it8 -> FileBuffer;
ParseIT8(it8);
CookPointers(it8);
free(it8->FileBuffer);
it8 -> FileBuffer = NULL;
return hIT8;
}
LCMSHANDLE cmsxIT8LoadFromFile(const char* cFileName)
{
LCMSHANDLE hIT8 = cmsxIT8Alloc();
LPIT8 it8 = (LPIT8) hIT8;
size_t Len;
if (!hIT8) return NULL;
if (!ReadFileInMemory(cFileName, &it8->FileBuffer, &Len)) return NULL;
strncpy(it8->FileName, cFileName, MAX_PATH-1);
it8-> Source = it8 -> FileBuffer;
ParseIT8(it8);
CookPointers(it8);
free(it8->FileBuffer);
it8 -> FileBuffer = NULL;
return hIT8;
}
int cmsxIT8EnumDataFormat(LCMSHANDLE hIT8, char ***SampleNames)
{
LPIT8 it8 = (LPIT8) hIT8;
*SampleNames = it8 -> DataFormat;
return it8 -> nSamples;
}
int cmsxIT8EnumProperties(LCMSHANDLE hIT8, char ***PropertyNames)
{
LPIT8 it8 = (LPIT8) hIT8;
LPKEYVALUE p;
int n;
char **Props;
/* Pass#1 - count properties */
n = 0;
for (p = it8 -> HeaderList; p != NULL; p = p->Next) {
n++;
}
Props = (char **) malloc(sizeof(char *) * n);
/* Pass#2 - Fill pointers */
n = 0;
for (p = it8 -> HeaderList; p != NULL; p = p->Next) {
Props[n++] = p -> Keyword;
}
*PropertyNames = Props;
return n;
}
static
int LocatePatch(LPIT8 it8, const char* cPatch)
{
int i;
const char *data;
for (i=0; i < it8-> nPatches; i++) {
data = GetData(it8, i, it8->SampleID);
if (data != NULL) {
if (stricmp(data, cPatch) == 0)
return i;
}
}
return -1;
}
static
int LocateEmptyPatch(LPIT8 it8, const char* cPatch)
{
int i;
const char *data;
for (i=0; i < it8-> nPatches; i++) {
data = GetData(it8, i, it8->SampleID);
if (data == NULL)
return i;
}
return -1;
}
static
int LocateSample(LPIT8 it8, const char* cSample)
{
int i;
const char *fld;
for (i=0; i < it8->nSamples; i++) {
fld = GetDataFormat(it8, i);
if (stricmp(fld, cSample) == 0)
return i;
}
return -1;
}
BOOL cmsxIT8GetDataSetByPos(LCMSHANDLE hIT8, int col, int row, char* Val, int ValBufferLen)
{
LPIT8 it8 = (LPIT8) hIT8;
const char *data = GetData(it8, row, col);
if (!data)
{
*Val = '\0';
return false;
}
strncpy(Val, data, ValBufferLen-1);
return true;
}
BOOL cmsxIT8GetDataSet(LCMSHANDLE hIT8, const char* cPatch,
const char* cSample,
char* Val, int ValBuffLen)
{
LPIT8 it8 = (LPIT8) hIT8;
int iField, iSet;
iField = LocateSample(it8, cSample);
if (iField < 0) {
/* cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find data field %s\n", cSample); */
return false;
}
iSet = LocatePatch(it8, cPatch);
if (iSet < 0) {
/* cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find patch '%s'\n", cPatch); */
return false;
}
strncpy(Val, GetData(it8, iSet, iField), ValBuffLen-1);
return true;
}
BOOL cmsxIT8GetDataSetDbl(LCMSHANDLE it8, const char* cPatch, const char* cSample, double* v)
{
char Buffer[20];
if (cmsxIT8GetDataSet(it8, cPatch, cSample, Buffer, 20)) {
*v = atof(Buffer);
return true;
} else
return false;
}
BOOL cmsxIT8SetDataSet(LCMSHANDLE hIT8, const char* cPatch,
const char* cSample,
char *Val)
{
LPIT8 it8 = (LPIT8) hIT8;
int iField, iSet;
iField = LocateSample(it8, cSample);
if (iField < 0) {
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find data field %s\n", cSample);
return false;
}
if (it8-> nPatches == 0) {
AllocateDataFormat(it8);
AllocateDataSet(it8);
CookPointers(it8);
}
if (stricmp(cSample, "SAMPLE_ID") == 0)
{
iSet = LocateEmptyPatch(it8, cPatch);
if (iSet < 0) {
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't add more patches '%s'\n", cPatch);
return false;
}
iField = it8 -> SampleID;
}
else {
iSet = LocatePatch(it8, cPatch);
if (iSet < 0) {
cmsSignalError(LCMS_ERRC_ABORTED, "Couldn't find patch '%s'\n", cPatch);
return false;
}
}
return SetData(it8, iSet, iField, Val);
}
BOOL cmsxIT8SetDataSetDbl(LCMSHANDLE hIT8, const char* cPatch,
const char* cSample,
double Val)
{
char Buff[256];
sprintf(Buff, "%g", Val);
return cmsxIT8SetDataSet(hIT8, cPatch, cSample, Buff);
}
const char* cmsxIT8GetPatchName(LCMSHANDLE hIT8, int nPatch, char* buffer)
{
LPIT8 it8 = (LPIT8) hIT8;
char* Data = GetData(it8, nPatch, it8->SampleID);
if (!Data) return NULL;
strcpy(buffer, Data);
return buffer;
}
const char* cmsxIT8GenericPatchName(int nPatch, char* buffer)
{
int row, col;
if (nPatch >= cmsxIT8_NORMAL_PATCHES)
return "$CUSTOM";
if (nPatch >= (cmsxIT8_ROWS * cmsxIT8_COLS)) {
nPatch -= cmsxIT8_ROWS * cmsxIT8_COLS;
if (nPatch == 0)
return "DMIN";
else
if (nPatch == cmsxIT8_GRAYCOLS - 1)
return "DMAX";
else
sprintf(buffer, "GS%d", nPatch);
return buffer;
}
row = nPatch / cmsxIT8_COLS;
col = nPatch % cmsxIT8_COLS;
sprintf (buffer, "%c%d", 'A'+row, col+1);
return buffer;
}
int cmsxIT8GenericPatchNum(const char *name)
{
int i;
char Buff[256];
for (i=0; i < cmsxIT8_TOTAL_PATCHES; i++)
if (stricmp(cmsxIT8GenericPatchName(i, Buff), name) == 0)
return i;
return -1;
}