1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2011 Marti Maria Saguer
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 //---------------------------------------------------------------------------------
27 #include "lcms2_internal.h"
30 // IT8.7 / CGATS.17-200x handling -----------------------------------------------------------------------------
33 #define MAXID 128 // Max lenght of identifier
34 #define MAXSTR 1024 // Max lenght of string
35 #define MAXTABLES 255 // Max Number of tables in a single stream
36 #define MAXINCLUDE 20 // Max number of nested includes
38 #define DEFAULT_DBL_FORMAT "%.10g" // Double formatting
40 #ifdef CMS_IS_WINDOWS_
42 # define DIR_CHAR '\\'
57 SEOF, // End of stream
58 SSYNERROR, // Syntax error found on stream
73 // How to write the value
84 // Linked list of variable names
85 typedef struct _KeyVal {
88 char* Keyword; // Name of variable
89 struct _KeyVal* NextSubkey; // If key is a dictionary, points to the next item
90 char* Subkey; // If key is a dictionary, points to the subkey name
91 char* Value; // Points to value
92 WRITEMODE WriteAs; // How to write the value
97 // Linked list of memory chunks (Memory sink)
98 typedef struct _OwnedMem {
100 struct _OwnedMem* Next;
101 void * Ptr; // Point to value
106 typedef struct _SubAllocator {
108 cmsUInt8Number* Block;
109 cmsUInt32Number BlockSize;
110 cmsUInt32Number Used;
114 // Table. Each individual table can hold properties and rows & cols
115 typedef struct _Table {
117 char SheetType[MAXSTR]; // The first row of the IT8 (the type)
119 int nSamples, nPatches; // Cols, Rows
120 int SampleID; // Pos of ID
122 KEYVALUE* HeaderList; // The properties
124 char** DataFormat; // The binary stream descriptor
125 char** Data; // The binary stream
129 // File stream being parsed
130 typedef struct _FileContext {
131 char FileName[cmsMAX_PATH]; // File name if being readed from file
132 FILE* Stream; // File stream or NULL if holded in memory
135 // This struct hold all information about an open IT8 handler.
139 cmsUInt32Number TablesCount; // How many tables in this stream
140 cmsUInt32Number nTable; // The actual table
142 TABLE Tab[MAXTABLES];
145 OWNEDMEM* MemorySink; // The storage backend
146 SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast
148 // Parser state machine
149 SYMBOL sy; // Current symbol
150 int ch; // Current character
152 int inum; // integer value
153 cmsFloat64Number dnum; // real value
154 char id[MAXID]; // identifier
155 char str[MAXSTR]; // string
157 // Allowed keywords & datasets. They have visibility on whole stream
158 KEYVALUE* ValidKeywords;
159 KEYVALUE* ValidSampleID;
161 char* Source; // Points to loc. being parsed
162 int lineno; // line counter for error reporting
164 FILECTX* FileStack[MAXINCLUDE]; // Stack of files being parsed
165 int IncludeSP; // Include Stack Pointer
167 char* MemoryBlock; // The stream if holded in memory
169 char DoubleFormatter[MAXID];// Printf-like 'cmsFloat64Number' formatter
171 cmsContext ContextID; // The threading context
176 // The stream for save operations
179 FILE* stream; // For save-to-file behaviour
181 cmsUInt8Number* Base;
182 cmsUInt8Number* Ptr; // For save-to-mem behaviour
183 cmsUInt32Number Used;
189 // ------------------------------------------------------ cmsIT8 parsing routines
200 // The keyword->symbol translation table. Sorting is required.
201 static const KEYWORD TabKeys[] = {
203 {"$INCLUDE", SINCLUDE}, // This is an extension!
204 {".INCLUDE", SINCLUDE}, // This is an extension!
206 {"BEGIN_DATA", SBEGIN_DATA },
207 {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT },
208 {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID},
209 {"END_DATA", SEND_DATA},
210 {"END_DATA_FORMAT", SEND_DATA_FORMAT},
211 {"KEYWORD", SKEYWORD}
214 #define NUMKEYS (sizeof(TabKeys)/sizeof(KEYWORD))
216 // Predefined properties
220 const char *id; // The identifier
221 WRITEMODE as; // How is supposed to be written
224 static PROPERTY PredefinedProperties[] = {
226 {"NUMBER_OF_FIELDS", WRITE_UNCOOKED}, // Required - NUMBER OF FIELDS
227 {"NUMBER_OF_SETS", WRITE_UNCOOKED}, // Required - NUMBER OF SETS
228 {"ORIGINATOR", WRITE_STRINGIFY}, // Required - Identifies the specific system, organization or individual that created the data file.
229 {"FILE_DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file.
230 {"CREATED", WRITE_STRINGIFY}, // Required - Indicates date of creation of the data file.
231 {"DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file.
232 {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY}, // The diffuse geometry used. Allowed values are "sphere" or "opal".
233 {"MANUFACTURER", WRITE_STRINGIFY},
234 {"MANUFACTURE", WRITE_STRINGIFY}, // Some broken Fuji targets does store this value
235 {"PROD_DATE", WRITE_STRINGIFY}, // Identifies year and month of production of the target in the form yyyy:mm.
236 {"SERIAL", WRITE_STRINGIFY}, // Uniquely identifies individual physical target.
238 {"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code
239 // uniquely identifying th e material. This is intend ed to be used for IT8.7
240 // physical targets only (i.e . IT8.7/1 a nd IT8.7/2).
242 {"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and
243 // model number) to generate the data reported. This data will often
244 // provide more information about the particular data collected than an
245 // extensive list of specific details. This is particularly important for
246 // spectral data or data derived from spectrophotometry.
248 {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide
249 // a guide to the potential for issues of paper fluorescence, etc.
251 {"PRINT_CONDITIONS", WRITE_STRINGIFY}, // Used to define the characteristics of the printed sheet being reported.
252 // Where standard conditions have been defined (e.g., SWOP at nominal)
253 // named conditions may suffice. Otherwise, detailed information is
256 {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during
257 // measurement. Allowed values are
\93black
\94,
\93white
\94, or {"na".
259 {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic
261 // below properties are new in recent specs:
263 {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated
264 // along with details of the geometry and the aperture size and shape. For example,
265 // for transmission measurements it is important to identify 0/diffuse, diffuse/0,
266 // opal or integrating sphere, etc. For reflection it is important to identify 0/45,
267 // 45/0, sphere (specular included or excluded), etc.
269 {"FILTER", WRITE_STRINGIFY}, // Identifies the use of physical filter(s) during measurement. Typically used to
270 // denote the use of filters such as none, D65, Red, Green or Blue.
272 {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed
273 // values are {"yes
\94,
\93white
\94,
\93none
\94 or
\93na
\94.
275 {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the
276 // calculation of various data parameters (2 degree and 10 degree), CIE standard
277 // illuminant functions used in the calculation of various data parameters (e.g., D50,
278 // D65, etc.), density status response, etc. If used there shall be at least one
279 // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute
280 // in the set shall be {"name" and shall identify the particular parameter used.
281 // The second shall be {"value" and shall provide the value associated with that name.
282 // For ASCII data, a string containing the Name and Value attribute pairs shall follow
283 // the weighting function keyword. A semi-colon separates attribute pairs from each
284 // other and within the attribute the name and value are separated by a comma.
286 {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name
287 // of the calculation, parameter is the name of the parameter used in the calculation
288 // and value is the value of the parameter.
290 {"TARGET_TYPE", WRITE_STRINGIFY}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc.
292 {"COLORANT", WRITE_STRINGIFY}, // Identifies the colorant(s) used in creating the target.
294 {"TABLE_DESCRIPTOR", WRITE_STRINGIFY}, // Describes the purpose or contents of a data table.
296 {"TABLE_NAME", WRITE_STRINGIFY} // Provides a short name for a data table.
299 #define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY))
302 // Predefined sample types on dataset
303 static const char* PredefinedSampleID[] = {
304 "SAMPLE_ID", // Identifies sample that data represents
305 "STRING", // Identifies label, or other non-machine readable value.
306 // Value must begin and end with a " symbol
308 "CMYK_C", // Cyan component of CMYK data expressed as a percentage
309 "CMYK_M", // Magenta component of CMYK data expressed as a percentage
310 "CMYK_Y", // Yellow component of CMYK data expressed as a percentage
311 "CMYK_K", // Black component of CMYK data expressed as a percentage
312 "D_RED", // Red filter density
313 "D_GREEN", // Green filter density
314 "D_BLUE", // Blue filter density
315 "D_VIS", // Visual filter density
316 "D_MAJOR_FILTER", // Major filter d ensity
317 "RGB_R", // Red component of RGB data
318 "RGB_G", // Green component of RGB data
319 "RGB_B", // Blue com ponent of RGB data
320 "SPECTRAL_NM", // Wavelength of measurement expressed in nanometers
321 "SPECTRAL_PCT", // Percentage reflectance/transmittance
322 "SPECTRAL_DEC", // Reflectance/transmittance
323 "XYZ_X", // X component of tristimulus data
324 "XYZ_Y", // Y component of tristimulus data
325 "XYZ_Z", // Z component of tristimulus data
326 "XYY_X" // x component of chromaticity data
327 "XYY_Y", // y component of chromaticity data
328 "XYY_CAPY", // Y component of tristimulus data
329 "LAB_L", // L* component of Lab data
330 "LAB_A", // a* component of Lab data
331 "LAB_B", // b* component of Lab data
332 "LAB_C", // C*ab component of Lab data
333 "LAB_H", // hab component of Lab data
335 "LAB_DE_94", // CIE dE using CIE 94
336 "LAB_DE_CMC", // dE using CMC
337 "LAB_DE_2000", // CIE dE using CIE DE 2000
338 "MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average
339 // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets)
340 "STDEV_X", // Standard deviation of X (tristimulus data)
341 "STDEV_Y", // Standard deviation of Y (tristimulus data)
342 "STDEV_Z", // Standard deviation of Z (tristimulus data)
343 "STDEV_L", // Standard deviation of L*
344 "STDEV_A", // Standard deviation of a*
345 "STDEV_B", // Standard deviation of b*
346 "STDEV_DE", // Standard deviation of CIE dE
347 "CHI_SQD_PAR"}; // The average of the standard deviations of L*, a* and b*. It is
348 // used to derive an estimate of the chi-squared parameter which is
349 // recommended as the predictor of the variability of dE
351 #define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
353 //Forward declaration of some internal functions
354 static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size);
356 // Checks if c is a separator
358 cmsBool isseparator(int c)
360 return (c == ' ') || (c == '\t') || (c == '\r');
363 // Checks whatever if c is a valid identifier char
365 cmsBool ismiddle(int c)
367 return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127));
370 // Checks whatsever if c is a valid identifier middle char.
372 cmsBool isidchar(int c)
374 return isalnum(c) || ismiddle(c);
377 // Checks whatsever if c is a valid identifier first char.
379 cmsBool isfirstidchar(int c)
381 return !isdigit(c) && ismiddle(c);
384 // Guess whether the supplied path looks like an absolute path
386 cmsBool isabsolutepath(const char *path)
395 strncpy(ThreeChars, path, 3);
398 if(ThreeChars[0] == DIR_CHAR)
401 #ifdef CMS_IS_WINDOWS_
402 if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':')
408 // Makes a file path based on a given reference path
409 // NOTE: this function doesn't check if the path exists or even if it's legal
411 cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen)
417 if (isabsolutepath(relPath)) {
419 strncpy(buffer, relPath, MaxLen);
420 buffer[MaxLen-1] = 0;
424 // No, search for last
425 strncpy(buffer, basePath, MaxLen);
426 buffer[MaxLen-1] = 0;
428 tail = strrchr(buffer, DIR_CHAR);
429 if (tail == NULL) return FALSE; // Is not absolute and has no separators??
431 len = (cmsUInt32Number) (tail - buffer);
432 if (len >= MaxLen) return FALSE;
434 // No need to assure zero terminator over here
435 strncpy(tail + 1, relPath, MaxLen - len);
441 // Make sure no exploit is being even tried
443 const char* NoMeta(const char* str)
445 if (strchr(str, '%') != NULL)
446 return "**** CORRUPTED FORMAT STRING ***";
453 cmsBool SynError(cmsIT8* it8, const char *Txt, ...)
455 char Buffer[256], ErrMsg[1024];
459 vsnprintf(Buffer, 255, Txt, args);
463 snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer);
466 cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg);
470 // Check if current symbol is same as specified. issue an error else.
472 cmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err)
475 return SynError(it8, NoMeta(Err));
479 // Read Next character from stream
481 void NextCh(cmsIT8* it8)
483 if (it8 -> FileStack[it8 ->IncludeSP]->Stream) {
485 it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream);
487 if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream)) {
489 if (it8 ->IncludeSP > 0) {
491 fclose(it8 ->FileStack[it8->IncludeSP--]->Stream);
492 it8 -> ch = ' '; // Whitespace to be ignored
499 it8->ch = *it8->Source;
500 if (it8->ch) it8->Source++;
505 // Try to see if current identifier is a keyword, if so return the referred symbol
507 SYMBOL BinSrchKey(const char *id)
516 res = cmsstrcasecmp(id, TabKeys[x-1].id);
517 if (res == 0) return TabKeys[x-1].sy;
518 if (res < 0) r = x - 1;
528 cmsFloat64Number xpow10(int n)
530 return pow(10, (cmsFloat64Number) n);
534 // Reads a Real number, tries to follow from integer number
536 void ReadReal(cmsIT8* it8, int inum)
538 it8->dnum = (cmsFloat64Number) inum;
540 while (isdigit(it8->ch)) {
542 it8->dnum = it8->dnum * 10.0 + (it8->ch - '0');
546 if (it8->ch == '.') { // Decimal point
548 cmsFloat64Number frac = 0.0; // fraction
549 int prec = 0; // precision
551 NextCh(it8); // Eats dec. point
553 while (isdigit(it8->ch)) {
555 frac = frac * 10.0 + (it8->ch - '0');
560 it8->dnum = it8->dnum + (frac / xpow10(prec));
563 // Exponent, example 34.00E+20
564 if (toupper(it8->ch) == 'E') {
569 NextCh(it8); sgn = 1;
571 if (it8->ch == '-') {
573 sgn = -1; NextCh(it8);
576 if (it8->ch == '+') {
583 while (isdigit(it8->ch)) {
585 if ((cmsFloat64Number) e * 10L < INT_MAX)
586 e = e * 10 + (it8->ch - '0');
592 it8 -> dnum = it8 -> dnum * xpow10(e);
600 void InSymbol(cmsIT8* it8)
602 register char *idptr;
609 while (isseparator(it8->ch))
612 if (isfirstidchar(it8->ch)) { // Identifier
619 if (++k < MAXID) *idptr++ = (char) it8->ch;
623 } while (isidchar(it8->ch));
628 key = BinSrchKey(it8->id);
629 if (key == SNONE) it8->sy = SIDENT;
634 if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
638 if (it8->ch == '-') {
646 if (it8->ch == '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary)
649 if (toupper(it8->ch) == 'X') {
654 while (isxdigit(it8->ch))
656 it8->ch = toupper(it8->ch);
657 if (it8->ch >= 'A' && it8->ch <= 'F') j = it8->ch -'A'+10;
658 else j = it8->ch - '0';
660 if ((long) it8->inum * 16L > (long) INT_MAX)
662 SynError(it8, "Invalid hexadecimal number");
666 it8->inum = it8->inum * 16 + j;
672 if (toupper(it8->ch) == 'B') { // Binary
677 while (it8->ch == '0' || it8->ch == '1')
681 if ((long) it8->inum * 2L > (long) INT_MAX)
683 SynError(it8, "Invalid binary number");
687 it8->inum = it8->inum * 2 + j;
695 while (isdigit(it8->ch)) {
697 if ((long) it8->inum * 10L > (long) INT_MAX) {
698 ReadReal(it8, it8->inum);
704 it8->inum = it8->inum * 10 + (it8->ch - '0');
708 if (it8->ch == '.') {
710 ReadReal(it8, it8->inum);
718 // Special case. Numbers followed by letters are taken as identifiers
720 if (isidchar(it8 ->ch)) {
722 if (it8 ->sy == SINUM) {
724 sprintf(it8->id, "%d", it8->inum);
728 sprintf(it8->id, it8 ->DoubleFormatter, it8->dnum);
731 k = (int) strlen(it8 ->id);
732 idptr = it8 ->id + k;
735 if (++k < MAXID) *idptr++ = (char) it8->ch;
739 } while (isidchar(it8->ch));
748 switch ((int) it8->ch) {
750 // EOF marker -- ignore it
755 // Eof stream markers
772 while (it8->ch && it8->ch != '\n')
786 while (k < MAXSTR && it8->ch != sng) {
788 if (it8->ch == '\n'|| it8->ch == '\r') k = MAXSTR+1;
790 *idptr++ = (char) it8->ch;
803 SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);
807 } while (it8->sy == SCOMMENT);
809 // Handle the include special token
811 if (it8 -> sy == SINCLUDE) {
815 if(it8 -> IncludeSP >= (MAXINCLUDE-1)) {
817 SynError(it8, "Too many recursion levels");
822 if (!Check(it8, SSTRING, "Filename expected")) return;
824 FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
825 if(FileNest == NULL) {
827 FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
828 //if(FileNest == NULL)
829 // TODO: how to manage out-of-memory conditions?
832 if (BuildAbsolutePath(it8->str,
833 it8->FileStack[it8->IncludeSP]->FileName,
834 FileNest->FileName, cmsMAX_PATH-1) == FALSE) {
835 SynError(it8, "File path too long");
839 FileNest->Stream = fopen(FileNest->FileName, "rt");
840 if (FileNest->Stream == NULL) {
842 SynError(it8, "File %s not found", FileNest->FileName);
853 // Checks end of line separator
855 cmsBool CheckEOLN(cmsIT8* it8)
857 if (!Check(it8, SEOLN, "Expected separator")) return FALSE;
858 while (it8 -> sy == SEOLN)
867 void Skip(cmsIT8* it8, SYMBOL sy)
869 if (it8->sy == sy && it8->sy != SEOF)
874 // Skip multiple EOLN
876 void SkipEOLN(cmsIT8* it8)
878 while (it8->sy == SEOLN) {
884 // Returns a string holding current value
886 cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle)
890 case SIDENT: strncpy(Buffer, it8->id, max);
893 case SINUM: snprintf(Buffer, max, "%d", it8 -> inum); break;
894 case SDNUM: snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break;
895 case SSTRING: strncpy(Buffer, it8->str, max);
901 return SynError(it8, "%s", ErrorTitle);
908 // ---------------------------------------------------------- Table
911 TABLE* GetTable(cmsIT8* it8)
913 if ((it8 -> nTable >= it8 ->TablesCount)) {
915 SynError(it8, "Table %d out of sequence", it8 -> nTable);
919 return it8 ->Tab + it8 ->nTable;
922 // ---------------------------------------------------------- Memory management
925 // Frees an allocator and owned memory
926 void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8)
928 cmsIT8* it8 = (cmsIT8*) hIT8;
933 if (it8->MemorySink) {
938 for (p = it8->MemorySink; p != NULL; p = n) {
941 if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr);
942 _cmsFree(it8 ->ContextID, p);
946 if (it8->MemoryBlock)
947 _cmsFree(it8 ->ContextID, it8->MemoryBlock);
949 _cmsFree(it8 ->ContextID, it8);
953 // Allocates a chunk of data, keep linked list
955 void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size)
958 void* ptr = _cmsMallocZero(it8->ContextID, size);
962 ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM));
966 _cmsFree(it8 ->ContextID, ptr);
971 ptr1-> Next = it8 -> MemorySink;
972 it8 -> MemorySink = ptr1;
981 void* AllocChunk(cmsIT8* it8, cmsUInt32Number size)
983 cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;
986 size = _cmsALIGNMEM(size);
990 if (it8 -> Allocator.BlockSize == 0)
992 it8 -> Allocator.BlockSize = 20*1024;
994 it8 ->Allocator.BlockSize *= 2;
996 if (it8 ->Allocator.BlockSize < size)
997 it8 ->Allocator.BlockSize = size;
999 it8 ->Allocator.Used = 0;
1000 it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize);
1003 ptr = it8 ->Allocator.Block + it8 ->Allocator.Used;
1004 it8 ->Allocator.Used += size;
1011 // Allocates a string
1013 char *AllocString(cmsIT8* it8, const char* str)
1015 cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1;
1019 ptr = (char *) AllocChunk(it8, Size);
1020 if (ptr) strncpy (ptr, str, Size-1);
1025 // Searches through linked list
1028 cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr)
1030 if (LastPtr) *LastPtr = p;
1032 for (; p != NULL; p = p->Next) {
1034 if (LastPtr) *LastPtr = p;
1036 if (*Key != '#') { // Comments are ignored
1038 if (cmsstrcasecmp(Key, p->Keyword) == 0)
1049 for (; p != NULL; p = p->NextSubkey) {
1051 if (LastPtr) *LastPtr = p;
1053 if (cmsstrcasecmp(Subkey, p->Subkey) == 0)
1062 // Add a property into a linked list
1064 KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs)
1070 // Check if property is already in list
1072 if (IsAvailableOnList(*Head, Key, Subkey, &p)) {
1074 // This may work for editing properties
1076 // return SynError(it8, "duplicate key <%s>", Key);
1082 // Allocate the container
1083 p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE));
1086 SynError(it8, "AddToList: out of memory");
1090 // Store name and value
1091 p->Keyword = AllocString(it8, Key);
1092 p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey);
1094 // Keep the container in our list
1095 if (*Head == NULL) {
1100 if (Subkey != NULL && last != NULL) {
1102 last->NextSubkey = p;
1104 // If Subkey is not null, then last is the last property with the same key,
1105 // but not necessarily is the last property in the list, so we need to move
1106 // to the actual list end
1107 while (last->Next != NULL)
1111 if (last != NULL) last->Next = p;
1115 p->NextSubkey = NULL;
1118 p->WriteAs = WriteAs;
1120 if (xValue != NULL) {
1122 p->Value = AllocString(it8, xValue);
1132 KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as)
1134 return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as);
1139 KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key)
1141 return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED);
1146 void AllocTable(cmsIT8* it8)
1150 t = it8 ->Tab + it8 ->TablesCount;
1152 t->HeaderList = NULL;
1153 t->DataFormat = NULL;
1156 it8 ->TablesCount++;
1160 cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable)
1162 cmsIT8* it8 = (cmsIT8*) IT8;
1164 if (nTable >= it8 ->TablesCount) {
1166 if (nTable == it8 ->TablesCount) {
1171 SynError(it8, "Table %d is out of sequence", nTable);
1176 it8 ->nTable = nTable;
1183 // Init an empty container
1184 cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
1189 it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8));
1190 if (it8 == NULL) return NULL;
1194 it8->MemoryBlock = NULL;
1195 it8->MemorySink = NULL;
1199 it8->ContextID = ContextID;
1200 it8->Allocator.Used = 0;
1201 it8->Allocator.Block = NULL;
1202 it8->Allocator.BlockSize = 0;
1204 it8->ValidKeywords = NULL;
1205 it8->ValidSampleID = NULL;
1209 it8 -> Source = NULL;
1213 it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
1217 strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
1218 cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");
1220 // Initialize predefined properties & data
1222 for (i=0; i < NUMPREDEFINEDPROPS; i++)
1223 AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as);
1225 for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)
1226 AddAvailableSampleID(it8, PredefinedSampleID[i]);
1229 return (cmsHANDLE) it8;
1233 const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8)
1235 return GetTable((cmsIT8*) hIT8)->SheetType;
1238 cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type)
1240 TABLE* t = GetTable((cmsIT8*) hIT8);
1242 strncpy(t ->SheetType, Type, MAXSTR-1);
1243 t ->SheetType[MAXSTR-1] = 0;
1247 cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val)
1249 cmsIT8* it8 = (cmsIT8*) hIT8;
1251 if (!Val) return FALSE;
1252 if (!*Val) return FALSE;
1254 return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL;
1260 cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val)
1262 cmsIT8* it8 = (cmsIT8*) hIT8;
1264 if (!Val) return FALSE;
1265 if (!*Val) return FALSE;
1267 return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL;
1271 cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val)
1273 cmsIT8* it8 = (cmsIT8*) hIT8;
1276 sprintf(Buffer, it8->DoubleFormatter, Val);
1278 return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1281 cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val)
1283 cmsIT8* it8 = (cmsIT8*) hIT8;
1286 sprintf(Buffer, "%d", Val);
1288 return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL;
1291 cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer)
1293 cmsIT8* it8 = (cmsIT8*) hIT8;
1295 return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1298 cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer)
1300 cmsIT8* it8 = (cmsIT8*) hIT8;
1302 return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL;
1306 const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key)
1308 cmsIT8* it8 = (cmsIT8*) hIT8;
1311 if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p))
1319 cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp)
1321 const char *v = cmsIT8GetProperty(hIT8, cProp);
1323 if (v) return atof(v);
1327 const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey)
1329 cmsIT8* it8 = (cmsIT8*) hIT8;
1332 if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) {
1338 // ----------------------------------------------------------------- Datasets
1342 void AllocateDataFormat(cmsIT8* it8)
1344 TABLE* t = GetTable(it8);
1346 if (t -> DataFormat) return; // Already allocated
1348 t -> nSamples = (int) cmsIT8GetPropertyDbl(it8, "NUMBER_OF_FIELDS");
1350 if (t -> nSamples <= 0) {
1352 SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS");
1356 t -> DataFormat = (char**) AllocChunk (it8, (t->nSamples + 1) * sizeof(char *));
1357 if (t->DataFormat == NULL) {
1359 SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array");
1365 const char *GetDataFormat(cmsIT8* it8, int n)
1367 TABLE* t = GetTable(it8);
1370 return t->DataFormat[n];
1376 cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label)
1378 TABLE* t = GetTable(it8);
1381 AllocateDataFormat(it8);
1383 if (n > t -> nSamples) {
1384 SynError(it8, "More than NUMBER_OF_FIELDS fields.");
1388 if (t->DataFormat) {
1389 t->DataFormat[n] = AllocString(it8, label);
1396 cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample)
1398 cmsIT8* it8 = (cmsIT8*) h;
1399 return SetDataFormat(it8, n, Sample);
1403 void AllocateDataSet(cmsIT8* it8)
1405 TABLE* t = GetTable(it8);
1407 if (t -> Data) return; // Already allocated
1409 t-> nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1410 t-> nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1412 t-> Data = (char**)AllocChunk (it8, (t->nSamples + 1) * (t->nPatches + 1) *sizeof (char*));
1413 if (t->Data == NULL) {
1415 SynError(it8, "AllocateDataSet: Unable to allocate data array");
1421 char* GetData(cmsIT8* it8, int nSet, int nField)
1423 TABLE* t = GetTable(it8);
1424 int nSamples = t -> nSamples;
1425 int nPatches = t -> nPatches;
1427 if (nSet >= nPatches || nField >= nSamples)
1430 if (!t->Data) return NULL;
1431 return t->Data [nSet * nSamples + nField];
1435 cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val)
1437 TABLE* t = GetTable(it8);
1440 AllocateDataSet(it8);
1442 if (!t->Data) return FALSE;
1444 if (nSet > t -> nPatches || nSet < 0) {
1446 return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches);
1449 if (nField > t ->nSamples || nField < 0) {
1450 return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples);
1454 t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val);
1459 // --------------------------------------------------------------- File I/O
1462 // Writes a string to file
1464 void WriteStr(SAVESTREAM* f, const char *str)
1466 cmsUInt32Number len;
1472 len = (cmsUInt32Number) strlen(str);
1476 if (f ->stream) { // Should I write it to a file?
1478 if (fwrite(str, 1, len, f->stream) != len) {
1479 cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser");
1484 else { // Or to a memory block?
1486 if (f ->Base) { // Am I just counting the bytes?
1488 if (f ->Used > f ->Max) {
1490 cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser");
1494 memmove(f ->Ptr, str, len);
1505 void Writef(SAVESTREAM* f, const char* frm, ...)
1510 va_start(args, frm);
1511 vsnprintf(Buffer, 4095, frm, args);
1513 WriteStr(f, Buffer);
1518 // Writes full header
1520 void WriteHeader(cmsIT8* it8, SAVESTREAM* fp)
1523 TABLE* t = GetTable(it8);
1526 WriteStr(fp, t->SheetType);
1529 for (p = t->HeaderList; (p != NULL); p = p->Next)
1531 if (*p ->Keyword == '#') {
1535 WriteStr(fp, "#\n# ");
1536 for (Pt = p ->Value; *Pt; Pt++) {
1539 Writef(fp, "%c", *Pt);
1546 WriteStr(fp, "\n#\n");
1551 if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) {
1553 #ifdef CMS_STRICT_CGATS
1554 WriteStr(fp, "KEYWORD\t\"");
1555 WriteStr(fp, p->Keyword);
1556 WriteStr(fp, "\"\n");
1559 AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED);
1562 WriteStr(fp, p->Keyword);
1565 switch (p ->WriteAs) {
1567 case WRITE_UNCOOKED:
1568 Writef(fp, "\t%s", p ->Value);
1571 case WRITE_STRINGIFY:
1572 Writef(fp, "\t\"%s\"", p->Value );
1575 case WRITE_HEXADECIMAL:
1576 Writef(fp, "\t0x%X", atoi(p ->Value));
1580 Writef(fp, "\t0x%B", atoi(p ->Value));
1584 Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value);
1587 default: SynError(it8, "Unknown write mode %d", p ->WriteAs);
1592 WriteStr (fp, "\n");
1598 // Writes the data format
1600 void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8)
1603 TABLE* t = GetTable(it8);
1605 if (!t -> DataFormat) return;
1607 WriteStr(fp, "BEGIN_DATA_FORMAT\n");
1609 nSamples = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1611 for (i = 0; i < nSamples; i++) {
1613 WriteStr(fp, t->DataFormat[i]);
1614 WriteStr(fp, ((i == (nSamples-1)) ? "\n" : "\t"));
1617 WriteStr (fp, "END_DATA_FORMAT\n");
1621 // Writes data array
1623 void WriteData(SAVESTREAM* fp, cmsIT8* it8)
1626 TABLE* t = GetTable(it8);
1628 if (!t->Data) return;
1630 WriteStr (fp, "BEGIN_DATA\n");
1632 t->nPatches = atoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1634 for (i = 0; i < t-> nPatches; i++) {
1638 for (j = 0; j < t->nSamples; j++) {
1640 char *ptr = t->Data[i*t->nSamples+j];
1642 if (ptr == NULL) WriteStr(fp, "\"\"");
1644 // If value contains whitespace, enclose within quote
1646 if (strchr(ptr, ' ') != NULL) {
1656 WriteStr(fp, ((j == (t->nSamples-1)) ? "\n" : "\t"));
1659 WriteStr (fp, "END_DATA\n");
1665 cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName)
1669 cmsIT8* it8 = (cmsIT8*) hIT8;
1671 memset(&sd, 0, sizeof(sd));
1673 sd.stream = fopen(cFileName, "wt");
1674 if (!sd.stream) return FALSE;
1676 for (i=0; i < it8 ->TablesCount; i++) {
1678 cmsIT8SetTable(hIT8, i);
1679 WriteHeader(it8, &sd);
1680 WriteDataFormat(&sd, it8);
1681 WriteData(&sd, it8);
1684 if (fclose(sd.stream) != 0) return FALSE;
1691 cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded)
1695 cmsIT8* it8 = (cmsIT8*) hIT8;
1697 memset(&sd, 0, sizeof(sd));
1700 sd.Base = (cmsUInt8Number*) MemPtr;
1706 sd.Max = *BytesNeeded; // Write to memory?
1708 sd.Max = 0; // Just counting the needed bytes
1710 for (i=0; i < it8 ->TablesCount; i++) {
1712 cmsIT8SetTable(hIT8, i);
1713 WriteHeader(it8, &sd);
1714 WriteDataFormat(&sd, it8);
1715 WriteData(&sd, it8);
1718 sd.Used++; // The \0 at the very end
1723 *BytesNeeded = sd.Used;
1729 // -------------------------------------------------------------- Higer level parsing
1732 cmsBool DataFormatSection(cmsIT8* it8)
1735 TABLE* t = GetTable(it8);
1737 InSymbol(it8); // Eats "BEGIN_DATA_FORMAT"
1740 while (it8->sy != SEND_DATA_FORMAT &&
1743 it8->sy != SSYNERROR) {
1745 if (it8->sy != SIDENT) {
1747 return SynError(it8, "Sample type expected");
1750 if (!SetDataFormat(it8, iField, it8->id)) return FALSE;
1758 Skip(it8, SEND_DATA_FORMAT);
1761 if (iField != t ->nSamples) {
1762 SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField);
1773 cmsBool DataSection (cmsIT8* it8)
1778 TABLE* t = GetTable(it8);
1780 InSymbol(it8); // Eats "BEGIN_DATA"
1784 AllocateDataSet(it8);
1786 while (it8->sy != SEND_DATA && it8->sy != SEOF)
1788 if (iField >= t -> nSamples) {
1794 if (it8->sy != SEND_DATA && it8->sy != SEOF) {
1796 if (!GetVal(it8, Buffer, 255, "Sample data expected"))
1799 if (!SetData(it8, iSet, iField, Buffer))
1810 Skip(it8, SEND_DATA);
1813 // Check for data completion.
1815 if ((iSet+1) != t -> nPatches)
1816 return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1);
1825 cmsBool HeaderSection(cmsIT8* it8)
1827 char VarName[MAXID];
1828 char Buffer[MAXSTR];
1831 while (it8->sy != SEOF &&
1832 it8->sy != SSYNERROR &&
1833 it8->sy != SBEGIN_DATA_FORMAT &&
1834 it8->sy != SBEGIN_DATA) {
1837 switch (it8 -> sy) {
1841 if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
1842 if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE;
1847 case SDATA_FORMAT_ID:
1849 if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
1850 if (!AddAvailableSampleID(it8, Buffer)) return FALSE;
1856 strncpy(VarName, it8->id, MAXID-1);
1857 VarName[MAXID-1] = 0;
1859 if (!IsAvailableOnList(it8-> ValidKeywords, VarName, NULL, &Key)) {
1861 #ifdef CMS_STRICT_CGATS
1862 return SynError(it8, "Undefined keyword '%s'", VarName);
1864 Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED);
1865 if (Key == NULL) return FALSE;
1870 if (!GetVal(it8, Buffer, MAXSTR-1, "Property data expected")) return FALSE;
1872 if(Key->WriteAs != WRITE_PAIR) {
1873 AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer,
1874 (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED);
1879 if (it8->sy != SSTRING)
1880 return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName);
1882 // chop the string as a list of "subkey, value" pairs, using ';' as a separator
1883 for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey)
1887 // identify token pair boundary
1888 Nextkey = (char*) strchr(Subkey, ';');
1892 // for each pair, split the subkey and the value
1893 Value = (char*) strrchr(Subkey, ',');
1895 return SynError(it8, "Invalid value for property '%s'.", VarName);
1897 // gobble the spaces before the coma, and the coma itself
1899 do *temp-- = '\0'; while(temp >= Subkey && *temp == ' ');
1901 // gobble any space at the right
1902 temp = Value + strlen(Value) - 1;
1903 while(*temp == ' ') *temp-- = '\0';
1905 // trim the strings from the left
1906 Subkey += strspn(Subkey, " ");
1907 Value += strspn(Value, " ");
1909 if(Subkey[0] == 0 || Value[0] == 0)
1910 return SynError(it8, "Invalid value for property '%s'.", VarName);
1911 AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR);
1922 return SynError(it8, "expected keyword or identifier");
1934 void ReadType(cmsIT8* it8, char* SheetTypePtr)
1936 // First line is a very special case.
1938 while (isseparator(it8->ch))
1941 while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != -1) {
1943 *SheetTypePtr++= (char) it8 ->ch;
1952 cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
1954 char* SheetTypePtr = it8 ->Tab[0].SheetType;
1957 ReadType(it8, SheetTypePtr);
1964 while (it8-> sy != SEOF &&
1965 it8-> sy != SSYNERROR) {
1967 switch (it8 -> sy) {
1969 case SBEGIN_DATA_FORMAT:
1970 if (!DataFormatSection(it8)) return FALSE;
1975 if (!DataSection(it8)) return FALSE;
1977 if (it8 -> sy != SEOF) {
1980 it8 ->nTable = it8 ->TablesCount - 1;
1982 // Read sheet type if present. We only support identifier and string.
1983 // <ident> <eoln> is a type string
1984 // anything else, is not a type string
1987 if (it8 ->sy == SIDENT) {
1989 // May be a type sheet or may be a prop value statement. We cannot use insymbol in
1990 // this special case...
1991 while (isseparator(it8->ch))
1994 // If a newline is found, then this is a type string
1995 if (it8 ->ch == '\n') {
1997 cmsIT8SetSheetType(it8, it8 ->id);
2002 // It is not. Just continue
2003 cmsIT8SetSheetType(it8, "");
2007 // Validate quoted strings
2008 if (it8 ->sy == SSTRING) {
2009 cmsIT8SetSheetType(it8, it8 ->str);
2022 if (!HeaderSection(it8)) return FALSE;
2027 return (it8 -> sy != SSYNERROR);
2032 // Init usefull pointers
2035 void CookPointers(cmsIT8* it8)
2040 cmsUInt32Number nOldTable = it8 ->nTable;
2042 for (j=0; j < it8 ->TablesCount; j++) {
2044 TABLE* t = it8 ->Tab + j;
2049 for (idField = 0; idField < t -> nSamples; idField++)
2051 if (t ->DataFormat == NULL){
2052 SynError(it8, "Undefined DATA_FORMAT");
2056 Fld = t->DataFormat[idField];
2060 if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {
2062 t -> SampleID = idField;
2064 for (i=0; i < t -> nPatches; i++) {
2066 char *Data = GetData(it8, i, idField);
2070 strncpy(Buffer, Data, 255);
2073 if (strlen(Buffer) <= strlen(Data))
2074 strcpy(Data, Buffer);
2076 SetData(it8, i, idField, Buffer);
2083 // "LABEL" is an extension. It keeps references to forward tables
2085 if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$' ) {
2087 // Search for table references...
2088 for (i=0; i < t -> nPatches; i++) {
2090 char *Label = GetData(it8, i, idField);
2096 // This is the label, search for a table containing
2099 for (k=0; k < it8 ->TablesCount; k++) {
2101 TABLE* Table = it8 ->Tab + k;
2104 if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
2106 // Available, keep type and table
2109 char *Type = p ->Value;
2112 snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type );
2114 SetData(it8, i, idField, Buffer);
2129 it8 ->nTable = nOldTable;
2132 // Try to infere if the file is a CGATS/IT8 file at all. Read first line
2133 // that should be something like some printable characters plus a \n
2134 // returns 0 if this is not like a CGATS, or an integer otherwise
2136 int IsMyBlock(cmsUInt8Number* Buffer, int n)
2138 int cols = 1, space = 0, quot = 0;
2141 if (n < 10) return 0; // Too small
2146 for (i = 1; i < n; i++) {
2152 return ((quot == 1) || (cols > 2)) ? 0 : cols;
2162 if (Buffer[i] < 32) return 0;
2163 if (Buffer[i] > 127) return 0;
2175 cmsBool IsMyFile(const char* FileName)
2178 cmsUInt32Number Size;
2179 cmsUInt8Number Ptr[133];
2181 fp = fopen(FileName, "rt");
2183 cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName);
2187 Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp);
2189 if (fclose(fp) != 0)
2194 return IsMyBlock(Ptr, Size);
2197 // ---------------------------------------------------------- Exported routines
2200 cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, void *Ptr, cmsUInt32Number len)
2206 _cmsAssert(Ptr != NULL);
2207 _cmsAssert(len != 0);
2209 type = IsMyBlock((cmsUInt8Number*)Ptr, len);
2210 if (type == 0) return NULL;
2212 hIT8 = cmsIT8Alloc(ContextID);
2213 if (!hIT8) return NULL;
2215 it8 = (cmsIT8*) hIT8;
2216 it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1);
2218 strncpy(it8 ->MemoryBlock, (const char*) Ptr, len);
2219 it8 ->MemoryBlock[len] = 0;
2221 strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1);
2222 it8-> Source = it8 -> MemoryBlock;
2224 if (!ParseIT8(it8, type-1)) {
2233 _cmsFree(ContextID, it8->MemoryBlock);
2234 it8 -> MemoryBlock = NULL;
2242 cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName)
2249 _cmsAssert(cFileName != NULL);
2251 type = IsMyFile(cFileName);
2252 if (type == 0) return NULL;
2254 hIT8 = cmsIT8Alloc(ContextID);
2255 it8 = (cmsIT8*) hIT8;
2256 if (!hIT8) return NULL;
2259 it8 ->FileStack[0]->Stream = fopen(cFileName, "rt");
2261 if (!it8 ->FileStack[0]->Stream) {
2267 strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1);
2268 it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0;
2270 if (!ParseIT8(it8, type-1)) {
2272 fclose(it8 ->FileStack[0]->Stream);
2280 if (fclose(it8 ->FileStack[0]->Stream)!= 0) {
2289 int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames)
2291 cmsIT8* it8 = (cmsIT8*) hIT8;
2294 _cmsAssert(hIT8 != NULL);
2299 *SampleNames = t -> DataFormat;
2300 return t -> nSamples;
2304 cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames)
2306 cmsIT8* it8 = (cmsIT8*) hIT8;
2312 _cmsAssert(hIT8 != NULL);
2316 // Pass#1 - count properties
2319 for (p = t -> HeaderList; p != NULL; p = p->Next) {
2324 Props = (char **) AllocChunk(it8, sizeof(char *) * n);
2326 // Pass#2 - Fill pointers
2328 for (p = t -> HeaderList; p != NULL; p = p->Next) {
2329 Props[n++] = p -> Keyword;
2332 *PropertyNames = Props;
2336 cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames)
2338 cmsIT8* it8 = (cmsIT8*) hIT8;
2344 _cmsAssert(hIT8 != NULL);
2349 if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) {
2350 *SubpropertyNames = 0;
2354 // Pass#1 - count properties
2357 for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
2358 if(tmp->Subkey != NULL)
2363 Props = (const char **) AllocChunk(it8, sizeof(char *) * n);
2365 // Pass#2 - Fill pointers
2367 for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
2368 if(tmp->Subkey != NULL)
2369 Props[n++] = p ->Subkey;
2372 *SubpropertyNames = Props;
2377 int LocatePatch(cmsIT8* it8, const char* cPatch)
2381 TABLE* t = GetTable(it8);
2383 for (i=0; i < t-> nPatches; i++) {
2385 data = GetData(it8, i, t->SampleID);
2389 if (cmsstrcasecmp(data, cPatch) == 0)
2394 // SynError(it8, "Couldn't find patch '%s'\n", cPatch);
2400 int LocateEmptyPatch(cmsIT8* it8)
2404 TABLE* t = GetTable(it8);
2406 for (i=0; i < t-> nPatches; i++) {
2408 data = GetData(it8, i, t->SampleID);
2419 int LocateSample(cmsIT8* it8, const char* cSample)
2423 TABLE* t = GetTable(it8);
2425 for (i=0; i < t->nSamples; i++) {
2427 fld = GetDataFormat(it8, i);
2428 if (cmsstrcasecmp(fld, cSample) == 0)
2437 int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample)
2439 cmsIT8* it8 = (cmsIT8*) hIT8;
2441 _cmsAssert(hIT8 != NULL);
2443 return LocateSample(it8, cSample);
2448 const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col)
2450 cmsIT8* it8 = (cmsIT8*) hIT8;
2452 _cmsAssert(hIT8 != NULL);
2454 return GetData(it8, row, col);
2458 cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col)
2462 Buffer = cmsIT8GetDataRowCol(hIT8, row, col);
2466 return atof(Buffer);
2474 cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val)
2476 cmsIT8* it8 = (cmsIT8*) hIT8;
2478 _cmsAssert(hIT8 != NULL);
2480 return SetData(it8, row, col, Val);
2484 cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val)
2486 cmsIT8* it8 = (cmsIT8*) hIT8;
2489 _cmsAssert(hIT8 != NULL);
2491 sprintf(Buff, it8->DoubleFormatter, Val);
2493 return SetData(it8, row, col, Buff);
2498 const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample)
2500 cmsIT8* it8 = (cmsIT8*) hIT8;
2503 _cmsAssert(hIT8 != NULL);
2505 iField = LocateSample(it8, cSample);
2510 iSet = LocatePatch(it8, cPatch);
2515 return GetData(it8, iSet, iField);
2519 cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch, const char* cSample)
2523 Buffer = cmsIT8GetData(it8, cPatch, cSample);
2527 return atof(Buffer);
2537 cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val)
2539 cmsIT8* it8 = (cmsIT8*) hIT8;
2543 _cmsAssert(hIT8 != NULL);
2547 iField = LocateSample(it8, cSample);
2552 if (t-> nPatches == 0) {
2554 AllocateDataFormat(it8);
2555 AllocateDataSet(it8);
2559 if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) {
2561 iSet = LocateEmptyPatch(it8);
2563 return SynError(it8, "Couldn't add more patches '%s'\n", cPatch);
2566 iField = t -> SampleID;
2569 iSet = LocatePatch(it8, cPatch);
2575 return SetData(it8, iSet, iField, Val);
2579 cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch,
2580 const char* cSample,
2581 cmsFloat64Number Val)
2583 cmsIT8* it8 = (cmsIT8*) hIT8;
2586 _cmsAssert(hIT8 != NULL);
2588 snprintf(Buff, 255, it8->DoubleFormatter, Val);
2589 return cmsIT8SetData(hIT8, cPatch, cSample, Buff);
2592 // Buffer should get MAXSTR at least
2594 const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer)
2596 cmsIT8* it8 = (cmsIT8*) hIT8;
2600 _cmsAssert(hIT8 != NULL);
2603 Data = GetData(it8, nPatch, t->SampleID);
2605 if (!Data) return NULL;
2606 if (!buffer) return Data;
2608 strncpy(buffer, Data, MAXSTR-1);
2609 buffer[MAXSTR-1] = 0;
2613 int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch)
2615 _cmsAssert(hIT8 != NULL);
2617 return LocatePatch((cmsIT8*)hIT8, cPatch);
2620 cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8)
2622 cmsIT8* it8 = (cmsIT8*) hIT8;
2624 _cmsAssert(hIT8 != NULL);
2626 return it8 ->TablesCount;
2629 // This handles the "LABEL" extension.
2630 // Label, nTable, Type
2632 int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType)
2634 const char* cLabelFld;
2635 char Type[256], Label[256];
2638 _cmsAssert(hIT8 != NULL);
2640 if (cField != NULL && *cField == 0)
2646 cLabelFld = cmsIT8GetData(hIT8, cSet, cField);
2647 if (!cLabelFld) return -1;
2649 if (sscanf(cLabelFld, "%255s %d %255s", Label, &nTable, Type) != 3)
2652 if (ExpectedType != NULL && *ExpectedType == 0)
2653 ExpectedType = NULL;
2657 if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1;
2660 return cmsIT8SetTable(hIT8, nTable);
2664 cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample)
2666 cmsIT8* it8 = (cmsIT8*) hIT8;
2669 _cmsAssert(hIT8 != NULL);
2671 pos = LocateSample(it8, cSample);
2675 it8->Tab[it8->nTable].SampleID = pos;
2680 void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter)
2682 cmsIT8* it8 = (cmsIT8*) hIT8;
2684 _cmsAssert(hIT8 != NULL);
2686 if (Formatter == NULL)
2687 strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
2689 strcpy(it8->DoubleFormatter, Formatter);
2691 it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0;