/* */ /* Little cms - profiler construction set */ /* Copyright (C) 1998-2001 Marti Maria */ /* */ /* 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 #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 “black”, “white”, 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; }