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-2016 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 // Position 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     cmsInt32Number fileLen;
 401 
 402     _cmsAssert(FileName != NULL);
 403     _cmsAssert(AccessMode != NULL);
 404 
 405     iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
 406     if (iohandler == NULL) return NULL;
 407 
 408     switch (*AccessMode) {
 409 
 410     case 'r':
 411         fm = fopen(FileName, "rb");
 412         if (fm == NULL) {
 413             _cmsFree(ContextID, iohandler);
 414              cmsSignalError(ContextID, cmsERROR_FILE, "File '%s' not found", FileName);
 415             return NULL;
 416         }
 417         fileLen = cmsfilelength(fm);
 418         if (fileLen < 0)
 419         {
 420             fclose(fm);
 421             _cmsFree(ContextID, iohandler);
 422             cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of file '%s'", FileName);
 423             return NULL;
 424         }
 425 
 426         iohandler -> ReportedSize = (cmsUInt32Number) fileLen;
 427         break;
 428 
 429     case 'w':
 430         fm = fopen(FileName, "wb");
 431         if (fm == NULL) {
 432             _cmsFree(ContextID, iohandler);
 433              cmsSignalError(ContextID, cmsERROR_FILE, "Couldn't create '%s'", FileName);
 434             return NULL;
 435         }
 436         iohandler -> ReportedSize = 0;
 437         break;
 438 
 439     default:
 440         _cmsFree(ContextID, iohandler);
 441          cmsSignalError(ContextID, cmsERROR_FILE, "Unknown access mode '%c'", *AccessMode);
 442         return NULL;
 443     }
 444 
 445     iohandler ->ContextID = ContextID;
 446     iohandler ->stream = (void*) fm;
 447     iohandler ->UsedSpace = 0;
 448 
 449     // Keep track of the original file
 450     strncpy(iohandler -> PhysicalFile, FileName, sizeof(iohandler -> PhysicalFile)-1);
 451     iohandler -> PhysicalFile[sizeof(iohandler -> PhysicalFile)-1] = 0;
 452 
 453     iohandler ->Read    = FileRead;
 454     iohandler ->Seek    = FileSeek;
 455     iohandler ->Close   = FileClose;
 456     iohandler ->Tell    = FileTell;
 457     iohandler ->Write   = FileWrite;
 458 
 459     return iohandler;
 460 }
 461 
 462 // Create a iohandler for stream based files
 463 cmsIOHANDLER* CMSEXPORT cmsOpenIOhandlerFromStream(cmsContext ContextID, FILE* Stream)
 464 {
 465     cmsIOHANDLER* iohandler = NULL;
 466     cmsInt32Number fileSize;
 467 
 468     fileSize = cmsfilelength(Stream);
 469     if (fileSize < 0)
 470     {
 471         cmsSignalError(ContextID, cmsERROR_FILE, "Cannot get size of stream");
 472         return NULL;
 473     }
 474 
 475     iohandler = (cmsIOHANDLER*) _cmsMallocZero(ContextID, sizeof(cmsIOHANDLER));
 476     if (iohandler == NULL) return NULL;
 477 
 478     iohandler -> ContextID = ContextID;
 479     iohandler -> stream = (void*) Stream;
 480     iohandler -> UsedSpace = 0;
 481     iohandler -> ReportedSize = (cmsUInt32Number) fileSize;
 482     iohandler -> PhysicalFile[0] = 0;
 483 
 484     iohandler ->Read    = FileRead;
 485     iohandler ->Seek    = FileSeek;
 486     iohandler ->Close   = FileClose;
 487     iohandler ->Tell    = FileTell;
 488     iohandler ->Write   = FileWrite;
 489 
 490     return iohandler;
 491 }
 492 
 493 
 494 
 495 // Close an open IO handler
 496 cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io)
 497 {
 498     return io -> Close(io);
 499 }
 500 
 501 // -------------------------------------------------------------------------------------------------------
 502 
 503 cmsIOHANDLER* CMSEXPORT cmsGetProfileIOhandler(cmsHPROFILE hProfile)
 504 {
 505         _cmsICCPROFILE* Icc = (_cmsICCPROFILE*)hProfile;
 506 
 507         if (Icc == NULL) return NULL;
 508         return Icc->IOhandler;
 509 }
 510 
 511 // Creates an empty structure holding all required parameters
 512 cmsHPROFILE CMSEXPORT cmsCreateProfilePlaceholder(cmsContext ContextID)
 513 {
 514     time_t now = time(NULL);
 515     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) _cmsMallocZero(ContextID, sizeof(_cmsICCPROFILE));
 516     if (Icc == NULL) return NULL;
 517 
 518     Icc ->ContextID = ContextID;
 519 
 520     // Set it to empty
 521     Icc -> TagCount   = 0;
 522 
 523     // Set default version
 524     Icc ->Version =  0x02100000;
 525 
 526     // Set creation date/time
 527     memmove(&Icc ->Created, gmtime(&now), sizeof(Icc ->Created));
 528 
 529     // Create a mutex if the user provided proper plugin. NULL otherwise
 530     Icc ->UsrMutex = _cmsCreateMutex(ContextID);
 531 
 532     // Return the handle
 533     return (cmsHPROFILE) Icc;
 534 }
 535 
 536 cmsContext CMSEXPORT cmsGetProfileContextID(cmsHPROFILE hProfile)
 537 {
 538      _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
 539 
 540     if (Icc == NULL) return NULL;
 541     return Icc -> ContextID;
 542 }
 543 
 544 
 545 // Return the number of tags
 546 cmsInt32Number CMSEXPORT cmsGetTagCount(cmsHPROFILE hProfile)
 547 {
 548     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
 549     if (Icc == NULL) return -1;
 550 
 551     return  Icc->TagCount;
 552 }
 553 
 554 // Return the tag signature of a given tag number
 555 cmsTagSignature CMSEXPORT cmsGetTagSignature(cmsHPROFILE hProfile, cmsUInt32Number n)
 556 {
 557     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
 558 
 559     if (n > Icc->TagCount) return (cmsTagSignature) 0;  // Mark as not available
 560     if (n >= MAX_TABLE_TAG) return (cmsTagSignature) 0; // As double check
 561 
 562     return Icc ->TagNames[n];
 563 }
 564 
 565 
 566 static
 567 int SearchOneTag(_cmsICCPROFILE* Profile, cmsTagSignature sig)
 568 {
 569     cmsUInt32Number i;
 570 
 571     for (i=0; i < Profile -> TagCount; i++) {
 572 
 573         if (sig == Profile -> TagNames[i])
 574             return i;
 575     }
 576 
 577     return -1;
 578 }
 579 
 580 // Search for a specific tag in tag dictionary. Returns position or -1 if tag not found.
 581 // If followlinks is turned on, then the position of the linked tag is returned
 582 int _cmsSearchTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, cmsBool lFollowLinks)
 583 {
 584     int n;
 585     cmsTagSignature LinkedSig;
 586 
 587     do {
 588 
 589         // Search for given tag in ICC profile directory
 590         n = SearchOneTag(Icc, sig);
 591         if (n < 0)
 592             return -1;        // Not found
 593 
 594         if (!lFollowLinks)
 595             return n;         // Found, don't follow links
 596 
 597         // Is this a linked tag?
 598         LinkedSig = Icc ->TagLinked[n];
 599 
 600         // Yes, follow link
 601         if (LinkedSig != (cmsTagSignature) 0) {
 602             sig = LinkedSig;
 603         }
 604 
 605     } while (LinkedSig != (cmsTagSignature) 0);
 606 
 607     return n;
 608 }
 609 
 610 // Deletes a tag entry
 611 
 612 static
 613 void _cmsDeleteTagByPos(_cmsICCPROFILE* Icc, int i)
 614 {
 615     _cmsAssert(Icc != NULL);
 616     _cmsAssert(i >= 0);
 617 
 618 
 619     if (Icc -> TagPtrs[i] != NULL) {
 620 
 621         // Free previous version
 622         if (Icc ->TagSaveAsRaw[i]) {
 623             _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]);
 624         }
 625         else {
 626             cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i];
 627 
 628             if (TypeHandler != NULL) {
 629 
 630                 cmsTagTypeHandler LocalTypeHandler = *TypeHandler;
 631                 LocalTypeHandler.ContextID = Icc ->ContextID;              // As an additional parameter
 632                 LocalTypeHandler.ICCVersion = Icc ->Version;
 633                 LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]);
 634                 Icc ->TagPtrs[i] = NULL;
 635             }
 636         }
 637 
 638     }
 639 }
 640 
 641 
 642 // Creates a new tag entry
 643 static
 644 cmsBool _cmsNewTag(_cmsICCPROFILE* Icc, cmsTagSignature sig, int* NewPos)
 645 {
 646     int i;
 647 
 648     // Search for the tag
 649     i = _cmsSearchTag(Icc, sig, FALSE);
 650     if (i >= 0) {
 651 
 652         // Already exists? delete it
 653         _cmsDeleteTagByPos(Icc, i);
 654         *NewPos = i;
 655     }
 656     else  {
 657 
 658         // No, make a new one
 659 
 660         if (Icc -> TagCount >= MAX_TABLE_TAG) {
 661             cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", MAX_TABLE_TAG);
 662             return FALSE;
 663         }
 664 
 665         *NewPos = Icc ->TagCount;
 666         Icc -> TagCount++;
 667     }
 668 
 669     return TRUE;
 670 }
 671 
 672 
 673 // Check existence
 674 cmsBool CMSEXPORT cmsIsTag(cmsHPROFILE hProfile, cmsTagSignature sig)
 675 {
 676        _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) (void*) hProfile;
 677        return _cmsSearchTag(Icc, sig, FALSE) >= 0;
 678 }
 679 
 680 
 681 
 682 // Enforces that the profile version is per. spec.
 683 // Operates on the big endian bytes from the profile.
 684 // Called before converting to platform endianness.
 685 // Byte 0 is BCD major version, so max 9.
 686 // Byte 1 is 2 BCD digits, one per nibble.
 687 // Reserved bytes 2 & 3 must be 0.
 688 static
 689 cmsUInt32Number _validatedVersion(cmsUInt32Number DWord)
 690 {
 691     cmsUInt8Number* pByte = (cmsUInt8Number*) &DWord;
 692     cmsUInt8Number temp1;
 693     cmsUInt8Number temp2;
 694 
 695     if (*pByte > 0x09) *pByte = (cmsUInt8Number) 0x09;
 696     temp1 = *(pByte+1) & 0xf0;
 697     temp2 = *(pByte+1) & 0x0f;
 698     if (temp1 > 0x90) temp1 = 0x90;
 699     if (temp2 > 0x09) temp2 = 0x09;
 700     *(pByte+1) = (cmsUInt8Number)(temp1 | temp2);
 701     *(pByte+2) = (cmsUInt8Number)0;
 702     *(pByte+3) = (cmsUInt8Number)0;
 703 
 704     return DWord;
 705 }
 706 
 707 // Read profile header and validate it
 708 cmsBool _cmsReadHeader(_cmsICCPROFILE* Icc)
 709 {
 710     cmsTagEntry Tag;
 711     cmsICCHeader Header;
 712     cmsUInt32Number i, j;
 713     cmsUInt32Number HeaderSize;
 714     cmsIOHANDLER* io = Icc ->IOhandler;
 715     cmsUInt32Number TagCount;
 716 
 717 
 718     // Read the header
 719     if (io -> Read(io, &Header, sizeof(cmsICCHeader), 1) != 1) {
 720         return FALSE;
 721     }
 722 
 723     // Validate file as an ICC profile
 724     if (_cmsAdjustEndianess32(Header.magic) != cmsMagicNumber) {
 725         cmsSignalError(Icc ->ContextID, cmsERROR_BAD_SIGNATURE, "not an ICC profile, invalid signature");
 726         return FALSE;
 727     }
 728 
 729     // Adjust endianness of the used parameters
 730     Icc -> DeviceClass     = (cmsProfileClassSignature) _cmsAdjustEndianess32(Header.deviceClass);
 731     Icc -> ColorSpace      = (cmsColorSpaceSignature)   _cmsAdjustEndianess32(Header.colorSpace);
 732     Icc -> PCS             = (cmsColorSpaceSignature)   _cmsAdjustEndianess32(Header.pcs);
 733 
 734     Icc -> RenderingIntent = _cmsAdjustEndianess32(Header.renderingIntent);
 735     Icc -> flags           = _cmsAdjustEndianess32(Header.flags);
 736     Icc -> manufacturer    = _cmsAdjustEndianess32(Header.manufacturer);
 737     Icc -> model           = _cmsAdjustEndianess32(Header.model);
 738     Icc -> creator         = _cmsAdjustEndianess32(Header.creator);
 739 
 740     _cmsAdjustEndianess64(&Icc -> attributes, &Header.attributes);
 741     Icc -> Version         = _cmsAdjustEndianess32(_validatedVersion(Header.version));
 742 
 743     // Get size as reported in header
 744     HeaderSize = _cmsAdjustEndianess32(Header.size);
 745 
 746     // Make sure HeaderSize is lower than profile size
 747     if (HeaderSize >= Icc ->IOhandler ->ReportedSize)
 748             HeaderSize = Icc ->IOhandler ->ReportedSize;
 749 
 750 
 751     // Get creation date/time
 752     _cmsDecodeDateTimeNumber(&Header.date, &Icc ->Created);
 753 
 754     // The profile ID are 32 raw bytes
 755     memmove(Icc ->ProfileID.ID32, Header.profileID.ID32, 16);
 756 
 757 
 758     // Read tag directory
 759     if (!_cmsReadUInt32Number(io, &TagCount)) return FALSE;
 760     if (TagCount > MAX_TABLE_TAG) {
 761 
 762         cmsSignalError(Icc ->ContextID, cmsERROR_RANGE, "Too many tags (%d)", TagCount);
 763         return FALSE;
 764     }
 765 
 766 
 767     // Read tag directory
 768     Icc -> TagCount = 0;
 769     for (i=0; i < TagCount; i++) {
 770 
 771         if (!_cmsReadUInt32Number(io, (cmsUInt32Number *) &Tag.sig)) return FALSE;
 772         if (!_cmsReadUInt32Number(io, &Tag.offset)) return FALSE;
 773         if (!_cmsReadUInt32Number(io, &Tag.size)) return FALSE;
 774 
 775         // Perform some sanity check. Offset + size should fall inside file.
 776         if (Tag.offset + Tag.size > HeaderSize ||
 777             Tag.offset + Tag.size < Tag.offset)
 778                   continue;
 779 
 780         Icc -> TagNames[Icc ->TagCount]   = Tag.sig;
 781         Icc -> TagOffsets[Icc ->TagCount] = Tag.offset;
 782         Icc -> TagSizes[Icc ->TagCount]   = Tag.size;
 783 
 784        // Search for links
 785         for (j=0; j < Icc ->TagCount; j++) {
 786 
 787             if ((Icc ->TagOffsets[j] == Tag.offset) &&
 788                 (Icc ->TagSizes[j]   == Tag.size)) {
 789 
 790                 Icc ->TagLinked[Icc ->TagCount] = Icc ->TagNames[j];
 791             }
 792 
 793         }
 794 
 795         Icc ->TagCount++;
 796     }
 797 
 798     return TRUE;
 799 }
 800 
 801 // Saves profile header
 802 cmsBool _cmsWriteHeader(_cmsICCPROFILE* Icc, cmsUInt32Number UsedSpace)
 803 {
 804     cmsICCHeader Header;
 805     cmsUInt32Number i;
 806     cmsTagEntry Tag;
 807     cmsInt32Number Count = 0;
 808 
 809     Header.size        = _cmsAdjustEndianess32(UsedSpace);
 810     Header.cmmId       = _cmsAdjustEndianess32(lcmsSignature);
 811     Header.version     = _cmsAdjustEndianess32(Icc ->Version);
 812 
 813     Header.deviceClass = (cmsProfileClassSignature) _cmsAdjustEndianess32(Icc -> DeviceClass);
 814     Header.colorSpace  = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> ColorSpace);
 815     Header.pcs         = (cmsColorSpaceSignature) _cmsAdjustEndianess32(Icc -> PCS);
 816 
 817     //   NOTE: in v4 Timestamp must be in UTC rather than in local time
 818     _cmsEncodeDateTimeNumber(&Header.date, &Icc ->Created);
 819 
 820     Header.magic       = _cmsAdjustEndianess32(cmsMagicNumber);
 821 
 822 #ifdef CMS_IS_WINDOWS_
 823     Header.platform    = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMicrosoft);
 824 #else
 825     Header.platform    = (cmsPlatformSignature) _cmsAdjustEndianess32(cmsSigMacintosh);
 826 #endif
 827 
 828     Header.flags        = _cmsAdjustEndianess32(Icc -> flags);
 829     Header.manufacturer = _cmsAdjustEndianess32(Icc -> manufacturer);
 830     Header.model        = _cmsAdjustEndianess32(Icc -> model);
 831 
 832     _cmsAdjustEndianess64(&Header.attributes, &Icc -> attributes);
 833 
 834     // Rendering intent in the header (for embedded profiles)
 835     Header.renderingIntent = _cmsAdjustEndianess32(Icc -> RenderingIntent);
 836 
 837     // Illuminant is always D50
 838     Header.illuminant.X = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->X));
 839     Header.illuminant.Y = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Y));
 840     Header.illuminant.Z = _cmsAdjustEndianess32(_cmsDoubleTo15Fixed16(cmsD50_XYZ()->Z));
 841 
 842     // Created by LittleCMS (that's me!)
 843     Header.creator      = _cmsAdjustEndianess32(lcmsSignature);
 844 
 845     memset(&Header.reserved, 0, sizeof(Header.reserved));
 846 
 847     // Set profile ID. Endianness is always big endian
 848     memmove(&Header.profileID, &Icc ->ProfileID, 16);
 849 
 850     // Dump the header
 851     if (!Icc -> IOhandler->Write(Icc->IOhandler, sizeof(cmsICCHeader), &Header)) return FALSE;
 852 
 853     // Saves Tag directory
 854 
 855     // Get true count
 856     for (i=0;  i < Icc -> TagCount; i++) {
 857         if (Icc ->TagNames[i] != (cmsTagSignature) 0)
 858             Count++;
 859     }
 860 
 861     // Store number of tags
 862     if (!_cmsWriteUInt32Number(Icc ->IOhandler, Count)) return FALSE;
 863 
 864     for (i=0; i < Icc -> TagCount; i++) {
 865 
 866         if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue;   // It is just a placeholder
 867 
 868         Tag.sig    = (cmsTagSignature) _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagNames[i]);
 869         Tag.offset = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagOffsets[i]);
 870         Tag.size   = _cmsAdjustEndianess32((cmsInt32Number) Icc -> TagSizes[i]);
 871 
 872         if (!Icc ->IOhandler -> Write(Icc-> IOhandler, sizeof(cmsTagEntry), &Tag)) return FALSE;
 873     }
 874 
 875     return TRUE;
 876 }
 877 
 878 // ----------------------------------------------------------------------- Set/Get several struct members
 879 
 880 
 881 cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile)
 882 {
 883     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 884     return Icc -> RenderingIntent;
 885 }
 886 
 887 void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent)
 888 {
 889     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 890     Icc -> RenderingIntent = RenderingIntent;
 891 }
 892 
 893 cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile)
 894 {
 895     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 896     return (cmsUInt32Number) Icc -> flags;
 897 }
 898 
 899 void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags)
 900 {
 901     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 902     Icc -> flags = (cmsUInt32Number) Flags;
 903 }
 904 
 905 cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile)
 906 {
 907     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 908     return Icc ->manufacturer;
 909 }
 910 
 911 void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer)
 912 {
 913     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 914     Icc -> manufacturer = manufacturer;
 915 }
 916 
 917 cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile)
 918 {
 919     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 920     return Icc ->creator;
 921 }
 922 
 923 cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile)
 924 {
 925     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 926     return Icc ->model;
 927 }
 928 
 929 void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model)
 930 {
 931     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 932     Icc -> model = model;
 933 }
 934 
 935 void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags)
 936 {
 937     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 938     memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number));
 939 }
 940 
 941 void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags)
 942 {
 943     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 944     memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number));
 945 }
 946 
 947 void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID)
 948 {
 949     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 950     memmove(ProfileID, Icc ->ProfileID.ID8, 16);
 951 }
 952 
 953 void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID)
 954 {
 955     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 956     memmove(&Icc -> ProfileID, ProfileID, 16);
 957 }
 958 
 959 cmsBool  CMSEXPORT cmsGetHeaderCreationDateTime(cmsHPROFILE hProfile, struct tm *Dest)
 960 {
 961     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 962     memmove(Dest, &Icc ->Created, sizeof(struct tm));
 963     return TRUE;
 964 }
 965 
 966 cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile)
 967 {
 968     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 969     return Icc -> PCS;
 970 }
 971 
 972 void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs)
 973 {
 974     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 975     Icc -> PCS = pcs;
 976 }
 977 
 978 cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile)
 979 {
 980     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 981     return Icc -> ColorSpace;
 982 }
 983 
 984 void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig)
 985 {
 986     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 987     Icc -> ColorSpace = sig;
 988 }
 989 
 990 cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile)
 991 {
 992     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 993     return Icc -> DeviceClass;
 994 }
 995 
 996 void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig)
 997 {
 998     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
 999     Icc -> DeviceClass = sig;
1000 }
1001 
1002 cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile)
1003 {
1004     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1005     return Icc -> Version;
1006 }
1007 
1008 void CMSEXPORT cmsSetEncodedICCversion(cmsHPROFILE hProfile, cmsUInt32Number Version)
1009 {
1010     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1011     Icc -> Version = Version;
1012 }
1013 
1014 // Get an hexadecimal number with same digits as v
1015 static
1016 cmsUInt32Number BaseToBase(cmsUInt32Number in, int BaseIn, int BaseOut)
1017 {
1018     char Buff[100];
1019     int i, len;
1020     cmsUInt32Number out;
1021 
1022     for (len=0; in > 0 && len < 100; len++) {
1023 
1024         Buff[len] = (char) (in % BaseIn);
1025         in /= BaseIn;
1026     }
1027 
1028     for (i=len-1, out=0; i >= 0; --i) {
1029         out = out * BaseOut + Buff[i];
1030     }
1031 
1032     return out;
1033 }
1034 
1035 void  CMSEXPORT cmsSetProfileVersion(cmsHPROFILE hProfile, cmsFloat64Number Version)
1036 {
1037     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1038 
1039     // 4.2 -> 0x4200000
1040 
1041     Icc -> Version = BaseToBase((cmsUInt32Number) floor(Version * 100.0 + 0.5), 10, 16) << 16;
1042 }
1043 
1044 cmsFloat64Number CMSEXPORT cmsGetProfileVersion(cmsHPROFILE hProfile)
1045 {
1046     _cmsICCPROFILE*  Icc = (_cmsICCPROFILE*) hProfile;
1047     cmsUInt32Number n = Icc -> Version >> 16;
1048 
1049     return BaseToBase(n, 16, 10) / 100.0;
1050 }
1051 // --------------------------------------------------------------------------------------------------------------
1052 
1053 
1054 // Create profile from IOhandler
1055 cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandlerTHR(cmsContext ContextID, cmsIOHANDLER* io)
1056 {
1057     _cmsICCPROFILE* NewIcc;
1058     cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);
1059 
1060     if (hEmpty == NULL) return NULL;
1061 
1062     NewIcc = (_cmsICCPROFILE*) hEmpty;
1063 
1064     NewIcc ->IOhandler = io;
1065     if (!_cmsReadHeader(NewIcc)) goto Error;
1066     return hEmpty;
1067 
1068 Error:
1069     cmsCloseProfile(hEmpty);
1070     return NULL;
1071 }
1072 
1073 // Create profile from IOhandler
1074 cmsHPROFILE CMSEXPORT cmsOpenProfileFromIOhandler2THR(cmsContext ContextID, cmsIOHANDLER* io, cmsBool write)
1075 {
1076     _cmsICCPROFILE* NewIcc;
1077     cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);
1078 
1079     if (hEmpty == NULL) return NULL;
1080 
1081     NewIcc = (_cmsICCPROFILE*) hEmpty;
1082 
1083     NewIcc ->IOhandler = io;
1084     if (write) {
1085 
1086         NewIcc -> IsWrite = TRUE;
1087         return hEmpty;
1088     }
1089 
1090     if (!_cmsReadHeader(NewIcc)) goto Error;
1091     return hEmpty;
1092 
1093 Error:
1094     cmsCloseProfile(hEmpty);
1095     return NULL;
1096 }
1097 
1098 
1099 // Create profile from disk file
1100 cmsHPROFILE CMSEXPORT cmsOpenProfileFromFileTHR(cmsContext ContextID, const char *lpFileName, const char *sAccess)
1101 {
1102     _cmsICCPROFILE* NewIcc;
1103     cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);
1104 
1105     if (hEmpty == NULL) return NULL;
1106 
1107     NewIcc = (_cmsICCPROFILE*) hEmpty;
1108 
1109     NewIcc ->IOhandler = cmsOpenIOhandlerFromFile(ContextID, lpFileName, sAccess);
1110     if (NewIcc ->IOhandler == NULL) goto Error;
1111 
1112     if (*sAccess == 'W' || *sAccess == 'w') {
1113 
1114         NewIcc -> IsWrite = TRUE;
1115 
1116         return hEmpty;
1117     }
1118 
1119     if (!_cmsReadHeader(NewIcc)) goto Error;
1120     return hEmpty;
1121 
1122 Error:
1123     cmsCloseProfile(hEmpty);
1124     return NULL;
1125 }
1126 
1127 
1128 cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess)
1129 {
1130     return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess);
1131 }
1132 
1133 
1134 cmsHPROFILE  CMSEXPORT cmsOpenProfileFromStreamTHR(cmsContext ContextID, FILE* ICCProfile, const char *sAccess)
1135 {
1136     _cmsICCPROFILE* NewIcc;
1137     cmsHPROFILE hEmpty = cmsCreateProfilePlaceholder(ContextID);
1138 
1139     if (hEmpty == NULL) return NULL;
1140 
1141     NewIcc = (_cmsICCPROFILE*) hEmpty;
1142 
1143     NewIcc ->IOhandler = cmsOpenIOhandlerFromStream(ContextID, ICCProfile);
1144     if (NewIcc ->IOhandler == NULL) goto Error;
1145 
1146     if (*sAccess == 'w') {
1147 
1148         NewIcc -> IsWrite = TRUE;
1149         return hEmpty;
1150     }
1151 
1152     if (!_cmsReadHeader(NewIcc)) goto Error;
1153     return hEmpty;
1154 
1155 Error:
1156     cmsCloseProfile(hEmpty);
1157     return NULL;
1158 
1159 }
1160 
1161 cmsHPROFILE  CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess)
1162 {
1163     return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess);
1164 }
1165 
1166 
1167 // Open from memory block
1168 cmsHPROFILE CMSEXPORT cmsOpenProfileFromMemTHR(cmsContext ContextID, const void* MemPtr, cmsUInt32Number dwSize)
1169 {
1170     _cmsICCPROFILE* NewIcc;
1171     cmsHPROFILE hEmpty;
1172 
1173     hEmpty = cmsCreateProfilePlaceholder(ContextID);
1174     if (hEmpty == NULL) return NULL;
1175 
1176     NewIcc = (_cmsICCPROFILE*) hEmpty;
1177 
1178     // Ok, in this case const void* is casted to void* just because open IO handler
1179     // shares read and writting modes. Don't abuse this feature!
1180     NewIcc ->IOhandler = cmsOpenIOhandlerFromMem(ContextID, (void*) MemPtr, dwSize, "r");
1181     if (NewIcc ->IOhandler == NULL) goto Error;
1182 
1183     if (!_cmsReadHeader(NewIcc)) goto Error;
1184 
1185     return hEmpty;
1186 
1187 Error:
1188     cmsCloseProfile(hEmpty);
1189     return NULL;
1190 }
1191 
1192 cmsHPROFILE CMSEXPORT cmsOpenProfileFromMem(const void* MemPtr, cmsUInt32Number dwSize)
1193 {
1194     return cmsOpenProfileFromMemTHR(NULL, MemPtr, dwSize);
1195 }
1196 
1197 
1198 
1199 // Dump tag contents. If the profile is being modified, untouched tags are copied from FileOrig
1200 static
1201 cmsBool SaveTags(_cmsICCPROFILE* Icc, _cmsICCPROFILE* FileOrig)
1202 {
1203     cmsUInt8Number* Data;
1204     cmsUInt32Number i;
1205     cmsUInt32Number Begin;
1206     cmsIOHANDLER* io = Icc ->IOhandler;
1207     cmsTagDescriptor* TagDescriptor;
1208     cmsTagTypeSignature TypeBase;
1209     cmsTagTypeSignature Type;
1210     cmsTagTypeHandler* TypeHandler;
1211     cmsFloat64Number   Version = cmsGetProfileVersion((cmsHPROFILE) Icc);
1212     cmsTagTypeHandler LocalTypeHandler;
1213 
1214     for (i=0; i < Icc -> TagCount; i++) {
1215 
1216         if (Icc ->TagNames[i] == (cmsTagSignature) 0) continue;
1217 
1218         // Linked tags are not written
1219         if (Icc ->TagLinked[i] != (cmsTagSignature) 0) continue;
1220 
1221         Icc -> TagOffsets[i] = Begin = io ->UsedSpace;
1222 
1223         Data = (cmsUInt8Number*)  Icc -> TagPtrs[i];
1224 
1225         if (!Data) {
1226 
1227             // Reach here if we are copying a tag from a disk-based ICC profile which has not been modified by user.
1228             // In this case a blind copy of the block data is performed
1229             if (FileOrig != NULL && Icc -> TagOffsets[i]) {
1230 
1231                 cmsUInt32Number TagSize   = FileOrig -> TagSizes[i];
1232                 cmsUInt32Number TagOffset = FileOrig -> TagOffsets[i];
1233                 void* Mem;
1234 
1235                 if (!FileOrig ->IOhandler->Seek(FileOrig ->IOhandler, TagOffset)) return FALSE;
1236 
1237                 Mem = _cmsMalloc(Icc ->ContextID, TagSize);
1238                 if (Mem == NULL) return FALSE;
1239 
1240                 if (FileOrig ->IOhandler->Read(FileOrig->IOhandler, Mem, TagSize, 1) != 1) return FALSE;
1241                 if (!io ->Write(io, TagSize, Mem)) return FALSE;
1242                 _cmsFree(Icc ->ContextID, Mem);
1243 
1244                 Icc -> TagSizes[i] = (io ->UsedSpace - Begin);
1245 
1246 
1247                 // Align to 32 bit boundary.
1248                 if (! _cmsWriteAlignment(io))
1249                     return FALSE;
1250             }
1251 
1252             continue;
1253         }
1254 
1255 
1256         // Should this tag be saved as RAW? If so, tagsizes should be specified in advance (no further cooking is done)
1257         if (Icc ->TagSaveAsRaw[i]) {
1258 
1259             if (io -> Write(io, Icc ->TagSizes[i], Data) != 1) return FALSE;
1260         }
1261         else {
1262 
1263             // Search for support on this tag
1264             TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, Icc -> TagNames[i]);
1265             if (TagDescriptor == NULL) continue;                        // Unsupported, ignore it
1266 
1267             if (TagDescriptor ->DecideType != NULL) {
1268 
1269                 Type = TagDescriptor ->DecideType(Version, Data);
1270             }
1271             else {
1272 
1273                 Type = TagDescriptor ->SupportedTypes[0];
1274             }
1275 
1276             TypeHandler =  _cmsGetTagTypeHandler(Icc->ContextID, Type);
1277 
1278             if (TypeHandler == NULL) {
1279                 cmsSignalError(Icc ->ContextID, cmsERROR_INTERNAL, "(Internal) no handler for tag %x", Icc -> TagNames[i]);
1280                 continue;
1281             }
1282 
1283             TypeBase = TypeHandler ->Signature;
1284             if (!_cmsWriteTypeBase(io, TypeBase))
1285                 return FALSE;
1286 
1287             LocalTypeHandler = *TypeHandler;
1288             LocalTypeHandler.ContextID  = Icc ->ContextID;
1289             LocalTypeHandler.ICCVersion = Icc ->Version;
1290             if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, io, Data, TagDescriptor ->ElemCount)) {
1291 
1292                 char String[5];
1293 
1294                 _cmsTagSignature2String(String, (cmsTagSignature) TypeBase);
1295                 cmsSignalError(Icc ->ContextID, cmsERROR_WRITE, "Couldn't write type '%s'", String);
1296                 return FALSE;
1297             }
1298         }
1299 
1300 
1301         Icc -> TagSizes[i] = (io ->UsedSpace - Begin);
1302 
1303         // Align to 32 bit boundary.
1304         if (! _cmsWriteAlignment(io))
1305             return FALSE;
1306     }
1307 
1308 
1309     return TRUE;
1310 }
1311 
1312 
1313 // Fill the offset and size fields for all linked tags
1314 static
1315 cmsBool SetLinks( _cmsICCPROFILE* Icc)
1316 {
1317     cmsUInt32Number i;
1318 
1319     for (i=0; i < Icc -> TagCount; i++) {
1320 
1321         cmsTagSignature lnk = Icc ->TagLinked[i];
1322         if (lnk != (cmsTagSignature) 0) {
1323 
1324             int j = _cmsSearchTag(Icc, lnk, FALSE);
1325             if (j >= 0) {
1326 
1327                 Icc ->TagOffsets[i] = Icc ->TagOffsets[j];
1328                 Icc ->TagSizes[i]   = Icc ->TagSizes[j];
1329             }
1330 
1331         }
1332     }
1333 
1334     return TRUE;
1335 }
1336 
1337 // Low-level save to IOHANDLER. It returns the number of bytes used to
1338 // store the profile, or zero on error. io may be NULL and in this case
1339 // no data is written--only sizes are calculated
1340 cmsUInt32Number CMSEXPORT cmsSaveProfileToIOhandler(cmsHPROFILE hProfile, cmsIOHANDLER* io)
1341 {
1342     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1343     _cmsICCPROFILE Keep;
1344     cmsIOHANDLER* PrevIO = NULL;
1345     cmsUInt32Number UsedSpace;
1346     cmsContext ContextID;
1347 
1348     _cmsAssert(hProfile != NULL);
1349 
1350     if (!_cmsLockMutex(Icc->ContextID, Icc->UsrMutex)) return 0;
1351     memmove(&Keep, Icc, sizeof(_cmsICCPROFILE));
1352 
1353     ContextID = cmsGetProfileContextID(hProfile);
1354     PrevIO = Icc ->IOhandler = cmsOpenIOhandlerFromNULL(ContextID);
1355     if (PrevIO == NULL) {
1356         _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
1357         return 0;
1358     }
1359 
1360     // Pass #1 does compute offsets
1361 
1362     if (!_cmsWriteHeader(Icc, 0)) goto Error;
1363     if (!SaveTags(Icc, &Keep)) goto Error;
1364 
1365     UsedSpace = PrevIO ->UsedSpace;
1366 
1367     // Pass #2 does save to iohandler
1368 
1369     if (io != NULL) {
1370 
1371         Icc ->IOhandler = io;
1372         if (!SetLinks(Icc)) goto Error;
1373         if (!_cmsWriteHeader(Icc, UsedSpace)) goto Error;
1374         if (!SaveTags(Icc, &Keep)) goto Error;
1375     }
1376 
1377     memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
1378     if (!cmsCloseIOhandler(PrevIO))
1379         UsedSpace = 0; // As a error marker
1380 
1381     _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
1382 
1383     return UsedSpace;
1384 
1385 
1386 Error:
1387     cmsCloseIOhandler(PrevIO);
1388     memmove(Icc, &Keep, sizeof(_cmsICCPROFILE));
1389     _cmsUnlockMutex(Icc->ContextID, Icc->UsrMutex);
1390 
1391     return 0;
1392 }
1393 
1394 
1395 // Low-level save to disk.
1396 cmsBool  CMSEXPORT cmsSaveProfileToFile(cmsHPROFILE hProfile, const char* FileName)
1397 {
1398     cmsContext ContextID = cmsGetProfileContextID(hProfile);
1399     cmsIOHANDLER* io = cmsOpenIOhandlerFromFile(ContextID, FileName, "w");
1400     cmsBool rc;
1401 
1402     if (io == NULL) return FALSE;
1403 
1404     rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0);
1405     rc &= cmsCloseIOhandler(io);
1406 
1407     if (rc == FALSE) {          // remove() is C99 per 7.19.4.1
1408             remove(FileName);   // We have to IGNORE return value in this case
1409     }
1410     return rc;
1411 }
1412 
1413 // Same as anterior, but for streams
1414 cmsBool CMSEXPORT cmsSaveProfileToStream(cmsHPROFILE hProfile, FILE* Stream)
1415 {
1416     cmsBool rc;
1417     cmsContext ContextID = cmsGetProfileContextID(hProfile);
1418     cmsIOHANDLER* io = cmsOpenIOhandlerFromStream(ContextID, Stream);
1419 
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 // Same as anterior, but for memory blocks. In this case, a NULL as MemPtr means calculate needed space only
1430 cmsBool CMSEXPORT cmsSaveProfileToMem(cmsHPROFILE hProfile, void *MemPtr, cmsUInt32Number* BytesNeeded)
1431 {
1432     cmsBool rc;
1433     cmsIOHANDLER* io;
1434     cmsContext ContextID = cmsGetProfileContextID(hProfile);
1435 
1436     _cmsAssert(BytesNeeded != NULL);
1437 
1438     // Should we just calculate the needed space?
1439     if (MemPtr == NULL) {
1440 
1441            *BytesNeeded =  cmsSaveProfileToIOhandler(hProfile, NULL);
1442             return (*BytesNeeded == 0) ? FALSE : TRUE;
1443     }
1444 
1445     // That is a real write operation
1446     io =  cmsOpenIOhandlerFromMem(ContextID, MemPtr, *BytesNeeded, "w");
1447     if (io == NULL) return FALSE;
1448 
1449     rc = (cmsSaveProfileToIOhandler(hProfile, io) != 0);
1450     rc &= cmsCloseIOhandler(io);
1451 
1452     return rc;
1453 }
1454 
1455 
1456 
1457 // Closes a profile freeing any involved resources
1458 cmsBool  CMSEXPORT cmsCloseProfile(cmsHPROFILE hProfile)
1459 {
1460     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1461     cmsBool  rc = TRUE;
1462     cmsUInt32Number i;
1463 
1464     if (!Icc) return FALSE;
1465 
1466     // Was open in write mode?
1467     if (Icc ->IsWrite) {
1468 
1469         Icc ->IsWrite = FALSE;      // Assure no further writting
1470         rc &= cmsSaveProfileToFile(hProfile, Icc ->IOhandler->PhysicalFile);
1471     }
1472 
1473     for (i=0; i < Icc -> TagCount; i++) {
1474 
1475         if (Icc -> TagPtrs[i]) {
1476 
1477             cmsTagTypeHandler* TypeHandler = Icc ->TagTypeHandlers[i];
1478 
1479             if (TypeHandler != NULL) {
1480                 cmsTagTypeHandler LocalTypeHandler = *TypeHandler;
1481 
1482                 LocalTypeHandler.ContextID = Icc ->ContextID;              // As an additional parameters
1483                 LocalTypeHandler.ICCVersion = Icc ->Version;
1484                 LocalTypeHandler.FreePtr(&LocalTypeHandler, Icc -> TagPtrs[i]);
1485             }
1486             else
1487                 _cmsFree(Icc ->ContextID, Icc ->TagPtrs[i]);
1488         }
1489     }
1490 
1491     if (Icc ->IOhandler != NULL) {
1492         rc &= cmsCloseIOhandler(Icc->IOhandler);
1493     }
1494 
1495     _cmsDestroyMutex(Icc->ContextID, Icc->UsrMutex);
1496 
1497     _cmsFree(Icc ->ContextID, Icc);   // Free placeholder memory
1498 
1499     return rc;
1500 }
1501 
1502 
1503 // -------------------------------------------------------------------------------------------------------------------
1504 
1505 
1506 // Returns TRUE if a given tag is supported by a plug-in
1507 static
1508 cmsBool IsTypeSupported(cmsTagDescriptor* TagDescriptor, cmsTagTypeSignature Type)
1509 {
1510     cmsUInt32Number i, nMaxTypes;
1511 
1512     nMaxTypes = TagDescriptor->nSupportedTypes;
1513     if (nMaxTypes >= MAX_TYPES_IN_LCMS_PLUGIN)
1514         nMaxTypes = MAX_TYPES_IN_LCMS_PLUGIN;
1515 
1516     for (i=0; i < nMaxTypes; i++) {
1517         if (Type == TagDescriptor ->SupportedTypes[i]) return TRUE;
1518     }
1519 
1520     return FALSE;
1521 }
1522 
1523 
1524 // That's the main read function
1525 void* CMSEXPORT cmsReadTag(cmsHPROFILE hProfile, cmsTagSignature sig)
1526 {
1527     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1528     cmsIOHANDLER* io = Icc ->IOhandler;
1529     cmsTagTypeHandler* TypeHandler;
1530     cmsTagTypeHandler LocalTypeHandler;
1531     cmsTagDescriptor*  TagDescriptor;
1532     cmsTagTypeSignature BaseType;
1533     cmsUInt32Number Offset, TagSize;
1534     cmsUInt32Number ElemCount;
1535     int n;
1536 
1537     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return NULL;
1538 
1539     n = _cmsSearchTag(Icc, sig, TRUE);
1540     if (n < 0) goto Error;               // Not found, return NULL
1541 
1542 
1543     // If the element is already in memory, return the pointer
1544     if (Icc -> TagPtrs[n]) {
1545 
1546         if (Icc ->TagSaveAsRaw[n]) goto Error;  // We don't support read raw tags as cooked
1547 
1548         _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1549         return Icc -> TagPtrs[n];
1550     }
1551 
1552     // We need to read it. Get the offset and size to the file
1553     Offset    = Icc -> TagOffsets[n];
1554     TagSize   = Icc -> TagSizes[n];
1555 
1556     // Seek to its location
1557     if (!io -> Seek(io, Offset))
1558         goto Error;
1559 
1560     // Search for support on this tag
1561     TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig);
1562     if (TagDescriptor == NULL) {
1563 
1564         char String[5];
1565 
1566         _cmsTagSignature2String(String, sig);
1567 
1568         // An unknown element was found.
1569         cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String);
1570         goto Error;     // Unsupported.
1571     }
1572 
1573     // if supported, get type and check if in list
1574     BaseType = _cmsReadTypeBase(io);
1575     if (BaseType == 0) goto Error;
1576 
1577     if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error;
1578 
1579     TagSize  -= 8;                      // Alredy read by the type base logic
1580 
1581     // Get type handler
1582     TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType);
1583     if (TypeHandler == NULL) goto Error;
1584     LocalTypeHandler = *TypeHandler;
1585 
1586 
1587     // Read the tag
1588     Icc -> TagTypeHandlers[n] = TypeHandler;
1589 
1590     LocalTypeHandler.ContextID = Icc ->ContextID;
1591     LocalTypeHandler.ICCVersion = Icc ->Version;
1592     Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize);
1593 
1594     // The tag type is supported, but something wrong happened and we cannot read the tag.
1595     // let know the user about this (although it is just a warning)
1596     if (Icc -> TagPtrs[n] == NULL) {
1597 
1598         char String[5];
1599 
1600         _cmsTagSignature2String(String, sig);
1601         cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String);
1602         goto Error;
1603     }
1604 
1605     // This is a weird error that may be a symptom of something more serious, the number of
1606     // stored item is actually less than the number of required elements.
1607     if (ElemCount < TagDescriptor ->ElemCount) {
1608 
1609         char String[5];
1610 
1611         _cmsTagSignature2String(String, sig);
1612         cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d",
1613             String, TagDescriptor ->ElemCount, ElemCount);
1614     }
1615 
1616 
1617     // Return the data
1618     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1619     return Icc -> TagPtrs[n];
1620 
1621 
1622     // Return error and unlock tha data
1623 Error:
1624     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1625     return NULL;
1626 }
1627 
1628 
1629 // Get true type of data
1630 cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig)
1631 {
1632     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1633     cmsTagTypeHandler* TypeHandler;
1634     int n;
1635 
1636     // Search for given tag in ICC profile directory
1637     n = _cmsSearchTag(Icc, sig, TRUE);
1638     if (n < 0) return (cmsTagTypeSignature) 0;                // Not found, return NULL
1639 
1640     // Get the handler. The true type is there
1641     TypeHandler =  Icc -> TagTypeHandlers[n];
1642     return TypeHandler ->Signature;
1643 }
1644 
1645 
1646 // Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already
1647 // in that list, the previous version is deleted.
1648 cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data)
1649 {
1650     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1651     cmsTagTypeHandler* TypeHandler = NULL;
1652     cmsTagTypeHandler LocalTypeHandler;
1653     cmsTagDescriptor* TagDescriptor = NULL;
1654     cmsTagTypeSignature Type;
1655     int i;
1656     cmsFloat64Number Version;
1657     char TypeString[5], SigString[5];
1658 
1659     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE;
1660 
1661     // To delete tags.
1662     if (data == NULL) {
1663 
1664          // Delete the tag
1665          i = _cmsSearchTag(Icc, sig, FALSE);
1666          if (i >= 0) {
1667 
1668              // Use zero as a mark of deleted
1669              _cmsDeleteTagByPos(Icc, i);
1670              Icc ->TagNames[i] = (cmsTagSignature) 0;
1671              _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1672              return TRUE;
1673          }
1674          // Didn't find the tag
1675         goto Error;
1676     }
1677 
1678     if (!_cmsNewTag(Icc, sig, &i)) goto Error;
1679 
1680     // This is not raw
1681     Icc ->TagSaveAsRaw[i] = FALSE;
1682 
1683     // This is not a link
1684     Icc ->TagLinked[i] = (cmsTagSignature) 0;
1685 
1686     // Get information about the TAG.
1687     TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig);
1688     if (TagDescriptor == NULL){
1689          cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig);
1690         goto Error;
1691     }
1692 
1693 
1694     // Now we need to know which type to use. It depends on the version.
1695     Version = cmsGetProfileVersion(hProfile);
1696 
1697     if (TagDescriptor ->DecideType != NULL) {
1698 
1699         // Let the tag descriptor to decide the type base on depending on
1700         // the data. This is useful for example on parametric curves, where
1701         // curves specified by a table cannot be saved as parametric and needs
1702         // to be casted to single v2-curves, even on v4 profiles.
1703 
1704         Type = TagDescriptor ->DecideType(Version, data);
1705     }
1706     else {
1707 
1708         Type = TagDescriptor ->SupportedTypes[0];
1709     }
1710 
1711     // Does the tag support this type?
1712     if (!IsTypeSupported(TagDescriptor, Type)) {
1713 
1714         _cmsTagSignature2String(TypeString, (cmsTagSignature) Type);
1715         _cmsTagSignature2String(SigString,  sig);
1716 
1717         cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString);
1718         goto Error;
1719     }
1720 
1721     // Does we have a handler for this type?
1722     TypeHandler =  _cmsGetTagTypeHandler(Icc->ContextID, Type);
1723     if (TypeHandler == NULL) {
1724 
1725         _cmsTagSignature2String(TypeString, (cmsTagSignature) Type);
1726         _cmsTagSignature2String(SigString,  sig);
1727 
1728         cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString);
1729         goto Error;           // Should never happen
1730     }
1731 
1732 
1733     // Fill fields on icc structure
1734     Icc ->TagTypeHandlers[i]  = TypeHandler;
1735     Icc ->TagNames[i]         = sig;
1736     Icc ->TagSizes[i]         = 0;
1737     Icc ->TagOffsets[i]       = 0;
1738 
1739     LocalTypeHandler = *TypeHandler;
1740     LocalTypeHandler.ContextID  = Icc ->ContextID;
1741     LocalTypeHandler.ICCVersion = Icc ->Version;
1742     Icc ->TagPtrs[i]            = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount);
1743 
1744     if (Icc ->TagPtrs[i] == NULL)  {
1745 
1746         _cmsTagSignature2String(TypeString, (cmsTagSignature) Type);
1747         _cmsTagSignature2String(SigString,  sig);
1748         cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString);
1749 
1750         goto Error;
1751     }
1752 
1753     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1754     return TRUE;
1755 
1756 Error:
1757     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1758     return FALSE;
1759 
1760 }
1761 
1762 // Read and write raw data. The only way those function would work and keep consistence with normal read and write
1763 // is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained
1764 // data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where
1765 // raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows
1766 // to write a tag as raw data and the read it as handled.
1767 
1768 cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize)
1769 {
1770     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1771     void *Object;
1772     int i;
1773     cmsIOHANDLER* MemIO;
1774     cmsTagTypeHandler* TypeHandler = NULL;
1775     cmsTagTypeHandler LocalTypeHandler;
1776     cmsTagDescriptor* TagDescriptor = NULL;
1777     cmsUInt32Number rc;
1778     cmsUInt32Number Offset, TagSize;
1779 
1780     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
1781 
1782     // Search for given tag in ICC profile directory
1783     i = _cmsSearchTag(Icc, sig, TRUE);
1784     if (i < 0) goto Error;                 // Not found,
1785 
1786     // It is already read?
1787     if (Icc -> TagPtrs[i] == NULL) {
1788 
1789         // No yet, get original position
1790         Offset   = Icc ->TagOffsets[i];
1791         TagSize  = Icc ->TagSizes[i];
1792 
1793         // read the data directly, don't keep copy
1794         if (data != NULL) {
1795 
1796             if (BufferSize < TagSize)
1797                 TagSize = BufferSize;
1798 
1799             if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error;
1800             if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error;
1801 
1802             _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1803             return TagSize;
1804         }
1805 
1806         _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1807         return Icc ->TagSizes[i];
1808     }
1809 
1810     // The data has been already read, or written. But wait!, maybe the user choosed to save as
1811     // raw data. In this case, return the raw data directly
1812     if (Icc ->TagSaveAsRaw[i]) {
1813 
1814         if (data != NULL)  {
1815 
1816             TagSize  = Icc ->TagSizes[i];
1817             if (BufferSize < TagSize)
1818                 TagSize = BufferSize;
1819 
1820             memmove(data, Icc ->TagPtrs[i], TagSize);
1821 
1822             _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1823             return TagSize;
1824         }
1825 
1826         _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1827         return Icc ->TagSizes[i];
1828     }
1829 
1830     // Already readed, or previously set by cmsWriteTag(). We need to serialize that
1831     // data to raw in order to maintain consistency.
1832 
1833     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1834     Object = cmsReadTag(hProfile, sig);
1835     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
1836 
1837     if (Object == NULL) goto Error;
1838 
1839     // Now we need to serialize to a memory block: just use a memory iohandler
1840 
1841     if (data == NULL) {
1842         MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile));
1843     } else{
1844         MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w");
1845     }
1846     if (MemIO == NULL) goto Error;
1847 
1848     // Obtain type handling for the tag
1849     TypeHandler = Icc ->TagTypeHandlers[i];
1850     TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig);
1851     if (TagDescriptor == NULL) {
1852         cmsCloseIOhandler(MemIO);
1853         goto Error;
1854     }
1855 
1856     if (TypeHandler == NULL) goto Error;
1857 
1858     // Serialize
1859     LocalTypeHandler = *TypeHandler;
1860     LocalTypeHandler.ContextID  = Icc ->ContextID;
1861     LocalTypeHandler.ICCVersion = Icc ->Version;
1862 
1863     if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) {
1864         cmsCloseIOhandler(MemIO);
1865         goto Error;
1866     }
1867 
1868     if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) {
1869         cmsCloseIOhandler(MemIO);
1870         goto Error;
1871     }
1872 
1873     // Get Size and close
1874     rc = MemIO ->Tell(MemIO);
1875     cmsCloseIOhandler(MemIO);      // Ignore return code this time
1876 
1877     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1878     return rc;
1879 
1880 Error:
1881     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1882     return 0;
1883 }
1884 
1885 // Similar to the anterior. This function allows to write directly to the ICC profile any data, without
1886 // checking anything. As a rule, mixing Raw with cooked doesn't work, so writting a tag as raw and then reading
1887 // it as cooked without serializing does result into an error. If that is what you want, you will need to dump
1888 // the profile to memry or disk and then reopen it.
1889 cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size)
1890 {
1891     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1892     int i;
1893 
1894     if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0;
1895 
1896     if (!_cmsNewTag(Icc, sig, &i)) {
1897         _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1898          return FALSE;
1899     }
1900 
1901     // Mark the tag as being written as RAW
1902     Icc ->TagSaveAsRaw[i] = TRUE;
1903     Icc ->TagNames[i]     = sig;
1904     Icc ->TagLinked[i]    = (cmsTagSignature) 0;
1905 
1906     // Keep a copy of the block
1907     Icc ->TagPtrs[i]  = _cmsDupMem(Icc ->ContextID, data, Size);
1908     Icc ->TagSizes[i] = Size;
1909 
1910     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1911 
1912     if (Icc->TagPtrs[i] == NULL) {
1913            Icc->TagNames[i] = (cmsTagSignature) 0;
1914            return FALSE;
1915     }
1916     return TRUE;
1917 }
1918 
1919 // Using this function you can collapse several tag entries to the same block in the profile
1920 cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest)
1921 {
1922     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1923     int i;
1924 
1925      if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE;
1926 
1927     if (!_cmsNewTag(Icc, sig, &i)) {
1928         _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1929         return FALSE;
1930     }
1931 
1932     // Keep necessary information
1933     Icc ->TagSaveAsRaw[i] = FALSE;
1934     Icc ->TagNames[i]     = sig;
1935     Icc ->TagLinked[i]    = dest;
1936 
1937     Icc ->TagPtrs[i]    = NULL;
1938     Icc ->TagSizes[i]   = 0;
1939     Icc ->TagOffsets[i] = 0;
1940 
1941     _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex);
1942     return TRUE;
1943 }
1944 
1945 
1946 // Returns the tag linked to sig, in the case two tags are sharing same resource
1947 cmsTagSignature  CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig)
1948 {
1949     _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile;
1950     int i;
1951 
1952     // Search for given tag in ICC profile directory
1953     i = _cmsSearchTag(Icc, sig, FALSE);
1954     if (i < 0) return (cmsTagSignature) 0;                 // Not found, return 0
1955 
1956     return Icc -> TagLinked[i];
1957 }