Coverage Report

Created: 2022-10-31 07:00

/src/ghostpdl/lcms2mt/src/cmsnamed.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
// Multilocalized unicode objects. That is an attempt to encapsulate i18n.
30
31
32
// Allocates an empty multi localizad unicode object
33
cmsMLU* CMSEXPORT cmsMLUalloc(cmsContext ContextID, cmsUInt32Number nItems)
34
5.39M
{
35
5.39M
    cmsMLU* mlu;
36
37
    // nItems should be positive if given
38
5.39M
    if (nItems <= 0) nItems = 2;
39
40
    // Create the container
41
5.39M
    mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU));
42
5.39M
    if (mlu == NULL) return NULL;
43
44
    // Create entry array
45
5.39M
    mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry));
46
5.39M
    if (mlu ->Entries == NULL) {
47
0
        _cmsFree(ContextID, mlu);
48
0
        return NULL;
49
0
    }
50
51
    // Ok, keep indexes up to date
52
5.39M
    mlu ->AllocatedEntries    = nItems;
53
5.39M
    mlu ->UsedEntries         = 0;
54
55
5.39M
    return mlu;
56
5.39M
}
57
58
59
// Grows a mempool table for a MLU. Each time this function is called, mempool size is multiplied times two.
60
static
61
cmsBool GrowMLUpool(cmsContext ContextID, cmsMLU* mlu)
62
2.69M
{
63
2.69M
    cmsUInt32Number size;
64
2.69M
    void *NewPtr;
65
66
    // Sanity check
67
2.69M
    if (mlu == NULL) return FALSE;
68
69
2.69M
    if (mlu ->PoolSize == 0)
70
2.69M
        size = 256;
71
0
    else
72
0
        size = mlu ->PoolSize * 2;
73
74
    // Check for overflow
75
2.69M
    if (size < mlu ->PoolSize) return FALSE;
76
77
    // Reallocate the pool
78
2.69M
    NewPtr = _cmsRealloc(ContextID, mlu ->MemPool, size);
79
2.69M
    if (NewPtr == NULL) return FALSE;
80
81
82
2.69M
    mlu ->MemPool  = NewPtr;
83
2.69M
    mlu ->PoolSize = size;
84
85
2.69M
    return TRUE;
86
2.69M
}
87
88
89
// Grows a entry table for a MLU. Each time this function is called, table size is multiplied times two.
90
static
91
cmsBool GrowMLUtable(cmsContext ContextID, cmsMLU* mlu)
92
0
{
93
0
    cmsUInt32Number AllocatedEntries;
94
0
    _cmsMLUentry *NewPtr;
95
96
    // Sanity check
97
0
    if (mlu == NULL) return FALSE;
98
99
0
    AllocatedEntries = mlu ->AllocatedEntries * 2;
100
101
    // Check for overflow
102
0
    if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE;
103
104
    // Reallocate the memory
105
0
    NewPtr = (_cmsMLUentry*)_cmsRealloc(ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry));
106
0
    if (NewPtr == NULL) return FALSE;
107
108
0
    mlu ->Entries          = NewPtr;
109
0
    mlu ->AllocatedEntries = AllocatedEntries;
110
111
0
    return TRUE;
112
0
}
113
114
115
// Search for a specific entry in the structure. Language and Country are used.
116
static
117
int SearchMLUEntry(cmsMLU* mlu, cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)
118
2.69M
{
119
2.69M
    cmsUInt32Number i;
120
121
    // Sanity check
122
2.69M
    if (mlu == NULL) return -1;
123
124
    // Iterate whole table
125
2.69M
    for (i=0; i < mlu ->UsedEntries; i++) {
126
127
0
        if (mlu ->Entries[i].Country  == CountryCode &&
128
0
            mlu ->Entries[i].Language == LanguageCode) return (int) i;
129
0
    }
130
131
    // Not found
132
2.69M
    return -1;
133
2.69M
}
134
135
// Add a block of characters to the intended MLU. Language and country are specified.
136
// Only one entry for Language/country pair is allowed.
137
static
138
cmsBool AddMLUBlock(cmsContext ContextID, cmsMLU* mlu, cmsUInt32Number size, const wchar_t *Block,
139
                     cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode)
140
2.69M
{
141
2.69M
    cmsUInt32Number Offset;
142
2.69M
    cmsUInt8Number* Ptr;
143
144
    // Sanity check
145
2.69M
    if (mlu == NULL) return FALSE;
146
147
    // Is there any room available?
148
2.69M
    if (mlu ->UsedEntries >= mlu ->AllocatedEntries) {
149
0
        if (!GrowMLUtable(ContextID, mlu)) return FALSE;
150
0
    }
151
152
    // Only one ASCII string
153
2.69M
    if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE;  // Only one  is allowed!
154
155
    // Check for size
156
5.39M
    while ((mlu ->PoolSize - mlu ->PoolUsed) < size) {
157
158
2.69M
            if (!GrowMLUpool(ContextID, mlu)) return FALSE;
159
2.69M
    }
160
161
2.69M
    Offset = mlu ->PoolUsed;
162
163
2.69M
    Ptr = (cmsUInt8Number*) mlu ->MemPool;
164
2.69M
    if (Ptr == NULL) return FALSE;
165
166
    // Set the entry
167
2.69M
    memmove(Ptr + Offset, Block, size);
168
2.69M
    mlu ->PoolUsed += size;
169
170
2.69M
    mlu ->Entries[mlu ->UsedEntries].StrW     = Offset;
171
2.69M
    mlu ->Entries[mlu ->UsedEntries].Len      = size;
172
2.69M
    mlu ->Entries[mlu ->UsedEntries].Country  = CountryCode;
173
2.69M
    mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode;
174
2.69M
    mlu ->UsedEntries++;
175
176
2.69M
    return TRUE;
177
2.69M
}
178
179
// Convert from a 3-char code to a cmsUInt16Number. It is done in this way because some
180
// compilers don't properly align beginning of strings
181
static
182
cmsUInt16Number strTo16(const char str[3])
183
5.39M
{
184
5.39M
    const cmsUInt8Number* ptr8;
185
5.39M
    cmsUInt16Number n;
186
187
    // For non-existent strings
188
5.39M
    if (str == NULL) return 0;
189
5.39M
    ptr8 = (const cmsUInt8Number*)str;
190
5.39M
    n = (cmsUInt16Number)(((cmsUInt16Number)ptr8[0] << 8) | ptr8[1]);
191
192
5.39M
    return n;
193
5.39M
}
194
195
static
196
void strFrom16(char str[3], cmsUInt16Number n)
197
0
{
198
0
    str[0] = (char)(n >> 8);
199
0
    str[1] = (char)n;
200
0
    str[2] = (char)0;
201
202
0
}
203
204
// Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)
205
// In the case the user explicitely sets an empty string, we force a \0
206
cmsBool CMSEXPORT cmsMLUsetASCII(cmsContext ContextID, cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString)
207
0
{
208
0
    cmsUInt32Number i, len = (cmsUInt32Number) strlen(ASCIIString);
209
0
    wchar_t* WStr;
210
0
    cmsBool  rc;
211
0
    cmsUInt16Number Lang  = strTo16(LanguageCode);
212
0
    cmsUInt16Number Cntry = strTo16(CountryCode);
213
214
0
    if (mlu == NULL) return FALSE;
215
216
    // len == 0 would prevent operation, so we set a empty string pointing to zero
217
0
    if (len == 0)
218
0
    {
219
0
        len = 1;
220
0
    }
221
222
0
    WStr = (wchar_t*) _cmsCalloc(ContextID, len,  sizeof(wchar_t));
223
0
    if (WStr == NULL) return FALSE;
224
225
0
    for (i=0; i < len; i++)
226
0
        WStr[i] = (wchar_t) ASCIIString[i];
227
228
0
    rc = AddMLUBlock(ContextID, mlu, len  * sizeof(wchar_t), WStr, Lang, Cntry);
229
230
0
    _cmsFree(ContextID, WStr);
231
0
    return rc;
232
233
0
}
234
235
// We don't need any wcs support library
236
static
237
cmsUInt32Number mywcslen(const wchar_t *s)
238
2.69M
{
239
2.69M
    const wchar_t *p;
240
241
2.69M
    p = s;
242
57.3M
    while (*p)
243
54.6M
        p++;
244
245
2.69M
    return (cmsUInt32Number)(p - s);
246
2.69M
}
247
248
// Add a wide entry. Do not add any \0 terminator (ICC1v43_2010-12.pdf page 61)
249
cmsBool  CMSEXPORT cmsMLUsetWide(cmsContext ContextID, cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString)
250
2.69M
{
251
2.69M
    cmsUInt16Number Lang  = strTo16(Language);
252
2.69M
    cmsUInt16Number Cntry = strTo16(Country);
253
2.69M
    cmsUInt32Number len;
254
255
2.69M
    if (mlu == NULL) return FALSE;
256
2.69M
    if (WideString == NULL) return FALSE;
257
258
2.69M
    len = (cmsUInt32Number) (mywcslen(WideString)) * sizeof(wchar_t);
259
2.69M
    if (len == 0)
260
0
        len = sizeof(wchar_t);
261
262
2.69M
    return AddMLUBlock(ContextID, mlu, len, WideString, Lang, Cntry);
263
2.69M
}
264
265
// Duplicating a MLU is as easy as copying all members
266
cmsMLU* CMSEXPORT cmsMLUdup(cmsContext ContextID, const cmsMLU* mlu)
267
2.69M
{
268
2.69M
    cmsMLU* NewMlu = NULL;
269
270
    // Duplicating a NULL obtains a NULL
271
2.69M
    if (mlu == NULL) return NULL;
272
273
2.69M
    NewMlu = cmsMLUalloc(ContextID, mlu ->UsedEntries);
274
2.69M
    if (NewMlu == NULL) return NULL;
275
276
    // Should never happen
277
2.69M
    if (NewMlu ->AllocatedEntries < mlu ->UsedEntries)
278
0
        goto Error;
279
280
    // Sanitize...
281
2.69M
    if (NewMlu ->Entries == NULL || mlu ->Entries == NULL)  goto Error;
282
283
2.69M
    memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry));
284
2.69M
    NewMlu ->UsedEntries = mlu ->UsedEntries;
285
286
    // The MLU may be empty
287
2.69M
    if (mlu ->PoolUsed == 0) {
288
0
        NewMlu ->MemPool = NULL;
289
0
    }
290
2.69M
    else {
291
        // It is not empty
292
2.69M
        NewMlu ->MemPool = _cmsMalloc(ContextID, mlu ->PoolUsed);
293
2.69M
        if (NewMlu ->MemPool == NULL) goto Error;
294
2.69M
    }
295
296
2.69M
    NewMlu ->PoolSize = mlu ->PoolUsed;
297
298
2.69M
    if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error;
299
300
2.69M
    memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed);
301
2.69M
    NewMlu ->PoolUsed = mlu ->PoolUsed;
302
303
2.69M
    return NewMlu;
304
305
0
Error:
306
307
0
    if (NewMlu != NULL) cmsMLUfree(ContextID, NewMlu);
308
0
    return NULL;
309
2.69M
}
310
311
// Free any used memory
312
void CMSEXPORT cmsMLUfree(cmsContext ContextID, cmsMLU* mlu)
313
5.39M
{
314
5.39M
    if (mlu) {
315
316
5.39M
        if (mlu -> Entries) _cmsFree(ContextID, mlu->Entries);
317
5.39M
        if (mlu -> MemPool) _cmsFree(ContextID, mlu->MemPool);
318
319
5.39M
        _cmsFree(ContextID, mlu);
320
5.39M
    }
321
5.39M
}
322
323
324
// The algorithm first searches for an exact match of country and language, if not found it uses
325
// the Language. If none is found, first entry is used instead.
326
static
327
const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
328
                              cmsUInt32Number *len,
329
                              cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode,
330
                              cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode)
331
0
{
332
0
    cmsUInt32Number i;
333
0
    int Best = -1;
334
0
    _cmsMLUentry* v;
335
336
0
    if (mlu == NULL) return NULL;
337
338
0
    if (mlu -> AllocatedEntries <= 0) return NULL;
339
340
0
    for (i=0; i < mlu ->UsedEntries; i++) {
341
342
0
        v = mlu ->Entries + i;
343
344
0
        if (v -> Language == LanguageCode) {
345
346
0
            if (Best == -1) Best = (int) i;
347
348
0
            if (v -> Country == CountryCode) {
349
350
0
                if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
351
0
                if (UsedCountryCode  != NULL) *UsedCountryCode = v ->Country;
352
353
0
                if (len != NULL) *len = v ->Len;
354
355
0
                return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW);        // Found exact match
356
0
            }
357
0
        }
358
0
    }
359
360
    // No string found. Return First one
361
0
    if (Best == -1)
362
0
        Best = 0;
363
364
0
    v = mlu ->Entries + Best;
365
366
0
    if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
367
0
    if (UsedCountryCode  != NULL) *UsedCountryCode = v ->Country;
368
369
0
    if (len != NULL) *len   = v ->Len;
370
371
0
    return(wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW);
372
0
}
373
374
375
// Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len
376
cmsUInt32Number CMSEXPORT cmsMLUgetASCII(cmsContext ContextID, const cmsMLU* mlu,
377
                                       const char LanguageCode[3], const char CountryCode[3],
378
                                       char* Buffer, cmsUInt32Number BufferSize)
379
0
{
380
0
    const wchar_t *Wide;
381
0
    cmsUInt32Number  StrLen = 0;
382
0
    cmsUInt32Number ASCIIlen, i;
383
384
0
    cmsUInt16Number Lang  = strTo16(LanguageCode);
385
0
    cmsUInt16Number Cntry = strTo16(CountryCode);
386
0
    cmsUNUSED_PARAMETER(ContextID);
387
388
    // Sanitize
389
0
    if (mlu == NULL) return 0;
390
391
    // Get WideChar
392
0
    Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
393
0
    if (Wide == NULL) return 0;
394
395
0
    ASCIIlen = StrLen / sizeof(wchar_t);
396
397
    // Maybe we want only to know the len?
398
0
    if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end
399
400
    // No buffer size means no data
401
0
    if (BufferSize <= 0) return 0;
402
403
    // Some clipping may be required
404
0
    if (BufferSize < ASCIIlen + 1)
405
0
        ASCIIlen = BufferSize - 1;
406
407
    // Precess each character
408
0
    for (i=0; i < ASCIIlen; i++) {
409
410
0
        if (Wide[i] == 0)
411
0
            Buffer[i] = 0;
412
0
        else
413
0
            Buffer[i] = (char) Wide[i];
414
0
    }
415
416
    // We put a termination "\0"
417
0
    Buffer[ASCIIlen] = 0;
418
0
    return ASCIIlen + 1;
419
0
}
420
421
// Obtain a wide representation of the MLU, on depending on current locale settings
422
cmsUInt32Number CMSEXPORT cmsMLUgetWide(cmsContext ContextID, const cmsMLU* mlu,
423
                                      const char LanguageCode[3], const char CountryCode[3],
424
                                      wchar_t* Buffer, cmsUInt32Number BufferSize)
425
0
{
426
0
    const wchar_t *Wide;
427
0
    cmsUInt32Number  StrLen = 0;
428
429
0
    cmsUInt16Number Lang  = strTo16(LanguageCode);
430
0
    cmsUInt16Number Cntry = strTo16(CountryCode);
431
0
    cmsUNUSED_PARAMETER(ContextID);
432
433
    // Sanitize
434
0
    if (mlu == NULL) return 0;
435
436
0
    Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
437
0
    if (Wide == NULL) return 0;
438
439
    // Maybe we want only to know the len?
440
0
    if (Buffer == NULL) return StrLen + sizeof(wchar_t);
441
442
  // No buffer size means no data
443
0
    if (BufferSize <= 0) return 0;
444
445
    // Some clipping may be required
446
0
    if (BufferSize < StrLen + sizeof(wchar_t))
447
0
        StrLen = BufferSize - + sizeof(wchar_t);
448
449
0
    memmove(Buffer, Wide, StrLen);
450
0
    Buffer[StrLen / sizeof(wchar_t)] = 0;
451
452
0
    return StrLen + sizeof(wchar_t);
453
0
}
454
455
456
// Get also the language and country
457
CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(cmsContext ContextID, const cmsMLU* mlu,
458
                                              const char LanguageCode[3], const char CountryCode[3],
459
                                              char ObtainedLanguage[3], char ObtainedCountry[3])
460
0
{
461
0
    const wchar_t *Wide;
462
463
0
    cmsUInt16Number Lang  = strTo16(LanguageCode);
464
0
    cmsUInt16Number Cntry = strTo16(CountryCode);
465
0
    cmsUInt16Number ObtLang, ObtCode;
466
0
    cmsUNUSED_PARAMETER(ContextID);
467
468
    // Sanitize
469
0
    if (mlu == NULL) return FALSE;
470
471
0
    Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode);
472
0
    if (Wide == NULL) return FALSE;
473
474
    // Get used language and code
475
0
    strFrom16(ObtainedLanguage, ObtLang);
476
0
    strFrom16(ObtainedCountry, ObtCode);
477
478
0
    return TRUE;
479
0
}
480
481
482
483
// Get the number of translations in the MLU object
484
cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(cmsContext ContextID, const cmsMLU* mlu)
485
0
{
486
0
    cmsUNUSED_PARAMETER(ContextID);
487
0
    if (mlu == NULL) return 0;
488
0
    return mlu->UsedEntries;
489
0
}
490
491
// Get the language and country codes for a specific MLU index
492
cmsBool CMSEXPORT cmsMLUtranslationsCodes(cmsContext ContextID,
493
                                          const cmsMLU* mlu,
494
                                          cmsUInt32Number idx,
495
                                          char LanguageCode[3],
496
                                          char CountryCode[3])
497
0
{
498
0
    _cmsMLUentry *entry;
499
0
    cmsUNUSED_PARAMETER(ContextID);
500
501
0
    if (mlu == NULL) return FALSE;
502
503
0
    if (idx >= mlu->UsedEntries) return FALSE;
504
505
0
    entry = &mlu->Entries[idx];
506
507
0
    strFrom16(LanguageCode, entry->Language);
508
0
    strFrom16(CountryCode, entry->Country);
509
510
0
    return TRUE;
511
0
}
512
513
514
// Named color lists --------------------------------------------------------------------------------------------
515
516
// Grow the list to keep at least NumElements
517
static
518
cmsBool  GrowNamedColorList(cmsContext ContextID, cmsNAMEDCOLORLIST* v)
519
0
{
520
0
    cmsUInt32Number size;
521
0
    _cmsNAMEDCOLOR * NewPtr;
522
523
0
    if (v == NULL) return FALSE;
524
525
0
    if (v ->Allocated == 0)
526
0
        size = 64;   // Initial guess
527
0
    else
528
0
        size = v ->Allocated * 2;
529
530
    // Keep a maximum color lists can grow, 100K entries seems reasonable
531
0
    if (size > 1024 * 100) {
532
0
        _cmsFree(ContextID, (void*) v->List);
533
0
        v->List = NULL;
534
0
        return FALSE;
535
0
    }
536
537
0
    NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR));
538
0
    if (NewPtr == NULL)
539
0
        return FALSE;
540
541
0
    v ->List      = NewPtr;
542
0
    v ->Allocated = size;
543
0
    return TRUE;
544
0
}
545
546
// Allocate a list for n elements
547
cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix)
548
0
{
549
0
    cmsNAMEDCOLORLIST* v = (cmsNAMEDCOLORLIST*) _cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST));
550
551
0
    if (v == NULL) return NULL;
552
553
0
    v ->List      = NULL;
554
0
    v ->nColors   = 0;
555
556
0
    while (v -> Allocated < n) {
557
0
        if (!GrowNamedColorList(ContextID, v)) {
558
0
            cmsFreeNamedColorList(ContextID, v);
559
0
            return NULL;
560
0
        }
561
0
    }
562
563
0
    strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1);
564
0
    strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1);
565
0
    v->Prefix[32] = v->Suffix[32] = 0;
566
567
0
    v -> ColorantCount = ColorantCount;
568
569
0
    return v;
570
0
}
571
572
// Free a list
573
void CMSEXPORT cmsFreeNamedColorList(cmsContext ContextID, cmsNAMEDCOLORLIST* v)
574
0
{
575
0
    if (v == NULL) return;
576
0
    if (v ->List) _cmsFree(ContextID, v ->List);
577
0
    _cmsFree(ContextID, v);
578
0
}
579
580
cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(cmsContext ContextID, const cmsNAMEDCOLORLIST* v)
581
0
{
582
0
    cmsNAMEDCOLORLIST* NewNC;
583
584
0
    if (v == NULL) return NULL;
585
586
0
    NewNC= cmsAllocNamedColorList(ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix);
587
0
    if (NewNC == NULL) return NULL;
588
589
    // For really large tables we need this
590
0
    while (NewNC ->Allocated < v ->Allocated){
591
0
        if (!GrowNamedColorList(ContextID, NewNC))
592
0
        {
593
0
            cmsFreeNamedColorList(ContextID, NewNC);
594
0
            return NULL;
595
0
        }
596
0
    }
597
598
0
    memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix));
599
0
    memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix));
600
0
    NewNC ->ColorantCount = v ->ColorantCount;
601
0
    memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR));
602
0
    NewNC ->nColors = v ->nColors;
603
0
    return NewNC;
604
0
}
605
606
607
// Append a color to a list. List pointer may change if reallocated
608
cmsBool  CMSEXPORT cmsAppendNamedColor(cmsContext ContextID, cmsNAMEDCOLORLIST* NamedColorList,
609
                                       const char* Name,
610
                                       cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS])
611
0
{
612
0
    cmsUInt32Number i;
613
614
0
    if (NamedColorList == NULL) return FALSE;
615
616
0
    if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) {
617
0
        if (!GrowNamedColorList(ContextID, NamedColorList)) return FALSE;
618
0
    }
619
620
0
    for (i=0; i < NamedColorList ->ColorantCount; i++)
621
0
        NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL ? (cmsUInt16Number)0 : Colorant[i];
622
623
0
    for (i=0; i < 3; i++)
624
0
        NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? (cmsUInt16Number) 0 : PCS[i];
625
626
0
    if (Name != NULL) {
627
628
0
        strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1);
629
0
        NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0;
630
631
0
    }
632
0
    else
633
0
        NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0;
634
635
636
0
    NamedColorList ->nColors++;
637
0
    return TRUE;
638
0
}
639
640
// Returns number of elements
641
cmsUInt32Number CMSEXPORT cmsNamedColorCount(cmsContext ContextID, const cmsNAMEDCOLORLIST* NamedColorList)
642
0
{
643
0
     cmsUNUSED_PARAMETER(ContextID);
644
0
     if (NamedColorList == NULL) return 0;
645
0
     return NamedColorList ->nColors;
646
0
}
647
648
// Info aboout a given color
649
cmsBool  CMSEXPORT cmsNamedColorInfo(cmsContext ContextID, const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor,
650
                                     char* Name,
651
                                     char* Prefix,
652
                                     char* Suffix,
653
                                     cmsUInt16Number* PCS,
654
                                     cmsUInt16Number* Colorant)
655
0
{
656
0
    if (NamedColorList == NULL) return FALSE;
657
658
0
    if (nColor >= cmsNamedColorCount(ContextID, NamedColorList)) return FALSE;
659
660
    // strcpy instead of strncpy because many apps are using small buffers
661
0
    if (Name) strcpy(Name, NamedColorList->List[nColor].Name);
662
0
    if (Prefix) strcpy(Prefix, NamedColorList->Prefix);
663
0
    if (Suffix) strcpy(Suffix, NamedColorList->Suffix);
664
0
    if (PCS)
665
0
        memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number));
666
667
0
    if (Colorant)
668
0
        memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant,
669
0
                                sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount);
670
671
672
0
    return TRUE;
673
0
}
674
675
// Search for a given color name (no prefix or suffix)
676
cmsInt32Number CMSEXPORT cmsNamedColorIndex(cmsContext ContextID, const cmsNAMEDCOLORLIST* NamedColorList, const char* Name)
677
0
{
678
0
    cmsUInt32Number i;
679
0
    cmsUInt32Number n;
680
681
0
    if (NamedColorList == NULL) return -1;
682
0
    n = cmsNamedColorCount(ContextID, NamedColorList);
683
0
    for (i=0; i < n; i++) {
684
0
        if (cmsstrcasecmp(Name,  NamedColorList->List[i].Name) == 0)
685
0
            return (cmsInt32Number) i;
686
0
    }
687
688
0
    return -1;
689
0
}
690
691
// MPE support -----------------------------------------------------------------------------------------------------------------
692
693
static
694
void FreeNamedColorList(cmsContext ContextID, cmsStage* mpe)
695
0
{
696
0
    cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
697
0
    cmsFreeNamedColorList(ContextID, List);
698
0
}
699
700
static
701
void* DupNamedColorList(cmsContext ContextID, cmsStage* mpe)
702
0
{
703
0
    cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
704
0
    return cmsDupNamedColorList(ContextID, List);
705
0
}
706
707
static
708
void EvalNamedColorPCS(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
709
0
{
710
0
    cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;
711
0
    cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
712
713
0
    if (index >= NamedColorList-> nColors) {
714
0
        cmsSignalError(ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index);
715
0
        Out[0] = Out[1] = Out[2] = 0.0f;
716
0
    }
717
0
    else {
718
719
            // Named color always uses Lab
720
0
            Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0);
721
0
            Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0);
722
0
            Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0);
723
0
    }
724
0
}
725
726
static
727
void EvalNamedColor(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
728
0
{
729
0
    cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;
730
0
    cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
731
0
    cmsUInt32Number j;
732
733
0
    if (index >= NamedColorList-> nColors) {
734
0
        cmsSignalError(ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index);
735
0
        for (j = 0; j < NamedColorList->ColorantCount; j++)
736
0
            Out[j] = 0.0f;
737
0
    }
738
0
    else {
739
0
        for (j=0; j < NamedColorList ->ColorantCount; j++)
740
0
            Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0);
741
0
    }
742
0
}
743
744
745
// Named color lookup element
746
cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsContext ContextID, cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS)
747
0
{
748
0
    return _cmsStageAllocPlaceholder(ContextID,
749
0
                                   cmsSigNamedColorElemType,
750
0
                                   1, UsePCS ? 3 : NamedColorList ->ColorantCount,
751
0
                                   UsePCS ? EvalNamedColorPCS : EvalNamedColor,
752
0
                                   DupNamedColorList,
753
0
                                   FreeNamedColorList,
754
0
                                   cmsDupNamedColorList(ContextID, NamedColorList));
755
756
0
}
757
758
759
// Retrieve the named color list from a transform. Should be first element in the LUT
760
cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform)
761
0
{
762
0
    _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
763
0
    cmsStage* mpe  = v ->core->Lut->Elements;
764
765
0
    if (mpe ->Type != cmsSigNamedColorElemType) return NULL;
766
0
    return (cmsNAMEDCOLORLIST*) mpe ->Data;
767
0
}
768
769
770
// Profile sequence description routines -------------------------------------------------------------------------------------
771
772
cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n)
773
0
{
774
0
    cmsSEQ* Seq;
775
0
    cmsUInt32Number i;
776
777
0
    if (n == 0) return NULL;
778
779
    // In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked
780
    // in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door!
781
0
    if (n > 255) return NULL;
782
783
0
    Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ));
784
0
    if (Seq == NULL) return NULL;
785
786
0
    Seq -> seq      = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC));
787
0
    Seq -> n        = n;
788
789
0
    if (Seq -> seq == NULL) {
790
0
        _cmsFree(ContextID, Seq);
791
0
        return NULL;
792
0
    }
793
794
0
    for (i=0; i < n; i++) {
795
0
        Seq -> seq[i].Manufacturer = NULL;
796
0
        Seq -> seq[i].Model        = NULL;
797
0
        Seq -> seq[i].Description  = NULL;
798
0
    }
799
800
0
    return Seq;
801
0
}
802
803
void CMSEXPORT cmsFreeProfileSequenceDescription(cmsContext ContextID, cmsSEQ* pseq)
804
0
{
805
0
    cmsUInt32Number i;
806
807
0
    for (i=0; i < pseq ->n; i++) {
808
0
        if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(ContextID, pseq ->seq[i].Manufacturer);
809
0
        if (pseq ->seq[i].Model != NULL) cmsMLUfree(ContextID, pseq ->seq[i].Model);
810
0
        if (pseq ->seq[i].Description != NULL) cmsMLUfree(ContextID, pseq ->seq[i].Description);
811
0
    }
812
813
0
    if (pseq ->seq != NULL) _cmsFree(ContextID, pseq ->seq);
814
0
    _cmsFree(ContextID, pseq);
815
0
}
816
817
cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(cmsContext ContextID, const cmsSEQ* pseq)
818
0
{
819
0
    cmsSEQ *NewSeq;
820
0
    cmsUInt32Number i;
821
822
0
    if (pseq == NULL)
823
0
        return NULL;
824
825
0
    NewSeq = (cmsSEQ*) _cmsMalloc(ContextID, sizeof(cmsSEQ));
826
0
    if (NewSeq == NULL) return NULL;
827
828
829
0
    NewSeq -> seq      = (cmsPSEQDESC*) _cmsCalloc(ContextID, pseq ->n, sizeof(cmsPSEQDESC));
830
0
    if (NewSeq ->seq == NULL) goto Error;
831
832
0
    NewSeq -> n        = pseq ->n;
833
834
0
    for (i=0; i < pseq->n; i++) {
835
836
0
        memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number));
837
838
0
        NewSeq ->seq[i].deviceMfg   = pseq ->seq[i].deviceMfg;
839
0
        NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel;
840
0
        memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID));
841
0
        NewSeq ->seq[i].technology  = pseq ->seq[i].technology;
842
843
0
        NewSeq ->seq[i].Manufacturer = cmsMLUdup(ContextID, pseq ->seq[i].Manufacturer);
844
0
        NewSeq ->seq[i].Model        = cmsMLUdup(ContextID, pseq ->seq[i].Model);
845
0
        NewSeq ->seq[i].Description  = cmsMLUdup(ContextID, pseq ->seq[i].Description);
846
847
0
    }
848
849
0
    return NewSeq;
850
851
0
Error:
852
853
0
    cmsFreeProfileSequenceDescription(ContextID, NewSeq);
854
0
    return NULL;
855
0
}
856
857
// Dictionaries --------------------------------------------------------------------------------------------------------
858
859
// Dictionaries are just very simple linked lists
860
861
862
typedef struct _cmsDICT_struct {
863
    cmsDICTentry* head;
864
} _cmsDICT;
865
866
867
// Allocate an empty dictionary
868
cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID)
869
0
{
870
0
    _cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT));
871
0
    if (dict == NULL) return NULL;
872
873
0
    return (cmsHANDLE) dict;
874
875
0
}
876
877
// Dispose resources
878
void CMSEXPORT cmsDictFree(cmsContext ContextID, cmsHANDLE hDict)
879
0
{
880
0
    _cmsDICT* dict = (_cmsDICT*) hDict;
881
0
    cmsDICTentry *entry, *next;
882
883
0
    _cmsAssert(dict != NULL);
884
885
    // Walk the list freeing all nodes
886
0
    entry = dict ->head;
887
0
    while (entry != NULL) {
888
889
0
            if (entry ->DisplayName  != NULL) cmsMLUfree(ContextID, entry ->DisplayName);
890
0
            if (entry ->DisplayValue != NULL) cmsMLUfree(ContextID, entry ->DisplayValue);
891
0
            if (entry ->Name != NULL) _cmsFree(ContextID, entry -> Name);
892
0
            if (entry ->Value != NULL) _cmsFree(ContextID, entry -> Value);
893
894
            // Don't fall in the habitual trap...
895
0
            next = entry ->Next;
896
0
            _cmsFree(ContextID, entry);
897
898
0
            entry = next;
899
0
    }
900
901
0
    _cmsFree(ContextID, dict);
902
0
}
903
904
905
// Duplicate a wide char string
906
static
907
wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr)
908
0
{
909
0
    if (ptr == NULL) return NULL;
910
0
    return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t));
911
0
}
912
913
// Add a new entry to the linked list
914
cmsBool CMSEXPORT cmsDictAddEntry(cmsContext ContextID, cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue)
915
0
{
916
0
    _cmsDICT* dict = (_cmsDICT*) hDict;
917
0
    cmsDICTentry *entry;
918
919
0
    _cmsAssert(dict != NULL);
920
0
    _cmsAssert(Name != NULL);
921
922
0
    entry = (cmsDICTentry*) _cmsMallocZero(ContextID, sizeof(cmsDICTentry));
923
0
    if (entry == NULL) return FALSE;
924
925
0
    entry ->DisplayName  = cmsMLUdup(ContextID, DisplayName);
926
0
    entry ->DisplayValue = cmsMLUdup(ContextID, DisplayValue);
927
0
    entry ->Name         = DupWcs(ContextID, Name);
928
0
    entry ->Value        = DupWcs(ContextID, Value);
929
930
0
    entry ->Next = dict ->head;
931
0
    dict ->head = entry;
932
933
0
    return TRUE;
934
0
}
935
936
937
// Duplicates an existing dictionary
938
cmsHANDLE CMSEXPORT cmsDictDup(cmsContext ContextID, cmsHANDLE hDict)
939
0
{
940
0
    _cmsDICT* old_dict = (_cmsDICT*) hDict;
941
0
    cmsHANDLE hNew;
942
0
    cmsDICTentry *entry;
943
944
0
    _cmsAssert(old_dict != NULL);
945
946
0
    hNew  = cmsDictAlloc(ContextID);
947
0
    if (hNew == NULL) return NULL;
948
949
    // Walk the list freeing all nodes
950
0
    entry = old_dict ->head;
951
0
    while (entry != NULL) {
952
953
0
        if (!cmsDictAddEntry(ContextID, hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) {
954
955
0
            cmsDictFree(ContextID, hNew);
956
0
            return NULL;
957
0
        }
958
959
0
        entry = entry -> Next;
960
0
    }
961
962
0
    return hNew;
963
0
}
964
965
// Get a pointer to the linked list
966
const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsContext ContextID, cmsHANDLE hDict)
967
0
{
968
0
    _cmsDICT* dict = (_cmsDICT*) hDict;
969
0
    cmsUNUSED_PARAMETER(ContextID);
970
971
0
    if (dict == NULL) return NULL;
972
0
    return dict ->head;
973
0
}
974
975
// Helper For external languages
976
const cmsDICTentry* CMSEXPORT cmsDictNextEntry(cmsContext ContextID, const cmsDICTentry* e)
977
0
{
978
0
    cmsUNUSED_PARAMETER(ContextID);
979
0
    if (e == NULL) return NULL;
980
0
     return e ->Next;
981
0
}