#include "lcms2_internal.h"
#define MAXID 128
#define MAXSTR 1024
#define MAXTABLES 255
#define MAXINCLUDE 20
#define DEFAULT_DBL_FORMAT "%.10g"
#ifdef CMS_IS_WINDOWS_
# include <io.h>
# define DIR_CHAR '\\'
#else
# define DIR_CHAR '/'
#endif
typedef enum {
SUNDEFINED,
SINUM, SDNUM, SIDENT, SSTRING, SCOMMENT, SEOLN, SEOF, SSYNERROR,
SBEGIN_DATA,
SBEGIN_DATA_FORMAT,
SEND_DATA,
SEND_DATA_FORMAT,
SKEYWORD,
SDATA_FORMAT_ID,
SINCLUDE,
SDOMAIN_MAX,
SDOMAIN_MIN,
S_LUT1D_SIZE,
S_LUT1D_INPUT_RANGE,
S_LUT3D_SIZE,
S_LUT3D_INPUT_RANGE,
S_LUT_IN_VIDEO_RANGE,
S_LUT_OUT_VIDEO_RANGE,
STITLE
} SYMBOL;
typedef enum {
WRITE_UNCOOKED,
WRITE_STRINGIFY,
WRITE_HEXADECIMAL,
WRITE_BINARY,
WRITE_PAIR
} WRITEMODE;
typedef struct _KeyVal {
struct _KeyVal* Next;
char* Keyword; struct _KeyVal* NextSubkey; char* Subkey; char* Value; WRITEMODE WriteAs;
} KEYVALUE;
typedef struct _OwnedMem {
struct _OwnedMem* Next;
void * Ptr;
} OWNEDMEM;
typedef struct _SubAllocator {
cmsUInt8Number* Block;
cmsUInt32Number BlockSize;
cmsUInt32Number Used;
} SUBALLOCATOR;
typedef struct _Table {
char SheetType[MAXSTR];
int nSamples, nPatches; int SampleID;
KEYVALUE* HeaderList;
char** DataFormat; char** Data;
} TABLE;
typedef struct _FileContext {
char FileName[cmsMAX_PATH]; FILE* Stream; } FILECTX;
typedef struct {
struct struct_it8* it8;
cmsInt32Number max;
cmsInt32Number len;
char* begin;
} string;
typedef struct struct_it8 {
cmsUInt32Number TablesCount; cmsUInt32Number nTable;
cmsBool IsCUBE;
TABLE Tab[MAXTABLES];
OWNEDMEM* MemorySink; SUBALLOCATOR Allocator;
SYMBOL sy; int ch;
cmsInt32Number inum; cmsFloat64Number dnum;
string* id; string* str;
KEYVALUE* ValidKeywords;
KEYVALUE* ValidSampleID;
char* Source; cmsInt32Number lineno;
FILECTX* FileStack[MAXINCLUDE]; cmsInt32Number IncludeSP;
char* MemoryBlock;
char DoubleFormatter[MAXID];
cmsContext ContextID;
} cmsIT8;
typedef struct {
FILE* stream;
cmsUInt8Number* Base;
cmsUInt8Number* Ptr; cmsUInt32Number Used;
cmsUInt32Number Max;
} SAVESTREAM;
typedef struct {
const char *id;
SYMBOL sy;
} KEYWORD;
static const KEYWORD TabKeysIT8[] = {
{"$INCLUDE", SINCLUDE}, {".INCLUDE", SINCLUDE},
{"BEGIN_DATA", SBEGIN_DATA },
{"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT },
{"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID},
{"END_DATA", SEND_DATA},
{"END_DATA_FORMAT", SEND_DATA_FORMAT},
{"KEYWORD", SKEYWORD}
};
#define NUMKEYS_IT8 (sizeof(TabKeysIT8)/sizeof(KEYWORD))
static const KEYWORD TabKeysCUBE[] = {
{"DOMAIN_MAX", SDOMAIN_MAX },
{"DOMAIN_MIN", SDOMAIN_MIN },
{"LUT_1D_SIZE", S_LUT1D_SIZE },
{"LUT_1D_INPUT_RANGE", S_LUT1D_INPUT_RANGE },
{"LUT_3D_SIZE", S_LUT3D_SIZE },
{"LUT_3D_INPUT_RANGE", S_LUT3D_INPUT_RANGE },
{"LUT_IN_VIDEO_RANGE", S_LUT_IN_VIDEO_RANGE },
{"LUT_OUT_VIDEO_RANGE", S_LUT_OUT_VIDEO_RANGE },
{"TITLE", STITLE }
};
#define NUMKEYS_CUBE (sizeof(TabKeysCUBE)/sizeof(KEYWORD))
typedef struct {
const char *id; WRITEMODE as; } PROPERTY;
static const PROPERTY PredefinedProperties[] = {
{"NUMBER_OF_FIELDS", WRITE_UNCOOKED}, {"NUMBER_OF_SETS", WRITE_UNCOOKED}, {"ORIGINATOR", WRITE_STRINGIFY}, {"FILE_DESCRIPTOR", WRITE_STRINGIFY}, {"CREATED", WRITE_STRINGIFY}, {"DESCRIPTOR", WRITE_STRINGIFY}, {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY}, {"MANUFACTURER", WRITE_STRINGIFY},
{"MANUFACTURE", WRITE_STRINGIFY}, {"PROD_DATE", WRITE_STRINGIFY}, {"SERIAL", WRITE_STRINGIFY},
{"MATERIAL", WRITE_STRINGIFY},
{"INSTRUMENTATION", WRITE_STRINGIFY},
{"MEASUREMENT_SOURCE", WRITE_STRINGIFY},
{"PRINT_CONDITIONS", WRITE_STRINGIFY},
{"SAMPLE_BACKING", WRITE_STRINGIFY},
{"CHISQ_DOF", WRITE_STRINGIFY},
{"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY},
{"FILTER", WRITE_STRINGIFY},
{"POLARIZATION", WRITE_STRINGIFY},
{"WEIGHTING_FUNCTION", WRITE_PAIR},
{"COMPUTATIONAL_PARAMETER", WRITE_PAIR},
{"TARGET_TYPE", WRITE_STRINGIFY},
{"COLORANT", WRITE_STRINGIFY},
{"TABLE_DESCRIPTOR", WRITE_STRINGIFY},
{"TABLE_NAME", WRITE_STRINGIFY} };
#define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY))
static const char* PredefinedSampleID[] = {
"SAMPLE_ID", "STRING",
"CMYK_C", "CMYK_M", "CMYK_Y", "CMYK_K", "D_RED", "D_GREEN", "D_BLUE", "D_VIS", "D_MAJOR_FILTER", "RGB_R", "RGB_G", "RGB_B", "SPECTRAL_NM", "SPECTRAL_PCT", "SPECTRAL_DEC", "XYZ_X", "XYZ_Y", "XYZ_Z", "XYY_X", "XYY_Y", "XYY_CAPY", "LAB_L", "LAB_A", "LAB_B", "LAB_C", "LAB_H", "LAB_DE", "LAB_DE_94", "LAB_DE_CMC", "LAB_DE_2000", "MEAN_DE", "STDEV_X", "STDEV_Y", "STDEV_Z", "STDEV_L", "STDEV_A", "STDEV_B", "STDEV_DE", "CHI_SQD_PAR"};
#define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size);
static
string* StringAlloc(cmsIT8* it8, int max)
{
string* s = (string*) AllocChunk(it8, sizeof(string));
if (s == NULL) return NULL;
s->it8 = it8;
s->max = max;
s->len = 0;
s->begin = (char*) AllocChunk(it8, s->max);
return s;
}
static
void StringClear(string* s)
{
s->len = 0;
s->begin[0] = 0;
}
static
cmsBool StringAppend(string* s, char c)
{
if (s->len + 1 >= s->max)
{
char* new_ptr;
s->max *= 10;
new_ptr = (char*) AllocChunk(s->it8, s->max);
if (new_ptr == NULL) return FALSE;
if (s->begin != NULL)
memcpy(new_ptr, s->begin, s->len);
s->begin = new_ptr;
}
if (s->begin != NULL)
{
s->begin[s->len++] = c;
s->begin[s->len] = 0;
}
return TRUE;
}
static
char* StringPtr(string* s)
{
return s->begin;
}
static
cmsBool StringCat(string* s, const char* c)
{
while (*c)
{
if (!StringAppend(s, *c)) return FALSE;
c++;
}
return TRUE;
}
static
cmsBool isseparator(int c)
{
return (c == ' ') || (c == '\t');
}
static
cmsBool ismiddle(int c)
{
return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127));
}
static
cmsBool isidchar(int c)
{
return isalnum(c) || ismiddle(c);
}
static
cmsBool isfirstidchar(int c)
{
return c != '-' && !isdigit(c) && ismiddle(c);
}
static
cmsBool isabsolutepath(const char *path)
{
char ThreeChars[4];
if(path == NULL)
return FALSE;
if (path[0] == 0)
return FALSE;
strncpy(ThreeChars, path, 3);
ThreeChars[3] = 0;
if(ThreeChars[0] == DIR_CHAR)
return TRUE;
#ifdef CMS_IS_WINDOWS_
if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':')
return TRUE;
#endif
return FALSE;
}
static
cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen)
{
char *tail;
cmsUInt32Number len;
if (isabsolutepath(relPath)) {
memcpy(buffer, relPath, MaxLen);
buffer[MaxLen-1] = 0;
return TRUE;
}
memcpy(buffer, basePath, MaxLen);
buffer[MaxLen-1] = 0;
tail = strrchr(buffer, DIR_CHAR);
if (tail == NULL) return FALSE;
len = (cmsUInt32Number) (tail - buffer);
if (len >= MaxLen) return FALSE;
strncpy(tail + 1, relPath, MaxLen - len);
return TRUE;
}
static
const char* NoMeta(const char* str)
{
if (strchr(str, '%') != NULL)
return "**** CORRUPTED FORMAT STRING ***";
return str;
}
static
cmsBool SynError(cmsIT8* it8, const char *Txt, ...)
{
char Buffer[256], ErrMsg[1024];
va_list args;
va_start(args, Txt);
vsnprintf(Buffer, 255, Txt, args);
Buffer[255] = 0;
va_end(args);
snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer);
ErrMsg[1023] = 0;
it8->sy = SSYNERROR;
cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg);
return FALSE;
}
static
cmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err)
{
if (it8 -> sy != sy)
return SynError(it8, NoMeta(Err));
return TRUE;
}
static
void NextCh(cmsIT8* it8)
{
if (it8 -> FileStack[it8 ->IncludeSP]->Stream) {
it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream);
if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream)) {
if (it8 ->IncludeSP > 0) {
fclose(it8 ->FileStack[it8->IncludeSP--]->Stream);
it8 -> ch = ' ';
} else
it8 ->ch = 0; }
}
else {
it8->ch = *it8->Source;
if (it8->ch) it8->Source++;
}
}
static
SYMBOL BinSrchKey(const char *id, int NumKeys, const KEYWORD* TabKeys)
{
int l = 1;
int r = NumKeys;
int x, res;
while (r >= l)
{
x = (l+r)/2;
res = cmsstrcasecmp(id, TabKeys[x-1].id);
if (res == 0) return TabKeys[x-1].sy;
if (res < 0) r = x - 1;
else l = x + 1;
}
return SUNDEFINED;
}
static
cmsFloat64Number xpow10(int n)
{
return pow(10, (cmsFloat64Number) n);
}
static
void ReadReal(cmsIT8* it8, cmsInt32Number inum)
{
it8->dnum = (cmsFloat64Number)inum;
while (isdigit(it8->ch)) {
it8->dnum = (cmsFloat64Number)it8->dnum * 10.0 + (cmsFloat64Number)(it8->ch - '0');
NextCh(it8);
}
if (it8->ch == '.') {
cmsFloat64Number frac = 0.0; int prec = 0;
NextCh(it8);
while (isdigit(it8->ch)) {
frac = frac * 10.0 + (cmsFloat64Number)(it8->ch - '0');
prec++;
NextCh(it8);
}
it8->dnum = it8->dnum + (frac / xpow10(prec));
}
if (toupper(it8->ch) == 'E') {
cmsInt32Number e;
cmsInt32Number 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)) {
cmsInt32Number digit = (it8->ch - '0');
if ((cmsFloat64Number)e * 10.0 + (cmsFloat64Number)digit < (cmsFloat64Number)+2147483647.0)
e = e * 10 + digit;
NextCh(it8);
}
e = sgn*e;
it8->dnum = it8->dnum * xpow10(e);
}
}
static
cmsFloat64Number ParseFloatNumber(const char *Buffer)
{
cmsFloat64Number dnum = 0.0;
int sign = 1;
if (Buffer == NULL) return 0.0;
if (*Buffer == '-' || *Buffer == '+') {
sign = (*Buffer == '-') ? -1 : 1;
Buffer++;
}
while (*Buffer && isdigit((int)*Buffer)) {
dnum = dnum * 10.0 + (*Buffer - '0');
if (*Buffer) Buffer++;
}
if (*Buffer == '.') {
cmsFloat64Number frac = 0.0; int prec = 0;
if (*Buffer) Buffer++;
while (*Buffer && isdigit((int)*Buffer)) {
frac = frac * 10.0 + (*Buffer - '0');
prec++;
if (*Buffer) Buffer++;
}
dnum = dnum + (frac / xpow10(prec));
}
if (*Buffer && toupper(*Buffer) == 'E') {
int e;
int sgn;
if (*Buffer) Buffer++;
sgn = 1;
if (*Buffer == '-') {
sgn = -1;
if (*Buffer) Buffer++;
}
else
if (*Buffer == '+') {
sgn = +1;
if (*Buffer) Buffer++;
}
e = 0;
while (*Buffer && isdigit((int)*Buffer)) {
cmsInt32Number digit = (*Buffer - '0');
if ((cmsFloat64Number)e * 10.0 + digit < (cmsFloat64Number)+2147483647.0)
e = e * 10 + digit;
if (*Buffer) Buffer++;
}
e = sgn*e;
dnum = dnum * xpow10(e);
}
return sign * dnum;
}
static
void InStringSymbol(cmsIT8* it8)
{
while (isseparator(it8->ch))
NextCh(it8);
if (it8->ch == '\'' || it8->ch == '\"')
{
int sng;
sng = it8->ch;
StringClear(it8->str);
NextCh(it8);
while (it8->ch != sng) {
if (it8->ch == '\n' || it8->ch == '\r' || it8->ch == 0) break;
else {
if (!StringAppend(it8->str, (char)it8->ch)) {
SynError(it8, "Out of memory");
return;
}
NextCh(it8);
}
}
it8->sy = SSTRING;
NextCh(it8);
}
else
SynError(it8, "String expected");
}
static
void InSymbol(cmsIT8* it8)
{
SYMBOL key;
do {
while (isseparator(it8->ch))
NextCh(it8);
if (isfirstidchar(it8->ch)) {
StringClear(it8->id);
do {
if (!StringAppend(it8->id, (char)it8->ch)) {
SynError(it8, "Out of memory");
return;
}
NextCh(it8);
} while (isidchar(it8->ch));
key = BinSrchKey(StringPtr(it8->id),
it8->IsCUBE ? NUMKEYS_CUBE : NUMKEYS_IT8,
it8->IsCUBE ? TabKeysCUBE : TabKeysIT8);
if (key == SUNDEFINED) it8->sy = SIDENT;
else it8->sy = key;
}
else if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
{
int sign = 1;
if (it8->ch == '-') {
sign = -1;
NextCh(it8);
}
else
if (it8->ch == '+') {
sign = +1;
NextCh(it8);
}
it8->inum = 0;
it8->sy = SINUM;
if (it8->ch == '0') {
NextCh(it8);
if (toupper(it8->ch) == 'X') {
int j;
NextCh(it8);
while (isxdigit(it8->ch))
{
it8->ch = toupper(it8->ch);
if (it8->ch >= 'A' && it8->ch <= 'F') j = it8->ch -'A'+10;
else j = it8->ch - '0';
if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0)
{
SynError(it8, "Invalid hexadecimal number");
return;
}
it8->inum = it8->inum * 16 + j;
NextCh(it8);
}
return;
}
if (toupper(it8->ch) == 'B') {
int j;
NextCh(it8);
while (it8->ch == '0' || it8->ch == '1')
{
j = it8->ch - '0';
if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0)
{
SynError(it8, "Invalid binary number");
return;
}
it8->inum = it8->inum * 2 + j;
NextCh(it8);
}
return;
}
}
while (isdigit(it8->ch)) {
cmsInt32Number digit = (it8->ch - '0');
if ((cmsFloat64Number) it8->inum * 10.0 + (cmsFloat64Number) digit > (cmsFloat64Number) +2147483647.0) {
ReadReal(it8, it8->inum);
it8->sy = SDNUM;
it8->dnum *= sign;
return;
}
it8->inum = it8->inum * 10 + digit;
NextCh(it8);
}
if (it8->ch == '.') {
ReadReal(it8, it8->inum);
it8->sy = SDNUM;
it8->dnum *= sign;
return;
}
it8 -> inum *= sign;
if (isidchar(it8 ->ch)) {
char buffer[127];
if (it8 ->sy == SINUM) {
snprintf(buffer, sizeof(buffer), "%d", it8->inum);
}
else {
snprintf(buffer, sizeof(buffer), it8 ->DoubleFormatter, it8->dnum);
}
StringClear(it8->id);
if (!StringCat(it8->id, buffer)) {
SynError(it8, "Out of memory");
return;
}
do {
if (!StringAppend(it8->id, (char)it8->ch)) {
SynError(it8, "Out of memory");
return;
}
NextCh(it8);
} while (isidchar(it8->ch));
it8->sy = SIDENT;
}
return;
}
else
switch ((int) it8->ch) {
case '\x1a':
case 0:
case -1:
it8->sy = SEOF;
break;
case '\r':
NextCh(it8);
if (it8->ch == '\n')
NextCh(it8);
it8->sy = SEOLN;
it8->lineno++;
break;
case '\n':
NextCh(it8);
it8->sy = SEOLN;
it8->lineno++;
break;
case '#':
NextCh(it8);
while (it8->ch && it8->ch != '\n' && it8->ch != '\r')
NextCh(it8);
it8->sy = SCOMMENT;
break;
case '\'':
case '\"':
InStringSymbol(it8);
break;
default:
SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);
return;
}
} while (it8->sy == SCOMMENT);
if (it8 -> sy == SINCLUDE) {
FILECTX* FileNest;
if(it8 -> IncludeSP >= (MAXINCLUDE-1)) {
SynError(it8, "Too many recursion levels");
return;
}
InStringSymbol(it8);
if (!Check(it8, SSTRING, "Filename expected"))
return;
FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
if(FileNest == NULL) {
FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
if (FileNest == NULL) {
SynError(it8, "Out of memory");
return;
}
}
if (BuildAbsolutePath(StringPtr(it8->str),
it8->FileStack[it8->IncludeSP]->FileName,
FileNest->FileName, cmsMAX_PATH-1) == FALSE) {
SynError(it8, "File path too long");
return;
}
FileNest->Stream = fopen(FileNest->FileName, "rt");
if (FileNest->Stream == NULL) {
SynError(it8, "File %s not found", FileNest->FileName);
return;
}
it8->IncludeSP++;
it8 ->ch = ' ';
InSymbol(it8);
}
}
static
cmsBool CheckEOLN(cmsIT8* it8)
{
if (!Check(it8, SEOLN, "Expected separator")) return FALSE;
while (it8->sy == SEOLN)
InSymbol(it8);
return TRUE;
}
static
void Skip(cmsIT8* it8, SYMBOL sy)
{
if (it8->sy == sy && it8->sy != SEOF && it8->sy != SSYNERROR)
InSymbol(it8);
}
static
void SkipEOLN(cmsIT8* it8)
{
while (it8->sy == SEOLN) {
InSymbol(it8);
}
}
static
cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle)
{
switch (it8->sy) {
case SEOLN: Buffer[0]=0;
break;
case SIDENT: strncpy(Buffer, StringPtr(it8->id), max);
Buffer[max-1]=0;
break;
case SINUM: snprintf(Buffer, max, "%d", it8 -> inum); break;
case SDNUM: snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break;
case SSTRING: strncpy(Buffer, StringPtr(it8->str), max);
Buffer[max-1] = 0;
break;
default:
return SynError(it8, "%s", ErrorTitle);
}
Buffer[max] = 0;
return TRUE;
}
static
TABLE* GetTable(cmsIT8* it8)
{
if ((it8 -> nTable >= it8 ->TablesCount)) {
SynError(it8, "Table %d out of sequence", it8 -> nTable);
return it8 -> Tab;
}
return it8 ->Tab + it8 ->nTable;
}
void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
if (it8 == NULL)
return;
if (it8->MemorySink) {
OWNEDMEM* p;
OWNEDMEM* n;
for (p = it8->MemorySink; p != NULL; p = n) {
n = p->Next;
if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr);
_cmsFree(it8 ->ContextID, p);
}
}
if (it8->MemoryBlock)
_cmsFree(it8 ->ContextID, it8->MemoryBlock);
_cmsFree(it8 ->ContextID, it8);
}
static
void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size)
{
OWNEDMEM* ptr1;
void* ptr = _cmsMallocZero(it8->ContextID, size);
if (ptr != NULL) {
ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM));
if (ptr1 == NULL) {
_cmsFree(it8 ->ContextID, ptr);
return NULL;
}
ptr1-> Ptr = ptr;
ptr1-> Next = it8 -> MemorySink;
it8 -> MemorySink = ptr1;
}
return ptr;
}
static
void* AllocChunk(cmsIT8* it8, cmsUInt32Number size)
{
cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;
cmsUInt8Number* ptr;
size = _cmsALIGNMEM(size);
if (size == 0) return NULL;
if (size > Free) {
cmsUInt8Number* new_block;
if (it8 -> Allocator.BlockSize == 0)
it8 -> Allocator.BlockSize = 20*1024;
else
it8 ->Allocator.BlockSize *= 2;
if (it8 ->Allocator.BlockSize < size)
it8 ->Allocator.BlockSize = size;
it8 ->Allocator.Used = 0;
new_block = (cmsUInt8Number*)AllocBigBlock(it8, it8->Allocator.BlockSize);
if (new_block == NULL) goto Error;
it8->Allocator.Block = new_block;
}
if (it8->Allocator.Block == NULL)
goto Error;
ptr = it8 ->Allocator.Block + it8 ->Allocator.Used;
it8 ->Allocator.Used += size;
return (void*) ptr;
Error:
SynError(it8, "Allocation error");
it8->Allocator.BlockSize = 0;
it8->Allocator.Used = 0;
it8->Allocator.Block = NULL;
return NULL;
}
static
char *AllocString(cmsIT8* it8, const char* str)
{
cmsUInt32Number Size;
char *ptr;
if (str == NULL) return NULL;
Size = (cmsUInt32Number)strlen(str) + 1;
ptr = (char *) AllocChunk(it8, Size);
if (ptr) memcpy(ptr, str, Size-1);
return ptr;
}
static
cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr)
{
if (LastPtr) *LastPtr = p;
for (; p != NULL; p = p->Next) {
if (LastPtr) *LastPtr = p;
if (*Key != '#') {
if (cmsstrcasecmp(Key, p->Keyword) == 0)
break;
}
}
if (p == NULL)
return FALSE;
if (Subkey == 0)
return TRUE;
for (; p != NULL; p = p->NextSubkey) {
if (p ->Subkey == NULL) continue;
if (LastPtr) *LastPtr = p;
if (cmsstrcasecmp(Subkey, p->Subkey) == 0)
return TRUE;
}
return FALSE;
}
static
KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs)
{
KEYVALUE* p;
KEYVALUE* last;
if (IsAvailableOnList(*Head, Key, Subkey, &p)) {
if (cmsstrcasecmp(Key, "NUMBER_OF_FIELDS") == 0 ||
cmsstrcasecmp(Key, "NUMBER_OF_SETS") == 0) {
SynError(it8, "duplicate key <%s>", Key);
return NULL;
}
}
else {
last = p;
p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE));
if (p == NULL)
{
SynError(it8, "AddToList: out of memory");
return NULL;
}
p->Keyword = AllocString(it8, Key);
p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey);
if (*Head == NULL) {
*Head = p;
}
else
{
if (Subkey != NULL && last != NULL) {
last->NextSubkey = p;
while (last->Next != NULL)
last = last->Next;
}
if (last != NULL) last->Next = p;
}
p->Next = NULL;
p->NextSubkey = NULL;
}
p->WriteAs = WriteAs;
if (xValue != NULL) {
p->Value = AllocString(it8, xValue);
}
else {
p->Value = NULL;
}
return p;
}
static
KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as)
{
return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as);
}
static
KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key)
{
return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED);
}
static
cmsBool AllocTable(cmsIT8* it8)
{
TABLE* t;
if (it8->TablesCount >= (MAXTABLES-1))
return FALSE;
t = it8 ->Tab + it8 ->TablesCount;
t->HeaderList = NULL;
t->DataFormat = NULL;
t->Data = NULL;
it8 ->TablesCount++;
return TRUE;
}
cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable)
{
cmsIT8* it8 = (cmsIT8*) IT8;
if (nTable >= it8 ->TablesCount) {
if (nTable == it8 ->TablesCount) {
if (!AllocTable(it8)) {
SynError(it8, "Too many tables");
return -1;
}
}
else {
SynError(it8, "Table %d is out of sequence", nTable);
return -1;
}
}
it8 ->nTable = nTable;
return (cmsInt32Number) nTable;
}
cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
{
cmsIT8* it8;
cmsUInt32Number i;
it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8));
if (it8 == NULL) return NULL;
AllocTable(it8);
it8->MemoryBlock = NULL;
it8->MemorySink = NULL;
it8->IsCUBE = FALSE;
it8 ->nTable = 0;
it8->ContextID = ContextID;
it8->Allocator.Used = 0;
it8->Allocator.Block = NULL;
it8->Allocator.BlockSize = 0;
it8->ValidKeywords = NULL;
it8->ValidSampleID = NULL;
it8 -> sy = SUNDEFINED;
it8 -> ch = ' ';
it8 -> Source = NULL;
it8 -> inum = 0;
it8 -> dnum = 0.0;
it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
it8->IncludeSP = 0;
it8 -> lineno = 1;
it8->id = StringAlloc(it8, MAXSTR);
it8->str = StringAlloc(it8, MAXSTR);
strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");
for (i=0; i < NUMPREDEFINEDPROPS; i++)
AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as);
for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)
AddAvailableSampleID(it8, PredefinedSampleID[i]);
return (cmsHANDLE) it8;
}
const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8)
{
return GetTable((cmsIT8*) hIT8)->SheetType;
}
cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type)
{
TABLE* t = GetTable((cmsIT8*) hIT8);
strncpy(t ->SheetType, Type, MAXSTR-1);
t ->SheetType[MAXSTR-1] = 0;
return TRUE;
}
cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
if (!Val) return FALSE;
if (!*Val) return FALSE;
return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL;
}
cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
if (!Val) return FALSE;
if (!*Val) return FALSE;
return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL;
}
cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
char Buffer[1024];
snprintf(Buffer, 1023, it8->DoubleFormatter, Val);
return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL;
}
cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
char Buffer[1024];
snprintf(Buffer, 1023, "%u", Val);
return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL;
}
cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL;
}
cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL;
}
const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
KEYVALUE* p;
if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p))
{
return p -> Value;
}
return NULL;
}
cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp)
{
const char *v = cmsIT8GetProperty(hIT8, cProp);
if (v == NULL) return 0.0;
return ParseFloatNumber(v);
}
const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
KEYVALUE* p;
if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) {
return p -> Value;
}
return NULL;
}
static
cmsInt32Number satoi(const char* b)
{
int n;
if (b == NULL) return 0;
n = atoi(b);
if (n > 0x7ffffff0L) return 0x7ffffff0L;
if (n < -0x7ffffff0L) return -0x7ffffff0L;
return (cmsInt32Number)n;
}
static
cmsBool AllocateDataFormat(cmsIT8* it8)
{
cmsUInt32Number size;
TABLE* t = GetTable(it8);
if (t->DataFormat) return TRUE;
t->nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
if (t->nSamples <= 0 || t->nSamples > 0x7ffe) {
SynError(it8, "Wrong NUMBER_OF_FIELDS");
return FALSE;
}
size = ((cmsUInt32Number)t->nSamples + 1) * sizeof(char*);
t->DataFormat = (char**)AllocChunk(it8, size);
if (t->DataFormat == NULL) {
SynError(it8, "Unable to allocate dataFormat array");
return FALSE;
}
return TRUE;
}
static
const char *GetDataFormat(cmsIT8* it8, int n)
{
TABLE* t = GetTable(it8);
if (t->DataFormat)
return t->DataFormat[n];
return NULL;
}
static
cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label)
{
TABLE* t = GetTable(it8);
if (!t->DataFormat) {
if (!AllocateDataFormat(it8))
return FALSE;
}
if (n < 0 || n >= t -> nSamples) {
SynError(it8, "Invalid or more than NUMBER_OF_FIELDS fields.");
return FALSE;
}
if (t->DataFormat) {
t->DataFormat[n] = AllocString(it8, label);
if (t->DataFormat[n] == NULL) return FALSE;
}
return TRUE;
}
cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample)
{
cmsIT8* it8 = (cmsIT8*)h;
_cmsAssert(n >= 0);
return SetDataFormat(it8, n, Sample);
}
static
const char* satob(const char* v)
{
cmsUInt32Number x;
static char buf[33];
char *s = buf + 33;
if (v == NULL) return "0";
x = atoi(v);
*--s = 0;
if (!x) *--s = '0';
for (; x; x /= 2) *--s = '0' + x%2;
return s;
}
static
cmsBool AllocateDataSet(cmsIT8* it8)
{
TABLE* t = GetTable(it8);
if (t -> Data) return TRUE;
t-> nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
t-> nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe ||
(t->nPatches * t->nSamples) > 200000)
{
SynError(it8, "AllocateDataSet: too much data");
return FALSE;
}
else {
t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*));
if (t->Data == NULL) {
SynError(it8, "AllocateDataSet: Unable to allocate data array");
return FALSE;
}
}
return TRUE;
}
static
char* GetData(cmsIT8* it8, int nSet, int nField)
{
TABLE* t = GetTable(it8);
int nSamples = t -> nSamples;
int nPatches = t -> nPatches;
if (nSet < 0 || nSet >= nPatches || nField < 0 || nField >= nSamples)
return NULL;
if (!t->Data) return NULL;
return t->Data [nSet * nSamples + nField];
}
static
cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val)
{
char* ptr;
TABLE* t = GetTable(it8);
if (!t->Data) {
if (!AllocateDataSet(it8)) return FALSE;
}
if (!t->Data) return FALSE;
if (nSet > t -> nPatches || nSet < 0) {
return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches);
}
if (nField > t ->nSamples || nField < 0) {
return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples);
}
ptr = AllocString(it8, Val);
if (ptr == NULL)
return FALSE;
t->Data [nSet * t -> nSamples + nField] = ptr;
return TRUE;
}
static
void WriteStr(SAVESTREAM* f, const char *str)
{
cmsUInt32Number len;
if (str == NULL)
str = " ";
len = (cmsUInt32Number) strlen(str);
f ->Used += len;
if (f ->stream) {
if (fwrite(str, 1, len, f->stream) != len) {
cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser");
return;
}
}
else {
if (f ->Base) {
if (f ->Used > f ->Max) {
cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser");
return;
}
memmove(f ->Ptr, str, len);
f->Ptr += len;
}
}
}
static
void Writef(SAVESTREAM* f, const char* frm, ...)
{
char Buffer[4096];
va_list args;
va_start(args, frm);
vsnprintf(Buffer, 4095, frm, args);
Buffer[4095] = 0;
WriteStr(f, Buffer);
va_end(args);
}
static
void WriteHeader(cmsIT8* it8, SAVESTREAM* fp)
{
KEYVALUE* p;
TABLE* t = GetTable(it8);
WriteStr(fp, t->SheetType);
WriteStr(fp, "\n");
for (p = t->HeaderList; (p != NULL); p = p->Next)
{
if (*p ->Keyword == '#') {
char* Pt;
WriteStr(fp, "#\n# ");
for (Pt = p ->Value; *Pt; Pt++) {
Writef(fp, "%c", *Pt);
if (*Pt == '\n') {
WriteStr(fp, "# ");
}
}
WriteStr(fp, "\n#\n");
continue;
}
if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) {
#ifdef CMS_STRICT_CGATS
WriteStr(fp, "KEYWORD\t\"");
WriteStr(fp, p->Keyword);
WriteStr(fp, "\"\n");
#endif
AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED);
}
WriteStr(fp, p->Keyword);
if (p->Value) {
switch (p ->WriteAs) {
case WRITE_UNCOOKED:
Writef(fp, "\t%s", p ->Value);
break;
case WRITE_STRINGIFY:
Writef(fp, "\t\"%s\"", p->Value );
break;
case WRITE_HEXADECIMAL:
Writef(fp, "\t0x%X", satoi(p ->Value));
break;
case WRITE_BINARY:
Writef(fp, "\t0b%s", satob(p ->Value));
break;
case WRITE_PAIR:
Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value);
break;
default: SynError(it8, "Unknown write mode %d", p ->WriteAs);
return;
}
}
WriteStr (fp, "\n");
}
}
static
void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8)
{
int i, nSamples;
TABLE* t = GetTable(it8);
if (!t -> DataFormat) return;
WriteStr(fp, "BEGIN_DATA_FORMAT\n");
WriteStr(fp, " ");
nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
if (nSamples <= t->nSamples) {
for (i = 0; i < nSamples; i++) {
WriteStr(fp, t->DataFormat[i]);
WriteStr(fp, ((i == (nSamples - 1)) ? "\n" : "\t"));
}
}
WriteStr (fp, "END_DATA_FORMAT\n");
}
static
void WriteData(SAVESTREAM* fp, cmsIT8* it8)
{
int i, j, nPatches;
TABLE* t = GetTable(it8);
if (!t->Data) return;
WriteStr (fp, "BEGIN_DATA\n");
nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
if (nPatches <= t->nPatches) {
for (i = 0; i < nPatches; i++) {
WriteStr(fp, " ");
for (j = 0; j < t->nSamples; j++) {
char* ptr = t->Data[i * t->nSamples + j];
if (ptr == NULL) WriteStr(fp, "\"\"");
else {
if (strchr(ptr, ' ') != NULL) {
WriteStr(fp, "\"");
WriteStr(fp, ptr);
WriteStr(fp, "\"");
}
else
WriteStr(fp, ptr);
}
WriteStr(fp, ((j == (t->nSamples - 1)) ? "\n" : "\t"));
}
}
}
WriteStr (fp, "END_DATA\n");
}
cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName)
{
SAVESTREAM sd;
cmsUInt32Number i;
cmsIT8* it8 = (cmsIT8*) hIT8;
memset(&sd, 0, sizeof(sd));
sd.stream = fopen(cFileName, "wt");
if (!sd.stream) return FALSE;
for (i=0; i < it8 ->TablesCount; i++) {
TABLE* t;
if (cmsIT8SetTable(hIT8, i) < 0) goto Error;
t = GetTable(it8);
if (t->Data == NULL) goto Error;
if (t->DataFormat == NULL) goto Error;
WriteHeader(it8, &sd);
WriteDataFormat(&sd, it8);
WriteData(&sd, it8);
}
if (fclose(sd.stream) != 0) return FALSE;
return TRUE;
Error:
fclose(sd.stream);
return FALSE;
}
cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded)
{
SAVESTREAM sd;
cmsUInt32Number i;
cmsIT8* it8 = (cmsIT8*) hIT8;
memset(&sd, 0, sizeof(sd));
sd.stream = NULL;
sd.Base = (cmsUInt8Number*) MemPtr;
sd.Ptr = sd.Base;
sd.Used = 0;
if (sd.Base && (*BytesNeeded > 0)) {
sd.Max = (*BytesNeeded) - 1; }
else
sd.Max = 0;
for (i=0; i < it8 ->TablesCount; i++) {
cmsIT8SetTable(hIT8, i);
WriteHeader(it8, &sd);
WriteDataFormat(&sd, it8);
WriteData(&sd, it8);
}
sd.Used++;
if (sd.Base)
*sd.Ptr = 0;
*BytesNeeded = sd.Used;
return TRUE;
}
static
cmsBool DataFormatSection(cmsIT8* it8)
{
int iField = 0;
TABLE* t = GetTable(it8);
InSymbol(it8); CheckEOLN(it8);
while (it8->sy != SEND_DATA_FORMAT &&
it8->sy != SEOLN &&
it8->sy != SEOF &&
it8->sy != SSYNERROR) {
if (it8->sy != SIDENT) {
return SynError(it8, "Sample type expected");
}
if (!SetDataFormat(it8, iField, StringPtr(it8->id))) return FALSE;
iField++;
InSymbol(it8);
SkipEOLN(it8);
}
SkipEOLN(it8);
Skip(it8, SEND_DATA_FORMAT);
SkipEOLN(it8);
if (iField != t ->nSamples) {
SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField);
}
return TRUE;
}
static
cmsBool DataSection (cmsIT8* it8)
{
int iField = 0;
int iSet = 0;
char Buffer[256];
TABLE* t = GetTable(it8);
InSymbol(it8); CheckEOLN(it8);
if (!t->Data) {
if (!AllocateDataSet(it8)) return FALSE;
}
while (it8->sy != SEND_DATA && it8->sy != SEOF && it8->sy != SSYNERROR)
{
if (iField >= t -> nSamples) {
iField = 0;
iSet++;
}
if (it8->sy != SEND_DATA && it8->sy != SEOF && it8->sy != SSYNERROR) {
switch (it8->sy)
{
case SIDENT:
if (!SetData(it8, iSet, iField, StringPtr(it8->id)))
return FALSE;
break;
case SSTRING:
if (!SetData(it8, iSet, iField, StringPtr(it8->str)))
return FALSE;
break;
default:
if (!GetVal(it8, Buffer, 255, "Sample data expected"))
return FALSE;
if (!SetData(it8, iSet, iField, Buffer))
return FALSE;
}
iField++;
InSymbol(it8);
SkipEOLN(it8);
}
}
SkipEOLN(it8);
Skip(it8, SEND_DATA);
SkipEOLN(it8);
if ((iSet+1) != t -> nPatches)
return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1);
return TRUE;
}
static
cmsBool HeaderSection(cmsIT8* it8)
{
char VarName[MAXID];
char Buffer[MAXSTR];
KEYVALUE* Key;
while (it8->sy != SEOF &&
it8->sy != SSYNERROR &&
it8->sy != SBEGIN_DATA_FORMAT &&
it8->sy != SBEGIN_DATA) {
switch (it8 -> sy) {
case SKEYWORD:
InSymbol(it8);
if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE;
InSymbol(it8);
break;
case SDATA_FORMAT_ID:
InSymbol(it8);
if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
if (!AddAvailableSampleID(it8, Buffer)) return FALSE;
InSymbol(it8);
break;
case SIDENT:
strncpy(VarName, StringPtr(it8->id), MAXID - 1);
VarName[MAXID - 1] = 0;
if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) {
#ifdef CMS_STRICT_CGATS
return SynError(it8, "Undefined keyword '%s'", VarName);
#else
Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED);
if (Key == NULL) return FALSE;
#endif
}
InSymbol(it8);
if (!GetVal(it8, Buffer, MAXSTR - 1, "Property data expected")) return FALSE;
if (Key->WriteAs != WRITE_PAIR) {
if (AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer,
(it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED) == NULL) return FALSE;
}
else {
const char *Subkey;
char *Nextkey;
if (it8->sy != SSTRING)
return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName);
for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey)
{
char *Value, *temp;
Nextkey = (char*)strchr(Subkey, ';');
if (Nextkey)
*Nextkey++ = '\0';
Value = (char*)strrchr(Subkey, ',');
if (Value == NULL)
return SynError(it8, "Invalid value for property '%s'.", VarName);
temp = Value++;
do *temp-- = '\0'; while (temp >= Subkey && *temp == ' ');
temp = Value + strlen(Value) - 1;
while (*temp == ' ') *temp-- = '\0';
Subkey += strspn(Subkey, " ");
Value += strspn(Value, " ");
if (Subkey[0] == 0 || Value[0] == 0)
return SynError(it8, "Invalid value for property '%s'.", VarName);
AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR);
}
}
InSymbol(it8);
break;
case SEOLN: break;
default:
return SynError(it8, "expected keyword or identifier");
}
SkipEOLN(it8);
}
return TRUE;
}
static
void ReadType(cmsIT8* it8, char* SheetTypePtr)
{
cmsInt32Number cnt = 0;
while (isseparator(it8->ch))
NextCh(it8);
while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != 0) {
if (cnt++ < MAXSTR)
*SheetTypePtr++= (char) it8 ->ch;
NextCh(it8);
}
*SheetTypePtr = 0;
}
static
cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
{
char* SheetTypePtr = it8 ->Tab[0].SheetType;
if (nosheet == 0) {
ReadType(it8, SheetTypePtr);
}
InSymbol(it8);
SkipEOLN(it8);
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;
if (it8 -> sy != SEOF && it8->sy != SSYNERROR) {
if (!AllocTable(it8)) return FALSE;
it8 ->nTable = it8 ->TablesCount - 1;
if (nosheet == 0) {
if (it8 ->sy == SIDENT) {
while (isseparator(it8->ch))
NextCh(it8);
if (it8 ->ch == '\n' || it8->ch == '\r') {
cmsIT8SetSheetType(it8, StringPtr(it8 ->id));
InSymbol(it8);
}
else
{
cmsIT8SetSheetType(it8, "");
}
}
else
if (it8 ->sy == SSTRING) {
cmsIT8SetSheetType(it8, StringPtr(it8 ->str));
InSymbol(it8);
}
}
}
break;
case SEOLN:
SkipEOLN(it8);
break;
default:
if (!HeaderSection(it8)) return FALSE;
}
}
return (it8 -> sy != SSYNERROR);
}
static
void CookPointers(cmsIT8* it8)
{
int idField, i;
char* Fld;
cmsUInt32Number j;
cmsUInt32Number nOldTable = it8->nTable;
for (j = 0; j < it8->TablesCount; j++) {
TABLE* t = it8->Tab + j;
t->SampleID = 0;
it8->nTable = j;
for (idField = 0; idField < t->nSamples; idField++)
{
if (t->DataFormat == NULL) {
SynError(it8, "Undefined DATA_FORMAT");
return;
}
Fld = t->DataFormat[idField];
if (!Fld) continue;
if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {
t->SampleID = idField;
}
if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') {
for (i = 0; i < t->nPatches; i++) {
char* Label = GetData(it8, i, idField);
if (Label) {
cmsUInt32Number k;
for (k = 0; k < it8->TablesCount; k++) {
TABLE* Table = it8->Tab + k;
KEYVALUE* p;
if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
char Buffer[256];
char* Type = p->Value;
int nTable = (int)k;
snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type);
SetData(it8, i, idField, Buffer);
}
}
}
}
}
}
}
it8->nTable = nOldTable;
}
static
int IsMyBlock(const cmsUInt8Number* Buffer, cmsUInt32Number n)
{
int words = 1, space = 0, quot = 0;
cmsUInt32Number i;
if (n < 10) return 0;
if (n > 132)
n = 132;
for (i = 1; i < n; i++) {
switch(Buffer[i])
{
case '\n':
case '\r':
return ((quot == 1) || (words > 2)) ? 0 : words;
case '\t':
case ' ':
if(!quot && !space)
space = 1;
break;
case '\"':
quot = !quot;
break;
default:
if (Buffer[i] < 32) return 0;
if (Buffer[i] > 127) return 0;
words += space;
space = 0;
break;
}
}
return 0;
}
static
cmsBool IsMyFile(const char* FileName)
{
FILE *fp;
cmsUInt32Number Size;
cmsUInt8Number Ptr[133];
fp = fopen(FileName, "rt");
if (!fp) {
cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName);
return FALSE;
}
Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp);
if (fclose(fp) != 0)
return FALSE;
Ptr[Size] = '\0';
return IsMyBlock(Ptr, Size);
}
cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len)
{
cmsHANDLE hIT8;
cmsIT8* it8;
int type;
_cmsAssert(Ptr != NULL);
_cmsAssert(len != 0);
type = IsMyBlock((const cmsUInt8Number*)Ptr, len);
if (type == 0) return NULL;
hIT8 = cmsIT8Alloc(ContextID);
if (!hIT8) return NULL;
it8 = (cmsIT8*) hIT8;
it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1);
if (it8->MemoryBlock == NULL)
{
cmsIT8Free(hIT8);
return NULL;
}
strncpy(it8 ->MemoryBlock, (const char*) Ptr, len);
it8 ->MemoryBlock[len] = 0;
strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1);
it8-> Source = it8 -> MemoryBlock;
if (!ParseIT8(it8, type-1)) {
cmsIT8Free(hIT8);
return NULL;
}
CookPointers(it8);
it8 ->nTable = 0;
_cmsFree(ContextID, it8->MemoryBlock);
it8 -> MemoryBlock = NULL;
return hIT8;
}
cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName)
{
cmsHANDLE hIT8;
cmsIT8* it8;
int type;
_cmsAssert(cFileName != NULL);
type = IsMyFile(cFileName);
if (type == 0) return NULL;
hIT8 = cmsIT8Alloc(ContextID);
it8 = (cmsIT8*) hIT8;
if (!hIT8) return NULL;
it8 ->FileStack[0]->Stream = fopen(cFileName, "rt");
if (!it8 ->FileStack[0]->Stream) {
cmsIT8Free(hIT8);
return NULL;
}
strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1);
it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0;
if (!ParseIT8(it8, type-1)) {
fclose(it8 ->FileStack[0]->Stream);
cmsIT8Free(hIT8);
return NULL;
}
CookPointers(it8);
it8 ->nTable = 0;
if (fclose(it8 ->FileStack[0]->Stream)!= 0) {
cmsIT8Free(hIT8);
return NULL;
}
return hIT8;
}
int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
TABLE* t;
_cmsAssert(hIT8 != NULL);
t = GetTable(it8);
if (SampleNames)
*SampleNames = t -> DataFormat;
return t -> nSamples;
}
cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
KEYVALUE* p;
cmsUInt32Number n;
char **Props;
TABLE* t;
_cmsAssert(hIT8 != NULL);
t = GetTable(it8);
n = 0;
for (p = t -> HeaderList; p != NULL; p = p->Next) {
n++;
}
Props = (char**)AllocChunk(it8, sizeof(char*) * n);
if (Props != NULL) {
n = 0;
for (p = t->HeaderList; p != NULL; p = p->Next) {
Props[n++] = p->Keyword;
}
}
*PropertyNames = Props;
return n;
}
cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
KEYVALUE *p, *tmp;
cmsUInt32Number n;
const char **Props;
TABLE* t;
_cmsAssert(hIT8 != NULL);
t = GetTable(it8);
if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) {
*SubpropertyNames = 0;
return 0;
}
n = 0;
for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
if(tmp->Subkey != NULL)
n++;
}
Props = (const char **) AllocChunk(it8, sizeof(char *) * n);
if (Props != NULL) {
n = 0;
for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
if (tmp->Subkey != NULL)
Props[n++] = p->Subkey;
}
}
*SubpropertyNames = Props;
return n;
}
static
int LocatePatch(cmsIT8* it8, const char* cPatch)
{
int i;
const char *data;
TABLE* t = GetTable(it8);
for (i=0; i < t-> nPatches; i++) {
data = GetData(it8, i, t->SampleID);
if (data != NULL) {
if (cmsstrcasecmp(data, cPatch) == 0)
return i;
}
}
return -1;
}
static
int LocateEmptyPatch(cmsIT8* it8)
{
int i;
const char *data;
TABLE* t = GetTable(it8);
for (i=0; i < t-> nPatches; i++) {
data = GetData(it8, i, t->SampleID);
if (data == NULL)
return i;
}
return -1;
}
static
int LocateSample(cmsIT8* it8, const char* cSample)
{
int i;
const char *fld;
TABLE* t = GetTable(it8);
for (i=0; i < t->nSamples; i++) {
fld = GetDataFormat(it8, i);
if (fld != NULL) {
if (cmsstrcasecmp(fld, cSample) == 0)
return i;
}
}
return -1;
}
int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL);
return LocateSample(it8, cSample);
}
const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL);
return GetData(it8, row, col);
}
cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col)
{
const char* Buffer;
Buffer = cmsIT8GetDataRowCol(hIT8, row, col);
if (Buffer == NULL) return 0.0;
return ParseFloatNumber(Buffer);
}
cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL);
return SetData(it8, row, col, Val);
}
cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
char Buff[256];
_cmsAssert(hIT8 != NULL);
snprintf(Buff, 255, it8->DoubleFormatter, Val);
return SetData(it8, row, col, Buff);
}
const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
int iField, iSet;
_cmsAssert(hIT8 != NULL);
iField = LocateSample(it8, cSample);
if (iField < 0) {
return NULL;
}
iSet = LocatePatch(it8, cPatch);
if (iSet < 0) {
return NULL;
}
return GetData(it8, iSet, iField);
}
cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch, const char* cSample)
{
const char* Buffer;
Buffer = cmsIT8GetData(it8, cPatch, cSample);
return ParseFloatNumber(Buffer);
}
cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
int iField, iSet;
TABLE* t;
_cmsAssert(hIT8 != NULL);
t = GetTable(it8);
iField = LocateSample(it8, cSample);
if (iField < 0)
return FALSE;
if (t-> nPatches == 0) {
if (!AllocateDataFormat(it8))
return FALSE;
if (!AllocateDataSet(it8))
return FALSE;
CookPointers(it8);
}
if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) {
iSet = LocateEmptyPatch(it8);
if (iSet < 0) {
return SynError(it8, "Couldn't add more patches '%s'\n", cPatch);
}
iField = t -> SampleID;
}
else {
iSet = LocatePatch(it8, cPatch);
if (iSet < 0) {
return FALSE;
}
}
return SetData(it8, iSet, iField, Val);
}
cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch,
const char* cSample,
cmsFloat64Number Val)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
char Buff[256];
_cmsAssert(hIT8 != NULL);
snprintf(Buff, 255, it8->DoubleFormatter, Val);
return cmsIT8SetData(hIT8, cPatch, cSample, Buff);
}
const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
TABLE* t;
char* Data;
_cmsAssert(hIT8 != NULL);
t = GetTable(it8);
Data = GetData(it8, nPatch, t->SampleID);
if (!Data) return NULL;
if (!buffer) return Data;
strncpy(buffer, Data, MAXSTR-1);
buffer[MAXSTR-1] = 0;
return buffer;
}
int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch)
{
_cmsAssert(hIT8 != NULL);
return LocatePatch((cmsIT8*)hIT8, cPatch);
}
cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL);
return it8 ->TablesCount;
}
int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType)
{
const char* cLabelFld;
char Type[256], Label[256];
cmsUInt32Number nTable;
_cmsAssert(hIT8 != NULL);
if (cField != NULL && *cField == 0)
cField = "LABEL";
if (cField == NULL)
cField = "LABEL";
cLabelFld = cmsIT8GetData(hIT8, cSet, cField);
if (!cLabelFld) return -1;
if (sscanf(cLabelFld, "%255s %u %255s", Label, &nTable, Type) != 3)
return -1;
if (ExpectedType != NULL && *ExpectedType == 0)
ExpectedType = NULL;
if (ExpectedType) {
if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1;
}
return cmsIT8SetTable(hIT8, nTable);
}
cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
int pos;
_cmsAssert(hIT8 != NULL);
pos = LocateSample(it8, cSample);
if(pos == -1)
return FALSE;
it8->Tab[it8->nTable].SampleID = pos;
return TRUE;
}
void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter)
{
cmsIT8* it8 = (cmsIT8*) hIT8;
_cmsAssert(hIT8 != NULL);
if (Formatter == NULL)
strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
else
strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter));
it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0;
}
static
cmsBool ReadNumbers(cmsIT8* cube, int n, cmsFloat64Number* arr)
{
int i;
for (i = 0; i < n; i++) {
if (cube->sy == SINUM)
arr[i] = cube->inum;
else
if (cube->sy == SDNUM)
arr[i] = cube->dnum;
else
return SynError(cube, "Number expected");
InSymbol(cube);
}
return CheckEOLN(cube);
}
static
cmsBool ParseCube(cmsIT8* cube, cmsStage** Shaper, cmsStage** CLUT, char title[])
{
cmsFloat64Number domain_min[3] = { 0, 0, 0 };
cmsFloat64Number domain_max[3] = { 1.0, 1.0, 1.0 };
cmsFloat64Number check_0_1[2] = { 0, 1.0 };
int shaper_size = 0;
int lut_size = 0;
int i;
InSymbol(cube);
while (cube->sy != SEOF && cube->sy != SSYNERROR) {
switch (cube->sy)
{
case STITLE:
InSymbol(cube);
if (!Check(cube, SSTRING, "Title string expected")) return FALSE;
memcpy(title, StringPtr(cube->str), MAXSTR);
title[MAXSTR - 1] = 0;
InSymbol(cube);
break;
case SDOMAIN_MIN:
InSymbol(cube);
if (!ReadNumbers(cube, 3, domain_min)) return FALSE;
break;
case SDOMAIN_MAX:
InSymbol(cube);
if (!ReadNumbers(cube, 3, domain_max)) return FALSE;
break;
case S_LUT1D_SIZE:
InSymbol(cube);
if (!Check(cube, SINUM, "Shaper size expected")) return FALSE;
shaper_size = cube->inum;
if (shaper_size < 2 || shaper_size > 65536)
return SynError(cube, "LUT_1D_SIZE '%d' is out of bounds", shaper_size);
InSymbol(cube);
break;
case S_LUT3D_SIZE:
InSymbol(cube);
if (!Check(cube, SINUM, "LUT size expected")) return FALSE;
lut_size = cube->inum;
InSymbol(cube);
break;
case S_LUT1D_INPUT_RANGE:
case S_LUT3D_INPUT_RANGE:
InSymbol(cube);
if (!ReadNumbers(cube, 2, check_0_1)) return FALSE;
if (check_0_1[0] != 0 || check_0_1[1] != 1.0) {
return SynError(cube, "Unsupported format");
}
break;
case SEOLN:
InSymbol(cube);
break;
default:
case S_LUT_IN_VIDEO_RANGE:
case S_LUT_OUT_VIDEO_RANGE:
return SynError(cube, "Unsupported format");
case SINUM:
case SDNUM:
if (shaper_size > 0) {
cmsToneCurve* curves[3];
cmsFloat32Number* shapers = (cmsFloat32Number*)_cmsMalloc(cube->ContextID, 3 * shaper_size * sizeof(cmsFloat32Number));
if (shapers == NULL) return FALSE;
for (i = 0; i < shaper_size; i++) {
cmsFloat64Number nums[3];
if (!ReadNumbers(cube, 3, nums)) return FALSE;
shapers[i + 0] = (cmsFloat32Number) ((nums[0] - domain_min[0]) / (domain_max[0] - domain_min[0]));
shapers[i + 1 * shaper_size] = (cmsFloat32Number) ((nums[1] - domain_min[1]) / (domain_max[1] - domain_min[1]));
shapers[i + 2 * shaper_size] = (cmsFloat32Number) ((nums[2] - domain_min[2]) / (domain_max[2] - domain_min[2]));
}
for (i = 0; i < 3; i++) {
curves[i] = cmsBuildTabulatedToneCurveFloat(cube->ContextID, shaper_size,
&shapers[i * shaper_size]);
if (curves[i] == NULL) return FALSE;
}
*Shaper = cmsStageAllocToneCurves(cube->ContextID, 3, curves);
cmsFreeToneCurveTriple(curves);
}
if (lut_size > 0) {
int nodes;
if (lut_size < 2 || lut_size > 65)
return SynError(cube, "LUT size '%d' is not allowed", lut_size);
nodes = lut_size * lut_size * lut_size;
cmsFloat32Number* lut_table = (cmsFloat32Number*) _cmsMalloc(cube->ContextID, nodes * 3 * sizeof(cmsFloat32Number));
if (lut_table == NULL) return FALSE;
for (i = 0; i < nodes; i++) {
cmsFloat64Number nums[3];
if (!ReadNumbers(cube, 3, nums)) return FALSE;
lut_table[i * 3 + 2] = (cmsFloat32Number) ((nums[0] - domain_min[0]) / (domain_max[0] - domain_min[0]));
lut_table[i * 3 + 1] = (cmsFloat32Number) ((nums[1] - domain_min[1]) / (domain_max[1] - domain_min[1]));
lut_table[i * 3 + 0] = (cmsFloat32Number) ((nums[2] - domain_min[2]) / (domain_max[2] - domain_min[2]));
}
*CLUT = cmsStageAllocCLutFloat(cube->ContextID, lut_size, 3, 3, lut_table);
_cmsFree(cube->ContextID, lut_table);
}
if (!Check(cube, SEOF, "Extra symbols found in file")) return FALSE;
}
}
return TRUE;
}
cmsHPROFILE CMSEXPORT cmsCreateDeviceLinkFromCubeFileTHR(cmsContext ContextID, const char* cFileName)
{
cmsHPROFILE hProfile = NULL;
cmsIT8* cube = NULL;
cmsPipeline* Pipeline = NULL;
cmsStage* CLUT = NULL;
cmsStage* Shaper = NULL;
cmsMLU* DescriptionMLU = NULL;
char title[MAXSTR];
_cmsAssert(cFileName != NULL);
cube = (cmsIT8*) cmsIT8Alloc(ContextID);
if (!cube) return NULL;
cube->IsCUBE = TRUE;
cube->FileStack[0]->Stream = fopen(cFileName, "rt");
if (!cube->FileStack[0]->Stream) goto Done;
strncpy(cube->FileStack[0]->FileName, cFileName, cmsMAX_PATH - 1);
cube->FileStack[0]->FileName[cmsMAX_PATH - 1] = 0;
if (!ParseCube(cube, &Shaper, &CLUT, title)) goto Done;
hProfile = cmsCreateProfilePlaceholder(ContextID);
if (!hProfile) goto Done;
cmsSetProfileVersion(hProfile, 4.4);
cmsSetDeviceClass(hProfile, cmsSigLinkClass);
cmsSetColorSpace(hProfile, cmsSigRgbData);
cmsSetPCS(hProfile, cmsSigRgbData);
cmsSetHeaderRenderingIntent(hProfile, INTENT_PERCEPTUAL);
Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
if (Pipeline == NULL) goto Done;
if (Shaper != NULL) {
if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, Shaper)) {
cmsStageFree(Shaper);
goto Done;
}
}
if (CLUT != NULL) {
if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT)) {
cmsStageFree(CLUT);
goto Done;
}
}
DescriptionMLU = cmsMLUalloc(ContextID, 1);
if (!cmsMLUsetUTF8(DescriptionMLU, cmsNoLanguage, cmsNoCountry, title)) goto Done;
if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Done;
if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, (void*)Pipeline)) goto Done;
Done:
if (DescriptionMLU != NULL)
cmsMLUfree(DescriptionMLU);
if (Pipeline != NULL)
cmsPipelineFree(Pipeline);
cmsIT8Free((cmsHANDLE) cube);
return hProfile;
}
cmsHPROFILE CMSEXPORT cmsCreateDeviceLinkFromCubeFile(const char* cFileName)
{
return cmsCreateDeviceLinkFromCubeFileTHR(NULL, cFileName);
}