1 //---------------------------------------------------------------------------------
3 // Little Color Management System
4 // Copyright (c) 1998-2023 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 length of identifier
34 #define MAXSTR 1024 // Max length 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 '\\'
58 SEOF, // End of stream
59 SSYNERROR, // Syntax error found on stream
80 S_LUT_OUT_VIDEO_RANGE,
86 // How to write the value
97 // Linked list of variable names
98 typedef struct _KeyVal {
100 struct _KeyVal* Next;
101 char* Keyword; // Name of variable
102 struct _KeyVal* NextSubkey; // If key is a dictionary, points to the next item
103 char* Subkey; // If key is a dictionary, points to the subkey name
104 char* Value; // Points to value
105 WRITEMODE WriteAs; // How to write the value
110 // Linked list of memory chunks (Memory sink)
111 typedef struct _OwnedMem {
113 struct _OwnedMem* Next;
114 void * Ptr; // Point to value
119 typedef struct _SubAllocator {
121 cmsUInt8Number* Block;
122 cmsUInt32Number BlockSize;
123 cmsUInt32Number Used;
127 // Table. Each individual table can hold properties and rows & cols
128 typedef struct _Table {
130 char SheetType[MAXSTR]; // The first row of the IT8 (the type)
132 int nSamples, nPatches; // Cols, Rows
133 int SampleID; // Pos of ID
135 KEYVALUE* HeaderList; // The properties
137 char** DataFormat; // The binary stream descriptor
138 char** Data; // The binary stream
142 // File stream being parsed
143 typedef struct _FileContext {
144 char FileName[cmsMAX_PATH]; // File name if being read from file
145 FILE* Stream; // File stream or NULL if holded in memory
151 struct struct_it8* it8;
158 // This struct hold all information about an open IT8 handler.
159 typedef struct struct_it8 {
161 cmsUInt32Number TablesCount; // How many tables in this stream
162 cmsUInt32Number nTable; // The actual table
168 TABLE Tab[MAXTABLES];
171 OWNEDMEM* MemorySink; // The storage backend
172 SUBALLOCATOR Allocator; // String suballocator -- just to keep it fast
174 // Parser state machine
175 SYMBOL sy; // Current symbol
176 int ch; // Current character
178 cmsInt32Number inum; // integer value
179 cmsFloat64Number dnum; // real value
181 string* id; // identifier
182 string* str; // string
184 // Allowed keywords & datasets. They have visibility on whole stream
185 KEYVALUE* ValidKeywords;
186 KEYVALUE* ValidSampleID;
188 char* Source; // Points to loc. being parsed
189 cmsInt32Number lineno; // line counter for error reporting
191 FILECTX* FileStack[MAXINCLUDE]; // Stack of files being parsed
192 cmsInt32Number IncludeSP; // Include Stack Pointer
194 char* MemoryBlock; // The stream if holded in memory
196 char DoubleFormatter[MAXID];// Printf-like 'cmsFloat64Number' formatter
198 cmsContext ContextID; // The threading context
203 // The stream for save operations
206 FILE* stream; // For save-to-file behaviour
208 cmsUInt8Number* Base;
209 cmsUInt8Number* Ptr; // For save-to-mem behaviour
210 cmsUInt32Number Used;
216 // ------------------------------------------------------ cmsIT8 parsing routines
227 // The keyword->symbol translation tables. Sorting is required.
228 static const KEYWORD TabKeysIT8[] = {
230 {"$INCLUDE", SINCLUDE}, // This is an extension!
231 {".INCLUDE", SINCLUDE}, // This is an extension!
233 {"BEGIN_DATA", SBEGIN_DATA },
234 {"BEGIN_DATA_FORMAT", SBEGIN_DATA_FORMAT },
235 {"DATA_FORMAT_IDENTIFIER", SDATA_FORMAT_ID},
236 {"END_DATA", SEND_DATA},
237 {"END_DATA_FORMAT", SEND_DATA_FORMAT},
238 {"KEYWORD", SKEYWORD}
241 #define NUMKEYS_IT8 (sizeof(TabKeysIT8)/sizeof(KEYWORD))
243 static const KEYWORD TabKeysCUBE[] = {
245 {"DOMAIN_MAX", SDOMAIN_MAX },
246 {"DOMAIN_MIN", SDOMAIN_MIN },
247 {"LUT_1D_SIZE", S_LUT1D_SIZE },
248 {"LUT_1D_INPUT_RANGE", S_LUT1D_INPUT_RANGE },
249 {"LUT_3D_SIZE", S_LUT3D_SIZE },
250 {"LUT_3D_INPUT_RANGE", S_LUT3D_INPUT_RANGE },
251 {"LUT_IN_VIDEO_RANGE", S_LUT_IN_VIDEO_RANGE },
252 {"LUT_OUT_VIDEO_RANGE", S_LUT_OUT_VIDEO_RANGE },
257 #define NUMKEYS_CUBE (sizeof(TabKeysCUBE)/sizeof(KEYWORD))
261 // Predefined properties
265 const char *id; // The identifier
266 WRITEMODE as; // How is supposed to be written
269 static PROPERTY PredefinedProperties[] = {
271 {"NUMBER_OF_FIELDS", WRITE_UNCOOKED}, // Required - NUMBER OF FIELDS
272 {"NUMBER_OF_SETS", WRITE_UNCOOKED}, // Required - NUMBER OF SETS
273 {"ORIGINATOR", WRITE_STRINGIFY}, // Required - Identifies the specific system, organization or individual that created the data file.
274 {"FILE_DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file.
275 {"CREATED", WRITE_STRINGIFY}, // Required - Indicates date of creation of the data file.
276 {"DESCRIPTOR", WRITE_STRINGIFY}, // Required - Describes the purpose or contents of the data file.
277 {"DIFFUSE_GEOMETRY", WRITE_STRINGIFY}, // The diffuse geometry used. Allowed values are "sphere" or "opal".
278 {"MANUFACTURER", WRITE_STRINGIFY},
279 {"MANUFACTURE", WRITE_STRINGIFY}, // Some broken Fuji targets does store this value
280 {"PROD_DATE", WRITE_STRINGIFY}, // Identifies year and month of production of the target in the form yyyy:mm.
281 {"SERIAL", WRITE_STRINGIFY}, // Uniquely identifies individual physical target.
283 {"MATERIAL", WRITE_STRINGIFY}, // Identifies the material on which the target was produced using a code
284 // uniquely identifying th e material. This is intend ed to be used for IT8.7
285 // physical targets only (i.e . IT8.7/1 and IT8.7/2).
287 {"INSTRUMENTATION", WRITE_STRINGIFY}, // Used to report the specific instrumentation used (manufacturer and
288 // model number) to generate the data reported. This data will often
289 // provide more information about the particular data collected than an
290 // extensive list of specific details. This is particularly important for
291 // spectral data or data derived from spectrophotometry.
293 {"MEASUREMENT_SOURCE", WRITE_STRINGIFY}, // Illumination used for spectral measurements. This data helps provide
294 // a guide to the potential for issues of paper fluorescence, etc.
296 {"PRINT_CONDITIONS", WRITE_STRINGIFY}, // Used to define the characteristics of the printed sheet being reported.
297 // Where standard conditions have been defined (e.g., SWOP at nominal)
298 // named conditions may suffice. Otherwise, detailed information is
301 {"SAMPLE_BACKING", WRITE_STRINGIFY}, // Identifies the backing material used behind the sample during
302 // measurement. Allowed values are "black", "white", or {"na".
304 {"CHISQ_DOF", WRITE_STRINGIFY}, // Degrees of freedom associated with the Chi squared statistic
305 // below properties are new in recent specs:
307 {"MEASUREMENT_GEOMETRY", WRITE_STRINGIFY}, // The type of measurement, either reflection or transmission, should be indicated
308 // along with details of the geometry and the aperture size and shape. For example,
309 // for transmission measurements it is important to identify 0/diffuse, diffuse/0,
310 // opal or integrating sphere, etc. For reflection it is important to identify 0/45,
311 // 45/0, sphere (specular included or excluded), etc.
313 {"FILTER", WRITE_STRINGIFY}, // Identifies the use of physical filter(s) during measurement. Typically used to
314 // denote the use of filters such as none, D65, Red, Green or Blue.
316 {"POLARIZATION", WRITE_STRINGIFY}, // Identifies the use of a physical polarization filter during measurement. Allowed
317 // values are {"yes", "white", "none" or "na".
319 {"WEIGHTING_FUNCTION", WRITE_PAIR}, // Indicates such functions as: the CIE standard observer functions used in the
320 // calculation of various data parameters (2 degree and 10 degree), CIE standard
321 // illuminant functions used in the calculation of various data parameters (e.g., D50,
322 // D65, etc.), density status response, etc. If used there shall be at least one
323 // name-value pair following the WEIGHTING_FUNCTION tag/keyword. The first attribute
324 // in the set shall be {"name" and shall identify the particular parameter used.
325 // The second shall be {"value" and shall provide the value associated with that name.
326 // For ASCII data, a string containing the Name and Value attribute pairs shall follow
327 // the weighting function keyword. A semi-colon separates attribute pairs from each
328 // other and within the attribute the name and value are separated by a comma.
330 {"COMPUTATIONAL_PARAMETER", WRITE_PAIR}, // Parameter that is used in computing a value from measured data. Name is the name
331 // of the calculation, parameter is the name of the parameter used in the calculation
332 // and value is the value of the parameter.
334 {"TARGET_TYPE", WRITE_STRINGIFY}, // The type of target being measured, e.g. IT8.7/1, IT8.7/3, user defined, etc.
336 {"COLORANT", WRITE_STRINGIFY}, // Identifies the colorant(s) used in creating the target.
338 {"TABLE_DESCRIPTOR", WRITE_STRINGIFY}, // Describes the purpose or contents of a data table.
340 {"TABLE_NAME", WRITE_STRINGIFY} // Provides a short name for a data table.
343 #define NUMPREDEFINEDPROPS (sizeof(PredefinedProperties)/sizeof(PROPERTY))
346 // Predefined sample types on dataset
347 static const char* PredefinedSampleID[] = {
348 "SAMPLE_ID", // Identifies sample that data represents
349 "STRING", // Identifies label, or other non-machine readable value.
350 // Value must begin and end with a " symbol
352 "CMYK_C", // Cyan component of CMYK data expressed as a percentage
353 "CMYK_M", // Magenta component of CMYK data expressed as a percentage
354 "CMYK_Y", // Yellow component of CMYK data expressed as a percentage
355 "CMYK_K", // Black component of CMYK data expressed as a percentage
356 "D_RED", // Red filter density
357 "D_GREEN", // Green filter density
358 "D_BLUE", // Blue filter density
359 "D_VIS", // Visual filter density
360 "D_MAJOR_FILTER", // Major filter d ensity
361 "RGB_R", // Red component of RGB data
362 "RGB_G", // Green component of RGB data
363 "RGB_B", // Blue com ponent of RGB data
364 "SPECTRAL_NM", // Wavelength of measurement expressed in nanometers
365 "SPECTRAL_PCT", // Percentage reflectance/transmittance
366 "SPECTRAL_DEC", // Reflectance/transmittance
367 "XYZ_X", // X component of tristimulus data
368 "XYZ_Y", // Y component of tristimulus data
369 "XYZ_Z", // Z component of tristimulus data
370 "XYY_X", // x component of chromaticity data
371 "XYY_Y", // y component of chromaticity data
372 "XYY_CAPY", // Y component of tristimulus data
373 "LAB_L", // L* component of Lab data
374 "LAB_A", // a* component of Lab data
375 "LAB_B", // b* component of Lab data
376 "LAB_C", // C*ab component of Lab data
377 "LAB_H", // hab component of Lab data
379 "LAB_DE_94", // CIE dE using CIE 94
380 "LAB_DE_CMC", // dE using CMC
381 "LAB_DE_2000", // CIE dE using CIE DE 2000
382 "MEAN_DE", // Mean Delta E (LAB_DE) of samples compared to batch average
383 // (Used for data files for ANSI IT8.7/1 and IT8.7/2 targets)
384 "STDEV_X", // Standard deviation of X (tristimulus data)
385 "STDEV_Y", // Standard deviation of Y (tristimulus data)
386 "STDEV_Z", // Standard deviation of Z (tristimulus data)
387 "STDEV_L", // Standard deviation of L*
388 "STDEV_A", // Standard deviation of a*
389 "STDEV_B", // Standard deviation of b*
390 "STDEV_DE", // Standard deviation of CIE dE
391 "CHI_SQD_PAR"}; // The average of the standard deviations of L*, a* and b*. It is
392 // used to derive an estimate of the chi-squared parameter which is
393 // recommended as the predictor of the variability of dE
395 #define NUMPREDEFINEDSAMPLEID (sizeof(PredefinedSampleID)/sizeof(char *))
397 //Forward declaration of some internal functions
398 static void* AllocChunk(cmsIT8* it8, cmsUInt32Number size);
401 string* StringAlloc(cmsIT8* it8, int max)
403 string* s = (string*) AllocChunk(it8, sizeof(string));
404 if (s == NULL) return NULL;
409 s->begin = (char*) AllocChunk(it8, s->max);
415 void StringClear(string* s)
421 void StringAppend(string* s, char c)
423 if (s->len + 1 >= s->max)
428 new_ptr = (char*) AllocChunk(s->it8, s->max);
429 if (new_ptr != NULL && s->begin != NULL)
430 memcpy(new_ptr, s->begin, s->len);
435 if (s->begin != NULL)
437 s->begin[s->len++] = c;
438 s->begin[s->len] = 0;
443 char* StringPtr(string* s)
449 void StringCat(string* s, const char* c)
459 // Checks whatever c is a separator
461 cmsBool isseparator(int c)
463 return (c == ' ') || (c == '\t');
466 // Checks whatever c is a valid identifier char
468 cmsBool ismiddle(int c)
470 return (!isseparator(c) && (c != '#') && (c !='\"') && (c != '\'') && (c > 32) && (c < 127));
473 // Checks whatsever c is a valid identifier middle char.
475 cmsBool isidchar(int c)
477 return isalnum(c) || ismiddle(c);
480 // Checks whatsever c is a valid identifier first char.
482 cmsBool isfirstidchar(int c)
484 return c != '-' && !isdigit(c) && ismiddle(c);
487 // Guess whether the supplied path looks like an absolute path
489 cmsBool isabsolutepath(const char *path)
498 strncpy(ThreeChars, path, 3);
501 if(ThreeChars[0] == DIR_CHAR)
504 #ifdef CMS_IS_WINDOWS_
505 if (isalpha((int) ThreeChars[0]) && ThreeChars[1] == ':')
512 // Makes a file path based on a given reference path
513 // NOTE: this function doesn't check if the path exists or even if it's legal
515 cmsBool BuildAbsolutePath(const char *relPath, const char *basePath, char *buffer, cmsUInt32Number MaxLen)
521 if (isabsolutepath(relPath)) {
523 memcpy(buffer, relPath, MaxLen);
524 buffer[MaxLen-1] = 0;
528 // No, search for last
529 memcpy(buffer, basePath, MaxLen);
530 buffer[MaxLen-1] = 0;
532 tail = strrchr(buffer, DIR_CHAR);
533 if (tail == NULL) return FALSE; // Is not absolute and has no separators??
535 len = (cmsUInt32Number) (tail - buffer);
536 if (len >= MaxLen) return FALSE;
538 // No need to assure zero terminator over here
539 strncpy(tail + 1, relPath, MaxLen - len);
545 // Make sure no exploit is being even tried
547 const char* NoMeta(const char* str)
549 if (strchr(str, '%') != NULL)
550 return "**** CORRUPTED FORMAT STRING ***";
557 cmsBool SynError(cmsIT8* it8, const char *Txt, ...)
559 char Buffer[256], ErrMsg[1024];
563 vsnprintf(Buffer, 255, Txt, args);
567 snprintf(ErrMsg, 1023, "%s: Line %d, %s", it8->FileStack[it8 ->IncludeSP]->FileName, it8->lineno, Buffer);
570 cmsSignalError(it8 ->ContextID, cmsERROR_CORRUPTION_DETECTED, "%s", ErrMsg);
574 // Check if current symbol is same as specified. issue an error else.
576 cmsBool Check(cmsIT8* it8, SYMBOL sy, const char* Err)
579 return SynError(it8, NoMeta(Err));
583 // Read Next character from stream
585 void NextCh(cmsIT8* it8)
587 if (it8 -> FileStack[it8 ->IncludeSP]->Stream) {
589 it8 ->ch = fgetc(it8 ->FileStack[it8 ->IncludeSP]->Stream);
591 if (feof(it8 -> FileStack[it8 ->IncludeSP]->Stream)) {
593 if (it8 ->IncludeSP > 0) {
595 fclose(it8 ->FileStack[it8->IncludeSP--]->Stream);
596 it8 -> ch = ' '; // Whitespace to be ignored
603 it8->ch = *it8->Source;
604 if (it8->ch) it8->Source++;
609 // Try to see if current identifier is a keyword, if so return the referred symbol
611 SYMBOL BinSrchKey(const char *id, int NumKeys, const KEYWORD* TabKeys)
620 res = cmsstrcasecmp(id, TabKeys[x-1].id);
621 if (res == 0) return TabKeys[x-1].sy;
622 if (res < 0) r = x - 1;
632 cmsFloat64Number xpow10(int n)
634 return pow(10, (cmsFloat64Number) n);
638 // Reads a Real number, tries to follow from integer number
640 void ReadReal(cmsIT8* it8, cmsInt32Number inum)
642 it8->dnum = (cmsFloat64Number)inum;
644 while (isdigit(it8->ch)) {
646 it8->dnum = (cmsFloat64Number)it8->dnum * 10.0 + (cmsFloat64Number)(it8->ch - '0');
650 if (it8->ch == '.') { // Decimal point
652 cmsFloat64Number frac = 0.0; // fraction
653 int prec = 0; // precision
655 NextCh(it8); // Eats dec. point
657 while (isdigit(it8->ch)) {
659 frac = frac * 10.0 + (cmsFloat64Number)(it8->ch - '0');
664 it8->dnum = it8->dnum + (frac / xpow10(prec));
667 // Exponent, example 34.00E+20
668 if (toupper(it8->ch) == 'E') {
673 NextCh(it8); sgn = 1;
675 if (it8->ch == '-') {
677 sgn = -1; NextCh(it8);
680 if (it8->ch == '+') {
687 while (isdigit(it8->ch)) {
689 cmsInt32Number digit = (it8->ch - '0');
691 if ((cmsFloat64Number)e * 10.0 + (cmsFloat64Number)digit < (cmsFloat64Number)+2147483647.0)
698 it8->dnum = it8->dnum * xpow10(e);
702 // Parses a float number
703 // This can not call directly atof because it uses locale dependent
704 // parsing, while CCMX files always use . as decimal separator
706 cmsFloat64Number ParseFloatNumber(const char *Buffer)
708 cmsFloat64Number dnum = 0.0;
712 if (Buffer == NULL) return 0.0;
714 if (*Buffer == '-' || *Buffer == '+') {
716 sign = (*Buffer == '-') ? -1 : 1;
721 while (*Buffer && isdigit((int)*Buffer)) {
723 dnum = dnum * 10.0 + (*Buffer - '0');
724 if (*Buffer) Buffer++;
727 if (*Buffer == '.') {
729 cmsFloat64Number frac = 0.0; // fraction
730 int prec = 0; // precision
732 if (*Buffer) Buffer++;
734 while (*Buffer && isdigit((int)*Buffer)) {
736 frac = frac * 10.0 + (*Buffer - '0');
738 if (*Buffer) Buffer++;
741 dnum = dnum + (frac / xpow10(prec));
744 // Exponent, example 34.00E+20
745 if (*Buffer && toupper(*Buffer) == 'E') {
750 if (*Buffer) Buffer++;
753 if (*Buffer == '-') {
756 if (*Buffer) Buffer++;
759 if (*Buffer == '+') {
762 if (*Buffer) Buffer++;
766 while (*Buffer && isdigit((int)*Buffer)) {
768 cmsInt32Number digit = (*Buffer - '0');
770 if ((cmsFloat64Number)e * 10.0 + digit < (cmsFloat64Number)+2147483647.0)
773 if (*Buffer) Buffer++;
777 dnum = dnum * xpow10(e);
784 // Reads a string, special case to avoid infinite recursion on .include
786 void InStringSymbol(cmsIT8* it8)
788 while (isseparator(it8->ch))
791 if (it8->ch == '\'' || it8->ch == '\"')
796 StringClear(it8->str);
800 while (it8->ch != sng) {
802 if (it8->ch == '\n' || it8->ch == '\r' || it8->ch == 0) break;
804 StringAppend(it8->str, (char)it8->ch);
813 SynError(it8, "String expected");
819 void InSymbol(cmsIT8* it8)
825 while (isseparator(it8->ch))
828 if (isfirstidchar(it8->ch)) { // Identifier
830 StringClear(it8->id);
834 StringAppend(it8->id, (char) it8->ch);
838 } while (isidchar(it8->ch));
841 key = BinSrchKey(StringPtr(it8->id),
842 it8->IsCUBE ? NUMKEYS_CUBE : NUMKEYS_IT8,
843 it8->IsCUBE ? TabKeysCUBE : TabKeysIT8);
844 if (key == SUNDEFINED) it8->sy = SIDENT;
849 if (isdigit(it8->ch) || it8->ch == '.' || it8->ch == '-' || it8->ch == '+')
853 if (it8->ch == '-') {
861 if (it8->ch == '0') { // 0xnnnn (Hexa) or 0bnnnn (Binary)
864 if (toupper(it8->ch) == 'X') {
869 while (isxdigit(it8->ch))
871 it8->ch = toupper(it8->ch);
872 if (it8->ch >= 'A' && it8->ch <= 'F') j = it8->ch -'A'+10;
873 else j = it8->ch - '0';
875 if ((cmsFloat64Number) it8->inum * 16.0 + (cmsFloat64Number) j > (cmsFloat64Number)+2147483647.0)
877 SynError(it8, "Invalid hexadecimal number");
882 it8->inum = it8->inum * 16 + j;
888 if (toupper(it8->ch) == 'B') { // Binary
893 while (it8->ch == '0' || it8->ch == '1')
897 if ((cmsFloat64Number) it8->inum * 2.0 + j > (cmsFloat64Number)+2147483647.0)
899 SynError(it8, "Invalid binary number");
904 it8->inum = it8->inum * 2 + j;
912 while (isdigit(it8->ch)) {
914 cmsInt32Number digit = (it8->ch - '0');
916 if ((cmsFloat64Number) it8->inum * 10.0 + (cmsFloat64Number) digit > (cmsFloat64Number) +2147483647.0) {
917 ReadReal(it8, it8->inum);
923 it8->inum = it8->inum * 10 + digit;
927 if (it8->ch == '.') {
929 ReadReal(it8, it8->inum);
937 // Special case. Numbers followed by letters are taken as identifiers
939 if (isidchar(it8 ->ch)) {
943 if (it8 ->sy == SINUM) {
945 snprintf(buffer, sizeof(buffer), "%d", it8->inum);
949 snprintf(buffer, sizeof(buffer), it8 ->DoubleFormatter, it8->dnum);
952 StringClear(it8->id);
953 StringCat(it8->id, buffer);
957 StringAppend(it8->id, (char) it8->ch);
961 } while (isidchar(it8->ch));
969 switch ((int) it8->ch) {
971 // Eof stream markers
997 while (it8->ch && it8->ch != '\n' && it8->ch != '\r')
1006 InStringSymbol(it8);
1011 SynError(it8, "Unrecognized character: 0x%x", it8 ->ch);
1016 } while (it8->sy == SCOMMENT);
1018 // Handle the include special token
1020 if (it8 -> sy == SINCLUDE) {
1024 if(it8 -> IncludeSP >= (MAXINCLUDE-1)) {
1026 SynError(it8, "Too many recursion levels");
1031 InStringSymbol(it8);
1032 if (!Check(it8, SSTRING, "Filename expected"))
1038 FileNest = it8 -> FileStack[it8 -> IncludeSP + 1];
1039 if(FileNest == NULL) {
1041 FileNest = it8 ->FileStack[it8 -> IncludeSP + 1] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
1042 if (FileNest == NULL) {
1043 SynError(it8, "Out of memory");
1049 if (BuildAbsolutePath(StringPtr(it8->str),
1050 it8->FileStack[it8->IncludeSP]->FileName,
1051 FileNest->FileName, cmsMAX_PATH-1) == FALSE) {
1052 SynError(it8, "File path too long");
1057 FileNest->Stream = fopen(FileNest->FileName, "rte");
1058 if (FileNest->Stream == NULL) {
1060 SynError(it8, "File %s not found", FileNest->FileName);
1072 // Checks end of line separator
1074 cmsBool CheckEOLN(cmsIT8* it8)
1076 if (!Check(it8, SEOLN, "Expected separator")) return FALSE;
1077 while (it8 -> sy == SEOLN)
1086 void Skip(cmsIT8* it8, SYMBOL sy)
1088 if (it8->sy == sy && it8->sy != SEOF)
1093 // Skip multiple EOLN
1095 void SkipEOLN(cmsIT8* it8)
1097 while (it8->sy == SEOLN) {
1103 // Returns a string holding current value
1105 cmsBool GetVal(cmsIT8* it8, char* Buffer, cmsUInt32Number max, const char* ErrorTitle)
1109 case SEOLN: // Empty value
1112 case SIDENT: strncpy(Buffer, StringPtr(it8->id), max);
1115 case SINUM: snprintf(Buffer, max, "%d", it8 -> inum); break;
1116 case SDNUM: snprintf(Buffer, max, it8->DoubleFormatter, it8 -> dnum); break;
1117 case SSTRING: strncpy(Buffer, StringPtr(it8->str), max);
1123 return SynError(it8, "%s", ErrorTitle);
1130 // ---------------------------------------------------------- Table
1133 TABLE* GetTable(cmsIT8* it8)
1135 if ((it8 -> nTable >= it8 ->TablesCount)) {
1137 SynError(it8, "Table %d out of sequence", it8 -> nTable);
1141 return it8 ->Tab + it8 ->nTable;
1144 // ---------------------------------------------------------- Memory management
1147 // Frees an allocator and owned memory
1148 void CMSEXPORT cmsIT8Free(cmsHANDLE hIT8)
1150 cmsIT8* it8 = (cmsIT8*) hIT8;
1155 if (it8->MemorySink) {
1160 for (p = it8->MemorySink; p != NULL; p = n) {
1163 if (p->Ptr) _cmsFree(it8 ->ContextID, p->Ptr);
1164 _cmsFree(it8 ->ContextID, p);
1168 if (it8->MemoryBlock)
1169 _cmsFree(it8 ->ContextID, it8->MemoryBlock);
1171 _cmsFree(it8 ->ContextID, it8);
1175 // Allocates a chunk of data, keep linked list
1177 void* AllocBigBlock(cmsIT8* it8, cmsUInt32Number size)
1180 void* ptr = _cmsMallocZero(it8->ContextID, size);
1184 ptr1 = (OWNEDMEM*) _cmsMallocZero(it8 ->ContextID, sizeof(OWNEDMEM));
1188 _cmsFree(it8 ->ContextID, ptr);
1193 ptr1-> Next = it8 -> MemorySink;
1194 it8 -> MemorySink = ptr1;
1203 void* AllocChunk(cmsIT8* it8, cmsUInt32Number size)
1205 cmsUInt32Number Free = it8 ->Allocator.BlockSize - it8 ->Allocator.Used;
1206 cmsUInt8Number* ptr;
1208 size = _cmsALIGNMEM(size);
1212 if (it8 -> Allocator.BlockSize == 0)
1214 it8 -> Allocator.BlockSize = 20*1024;
1216 it8 ->Allocator.BlockSize *= 2;
1218 if (it8 ->Allocator.BlockSize < size)
1219 it8 ->Allocator.BlockSize = size;
1221 it8 ->Allocator.Used = 0;
1222 it8 ->Allocator.Block = (cmsUInt8Number*) AllocBigBlock(it8, it8 ->Allocator.BlockSize);
1225 if (it8->Allocator.Block == NULL)
1228 ptr = it8 ->Allocator.Block + it8 ->Allocator.Used;
1229 it8 ->Allocator.Used += size;
1236 // Allocates a string
1238 char *AllocString(cmsIT8* it8, const char* str)
1240 cmsUInt32Number Size = (cmsUInt32Number) strlen(str)+1;
1244 ptr = (char *) AllocChunk(it8, Size);
1245 if (ptr) memcpy(ptr, str, Size-1);
1250 // Searches through linked list
1253 cmsBool IsAvailableOnList(KEYVALUE* p, const char* Key, const char* Subkey, KEYVALUE** LastPtr)
1255 if (LastPtr) *LastPtr = p;
1257 for (; p != NULL; p = p->Next) {
1259 if (LastPtr) *LastPtr = p;
1261 if (*Key != '#') { // Comments are ignored
1263 if (cmsstrcasecmp(Key, p->Keyword) == 0)
1274 for (; p != NULL; p = p->NextSubkey) {
1276 if (p ->Subkey == NULL) continue;
1278 if (LastPtr) *LastPtr = p;
1280 if (cmsstrcasecmp(Subkey, p->Subkey) == 0)
1289 // Add a property into a linked list
1291 KEYVALUE* AddToList(cmsIT8* it8, KEYVALUE** Head, const char *Key, const char *Subkey, const char* xValue, WRITEMODE WriteAs)
1297 // Check if property is already in list
1299 if (IsAvailableOnList(*Head, Key, Subkey, &p)) {
1301 // This may work for editing properties
1303 if (cmsstrcasecmp(Key, "NUMBER_OF_FIELDS") == 0 ||
1304 cmsstrcasecmp(Key, "NUMBER_OF_SETS") == 0) {
1306 SynError(it8, "duplicate key <%s>", Key);
1314 // Allocate the container
1315 p = (KEYVALUE*) AllocChunk(it8, sizeof(KEYVALUE));
1318 SynError(it8, "AddToList: out of memory");
1322 // Store name and value
1323 p->Keyword = AllocString(it8, Key);
1324 p->Subkey = (Subkey == NULL) ? NULL : AllocString(it8, Subkey);
1326 // Keep the container in our list
1327 if (*Head == NULL) {
1332 if (Subkey != NULL && last != NULL) {
1334 last->NextSubkey = p;
1336 // If Subkey is not null, then last is the last property with the same key,
1337 // but not necessarily is the last property in the list, so we need to move
1338 // to the actual list end
1339 while (last->Next != NULL)
1343 if (last != NULL) last->Next = p;
1347 p->NextSubkey = NULL;
1350 p->WriteAs = WriteAs;
1352 if (xValue != NULL) {
1354 p->Value = AllocString(it8, xValue);
1364 KEYVALUE* AddAvailableProperty(cmsIT8* it8, const char* Key, WRITEMODE as)
1366 return AddToList(it8, &it8->ValidKeywords, Key, NULL, NULL, as);
1371 KEYVALUE* AddAvailableSampleID(cmsIT8* it8, const char* Key)
1373 return AddToList(it8, &it8->ValidSampleID, Key, NULL, NULL, WRITE_UNCOOKED);
1378 void AllocTable(cmsIT8* it8)
1382 t = it8 ->Tab + it8 ->TablesCount;
1384 t->HeaderList = NULL;
1385 t->DataFormat = NULL;
1388 it8 ->TablesCount++;
1392 cmsInt32Number CMSEXPORT cmsIT8SetTable(cmsHANDLE IT8, cmsUInt32Number nTable)
1394 cmsIT8* it8 = (cmsIT8*) IT8;
1396 if (nTable >= it8 ->TablesCount) {
1398 if (nTable == it8 ->TablesCount) {
1403 SynError(it8, "Table %d is out of sequence", nTable);
1408 it8 ->nTable = nTable;
1410 return (cmsInt32Number) nTable;
1415 // Init an empty container
1416 cmsHANDLE CMSEXPORT cmsIT8Alloc(cmsContext ContextID)
1421 it8 = (cmsIT8*) _cmsMallocZero(ContextID, sizeof(cmsIT8));
1422 if (it8 == NULL) return NULL;
1426 it8->MemoryBlock = NULL;
1427 it8->MemorySink = NULL;
1429 it8->IsCUBE = FALSE;
1433 it8->ContextID = ContextID;
1434 it8->Allocator.Used = 0;
1435 it8->Allocator.Block = NULL;
1436 it8->Allocator.BlockSize = 0;
1438 it8->ValidKeywords = NULL;
1439 it8->ValidSampleID = NULL;
1441 it8 -> sy = SUNDEFINED;
1443 it8 -> Source = NULL;
1447 it8->FileStack[0] = (FILECTX*)AllocChunk(it8, sizeof(FILECTX));
1451 it8->id = StringAlloc(it8, MAXSTR);
1452 it8->str = StringAlloc(it8, MAXSTR);
1454 strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
1455 cmsIT8SetSheetType((cmsHANDLE) it8, "CGATS.17");
1457 // Initialize predefined properties & data
1459 for (i=0; i < NUMPREDEFINEDPROPS; i++)
1460 AddAvailableProperty(it8, PredefinedProperties[i].id, PredefinedProperties[i].as);
1462 for (i=0; i < NUMPREDEFINEDSAMPLEID; i++)
1463 AddAvailableSampleID(it8, PredefinedSampleID[i]);
1466 return (cmsHANDLE) it8;
1470 const char* CMSEXPORT cmsIT8GetSheetType(cmsHANDLE hIT8)
1472 return GetTable((cmsIT8*) hIT8)->SheetType;
1475 cmsBool CMSEXPORT cmsIT8SetSheetType(cmsHANDLE hIT8, const char* Type)
1477 TABLE* t = GetTable((cmsIT8*) hIT8);
1479 strncpy(t ->SheetType, Type, MAXSTR-1);
1480 t ->SheetType[MAXSTR-1] = 0;
1484 cmsBool CMSEXPORT cmsIT8SetComment(cmsHANDLE hIT8, const char* Val)
1486 cmsIT8* it8 = (cmsIT8*) hIT8;
1488 if (!Val) return FALSE;
1489 if (!*Val) return FALSE;
1491 return AddToList(it8, &GetTable(it8)->HeaderList, "# ", NULL, Val, WRITE_UNCOOKED) != NULL;
1495 cmsBool CMSEXPORT cmsIT8SetPropertyStr(cmsHANDLE hIT8, const char* Key, const char *Val)
1497 cmsIT8* it8 = (cmsIT8*) hIT8;
1499 if (!Val) return FALSE;
1500 if (!*Val) return FALSE;
1502 return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Val, WRITE_STRINGIFY) != NULL;
1505 cmsBool CMSEXPORT cmsIT8SetPropertyDbl(cmsHANDLE hIT8, const char* cProp, cmsFloat64Number Val)
1507 cmsIT8* it8 = (cmsIT8*) hIT8;
1510 snprintf(Buffer, 1023, it8->DoubleFormatter, Val);
1512 return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1515 cmsBool CMSEXPORT cmsIT8SetPropertyHex(cmsHANDLE hIT8, const char* cProp, cmsUInt32Number Val)
1517 cmsIT8* it8 = (cmsIT8*) hIT8;
1520 snprintf(Buffer, 1023, "%u", Val);
1522 return AddToList(it8, &GetTable(it8)->HeaderList, cProp, NULL, Buffer, WRITE_HEXADECIMAL) != NULL;
1525 cmsBool CMSEXPORT cmsIT8SetPropertyUncooked(cmsHANDLE hIT8, const char* Key, const char* Buffer)
1527 cmsIT8* it8 = (cmsIT8*) hIT8;
1529 return AddToList(it8, &GetTable(it8)->HeaderList, Key, NULL, Buffer, WRITE_UNCOOKED) != NULL;
1532 cmsBool CMSEXPORT cmsIT8SetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char* SubKey, const char *Buffer)
1534 cmsIT8* it8 = (cmsIT8*) hIT8;
1536 return AddToList(it8, &GetTable(it8)->HeaderList, Key, SubKey, Buffer, WRITE_PAIR) != NULL;
1540 const char* CMSEXPORT cmsIT8GetProperty(cmsHANDLE hIT8, const char* Key)
1542 cmsIT8* it8 = (cmsIT8*) hIT8;
1545 if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, NULL, &p))
1553 cmsFloat64Number CMSEXPORT cmsIT8GetPropertyDbl(cmsHANDLE hIT8, const char* cProp)
1555 const char *v = cmsIT8GetProperty(hIT8, cProp);
1557 if (v == NULL) return 0.0;
1559 return ParseFloatNumber(v);
1562 const char* CMSEXPORT cmsIT8GetPropertyMulti(cmsHANDLE hIT8, const char* Key, const char *SubKey)
1564 cmsIT8* it8 = (cmsIT8*) hIT8;
1567 if (IsAvailableOnList(GetTable(it8) -> HeaderList, Key, SubKey, &p)) {
1573 // ----------------------------------------------------------------- Datasets
1575 // A safe atoi that returns 0 when NULL input is given
1577 cmsInt32Number satoi(const char* b)
1581 if (b == NULL) return 0;
1584 if (n > 0x7fffffffL) return 0x7fffffffL;
1585 if (n < -0x7ffffffeL) return -0x7ffffffeL;
1587 return (cmsInt32Number)n;
1592 cmsBool AllocateDataFormat(cmsIT8* it8)
1594 TABLE* t = GetTable(it8);
1596 if (t -> DataFormat) return TRUE; // Already allocated
1598 t -> nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1600 if (t -> nSamples <= 0) {
1602 SynError(it8, "AllocateDataFormat: Unknown NUMBER_OF_FIELDS");
1606 t -> DataFormat = (char**) AllocChunk (it8, ((cmsUInt32Number) t->nSamples + 1) * sizeof(char *));
1607 if (t->DataFormat == NULL) {
1609 SynError(it8, "AllocateDataFormat: Unable to allocate dataFormat array");
1617 const char *GetDataFormat(cmsIT8* it8, int n)
1619 TABLE* t = GetTable(it8);
1622 return t->DataFormat[n];
1628 cmsBool SetDataFormat(cmsIT8* it8, int n, const char *label)
1630 TABLE* t = GetTable(it8);
1632 if (!t->DataFormat) {
1634 if (!AllocateDataFormat(it8))
1638 if (n > t -> nSamples) {
1639 SynError(it8, "More than NUMBER_OF_FIELDS fields.");
1643 if (t->DataFormat) {
1644 t->DataFormat[n] = AllocString(it8, label);
1645 if (t->DataFormat[n] == NULL) return FALSE;
1652 cmsBool CMSEXPORT cmsIT8SetDataFormat(cmsHANDLE h, int n, const char *Sample)
1654 cmsIT8* it8 = (cmsIT8*)h;
1655 return SetDataFormat(it8, n, Sample);
1658 // Convert to binary
1660 const char* satob(const char* v)
1663 static char buf[33];
1666 if (v == NULL) return "0";
1671 for (; x; x /= 2) *--s = '0' + x%2;
1678 cmsBool AllocateDataSet(cmsIT8* it8)
1680 TABLE* t = GetTable(it8);
1682 if (t -> Data) return TRUE; // Already allocated
1684 t-> nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1685 t-> nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1687 if (t -> nSamples < 0 || t->nSamples > 0x7ffe || t->nPatches < 0 || t->nPatches > 0x7ffe)
1689 SynError(it8, "AllocateDataSet: too much data");
1693 // Some dumb analizers warns of possible overflow here, just take a look couple of lines above.
1694 t->Data = (char**)AllocChunk(it8, ((cmsUInt32Number)t->nSamples + 1) * ((cmsUInt32Number)t->nPatches + 1) * sizeof(char*));
1695 if (t->Data == NULL) {
1697 SynError(it8, "AllocateDataSet: Unable to allocate data array");
1706 char* GetData(cmsIT8* it8, int nSet, int nField)
1708 TABLE* t = GetTable(it8);
1709 int nSamples = t -> nSamples;
1710 int nPatches = t -> nPatches;
1712 if (nSet < 0 || nSet >= nPatches || nField < 0 || nField >= nSamples)
1715 if (!t->Data) return NULL;
1716 return t->Data [nSet * nSamples + nField];
1720 cmsBool SetData(cmsIT8* it8, int nSet, int nField, const char *Val)
1722 TABLE* t = GetTable(it8);
1725 if (!AllocateDataSet(it8)) return FALSE;
1728 if (!t->Data) return FALSE;
1730 if (nSet > t -> nPatches || nSet < 0) {
1732 return SynError(it8, "Patch %d out of range, there are %d patches", nSet, t -> nPatches);
1735 if (nField > t ->nSamples || nField < 0) {
1736 return SynError(it8, "Sample %d out of range, there are %d samples", nField, t ->nSamples);
1740 t->Data [nSet * t -> nSamples + nField] = AllocString(it8, Val);
1745 // --------------------------------------------------------------- File I/O
1748 // Writes a string to file
1750 void WriteStr(SAVESTREAM* f, const char *str)
1752 cmsUInt32Number len;
1758 len = (cmsUInt32Number) strlen(str);
1762 if (f ->stream) { // Should I write it to a file?
1764 if (fwrite(str, 1, len, f->stream) != len) {
1765 cmsSignalError(0, cmsERROR_WRITE, "Write to file error in CGATS parser");
1770 else { // Or to a memory block?
1772 if (f ->Base) { // Am I just counting the bytes?
1774 if (f ->Used > f ->Max) {
1776 cmsSignalError(0, cmsERROR_WRITE, "Write to memory overflows in CGATS parser");
1780 memmove(f ->Ptr, str, len);
1791 void Writef(SAVESTREAM* f, const char* frm, ...)
1796 va_start(args, frm);
1797 vsnprintf(Buffer, 4095, frm, args);
1799 WriteStr(f, Buffer);
1804 // Writes full header
1806 void WriteHeader(cmsIT8* it8, SAVESTREAM* fp)
1809 TABLE* t = GetTable(it8);
1812 WriteStr(fp, t->SheetType);
1815 for (p = t->HeaderList; (p != NULL); p = p->Next)
1817 if (*p ->Keyword == '#') {
1821 WriteStr(fp, "#\n# ");
1822 for (Pt = p ->Value; *Pt; Pt++) {
1825 Writef(fp, "%c", *Pt);
1832 WriteStr(fp, "\n#\n");
1837 if (!IsAvailableOnList(it8-> ValidKeywords, p->Keyword, NULL, NULL)) {
1839 #ifdef CMS_STRICT_CGATS
1840 WriteStr(fp, "KEYWORD\t\"");
1841 WriteStr(fp, p->Keyword);
1842 WriteStr(fp, "\"\n");
1845 AddAvailableProperty(it8, p->Keyword, WRITE_UNCOOKED);
1848 WriteStr(fp, p->Keyword);
1851 switch (p ->WriteAs) {
1853 case WRITE_UNCOOKED:
1854 Writef(fp, "\t%s", p ->Value);
1857 case WRITE_STRINGIFY:
1858 Writef(fp, "\t\"%s\"", p->Value );
1861 case WRITE_HEXADECIMAL:
1862 Writef(fp, "\t0x%X", satoi(p ->Value));
1866 Writef(fp, "\t0b%s", satob(p ->Value));
1870 Writef(fp, "\t\"%s,%s\"", p->Subkey, p->Value);
1873 default: SynError(it8, "Unknown write mode %d", p ->WriteAs);
1878 WriteStr (fp, "\n");
1884 // Writes the data format
1886 void WriteDataFormat(SAVESTREAM* fp, cmsIT8* it8)
1889 TABLE* t = GetTable(it8);
1891 if (!t -> DataFormat) return;
1893 WriteStr(fp, "BEGIN_DATA_FORMAT\n");
1895 nSamples = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_FIELDS"));
1897 if (nSamples <= t->nSamples) {
1899 for (i = 0; i < nSamples; i++) {
1901 WriteStr(fp, t->DataFormat[i]);
1902 WriteStr(fp, ((i == (nSamples - 1)) ? "\n" : "\t"));
1906 WriteStr (fp, "END_DATA_FORMAT\n");
1910 // Writes data array
1912 void WriteData(SAVESTREAM* fp, cmsIT8* it8)
1915 TABLE* t = GetTable(it8);
1917 if (!t->Data) return;
1919 WriteStr (fp, "BEGIN_DATA\n");
1921 nPatches = satoi(cmsIT8GetProperty(it8, "NUMBER_OF_SETS"));
1923 if (nPatches <= t->nPatches) {
1925 for (i = 0; i < nPatches; i++) {
1929 for (j = 0; j < t->nSamples; j++) {
1931 char* ptr = t->Data[i * t->nSamples + j];
1933 if (ptr == NULL) WriteStr(fp, "\"\"");
1935 // If value contains whitespace, enclose within quote
1937 if (strchr(ptr, ' ') != NULL) {
1947 WriteStr(fp, ((j == (t->nSamples - 1)) ? "\n" : "\t"));
1951 WriteStr (fp, "END_DATA\n");
1957 cmsBool CMSEXPORT cmsIT8SaveToFile(cmsHANDLE hIT8, const char* cFileName)
1961 cmsIT8* it8 = (cmsIT8*) hIT8;
1963 memset(&sd, 0, sizeof(sd));
1965 sd.stream = fopen(cFileName, "wte");
1966 if (!sd.stream) return FALSE;
1968 for (i=0; i < it8 ->TablesCount; i++) {
1972 if (cmsIT8SetTable(hIT8, i) < 0) goto Error;
1975 * Check for wrong data
1978 if (t->Data == NULL) goto Error;
1979 if (t->DataFormat == NULL) goto Error;
1981 WriteHeader(it8, &sd);
1982 WriteDataFormat(&sd, it8);
1983 WriteData(&sd, it8);
1986 if (fclose(sd.stream) != 0) return FALSE;
1997 cmsBool CMSEXPORT cmsIT8SaveToMem(cmsHANDLE hIT8, void *MemPtr, cmsUInt32Number* BytesNeeded)
2001 cmsIT8* it8 = (cmsIT8*) hIT8;
2003 memset(&sd, 0, sizeof(sd));
2006 sd.Base = (cmsUInt8Number*) MemPtr;
2011 if (sd.Base && (*BytesNeeded > 0)) {
2013 sd.Max = (*BytesNeeded) - 1; // Write to memory?
2016 sd.Max = 0; // Just counting the needed bytes
2018 for (i=0; i < it8 ->TablesCount; i++) {
2020 cmsIT8SetTable(hIT8, i);
2021 WriteHeader(it8, &sd);
2022 WriteDataFormat(&sd, it8);
2023 WriteData(&sd, it8);
2026 sd.Used++; // The \0 at the very end
2031 *BytesNeeded = sd.Used;
2037 // -------------------------------------------------------------- Higher level parsing
2040 cmsBool DataFormatSection(cmsIT8* it8)
2043 TABLE* t = GetTable(it8);
2045 InSymbol(it8); // Eats "BEGIN_DATA_FORMAT"
2048 while (it8->sy != SEND_DATA_FORMAT &&
2051 it8->sy != SSYNERROR) {
2053 if (it8->sy != SIDENT) {
2055 return SynError(it8, "Sample type expected");
2058 if (!SetDataFormat(it8, iField, StringPtr(it8->id))) return FALSE;
2066 Skip(it8, SEND_DATA_FORMAT);
2069 if (iField != t ->nSamples) {
2070 SynError(it8, "Count mismatch. NUMBER_OF_FIELDS was %d, found %d\n", t ->nSamples, iField);
2081 cmsBool DataSection (cmsIT8* it8)
2086 TABLE* t = GetTable(it8);
2088 InSymbol(it8); // Eats "BEGIN_DATA"
2092 if (!AllocateDataSet(it8)) return FALSE;
2095 while (it8->sy != SEND_DATA && it8->sy != SEOF)
2097 if (iField >= t -> nSamples) {
2103 if (it8->sy != SEND_DATA && it8->sy != SEOF) {
2108 // To keep very long data
2110 if (!SetData(it8, iSet, iField, StringPtr(it8->id)))
2115 if (!SetData(it8, iSet, iField, StringPtr(it8->str)))
2121 if (!GetVal(it8, Buffer, 255, "Sample data expected"))
2124 if (!SetData(it8, iSet, iField, Buffer))
2136 Skip(it8, SEND_DATA);
2139 // Check for data completion.
2141 if ((iSet+1) != t -> nPatches)
2142 return SynError(it8, "Count mismatch. NUMBER_OF_SETS was %d, found %d\n", t ->nPatches, iSet+1);
2151 cmsBool HeaderSection(cmsIT8* it8)
2153 char VarName[MAXID];
2154 char Buffer[MAXSTR];
2157 while (it8->sy != SEOF &&
2158 it8->sy != SSYNERROR &&
2159 it8->sy != SBEGIN_DATA_FORMAT &&
2160 it8->sy != SBEGIN_DATA) {
2163 switch (it8 -> sy) {
2167 if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
2168 if (!AddAvailableProperty(it8, Buffer, WRITE_UNCOOKED)) return FALSE;
2173 case SDATA_FORMAT_ID:
2175 if (!GetVal(it8, Buffer, MAXSTR-1, "Keyword expected")) return FALSE;
2176 if (!AddAvailableSampleID(it8, Buffer)) return FALSE;
2182 strncpy(VarName, StringPtr(it8->id), MAXID - 1);
2183 VarName[MAXID - 1] = 0;
2185 if (!IsAvailableOnList(it8->ValidKeywords, VarName, NULL, &Key)) {
2187 #ifdef CMS_STRICT_CGATS
2188 return SynError(it8, "Undefined keyword '%s'", VarName);
2190 Key = AddAvailableProperty(it8, VarName, WRITE_UNCOOKED);
2191 if (Key == NULL) return FALSE;
2196 if (!GetVal(it8, Buffer, MAXSTR - 1, "Property data expected")) return FALSE;
2198 if (Key->WriteAs != WRITE_PAIR) {
2199 AddToList(it8, &GetTable(it8)->HeaderList, VarName, NULL, Buffer,
2200 (it8->sy == SSTRING) ? WRITE_STRINGIFY : WRITE_UNCOOKED);
2205 if (it8->sy != SSTRING)
2206 return SynError(it8, "Invalid value '%s' for property '%s'.", Buffer, VarName);
2208 // chop the string as a list of "subkey, value" pairs, using ';' as a separator
2209 for (Subkey = Buffer; Subkey != NULL; Subkey = Nextkey)
2213 // identify token pair boundary
2214 Nextkey = (char*)strchr(Subkey, ';');
2218 // for each pair, split the subkey and the value
2219 Value = (char*)strrchr(Subkey, ',');
2221 return SynError(it8, "Invalid value for property '%s'.", VarName);
2223 // gobble the spaces before the coma, and the coma itself
2225 do *temp-- = '\0'; while (temp >= Subkey && *temp == ' ');
2227 // gobble any space at the right
2228 temp = Value + strlen(Value) - 1;
2229 while (*temp == ' ') *temp-- = '\0';
2231 // trim the strings from the left
2232 Subkey += strspn(Subkey, " ");
2233 Value += strspn(Value, " ");
2235 if (Subkey[0] == 0 || Value[0] == 0)
2236 return SynError(it8, "Invalid value for property '%s'.", VarName);
2237 AddToList(it8, &GetTable(it8)->HeaderList, VarName, Subkey, Value, WRITE_PAIR);
2248 return SynError(it8, "expected keyword or identifier");
2260 void ReadType(cmsIT8* it8, char* SheetTypePtr)
2262 cmsInt32Number cnt = 0;
2264 // First line is a very special case.
2266 while (isseparator(it8->ch))
2269 while (it8->ch != '\r' && it8 ->ch != '\n' && it8->ch != '\t' && it8 -> ch != 0) {
2272 *SheetTypePtr++= (char) it8 ->ch;
2281 cmsBool ParseIT8(cmsIT8* it8, cmsBool nosheet)
2283 char* SheetTypePtr = it8 ->Tab[0].SheetType;
2286 ReadType(it8, SheetTypePtr);
2293 while (it8-> sy != SEOF &&
2294 it8-> sy != SSYNERROR) {
2296 switch (it8 -> sy) {
2298 case SBEGIN_DATA_FORMAT:
2299 if (!DataFormatSection(it8)) return FALSE;
2304 if (!DataSection(it8)) return FALSE;
2306 if (it8 -> sy != SEOF) {
2309 it8 ->nTable = it8 ->TablesCount - 1;
2311 // Read sheet type if present. We only support identifier and string.
2312 // <ident> <eoln> is a type string
2313 // anything else, is not a type string
2316 if (it8 ->sy == SIDENT) {
2318 // May be a type sheet or may be a prop value statement. We cannot use insymbol in
2319 // this special case...
2320 while (isseparator(it8->ch))
2323 // If a newline is found, then this is a type string
2324 if (it8 ->ch == '\n' || it8->ch == '\r') {
2326 cmsIT8SetSheetType(it8, StringPtr(it8 ->id));
2331 // It is not. Just continue
2332 cmsIT8SetSheetType(it8, "");
2336 // Validate quoted strings
2337 if (it8 ->sy == SSTRING) {
2338 cmsIT8SetSheetType(it8, StringPtr(it8 ->str));
2351 if (!HeaderSection(it8)) return FALSE;
2356 return (it8 -> sy != SSYNERROR);
2361 // Init useful pointers
2364 void CookPointers(cmsIT8* it8)
2369 cmsUInt32Number nOldTable = it8->nTable;
2371 for (j = 0; j < it8->TablesCount; j++) {
2373 TABLE* t = it8->Tab + j;
2378 for (idField = 0; idField < t->nSamples; idField++)
2380 if (t->DataFormat == NULL) {
2381 SynError(it8, "Undefined DATA_FORMAT");
2385 Fld = t->DataFormat[idField];
2389 if (cmsstrcasecmp(Fld, "SAMPLE_ID") == 0) {
2391 t->SampleID = idField;
2394 // "LABEL" is an extension. It keeps references to forward tables
2396 if ((cmsstrcasecmp(Fld, "LABEL") == 0) || Fld[0] == '$') {
2398 // Search for table references...
2399 for (i = 0; i < t->nPatches; i++) {
2401 char* Label = GetData(it8, i, idField);
2407 // This is the label, search for a table containing
2410 for (k = 0; k < it8->TablesCount; k++) {
2412 TABLE* Table = it8->Tab + k;
2415 if (IsAvailableOnList(Table->HeaderList, Label, NULL, &p)) {
2417 // Available, keep type and table
2420 char* Type = p->Value;
2421 int nTable = (int)k;
2423 snprintf(Buffer, 255, "%s %d %s", Label, nTable, Type);
2425 SetData(it8, i, idField, Buffer);
2434 it8->nTable = nOldTable;
2437 // Try to infere if the file is a CGATS/IT8 file at all. Read first line
2438 // that should be something like some printable characters plus a \n
2439 // returns 0 if this is not like a CGATS, or an integer otherwise. This integer is the number of words in first line?
2441 int IsMyBlock(const cmsUInt8Number* Buffer, cmsUInt32Number n)
2443 int words = 1, space = 0, quot = 0;
2446 if (n < 10) return 0; // Too small
2451 for (i = 1; i < n; i++) {
2457 return ((quot == 1) || (words > 2)) ? 0 : words;
2467 if (Buffer[i] < 32) return 0;
2468 if (Buffer[i] > 127) return 0;
2480 cmsBool IsMyFile(const char* FileName)
2483 cmsUInt32Number Size;
2484 cmsUInt8Number Ptr[133];
2486 fp = fopen(FileName, "rte");
2488 cmsSignalError(0, cmsERROR_FILE, "File '%s' not found", FileName);
2492 Size = (cmsUInt32Number) fread(Ptr, 1, 132, fp);
2494 if (fclose(fp) != 0)
2499 return IsMyBlock(Ptr, Size);
2502 // ---------------------------------------------------------- Exported routines
2505 cmsHANDLE CMSEXPORT cmsIT8LoadFromMem(cmsContext ContextID, const void *Ptr, cmsUInt32Number len)
2511 _cmsAssert(Ptr != NULL);
2512 _cmsAssert(len != 0);
2514 type = IsMyBlock((const cmsUInt8Number*)Ptr, len);
2515 if (type == 0) return NULL;
2517 hIT8 = cmsIT8Alloc(ContextID);
2518 if (!hIT8) return NULL;
2520 it8 = (cmsIT8*) hIT8;
2521 it8 ->MemoryBlock = (char*) _cmsMalloc(ContextID, len + 1);
2522 if (it8->MemoryBlock == NULL)
2528 strncpy(it8 ->MemoryBlock, (const char*) Ptr, len);
2529 it8 ->MemoryBlock[len] = 0;
2531 strncpy(it8->FileStack[0]->FileName, "", cmsMAX_PATH-1);
2532 it8-> Source = it8 -> MemoryBlock;
2534 if (!ParseIT8(it8, type-1)) {
2543 _cmsFree(ContextID, it8->MemoryBlock);
2544 it8 -> MemoryBlock = NULL;
2552 cmsHANDLE CMSEXPORT cmsIT8LoadFromFile(cmsContext ContextID, const char* cFileName)
2559 _cmsAssert(cFileName != NULL);
2561 type = IsMyFile(cFileName);
2562 if (type == 0) return NULL;
2564 hIT8 = cmsIT8Alloc(ContextID);
2565 it8 = (cmsIT8*) hIT8;
2566 if (!hIT8) return NULL;
2569 it8 ->FileStack[0]->Stream = fopen(cFileName, "rte");
2571 if (!it8 ->FileStack[0]->Stream) {
2577 strncpy(it8->FileStack[0]->FileName, cFileName, cmsMAX_PATH-1);
2578 it8->FileStack[0]->FileName[cmsMAX_PATH-1] = 0;
2580 if (!ParseIT8(it8, type-1)) {
2582 fclose(it8 ->FileStack[0]->Stream);
2590 if (fclose(it8 ->FileStack[0]->Stream)!= 0) {
2599 int CMSEXPORT cmsIT8EnumDataFormat(cmsHANDLE hIT8, char ***SampleNames)
2601 cmsIT8* it8 = (cmsIT8*) hIT8;
2604 _cmsAssert(hIT8 != NULL);
2609 *SampleNames = t -> DataFormat;
2610 return t -> nSamples;
2614 cmsUInt32Number CMSEXPORT cmsIT8EnumProperties(cmsHANDLE hIT8, char ***PropertyNames)
2616 cmsIT8* it8 = (cmsIT8*) hIT8;
2622 _cmsAssert(hIT8 != NULL);
2626 // Pass#1 - count properties
2629 for (p = t -> HeaderList; p != NULL; p = p->Next) {
2634 Props = (char**)AllocChunk(it8, sizeof(char*) * n);
2635 if (Props != NULL) {
2637 // Pass#2 - Fill pointers
2639 for (p = t->HeaderList; p != NULL; p = p->Next) {
2640 Props[n++] = p->Keyword;
2644 *PropertyNames = Props;
2649 cmsUInt32Number CMSEXPORT cmsIT8EnumPropertyMulti(cmsHANDLE hIT8, const char* cProp, const char ***SubpropertyNames)
2651 cmsIT8* it8 = (cmsIT8*) hIT8;
2657 _cmsAssert(hIT8 != NULL);
2662 if(!IsAvailableOnList(t->HeaderList, cProp, NULL, &p)) {
2663 *SubpropertyNames = 0;
2667 // Pass#1 - count properties
2670 for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
2671 if(tmp->Subkey != NULL)
2676 Props = (const char **) AllocChunk(it8, sizeof(char *) * n);
2677 if (Props != NULL) {
2679 // Pass#2 - Fill pointers
2681 for (tmp = p; tmp != NULL; tmp = tmp->NextSubkey) {
2682 if (tmp->Subkey != NULL)
2683 Props[n++] = p->Subkey;
2687 *SubpropertyNames = Props;
2692 int LocatePatch(cmsIT8* it8, const char* cPatch)
2696 TABLE* t = GetTable(it8);
2698 for (i=0; i < t-> nPatches; i++) {
2700 data = GetData(it8, i, t->SampleID);
2704 if (cmsstrcasecmp(data, cPatch) == 0)
2709 // SynError(it8, "Couldn't find patch '%s'\n", cPatch);
2715 int LocateEmptyPatch(cmsIT8* it8)
2719 TABLE* t = GetTable(it8);
2721 for (i=0; i < t-> nPatches; i++) {
2723 data = GetData(it8, i, t->SampleID);
2734 int LocateSample(cmsIT8* it8, const char* cSample)
2738 TABLE* t = GetTable(it8);
2740 for (i=0; i < t->nSamples; i++) {
2742 fld = GetDataFormat(it8, i);
2744 if (cmsstrcasecmp(fld, cSample) == 0)
2754 int CMSEXPORT cmsIT8FindDataFormat(cmsHANDLE hIT8, const char* cSample)
2756 cmsIT8* it8 = (cmsIT8*) hIT8;
2758 _cmsAssert(hIT8 != NULL);
2760 return LocateSample(it8, cSample);
2765 const char* CMSEXPORT cmsIT8GetDataRowCol(cmsHANDLE hIT8, int row, int col)
2767 cmsIT8* it8 = (cmsIT8*) hIT8;
2769 _cmsAssert(hIT8 != NULL);
2771 return GetData(it8, row, col);
2775 cmsFloat64Number CMSEXPORT cmsIT8GetDataRowColDbl(cmsHANDLE hIT8, int row, int col)
2779 Buffer = cmsIT8GetDataRowCol(hIT8, row, col);
2781 if (Buffer == NULL) return 0.0;
2783 return ParseFloatNumber(Buffer);
2787 cmsBool CMSEXPORT cmsIT8SetDataRowCol(cmsHANDLE hIT8, int row, int col, const char* Val)
2789 cmsIT8* it8 = (cmsIT8*) hIT8;
2791 _cmsAssert(hIT8 != NULL);
2793 return SetData(it8, row, col, Val);
2797 cmsBool CMSEXPORT cmsIT8SetDataRowColDbl(cmsHANDLE hIT8, int row, int col, cmsFloat64Number Val)
2799 cmsIT8* it8 = (cmsIT8*) hIT8;
2802 _cmsAssert(hIT8 != NULL);
2804 snprintf(Buff, 255, it8->DoubleFormatter, Val);
2806 return SetData(it8, row, col, Buff);
2811 const char* CMSEXPORT cmsIT8GetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample)
2813 cmsIT8* it8 = (cmsIT8*) hIT8;
2816 _cmsAssert(hIT8 != NULL);
2818 iField = LocateSample(it8, cSample);
2823 iSet = LocatePatch(it8, cPatch);
2828 return GetData(it8, iSet, iField);
2832 cmsFloat64Number CMSEXPORT cmsIT8GetDataDbl(cmsHANDLE it8, const char* cPatch, const char* cSample)
2836 Buffer = cmsIT8GetData(it8, cPatch, cSample);
2838 return ParseFloatNumber(Buffer);
2843 cmsBool CMSEXPORT cmsIT8SetData(cmsHANDLE hIT8, const char* cPatch, const char* cSample, const char *Val)
2845 cmsIT8* it8 = (cmsIT8*) hIT8;
2849 _cmsAssert(hIT8 != NULL);
2853 iField = LocateSample(it8, cSample);
2858 if (t-> nPatches == 0) {
2860 if (!AllocateDataFormat(it8))
2863 if (!AllocateDataSet(it8))
2869 if (cmsstrcasecmp(cSample, "SAMPLE_ID") == 0) {
2871 iSet = LocateEmptyPatch(it8);
2873 return SynError(it8, "Couldn't add more patches '%s'\n", cPatch);
2876 iField = t -> SampleID;
2879 iSet = LocatePatch(it8, cPatch);
2885 return SetData(it8, iSet, iField, Val);
2889 cmsBool CMSEXPORT cmsIT8SetDataDbl(cmsHANDLE hIT8, const char* cPatch,
2890 const char* cSample,
2891 cmsFloat64Number Val)
2893 cmsIT8* it8 = (cmsIT8*) hIT8;
2896 _cmsAssert(hIT8 != NULL);
2898 snprintf(Buff, 255, it8->DoubleFormatter, Val);
2899 return cmsIT8SetData(hIT8, cPatch, cSample, Buff);
2902 // Buffer should get MAXSTR at least
2904 const char* CMSEXPORT cmsIT8GetPatchName(cmsHANDLE hIT8, int nPatch, char* buffer)
2906 cmsIT8* it8 = (cmsIT8*) hIT8;
2910 _cmsAssert(hIT8 != NULL);
2913 Data = GetData(it8, nPatch, t->SampleID);
2915 if (!Data) return NULL;
2916 if (!buffer) return Data;
2918 strncpy(buffer, Data, MAXSTR-1);
2919 buffer[MAXSTR-1] = 0;
2923 int CMSEXPORT cmsIT8GetPatchByName(cmsHANDLE hIT8, const char *cPatch)
2925 _cmsAssert(hIT8 != NULL);
2927 return LocatePatch((cmsIT8*)hIT8, cPatch);
2930 cmsUInt32Number CMSEXPORT cmsIT8TableCount(cmsHANDLE hIT8)
2932 cmsIT8* it8 = (cmsIT8*) hIT8;
2934 _cmsAssert(hIT8 != NULL);
2936 return it8 ->TablesCount;
2939 // This handles the "LABEL" extension.
2940 // Label, nTable, Type
2942 int CMSEXPORT cmsIT8SetTableByLabel(cmsHANDLE hIT8, const char* cSet, const char* cField, const char* ExpectedType)
2944 const char* cLabelFld;
2945 char Type[256], Label[256];
2946 cmsUInt32Number nTable;
2948 _cmsAssert(hIT8 != NULL);
2950 if (cField != NULL && *cField == 0)
2956 cLabelFld = cmsIT8GetData(hIT8, cSet, cField);
2957 if (!cLabelFld) return -1;
2959 if (sscanf(cLabelFld, "%255s %u %255s", Label, &nTable, Type) != 3)
2962 if (ExpectedType != NULL && *ExpectedType == 0)
2963 ExpectedType = NULL;
2967 if (cmsstrcasecmp(Type, ExpectedType) != 0) return -1;
2970 return cmsIT8SetTable(hIT8, nTable);
2974 cmsBool CMSEXPORT cmsIT8SetIndexColumn(cmsHANDLE hIT8, const char* cSample)
2976 cmsIT8* it8 = (cmsIT8*) hIT8;
2979 _cmsAssert(hIT8 != NULL);
2981 pos = LocateSample(it8, cSample);
2985 it8->Tab[it8->nTable].SampleID = pos;
2990 void CMSEXPORT cmsIT8DefineDblFormat(cmsHANDLE hIT8, const char* Formatter)
2992 cmsIT8* it8 = (cmsIT8*) hIT8;
2994 _cmsAssert(hIT8 != NULL);
2996 if (Formatter == NULL)
2997 strcpy(it8->DoubleFormatter, DEFAULT_DBL_FORMAT);
2999 strncpy(it8->DoubleFormatter, Formatter, sizeof(it8->DoubleFormatter));
3001 it8 ->DoubleFormatter[sizeof(it8 ->DoubleFormatter)-1] = 0;
3006 cmsBool ReadNumbers(cmsIT8* cube, int n, cmsFloat64Number* arr)
3010 for (i = 0; i < n; i++) {
3012 if (cube->sy == SINUM)
3013 arr[i] = cube->inum;
3015 if (cube->sy == SDNUM)
3016 arr[i] = cube->dnum;
3018 return SynError(cube, "Number expected");
3023 return CheckEOLN(cube);
3027 cmsBool ParseCube(cmsIT8* cube, cmsStage** Shaper, cmsStage** CLUT, char title[])
3029 cmsFloat64Number domain_min[3] = { 0, 0, 0 };
3030 cmsFloat64Number domain_max[3] = { 1.0, 1.0, 1.0 };
3031 cmsFloat64Number check_0_1[2] = { 0, 1.0 };
3032 int shaper_size = 0;
3038 while (cube->sy != SEOF) {
3041 // Set profile description
3044 if (!Check(cube, SSTRING, "Title string expected")) return FALSE;
3045 memcpy(title, StringPtr(cube->str), MAXSTR);
3046 title[MAXSTR - 1] = 0;
3053 if (!ReadNumbers(cube, 3, domain_min)) return FALSE;
3058 if (!ReadNumbers(cube, 3, domain_max)) return FALSE;
3064 if (!Check(cube, SINUM, "Shaper size expected")) return FALSE;
3065 shaper_size = cube->inum;
3072 if (!Check(cube, SINUM, "LUT size expected")) return FALSE;
3073 lut_size = cube->inum;
3077 // Range. If present, has to be 0..1.0
3078 case S_LUT1D_INPUT_RANGE:
3079 case S_LUT3D_INPUT_RANGE:
3081 if (!ReadNumbers(cube, 2, check_0_1)) return FALSE;
3082 if (check_0_1[0] != 0 || check_0_1[1] != 1.0) {
3083 return SynError(cube, "Unsupported format");
3092 case S_LUT_IN_VIDEO_RANGE:
3093 case S_LUT_OUT_VIDEO_RANGE:
3094 return SynError(cube, "Unsupported format");
3096 // Read and create tables
3100 if (shaper_size > 0) {
3102 cmsToneCurve* curves[3];
3103 cmsFloat32Number* shapers = (cmsFloat32Number*)_cmsMalloc(cube->ContextID, 3 * shaper_size * sizeof(cmsFloat32Number));
3104 if (shapers == NULL) return FALSE;
3106 for (i = 0; i < shaper_size; i++) {
3108 cmsFloat64Number nums[3];
3110 if (!ReadNumbers(cube, 3, nums)) return FALSE;
3112 shapers[i + 0] = (cmsFloat32Number) ((nums[0] - domain_min[0]) / (domain_max[0] - domain_min[0]));
3113 shapers[i + 1 * shaper_size] = (cmsFloat32Number) ((nums[1] - domain_min[1]) / (domain_max[1] - domain_min[1]));
3114 shapers[i + 2 * shaper_size] = (cmsFloat32Number) ((nums[2] - domain_min[2]) / (domain_max[2] - domain_min[2]));
3117 for (i = 0; i < 3; i++) {
3119 curves[i] = cmsBuildTabulatedToneCurveFloat(cube->ContextID, shaper_size,
3120 &shapers[i * shaper_size]);
3121 if (curves[i] == NULL) return FALSE;
3124 *Shaper = cmsStageAllocToneCurves(cube->ContextID, 3, curves);
3126 cmsFreeToneCurveTriple(curves);
3131 int nodes = lut_size * lut_size * lut_size;
3133 cmsFloat32Number* lut_table = _cmsMalloc(cube->ContextID, nodes * 3 * sizeof(cmsFloat32Number));
3134 if (lut_table == NULL) return FALSE;
3136 for (i = 0; i < nodes; i++) {
3138 cmsFloat64Number nums[3];
3140 if (!ReadNumbers(cube, 3, nums)) return FALSE;
3142 lut_table[i * 3 + 2] = (cmsFloat32Number) ((nums[0] - domain_min[0]) / (domain_max[0] - domain_min[0]));
3143 lut_table[i * 3 + 1] = (cmsFloat32Number) ((nums[1] - domain_min[1]) / (domain_max[1] - domain_min[1]));
3144 lut_table[i * 3 + 0] = (cmsFloat32Number) ((nums[2] - domain_min[2]) / (domain_max[2] - domain_min[2]));
3147 *CLUT = cmsStageAllocCLutFloat(cube->ContextID, lut_size, 3, 3, lut_table);
3148 _cmsFree(cube->ContextID, lut_table);
3151 if (!Check(cube, SEOF, "Extra symbols found in file")) return FALSE;
3158 // Share the parser to read .cube format and create RGB devicelink profiles
3159 cmsHPROFILE CMSEXPORT cmsCreateDeviceLinkFromCubeFileTHR(cmsContext ContextID, const char* cFileName)
3161 cmsHPROFILE hProfile = NULL;
3162 cmsIT8* cube = NULL;
3163 cmsPipeline* Pipeline = NULL;
3164 cmsStage* CLUT = NULL;
3165 cmsStage* Shaper = NULL;
3166 cmsMLU* DescriptionMLU = NULL;
3169 _cmsAssert(cFileName != NULL);
3171 cube = (cmsIT8*) cmsIT8Alloc(ContextID);
3172 if (!cube) return NULL;
3174 cube->IsCUBE = TRUE;
3175 cube->FileStack[0]->Stream = fopen(cFileName, "rt");
3177 if (!cube->FileStack[0]->Stream) goto Done;
3179 strncpy(cube->FileStack[0]->FileName, cFileName, cmsMAX_PATH - 1);
3180 cube->FileStack[0]->FileName[cmsMAX_PATH - 1] = 0;
3182 if (!ParseCube(cube, &Shaper, &CLUT, title)) goto Done;
3184 // Success on parsing, let's create the profile
3185 hProfile = cmsCreateProfilePlaceholder(ContextID);
3186 if (!hProfile) goto Done;
3188 cmsSetProfileVersion(hProfile, 4.4);
3190 cmsSetDeviceClass(hProfile, cmsSigLinkClass);
3191 cmsSetColorSpace(hProfile, cmsSigRgbData);
3192 cmsSetPCS(hProfile, cmsSigRgbData);
3194 cmsSetHeaderRenderingIntent(hProfile, INTENT_PERCEPTUAL);
3196 // Creates a Pipeline to hold CLUT and shaper
3197 Pipeline = cmsPipelineAlloc(ContextID, 3, 3);
3198 if (Pipeline == NULL) goto Done;
3200 // Populates the pipeline
3201 if (Shaper != NULL) {
3202 if (!cmsPipelineInsertStage(Pipeline, cmsAT_BEGIN, Shaper))
3207 if (!cmsPipelineInsertStage(Pipeline, cmsAT_END, CLUT))
3211 // Propagate the description. We put no copyright because we know
3212 // nothing on the copyrighted state of the .cube
3213 DescriptionMLU = cmsMLUalloc(ContextID, 1);
3214 if (!cmsMLUsetUTF8(DescriptionMLU, cmsNoLanguage, cmsNoCountry, title)) goto Done;
3217 if (!cmsWriteTag(hProfile, cmsSigProfileDescriptionTag, DescriptionMLU)) goto Done;
3218 if (!cmsWriteTag(hProfile, cmsSigAToB0Tag, (void*)Pipeline)) goto Done;
3222 if (DescriptionMLU != NULL)
3223 cmsMLUfree(DescriptionMLU);
3225 if (Pipeline != NULL)
3226 cmsPipelineFree(Pipeline);
3228 cmsIT8Free((cmsHANDLE) cube);
3233 cmsHPROFILE CMSEXPORT cmsCreateDeviceLinkFromCubeFile(const char* cFileName)
3235 return cmsCreateDeviceLinkFromCubeFileTHR(NULL, cFileName);