/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 | } |