• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //---------------------------------------------------------------------------------
2 //
3 //  Little Color Management System
4 //  Copyright (c) 1998-2023 Marti Maria Saguer
5 //
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:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
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.
23 //
24 //---------------------------------------------------------------------------------
25 //
26 
27 #include "lcms2_internal.h"
28 
29 // Generic I/O, tag dictionary management, profile struct
30 
31 // IOhandlers are abstractions used by littleCMS to read from whatever file, stream,
32 // memory block or any storage. Each IOhandler provides implementations for read,
33 // write, seek and tell functions. LittleCMS code deals with IO across those objects.
34 // In this way, is easier to add support for new storage media.
35 
36 // NULL stream, for taking care of used space -------------------------------------
37 
38 // NULL IOhandler basically does nothing but keep track on how many bytes have been
39 // written. This is handy when creating profiles, where the file size is needed in the
40 // header. Then, whole profile is serialized across NULL IOhandler and a second pass
41 // writes the bytes to the pertinent IOhandler.
42 
43 typedef struct {
44     cmsUInt32Number Pointer;         // Points to current location
45 } FILENULL;
46 
47 static
NULLRead(cmsIOHANDLER * iohandler,void * Buffer,cmsUInt32Number size,cmsUInt32Number count)48 cmsUInt32Number NULLRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count)
49 {
50     FILENULL* ResData = (FILENULL*) iohandler ->stream;
51 
52     cmsUInt32Number len = size * count;
53     ResData -> Pointer += len;
54     return count;
55 
56     cmsUNUSED_PARAMETER(Buffer);
57 }
58 
59 static
NULLSeek(cmsIOHANDLER * iohandler,cmsUInt32Number offset)60 cmsBool  NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset)
61 {
62     FILENULL* ResData = (FILENULL*) iohandler ->stream;
63 
64     ResData ->Pointer = offset;
65     return TRUE;
66 }
67 
68 static
NULLTell(cmsIOHANDLER * iohandler)69 cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler)
70 {
71     FILENULL* ResData = (FILENULL*) iohandler ->stream;
72     return ResData -> Pointer;
73 }
74 
75 static
NULLWrite(cmsIOHANDLER * iohandler,cmsUInt32Number size,const void * Ptr)76 cmsBool  NULLWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void *Ptr)
77 {
78     FILENULL* ResData = (FILENULL*) iohandler ->stream;
79 
80     ResData ->Pointer += size;
81     if (ResData ->Pointer > iohandler->UsedSpace)
82         iohandler->UsedSpace = ResData ->Pointer;
83 
84     return TRUE;
85 
86     cmsUNUSED_PARAMETER(Ptr);
87 }
88 
89 static
NULLClose(cmsIOHANDLER * iohandler)90 cmsBool  NULLClose(cmsIOHANDLER* iohandler)
91 {
92     FILENULL* ResData = (FILENULL*) iohandler ->stream;
93 
94     _cmsFree(iohandler ->ContextID, ResData);
95     _cmsFree(iohandler ->ContextID, iohandler);
96     return TRUE;
97 }
98 
99 // The NULL IOhandler creator
cmsOpenIOhandlerFromNULL(cmsContext ContextID)100 cmsIOHANDLER*  CMSEXPORT cmsOpenIOhandlerFromNULL(cmsContext ContextID)
101 {
102     struct _cms_io_handler* iohandler = NULL;
103     FILENULL* fm = NULL;
104 
105     iohandler = (struct _cms_io_handler*) _cmsMallocZero(ContextID, sizeof(struct _cms_io_handler));
106     if (iohandler == NULL) return NULL;
107 
108     fm = (FILENULL*) _cmsMallocZero(ContextID, sizeof(FILENULL));
109     if (fm == NULL) goto Error;
110 
111     fm ->Pointer = 0;
112 
113     iohandler ->ContextID = ContextID;
114     iohandler ->stream  = (void*) fm;
115     iohandler ->UsedSpace = 0;
116     iohandler ->ReportedSize = 0;
117     iohandler ->PhysicalFile[0] = 0;
118 
119     iohandler ->Read    = NULLRead;
120     iohandler ->Seek    = NULLSeek;
121     iohandler ->Close   = NULLClose;
122     iohandler ->Tell    = NULLTell;
123     iohandler ->Write   = NULLWrite;
124 
125     return iohandler;
126 
127 Error:
128     if (iohandler) _cmsFree(ContextID, iohandler);
129     return NULL;
130 
131 }
132 
133 
134 // Memory-based stream --------------------------------------------------------------
135 
136 // Those functions implements an iohandler which takes a block of memory as storage medium.
137 
138 typedef struct {
139     cmsUInt8Number* Block;    // Points to allocated memory
140     cmsUInt32Number Size;     // Size of allocated memory
141     cmsUInt32Number Pointer;  // Points to current location
142     int FreeBlockOnClose;     // As title
143 
144 } FILEMEM;
145 
146 static
MemoryRead(struct _cms_io_handler * iohandler,void * Buffer,cmsUInt32Number size,cmsUInt32Number count)147 cmsUInt32Number MemoryRead(struct _cms_io_handler* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count)
148 {
149     FILEMEM* ResData = (FILEMEM*) iohandler ->stream;
150     cmsUInt8Number* Ptr;
151     cmsUInt32Number len = size * count;
152 
153     if (ResData -> Pointer + len > ResData -> Size){
154 
155         len = (ResData -> Size - ResData -> Pointer);
156         cmsSignalError(iohandler ->ContextID, cmsERROR_READ, "Read from memory error. Got %d bytes, block should be of %d bytes", len, count * size);
157         return 0;
158     }
159 
160     Ptr  = ResData -> Block;
161     Ptr += ResData -> Pointer;
162     memmove(Buffer, Ptr, len);
163     ResData -> Pointer += len;
164 
165     return count;
166 }
167 
168 // SEEK_CUR is assumed
169 static
MemorySeek(struct _cms_io_handler * iohandler,cmsUInt32Number offset)170 cmsBool  MemorySeek(struct _cms_io_handler* iohandler, cmsUInt32Number offset)
171 {
172     FILEMEM* ResData = (FILEMEM*) iohandler ->stream;
173 
174     if (offset > ResData ->Size) {
175         cmsSignalError(iohandler ->ContextID, cmsERROR_SEEK,  "Too few data; probably corrupted profile");
176         return FALSE;
177     }
178 
179     ResData ->Pointer = offset;
180     return TRUE;
181 }
182 
183 // Tell for memory
184 static
MemoryTell(struct _cms_io_handler * iohandler)185 cmsUInt32Number MemoryTell(struct _cms_io_handler* iohandler)
186 {
187     FILEMEM* ResData = (FILEMEM*) iohandler ->stream;
188 
189     if (ResData == NULL) return 0;
190     return ResData -> Pointer;
191 }
192 
193 
194 // Writes data to memory, also keeps used space for further reference.
195 static
MemoryWrite(struct _cms_io_handler * iohandler,cmsUInt32Number size,const void * Ptr)196 cmsBool MemoryWrite(struct _cms_io_handler* iohandler, cmsUInt32Number size, const void *Ptr)
197 {
198     FILEMEM* ResData = (FILEMEM*) iohandler ->stream;
199 
200     if (ResData == NULL) return FALSE; // Housekeeping
201 
202     // Check for available space. Clip.
203     if (ResData->Pointer + size > ResData->Size) {
204         size = ResData ->Size - ResData->Pointer;
205     }
206 
207     if (size == 0) return TRUE;     // Write zero bytes is ok, but does nothing
208 
209     memmove(ResData ->Block + ResData ->Pointer, Ptr, size);
210     ResData ->Pointer += size;
211 
212     if (ResData ->Pointer > iohandler->UsedSpace)
213         iohandler->UsedSpace = ResData ->Pointer;
214 
215     return TRUE;
216 }
217 
218 
219 static
MemoryClose(struct _cms_io_handler * iohandler)220 cmsBool  MemoryClose(struct _cms_io_handler* iohandler)
221 {
222     FILEMEM* ResData = (FILEMEM*) iohandler ->stream;
223 
224     if (ResData ->FreeBlockOnClose) {
225 
226         if (ResData ->Block) _cmsFree(iohandler ->ContextID, ResData ->Block);
227     }
228 
229     _cmsFree(iohandler ->ContextID, ResData);
230     _cmsFree(iohandler ->ContextID, iohandler);
231 
232     return TRUE;
233 }
234 
235 // Create a iohandler for memory block. AccessMode=='r' assumes the iohandler is going to read, and makes
236 // a copy of the memory block for letting user to free the memory after invoking open profile. In write
237 // mode ("w"), Buffer points to the begin of memory block to be written.
cmsOpenIOhandlerFromMem(cmsContext ContextID,void * Buffer,cmsUInt32Number size,const char * AccessMode)238 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromMem(cmsContext ContextID, void *Buffer, cmsUInt32Number size, const char* AccessMode)
239 {
240     cmsIOHANDLER* iohandler = NULL;
241     FILEMEM* fm = NULL;
242 
243     _cmsAssert(AccessMode != NULL);
244 
245     iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
246     if (iohandler == NULL) return NULL;
247 
248     switch (*AccessMode) {
249 
250     case 'r':
251         fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM));
252         if (fm == NULL) goto Error;
253 
254         if (Buffer == NULL) {
255             cmsSignalError(ContextID, cmsERROR_READ, "Couldn't read profile from NULL pointer");
256             goto Error;
257         }
258 
259         fm ->Block = (cmsUInt8Number*) _cmsMalloc(ContextID, size);
260         if (fm ->Block == NULL) {
261 
262             _cmsFree(ContextID, fm);
263             _cmsFree(ContextID, iohandler);
264             cmsSignalError(ContextID, cmsERROR_READ, "Couldn't allocate %ld bytes for profile", (long) size);
265             return NULL;
266         }
267 
268 
269         memmove(fm->Block, Buffer, size);
270         fm ->FreeBlockOnClose = TRUE;
271         fm ->Size    = size;
272         fm ->Pointer = 0;
273         iohandler -> ReportedSize = size;
274         break;
275 
276     case 'w':
277         fm = (FILEMEM*) _cmsMallocZero(ContextID, sizeof(FILEMEM));
278         if (fm == NULL) goto Error;
279 
280         fm ->Block = (cmsUInt8Number*) Buffer;
281         fm ->FreeBlockOnClose = FALSE;
282         fm ->Size    = size;
283         fm ->Pointer = 0;
284         iohandler -> ReportedSize = 0;
285         break;
286 
287     default:
288         cmsSignalError(ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown access mode '%c'", *AccessMode);
289         return NULL;
290     }
291 
292     iohandler ->ContextID = ContextID;
293     iohandler ->stream  = (void*) fm;
294     iohandler ->UsedSpace = 0;
295     iohandler ->PhysicalFile[0] = 0;
296 
297     iohandler ->Read    = MemoryRead;
298     iohandler ->Seek    = MemorySeek;
299     iohandler ->Close   = MemoryClose;
300     iohandler ->Tell    = MemoryTell;
301     iohandler ->Write   = MemoryWrite;
302 
303     return iohandler;
304 
305 Error:
306     if (fm) _cmsFree(ContextID, fm);
307     if (iohandler) _cmsFree(ContextID, iohandler);
308     return NULL;
309 }
310 
311 // File-based stream -------------------------------------------------------
312 
313 // Read count elements of size bytes each. Return number of elements read
314 static
FileRead(cmsIOHANDLER * iohandler,void * Buffer,cmsUInt32Number size,cmsUInt32Number count)315 cmsUInt32Number FileRead(cmsIOHANDLER* iohandler, void *Buffer, cmsUInt32Number size, cmsUInt32Number count)
316 {
317     cmsUInt32Number nReaded = (cmsUInt32Number) fread(Buffer, size, count, (FILE*) iohandler->stream);
318 
319     if (nReaded != count) {
320             cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Read error. Got %d bytes, block should be of %d bytes", nReaded * size, count * size);
321             return 0;
322     }
323 
324     return nReaded;
325 }
326 
327 // Position file pointer in the file
328 static
FileSeek(cmsIOHANDLER * iohandler,cmsUInt32Number offset)329 cmsBool  FileSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset)
330 {
331     if (fseek((FILE*) iohandler ->stream, (long) offset, SEEK_SET) != 0) {
332 
333        cmsSignalError(iohandler ->ContextID, cmsERROR_FILE, "Seek error; probably corrupted file");
334        return FALSE;
335     }
336 
337     return TRUE;
338 }
339 
340 // Returns file pointer position or 0 on error, which is also a valid position.
341 static
FileTell(cmsIOHANDLER * iohandler)342 cmsUInt32Number FileTell(cmsIOHANDLER* iohandler)
343 {
344     long t = ftell((FILE*)iohandler ->stream);
345     if (t == -1L) {
346         cmsSignalError(iohandler->ContextID, cmsERROR_FILE, "Tell error; probably corrupted file");
347         return 0;
348     }
349 
350     return (cmsUInt32Number)t;
351 }
352 
353 // Writes data to stream, also keeps used space for further reference. Returns TRUE on success, FALSE on error
354 static
FileWrite(cmsIOHANDLER * iohandler,cmsUInt32Number size,const void * Buffer)355 cmsBool  FileWrite(cmsIOHANDLER* iohandler, cmsUInt32Number size, const void* Buffer)
356 {
357     if (size == 0) return TRUE;  // We allow to write 0 bytes, but nothing is written
358 
359     iohandler->UsedSpace += size;
360     return (fwrite(Buffer, size, 1, (FILE*)iohandler->stream) == 1);
361 }
362 
363 // Closes the file
364 static
FileClose(cmsIOHANDLER * iohandler)365 cmsBool  FileClose(cmsIOHANDLER* iohandler)
366 {
367     if (fclose((FILE*) iohandler ->stream) != 0) return FALSE;
368     _cmsFree(iohandler ->ContextID, iohandler);
369     return TRUE;
370 }
371 
372 // Create a iohandler for disk based files.
cmsOpenIOhandlerFromFile(cmsContext ContextID,const char * FileName,const char * AccessMode)373 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromFile(cmsContext ContextID, const char* FileName, const char* AccessMode)
374 {
375     cmsIOHANDLER* iohandler = NULL;
376     FILE* fm = NULL;
377     cmsInt32Number fileLen;
378     char mode[4] = { 0,0,0,0 };
379 
380     _cmsAssert(FileName != NULL);
381     _cmsAssert(AccessMode != NULL);
382 
383     iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
384     if (iohandler == NULL) return NULL;
385 
386     // Validate access mode
387     while (*AccessMode) {
388 
389         switch (*AccessMode)
390         {
391         case 'r':
392         case 'w':
393 
394             if (mode[0] == 0) {
395                 mode[0] = *AccessMode;
396                 mode[1] = 'b';
397             }
398             else {
399                 _cmsFree(ContextID, iohandler);
400                 cmsSignalError(ContextID, cmsERROR_FILE, "Access mode already specified '%c'", *AccessMode);
401                 return NULL;
402             }
403             break;
404 
405         // Close on exec. Not all runtime supports that. Up to the caller to decide.
406         case 'e':
407             mode[2] = 'e';
408             break;
409 
410         default:
411             _cmsFree(ContextID, iohandler);
412             cmsSignalError(ContextID, cmsERROR_FILE, "Wrong access mode '%c'", *AccessMode);
413             return NULL;
414         }
415 
416         AccessMode++;
417     }
418 
419     switch (mode[0]) {
420 
421     case 'r':
422         fm = fopen(FileName, mode);
423         if (fm == NULL) {
424             _cmsFree(ContextID, iohandler);
425              cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName);
426             return NULL;
427         }
428         fileLen = (cmsInt32Number)cmsfilelength(fm);
429         if (fileLen < 0)
430         {
431             fclose(fm);
432             _cmsFree(ContextID, iohandler);
433             cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of file '%s'", FileName);
434             return NULL;
435         }
436         iohandler -> ReportedSize = (cmsUInt32Number) fileLen;
437         break;
438 
439     case 'w':
440         fm = fopen(FileName, mode);
441         if (fm == NULL) {
442             _cmsFree(ContextID, iohandler);
443              cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName);
444             return NULL;
445         }
446         iohandler -> ReportedSize = 0;
447         break;
448 
449     default:
450         _cmsFree(ContextID, iohandler);   // Would never reach
451         return NULL;
452     }
453 
454     iohandler ->ContextID = ContextID;
455     iohandler ->stream = (void*) fm;
456     iohandler ->UsedSpace = 0;
457 
458     // Keep track of the original file
459     strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1);
460     iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0;
461 
462     iohandler ->Read    = FileRead;
463     iohandler ->Seek    = FileSeek;
464     iohandler ->Close   = FileClose;
465     iohandler ->Tell    = FileTell;
466     iohandler ->Write   = FileWrite;
467 
468     return iohandler;
469 }
470 
471 // Create a iohandler for stream based files
cmsOpenIOhandlerFromStream(cmsContext ContextID,FILE * Stream)472 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream)
473 {
474     cmsIOHANDLER* iohandler = NULL;
475     cmsInt32Number fileSize;
476 
477     fileSize = (cmsInt32Number)cmsfilelength(Stream);
478     if (fileSize < 0)
479     {
480         cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of stream");
481         return NULL;
482     }
483 
484     iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
485     if (iohandler == NULL) return NULL;
486 
487     iohandler -> ContextID = ContextID;
488     iohandler -> stream = (void*) Stream;
489     iohandler -> UsedSpace = 0;
490     iohandler -> ReportedSize = (cmsUInt32Number) fileSize;
491     iohandler -> PhysicalFile[0] = 0;
492 
493     iohandler ->Read    = FileRead;
494     iohandler ->Seek    = FileSeek;
495     iohandler ->Close   = FileClose;
496     iohandler ->Tell    = FileTell;
497     iohandler ->Write   = FileWrite;
498 
499     return iohandler;
500 }
501 
502 
503 
504 // Close an open IO handler
cmsCloseIOhandler(cmsIOHANDLER * io)505 cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io)
506 {
507     return io -> Close(io);
508 }
509 
510 // -------------------------------------------------------------------------------------------------------
511 
cmsGetProfileIOhandler(cmsHPROFILE hProfile)512 cmsIOHANDLER* CMSEXPORT cmsGetProfileIOhandler(cmsHPROFILE hProfile)
513 {
514     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile;
515 
516     if (Icc == NULL) return NULL;
517     return Icc->IOhandler;
518 }
519 
520 #ifdef _WIN32_WCE
521 time_t wceex_time(time_t *timer);
522 struct tm * wceex_gmtime(const time_t *timer);
523 
524 #define time wceex_time
525 #define gmtime wceex_gmtime
526 #endif
527 
528 // Creates an empty structure holding all required parameters
cmsCreateProfilePlaceholder(cmsContext ContextID)529 cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID)
530 {
531     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE));
532     if (Icc == NULL) return NULL;
533 
534     Icc ->ContextID = ContextID;
535 
536     // Set it to empty
537     Icc -> TagCount   = 0;
538 
539     // Set default version
540     Icc ->Version =  0x02100000;
541 
542     // Set default device class
543     Icc->DeviceClass = cmsSigDisplayClass;
544 
545     // Set creation date/time
546     if (!_cmsGetTime(&Icc->Created))
547         goto Error;
548 
549     // Create a mutex if the user provided proper plugin. NULL otherwise
550     Icc ->UsrMutex = _cmsCreateMutex(ContextID);
551 
552     // Return the handle
553     return (cmsHPROFILE) Icc;
554 
555 Error:
556     _cmsFree(ContextID, Icc);
557     return NULL;
558 }
559 
cmsGetProfileContextID(cmsHPROFILE hProfile)560 cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile)
561 {
562      _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
563 
564     if (Icc == NULL) return NULL;
565     return Icc -> ContextID;
566 }
567 
568 
569 // Return the number of tags
cmsGetTagCount(cmsHPROFILE hProfile)570 cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile)
571 {
572     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
573     if (Icc == NULL) return -1;
574 
575     return  (cmsInt32Number) Icc->TagCount;
576 }
577 
578 // Return the tag signature of a given tag number
cmsGetTagSignature(cmsHPROFILE hProfile,cmsUInt32Number n)579 cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n)
580 {
581     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
582 
583     if (n > Icc->TagCount) return (cmsTagSignature) 0;  // Mark as not available
584     if (n >= MAX_TABLE_TAG) return (cmsTagSignature) 0; // As double check
585 
586     return Icc ->TagNames[n];
587 }
588 
589 
590 static
SearchOneTag(_cmsICCPROFILE * Profile,cmsTagSignature sig)591 int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig)
592 {
593     int i;
594 
595     for (i=0; i < (int) Profile -> TagCount; i++) {
596 
597         if (sig == Profile -> TagNames[i])
598             return i;
599     }
600 
601     return -1;
602 }
603 
604 // Search for a specific tag in tag dictionary. Returns position or -1 if tag not found.
605 // If followlinks is turned on, then the position of the linked tag is returned
_cmsSearchTag(_cmsICCPROFILE * Icc,cmsTagSignature sig,cmsBool lFollowLinks)606 int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks)
607 {
608     int n;
609     cmsTagSignature LinkedSig;
610 
611     do {
612 
613         // Search for given tag in ICC profile directory
614         n = SearchOneTag(Icc, sig);
615         if (n < 0)
616             return -1;        // Not found
617 
618         if (!lFollowLinks)
619             return n;         // Found, don't follow links
620 
621         // Is this a linked tag?
622         LinkedSig = Icc ->TagLinked[n];
623 
624         // Yes, follow link
625         if (LinkedSig != (cmsTagSignature) 0) {
626             // fix bug mantis id#0055942
627             // assume that TRCTag and ColorantTag can't be linked.
628             // Xiaochuan Liu 2014-04-23
629             if ((sig == cmsSigRedTRCTag || sig == cmsSigGreenTRCTag || sig == cmsSigBlueTRCTag) &&
630                 (LinkedSig == cmsSigRedColorantTag || LinkedSig == cmsSigGreenColorantTag || LinkedSig == cmsSigBlueColorantTag))
631             {
632                 return n;
633             }
634             sig = LinkedSig;
635         }
636 
637     } while (LinkedSig != (cmsTagSignature) 0);
638 
639     return n;
640 }
641 
642 // Deletes a tag entry
643 
644 static
_cmsDeleteTagByPos(_cmsICCPROFILE * Icc,int i)645 void _cmsDeleteTagByPos(_cmsICCPROFILE* Icc, int i)
646 {
647     _cmsAssert(Icc != NULL);
648     _cmsAssert(i >= 0);
649 
650 
651     if (Icc -> TagPtrs[i] != NULL) {
652 
653         // Free previous version
654         if (Icc ->TagSaveAsRaw[i]) {
655             _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]);
656         }
657         else {
658             cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i];
659 
660             if (TypeHandler != NULL) {
661 
662                 cmsTagTypeHandler LocalTypeHandler = *TypeHandler;
663                 LocalTypeHandler.ContextID = Icc ->ContextID;              // As an additional parameter
664                 LocalTypeHandler.ICCVersion = Icc ->Version;
665                 LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]);
666                 Icc ->TagPtrs[i] = NULL;
667             }
668         }
669 
670     }
671 }
672 
673 
674 // Creates a new tag entry
675 static
_cmsNewTag(_cmsICCPROFILE * Icc,cmsTagSignature sig,int * NewPos)676 cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos)
677 {
678     int i;
679 
680     // Search for the tag
681     i = _cmsSearchTag(Icc, sig, FALSE);
682     if (i >= 0) {
683 
684         // Already exists? delete it
685         _cmsDeleteTagByPos(Icc, i);
686         *NewPos = i;
687     }
688     else  {
689 
690         // No, make a new one
691         if (Icc -> TagCount >= MAX_TABLE_TAG) {
692             cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG);
693             return FALSE;
694         }
695 
696         *NewPos = (int) Icc ->TagCount;
697         Icc -> TagCount++;
698     }
699 
700     return TRUE;
701 }
702 
703 
704 // Check existence
cmsIsTag(cmsHPROFILE hProfile,cmsTagSignature sig)705 cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig)
706 {
707        _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) (void*) hProfile;
708        return _cmsSearchTag(Icc, sig, FALSE) >= 0;
709 }
710 
711 
712 
713 // Checks for link compatibility
714 static
CompatibleTypes(const cmsTagDescriptor * desc1,const cmsTagDescriptor * desc2)715 cmsBool CompatibleTypes(const cmsTagDescriptor* desc1, const cmsTagDescriptor* desc2)
716 {
717     cmsUInt32Number i;
718 
719     if (desc1 == NULL || desc2 == NULL) return FALSE;
720 
721     if (desc1->nSupportedTypes != desc2->nSupportedTypes) return FALSE;
722     if (desc1->ElemCount != desc2->ElemCount) return FALSE;
723 
724     for (i = 0; i < desc1->nSupportedTypes; i++)
725     {
726         if (desc1->SupportedTypes[i] != desc2->SupportedTypes[i]) return FALSE;
727     }
728 
729     return TRUE;
730 }
731 
732 // Enforces that the profile version is per. spec.
733 // Operates on the big endian bytes from the profile.
734 // Called before converting to platform endianness.
735 // Byte 0 is BCD major version, so max 9.
736 // Byte 1 is 2 BCD digits, one per nibble.
737 // Reserved bytes 2 & 3 must be 0.
738 static
_validatedVersion(cmsUInt32Number DWord)739 cmsUInt32Number _validatedVersion(cmsUInt32Number DWord)
740 {
741     cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;
742     cmsUInt8Number temp1;
743     cmsUInt8Number temp2;
744 
745     if (*pByte > 0x09) *pByte = (cmsUInt8Number) 0x09;
746     temp1 = (cmsUInt8Number) (*(pByte+1) & 0xf0);
747     temp2 = (cmsUInt8Number) (*(pByte+1) & 0x0f);
748     if (temp1 > 0x90U) temp1 = 0x90U;
749     if (temp2 > 0x09U) temp2 = 0x09U;
750     *(pByte+1) = (cmsUInt8Number)(temp1 | temp2);
751     *(pByte+2) = (cmsUInt8Number)0;
752     *(pByte+3) = (cmsUInt8Number)0;
753 
754     return DWord;
755 }
756 
757 // Check device class
758 static
validDeviceClass(cmsProfileClassSignature cl)759 cmsBool validDeviceClass(cmsProfileClassSignature cl)
760 {
761     if ((int)cl == 0) return TRUE; // We allow zero because older lcms versions defaulted to that.
762 
763     switch (cl)
764     {
765     case cmsSigInputClass:
766     case cmsSigDisplayClass:
767     case cmsSigOutputClass:
768     case cmsSigLinkClass:
769     case cmsSigAbstractClass:
770     case cmsSigColorSpaceClass:
771     case cmsSigNamedColorClass:
772         return TRUE;
773 
774     default:
775         return FALSE;
776     }
777 
778 }
779 
780 // Read profile header and validate it
_cmsReadHeader(_cmsICCPROFILE * Icc)781 cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc)
782 {
783     cmsTagEntry Tag;
784     cmsICCHeader Header;
785     cmsUInt32Number i, j;
786     cmsUInt32Number HeaderSize;
787     cmsIOHANDLER* io = Icc ->IOhandler;
788     cmsUInt32Number TagCount;
789 
790 
791     // Read the header
792     if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) {
793         return FALSE;
794     }
795 
796     // Validate file as an ICC profile
797     if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) {
798         cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature");
799         return FALSE;
800     }
801 
802     // Adjust endianness of the used parameters
803     Icc -> DeviceClass     = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass);
804     Icc -> ColorSpace      = (cmsColorSpaceSignature)   _cmsAdjustEndianess32(Header.colorSpace);
805     Icc -> PCS             = (cmsColorSpaceSignature)   _cmsAdjustEndianess32(Header.pcs);
806 
807     Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent);
808     Icc -> flags           = _cmsAdjustEndianess32(Header.flags);
809     Icc -> manufacturer    = _cmsAdjustEndianess32(Header.manufacturer);
810     Icc -> model           = _cmsAdjustEndianess32(Header.model);
811     Icc -> creator         = _cmsAdjustEndianess32(Header.creator);
812 
813     _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes);
814     Icc -> Version         = _cmsAdjustEndianess32(_validatedVersion(Header.version));
815 
816     if (Icc->Version > 0x5000000) {
817         cmsSignalError(Icc->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported profile version '0x%x'", Icc->Version);
818         return FALSE;
819     }
820 
821     if (!validDeviceClass(Icc->DeviceClass)) {
822         cmsSignalError(Icc->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported device class '0x%x'", Icc->DeviceClass);
823         return FALSE;
824     }
825 
826     // Get size as reported in header
827     HeaderSize = _cmsAdjustEndianess32(Header.size);
828 
829     // Make sure HeaderSize is lower than profile size
830     if (HeaderSize >= Icc ->IOhandler ->ReportedSize)
831             HeaderSize = Icc ->IOhandler ->ReportedSize;
832 
833 
834     // Get creation date/time
835     _cmsDecodeDateTimeNumber(&Header.date, &Icc ->Created);
836 
837     // The profile ID are 32 raw bytes
838     memmove(Icc ->ProfileID.ID32, Header.profileID.ID32, 16);
839 
840 
841     // Read tag directory
842     if (!_cmsReadUInt32Number(io, &TagCount)) return FALSE;
843     if (TagCount > MAX_TABLE_TAG) {
844 
845         cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", TagCount);
846         return FALSE;
847     }
848 
849 
850     // Read tag directory
851     Icc -> TagCount = 0;
852     for (i=0; i < TagCount; i++) {
853 
854         if (!_cmsReadUInt32Number(io, (cmsUInt32Number *) &Tag.sig)) return FALSE;
855         if (!_cmsReadUInt32Number(io, &Tag.offset)) return FALSE;
856         if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE;
857 
858         // Perform some sanity check. Offset + size should fall inside file.
859         if (Tag.size == 0 || Tag.offset == 0) continue;
860         if (Tag.offset + Tag.size > HeaderSize ||
861             Tag.offset + Tag.size < Tag.offset)
862                   continue;
863 
864         Icc -> TagNames[Icc ->TagCount]   = Tag.sig;
865         Icc -> TagOffsets[Icc ->TagCount] = Tag.offset;
866         Icc -> TagSizes[Icc ->TagCount]   = Tag.size;
867 
868        // Search for links
869         for (j=0; j < Icc ->TagCount; j++) {
870 
871             if ((Icc ->TagOffsets[j] == Tag.offset) &&
872                 (Icc ->TagSizes[j]   == Tag.size) &&
873                 (Icc ->TagNames[j]   == Tag.sig)) {
874 
875                 // Check types.
876                 if (CompatibleTypes(_cmsGetTagDescriptor(Icc->ContextID, Icc->TagNames[j]),
877                                     _cmsGetTagDescriptor(Icc->ContextID, Tag.sig))) {
878 
879                     Icc->TagLinked[Icc->TagCount] = Icc->TagNames[j];
880                 }
881             }
882 
883         }
884 
885         Icc ->TagCount++;
886     }
887 
888 
889     for (i = 0; i < Icc->TagCount; i++) {
890         for (j = 0; j < Icc->TagCount; j++) {
891 
892             // Tags cannot be duplicate
893             if ((i != j) && (Icc->TagNames[i] == Icc->TagNames[j])) {
894                 cmsSignalError(Icc->ContextID, cmsERROR_RANGE, "Duplicate tag found");
895                 return FALSE;
896             }
897 
898         }
899     }
900 
901     return TRUE;
902 }
903 
904 // Saves profile header
_cmsWriteHeader(_cmsICCPROFILE * Icc,cmsUInt32Number UsedSpace)905 cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace)
906 {
907     cmsICCHeader Header;
908     cmsUInt32Number i;
909     cmsTagEntry Tag;
910     cmsUInt32Number Count;
911 
912     Header.size        = _cmsAdjustEndianess32(UsedSpace);
913     Header.cmmId       = _cmsAdjustEndianess32(lcmsSignature);
914     Header.version     = _cmsAdjustEndianess32(Icc ->Version);
915 
916     Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass);
917     Header.colorSpace  = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> ColorSpace);
918     Header.pcs         = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> PCS);
919 
920     //   NOTE: in v4 Timestamp must be in UTC rather than in local time
921     _cmsEncodeDateTimeNumber(&Header.date, &Icc ->Created);
922 
923     Header.magic       = _cmsAdjustEndianess32(cmsMagicNumber);
924 
925 #ifdef CMS_IS_WINDOWS_
926     Header.platform    = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft);
927 #else
928     Header.platform    = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh);
929 #endif
930 
931     Header.flags        = _cmsAdjustEndianess32(Icc -> flags);
932     Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer);
933     Header.model        = _cmsAdjustEndianess32(Icc -> model);
934 
935     _cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes);
936 
937     // Rendering intent in the header (for embedded profiles)
938     Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent);
939 
940     // Illuminant is always D50
941     Header.illuminant.X = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->X));
942     Header.illuminant.Y = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y));
943     Header.illuminant.Z = (cmsS15Fixed16Number) _cmsAdjustEndianess32((cmsUInt32Number) _cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z));
944 
945     // Created by LittleCMS (that's me!)
946     Header.creator      = _cmsAdjustEndianess32(lcmsSignature);
947 
948     memset(&Header.reserved, 0, sizeof(Header.reserved));
949 
950     // Set profile ID. Endianness is always big endian
951     memmove(&Header.profileID, &Icc ->ProfileID, 16);
952 
953     // Dump the header
954     if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE;
955 
956     // Saves Tag directory
957 
958     // Get true count
959     Count = 0;
960     for (i=0;  i < Icc -> TagCount; i++) {
961         if (Icc ->TagNames[i] != (cmsTagSignature) 0)
962             Count++;
963     }
964 
965     // Store number of tags
966     if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE;
967 
968     for (i=0; i < Icc -> TagCount; i++) {
969 
970         if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue;   // It is just a placeholder
971 
972         Tag.sig    = (cmsTagSignature) _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagNames[i]);
973         Tag.offset = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagOffsets[i]);
974         Tag.size   = _cmsAdjustEndianess32((cmsUInt32Number) Icc -> TagSizes[i]);
975 
976         if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE;
977     }
978 
979     return TRUE;
980 }
981 
982 // ----------------------------------------------------------------------- Set/Get several struct members
983 
984 
cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile)985 cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile)
986 {
987     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
988     return Icc -> RenderingIntent;
989 }
990 
cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile,cmsUInt32Number RenderingIntent)991 void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent)
992 {
993     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
994     Icc -> RenderingIntent = RenderingIntent;
995 }
996 
cmsGetHeaderFlags(cmsHPROFILE hProfile)997 cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile)
998 {
999     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1000     return (cmsUInt32Number) Icc -> flags;
1001 }
1002 
cmsSetHeaderFlags(cmsHPROFILE hProfile,cmsUInt32Number Flags)1003 void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags)
1004 {
1005     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1006     Icc -> flags = (cmsUInt32Number) Flags;
1007 }
1008 
cmsGetHeaderManufacturer(cmsHPROFILE hProfile)1009 cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile)
1010 {
1011     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1012     return Icc ->manufacturer;
1013 }
1014 
cmsSetHeaderManufacturer(cmsHPROFILE hProfile,cmsUInt32Number manufacturer)1015 void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer)
1016 {
1017     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1018     Icc -> manufacturer = manufacturer;
1019 }
1020 
cmsGetHeaderCreator(cmsHPROFILE hProfile)1021 cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile)
1022 {
1023     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1024     return Icc ->creator;
1025 }
1026 
cmsGetHeaderModel(cmsHPROFILE hProfile)1027 cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile)
1028 {
1029     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1030     return Icc ->model;
1031 }
1032 
cmsSetHeaderModel(cmsHPROFILE hProfile,cmsUInt32Number model)1033 void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model)
1034 {
1035     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1036     Icc -> model = model;
1037 }
1038 
cmsGetHeaderAttributes(cmsHPROFILE hProfile,cmsUInt64Number * Flags)1039 void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags)
1040 {
1041     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1042     memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number));
1043 }
1044 
cmsSetHeaderAttributes(cmsHPROFILE hProfile,cmsUInt64Number Flags)1045 void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags)
1046 {
1047     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1048     memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number));
1049 }
1050 
cmsGetHeaderProfileID(cmsHPROFILE hProfile,cmsUInt8Number * ProfileID)1051 void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID)
1052 {
1053     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1054     memmove(ProfileID, Icc ->ProfileID.ID8, 16);
1055 }
1056 
cmsSetHeaderProfileID(cmsHPROFILE hProfile,cmsUInt8Number * ProfileID)1057 void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID)
1058 {
1059     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1060     memmove(&Icc -> ProfileID, ProfileID, 16);
1061 }
1062 
cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile,struct tm * Dest)1063 cmsBool  CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest)
1064 {
1065     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1066     memmove(Dest, &Icc ->Created, sizeof(struct tm));
1067     return TRUE;
1068 }
1069 
cmsGetPCS(cmsHPROFILE hProfile)1070 cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile)
1071 {
1072     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1073     return Icc -> PCS;
1074 }
1075 
cmsSetPCS(cmsHPROFILE hProfile,cmsColorSpaceSignature pcs)1076 void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs)
1077 {
1078     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1079     Icc -> PCS = pcs;
1080 }
1081 
cmsGetColorSpace(cmsHPROFILE hProfile)1082 cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile)
1083 {
1084     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1085     return Icc -> ColorSpace;
1086 }
1087 
cmsSetColorSpace(cmsHPROFILE hProfile,cmsColorSpaceSignature sig)1088 void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig)
1089 {
1090     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1091     Icc -> ColorSpace = sig;
1092 }
1093 
cmsGetDeviceClass(cmsHPROFILE hProfile)1094 cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile)
1095 {
1096     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1097     return Icc -> DeviceClass;
1098 }
1099 
cmsSetDeviceClass(cmsHPROFILE hProfile,cmsProfileClassSignature sig)1100 void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig)
1101 {
1102     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1103     Icc -> DeviceClass = sig;
1104 }
1105 
cmsGetEncodedICCversion(cmsHPROFILE hProfile)1106 cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile)
1107 {
1108     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1109     return Icc -> Version;
1110 }
1111 
cmsSetEncodedICCversion(cmsHPROFILE hProfile,cmsUInt32Number Version)1112 void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version)
1113 {
1114     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1115     Icc -> Version = Version;
1116 }
1117 
1118 // Get an hexadecimal number with same digits as v
1119 static
BaseToBase(cmsUInt32Number in,int BaseIn,int BaseOut)1120 cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut)
1121 {
1122     char Buff[100];
1123     int i, len;
1124     cmsUInt32Number out;
1125 
1126     for (len=0; in > 0 && len < 100; len++) {
1127 
1128         Buff[len] = (char) (in % BaseIn);
1129         in /= BaseIn;
1130     }
1131 
1132     for (i=len-1, out=0; i >= 0; --i) {
1133         out = out * BaseOut + Buff[i];
1134     }
1135 
1136     return out;
1137 }
1138 
cmsSetProfileVersion(cmsHPROFILE hProfile,cmsFloat64Number Version)1139 void  CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version)
1140 {
1141     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1142 
1143     // 4.2 -> 0x4200000
1144 
1145     Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0 + 0.5), 10, 16) << 16;
1146 }
1147 
cmsGetProfileVersion(cmsHPROFILE hProfile)1148 cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile)
1149 {
1150     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1151     cmsUInt32Number n = Icc -> Version >> 16;
1152 
1153     return BaseToBase(n, 16, 10) / 100.0;
1154 }
1155 // --------------------------------------------------------------------------------------------------------------
1156 
1157 
1158 // Create profile from IOhandler
cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID,cmsIOHANDLER * io)1159 cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io)
1160 {
1161     _cmsICCPROFILE* NewIcc;
1162     cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);
1163 
1164     if (hEmpty == NULL) return NULL;
1165 
1166     NewIcc = (_cmsICCPROFILE*) hEmpty;
1167 
1168     NewIcc ->IOhandler = io;
1169     if (!_cmsReadHeader(NewIcc)) goto Error;
1170     return hEmpty;
1171 
1172 Error:
1173     cmsCloseProfile(hEmpty);
1174     return NULL;
1175 }
1176 
1177 // Create profile from IOhandler
cmsOpenProfileFromIOhandler2THR(cmsContext ContextID,cmsIOHANDLER * io,cmsBool write)1178 cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write)
1179 {
1180     _cmsICCPROFILE* NewIcc;
1181     cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);
1182 
1183     if (hEmpty == NULL) return NULL;
1184 
1185     NewIcc = (_cmsICCPROFILE*) hEmpty;
1186 
1187     NewIcc ->IOhandler = io;
1188     if (write) {
1189 
1190         NewIcc -> IsWrite = TRUE;
1191         return hEmpty;
1192     }
1193 
1194     if (!_cmsReadHeader(NewIcc)) goto Error;
1195     return hEmpty;
1196 
1197 Error:
1198     cmsCloseProfile(hEmpty);
1199     return NULL;
1200 }
1201 
1202 
1203 // Create profile from disk file
cmsOpenProfileFromFileTHR(cmsContext ContextID,const char * lpFileName,const char * sAccess)1204 cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess)
1205 {
1206     _cmsICCPROFILE* NewIcc;
1207     cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);
1208 
1209     if (hEmpty == NULL) return NULL;
1210 
1211     NewIcc = (_cmsICCPROFILE*) hEmpty;
1212 
1213     NewIcc ->IOhandler = cmsOpenIOhandlerFromFile(ContextID, lpFileName, sAccess);
1214     if (NewIcc ->IOhandler == NULL) goto Error;
1215 
1216     if (*sAccess == 'W' || *sAccess == 'w') {
1217 
1218         NewIcc -> IsWrite = TRUE;
1219 
1220         return hEmpty;
1221     }
1222 
1223     if (!_cmsReadHeader(NewIcc)) goto Error;
1224     return hEmpty;
1225 
1226 Error:
1227     cmsCloseProfile(hEmpty);
1228     return NULL;
1229 }
1230 
1231 
cmsOpenProfileFromFile(const char * ICCProfile,const char * sAccess)1232 cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess)
1233 {
1234     return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess);
1235 }
1236 
1237 
cmsOpenProfileFromStreamTHR(cmsContext ContextID,FILE * ICCProfile,const char * sAccess)1238 cmsHPROFILE  CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char *sAccess)
1239 {
1240     _cmsICCPROFILE* NewIcc;
1241     cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);
1242 
1243     if (hEmpty == NULL) return NULL;
1244 
1245     NewIcc = (_cmsICCPROFILE*) hEmpty;
1246 
1247     NewIcc ->IOhandler = cmsOpenIOhandlerFromStream(ContextID, ICCProfile);
1248     if (NewIcc ->IOhandler == NULL) goto Error;
1249 
1250     if (*sAccess == 'w') {
1251 
1252         NewIcc -> IsWrite = TRUE;
1253         return hEmpty;
1254     }
1255 
1256     if (!_cmsReadHeader(NewIcc)) goto Error;
1257     return hEmpty;
1258 
1259 Error:
1260     cmsCloseProfile(hEmpty);
1261     return NULL;
1262 
1263 }
1264 
cmsOpenProfileFromStream(FILE * ICCProfile,const char * sAccess)1265 cmsHPROFILE  CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess)
1266 {
1267     return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess);
1268 }
1269 
1270 
1271 // Open from memory block
cmsOpenProfileFromMemTHR(cmsContext ContextID,const void * MemPtr,cmsUInt32Number dwSize)1272 cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize)
1273 {
1274     _cmsICCPROFILE* NewIcc;
1275     cmsHPROFILE hEmpty;
1276 
1277     hEmpty = cmsCreateProfilePlaceholder(ContextID);
1278     if (hEmpty == NULL) return NULL;
1279 
1280     NewIcc = (_cmsICCPROFILE*) hEmpty;
1281 
1282     // Ok, in this case const void* is casted to void* just because open IO handler
1283     // shares read and writing modes. Don't abuse this feature!
1284     NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r");
1285     if (NewIcc ->IOhandler == NULL) goto Error;
1286 
1287     if (!_cmsReadHeader(NewIcc)) goto Error;
1288 
1289     return hEmpty;
1290 
1291 Error:
1292     cmsCloseProfile(hEmpty);
1293     return NULL;
1294 }
1295 
cmsOpenProfileFromMem(const void * MemPtr,cmsUInt32Number dwSize)1296 cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void* MemPtr, cmsUInt32Number dwSize)
1297 {
1298     return cmsOpenProfileFromMemTHR(NULL, MemPtr, dwSize);
1299 }
1300 
1301 
1302 
1303 // Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig
1304 static
SaveTags(_cmsICCPROFILE * Icc,_cmsICCPROFILE * FileOrig)1305 cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig)
1306 {
1307     cmsUInt8Number* Data;
1308     cmsUInt32Number i;
1309     cmsUInt32Number Begin;
1310     cmsIOHANDLER* io = Icc ->IOhandler;
1311     cmsTagDescriptor* TagDescriptor;
1312     cmsTagTypeSignature TypeBase;
1313     cmsTagTypeSignature Type;
1314     cmsTagTypeHandler* TypeHandler;
1315     cmsFloat64Number   Version = cmsGetProfileVersion((cmsHPROFILE) Icc);
1316     cmsTagTypeHandler LocalTypeHandler;
1317 
1318     for (i=0; i < Icc -> TagCount; i++) {
1319 
1320         if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue;
1321 
1322         // Linked tags are not written
1323         if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue;
1324 
1325         Icc -> TagOffsets[i] = Begin = io ->UsedSpace;
1326 
1327         Data = (cmsUInt8Number*)  Icc -> TagPtrs[i];
1328 
1329         if (!Data) {
1330 
1331             // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.
1332             // In this case a blind copy of the block data is performed
1333             if (FileOrig != NULL && Icc -> TagOffsets[i]) {
1334 
1335                 if (FileOrig->IOhandler != NULL)
1336                 {
1337                     cmsUInt32Number TagSize = FileOrig->TagSizes[i];
1338                     cmsUInt32Number TagOffset = FileOrig->TagOffsets[i];
1339                     void* Mem;
1340 
1341                     if (!FileOrig->IOhandler->Seek(FileOrig->IOhandler, TagOffset)) return FALSE;
1342 
1343                     Mem = _cmsMalloc(Icc->ContextID, TagSize);
1344                     if (Mem == NULL) return FALSE;
1345 
1346                     if (FileOrig->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE;
1347                     if (!io->Write(io, TagSize, Mem)) return FALSE;
1348                     _cmsFree(Icc->ContextID, Mem);
1349 
1350                     Icc->TagSizes[i] = (io->UsedSpace - Begin);
1351 
1352 
1353                     // Align to 32 bit boundary.
1354                     if (!_cmsWriteAlignment(io))
1355                         return FALSE;
1356                 }
1357             }
1358 
1359             continue;
1360         }
1361 
1362 
1363         // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done)
1364         if (Icc ->TagSaveAsRaw[i]) {
1365 
1366             if (io -> Write(io, Icc ->TagSizes[i], Data) != 1) return FALSE;
1367         }
1368         else {
1369 
1370             // Search for support on this tag
1371             TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, Icc -> TagNames[i]);
1372             if (TagDescriptor == NULL) continue;                        // Unsupported, ignore it
1373 
1374             if (TagDescriptor ->DecideType != NULL) {
1375 
1376                 Type = TagDescriptor ->DecideType(Version, Data);
1377             }
1378             else {
1379 
1380                 Type = TagDescriptor ->SupportedTypes[0];
1381             }
1382 
1383             TypeHandler =  _cmsGetTagTypeHandler(Icc->ContextID, Type);
1384 
1385             if (TypeHandler == NULL) {
1386                 cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]);
1387                 continue;
1388             }
1389 
1390             TypeBase = TypeHandler ->Signature;
1391             if (!_cmsWriteTypeBase(io, TypeBase))
1392                 return FALSE;
1393 
1394             LocalTypeHandler = *TypeHandler;
1395             LocalTypeHandler.ContextID  = Icc ->ContextID;
1396             LocalTypeHandler.ICCVersion = Icc ->Version;
1397             if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, io, Data, TagDescriptor ->ElemCount)) {
1398 
1399                 char String[5];
1400 
1401                 _cmsTagSignature2String(String, (cmsTagSignature) TypeBase);
1402                 cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String);
1403                 return FALSE;
1404             }
1405         }
1406 
1407 
1408         Icc -> TagSizes[i] = (io ->UsedSpace - Begin);
1409 
1410         // Align to 32 bit boundary.
1411         if (! _cmsWriteAlignment(io))
1412             return FALSE;
1413     }
1414 
1415 
1416     return TRUE;
1417 }
1418 
1419 
1420 // Fill the offset and size fields for all linked tags
1421 static
SetLinks(_cmsICCPROFILE * Icc)1422 cmsBool SetLinks( _cmsICCPROFILE* Icc)
1423 {
1424     cmsUInt32Number i;
1425 
1426     for (i=0; i < Icc -> TagCount; i++) {
1427 
1428         cmsTagSignature lnk = Icc ->TagLinked[i];
1429         if (lnk != (cmsTagSignature) 0) {
1430 
1431             int j = _cmsSearchTag(Icc, lnk, FALSE);
1432             if (j >= 0) {
1433 
1434                 Icc ->TagOffsets[i] = Icc ->TagOffsets[j];
1435                 Icc ->TagSizes[i]   = Icc ->TagSizes[j];
1436             }
1437 
1438         }
1439     }
1440 
1441     return TRUE;
1442 }
1443 
1444 // Low-level save to IOHANDLER. It returns the number of bytes used to
1445 // store the profile, or zero on error. io may be NULL and in this case
1446 // no data is written--only sizes are calculated
cmsSaveProfileToIOhandler(cmsHPROFILE hProfile,cmsIOHANDLER * io)1447 cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io)
1448 {
1449     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1450     _cmsICCPROFILE Keep;
1451     cmsIOHANDLER* PrevIO = NULL;
1452     cmsUInt32Number UsedSpace;
1453     cmsContext ContextID;
1454 
1455     _cmsAssert(hProfile != NULL);
1456 
1457     if (!_cmsLockMutex(Icc->ContextID, Icc->UsrMutex)) return 0;
1458     memmove(&Keep, Icc, sizeof(_cmsICCPROFILE));
1459 
1460     ContextID = cmsGetProfileContextID(hProfile);
1461     PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID);
1462     if (PrevIO == NULL) {
1463         _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
1464         return 0;
1465     }
1466 
1467     // Pass #1 does compute offsets
1468 
1469     if (!_cmsWriteHeader(Icc, 0)) goto Error;
1470     if (!SaveTags(Icc, &Keep)) goto Error;
1471 
1472     UsedSpace = PrevIO ->UsedSpace;
1473 
1474     // Pass #2 does save to iohandler
1475 
1476     if (io != NULL) {
1477 
1478         Icc ->IOhandler = io;
1479         if (!SetLinks(Icc)) goto Error;
1480         if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error;
1481         if (!SaveTags(Icc, &Keep)) goto Error;
1482     }
1483 
1484     memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
1485     if (!cmsCloseIOhandler(PrevIO))
1486         UsedSpace = 0; // As a error marker
1487 
1488     _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
1489 
1490     return UsedSpace;
1491 
1492 
1493 Error:
1494     cmsCloseIOhandler(PrevIO);
1495     memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
1496     _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
1497 
1498     return 0;
1499 }
1500 
1501 #ifdef _WIN32_WCE
1502 int wceex_unlink(const char *filename);
1503 #ifndef remove
1504 #   define remove wceex_unlink
1505 #endif
1506 #endif
1507 
1508 // Low-level save to disk.
cmsSaveProfileToFile(cmsHPROFILE hProfile,const char * FileName)1509 cmsBool  CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName)
1510 {
1511     cmsContext ContextID = cmsGetProfileContextID(hProfile);
1512     cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w");
1513     cmsBool rc;
1514 
1515     if (io == NULL) return FALSE;
1516 
1517     rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0);
1518     rc &= cmsCloseIOhandler(io);
1519 
1520     if (rc == FALSE) {          // remove() is C99 per 7.19.4.1
1521             remove(FileName);   // We have to IGNORE return value in this case
1522     }
1523     return rc;
1524 }
1525 
1526 // Same as anterior, but for streams
cmsSaveProfileToStream(cmsHPROFILE hProfile,FILE * Stream)1527 cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream)
1528 {
1529     cmsBool rc;
1530     cmsContext ContextID = cmsGetProfileContextID(hProfile);
1531     cmsIOHANDLER* io = cmsOpenIOhandlerFromStream(ContextID, Stream);
1532 
1533     if (io == NULL) return FALSE;
1534 
1535     rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0);
1536     rc &= cmsCloseIOhandler(io);
1537 
1538     return rc;
1539 }
1540 
1541 
1542 // Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only
cmsSaveProfileToMem(cmsHPROFILE hProfile,void * MemPtr,cmsUInt32Number * BytesNeeded)1543 cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded)
1544 {
1545     cmsBool rc;
1546     cmsIOHANDLER* io;
1547     cmsContext ContextID = cmsGetProfileContextID(hProfile);
1548 
1549     _cmsAssert(BytesNeeded != NULL);
1550 
1551     // Should we just calculate the needed space?
1552     if (MemPtr == NULL) {
1553 
1554            *BytesNeeded =  cmsSaveProfileToIOhandler(hProfile, NULL);
1555             return (*BytesNeeded == 0) ? FALSE : TRUE;
1556     }
1557 
1558     // That is a real write operation
1559     io =  cmsOpenIOhandlerFromMem(ContextID, MemPtr, *BytesNeeded, "w");
1560     if (io == NULL) return FALSE;
1561 
1562     rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0);
1563     rc &= cmsCloseIOhandler(io);
1564 
1565     return rc;
1566 }
1567 
1568 // Free one tag contents
1569 static
freeOneTag(_cmsICCPROFILE * Icc,cmsUInt32Number i)1570 void freeOneTag(_cmsICCPROFILE* Icc, cmsUInt32Number i)
1571 {
1572     if (Icc->TagPtrs[i]) {
1573 
1574         cmsTagTypeHandler* TypeHandler = Icc->TagTypeHandlers[i];
1575 
1576         if (TypeHandler != NULL) {
1577             cmsTagTypeHandler LocalTypeHandler = *TypeHandler;
1578 
1579             LocalTypeHandler.ContextID = Icc->ContextID;
1580             LocalTypeHandler.ICCVersion = Icc->Version;
1581             LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc->TagPtrs[i]);
1582         }
1583         else
1584             _cmsFree(Icc->ContextID, Icc->TagPtrs[i]);
1585     }
1586 }
1587 
1588 // Closes a profile freeing any involved resources
cmsCloseProfile(cmsHPROFILE hProfile)1589 cmsBool  CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile)
1590 {
1591     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1592     cmsBool  rc = TRUE;
1593     cmsUInt32Number i;
1594 
1595     if (!Icc) return FALSE;
1596 
1597     // Was open in write mode?
1598     if (Icc ->IsWrite) {
1599 
1600         Icc ->IsWrite = FALSE;      // Assure no further writing
1601         rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile);
1602     }
1603 
1604     for (i=0; i < Icc -> TagCount; i++) {
1605 
1606         freeOneTag(Icc, i);
1607     }
1608 
1609     if (Icc ->IOhandler != NULL) {
1610         rc &= cmsCloseIOhandler(Icc->IOhandler);
1611     }
1612 
1613     _cmsDestroyMutex(Icc->ContextID, Icc->UsrMutex);
1614 
1615     _cmsFree(Icc ->ContextID, Icc);   // Free placeholder memory
1616 
1617     return rc;
1618 }
1619 
1620 
1621 // -------------------------------------------------------------------------------------------------------------------
1622 
1623 
1624 // Returns TRUE if a given tag is supported by a plug-in
1625 static
IsTypeSupported(cmsTagDescriptor * TagDescriptor,cmsTagTypeSignature Type)1626 cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Type)
1627 {
1628     cmsUInt32Number i, nMaxTypes;
1629 
1630     nMaxTypes = TagDescriptor->nSupportedTypes;
1631     if (nMaxTypes >= MAX_TYPES_IN_LCMS_PLUGIN)
1632         nMaxTypes = MAX_TYPES_IN_LCMS_PLUGIN;
1633 
1634     for (i=0; i < nMaxTypes; i++) {
1635         if (Type == TagDescriptor ->SupportedTypes[i]) return TRUE;
1636     }
1637 
1638     return FALSE;
1639 }
1640 
1641 
1642 // That's the main read function
cmsReadTag(cmsHPROFILE hProfile,cmsTagSignature sig)1643 void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
1644 {
1645     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1646     cmsIOHANDLER* io;
1647     cmsTagTypeHandler* TypeHandler;
1648     cmsTagTypeHandler LocalTypeHandler;
1649     cmsTagDescriptor*  TagDescriptor;
1650     cmsTagTypeSignature BaseType;
1651     cmsUInt32Number Offset, TagSize;
1652     cmsUInt32Number ElemCount;
1653     int n;
1654 
1655     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL;
1656 
1657     n = _cmsSearchTag(Icc, sig, TRUE);
1658     if (n < 0)
1659     {
1660         // Not found, return NULL
1661         _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
1662         return NULL;
1663     }
1664 
1665     // If the element is already in memory, return the pointer
1666     if (Icc -> TagPtrs[n]) {
1667 
1668         if (Icc->TagTypeHandlers[n] == NULL) goto Error;
1669 
1670         // Sanity check
1671         BaseType = Icc->TagTypeHandlers[n]->Signature;
1672         if (BaseType == 0) goto Error;
1673 
1674         TagDescriptor = _cmsGetTagDescriptor(Icc->ContextID, sig);
1675         if (TagDescriptor == NULL) goto Error;
1676 
1677         if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
1678 
1679         if (Icc ->TagSaveAsRaw[n]) goto Error;  // We don't support read raw tags as cooked
1680 
1681         _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1682         return Icc -> TagPtrs[n];
1683     }
1684 
1685     // We need to read it. Get the offset and size to the file
1686     Offset    = Icc -> TagOffsets[n];
1687     TagSize   = Icc -> TagSizes[n];
1688 
1689     if (TagSize < 8) goto Error;
1690 
1691     io = Icc ->IOhandler;
1692 
1693     if (io == NULL) { // This is a built-in profile that has been manipulated, abort early
1694 
1695         cmsSignalError(Icc->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted built-in profile.");
1696         goto Error;
1697     }
1698 
1699     // Seek to its location
1700     if (!io -> Seek(io, Offset))
1701         goto Error;
1702 
1703     // Search for support on this tag
1704     TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig);
1705     if (TagDescriptor == NULL) {
1706 
1707         char String[5];
1708 
1709         _cmsTagSignature2String(String, sig);
1710 
1711         // An unknown element was found.
1712         cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String);
1713         goto Error;     // Unsupported.
1714     }
1715 
1716     // if supported, get type and check if in list
1717     BaseType = _cmsReadTypeBase(io);
1718     if (BaseType == 0) goto Error;
1719 
1720     if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
1721 
1722     TagSize  -= 8;       // Already read by the type base logic
1723 
1724     // Get type handler
1725     TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType);
1726     if (TypeHandler == NULL) goto Error;
1727     LocalTypeHandler = *TypeHandler;
1728 
1729 
1730     // Read the tag
1731     Icc -> TagTypeHandlers[n] = TypeHandler;
1732 
1733     LocalTypeHandler.ContextID = Icc ->ContextID;
1734     LocalTypeHandler.ICCVersion = Icc ->Version;
1735     Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize);
1736 
1737     // The tag type is supported, but something wrong happened and we cannot read the tag.
1738     // let know the user about this (although it is just a warning)
1739     if (Icc -> TagPtrs[n] == NULL) {
1740 
1741         char String[5];
1742 
1743         _cmsTagSignature2String(String, sig);
1744         cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String);
1745         goto Error;
1746     }
1747 
1748     // This is a weird error that may be a symptom of something more serious, the number of
1749     // stored item is actually less than the number of required elements.
1750     if (ElemCount < TagDescriptor ->ElemCount) {
1751 
1752         char String[5];
1753 
1754         _cmsTagSignature2String(String, sig);
1755         cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d",
1756             String, TagDescriptor ->ElemCount, ElemCount);
1757         goto Error;
1758     }
1759 
1760 
1761     // Return the data
1762     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1763     return Icc -> TagPtrs[n];
1764 
1765 
1766     // Return error and unlock the data
1767 Error:
1768 
1769     freeOneTag(Icc, n);
1770     Icc->TagPtrs[n] = NULL;
1771 
1772     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1773     return NULL;
1774 }
1775 
1776 
1777 // Get true type of data
_cmsGetTagTrueType(cmsHPROFILE hProfile,cmsTagSignature sig)1778 cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig)
1779 {
1780     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1781     cmsTagTypeHandler* TypeHandler;
1782     int n;
1783 
1784     // Search for given tag in ICC profile directory
1785     n = _cmsSearchTag(Icc, sig, TRUE);
1786     if (n < 0) return (cmsTagTypeSignature) 0;                // Not found, return NULL
1787 
1788     // Get the handler. The true type is there
1789     TypeHandler =  Icc -> TagTypeHandlers[n];
1790     return TypeHandler ->Signature;
1791 }
1792 
1793 
1794 // Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already
1795 // in that list, the previous version is deleted.
cmsWriteTag(cmsHPROFILE hProfile,cmsTagSignature sig,const void * data)1796 cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data)
1797 {
1798     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1799     cmsTagTypeHandler* TypeHandler = NULL;
1800     cmsTagTypeHandler LocalTypeHandler;
1801     cmsTagDescriptor* TagDescriptor = NULL;
1802     cmsTagTypeSignature Type;
1803     int i;
1804     cmsFloat64Number Version;
1805     char TypeString[5], SigString[5];
1806 
1807     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE;
1808 
1809     // To delete tags.
1810     if (data == NULL) {
1811 
1812          // Delete the tag
1813          i = _cmsSearchTag(Icc, sig, FALSE);
1814          if (i >= 0) {
1815 
1816              // Use zero as a mark of deleted
1817              _cmsDeleteTagByPos(Icc, i);
1818              Icc ->TagNames[i] = (cmsTagSignature) 0;
1819              _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1820              return TRUE;
1821          }
1822          // Didn't find the tag
1823         goto Error;
1824     }
1825 
1826     if (!_cmsNewTag(Icc, sig, &i)) goto Error;
1827 
1828     // This is not raw
1829     Icc ->TagSaveAsRaw[i] = FALSE;
1830 
1831     // This is not a link
1832     Icc ->TagLinked[i] = (cmsTagSignature) 0;
1833 
1834     // Get information about the TAG.
1835     TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig);
1836     if (TagDescriptor == NULL){
1837          cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig);
1838         goto Error;
1839     }
1840 
1841 
1842     // Now we need to know which type to use. It depends on the version.
1843     Version = cmsGetProfileVersion(hProfile);
1844 
1845     if (TagDescriptor ->DecideType != NULL) {
1846 
1847         // Let the tag descriptor to decide the type base on depending on
1848         // the data. This is useful for example on parametric curves, where
1849         // curves specified by a table cannot be saved as parametric and needs
1850         // to be casted to single v2-curves, even on v4 profiles.
1851 
1852         Type = TagDescriptor ->DecideType(Version, data);
1853     }
1854     else {
1855 
1856         Type = TagDescriptor ->SupportedTypes[0];
1857     }
1858 
1859     // Does the tag support this type?
1860     if (!IsTypeSupported(TagDescriptor, Type)) {
1861 
1862         _cmsTagSignature2String(TypeString, (cmsTagSignature) Type);
1863         _cmsTagSignature2String(SigString,  sig);
1864 
1865         cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString);
1866         goto Error;
1867     }
1868 
1869     // Does we have a handler for this type?
1870     TypeHandler =  _cmsGetTagTypeHandler(Icc->ContextID, Type);
1871     if (TypeHandler == NULL) {
1872 
1873         _cmsTagSignature2String(TypeString, (cmsTagSignature) Type);
1874         _cmsTagSignature2String(SigString,  sig);
1875 
1876         cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString);
1877         goto Error;           // Should never happen
1878     }
1879 
1880 
1881     // Fill fields on icc structure
1882     Icc ->TagTypeHandlers[i]  = TypeHandler;
1883     Icc ->TagNames[i]         = sig;
1884     Icc ->TagSizes[i]         = 0;
1885     Icc ->TagOffsets[i]       = 0;
1886 
1887     LocalTypeHandler = *TypeHandler;
1888     LocalTypeHandler.ContextID  = Icc ->ContextID;
1889     LocalTypeHandler.ICCVersion = Icc ->Version;
1890     Icc ->TagPtrs[i]            = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount);
1891 
1892     if (Icc ->TagPtrs[i] == NULL)  {
1893 
1894         _cmsTagSignature2String(TypeString, (cmsTagSignature) Type);
1895         _cmsTagSignature2String(SigString,  sig);
1896         cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString);
1897 
1898         goto Error;
1899     }
1900 
1901     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1902     return TRUE;
1903 
1904 Error:
1905     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1906     return FALSE;
1907 
1908 }
1909 
1910 // Read and write raw data. Read/Write Raw/cooked pairs try to maintain consistency within the pair. Some sequences
1911 // raw/cooked would work, but at a cost. Data "cooked" may be converted to "raw" by using the "write" serialization logic.
1912 // In general it is better to avoid mixing pairs.
1913 
cmsReadRawTag(cmsHPROFILE hProfile,cmsTagSignature sig,void * data,cmsUInt32Number BufferSize)1914 cmsUInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize)
1915 {
1916     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1917     void *Object;
1918     int i;
1919     cmsIOHANDLER* MemIO;
1920     cmsTagTypeHandler* TypeHandler = NULL;
1921     cmsTagTypeHandler LocalTypeHandler;
1922     cmsTagDescriptor* TagDescriptor = NULL;
1923     cmsUInt32Number rc;
1924     cmsUInt32Number Offset, TagSize;
1925 
1926     // Sanity check
1927     if (data != NULL && BufferSize == 0) return 0;
1928 
1929     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
1930 
1931     // Search for given tag in ICC profile directory
1932 
1933     i = _cmsSearchTag(Icc, sig, TRUE);
1934     if (i < 0) goto Error;                 // Not found,
1935 
1936     // It is already read?
1937     if (Icc -> TagPtrs[i] == NULL) {
1938 
1939         // Not yet, get original position
1940         Offset   = Icc ->TagOffsets[i];
1941         TagSize  = Icc ->TagSizes[i];
1942 
1943         // read the data directly, don't keep copy
1944 
1945         if (data != NULL) {
1946 
1947             if (BufferSize < TagSize)
1948                 TagSize = BufferSize;
1949 
1950             if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error;
1951             if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error;
1952 
1953             _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1954             return TagSize;
1955         }
1956 
1957         _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1958         return Icc ->TagSizes[i];
1959     }
1960 
1961     // The data has been already read, or written. But wait!, maybe the user choose to save as
1962     // raw data. In this case, return the raw data directly
1963 
1964     if (Icc ->TagSaveAsRaw[i]) {
1965 
1966         if (data != NULL)  {
1967 
1968             TagSize  = Icc ->TagSizes[i];
1969             if (BufferSize < TagSize)
1970                 TagSize = BufferSize;
1971 
1972             memmove(data, Icc ->TagPtrs[i], TagSize);
1973 
1974             _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1975             return TagSize;
1976         }
1977 
1978         _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1979         return Icc ->TagSizes[i];
1980     }
1981 
1982     // Already read, or previously set by cmsWriteTag(). We need to serialize that
1983     // data to raw to get something that makes sense
1984 
1985     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1986     Object = cmsReadTag(hProfile, sig);
1987     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
1988 
1989     if (Object == NULL) goto Error;
1990 
1991     // Now we need to serialize to a memory block: just use a memory iohandler
1992 
1993     if (data == NULL) {
1994         MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile));
1995     } else{
1996         MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w");
1997     }
1998     if (MemIO == NULL) goto Error;
1999 
2000     // Obtain type handling for the tag
2001     TypeHandler = Icc ->TagTypeHandlers[i];
2002     TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig);
2003     if (TagDescriptor == NULL) {
2004         cmsCloseIOhandler(MemIO);
2005         goto Error;
2006     }
2007 
2008     if (TypeHandler == NULL) goto Error;
2009 
2010     // Serialize
2011     LocalTypeHandler = *TypeHandler;
2012     LocalTypeHandler.ContextID  = Icc ->ContextID;
2013     LocalTypeHandler.ICCVersion = Icc ->Version;
2014 
2015     if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) {
2016         cmsCloseIOhandler(MemIO);
2017         goto Error;
2018     }
2019 
2020     if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) {
2021         cmsCloseIOhandler(MemIO);
2022         goto Error;
2023     }
2024 
2025     // Get Size and close
2026     rc = MemIO ->Tell(MemIO);
2027     cmsCloseIOhandler(MemIO);      // Ignore return code this time
2028 
2029     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
2030     return rc;
2031 
2032 Error:
2033     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
2034     return 0;
2035 }
2036 
2037 // Similar to the anterior. This function allows to write directly to the ICC profile any data, without
2038 // checking anything. As a rule, mixing Raw with cooked doesn't work, so writing a tag as raw and then reading
2039 // it as cooked without serializing does result into an error. If that is what you want, you will need to dump
2040 // the profile to memry or disk and then reopen it.
cmsWriteRawTag(cmsHPROFILE hProfile,cmsTagSignature sig,const void * data,cmsUInt32Number Size)2041 cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size)
2042 {
2043     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
2044     int i;
2045 
2046     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
2047 
2048     if (!_cmsNewTag(Icc, sig, &i)) {
2049         _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
2050          return FALSE;
2051     }
2052 
2053     // Mark the tag as being written as RAW
2054     Icc ->TagSaveAsRaw[i] = TRUE;
2055     Icc ->TagNames[i]     = sig;
2056     Icc ->TagLinked[i]    = (cmsTagSignature) 0;
2057 
2058     // Keep a copy of the block
2059     Icc ->TagPtrs[i]  = _cmsDupMem(Icc ->ContextID, data, Size);
2060     Icc ->TagSizes[i] = Size;
2061 
2062     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
2063 
2064     if (Icc->TagPtrs[i] == NULL) {
2065            Icc->TagNames[i] = (cmsTagSignature) 0;
2066            return FALSE;
2067     }
2068     return TRUE;
2069 }
2070 
2071 // Using this function you can collapse several tag entries to the same block in the profile
cmsLinkTag(cmsHPROFILE hProfile,cmsTagSignature sig,cmsTagSignature dest)2072 cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest)
2073 {
2074     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
2075     int i;
2076 
2077      if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE;
2078 
2079     if (!_cmsNewTag(Icc, sig, &i)) {
2080         _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
2081         return FALSE;
2082     }
2083 
2084     // Keep necessary information
2085     Icc ->TagSaveAsRaw[i] = FALSE;
2086     Icc ->TagNames[i]     = sig;
2087     Icc ->TagLinked[i]    = dest;
2088 
2089     Icc ->TagPtrs[i]    = NULL;
2090     Icc ->TagSizes[i]   = 0;
2091     Icc ->TagOffsets[i] = 0;
2092 
2093     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
2094     return TRUE;
2095 }
2096 
2097 
2098 // Returns the tag linked to sig, in the case two tags are sharing same resource
cmsTagLinkedTo(cmsHPROFILE hProfile,cmsTagSignature sig)2099 cmsTagSignature  CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig)
2100 {
2101     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
2102     int i;
2103 
2104     // Search for given tag in ICC profile directory
2105     i = _cmsSearchTag(Icc, sig, FALSE);
2106     if (i < 0) return (cmsTagSignature) 0;                 // Not found, return 0
2107 
2108     return Icc -> TagLinked[i];
2109 }
2110