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