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