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