Subversion Repositories Scribus

Rev

Blame | Last modification | View Log | RSS feed

//
//  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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 lenght of identifier
#define MAXSTR  255     // Max lenght 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 “black”, “white”, or "na".

            "CHISQ_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_SQD_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 compatiblity 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, ((i == (nSamples-1)) ? "\n" : "\t"));
          }

       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, ((ptr == NULL) ? "0.00" : ptr));
                     WriteStr(fp, ((j == (it8->nSamples-1)) ? "\n" : "\t"));
              }
       }
       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  = malloc(Size+1);

       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 usefull 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, Ptr, len);
    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;
}