Coverage Report

Created: 2022-10-31 07:00

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