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 72typedef struct { 73 cmsUInt32Number Pointer; // Points to current location 74} FILENULL; 75 76static 77cmsUInt32Number 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 88static 89cmsBool NULLSeek(cmsIOHANDLER* iohandler, cmsUInt32Number offset) 90{ 91 FILENULL* ResData = (FILENULL*) iohandler ->stream; 92 93 ResData ->Pointer = offset; 94 return TRUE; 95} 96 97static 98cmsUInt32Number NULLTell(cmsIOHANDLER* iohandler) 99{ 100 FILENULL* ResData = (FILENULL*) iohandler ->stream; 101 return ResData -> Pointer; 102} 103 104static 105cmsBool 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 118static 119cmsBool 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 129cmsIOHANDLER* 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 156Error: 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 167typedef 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 175static 176cmsUInt32Number 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 198static 199cmsBool 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 213static 214cmsUInt32Number 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. 224static 225cmsBool 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 248static 249cmsBool 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. 267cmsIOHANDLER* 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 334Error: 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 343static 344cmsUInt32Number 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 357static 358cmsBool 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 370static 371cmsUInt32Number 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 377static 378cmsBool 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 387static 388cmsBool 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. 396cmsIOHANDLER* 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 463cmsIOHANDLER* 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 496cmsBool CMSEXPORT cmsCloseIOhandler(cmsIOHANDLER* io) 497{ 498 return io -> Close(io); 499} 500 501// ------------------------------------------------------------------------------------------------------- 502 503cmsIOHANDLER* 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 512cmsHPROFILE 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 536cmsContext 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 546cmsInt32Number 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 555cmsTagSignature 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 566static 567int 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 582int _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 612static 613void _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 643static 644cmsBool _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 674cmsBool 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. 688static 689cmsUInt32Number _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 708cmsBool _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 802cmsBool _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 881cmsUInt32Number CMSEXPORT cmsGetHeaderRenderingIntent(cmsHPROFILE hProfile) 882{ 883 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 884 return Icc -> RenderingIntent; 885} 886 887void CMSEXPORT cmsSetHeaderRenderingIntent(cmsHPROFILE hProfile, cmsUInt32Number RenderingIntent) 888{ 889 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 890 Icc -> RenderingIntent = RenderingIntent; 891} 892 893cmsUInt32Number CMSEXPORT cmsGetHeaderFlags(cmsHPROFILE hProfile) 894{ 895 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 896 return (cmsUInt32Number) Icc -> flags; 897} 898 899void CMSEXPORT cmsSetHeaderFlags(cmsHPROFILE hProfile, cmsUInt32Number Flags) 900{ 901 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 902 Icc -> flags = (cmsUInt32Number) Flags; 903} 904 905cmsUInt32Number CMSEXPORT cmsGetHeaderManufacturer(cmsHPROFILE hProfile) 906{ 907 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 908 return Icc ->manufacturer; 909} 910 911void CMSEXPORT cmsSetHeaderManufacturer(cmsHPROFILE hProfile, cmsUInt32Number manufacturer) 912{ 913 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 914 Icc -> manufacturer = manufacturer; 915} 916 917cmsUInt32Number CMSEXPORT cmsGetHeaderCreator(cmsHPROFILE hProfile) 918{ 919 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 920 return Icc ->creator; 921} 922 923cmsUInt32Number CMSEXPORT cmsGetHeaderModel(cmsHPROFILE hProfile) 924{ 925 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 926 return Icc ->model; 927} 928 929void CMSEXPORT cmsSetHeaderModel(cmsHPROFILE hProfile, cmsUInt32Number model) 930{ 931 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 932 Icc -> model = model; 933} 934 935void CMSEXPORT cmsGetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number* Flags) 936{ 937 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 938 memmove(Flags, &Icc -> attributes, sizeof(cmsUInt64Number)); 939} 940 941void CMSEXPORT cmsSetHeaderAttributes(cmsHPROFILE hProfile, cmsUInt64Number Flags) 942{ 943 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 944 memmove(&Icc -> attributes, &Flags, sizeof(cmsUInt64Number)); 945} 946 947void CMSEXPORT cmsGetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) 948{ 949 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 950 memmove(ProfileID, Icc ->ProfileID.ID8, 16); 951} 952 953void CMSEXPORT cmsSetHeaderProfileID(cmsHPROFILE hProfile, cmsUInt8Number* ProfileID) 954{ 955 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 956 memmove(&Icc -> ProfileID, ProfileID, 16); 957} 958 959cmsBool 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 966cmsColorSpaceSignature CMSEXPORT cmsGetPCS(cmsHPROFILE hProfile) 967{ 968 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 969 return Icc -> PCS; 970} 971 972void CMSEXPORT cmsSetPCS(cmsHPROFILE hProfile, cmsColorSpaceSignature pcs) 973{ 974 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 975 Icc -> PCS = pcs; 976} 977 978cmsColorSpaceSignature CMSEXPORT cmsGetColorSpace(cmsHPROFILE hProfile) 979{ 980 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 981 return Icc -> ColorSpace; 982} 983 984void CMSEXPORT cmsSetColorSpace(cmsHPROFILE hProfile, cmsColorSpaceSignature sig) 985{ 986 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 987 Icc -> ColorSpace = sig; 988} 989 990cmsProfileClassSignature CMSEXPORT cmsGetDeviceClass(cmsHPROFILE hProfile) 991{ 992 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 993 return Icc -> DeviceClass; 994} 995 996void CMSEXPORT cmsSetDeviceClass(cmsHPROFILE hProfile, cmsProfileClassSignature sig) 997{ 998 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 999 Icc -> DeviceClass = sig; 1000} 1001 1002cmsUInt32Number CMSEXPORT cmsGetEncodedICCversion(cmsHPROFILE hProfile) 1003{ 1004 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1005 return Icc -> Version; 1006} 1007 1008void 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 1015static 1016cmsUInt32Number 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 1035void 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 1044cmsFloat64Number 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 1055cmsHPROFILE 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 1068Error: 1069 cmsCloseProfile(hEmpty); 1070 return NULL; 1071} 1072 1073// Create profile from IOhandler 1074cmsHPROFILE 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 1093Error: 1094 cmsCloseProfile(hEmpty); 1095 return NULL; 1096} 1097 1098 1099// Create profile from disk file 1100cmsHPROFILE 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 1122Error: 1123 cmsCloseProfile(hEmpty); 1124 return NULL; 1125} 1126 1127 1128cmsHPROFILE CMSEXPORT cmsOpenProfileFromFile(const char *ICCProfile, const char *sAccess) 1129{ 1130 return cmsOpenProfileFromFileTHR(NULL, ICCProfile, sAccess); 1131} 1132 1133 1134cmsHPROFILE 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 1155Error: 1156 cmsCloseProfile(hEmpty); 1157 return NULL; 1158 1159} 1160 1161cmsHPROFILE CMSEXPORT cmsOpenProfileFromStream(FILE* ICCProfile, const char *sAccess) 1162{ 1163 return cmsOpenProfileFromStreamTHR(NULL, ICCProfile, sAccess); 1164} 1165 1166 1167// Open from memory block 1168cmsHPROFILE 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 1187Error: 1188 cmsCloseProfile(hEmpty); 1189 return NULL; 1190} 1191 1192cmsHPROFILE 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 1200static 1201cmsBool 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 1314static 1315cmsBool 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 1340cmsUInt32Number 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 1386Error: 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. 1396cmsBool 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 1414cmsBool 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 1430cmsBool 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 1458cmsBool 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 1507static 1508cmsBool 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 1525void* 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 -> TagTypeHandlers[n] == NULL) goto Error; 1547 BaseType = Icc -> TagTypeHandlers[n]->Signature; 1548 if (BaseType == 0) goto Error; 1549 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); 1550 if (TagDescriptor == NULL) goto Error; 1551 if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; 1552 1553 if (Icc ->TagSaveAsRaw[n]) goto Error; // We don't support read raw tags as cooked 1554 1555 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1556 return Icc -> TagPtrs[n]; 1557 } 1558 1559 // We need to read it. Get the offset and size to the file 1560 Offset = Icc -> TagOffsets[n]; 1561 TagSize = Icc -> TagSizes[n]; 1562 1563 // Seek to its location 1564 if (!io -> Seek(io, Offset)) 1565 goto Error; 1566 1567 // Search for support on this tag 1568 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); 1569 if (TagDescriptor == NULL) { 1570 1571 char String[5]; 1572 1573 _cmsTagSignature2String(String, sig); 1574 1575 // An unknown element was found. 1576 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unknown tag type '%s' found.", String); 1577 goto Error; // Unsupported. 1578 } 1579 1580 // if supported, get type and check if in list 1581 BaseType = _cmsReadTypeBase(io); 1582 if (BaseType == 0) goto Error; 1583 1584 if (!IsTypeSupported(TagDescriptor, BaseType)) goto Error; 1585 1586 TagSize -= 8; // Alredy read by the type base logic 1587 1588 // Get type handler 1589 TypeHandler = _cmsGetTagTypeHandler(Icc ->ContextID, BaseType); 1590 if (TypeHandler == NULL) goto Error; 1591 LocalTypeHandler = *TypeHandler; 1592 1593 1594 // Read the tag 1595 Icc -> TagTypeHandlers[n] = TypeHandler; 1596 1597 LocalTypeHandler.ContextID = Icc ->ContextID; 1598 LocalTypeHandler.ICCVersion = Icc ->Version; 1599 Icc -> TagPtrs[n] = LocalTypeHandler.ReadPtr(&LocalTypeHandler, io, &ElemCount, TagSize); 1600 1601 // The tag type is supported, but something wrong happened and we cannot read the tag. 1602 // let know the user about this (although it is just a warning) 1603 if (Icc -> TagPtrs[n] == NULL) { 1604 1605 char String[5]; 1606 1607 _cmsTagSignature2String(String, sig); 1608 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Corrupted tag '%s'", String); 1609 goto Error; 1610 } 1611 1612 // This is a weird error that may be a symptom of something more serious, the number of 1613 // stored item is actually less than the number of required elements. 1614 if (ElemCount < TagDescriptor ->ElemCount) { 1615 1616 char String[5]; 1617 1618 _cmsTagSignature2String(String, sig); 1619 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "'%s' Inconsistent number of items: expected %d, got %d", 1620 String, TagDescriptor ->ElemCount, ElemCount); 1621 } 1622 1623 1624 // Return the data 1625 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1626 return Icc -> TagPtrs[n]; 1627 1628 1629 // Return error and unlock tha data 1630Error: 1631 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1632 return NULL; 1633} 1634 1635 1636// Get true type of data 1637cmsTagTypeSignature _cmsGetTagTrueType(cmsHPROFILE hProfile, cmsTagSignature sig) 1638{ 1639 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1640 cmsTagTypeHandler* TypeHandler; 1641 int n; 1642 1643 // Search for given tag in ICC profile directory 1644 n = _cmsSearchTag(Icc, sig, TRUE); 1645 if (n < 0) return (cmsTagTypeSignature) 0; // Not found, return NULL 1646 1647 // Get the handler. The true type is there 1648 TypeHandler = Icc -> TagTypeHandlers[n]; 1649 return TypeHandler ->Signature; 1650} 1651 1652 1653// Write a single tag. This just keeps track of the tak into a list of "to be written". If the tag is already 1654// in that list, the previous version is deleted. 1655cmsBool CMSEXPORT cmsWriteTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data) 1656{ 1657 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1658 cmsTagTypeHandler* TypeHandler = NULL; 1659 cmsTagTypeHandler LocalTypeHandler; 1660 cmsTagDescriptor* TagDescriptor = NULL; 1661 cmsTagTypeSignature Type; 1662 int i; 1663 cmsFloat64Number Version; 1664 char TypeString[5], SigString[5]; 1665 1666 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; 1667 1668 // To delete tags. 1669 if (data == NULL) { 1670 1671 // Delete the tag 1672 i = _cmsSearchTag(Icc, sig, FALSE); 1673 if (i >= 0) { 1674 1675 // Use zero as a mark of deleted 1676 _cmsDeleteTagByPos(Icc, i); 1677 Icc ->TagNames[i] = (cmsTagSignature) 0; 1678 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1679 return TRUE; 1680 } 1681 // Didn't find the tag 1682 goto Error; 1683 } 1684 1685 if (!_cmsNewTag(Icc, sig, &i)) goto Error; 1686 1687 // This is not raw 1688 Icc ->TagSaveAsRaw[i] = FALSE; 1689 1690 // This is not a link 1691 Icc ->TagLinked[i] = (cmsTagSignature) 0; 1692 1693 // Get information about the TAG. 1694 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); 1695 if (TagDescriptor == NULL){ 1696 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported tag '%x'", sig); 1697 goto Error; 1698 } 1699 1700 1701 // Now we need to know which type to use. It depends on the version. 1702 Version = cmsGetProfileVersion(hProfile); 1703 1704 if (TagDescriptor ->DecideType != NULL) { 1705 1706 // Let the tag descriptor to decide the type base on depending on 1707 // the data. This is useful for example on parametric curves, where 1708 // curves specified by a table cannot be saved as parametric and needs 1709 // to be casted to single v2-curves, even on v4 profiles. 1710 1711 Type = TagDescriptor ->DecideType(Version, data); 1712 } 1713 else { 1714 1715 Type = TagDescriptor ->SupportedTypes[0]; 1716 } 1717 1718 // Does the tag support this type? 1719 if (!IsTypeSupported(TagDescriptor, Type)) { 1720 1721 _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); 1722 _cmsTagSignature2String(SigString, sig); 1723 1724 cmsSignalError(Icc ->ContextID, cmsERROR_UNKNOWN_EXTENSION, "Unsupported type '%s' for tag '%s'", TypeString, SigString); 1725 goto Error; 1726 } 1727 1728 // Does we have a handler for this type? 1729 TypeHandler = _cmsGetTagTypeHandler(Icc->ContextID, Type); 1730 if (TypeHandler == NULL) { 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; // Should never happen 1737 } 1738 1739 1740 // Fill fields on icc structure 1741 Icc ->TagTypeHandlers[i] = TypeHandler; 1742 Icc ->TagNames[i] = sig; 1743 Icc ->TagSizes[i] = 0; 1744 Icc ->TagOffsets[i] = 0; 1745 1746 LocalTypeHandler = *TypeHandler; 1747 LocalTypeHandler.ContextID = Icc ->ContextID; 1748 LocalTypeHandler.ICCVersion = Icc ->Version; 1749 Icc ->TagPtrs[i] = LocalTypeHandler.DupPtr(&LocalTypeHandler, data, TagDescriptor ->ElemCount); 1750 1751 if (Icc ->TagPtrs[i] == NULL) { 1752 1753 _cmsTagSignature2String(TypeString, (cmsTagSignature) Type); 1754 _cmsTagSignature2String(SigString, sig); 1755 cmsSignalError(Icc ->ContextID, cmsERROR_CORRUPTION_DETECTED, "Malformed struct in type '%s' for tag '%s'", TypeString, SigString); 1756 1757 goto Error; 1758 } 1759 1760 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1761 return TRUE; 1762 1763Error: 1764 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1765 return FALSE; 1766 1767} 1768 1769// Read and write raw data. The only way those function would work and keep consistence with normal read and write 1770// is to do an additional step of serialization. That means, readRaw would issue a normal read and then convert the obtained 1771// data to raw bytes by using the "write" serialization logic. And vice-versa. I know this may end in situations where 1772// raw data written does not exactly correspond with the raw data proposed to cmsWriteRaw data, but this approach allows 1773// to write a tag as raw data and the read it as handled. 1774 1775cmsInt32Number CMSEXPORT cmsReadRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, void* data, cmsUInt32Number BufferSize) 1776{ 1777 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1778 void *Object; 1779 int i; 1780 cmsIOHANDLER* MemIO; 1781 cmsTagTypeHandler* TypeHandler = NULL; 1782 cmsTagTypeHandler LocalTypeHandler; 1783 cmsTagDescriptor* TagDescriptor = NULL; 1784 cmsUInt32Number rc; 1785 cmsUInt32Number Offset, TagSize; 1786 1787 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; 1788 1789 // Search for given tag in ICC profile directory 1790 i = _cmsSearchTag(Icc, sig, TRUE); 1791 if (i < 0) goto Error; // Not found, 1792 1793 // It is already read? 1794 if (Icc -> TagPtrs[i] == NULL) { 1795 1796 // No yet, get original position 1797 Offset = Icc ->TagOffsets[i]; 1798 TagSize = Icc ->TagSizes[i]; 1799 1800 // read the data directly, don't keep copy 1801 if (data != NULL) { 1802 1803 if (BufferSize < TagSize) 1804 TagSize = BufferSize; 1805 1806 if (!Icc ->IOhandler ->Seek(Icc ->IOhandler, Offset)) goto Error; 1807 if (!Icc ->IOhandler ->Read(Icc ->IOhandler, data, 1, TagSize)) goto Error; 1808 1809 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1810 return TagSize; 1811 } 1812 1813 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1814 return Icc ->TagSizes[i]; 1815 } 1816 1817 // The data has been already read, or written. But wait!, maybe the user choosed to save as 1818 // raw data. In this case, return the raw data directly 1819 if (Icc ->TagSaveAsRaw[i]) { 1820 1821 if (data != NULL) { 1822 1823 TagSize = Icc ->TagSizes[i]; 1824 if (BufferSize < TagSize) 1825 TagSize = BufferSize; 1826 1827 memmove(data, Icc ->TagPtrs[i], TagSize); 1828 1829 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1830 return TagSize; 1831 } 1832 1833 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1834 return Icc ->TagSizes[i]; 1835 } 1836 1837 // Already readed, or previously set by cmsWriteTag(). We need to serialize that 1838 // data to raw in order to maintain consistency. 1839 1840 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1841 Object = cmsReadTag(hProfile, sig); 1842 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; 1843 1844 if (Object == NULL) goto Error; 1845 1846 // Now we need to serialize to a memory block: just use a memory iohandler 1847 1848 if (data == NULL) { 1849 MemIO = cmsOpenIOhandlerFromNULL(cmsGetProfileContextID(hProfile)); 1850 } else{ 1851 MemIO = cmsOpenIOhandlerFromMem(cmsGetProfileContextID(hProfile), data, BufferSize, "w"); 1852 } 1853 if (MemIO == NULL) goto Error; 1854 1855 // Obtain type handling for the tag 1856 TypeHandler = Icc ->TagTypeHandlers[i]; 1857 TagDescriptor = _cmsGetTagDescriptor(Icc-> ContextID, sig); 1858 if (TagDescriptor == NULL) { 1859 cmsCloseIOhandler(MemIO); 1860 goto Error; 1861 } 1862 1863 if (TypeHandler == NULL) goto Error; 1864 1865 // Serialize 1866 LocalTypeHandler = *TypeHandler; 1867 LocalTypeHandler.ContextID = Icc ->ContextID; 1868 LocalTypeHandler.ICCVersion = Icc ->Version; 1869 1870 if (!_cmsWriteTypeBase(MemIO, TypeHandler ->Signature)) { 1871 cmsCloseIOhandler(MemIO); 1872 goto Error; 1873 } 1874 1875 if (!LocalTypeHandler.WritePtr(&LocalTypeHandler, MemIO, Object, TagDescriptor ->ElemCount)) { 1876 cmsCloseIOhandler(MemIO); 1877 goto Error; 1878 } 1879 1880 // Get Size and close 1881 rc = MemIO ->Tell(MemIO); 1882 cmsCloseIOhandler(MemIO); // Ignore return code this time 1883 1884 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1885 return rc; 1886 1887Error: 1888 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1889 return 0; 1890} 1891 1892// Similar to the anterior. This function allows to write directly to the ICC profile any data, without 1893// checking anything. As a rule, mixing Raw with cooked doesn't work, so writting a tag as raw and then reading 1894// it as cooked without serializing does result into an error. If that is what you want, you will need to dump 1895// the profile to memry or disk and then reopen it. 1896cmsBool CMSEXPORT cmsWriteRawTag(cmsHPROFILE hProfile, cmsTagSignature sig, const void* data, cmsUInt32Number Size) 1897{ 1898 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1899 int i; 1900 1901 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return 0; 1902 1903 if (!_cmsNewTag(Icc, sig, &i)) { 1904 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1905 return FALSE; 1906 } 1907 1908 // Mark the tag as being written as RAW 1909 Icc ->TagSaveAsRaw[i] = TRUE; 1910 Icc ->TagNames[i] = sig; 1911 Icc ->TagLinked[i] = (cmsTagSignature) 0; 1912 1913 // Keep a copy of the block 1914 Icc ->TagPtrs[i] = _cmsDupMem(Icc ->ContextID, data, Size); 1915 Icc ->TagSizes[i] = Size; 1916 1917 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1918 1919 if (Icc->TagPtrs[i] == NULL) { 1920 Icc->TagNames[i] = (cmsTagSignature) 0; 1921 return FALSE; 1922 } 1923 return TRUE; 1924} 1925 1926// Using this function you can collapse several tag entries to the same block in the profile 1927cmsBool CMSEXPORT cmsLinkTag(cmsHPROFILE hProfile, cmsTagSignature sig, cmsTagSignature dest) 1928{ 1929 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1930 int i; 1931 1932 if (!_cmsLockMutex(Icc->ContextID, Icc ->UsrMutex)) return FALSE; 1933 1934 if (!_cmsNewTag(Icc, sig, &i)) { 1935 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1936 return FALSE; 1937 } 1938 1939 // Keep necessary information 1940 Icc ->TagSaveAsRaw[i] = FALSE; 1941 Icc ->TagNames[i] = sig; 1942 Icc ->TagLinked[i] = dest; 1943 1944 Icc ->TagPtrs[i] = NULL; 1945 Icc ->TagSizes[i] = 0; 1946 Icc ->TagOffsets[i] = 0; 1947 1948 _cmsUnlockMutex(Icc->ContextID, Icc ->UsrMutex); 1949 return TRUE; 1950} 1951 1952 1953// Returns the tag linked to sig, in the case two tags are sharing same resource 1954cmsTagSignature CMSEXPORT cmsTagLinkedTo(cmsHPROFILE hProfile, cmsTagSignature sig) 1955{ 1956 _cmsICCPROFILE* Icc = (_cmsICCPROFILE*) hProfile; 1957 int i; 1958 1959 // Search for given tag in ICC profile directory 1960 i = _cmsSearchTag(Icc, sig, FALSE); 1961 if (i < 0) return (cmsTagSignature) 0; // Not found, return 0 1962 1963 return Icc -> TagLinked[i]; 1964} 1965