comparison mupdf-source/thirdparty/lcms2/src/cmsio0.c @ 2:b50eed0cc0ef upstream

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