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

ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4. The directory name has changed: no version number in the expanded directory now.
author Franz Glasner <fzglas.hg@dom66.de>
date Mon, 15 Sep 2025 11:43:07 +0200
parents
children
comparison
equal deleted inserted replaced
1:1d09e1dec1d9 2:b50eed0cc0ef
1 //---------------------------------------------------------------------------------
2 //
3 // Little Color Management System
4 // Copyright (c) 1998-2023 Marti Maria Saguer
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining
7 // a copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the Software
11 // is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
18 // THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 //
24 //---------------------------------------------------------------------------------
25 //
26
27 #include "lcms2_internal.h"
28
29 // 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 {
35 cmsMLU* mlu;
36
37 // nItems should be positive if given
38 if (nItems <= 0) nItems = 2;
39
40 // Create the container
41 mlu = (cmsMLU*) _cmsMallocZero(ContextID, sizeof(cmsMLU));
42 if (mlu == NULL) return NULL;
43
44 // Create entry array
45 mlu ->Entries = (_cmsMLUentry*) _cmsCalloc(ContextID, nItems, sizeof(_cmsMLUentry));
46 if (mlu ->Entries == NULL) {
47 _cmsFree(ContextID, mlu);
48 return NULL;
49 }
50
51 // Ok, keep indexes up to date
52 mlu ->AllocatedEntries = nItems;
53 mlu ->UsedEntries = 0;
54
55 return mlu;
56 }
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 {
63 cmsUInt32Number size;
64 void *NewPtr;
65
66 // Sanity check
67 if (mlu == NULL) return FALSE;
68
69 if (mlu ->PoolSize == 0)
70 size = 256;
71 else
72 size = mlu ->PoolSize * 2;
73
74 // Check for overflow
75 if (size < mlu ->PoolSize) return FALSE;
76
77 // Reallocate the pool
78 NewPtr = _cmsRealloc(ContextID, mlu ->MemPool, size);
79 if (NewPtr == NULL) return FALSE;
80
81
82 mlu ->MemPool = NewPtr;
83 mlu ->PoolSize = size;
84
85 return TRUE;
86 }
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 {
93 cmsUInt32Number AllocatedEntries;
94 _cmsMLUentry *NewPtr;
95
96 // Sanity check
97 if (mlu == NULL) return FALSE;
98
99 AllocatedEntries = mlu ->AllocatedEntries * 2;
100
101 // Check for overflow
102 if (AllocatedEntries / 2 != mlu ->AllocatedEntries) return FALSE;
103
104 // Reallocate the memory
105 NewPtr = (_cmsMLUentry*)_cmsRealloc(ContextID, mlu ->Entries, AllocatedEntries*sizeof(_cmsMLUentry));
106 if (NewPtr == NULL) return FALSE;
107
108 mlu ->Entries = NewPtr;
109 mlu ->AllocatedEntries = AllocatedEntries;
110
111 return TRUE;
112 }
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 {
119 cmsUInt32Number i;
120
121 // Sanity check
122 if (mlu == NULL) return -1;
123
124 // Iterate whole table
125 for (i=0; i < mlu ->UsedEntries; i++) {
126
127 if (mlu ->Entries[i].Country == CountryCode &&
128 mlu ->Entries[i].Language == LanguageCode) return (int) i;
129 }
130
131 // Not found
132 return -1;
133 }
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 {
141 cmsUInt32Number Offset;
142 cmsUInt8Number* Ptr;
143
144 // Sanity check
145 if (mlu == NULL) return FALSE;
146
147 // Is there any room available?
148 if (mlu ->UsedEntries >= mlu ->AllocatedEntries) {
149 if (!GrowMLUtable(ContextID, mlu)) return FALSE;
150 }
151
152 // Only one ASCII string
153 if (SearchMLUEntry(mlu, LanguageCode, CountryCode) >= 0) return FALSE; // Only one is allowed!
154
155 // Check for size
156 while ((mlu ->PoolSize - mlu ->PoolUsed) < size) {
157
158 if (!GrowMLUpool(ContextID, mlu)) return FALSE;
159 }
160
161 Offset = mlu ->PoolUsed;
162
163 Ptr = (cmsUInt8Number*) mlu ->MemPool;
164 if (Ptr == NULL) return FALSE;
165
166 // Set the entry
167 memmove(Ptr + Offset, Block, size);
168 mlu ->PoolUsed += size;
169
170 mlu ->Entries[mlu ->UsedEntries].StrW = Offset;
171 mlu ->Entries[mlu ->UsedEntries].Len = size;
172 mlu ->Entries[mlu ->UsedEntries].Country = CountryCode;
173 mlu ->Entries[mlu ->UsedEntries].Language = LanguageCode;
174 mlu ->UsedEntries++;
175
176 return TRUE;
177 }
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 {
184 const cmsUInt8Number* ptr8;
185 cmsUInt16Number n;
186
187 // For non-existent strings
188 if (str == NULL) return 0;
189 ptr8 = (const cmsUInt8Number*)str;
190 n = (cmsUInt16Number)(((cmsUInt16Number)ptr8[0] << 8) | ptr8[1]);
191
192 return n;
193 }
194
195 static
196 void strFrom16(char str[3], cmsUInt16Number n)
197 {
198 str[0] = (char)(n >> 8);
199 str[1] = (char)n;
200 str[2] = (char)0;
201 }
202
203
204 // Convert from UTF8 to wchar, returns len.
205 static
206 cmsUInt32Number decodeUTF8(wchar_t* out, const char* in)
207 {
208 cmsUInt32Number codepoint = 0;
209 cmsUInt32Number size = 0;
210
211 while (*in)
212 {
213 cmsUInt8Number ch = (cmsUInt8Number) *in;
214
215 if (ch <= 0x7f)
216 {
217 codepoint = ch;
218 }
219 else if (ch <= 0xbf)
220 {
221 codepoint = (codepoint << 6) | (ch & 0x3f);
222 }
223 else if (ch <= 0xdf)
224 {
225 codepoint = ch & 0x1f;
226 }
227 else if (ch <= 0xef)
228 {
229 codepoint = ch & 0x0f;
230 }
231 else
232 {
233 codepoint = ch & 0x07;
234 }
235
236 in++;
237
238 if (((*in & 0xc0) != 0x80) && (codepoint <= 0x10ffff))
239 {
240 if (sizeof(wchar_t) > 2)
241 {
242 if (out) *out++ = (wchar_t) codepoint;
243 size++;
244 }
245 else
246 if (codepoint > 0xffff)
247 {
248 if (out)
249 {
250 *out++ = (wchar_t)(0xd800 + (codepoint >> 10));
251 *out++ = (wchar_t)(0xdc00 + (codepoint & 0x03ff));
252 size += 2;
253 }
254 }
255 else
256 if (codepoint < 0xd800 || codepoint >= 0xe000)
257 {
258 if (out) *out++ = (wchar_t) codepoint;
259 size++;
260 }
261 }
262 }
263
264 return size;
265 }
266
267 // Convert from wchar_t to UTF8
268 static
269 cmsUInt32Number encodeUTF8(char* out, const wchar_t* in, cmsUInt32Number max_wchars, cmsUInt32Number max_chars)
270 {
271 cmsUInt32Number codepoint = 0;
272 cmsUInt32Number size = 0;
273 cmsUInt32Number len_w = 0;
274
275 while (*in && len_w < max_wchars)
276 {
277 if (*in >= 0xd800 && *in <= 0xdbff)
278 codepoint = ((*in - 0xd800) << 10) + 0x10000;
279 else
280 {
281 if (*in >= 0xdc00 && *in <= 0xdfff)
282 codepoint |= *in - 0xdc00;
283 else
284 codepoint = *in;
285
286 if (codepoint <= 0x7f)
287 {
288 if (out && (size + 1 < max_chars)) *out++ = (char)codepoint;
289 size++;
290 }
291
292 else if (codepoint <= 0x7ff)
293 {
294 if (out && (max_chars > 0) && (size + 2 < max_chars))
295 {
296 *out++ = (char)(cmsUInt32Number)(0xc0 | ((codepoint >> 6) & 0x1f));
297 *out++ = (char)(cmsUInt32Number)(0x80 | (codepoint & 0x3f));
298 }
299 size += 2;
300 }
301 else if (codepoint <= 0xffff)
302 {
303 if (out && (max_chars > 0) && (size + 3 < max_chars))
304 {
305 *out++ = (char)(cmsUInt32Number)(0xe0 | ((codepoint >> 12) & 0x0f));
306 *out++ = (char)(cmsUInt32Number)(0x80 | ((codepoint >> 6) & 0x3f));
307 *out++ = (char)(cmsUInt32Number)(0x80 | (codepoint & 0x3f));
308 }
309 size += 3;
310 }
311 else
312 {
313 if (out && (max_chars > 0) && (size + 4 < max_chars))
314 {
315 *out++ = (char)(cmsUInt32Number)(0xf0 | ((codepoint >> 18) & 0x07));
316 *out++ = (char)(cmsUInt32Number)(0x80 | ((codepoint >> 12) & 0x3f));
317 *out++ = (char)(cmsUInt32Number)(0x80 | ((codepoint >> 6) & 0x3f));
318 *out++ = (char)(cmsUInt32Number)(0x80 | (codepoint & 0x3f));
319 }
320 size += 4;
321 }
322
323 codepoint = 0;
324 }
325
326 in++; len_w++;
327 }
328
329 return size;
330 }
331
332 // Add an ASCII entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)
333 // In the case the user explicitly sets an empty string, we force a \0
334 cmsBool CMSEXPORT cmsMLUsetASCII(cmsContext ContextID, cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* ASCIIString)
335 {
336 cmsUInt32Number i, len = (cmsUInt32Number)strlen(ASCIIString);
337 wchar_t* WStr;
338 cmsBool rc;
339 cmsUInt16Number Lang = strTo16(LanguageCode);
340 cmsUInt16Number Cntry = strTo16(CountryCode);
341
342 if (mlu == NULL) return FALSE;
343
344 // len == 0 would prevent operation, so we set a empty string pointing to zero
345 if (len == 0)
346 {
347 wchar_t empty = 0;
348 return AddMLUBlock(ContextID, mlu, sizeof(wchar_t), &empty, Lang, Cntry);
349 }
350
351 WStr = (wchar_t*) _cmsCalloc(ContextID, len, sizeof(wchar_t));
352 if (WStr == NULL) return FALSE;
353
354 for (i = 0; i < len; i++)
355 WStr[i] = (wchar_t)ASCIIString[i];
356
357 rc = AddMLUBlock(ContextID, mlu, len * sizeof(wchar_t), WStr, Lang, Cntry);
358
359 _cmsFree(ContextID, WStr);
360 return rc;
361
362 }
363
364 // Add an UTF8 entry. Do not add any \0 termination (ICC1v43_2010-12.pdf page 61)
365 // In the case the user explicitly sets an empty string, we force a \0
366 cmsBool CMSEXPORT cmsMLUsetUTF8(cmsContext ContextID, cmsMLU* mlu, const char LanguageCode[3], const char CountryCode[3], const char* UTF8String)
367 {
368 cmsUInt32Number UTF8len;
369 wchar_t* WStr;
370 cmsBool rc;
371 cmsUInt16Number Lang = strTo16(LanguageCode);
372 cmsUInt16Number Cntry = strTo16(CountryCode);
373
374 if (mlu == NULL) return FALSE;
375
376 if (*UTF8String == '\0')
377 {
378 wchar_t empty = 0;
379 return AddMLUBlock(ContextID, mlu, sizeof(wchar_t), &empty, Lang, Cntry);
380 }
381
382 // Len excluding terminator 0
383 UTF8len = decodeUTF8(NULL, UTF8String);
384
385 // Get space for dest
386 WStr = (wchar_t*) _cmsCalloc(ContextID, UTF8len, sizeof(wchar_t));
387 if (WStr == NULL) return FALSE;
388
389 decodeUTF8(WStr, UTF8String);
390
391 rc = AddMLUBlock(ContextID, mlu, UTF8len * sizeof(wchar_t), WStr, Lang, Cntry);
392
393 _cmsFree(ContextID, WStr);
394 return rc;
395 }
396
397 // We don't need any wcs support library
398 static
399 cmsUInt32Number mywcslen(const wchar_t *s)
400 {
401 const wchar_t *p;
402
403 p = s;
404 while (*p)
405 p++;
406
407 return (cmsUInt32Number)(p - s);
408 }
409
410 // Add a wide entry. Do not add any \0 terminator (ICC1v43_2010-12.pdf page 61)
411 cmsBool CMSEXPORT cmsMLUsetWide(cmsContext ContextID, cmsMLU* mlu, const char Language[3], const char Country[3], const wchar_t* WideString)
412 {
413 cmsUInt16Number Lang = strTo16(Language);
414 cmsUInt16Number Cntry = strTo16(Country);
415 cmsUInt32Number len;
416
417 if (mlu == NULL) return FALSE;
418 if (WideString == NULL) return FALSE;
419
420 len = (cmsUInt32Number) (mywcslen(WideString)) * sizeof(wchar_t);
421 if (len == 0)
422 len = sizeof(wchar_t);
423
424 return AddMLUBlock(ContextID, mlu, len, WideString, Lang, Cntry);
425 }
426
427 // Duplicating a MLU is as easy as copying all members
428 cmsMLU* CMSEXPORT cmsMLUdup(cmsContext ContextID, const cmsMLU* mlu)
429 {
430 cmsMLU* NewMlu = NULL;
431
432 // Duplicating a NULL obtains a NULL
433 if (mlu == NULL) return NULL;
434
435 NewMlu = cmsMLUalloc(ContextID, mlu ->UsedEntries);
436 if (NewMlu == NULL) return NULL;
437
438 // Should never happen
439 if (NewMlu ->AllocatedEntries < mlu ->UsedEntries)
440 goto Error;
441
442 // Sanitize...
443 if (NewMlu ->Entries == NULL || mlu ->Entries == NULL) goto Error;
444
445 memmove(NewMlu ->Entries, mlu ->Entries, mlu ->UsedEntries * sizeof(_cmsMLUentry));
446 NewMlu ->UsedEntries = mlu ->UsedEntries;
447
448 // The MLU may be empty
449 if (mlu ->PoolUsed == 0) {
450 NewMlu ->MemPool = NULL;
451 }
452 else {
453 // It is not empty
454 NewMlu ->MemPool = _cmsMalloc(ContextID, mlu ->PoolUsed);
455 if (NewMlu ->MemPool == NULL) goto Error;
456 }
457
458 NewMlu ->PoolSize = mlu ->PoolUsed;
459
460 if (NewMlu ->MemPool == NULL || mlu ->MemPool == NULL) goto Error;
461
462 memmove(NewMlu ->MemPool, mlu->MemPool, mlu ->PoolUsed);
463 NewMlu ->PoolUsed = mlu ->PoolUsed;
464
465 return NewMlu;
466
467 Error:
468
469 if (NewMlu != NULL) cmsMLUfree(ContextID, NewMlu);
470 return NULL;
471 }
472
473 // Free any used memory
474 void CMSEXPORT cmsMLUfree(cmsContext ContextID, cmsMLU* mlu)
475 {
476 if (mlu) {
477
478 if (mlu -> Entries) _cmsFree(ContextID, mlu->Entries);
479 if (mlu -> MemPool) _cmsFree(ContextID, mlu->MemPool);
480
481 _cmsFree(ContextID, mlu);
482 }
483 }
484
485
486 // The algorithm first searches for an exact match of country and language, if not found it uses
487 // the Language. If none is found, first entry is used instead.
488 static
489 const wchar_t* _cmsMLUgetWide(const cmsMLU* mlu,
490 cmsUInt32Number *len,
491 cmsUInt16Number LanguageCode, cmsUInt16Number CountryCode,
492 cmsUInt16Number* UsedLanguageCode, cmsUInt16Number* UsedCountryCode)
493 {
494 cmsUInt32Number i;
495 int Best = -1;
496 _cmsMLUentry* v;
497
498 if (mlu == NULL) return NULL;
499
500 if (mlu -> AllocatedEntries <= 0) return NULL;
501
502 for (i=0; i < mlu ->UsedEntries; i++) {
503
504 v = mlu ->Entries + i;
505
506 if (v -> Language == LanguageCode) {
507
508 if (Best == -1) Best = (int) i;
509
510 if (v -> Country == CountryCode) {
511
512 if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
513 if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;
514
515 if (len != NULL) *len = v ->Len;
516
517 return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v -> StrW); // Found exact match
518 }
519 }
520 }
521
522 // No string found. Return First one
523 if (Best == -1)
524 Best = 0;
525
526 v = mlu ->Entries + Best;
527
528 if (UsedLanguageCode != NULL) *UsedLanguageCode = v ->Language;
529 if (UsedCountryCode != NULL) *UsedCountryCode = v ->Country;
530
531 if (len != NULL) *len = v ->Len;
532
533 if (v->StrW + v->Len > mlu->PoolSize) return NULL;
534
535 return (wchar_t*) ((cmsUInt8Number*) mlu ->MemPool + v ->StrW);
536 }
537
538
539 // Obtain an ASCII representation of the wide string. Setting buffer to NULL returns the len
540 cmsUInt32Number CMSEXPORT cmsMLUgetASCII(cmsContext ContextID, const cmsMLU* mlu,
541 const char LanguageCode[3], const char CountryCode[3],
542 char* Buffer, cmsUInt32Number BufferSize)
543 {
544 const wchar_t *Wide;
545 cmsUInt32Number StrLen = 0;
546 cmsUInt32Number ASCIIlen, i;
547
548 cmsUInt16Number Lang = strTo16(LanguageCode);
549 cmsUInt16Number Cntry = strTo16(CountryCode);
550 cmsUNUSED_PARAMETER(ContextID);
551
552 // Sanitize
553 if (mlu == NULL) return 0;
554
555 // Get WideChar
556 Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
557 if (Wide == NULL) return 0;
558
559 ASCIIlen = StrLen / sizeof(wchar_t);
560
561 // Maybe we want only to know the len?
562 if (Buffer == NULL) return ASCIIlen + 1; // Note the zero at the end
563
564 // No buffer size means no data
565 if (BufferSize <= 0) return 0;
566
567 // Some clipping may be required
568 if (BufferSize < ASCIIlen + 1)
569 ASCIIlen = BufferSize - 1;
570
571 // Precess each character
572 for (i=0; i < ASCIIlen; i++) {
573
574 wchar_t wc = Wide[i];
575
576 if (wc < 0xff)
577 Buffer[i] = (char)wc;
578 else
579 Buffer[i] = '?';
580 }
581
582 // We put a termination "\0"
583 Buffer[ASCIIlen] = 0;
584 return ASCIIlen + 1;
585 }
586
587
588 // Obtain a UTF8 representation of the wide string. Setting buffer to NULL returns the len
589 cmsUInt32Number CMSEXPORT cmsMLUgetUTF8(cmsContext ContextID, const cmsMLU* mlu,
590 const char LanguageCode[3], const char CountryCode[3],
591 char* Buffer, cmsUInt32Number BufferSize)
592 {
593 const wchar_t *Wide;
594 cmsUInt32Number StrLen = 0;
595 cmsUInt32Number UTF8len;
596
597 cmsUInt16Number Lang = strTo16(LanguageCode);
598 cmsUInt16Number Cntry = strTo16(CountryCode);
599
600 // Sanitize
601 if (mlu == NULL) return 0;
602
603 // Get WideChar
604 Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
605 if (Wide == NULL) return 0;
606
607 UTF8len = encodeUTF8(NULL, Wide, StrLen / sizeof(wchar_t), BufferSize);
608
609 // Maybe we want only to know the len?
610 if (Buffer == NULL) return UTF8len + 1; // Note the zero at the end
611
612 // No buffer size means no data
613 if (BufferSize <= 0) return 0;
614
615 // Some clipping may be required
616 if (BufferSize < UTF8len + 1)
617 UTF8len = BufferSize - 1;
618
619 // Process it
620 encodeUTF8(Buffer, Wide, StrLen / sizeof(wchar_t), BufferSize);
621
622 // We put a termination "\0"
623 Buffer[UTF8len] = 0;
624 return UTF8len + 1;
625 }
626
627 // Obtain a wide representation of the MLU, on depending on current locale settings
628 cmsUInt32Number CMSEXPORT cmsMLUgetWide(cmsContext ContextID, const cmsMLU* mlu,
629 const char LanguageCode[3], const char CountryCode[3],
630 wchar_t* Buffer, cmsUInt32Number BufferSize)
631 {
632 const wchar_t *Wide;
633 cmsUInt32Number StrLen = 0;
634
635 cmsUInt16Number Lang = strTo16(LanguageCode);
636 cmsUInt16Number Cntry = strTo16(CountryCode);
637 cmsUNUSED_PARAMETER(ContextID);
638
639 // Sanitize
640 if (mlu == NULL) return 0;
641
642 Wide = _cmsMLUgetWide(mlu, &StrLen, Lang, Cntry, NULL, NULL);
643 if (Wide == NULL) return 0;
644
645 // Maybe we want only to know the len?
646 if (Buffer == NULL) return StrLen + sizeof(wchar_t);
647
648 // Invalid buffer size means no data
649 if (BufferSize < sizeof(wchar_t)) return 0;
650
651 // Some clipping may be required
652 if (BufferSize < StrLen + sizeof(wchar_t))
653 StrLen = BufferSize - sizeof(wchar_t);
654
655 memmove(Buffer, Wide, StrLen);
656 Buffer[StrLen / sizeof(wchar_t)] = 0;
657
658 return StrLen + sizeof(wchar_t);
659 }
660
661
662 // Get also the language and country
663 CMSAPI cmsBool CMSEXPORT cmsMLUgetTranslation(cmsContext ContextID, const cmsMLU* mlu,
664 const char LanguageCode[3], const char CountryCode[3],
665 char ObtainedLanguage[3], char ObtainedCountry[3])
666 {
667 const wchar_t *Wide;
668
669 cmsUInt16Number Lang = strTo16(LanguageCode);
670 cmsUInt16Number Cntry = strTo16(CountryCode);
671 cmsUInt16Number ObtLang, ObtCode;
672 cmsUNUSED_PARAMETER(ContextID);
673
674 // Sanitize
675 if (mlu == NULL) return FALSE;
676
677 Wide = _cmsMLUgetWide(mlu, NULL, Lang, Cntry, &ObtLang, &ObtCode);
678 if (Wide == NULL) return FALSE;
679
680 // Get used language and code
681 strFrom16(ObtainedLanguage, ObtLang);
682 strFrom16(ObtainedCountry, ObtCode);
683
684 return TRUE;
685 }
686
687
688
689 // Get the number of translations in the MLU object
690 cmsUInt32Number CMSEXPORT cmsMLUtranslationsCount(cmsContext ContextID, const cmsMLU* mlu)
691 {
692 cmsUNUSED_PARAMETER(ContextID);
693 if (mlu == NULL) return 0;
694 return mlu->UsedEntries;
695 }
696
697 // Get the language and country codes for a specific MLU index
698 cmsBool CMSEXPORT cmsMLUtranslationsCodes(cmsContext ContextID,
699 const cmsMLU* mlu,
700 cmsUInt32Number idx,
701 char LanguageCode[3],
702 char CountryCode[3])
703 {
704 _cmsMLUentry *entry;
705 cmsUNUSED_PARAMETER(ContextID);
706
707 if (mlu == NULL) return FALSE;
708
709 if (idx >= mlu->UsedEntries) return FALSE;
710
711 entry = &mlu->Entries[idx];
712
713 strFrom16(LanguageCode, entry->Language);
714 strFrom16(CountryCode, entry->Country);
715
716 return TRUE;
717 }
718
719
720 // Named color lists --------------------------------------------------------------------------------------------
721
722 // Grow the list to keep at least NumElements
723 static
724 cmsBool GrowNamedColorList(cmsContext ContextID, cmsNAMEDCOLORLIST* v)
725 {
726 cmsUInt32Number size;
727 _cmsNAMEDCOLOR * NewPtr;
728
729 if (v == NULL) return FALSE;
730
731 if (v ->Allocated == 0)
732 size = 64; // Initial guess
733 else
734 size = v ->Allocated * 2;
735
736 // Keep a maximum color lists can grow, 100K entries seems reasonable
737 if (size > 1024 * 100) {
738 _cmsFree(ContextID, (void*) v->List);
739 v->List = NULL;
740 return FALSE;
741 }
742
743 NewPtr = (_cmsNAMEDCOLOR*) _cmsRealloc(ContextID, v ->List, size * sizeof(_cmsNAMEDCOLOR));
744 if (NewPtr == NULL)
745 return FALSE;
746
747 v ->List = NewPtr;
748 v ->Allocated = size;
749 return TRUE;
750 }
751
752 // Allocate a list for n elements
753 cmsNAMEDCOLORLIST* CMSEXPORT cmsAllocNamedColorList(cmsContext ContextID, cmsUInt32Number n, cmsUInt32Number ColorantCount, const char* Prefix, const char* Suffix)
754 {
755 cmsNAMEDCOLORLIST* v;
756
757 if (ColorantCount > cmsMAXCHANNELS)
758 return NULL;
759
760 v = (cmsNAMEDCOLORLIST*)_cmsMallocZero(ContextID, sizeof(cmsNAMEDCOLORLIST));
761 if (v == NULL) return NULL;
762
763 v ->List = NULL;
764 v ->nColors = 0;
765
766 while (v -> Allocated < n) {
767 if (!GrowNamedColorList(ContextID, v)) {
768 cmsFreeNamedColorList(ContextID, v);
769 return NULL;
770 }
771 }
772
773 strncpy(v ->Prefix, Prefix, sizeof(v ->Prefix)-1);
774 strncpy(v ->Suffix, Suffix, sizeof(v ->Suffix)-1);
775 v->Prefix[32] = v->Suffix[32] = 0;
776
777 v -> ColorantCount = ColorantCount;
778
779 return v;
780 }
781
782 // Free a list
783 void CMSEXPORT cmsFreeNamedColorList(cmsContext ContextID, cmsNAMEDCOLORLIST* v)
784 {
785 if (v == NULL) return;
786 if (v ->List) _cmsFree(ContextID, v ->List);
787 _cmsFree(ContextID, v);
788 }
789
790 cmsNAMEDCOLORLIST* CMSEXPORT cmsDupNamedColorList(cmsContext ContextID, const cmsNAMEDCOLORLIST* v)
791 {
792 cmsNAMEDCOLORLIST* NewNC;
793
794 if (v == NULL) return NULL;
795
796 NewNC= cmsAllocNamedColorList(ContextID, v -> nColors, v ->ColorantCount, v ->Prefix, v ->Suffix);
797 if (NewNC == NULL) return NULL;
798
799 // For really large tables we need this
800 while (NewNC ->Allocated < v ->Allocated){
801 if (!GrowNamedColorList(ContextID, NewNC))
802 {
803 cmsFreeNamedColorList(ContextID, NewNC);
804 return NULL;
805 }
806 }
807
808 memmove(NewNC ->Prefix, v ->Prefix, sizeof(v ->Prefix));
809 memmove(NewNC ->Suffix, v ->Suffix, sizeof(v ->Suffix));
810 NewNC ->ColorantCount = v ->ColorantCount;
811 memmove(NewNC->List, v ->List, v->nColors * sizeof(_cmsNAMEDCOLOR));
812 NewNC ->nColors = v ->nColors;
813 return NewNC;
814 }
815
816
817 // Append a color to a list. List pointer may change if reallocated
818 cmsBool CMSEXPORT cmsAppendNamedColor(cmsContext ContextID, cmsNAMEDCOLORLIST* NamedColorList,
819 const char* Name,
820 cmsUInt16Number PCS[3], cmsUInt16Number Colorant[cmsMAXCHANNELS])
821 {
822 cmsUInt32Number i;
823
824 if (NamedColorList == NULL) return FALSE;
825
826 if (NamedColorList ->nColors + 1 > NamedColorList ->Allocated) {
827 if (!GrowNamedColorList(ContextID, NamedColorList)) return FALSE;
828 }
829
830 for (i=0; i < NamedColorList ->ColorantCount; i++)
831 NamedColorList ->List[NamedColorList ->nColors].DeviceColorant[i] = Colorant == NULL ? (cmsUInt16Number)0 : Colorant[i];
832
833 for (i=0; i < 3; i++)
834 NamedColorList ->List[NamedColorList ->nColors].PCS[i] = PCS == NULL ? (cmsUInt16Number) 0 : PCS[i];
835
836 if (Name != NULL) {
837
838 strncpy(NamedColorList ->List[NamedColorList ->nColors].Name, Name, cmsMAX_PATH-1);
839 NamedColorList ->List[NamedColorList ->nColors].Name[cmsMAX_PATH-1] = 0;
840
841 }
842 else
843 NamedColorList ->List[NamedColorList ->nColors].Name[0] = 0;
844
845
846 NamedColorList ->nColors++;
847 return TRUE;
848 }
849
850 // Returns number of elements
851 cmsUInt32Number CMSEXPORT cmsNamedColorCount(cmsContext ContextID, const cmsNAMEDCOLORLIST* NamedColorList)
852 {
853 cmsUNUSED_PARAMETER(ContextID);
854 if (NamedColorList == NULL) return 0;
855 return NamedColorList ->nColors;
856 }
857
858 // Info about a given color
859 cmsBool CMSEXPORT cmsNamedColorInfo(cmsContext ContextID, const cmsNAMEDCOLORLIST* NamedColorList, cmsUInt32Number nColor,
860 char* Name,
861 char* Prefix,
862 char* Suffix,
863 cmsUInt16Number* PCS,
864 cmsUInt16Number* Colorant)
865 {
866 if (NamedColorList == NULL) return FALSE;
867
868 if (nColor >= cmsNamedColorCount(ContextID, NamedColorList)) return FALSE;
869
870 // strcpy instead of strncpy because many apps are using small buffers
871 if (Name) strcpy(Name, NamedColorList->List[nColor].Name);
872 if (Prefix) strcpy(Prefix, NamedColorList->Prefix);
873 if (Suffix) strcpy(Suffix, NamedColorList->Suffix);
874 if (PCS)
875 memmove(PCS, NamedColorList ->List[nColor].PCS, 3*sizeof(cmsUInt16Number));
876
877 if (Colorant)
878 memmove(Colorant, NamedColorList ->List[nColor].DeviceColorant,
879 sizeof(cmsUInt16Number) * NamedColorList ->ColorantCount);
880
881
882 return TRUE;
883 }
884
885 // Search for a given color name (no prefix or suffix)
886 cmsInt32Number CMSEXPORT cmsNamedColorIndex(cmsContext ContextID, const cmsNAMEDCOLORLIST* NamedColorList, const char* Name)
887 {
888 cmsUInt32Number i;
889 cmsUInt32Number n;
890
891 if (NamedColorList == NULL) return -1;
892 n = cmsNamedColorCount(ContextID, NamedColorList);
893 for (i=0; i < n; i++) {
894 if (cmsstrcasecmp(Name, NamedColorList->List[i].Name) == 0)
895 return (cmsInt32Number) i;
896 }
897
898 return -1;
899 }
900
901 // MPE support -----------------------------------------------------------------------------------------------------------------
902
903 static
904 void FreeNamedColorList(cmsContext ContextID, cmsStage* mpe)
905 {
906 cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
907 cmsFreeNamedColorList(ContextID, List);
908 }
909
910 static
911 void* DupNamedColorList(cmsContext ContextID, cmsStage* mpe)
912 {
913 cmsNAMEDCOLORLIST* List = (cmsNAMEDCOLORLIST*) mpe ->Data;
914 return cmsDupNamedColorList(ContextID, List);
915 }
916
917 static
918 void EvalNamedColorPCS(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
919 {
920 cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;
921 cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
922
923 if (index >= NamedColorList-> nColors) {
924 cmsSignalError(ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index);
925 Out[0] = Out[1] = Out[2] = 0.0f;
926 }
927 else {
928
929 // Named color always uses Lab
930 Out[0] = (cmsFloat32Number) (NamedColorList->List[index].PCS[0] / 65535.0);
931 Out[1] = (cmsFloat32Number) (NamedColorList->List[index].PCS[1] / 65535.0);
932 Out[2] = (cmsFloat32Number) (NamedColorList->List[index].PCS[2] / 65535.0);
933 }
934 }
935
936 static
937 void EvalNamedColor(cmsContext ContextID, const cmsFloat32Number In[], cmsFloat32Number Out[], const cmsStage *mpe)
938 {
939 cmsNAMEDCOLORLIST* NamedColorList = (cmsNAMEDCOLORLIST*) mpe ->Data;
940 cmsUInt16Number index = (cmsUInt16Number) _cmsQuickSaturateWord(In[0] * 65535.0);
941 cmsUInt32Number j;
942
943 if (index >= NamedColorList-> nColors) {
944 cmsSignalError(ContextID, cmsERROR_RANGE, "Color %d out of range; ignored", index);
945 for (j = 0; j < NamedColorList->ColorantCount; j++)
946 Out[j] = 0.0f;
947 }
948 else {
949 for (j=0; j < NamedColorList ->ColorantCount; j++)
950 Out[j] = (cmsFloat32Number) (NamedColorList->List[index].DeviceColorant[j] / 65535.0);
951 }
952 }
953
954
955 // Named color lookup element
956 cmsStage* CMSEXPORT _cmsStageAllocNamedColor(cmsContext ContextID, cmsNAMEDCOLORLIST* NamedColorList, cmsBool UsePCS)
957 {
958 return _cmsStageAllocPlaceholder(ContextID,
959 cmsSigNamedColorElemType,
960 1, UsePCS ? 3 : NamedColorList ->ColorantCount,
961 UsePCS ? EvalNamedColorPCS : EvalNamedColor,
962 DupNamedColorList,
963 FreeNamedColorList,
964 cmsDupNamedColorList(ContextID, NamedColorList));
965
966 }
967
968
969 // Retrieve the named color list from a transform. Should be first element in the LUT
970 cmsNAMEDCOLORLIST* CMSEXPORT cmsGetNamedColorList(cmsHTRANSFORM xform)
971 {
972 _cmsTRANSFORM* v = (_cmsTRANSFORM*) xform;
973 cmsStage* mpe;
974
975 if (v == NULL) return NULL;
976 if (v->core == NULL) return NULL;
977 if (v->core->Lut == NULL) return NULL;
978
979 mpe = v->core->Lut->Elements;
980 if (mpe == NULL) return NULL;
981
982 if (mpe ->Type != cmsSigNamedColorElemType) return NULL;
983 return (cmsNAMEDCOLORLIST*) mpe ->Data;
984 }
985
986
987 // Profile sequence description routines -------------------------------------------------------------------------------------
988
989 cmsSEQ* CMSEXPORT cmsAllocProfileSequenceDescription(cmsContext ContextID, cmsUInt32Number n)
990 {
991 cmsSEQ* Seq;
992 cmsUInt32Number i;
993
994 if (n == 0) return NULL;
995
996 // In a absolutely arbitrary way, I hereby decide to allow a maxim of 255 profiles linked
997 // in a devicelink. It makes not sense anyway and may be used for exploits, so let's close the door!
998 if (n > 255) return NULL;
999
1000 Seq = (cmsSEQ*) _cmsMallocZero(ContextID, sizeof(cmsSEQ));
1001 if (Seq == NULL) return NULL;
1002
1003 Seq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, n, sizeof(cmsPSEQDESC));
1004 Seq -> n = n;
1005
1006 if (Seq -> seq == NULL) {
1007 _cmsFree(ContextID, Seq);
1008 return NULL;
1009 }
1010
1011 for (i=0; i < n; i++) {
1012 Seq -> seq[i].Manufacturer = NULL;
1013 Seq -> seq[i].Model = NULL;
1014 Seq -> seq[i].Description = NULL;
1015 }
1016
1017 return Seq;
1018 }
1019
1020 void CMSEXPORT cmsFreeProfileSequenceDescription(cmsContext ContextID, cmsSEQ* pseq)
1021 {
1022 cmsUInt32Number i;
1023
1024 if (pseq == NULL)
1025 return;
1026
1027 if (pseq ->seq != NULL) {
1028 for (i=0; i < pseq ->n; i++) {
1029 if (pseq ->seq[i].Manufacturer != NULL) cmsMLUfree(ContextID, pseq ->seq[i].Manufacturer);
1030 if (pseq ->seq[i].Model != NULL) cmsMLUfree(ContextID, pseq ->seq[i].Model);
1031 if (pseq ->seq[i].Description != NULL) cmsMLUfree(ContextID, pseq ->seq[i].Description);
1032 }
1033
1034 _cmsFree(ContextID, pseq ->seq);
1035 }
1036
1037 _cmsFree(ContextID, pseq);
1038 }
1039
1040 cmsSEQ* CMSEXPORT cmsDupProfileSequenceDescription(cmsContext ContextID, const cmsSEQ* pseq)
1041 {
1042 cmsSEQ *NewSeq;
1043 cmsUInt32Number i;
1044
1045 if (pseq == NULL)
1046 return NULL;
1047
1048 NewSeq = (cmsSEQ*) _cmsMalloc(ContextID, sizeof(cmsSEQ));
1049 if (NewSeq == NULL) return NULL;
1050
1051
1052 NewSeq -> seq = (cmsPSEQDESC*) _cmsCalloc(ContextID, pseq ->n, sizeof(cmsPSEQDESC));
1053 if (NewSeq ->seq == NULL) goto Error;
1054
1055 NewSeq -> n = pseq ->n;
1056
1057 for (i=0; i < pseq->n; i++) {
1058
1059 memmove(&NewSeq ->seq[i].attributes, &pseq ->seq[i].attributes, sizeof(cmsUInt64Number));
1060
1061 NewSeq ->seq[i].deviceMfg = pseq ->seq[i].deviceMfg;
1062 NewSeq ->seq[i].deviceModel = pseq ->seq[i].deviceModel;
1063 memmove(&NewSeq ->seq[i].ProfileID, &pseq ->seq[i].ProfileID, sizeof(cmsProfileID));
1064 NewSeq ->seq[i].technology = pseq ->seq[i].technology;
1065
1066 NewSeq ->seq[i].Manufacturer = cmsMLUdup(ContextID, pseq ->seq[i].Manufacturer);
1067 NewSeq ->seq[i].Model = cmsMLUdup(ContextID, pseq ->seq[i].Model);
1068 NewSeq ->seq[i].Description = cmsMLUdup(ContextID, pseq ->seq[i].Description);
1069
1070 }
1071
1072 return NewSeq;
1073
1074 Error:
1075
1076 cmsFreeProfileSequenceDescription(ContextID, NewSeq);
1077 return NULL;
1078 }
1079
1080 // Dictionaries --------------------------------------------------------------------------------------------------------
1081
1082 // Dictionaries are just very simple linked lists
1083
1084
1085 typedef struct _cmsDICT_struct {
1086 cmsDICTentry* head;
1087 } _cmsDICT;
1088
1089
1090 // Allocate an empty dictionary
1091 cmsHANDLE CMSEXPORT cmsDictAlloc(cmsContext ContextID)
1092 {
1093 _cmsDICT* dict = (_cmsDICT*) _cmsMallocZero(ContextID, sizeof(_cmsDICT));
1094 if (dict == NULL) return NULL;
1095
1096 return (cmsHANDLE) dict;
1097
1098 }
1099
1100 // Dispose resources
1101 void CMSEXPORT cmsDictFree(cmsContext ContextID, cmsHANDLE hDict)
1102 {
1103 _cmsDICT* dict = (_cmsDICT*) hDict;
1104 cmsDICTentry *entry, *next;
1105
1106 _cmsAssert(dict != NULL);
1107
1108 // Walk the list freeing all nodes
1109 entry = dict ->head;
1110 while (entry != NULL) {
1111
1112 if (entry ->DisplayName != NULL) cmsMLUfree(ContextID, entry ->DisplayName);
1113 if (entry ->DisplayValue != NULL) cmsMLUfree(ContextID, entry ->DisplayValue);
1114 if (entry ->Name != NULL) _cmsFree(ContextID, entry -> Name);
1115 if (entry ->Value != NULL) _cmsFree(ContextID, entry -> Value);
1116
1117 // Don't fall in the habitual trap...
1118 next = entry ->Next;
1119 _cmsFree(ContextID, entry);
1120
1121 entry = next;
1122 }
1123
1124 _cmsFree(ContextID, dict);
1125 }
1126
1127
1128 // Duplicate a wide char string
1129 static
1130 wchar_t* DupWcs(cmsContext ContextID, const wchar_t* ptr)
1131 {
1132 if (ptr == NULL) return NULL;
1133 return (wchar_t*) _cmsDupMem(ContextID, ptr, (mywcslen(ptr) + 1) * sizeof(wchar_t));
1134 }
1135
1136 // Add a new entry to the linked list
1137 cmsBool CMSEXPORT cmsDictAddEntry(cmsContext ContextID, cmsHANDLE hDict, const wchar_t* Name, const wchar_t* Value, const cmsMLU *DisplayName, const cmsMLU *DisplayValue)
1138 {
1139 _cmsDICT* dict = (_cmsDICT*) hDict;
1140 cmsDICTentry *entry;
1141
1142 _cmsAssert(dict != NULL);
1143 _cmsAssert(Name != NULL);
1144
1145 entry = (cmsDICTentry*) _cmsMallocZero(ContextID, sizeof(cmsDICTentry));
1146 if (entry == NULL) return FALSE;
1147
1148 entry ->DisplayName = cmsMLUdup(ContextID, DisplayName);
1149 entry ->DisplayValue = cmsMLUdup(ContextID, DisplayValue);
1150 entry ->Name = DupWcs(ContextID, Name);
1151 entry ->Value = DupWcs(ContextID, Value);
1152
1153 entry ->Next = dict ->head;
1154 dict ->head = entry;
1155
1156 return TRUE;
1157 }
1158
1159
1160 // Duplicates an existing dictionary
1161 cmsHANDLE CMSEXPORT cmsDictDup(cmsContext ContextID, cmsHANDLE hDict)
1162 {
1163 _cmsDICT* old_dict = (_cmsDICT*) hDict;
1164 cmsHANDLE hNew;
1165 cmsDICTentry *entry;
1166
1167 _cmsAssert(old_dict != NULL);
1168
1169 hNew = cmsDictAlloc(ContextID);
1170 if (hNew == NULL) return NULL;
1171
1172 // Walk the list freeing all nodes
1173 entry = old_dict ->head;
1174 while (entry != NULL) {
1175
1176 if (!cmsDictAddEntry(ContextID, hNew, entry ->Name, entry ->Value, entry ->DisplayName, entry ->DisplayValue)) {
1177
1178 cmsDictFree(ContextID, hNew);
1179 return NULL;
1180 }
1181
1182 entry = entry -> Next;
1183 }
1184
1185 return hNew;
1186 }
1187
1188 // Get a pointer to the linked list
1189 const cmsDICTentry* CMSEXPORT cmsDictGetEntryList(cmsContext ContextID, cmsHANDLE hDict)
1190 {
1191 _cmsDICT* dict = (_cmsDICT*) hDict;
1192 cmsUNUSED_PARAMETER(ContextID);
1193
1194 if (dict == NULL) return NULL;
1195 return dict ->head;
1196 }
1197
1198 // Helper For external languages
1199 const cmsDICTentry* CMSEXPORT cmsDictNextEntry(cmsContext ContextID, const cmsDICTentry* e)
1200 {
1201 cmsUNUSED_PARAMETER(ContextID);
1202 if (e == NULL) return NULL;
1203 return e ->Next;
1204 }